Skip to forum content

E-HCS Support

온라인 서비스 지원센터에 오신걸 환영합니다.

Live News Feed


페이지 1

로그인 또는 가입만 답변을 작성할 수 있습니다.

RSS 주제 피드

글 [ 1 ]

1

주제: FAQ about C on the Microchip PIC

출처 : http://www.xargs.com/pic/c-faq.html
Frequently Asked Questions (FAQ) about C on the Microchip PIC

These are questions which are frequently asked on various Microchip and PIC-related forums which generally apply to the Hi-Tech C compilers and the MPLAB C18 compiler. Some of these are just general C questions that have nothing in particular to do with PICs or any specific compiler, and some are general PIC questions

What's the best way to get forum help with a code problem?
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:
◦Tell us which PIC you're using.
◦Tell us which compiler you're using.
◦Tell us which version of the compiler you're using.
◦Tell us about any non-standard build options you're using (large memory model, large stack model, modified linker file, etc.)
◦Tell us if it's a “lite” or “student” version of the compiler.
◦If it's a Hi-Tech compiler, tell us if you're using PRO or STD mode.
◦Cut and paste any error messages into your post.

Don't:
◦Type code into the forum.
◦Cut and paste code into the forum.
◦Put the code into your message with code tags.
◦Show an “example” of the problem.



2.How can I remap a group of pins on different ports into a single structure?
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.

3.How can I make an array of port pins?
4.How can I declare a pointer to a port pin?
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.


5.How can I pass a port pin as an argument to a function?
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);

6.Why am I getting a syntax error when I use a #define?
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.

7.What do these MPLAB C18 warnings mean?
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:
◦type qualifier mismatch in assignment
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;

◦suspicious pointer conversion
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.

◦type mismatch in assignment
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;


8.Why am I getting a syntax error on a declaration in this simple code?

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.

9.How can I save a float, int, double, long, array, or structure to EEPROM?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.

10.How can I share a global struct or other type among different C files?
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: #include “globals.h”

/* define the int */

int global_int;

/* define the struct */

struct tag global_struct;

/* define the string */

const char string[] = "Hello, world.";
In main.c: #include “globals.h”
#include <stdio.h>

void main(void)
{
    /* use the globals */

    global_struct.b = 42;

    global_int = 0x4242;

    printf("%s\n", string);
}


11.How can I find what's causing the “weird” behavior in my MPLAB C18 program?
Some common causes of “strange” behavior:
◦ An error in your interrupt declaration
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.

◦Spending too much time in the ISR
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.

◦ Calling functions from the ISR
◦ Manipulating GIE and/or GIEL in the interrupt handler. These are enabled and disabled automatically by the PIC; enabling them in the ISR can cause unexpected reentrancy.
◦ Software stack overflow
◦ Inappropriate modifications to the linker file
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.

◦ Writing past the end of an array
◦ Writing through an uninitialized pointer
◦ Not using the “c018iz” variant of the startup code when standard C zeroing behavior is expected.
◦ Not compiling with the large memory model on a 128K PIC
◦ Using a PIC with the fast interrupt errata
The easiest workaround for this errata is to use the interruptlow keyword on the high-priority interrupt.

◦Compiling for extended mode with the PIC configured in standard mode, or vice versa.



12.How can I debug a software stack overflow in MPLAB C18?
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:
◦Set a breakpoint at the beginning of your program. When you hit the breakpoint, fill the stack with a known value, such as 0xAA. This can be done in MPLAB IDE by opening the File Registers window, right clicking, and selecting Fill Registers. Let your program continue running until it starts misbehaving. Stop your program and inspect the stack area in the File Registers window; if all of your known values have been overwritten, you know you have a stack overflow.
◦Set a complex breakpoint on the last memory address of the stack. This will cause the debugger to halt at the point of the stack overflow. To set a complex breakpoint in the MPLAB IDE Simulator, select Debugger... Complex Breakpoints... Add Breakpoint... Data Memory. Enter the last address of the stack memory (for example, if your 256 byte stack is at 0x100, enter 0x1FF), and ensure the breakpoint type is “Write”. Other debuggers might have slightly different methods for creating a complex breakpoint (or may not support complex breakpoints).
◦Since FSR1 is the stack pointer, you can inspect it at various points in your program to see how much stack has been used.


13.What is the best way to declare a C18 interrupt service routine (ISR)?

#pragma code isr=0x08
#pragma interrupt isr

void    isr(void)
{
   /* interrupt handling code here */
}

#pragma code
This avoids the assembly language springboard function which ◦ wastes code space
◦ increases interrupt latency
◦ is ugly.

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.

14.Should I use interrupt priorities on the PIC18?
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:
◦More interrupt setup code
◦More complex ISR declaration
◦Higher latency for high-priority interrupts due to the necessary springboard GOTO
◦Much higher latency for low-priority interrupts since they can't use the fast register stack
◦Increased code space usage due to all of the above.
◦More hardware and software stack space is required.

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.


15.Why is my C18 interrupt behaving strangely or not running as fast as I expect?
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.


16.When should I use volatile?
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 effects” from 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
“volatile” tells the compiler to do exactly what the programmer said to do. To correct the code fragment above, “i” must 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.


17.Should I just make everything volatile to be safe?
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 “a” will 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.


18.I just upgraded C18 and there's no linker script for my processor?
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.


19.How can I pass a pointer to a two-dimensional array to a function, or declare a pointer to a two-dimensional array?
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;


20.What is Fosc? How is Fosc related to the clock speed? Does the PLL change Fosc?
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.


21.Why does the UART suddenly stop receiving data? Transmit still works.
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.

22.Why is my port pin not behaving the way I expect?
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.


23.Can I sleep in an ISR?
When an interrupt occurs on a PIC, two things happen:
1.The program counter is pushed on the stack
2.The PIC branches to the interrupt vector address.

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.


24.With C18, how can I place some data at a specific address in ROM without using a linker script?

Use #pragma romdata. For example:
#pragma romdata mac_addr=0x800
rom unsigned char mac_addr[] = { 1, 2, 3, 4, 5, 6 };
#pragma romdata


25.Why am I getting a syntax error at the end of my header file?

Because the file doesn't end in a newline.

26.What do the different MPLAB C18 memory models do?

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.

27.What is read-modify-write (RMW)?

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:
◦On the PIC18, PIC24/dsPIC, and enhanced midrange PIC16 (PIC16F1xxx parts), port latch registers are available. Reading from a latch register does not read back the state of the port pins, so BSF instructions can safely be used back-to-back. On these PICs, all writes should be done to these LAT registers, and all reads from the PORT registers. Never write to the PORT registers.
◦If using a baseline or standard midrange part (PIC10/12/16), use a shadow register for the port. For example: 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.
◦Insert a delay between writing to each port pin. This gives the port pin time to reach Vih so that its state will be read back correctly on the next BSF. The problem with this approach is that it's not trivial to determine how much of a delay is necessary, and changes made down the road to the PIC's clock speed or board layout can cause RMW issues to unexpectedly reappear.

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.

28.Why does the PIC's flash and/or EEPROM have all FFs or contain corrupted values when powering the PIC on or off?

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.

29.How does the PORTB change interrupt work?
30.How can I tell which pin caused the PORTB interrupt?
31.How can I clear the RBIF flag?

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.

32.What does “higher order function calls not supported yet in the presence of the overlay storage class” mean?

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.

33.Where can I find the settings for the MPLAB C18 “#pragma config” directive?

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 | more
Note the the configuration settings are built in to the compiler and are not in the processor's header file.

34.Why am I getting a syntax error on my “#pragma config FOSC = ...” directive?

Because you have a #define FOSC ..." directive somewhere in your code.

35.How can I fix an “undefined symbol: _putch” error 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.

36.Why does a simple function return an incorrect value in MPLAB C18?

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.

37.How can I combine two bytes into an integer?

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 “a” as the most significant byte (MSB) and “b” as the least significant byte (LSB). This has two major advantages over other approaches:

◦You can easily tell which byte is the MSB by looking at the code.
◦This code does the same thing on any standard C compiler or any processor.

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:
◦Use a union: 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:

■It isn't obvious which byte is the MSB and which is the LSB. You can call one byte “lsb” and the other “msb”, but you have to read the compiler's implementation details to know which is which.
■On different compilers and processors, which byte is the MSB will vary. The code will compile, but will give incorrect results.
■Writing into one member of a union and reading from a different member is “implementation defined behavior” in C. This means that you have to read the compiler's implementation documentation to determine whether this approach will even work.

◦Use a pointer:

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:

■It isn't obvious which byte in “buff” is the MSB and which is the LSB.
■On different compilers and processors, which byte is the MSB will vary.

38.How can I split an integer into two bytes?

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.


39.Why am I getting “call of function without prototype” even 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);

40.What does “can't find 0x108 words (0x108 withtotal) for psect bss in segment RAM” mean?

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:
1. uninitialized
2. static storage duration (at file scope and/or using the static keyword)
3. type char (including arrays)
4. size greater than 256 bytes

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:

◦Replace initialized data with data that's assigned initial values at runtime.
◦Make initialized data const where possible.
◦Wrap initialized data in a super-struct; a single initialized object that's larger than 256 bytes will be placed in a bigdata psect.

41.What does “stack frame too large” mean?

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.

42.Why is the Hi-Tech compiler issuing “variable 'x' is not used” warnings 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 “b” is not used and that the “b = a” assignment 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 “b” is assigned to and appears to be “used” in 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 “b” is 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 “a” and “b” have been removed, and the compiler correctly generates code only for the necessary side effect: loading 1 into PORTA.

43.Why is the preprocessor calculating an incorrect value?

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:

1.Integer overflow:

#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.

2.Missing parentheses:

#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;

Share

글 [ 1 ]

페이지 1

로그인 또는 가입만 답변을 작성할 수 있습니다.



PunBB.INFO - расширения и темы на заказ