05.08.2014 Views

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

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!