1 C Programming for Embedded Systems. Data RAM Pointer Types ...
1 C Programming for Embedded Systems. Data RAM Pointer Types ...
1 C Programming for Embedded Systems. Data RAM Pointer Types ...
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
P. Klimo Sep 2010<br />
C <strong>Programming</strong> <strong>for</strong> <strong>Embedded</strong> <strong>Systems</strong>.<br />
Initialization Library Code (start-up code) responsiblities:<br />
1) Set up status and configuration registers<br />
2) Set up the stack (and the memory heap)<br />
3) Process the run-time initialization table to auto-initialize global<br />
variables<br />
4) Call main<br />
5) Call exit when main returns<br />
Pragmas and Compiler Directives: Processor specific additions to ANSI C.<br />
The psect directive: breaks the code into different sections that are grouped together by the Linker.<br />
Note: After the build you can view the map file (.map)<br />
Bank1 Type Qualifier:<br />
static bank0 unsigned char fred; // fred allocated to bank 0<br />
<strong>Pointer</strong>s:<br />
<strong>Data</strong> <strong>RAM</strong> <strong>Pointer</strong> <strong>Types</strong>:<br />
• 8 bit wide pointer accessing a pair of Banks (Banks 0 and 1 or Banks 2 and 3).<br />
• 16 bit wide pointer accessing all <strong>Data</strong> memory space.<br />
<strong>Data</strong> ROM <strong>Pointer</strong> <strong>Types</strong>:<br />
• 8 bit pointer accessing first 256 addresses in the Program ROM.<br />
• 16 bit pointer accessing entire ROM.<br />
Examples:<br />
void SendBuff1(char * ptr);<br />
// parameters fetched from <strong>RAM</strong><br />
void SendBuff2(const char* ptr); // parameters fetched from ROM<br />
char msg1[] = “hello”;<br />
bank1 char[]= “hello”;<br />
char *cp1 = msg1;<br />
// an array in Bank 0 <strong>RAM</strong> that is initialized at start up.<br />
// as above but data placed in Bank1<br />
// pointer to <strong>RAM</strong><br />
1
P. Klimo Sep 2010<br />
const char msg2 [] = “hello”;<br />
const char *cp2 = msg2;<br />
SendBuff1(msg1);<br />
SendBuff2(msg2);<br />
SendBuff2(“hello”);<br />
SendBuff1(“hello”);<br />
// stores string in Program ROM<br />
// pointer to ROM<br />
// accesses data in <strong>RAM</strong><br />
// accesses data in ROM<br />
// same as above<br />
// produces compiler error<br />
Interrupts:<br />
long tick_count;<br />
void interrupt tc_int(void){<br />
// keyword interrupt needed<br />
tick_count++;<br />
}<br />
Variables storage:<br />
global: Initialized are placed in the .init segment and non-initialized in the (.bss)<br />
auto: Allocated to software stack created in memory Bank0.<br />
static: Occupy fixed memory locations (defined by the psect directive).<br />
volatile: will not be optimized by compiler<br />
register: advice to compiler to store in CPU rather then <strong>RAM</strong><br />
Structures:<br />
bank1 struct {<br />
int number;<br />
int* ptr;<br />
}record;<br />
struct record student;<br />
student.number =78;<br />
2
P. Klimo Sep 2010<br />
Assessing Individual bits:<br />
struct foo {<br />
// Declare foo as a structure type<br />
unsigned lo:1; // bit 0<br />
unsigned dummy: 6; // bits 1 to 6<br />
unsigned hi:1; // bit 7<br />
} *ptrfoo; // Declare ptrfoo as a pointer to structure type foo<br />
struct foo PortA;<br />
// Define PortA as a structure foo<br />
PortA.hi =0; // Initiate bit 7 of PortA to 0<br />
ptrfoo=&PortA;<br />
// Set pointer ptrfoo to point at PortA<br />
ptrfoo->lo = 1; // Initiate bit 0 of PortA to 1<br />
Macros:<br />
#define TWO 2<br />
// Define a constant macro<br />
int y = TWO*3;<br />
#define multiplyByTwo(x) 2*(x) // Define a “ function” macro<br />
y = multiplyByTwo(3);<br />
Typedef:<br />
int myFunc tion(int r);<br />
// Declare a function (defined elsewhere)<br />
int k = 6;<br />
typedef int (*Func)(int a) ptrFunc;<br />
ptrFunc = myFunction;<br />
// Define ptrFunc as a pointer to a function type<br />
// Point to myFunction<br />
ptrFunc(k); // Call myFunc tion with argument 6<br />
3
P. Klimo Sep 2010<br />
Pre-Processor:<br />
#define HERE_WE_GO<br />
# ifdef HERE_WE_GO<br />
/* Do something */<br />
#else<br />
static bank1 unsigned char fred; // fred allocated to bank 1<br />
/* do something else */<br />
#endif<br />
static bank3 unsigned char fred; // fred allocated to bank 3<br />
Example of a Simple Source File:<br />
const int a = 66;<br />
int c = 23;<br />
int myFunc( const int x, int y){<br />
static int count =0;<br />
int c;<br />
c = x + y + count++;<br />
// Define integer 66 in program ROM<br />
// Initialize 2 bytes in the general <strong>RAM</strong><br />
// Define a function in the general <strong>RAM</strong><br />
// Private variable placed in the general <strong>RAM</strong><br />
// Automatic variable defined on the myFunc stack<br />
// Will increment count between calls<br />
return c;<br />
}<br />
void main(){<br />
int b;<br />
// The main function executes after initialization<br />
// Automatic variable defined on main’s stack<br />
while(1){<br />
b = myFunc(a, c);<br />
// Infinite Loop calls repeatedly calls myFunc<br />
}<br />
}<br />
4
5<br />
P. Klimo Sep 2010
P. Klimo Sep 2010<br />
Explanation of the Compilation Process Example:<br />
File1.c contains the C source code <strong>for</strong> the main function. This is the function that executes first.<br />
Because of the #include directives the Pre-processor includes the two files, the one in angled brackets<br />
resides in the compiler install directory, the one bracketed with “ should be placed in the Project<br />
directory. The stdio.h header file declares the library printf function that is used in the main. The<br />
header.h is a user created file that defines a new variable type Int16 based on the compiler’s<br />
implementation of the integer variable (in this case 16 bit). Unlike a macro, the typedef per<strong>for</strong>ms strict<br />
type checking. Note the use of the macro construct #ifndef etc that encloses the declarations in the<br />
header file. This prevents the Pre-processor to include in any module the same file more than once in<br />
case of a circular reference.<br />
The two extern statements declare two functions that are defined outside the file1. This enables the<br />
Pre-processor to compile the File1.c without any reference to any other files.<br />
Following the declaration of the variables, three functions are called by the main. The function getArea<br />
that is defined in the File2.c, the function printf that is defined in the Standard Library that must be<br />
linked by the linker (either explicitly or by appropriately configuring the Project file) and the function<br />
sum that is defined in the assembler File3.asm.<br />
The File3.asm includes the assembler code <strong>for</strong> the function sum. The directive global allows the labels to<br />
be exported to the linker. The _sum label (notice the leading underscore) is the starting address of the<br />
subroutine sum. The label ?_sum is the address of the run-time function stack created by the main when<br />
the function sum is called. In C the function parameters are copied by the caller (in this case the main)<br />
onto the function stack in the reverse order. There are two parameters u and v, both one byte wide.<br />
The rules between the caller and the callee (the function) are standardised <strong>for</strong> any C compiler. In this<br />
case the second character (v) is passed in the CPU work register (w) and the first parameter (u) is copied<br />
to the stack location ?_sum.<br />
Having passed the parameters to CPU register an onto the function stack, the control is passed to the<br />
subroutine starting at the memory location _sum. The machine code that resides there adds the value of<br />
the parameter placed at ?_sum to the value of the working register and returns. Thus when the<br />
function sum returns to the caller the w register in the CPU contains the sum of the two input<br />
parameters. This value is used by the caller in the function printf.<br />
On function return to the caller, it is the responsibility of the caller to “remove” the callee’s function<br />
stack so any subsequent attempts to access the callee’s stack (e.g. the previously stored parameter at<br />
the memory address ?_sum ) will have an unreliable outcome.<br />
Having independently compiled the three source modules File1.c, File2.c and File3.asm, it is the role of<br />
the linker to link them together with any pre-compiled library files to produce a single executable file. In<br />
the case of embedded system, the <strong>for</strong>mat of the executive file normally chosen is the (Intel) hex file that<br />
is used by the device programmer to download the code into the microprocessor’s program ROM.<br />
6
P. Klimo Sep 2010<br />
Example: Driving Seven Segment Display<br />
Schematic<br />
EasyPic 4 Board<br />
Task:<br />
Drive a single digit Seven Segment display, displaying digits between 0 and 9 at 1 second interval.<br />
7
P. Klimo Sep 2010<br />
Flow Diagram:<br />
8
P. Klimo Sep 2010<br />
CODE<br />
/* File: 7SegDisplay_1D.c<br />
Description:<br />
Displays a digit on single 7-segment display. Segments connected to PORTD ( segment A to RD0, segment B to<br />
RD1, etc); Common cathode is connected to the pin RA1 on PORTA. Incremented every 1s.<br />
* Test configuration: MCU: P16F877A Dev.Board: EasyPIC4<br />
Xtal: HS, 8 MHz SW: PICC.0 */<br />
#include /* HI-tech compiler definitions */<br />
/* PIC configuration: HS oscillator, Watch Dog Timer off, Browning Effect off, Low Voltage Mode off */<br />
__CONFIG(HS&WDTDIS&PWRTDIS& BORDIS&LVPDIS ); // HiTec Pragmas<br />
void Delay_ms(int ms);<br />
unsigned short SendCode(unsigned short num);<br />
unsigned short count;<br />
// Delay routine<br />
// Convert binary to seven segment code<br />
// Display value<br />
void main()<br />
{ // Initialize Ports<br />
PORTA = 0;<br />
TRISA = 0xfd;<br />
PORTD = 0;<br />
TRISD = 0;<br />
// Configure bit 1 of Port A as output<br />
// by clearing bit 1 in the TRISA register<br />
// Configure all bits of Port D as outputs<br />
// by clearing the TRISD register<br />
do { // Proceed incrementing the displayed digit from 0 to 9<br />
<strong>for</strong> (count = 0; count
P. Klimo Sep 2010<br />
//-------------- Returns 7-seg. Display code<br />
unsigned short SendCode(unsigned short num) {<br />
switch (num) {<br />
case 0 : return 0x3F;<br />
case 1 : return 0x06;<br />
case 2 : return 0x5B;<br />
case 3 : return 0x4F;<br />
case 4 : return 0x66;<br />
case 5 : return 0x6D;<br />
case 6 : return 0x7D;<br />
case 7 : return 0x07;<br />
case 8 : return 0x7F;<br />
case 9 : return 0x6F;<br />
} //case end<br />
}//<br />
void Delay_ms(int ms){<br />
unsigned int Xtal_Freq = 153; // about 1 second delay <strong>for</strong> 8MHz Xtal<br />
unsigned int count = ms;<br />
unsigned int i;<br />
while(count>0){<br />
<strong>for</strong> (i=0; i< Xtal_Freq; i++){;}<br />
count--;<br />
}<br />
}<br />
10