Difference between revisions of "Index of sample code pages"
Russ hensel (talk | contribs) |
|||
(11 intermediate revisions by 4 users not shown) | |||
Line 16: | Line 16: | ||
Timing routines, showing how seconds can be counted. Want high accuracy even with odd clock rates. | Timing routines, showing how seconds can be counted. Want high accuracy even with odd clock rates. | ||
+ | |||
+ | This dudes routines will *absolutely* blow your sox off. Really. | ||
+ | http://www.romanblack.com/one_sec.htm | ||
+ | |||
+ | == Using I2C Memory == | ||
+ | |||
+ | |||
+ | [[Using I2C Memory]] | ||
== Output to A Shift Register == | == Output to A Shift Register == | ||
− | Often used for port expansion. | + | Often used for port expansion. |
+ | |||
+ | This seems to work for 8 bits to a 74HC/HCT164 | ||
+ | |||
+ | void shiftOutByte( unsigned char aData ) { | ||
+ | |||
+ | unsigned char ix; | ||
+ | |||
+ | for( ix = 0; ix < 7; ix++ ) { | ||
+ | |||
+ | if ( aData.1 ) { | ||
+ | set_bit( SRPort, SRDataBit ); | ||
+ | } else { | ||
+ | clear_bit( SRPort, SRDataBit ); | ||
+ | } | ||
+ | |||
+ | clear_bit( SRPort, SRClockBit ); // edge triggered low to high | ||
+ | aData >>= 1; // put here as delay | ||
+ | set_bit( SRPort, SRClockBit ); | ||
+ | |||
+ | } | ||
+ | return; | ||
+ | |||
+ | } | ||
== Software Circular Buffer == | == Software Circular Buffer == | ||
− | A place to store and retrieve data, say bytes. First in First out. Methods to add and remove data. | + | A place to store and retrieve data, say bytes. First in First out. Methods to add and remove data. |
+ | |||
+ | This technique requires 4 components: | ||
+ | |||
+ | buffer to store data | ||
+ | |||
+ | pointer to the start of data (head) | ||
+ | |||
+ | pointer to the end of data (tail) | ||
+ | |||
+ | size of data currently in the buffer | ||
+ | |||
+ | Typically these 4 are stored in a container like struct or class and all together define a circular buffer: | ||
+ | |||
+ | #define BUF_LEN 64 | ||
+ | |||
+ | struct SCircularBuffer | ||
+ | { | ||
+ | unsigned char data[BUF_LEN]; //buffer to store data | ||
+ | unsigned char head; //head pointer (not really a pointer but index where data starts) | ||
+ | unsigned char tail; //tail | ||
+ | unsigned char count; //number of data bytes currently in the buffer | ||
+ | }; | ||
+ | |||
+ | The rules to use such buffer are simple. When some data needs to be written into the buffer: | ||
+ | |||
+ | - when new data is written into the buffer it's written byte by byte at the head position and the head position is moved forward; | ||
+ | |||
+ | - when head reaches end of the buffer it rolls back to zero; | ||
+ | |||
+ | - data counter is incremented each time a data byte is added to the buffer. | ||
+ | |||
+ | To read data from buffer: | ||
+ | |||
+ | - check if data counter isn't zero, if it's not zero there is some data in the buffer; | ||
+ | |||
+ | - read data starting from tail position and increment tail accordingly; | ||
+ | |||
+ | - when head reaches end of the buffer it rolls back to zero; | ||
+ | |||
+ | - data counter is decremented each time a data byte is read from the buffer. | ||
+ | |||
+ | SCircularBuffer buf; | ||
+ | |||
+ | void InitBuffer( void ) | ||
+ | { | ||
+ | buf.count = 0; | ||
+ | buf.head = 0; | ||
+ | buf.tail = 0; | ||
+ | } | ||
+ | |||
+ | void WriteData( unsigned char dat ) | ||
+ | { | ||
+ | buf.count++; | ||
+ | buf.data[buf.head] = dat; | ||
+ | if( ++buf.head == sizeof(buf.data) ) | ||
+ | buf.head = 0; | ||
+ | } | ||
+ | |||
+ | unsigned char ReadData( void ) | ||
+ | { | ||
+ | unsigned char ret = buf.data[buf.tail]; | ||
+ | if( ++buf.tail == sizeof(buf.data) ) | ||
+ | buf.tail = 0; | ||
+ | buf.count--; | ||
+ | } | ||
+ | |||
+ | |||
+ | To use these functions one needs to initialise the buffer first. This is done inside the InitBuffer call. After this buffer is ready to be used. There is one caveat though. ReadData doesn't check if there is any data in the buffer and such check must be done every time this function is called. | ||
+ | //Init the buffer | ||
+ | InitBuffer(); | ||
+ | //Write one byte into the buffer | ||
+ | WriteData( 0x24 ); | ||
+ | //Read one byte from buffer | ||
+ | if( buf.count ) | ||
+ | { | ||
+ | unsigned char dat = ReadData(); | ||
+ | ... //do something with the data | ||
+ | } | ||
+ | |||
+ | Such functionality fits very well into the C++ techniquest and with the help of BoostC++ can be implemented like a self contained class: | ||
+ | |||
+ | class CCircularBuffer | ||
+ | { | ||
+ | public: | ||
+ | CCircularBuffer() | ||
+ | { | ||
+ | } | ||
+ | |||
+ | void WriteData( unsigned char dat ) | ||
+ | { | ||
+ | count++; | ||
+ | data[head] = dat; | ||
+ | if( ++head == sizeof(data) ) | ||
+ | head = 0; | ||
+ | } | ||
+ | |||
+ | unsigned char ReadData( unsigned char &dat ) | ||
+ | { | ||
+ | if( !count ) | ||
+ | return false; | ||
+ | dat = data[tail]; | ||
+ | if( ++tail == sizeof(data) ) | ||
+ | tail = 0; | ||
+ | count--; | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | protected: | ||
+ | unsigned char data[BUF_LEN]; //buffer to store data | ||
+ | unsigned char head; //head pointer (not really a pointer but index where data starts) | ||
+ | unsigned char tail; //tail | ||
+ | unsigned char count; //number of data bytes currently in the buffer | ||
+ | }; | ||
+ | |||
+ | You may notice that data availability check as well as buffer initialisation have already been included into the circular buffer class so the code that uses it becomes a bit simplier: | ||
+ | |||
+ | //Create a circular buffer | ||
+ | CCircularBuffer buf; | ||
+ | //Write one byte into the buffer | ||
+ | buf.WriteData( 0x24 ); | ||
+ | //Read one byte from buffer | ||
+ | unsigned char dat; | ||
+ | if( buf.ReadData( dat ) ) | ||
+ | { | ||
+ | ... //do something with the data | ||
+ | } | ||
+ | |||
+ | Why bother? A circular buffer is a good solution when it's not known when data can be read from or be written into a buffer. A good example is an interrupt driven serial port. Interrupt code reads UART and stores read data in a circular buffer and main code reads data from this buffer and processes it. Take a look at the Novo.UART sample project from SourceBoost installation. It shows how to use a circular buffer and extends design described here by making it thread safe. | ||
== [[read before write]] == | == [[read before write]] == | ||
Line 36: | Line 195: | ||
+ | Suppose you are lighting an LED on PORTB.3. If you use the following defines: | ||
+ | |||
+ | #define LED_PORT PORTB | ||
+ | #define LED_BIT 3 | ||
+ | |||
+ | then | ||
+ | |||
+ | set_bit( LED_PORT, LED_BIT ); | ||
+ | |||
+ | can be used instead of | ||
+ | |||
+ | set_bit( PORTB, 3 ); | ||
+ | |||
+ | There are a couple of advantages to this: | ||
+ | |||
+ | * The code is easier to read and write. | ||
+ | * Reconfiguring the hardware can be done simply by just changing the #defines. | ||
+ | * Reading the #defines ( normally all at the top of the program ) often gives a good overview of the hardware assignment. | ||
== Using #define to Support Multiple CPU Targets == | == Using #define to Support Multiple CPU Targets == | ||
+ | The compiler ( actually I think the preprocessor ) always defines a symbol that contains the name of the target processor, for example _PIC16F877A_H_. This means you can surround code with the following preprocessor commands: | ||
+ | |||
+ | #ifdef _PIC16F877A_H_ | ||
+ | TRISA = 0b00011111; | ||
+ | #endif | ||
+ | |||
+ | it will only compile if the target is a 16f877A. This way one file can contain code for a bunch of different processors. | ||
+ | |||
+ | |||
+ | If you want to include more than one processor you can use code like: | ||
+ | |||
+ | |||
+ | #if ( defined ( _PIC16F877A_H_ ) || defined ( _PIC16F877_H_ ) ) | ||
+ | TRISA = 0b00011111; | ||
+ | #endif | ||
== Using #define for Magic Numbers == | == Using #define for Magic Numbers == | ||
+ | |||
+ | A magic number is a number, that for some reason, has a special meaning. For example the maximum number for an 8 bit number is 255, but a reader of the program might not recognize this significance. If you: | ||
+ | |||
+ | #define MAX_8BIT = 255 | ||
+ | |||
+ | then a test like | ||
+ | |||
+ | if ( a == MAX_8BIT ) .... | ||
+ | |||
+ | might be more meaningful. ( note this is not a great example, best I could come up with right now ) You could consider that port addresses are actually numbers ( and different for different processors ) and that symbols like PORTB are actually magic numbers defined differently for different processors. | ||
== Counting Down with Unsigned Numbers == | == Counting Down with Unsigned Numbers == |
Latest revision as of 12:06, 18 December 2011
This page is more of an idea than a finished product, we need help to fill it out.
These are short sections of code for particular tasks. The first goal is to get code that works. Later goals include:
- Easily modified code..
- Robust: failure resistant even with bad input.
- Fast
- Plays well when imbeded with other routines.
- Minimum resource use.
Because of the different goals we may have more than one sample in each category.
Contents
- 1 The perfect second
- 2 Using I2C Memory
- 3 Output to A Shift Register
- 4 Software Circular Buffer
- 5 read before write
- 6 Good Main Program Header
- 7 Using #define to Configure Hardware
- 8 Using #define to Support Multiple CPU Targets
- 9 Using #define for Magic Numbers
- 10 Counting Down with Unsigned Numbers
- 11 Access Bytes Inside Another Type
The perfect second[edit]
Timing routines, showing how seconds can be counted. Want high accuracy even with odd clock rates.
This dudes routines will *absolutely* blow your sox off. Really. http://www.romanblack.com/one_sec.htm
Using I2C Memory[edit]
Output to A Shift Register[edit]
Often used for port expansion.
This seems to work for 8 bits to a 74HC/HCT164
void shiftOutByte( unsigned char aData ) { unsigned char ix; for( ix = 0; ix < 7; ix++ ) { if ( aData.1 ) { set_bit( SRPort, SRDataBit ); } else { clear_bit( SRPort, SRDataBit ); } clear_bit( SRPort, SRClockBit ); // edge triggered low to high aData >>= 1; // put here as delay set_bit( SRPort, SRClockBit ); } return;
}
Software Circular Buffer[edit]
A place to store and retrieve data, say bytes. First in First out. Methods to add and remove data.
This technique requires 4 components:
buffer to store data
pointer to the start of data (head)
pointer to the end of data (tail)
size of data currently in the buffer
Typically these 4 are stored in a container like struct or class and all together define a circular buffer:
#define BUF_LEN 64 struct SCircularBuffer { unsigned char data[BUF_LEN]; //buffer to store data unsigned char head; //head pointer (not really a pointer but index where data starts) unsigned char tail; //tail unsigned char count; //number of data bytes currently in the buffer };
The rules to use such buffer are simple. When some data needs to be written into the buffer:
- when new data is written into the buffer it's written byte by byte at the head position and the head position is moved forward;
- when head reaches end of the buffer it rolls back to zero;
- data counter is incremented each time a data byte is added to the buffer.
To read data from buffer:
- check if data counter isn't zero, if it's not zero there is some data in the buffer;
- read data starting from tail position and increment tail accordingly;
- when head reaches end of the buffer it rolls back to zero;
- data counter is decremented each time a data byte is read from the buffer.
SCircularBuffer buf; void InitBuffer( void ) { buf.count = 0; buf.head = 0; buf.tail = 0; } void WriteData( unsigned char dat ) { buf.count++; buf.data[buf.head] = dat; if( ++buf.head == sizeof(buf.data) ) buf.head = 0; } unsigned char ReadData( void ) { unsigned char ret = buf.data[buf.tail]; if( ++buf.tail == sizeof(buf.data) ) buf.tail = 0; buf.count--; }
To use these functions one needs to initialise the buffer first. This is done inside the InitBuffer call. After this buffer is ready to be used. There is one caveat though. ReadData doesn't check if there is any data in the buffer and such check must be done every time this function is called.
//Init the buffer InitBuffer(); //Write one byte into the buffer WriteData( 0x24 ); //Read one byte from buffer if( buf.count ) { unsigned char dat = ReadData(); ... //do something with the data }
Such functionality fits very well into the C++ techniquest and with the help of BoostC++ can be implemented like a self contained class:
class CCircularBuffer { public: CCircularBuffer() { } void WriteData( unsigned char dat ) { count++; data[head] = dat; if( ++head == sizeof(data) ) head = 0; } unsigned char ReadData( unsigned char &dat ) { if( !count ) return false; dat = data[tail]; if( ++tail == sizeof(data) ) tail = 0; count--; return true; } protected: unsigned char data[BUF_LEN]; //buffer to store data unsigned char head; //head pointer (not really a pointer but index where data starts) unsigned char tail; //tail unsigned char count; //number of data bytes currently in the buffer };
You may notice that data availability check as well as buffer initialisation have already been included into the circular buffer class so the code that uses it becomes a bit simplier:
//Create a circular buffer CCircularBuffer buf; //Write one byte into the buffer buf.WriteData( 0x24 ); //Read one byte from buffer unsigned char dat; if( buf.ReadData( dat ) ) { ... //do something with the data }
Why bother? A circular buffer is a good solution when it's not known when data can be read from or be written into a buffer. A good example is an interrupt driven serial port. Interrupt code reads UART and stores read data in a circular buffer and main code reads data from this buffer and processes it. Take a look at the Novo.UART sample project from SourceBoost installation. It shows how to use a circular buffer and extends design described here by making it thread safe.
read before write[edit]
Solving the issue of PIC16's pin state not being saved internally. Code still needed.
Good Main Program Header[edit]
A C Program Header Example A good header is a help to anyone who wants to use your code.
Using #define to Configure Hardware[edit]
Suppose you are lighting an LED on PORTB.3. If you use the following defines:
#define LED_PORT PORTB #define LED_BIT 3
then
set_bit( LED_PORT, LED_BIT );
can be used instead of
set_bit( PORTB, 3 );
There are a couple of advantages to this:
- The code is easier to read and write.
- Reconfiguring the hardware can be done simply by just changing the #defines.
- Reading the #defines ( normally all at the top of the program ) often gives a good overview of the hardware assignment.
Using #define to Support Multiple CPU Targets[edit]
The compiler ( actually I think the preprocessor ) always defines a symbol that contains the name of the target processor, for example _PIC16F877A_H_. This means you can surround code with the following preprocessor commands:
#ifdef _PIC16F877A_H_ TRISA = 0b00011111; #endif
it will only compile if the target is a 16f877A. This way one file can contain code for a bunch of different processors.
If you want to include more than one processor you can use code like:
#if ( defined ( _PIC16F877A_H_ ) || defined ( _PIC16F877_H_ ) ) TRISA = 0b00011111; #endif
Using #define for Magic Numbers[edit]
A magic number is a number, that for some reason, has a special meaning. For example the maximum number for an 8 bit number is 255, but a reader of the program might not recognize this significance. If you:
#define MAX_8BIT = 255
then a test like
if ( a == MAX_8BIT ) ....
might be more meaningful. ( note this is not a great example, best I could come up with right now ) You could consider that port addresses are actually numbers ( and different for different processors ) and that symbols like PORTB are actually magic numbers defined differently for different processors.
Counting Down with Unsigned Numbers[edit]
You are counting down and want to know when an unsigned number goes negative ( never ). You could declare it signed, slowing everything down. Instead check against FF. This assumes you do not use FF on the positive side. It also assumes you are counting down by one, else you could skip over FF.
if ( 0xFF == ix ) ..
Access Bytes Inside Another Type[edit]
Example use a union:
typedef union { unsigned int ui; unsigned char b[2]; } noportable_int; //main program starts here. void main() { noportable_int n; n.ui = 0x1234; n.b[0] = tmr1l; n.b[1] = tmr1h; }
idea from: [1]