weirdbehavior in my MPLAB C18 program?
higher order function calls not supported yet in the presence of the overlay storage classmean?
#pragma configdirective?
#pragma config FOSC = ...directive?
undefined symbol: _putcherror when using printf() with a Hi-Tech compiler?
call of function without prototypeeven though I have a prototype?
can't find 0x108 words (0x108 withtotal) for psect bss in segment RAMmean?
stack frame too largemean?
variable 'x' is not usedwarnings even though the variables are being used?
can not fit the sectionlinker error?
Create a minimal, self-contained example that demonstrates the problem. It should be a single C file -- no header files unless the problem specifically involves header files, and no external dependencies so that anyone can compile it. Remove any code (including commented-out code) that is not related to the problem. Use standard C types like char and int, not typedefs like DWORD or UINT8. Include your configuration fuse settings in the code. If the code compiles and runs, test the code in the simulator to confirm that it exhibits the problem. Attach the C file to your forum post.
Do:
liteor
studentversion of the compiler.
Don't:
exampleof the problem.
If you have pins scattered on different ports or pins that are
not in the desired order on one port, and you're hoping to
define something that lets you do lcd.data = 42
, you
can't. C places all of the members of a structure in address
order. C does not support the concept of writing to
scattered memory addresses in a single operation.
There is no simple or clean mechanism to logically rearrange port pins. The most straightforward technique is to write a function that encapsulates the necessary operations on the port pins. See the following three questions for further discussion.
You can't. The C language does not permit taking the address of a bit or of a bit-field member of a structure. This means you can't have pointers to bits or arrays of bits, whether or not they're port pins.
You can't, because you can't take the address of a port pin (refer to the previous question). You can pass the value of a port pin to a function just like any other integer, but the function won't be able to modify the port pin.
You can pass a reference to a port pin as an argument to a function, or make an array of port pin references, by using the address of the port (not the pin) and an identifier for the pin. For example:
void set_port_pin(volatile unsigned char *port, unsigned char pin) {
*port |= 1U << pin;
}
set_port_pin(&PORTA, 7);
This will set RA7 to 1. Refer to your compiler's header file for
the correct type to use for port.
Note that this type of code is grossly inefficient on a PIC, and will use far more code space and execution time than simply setting the pin value directly or through a macro, which is the preferred solution.
If you need a further layer of abstraction that lets you refer to the ports themselves indirectly (e.g., a PC program that sends commands controlling the state of arbitrary port bits), place the port addresses in an array:
volatile unsigned char * const ports[] =
{
&PORTA,
&PORTB,
&PORTC
};
/* an enum that defines indices into the ports array */
enum {
MY_PORTA = 0,
MY_PORTB = 1,
MY_PORTC = 2
};
void set_port_pin(unsigned char port, unsigned char pin) {
*ports[port] |= 1U << pin;
}
set_port_pin(MY_PORTB, 5);
You probably have a semicolon at the end of your #define.
#define is not a statement, and does not end in a semicolon. A #define just does simple text substution; if you have a semicolon in your #define text, the preprocessor will insert that semicolon into your C code, possibly in a place where a semicolon is a syntax error.
When compiling code that assigns pointers or passes them as function arguments, C18 tends to provide spurious warnings on valid code, or worse, fails to provide warnings on invalid code. These are the most commonly encountered issues with C18 pointer warnings:
This means that an assignment (or passing an argument to a function) attempted to add, remove, or modify a qualifier. In C18, the qualifiers are const, volatile, rom, ram, near, and far.
The most common source of this warning is mismatching a near and far qualifier on a call to a C library function. For example, a call to printf() will usually elicit this warning. If you are compiling with the default (small) memory model, the format string argument to printf() is qualified as "near rom", while the printf() function was actually compiled with the large memory model which uses "far rom" pointers, resulting in the qualifier mismatch.
In this particular case, the warning is spurious, because the compiler simply converts the 16-bit near pointer to a 24-bit far pointer, and everything works as expected. The warning can be suppressed by adding a (const far rom char *) cast to the format string, or by compiling with the large memory model, or by recompiling the C library with the small memory model.
In other cases, this warning probably indicates an error in your code, though bugs in the compiler cause it to elicit this warning on certain valid code sequences, such as:
const void *cvp;
int *ip;
cvp = ip;
This is generally caused by a sign mismatch in a pointer assignment or function call. A common cause is using unsigned char for strings (strings are char, not unsigned char). For example:
void func(char *string);
unsigned char data[10];
func(data);
Another common cause is passing a pointer to an incompatible type,
such as passing a pointer to a two-dimensional
array to a plain
pointer:
rom unsigned char array[2][3]; void func(rom unsigned char *ptr); func(array);or passing an integer to a function expecting a pointer:
int foo; void func(int *ptr); func(foo);This warning can also be elicited by valid code, such as:
void *vp;
unsigned char *ccp;
memcpy(vp, ccp, 0);
To work around this bug, simply cast ccp to the type that memcpy()
expects:
memcpy(vp, (const void *)ccp, 0);A cleaner, safer solution is to wrap function calls that trigger spurious warning in macros:
#define memcpy(a,b,c) memcpy((a),(const void*)(b),(c)) #define memcmp(a,b,c) memcmp((const void*)(a),(const void*)(b),(c)) #define strcpy(a,b) strcpy((char*)(a),(const char*)(b))Of course these casts, like any casts, can mask errors in your program. But C18 gives you the unfortunate choice between casting and having warnings for valid code.
This generally means an error in your code, such as:
char *a;
int *b;
a = b;
b = 0x1234;
It can also be elicited by valid code such as:
const void *cvp;
int *ip;
cvp = ip;
void main(void)
{
int a;
a = 42;
int b;
b = a;
}
Because most PIC C compilers support the C90 standard, which does not
permit mixing statements and declarations. This feature was added to
the C99 standard. C90 requires that all declarations appear at the
beginning of a block.
void eeprom_write_block(void *ptr, unsigned short addr, unsigned char len)
{
unsigned char *data = ptr;
while (len--) {
eeprom_write_byte(addr++, *data++);
}
}
/* examples of usage: */
struct { int a; unsigned char b; } s;
unsigned int c;
float d;
unsigned char array[10];
eeprom_write_block(&s, 10, sizeof s);
eeprom_write_block(&c, 20, sizeof c);
eeprom_write_block(&d, 30, sizeof d);
eeprom_write_block(array, 40, sizeof array);
Note that this code takes advantage of C's void type to eliminate casting.
In globals.h:
/* declare an int */
extern int global_int;
/* declare a struct type */
struct tag {
int a;
int b;
};
/* declare a struct */
extern struct tag global_struct;
/* declare a string that's initialized elsewhere */
extern const char string[];
In globals.c:
#includeIn main.c:globals.h/* define the int */ int global_int; /* define the struct */ struct tag global_struct; /* define the string */ const char string[] = "Hello, world.";
#includeglobals.h#include <stdio.h> void main(void) { /* use the globals */ global_struct.b = 42; global_int = 0x4242; printf("%s\n", string); }
weirdbehavior in my MPLAB C18 program?
Some common causes of strange
behavior:
C18's interrupt declaration scheme is extremely error prone,
and none of the errors described below will result in a
compiler diagnostic. Interrupt declaration errors are one of the most
common causes of weird
behavior. Erroneous interrupt
declarations can appear to work correctly, but then subsequently fail when
modifying other parts of the code, upgrading the compiler, or moving to a different processor.
Ensure you haven't mixed up #pragma code addresses, or interrupt vs. interruptlow keywords, or defined a low-priority interrupt with #pragma interrupt, or enabled low-priority interrupts with only a high-priority handler, or vice versa, or omitted the necessary #pragma code and #pragma interrupt statements, or configured a specific interrupt at one priority with the code that handles it defined at the other priority. If you're using assembly language springboard functions, ensure these are actually present in your code.
Don't use delays, write to slow peripherals like LCDs, write to EEPROM, or perform other time-consuming tasks in the ISR. If you have an interrupt that occurs more quickly than one of these tasks, your application never gets out of the ISR and your main code never runs.
If you've modified the linker file to create an object larger than 256 bytes, make sure you've followed all of the instructions here, especially the part about accessing the object through a pointer.
c018izvariant of the startup code when standard C zeroing behavior is expected.
The easiest workaround for this errata is to use the interruptlow keyword on the high-priority interrupt.
Start by reading about how the software stack is implemented in the
Calling Conventions
chapter of the user's guide.
Generally, PICs with at least 512 bytes of RAM have a 256 byte stack by default, and PICs with less RAM have a 64 byte stack by default. Check your PIC's generic linker file in bin/LKR to see how large the stack is and where it's located.
Look for large objects that use stack space. Things that are
allocated on the stack include function arguments and local
variables in functions which do not have the static
or
rom
qualifiers. Identify large structures and arrays and
move them off of the stack by making them static
or
global, or pass a pointer to the object rather than passing the entire
object. Some library functions, such as the
printf() family, can use more than 64 bytes of stack space, which
alone can blow the stack on parts with 64 bytes of stack space.
If you're still getting a stack overflow, here are some techniques to find it:
Write. Other debuggers might have slightly different methods for creating a complex breakpoint (or may not support complex breakpoints).
#pragma code isr=0x08
#pragma interrupt isr
void isr(void)
{
/* interrupt handling code here */
}
#pragma code
This avoids the assembly language springboard function which
This technique can generally only be used when not using interrupt priorities, which are generally not needed.
If you need interrupt priorities, this is how to declare the two interrupt handlers:
#pragma interrupt high_isr
void high_isr(void)
{
/* high priority interrupt handling code here */
}
#pragma interruptlow low_isr
void low_isr(void)
{
/* low priority interrupt handling code here */
}
#pragma code high_vector=0x08
void high_vector(void)
{
_asm GOTO high_isr _endasm
}
#pragma code low_vector=0x18
void low_vector(void)
{
_asm GOTO low_isr _endasm
}
#pragma code
Yes, you really need five pragmas and four functions to declare two
ISRs in C18. And because the vectors have to be defined as functions,
there will be a wasted RETURN instruction in each of them -- C18 isn't
smart enough to optimize the RETURNs out.
Probably not. By default (IPEN = 0), the PIC18's interrupts operate in PIC16 compatibility mode. This means there is a single interrupt vector at address 0x08, and no interrupts will vector to address 0x18. Even in compatibility mode, interrupts take advantage of the fast register stack, which makes zero-cycle context save/restore possible, so there is no inherent disadvantage to using compatibility mode. The disadvantages to using interrupt priorities are many:
Even ignoring the disadvantages of interrupt priorities, few applications actually need them to perform correctly. A well-written interrupt handler can process all of its interrupts quickly enough to prevent any interrupts from being lost.
A typical example of a situation that needs interrupt priorities is high-speed clock coming in on an INT pin that has very tight timing requirements. In such a case, it makes sense to enable interrupt priorities, and configure the INT interrupt as the only high-priority interrupt. Using multiple high-priority interrupts is not much different than using compatibility mode, and thus is generally pointless.
The most common cause is calling a function from the interrupt handler. In C18, calling a function from the interrupt handler triggers a worst-case context save. This results in interrupt latency of 50 instruction cycles or more, and total interrupt context save/restore overhead of 100 cycles or more. This makes high-speed interrupts impossible.
Note that the worst-case context save occurs even if the called function has no parameters or local variables, or even if the function is never actually called. The mere presence of a function call statement in the ISR triggers the worst-case scenario.
The easiest solution to this is simply not to call any functions from the interrupt handler, which will give best-case context saving. There is a more involved solution available if you absolutely must call functions from the ISR (local mirror).
Other possible causes are listed here.
The volatile qualifier originates with the as-if
rule in C:
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).The critical part here is,
An actual implementation need not evaluate part of an expression if it can deduce that its value is not used. For example,
int i;
PORTA = 1;
for (i = 0; i < 10; i++)
;
PORTA = 2;
Your expectation might be that 1 would be written to PORTA, then a
delay, then 2 would be written to PORTA. But the compiler is free to
just do PORTA = 2, or even to generate no code at all. This is because a compiler can look at the code and realize there are
no needed side effectsfrom assigning 1 to PORTA or from the loop.
But PORTA works as expected because it is declared as volatile in the compiler's header files; every operation on PORTA must be carried out exactly as stated by the programmer:
An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine
volatiletells the compiler to do exactly what the programmer said to do. To correct the code fragment above,
imust also be volatile to ensure the delay loop is executed exactly as stated.
volatile is also relevant in interrupt handlers in cases like this:
int interrupt_flag;
int main(void) {
interrupt_flag = 0;
while (interrupt_flag == 0) /* wait for interrupt */
;
/* do stuff */
}
The compiler can look at that code and realize that interrupt_flag is
0, and will always remain 0, so there is no need for it to generate
any code at all for main(), even though you might have an interrupt
handler somewhere that sets interrupt_flag to 1. The solution is to
make interrupt_flag volatile, so that the compiler understands that it
may be modified in ways unknown to the implementation.
Note that volatile does not provide atomic access. In the previous example, it will take multiple CPU instructions to compare interrupt_flag against 0 on an eight-bit processor. The volatile keyword does not prevent an interrupt from occurring in the middle of this sequence of instructions, which means interrupt_flag may take on an unexpected value with a properly timed interrupt. Atomic access requires additional protection, such as disabling interrupts around the comparison.
No. With a modern C compiler, you have to forget about the idea that the compiler is going to blindly follow your instructions step by step. You have to think in terms of what side effects your program generates.
In an embedded program, side effects are almost exclusively caused by reading or writing the processor's special function registers. If you're blinking an LED, sending data over a UART, or writing to internal EEPROM, you are accessing a special register that is declared as volatile, so all of these things are guaranteed to happen. But other concepts, like assigning a value to a variable, or the passage of time caused by a delay loop, are not side effects. For example:
int main(void)
{
int a, b;
a = 1;
a *= 2;
b = 3;
PORTA = a + b;
}
The only side effect in this program is writing 5 to PORTA. Since
PORTA is volatile, we are guaranteed that will happen. What we aren't
guaranteed is that some memory location called awill have the value 1 written to it, or that the processor's multiply instruction will be used in calculating the value to be written to PORTA. If the compiler is smart enough to replace the above with
int main(void)
{
PORTA = 5;
}
we've achieved the same result with less code space and faster
execution time.
There is one situation where you'd want to declare a
and
b
volatile in the above code: when you're debugging.
If your compiler is smart enough to optimize all of the assignments
away, you won't be able to step through the assignments in the
debugger to make sure they're working the way you want. Using the
volatile keyword temporarily during debugging can work around this, as
can choosing a lower optimization level during debugging.
Recent versions of C18 (after 3.20 or so) include a slightly more
powerful linker which does not require including a linker script in
your project. The linker now has a single generic linker
script
per processor type, which can be used to build a
standard, extended mode, or ICD debug build; the IDE passes options to
the linker describing the build type and processor type, and the linker
chooses the appropriate script automatically.
If you have a non-standard requirement like a bootloader, you can still include a linker script in your project. The generic linker scripts are located in the bin/LKR subdirectory of the compiler.
With a two-dimensional array, the compiler needs to know how many elements are in a column to move from one row to the next, but movement within a row is always by a single element, so the compiler doesn't need to know the number of rows. Thus the pointer declaration must include the number of columns in the array:
char array[2][3] = { "19", "42" };
void func(char (*p)[3])
{
puts(p[1]); // prints "42"
}
func(array);
/* or */
char (*p)[3];
p = array;
Fosc is a term that's not used consistently throughout the data sheet.
When you're looking at the electrical specifications, Fosc means the actual frequency of the external (or internal) clock.
When you're looking at the configuration bits, Fosc means the type of the oscillator (e.g., XT, HS), which isn't related to the clock speed at all.
When you're looking at something that affects software, such as the equations for setting up the PWM or UART or I2C baud rate, Fosc means the CPU clock speed, which might or might not be the same as the oscillator speed. The CPU clock speed is the oscillator speed scaled up or down by the chain of oscillator dividers and multipliers. For example, if you're using a PIC with a 4 MHz external oscillator and no PLL, Fosc is 4 MHz. If you're using a PIC with an 8 MHz internal oscillator and a 4x PLL, Fosc is 32 MHz.
You are getting overrun errors. An overrun error occurs when the UART's receive FIFO is full, and another byte of data arrives. When this happens, the UART turns off its receiver, and sets the OERR bit. The OERR bit is read-only and cannot be cleared in software. To restart the UART and clear OERR, it is necessary to clear and then set CREN.
Overrun errors are caused by software bugs. Unlike framing errors, they are not caused by noise, baud rate mismatches, or other external events.
To avoid overruns, receive serial data in an interrupt, not in a busy loop, and ensure your interrupt handler has low latency.
Most likely because you haven't disabled the analog features on the port. When a pin is attached to the A/D converter, it will default to analog mode. Such pins are generally labeled as ANxx. Pins on PORTA are almost always analog, while pins on other ports might also be analog. Some 18F PICs, such as the 18F4550 family, have analog pins on PORTB, but these pins can be made to default to digital with one of the configuration words.
Some PICs also have comparators that default to enabled and need to be disabled to allow use of their pins in digital mode.
When a pin is in analog mode, a digital read from the port will always return 0. The pin will still work as a digital output, but because the pin reads 0, it will be particularly susceptible to read-modify-write effects.
Consult the A/D converter chapter of your data sheet on how to disable analog features. There is no single method; Microchip has used a wide variety of techniques in different PIC families. Note that simply turning off the A/D converter is not enough; each analog pin must be configured for digital use.
If you have disabled analog features and are still not seeing the expected behavior, it might be the read-modify-write effect.
When an interrupt occurs on a PIC, two things happen:
That's it. The PIC does not store any internal state indicating
that it's in an ISR
; it continues executing code exactly the
same way as it did before it entered the ISR. The ISR is purely a
software concept that the PIC knows nothing about. This means you
can execute a SLEEP instruction or do anything else in an ISR that
you could do in your non-ISR code. Whether sleeping in the ISR is
a good idea or not is a different matter.
Use #pragma romdata. For example:
#pragma romdata mac_addr=0x800
rom unsigned char mac_addr[] = { 1, 2, 3, 4, 5, 6 };
#pragma romdata
Because the file doesn't end in a newline.
They do not make more RAM or ROM available. They will
not fix a can not fit the section
error, or any other
compiler or linker error.
The default model is the small model (MPLAB IDE refers to this as the
small code model
). This should be used on any PIC
with 64KB or less of program memory.
The large model should be enabled when using a PIC with more than 64KB of program memory. The only effect the large model has is to make pointers to rom 24 bits instead of 16 bits. If you don't use the large model on a PIC with more than 64KB, the code will compile and link successfully, but if you have pointers to any objects at addresses above 64K, accesses to those objects will fail, since a 16-bit pointer can't point at an address above 64K.
The large model increases the amount of code space used since the larger pointers have more overhead.
MPLAB IDE also incorrectly refers to a data model
option. C18
does not have a data model; this option refers to an optimization that
places all data in the access bank (small data model
). By
default, C18 uses all available banked RAM (large data model
).
While certain unusual applications that use very little RAM could
benefit from this optimization, it should normally be at the default
(off) setting.
Consider the following code:
RB0 = 1; RB1 = 1;A reasonable compiler will generate a BSF (bit set file register) instruction for each of these C statements. The BSF instruction does not simply set the value of a single bit. Internally, the PIC reads all eight bits from the port, sets the desired bit, and writes all eight bits back out to the port. For a single BSF instruction by itself, this doesn't cause an issue. But when two BSFs on the same port happen back-to-back, the first BSF may not work as expected.
The problem occurs when the second BSF reads back the state of the port. If the voltage on RB0 has not yet risen to the level the PIC considers a logic 1 (Vih), the PIC will read back a 0 for RB0, set bit 1 for RB1, and write the result back out to the port. This leaves RB0 at 0, and RB1 at 1, which is not the desired result.
RMW can become more of an issue with higher PIC clock frequencies, because the amount of time between instructions is smaller, requiring the port output to have a faster rise time to avoid being read back in the wrong state.
There are several different fixes for the RMW issue; these are listed in order of best to worst:
union {
unsigned char byte;
struct {
unsigned RB0:1;
unsigned RB1:1;
unsigned RB2:1;
unsigned RB3:1;
unsigned RB4:1;
unsigned RB5:1;
unsigned RB6:1;
unsigned RB7:1;
} bits;
} portb;
portb.bits.RB0 = 1;
portb.bits.RB1 = 1;
PORTB = portb.byte;
With the shadow register, the write to the actual port is a full eight
bits (no BSF instructions are used), so there is no RMW issue.
Section 9.10.2 of the PICmicro Mid-Range MCU Family Reference Manual (local mirror) has additional discussion and timing diagrams that explain what happens in each clock cycle.
Because the PIC is browning out: operating at a voltage below the allowed minimum. The brownout itself does not cause flash or EEPROM corruption, but when the PIC is in brownout, it is operating in an undefined state. This means that the program counter can take on a random value which will cause random code to be executed. Murphy's Law says that the most likely place for this random code execution to occur is in your flash or EEPROM write routine.
The fix is to prevent the PIC from browning out. This can be done with an external supervisor chip such as the MCP100, or by enabling the PIC's browout reset feature.
When the PORTB change interrupt is enabled, the PIC continuously compares the value on each of the PORTB change pins with the the last PORTB values read by your software. When the current pin value of any pin doesn't match the last read value, RBIF is set.
On most PICs, the PIC does not keep track of which pin change set RBIF (enhanced midrange parts are the exception); this has to be determined in software. One common method is to XOR the current PORTB value with the previous value:
if (RBIF) {
unsigned char changes, portb;
static unsigned char last_portb;
portb = PORTB;
RBIF = 0;
changes = portb ^ last_portb;
last_portb = portb;
/* process the bits in "changes" */
}
This code leaves a bit set in changes for each pin that changed
state.Note that the RBIF flag is cleared after reading the value of PORTB. Reading PORTB updates the PIC's change-detection latch with the current value of PORTB so that it no longer sees a mismatch between the current pin state and the last-read state. As long as this mismatch condition is present, RBIF cannot be cleared.
The PORTB change interrupt has a major design error in most PICs and is not recommend for use as a general-purpose interrupt. It should only be used as a wake-from-sleep source.
higher order function calls not supported yet in the presence of the overlay storage classmean?
C18 does not permit using function pointers while compiling in the
overlay
mode. If you want to use function pointers, don't
compile in overlay mode (-sco) or don't use the overlay
keyword. The not supported yet
in the error message is meaningless; they
will not ever be supported, since C18 development has been halted.
#pragma configdirective?
Open MPLAB IDE, and look under Help...Topics...PIC18 Config Settings.
Another option is to run the compiler from the command line:
mcc18 --help-config -p=18f87k22 | moreNote the the configuration settings are built in to the compiler and are not in the processor's header file.
#pragma config FOSC = ...directive?
Because you have a #define FOSC ..." directive somewhere in your code.
undefined symbol: _putcherror when using printf() with a Hi-Tech compiler?
Hi-Tech's printf() does not send its output to any particular device by default. It calls putch() for that purpose, and you must provide an implementation of putch() that sends the output where you want. This can be a UART, LCD, etc. A simple implementation that sends the output to the UART is:
void putch(char c)
{
while (!TXIF)
;
TXREG = c;
}
printf() does not initialize the UART or any other hardware; you must
do that yourself.
Because there is no function prototype in scope at the point the function is called. This means C18 assumes the return value of the function is int (this is standard C behavior), and if the return value isn't actually int, you'll get garbage as the return value.
C18 has an interesting
calling convention; rather than passing
all return values on the stack, it uses different registers depending on
the type of the return value. So the garbage value that's returned probably
won't even be related the correct return value at all.
There are several different ways to do this. The best way is to use shifting:
unsigned int i unsigned char a, b; a = 42; b = 24; i = (a << 8) | b;This creates an int with
aas the most significant byte (MSB) and
bas the least significant byte (LSB). This has two major advantages over other approaches:
On the surface, this code might appear to be inefficient since it
involves shifting the MSB eight bits -- an especially bad idea on a
PIC. But because of C's
as-if rule, this code does not have to be interpreted
literally by the compiler. Any reasonable compiler will recognize
this common idiom and simply assign a
to the upper byte of
i
.
If you're using MPLAB C18 in its default non-standard mode
(integer promotions disabled), the above code won't work, because C18
doesn't convert a
to an int as standard C requires. You
can either enable integer promotions, or add a cast:
i = ((unsigned int)a << 8) | b;
There are two additional approaches:
union {
struct {
unsigned char a, b;
} byte;
unsigned int i;
} u;
u.byte.a = 42;
u.byte.b = 24;
// u.i now contains a and b assembled into an int
This approach has several problems:
lsband the other
msb, but you have to read the compiler's implementation details to know which is which.
implementation defined behaviorin C. This means that you have to read the compiler's implementation documentation to determine whether this approach will even work.
unsigned char buff[2]; unsigned int i; buff[0] = 42; buff[1] = 24; i = *(unsigned int*)buff;As well as being the least readable, this approach suffers from some of the same problems as the union approach:
buffis the MSB and which is the LSB.
unsigned int i unsigned char a, b; i = 4242; a = (i >> 8) & 0xFF; b = i & 0xFF;Just like the code to combine bytes into an integer, this code does exactly the same thing on any compiler and processor, and it's obvious which byte is the LSB (b) and which is the MSB (a). The union and pointer approaches described in that link can also be used for this task, but they suffer from the same disadvantages.
call of function without prototypeeven though I have a prototype?
Because you really don't have a prototype. You might have something like
void func();which is not a function prototype. C requires that a function prototype specify the number and types of the function's parameters. If there are no parameters, void must be explicitly specified:
void func(void);
can't find 0x108 words (0x108 withtotal) for psect bss in segment RAMmean?
Hi-Tech's STD compilers for the PIC18 sometimes require a certain amount of manual memory management (this is not true with the PRO compilers). Here is some background and a workaround for the error:
Given these qualifiers:
If an object is ((1 and 2 and 3) or (1 and 2 and 4)), it is placed in the bigbss psect. Any remaining objects which are (1 and 2) are placed in a bss psect.
Each C module has one bss psect of at most 256 bytes. Thus each module can have no more than a total of 256 bytes of bss objects; this restriction may require moving objects into separate modules. E.g., two int[65] arrays could not appear in one module, because their total size is 260 bytes, and they do not meet the requirements that would place them in the bigbss psect. But if each array is placed in a separate .c file, the program should build successfully.
Another restriction is that a bss psect cannot straddle a bank boundary. Thus e.g. a PIC with 512 bytes of RAM could not have three int[65] arrays, even in separate modules. Even though their total size is only 390 bytes, there is no way to place them without straddling banks. Objects in bigbss can straddle banks, and do not have per-module size restrictions. But there is no way to force an object into bigbss.
If your error relates to the data psect (initialized data) rather than a bss psect, the above workaround doesn't apply. The compiler has a hard limit of 256 bytes for the entire program's data psect. The workarounds for this issue are:
stack frame too largemean?
MPLAB C18 has a limitation on how much stack data can be accessed in a single frame (one function). This is about 120 bytes in standard mode and about 95 bytes in extended mode. This limitation cannot be overcome by increasing the stack size. One workaround is to make any large auto objects in the function static, which takes them off the stack.
variable 'x' is not usedwarnings even though the variables are being used?
This warning might be more clear if it stated that the variable's value is not used.
As was noted in the discussion of the volatile keyword, the compiler is only looking for the side effects caused by your program. Side effects are limited to reads and writes to the processor's volatile special function registers. Any other code that doesn't cause side effects may be optimized away, resulting in unexpected warnings.
For example, given this code:
void main(void)
{
unsigned char a, b;
a = 1;
b = a;
}
PICC-18 9.80 will warn that bis not used and that the
b = aassignment generates no code. Looking at the list file, no code is generated for main(). This is because the program has no side effects (it does not write to any volatile special function registers), so the compiler is permitted to optimize the entire program away. Even though
bis assigned to and appears to be
usedin the code, its value is never used, so there is no reason to generate code for it.
Now consider this code:
void main(void)
{
unsigned char a, b;
a = 1;
b = a;
PORTA = b;
}
This compiles with no warnings, because the value of bis used to write to PORTA, which is a volatile object that causes side effects. However, if you look at the list file, you'll see that compiler still didn't blindly follow the code:
52 ;t.c: 71: PORTA = b;
53 001FF8 0E01 movlw 1
54 001FFA 6E80 movwf 3968,c ;volatile
All of the references to aand
bhave been removed, and the compiler correctly generates code only for the necessary side effect: loading 1 into PORTA.
It isn't. The preprocessor doesn't evaluate expressions in C code; it simply replaces macro(s) with their associated text. The compiler evaluates the resulting replaced text.
There are two common errors when using the preprocessor to insert numeric expressions into code:
#define SECONDS_PER_DAY (60 * 60 * 24) long week = SECONDS_PER_DAY * 7;This expands to:
long week = (60 * 60 * 24) * 7;The expected result is 604800, which fits easily into a long, but the actual result is something else. This is because the expression 60 * 60 * 24 is 86400, which does not fit into a 16-bit int, and thus overflows giving a garbage value. The fact that the result is being assigned to a long isn't relevant; 60 and 24 are ints, so the multiplication is done with int arithmetic. Use 60L to force long arithmetic.
Note that MPLAB C18 has integer promotions disabled by default, which means expressions may be evaluated with char arithmetic, making overflows even more likely. You can enable integer promotions to get standard C behavior.
#define CATS 2 #define DOGS 42 #define ANIMALS CATS + DOGS int pairs = ANIMALS / 2;This expands to:
int pairs = 2 + 42 / 2;giving the unexpected result 23 instead of 22. Since the preprocessor does simple text substitution, ANIMAL is not any sort of
variable, and doesn't get any special treatment when the compiler evaluates it. C's precedence rules cause the unexpected result. The fix is to always enclose expressions in parentheses:
#define ANIMALS (CATS + DOGS)giving
int pairs = (2 + 42) / 2;