Embedded Programming Tips for CCS C

From Mech
Jump to navigationJump to search

Declaring Variables

Implementation of datatypes like char, int, long, short, etc. are compiler specific. For example, the keyword int may mean an 8-bit unsigned integer in one compiler and a 16-bit signed integer in another compiler. This will often cause problems when people try to read or reuse your code. Be as specific as possible.

good:
int8 some_int;
int32 some_32bit_int;
unsigned char some_unsigned_8_bit_char;

bad:
char some_char;
int some_int;

Writing Directly to a Memory Address

When configuring the peripherals of the PIC, one usually checks the PIC's datasheet and manually writes the bits corresponding to the desired settings to the special function registers (SFRs). The CCS library attempts to make things easier by wrapping up the SFRs into function calls such as setup_adc, etc. However, some functionality may be "lost in translation," or the documentation may be so vague that it's easier just to the write the correct bits directly to the SFRs. Unfortunately, the SFR names used in the datasheet are not defined in CCS's device .h files, so we'll have to define them ourselves.

As an example, the CCS library doesn't have a function that will write to PR5 (Timer5 period match register) for devices (such as the PIC18F4431) that have them. However, we can look in the datasheet in the "Special Function Registers" section to find memory address of the SFR we want.

Note: When writing to 16-bit SFRs with a high and low byte which may be changing (such as a timer register), it is important to write to the high byte first and write to the low byte second. When reading from the 16-bit register, read the low byte first and the high byte second. The high byte is actually a buffer; writing to the low byte will automatically load the high byte into the register, and reading from the low byte will automatically copy the current value into the high byte register, in a single operation. This prevents the data from changing or rolling over in the time between reading or writing the first and second registers.

For the CCS compiler, you can use the #byte preprocessor directive to tell it to place a variable at a specific memory address. Look up the correct memory address in the datasheet (the 0x denotes hexadecimal notation):

//put variables at memory location
#byte PR5L=0xF90 //PR5 low byte
#byte PR5H=0xF91 //PR6 high byte

Later in the code, when we want to set the values, we can set it like any other variable:

PR5H = 10000/256;  //take only high bits
PR5L = 10000%256;  //take only low bits

Alternatively, we can also use pointers:

int8* PR5H;
int8* PRHL;
PR5H = 0xF91;
PR5H = 0xF90;
*PR5H = 10000/256; 
*PR5L = 10000%256;