| Ttelmah 
 
 
 Joined: 11 Mar 2010
 Posts: 19966
 
 
 
			    
 
 | 
			
				| 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
 |  |