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

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

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!