Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
Timer interrupt based serial receive routine |
Posted: Thu Oct 31, 2013 2:56 am |
|
|
This is based on a post I made a long time ago in the forums, showing a way of having async serial receivable on any PIC pin using a time interrupt.
Code: |
//Timer interrupt based serial rx - timer values shown at 9600bps on a 20MHz PIC4520
//demonstrates a way of retrieving characters from any pin, without having to sit waiting in the main code
//and buffers these for when they are needed....
//9600bps, is about the fastest 'comfortable' with the approach and clock speeds shown.
#include <18F4520.h>
#use delay(crystal=20MHz)
//Pic configuration here for whatever chip you use.
#define RX_INPUT_LINE PIN_A0 //choose the pin you want
#define BUFF_SIZ 16 //definitions for serial buffer
int8 rx_buffer[BUFF_SIZ];
int8 rx_in=0;
int8 rx_out=0;
#define rx_has_data() (rx_in!=rx_out)
#define increment(x) if (++x>=BUFF_SIZ) x=0
char rx_getc(void) //gets a character from the RX buffer if available - return zero if not
{
char temp_chr=0;
if (rx_has_data())
{
temp_chr=rx_buffer[rx_out];
increment(rx_out);
}
return temp_chr;
}
//Remember like all things _get out of the ISR quickly_ This code does this, and any other
//interrupts in use, must also be written this way, if they are not going to interfere.
#int_timer2 //For whatever timer you are using - easiest for timer2 to give accurate times
void timer_int(void) {
static int8 state=0;
static int8 incoming=0;
static int8 bitmask=1;
//any other timer code wanted here
switch (state) {
case 0:
if (input(RX_INPUT_LINE)==0) state=1; //start bit
break;
case 1:
case 2:
if (input(RX_INPUT_LINE)==0) state++; //test again
else state=0; //and error restart
break;
case 3: //since bit time is four interrupts, for three out of four
case 4: //loops, simply advance one state and exit
case 5: //This keeps work in the interrupt small
case 7:
case 8:
case 9:
case 11:
case 12:
case 13:
case 15:
case 16:
case 17:
case 19:
case 20:
case 21:
case 23:
case 24:
case 25:
case 27:
case 28:
case 29:
case 31:
case 32:
case 33:
case 35: //These last three, are delaying to the middle of the stop
case 36: //bit
case 37:
state++; //all that is done in three out of four interrupts....
break;
//Now sample at four interrupt intervals from the 'double checked'
//start bit edge - 8 bits
case 6:
case 10:
case 14:
case 18:
case 22:
case 26:
case 30:
case 34:
if (input(RX_INPUT_LINE)==1) incoming|=bitmask; //set the received bit
bitmask*=2; //next bit
state++;
break;
case 38:
//Here should be in the stop bit - save received byte
rx_buffer[rx_in]=incoming;
increment(rx_in);
if (rx_in==rx_out)
increment(rx_out); //throw oldest character if buffer overflows
state=incoming=0;
bitmask=1;
break;
}
}
//Now this is based on using a timer interrupt at 4* the baud rate. So for 9600bps, 38400ips
//main stuff
void main(void)
{
int8 val_rx;
//remember you may need to turn things off on the pin you want to use. analogs?.
setup_comparator(NC_NC_NC_NC);
setup_adc_ports(NO_ANALOGS);
//needs to suit the pin you are using.....
//start with the clock rate - say 20MHz. Instruction rate is 5Meg. For 9600bps require
//38400 samples per second, and divider of : (5000000/38400) = 130.2 - nearest integer = 130
//If this is above 256, use a 'divisor' other than one. Otherwise program as:
setup_timer_2(T2_DIV_BY_1,129,1); //remember one less than the divider required.
//For 4800bps (5000000/19200) = 260.4, so T2_DIV_BY_4,64,1 etc...
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
do
{
if (rx_has_data())
{
//you will get here when characters have been received
val_rx=rx_getc(); //retrieves the received character.
//Do what you want with it......
}
}
while (TRUE);
}
|
Makes something much closer to a hardware receive only UART with buffering.
Best Wishes |
|