CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

software uart routine

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
cfernandez



Joined: 18 Oct 2003
Posts: 145

View user's profile Send private message

software uart routine
PostPosted: Sat May 05, 2007 12:35 pm     Reply with quote

hi,

I need to use software routine, but I not want use #SERIAL, Anybody know a good software routine in C.

Thank you very much!
Ttelmah
Guest







PostPosted: Sat May 05, 2007 2:36 pm     Reply with quote

In a sense, this is a bit like somebody saying "I wan't to drive along a road, but not use a wheel"...
#serial, is (within certain limitations), about as good as you are going to get, and _certainly_, as good as you are going to get 'in C'. The only reasons to not use it, would be for handling special cases where (for example), interrupt driven events interfere with the timings, and realistically, except for very low baud rates, handling it using a timer, would require operation in assembler, rather than 'C'. Otherwise, you might as well just use #serial, which is assembler based, and gives more accurate timings, than any other 'C' solution...

Best Wishes
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Sun May 06, 2007 8:10 am     Reply with quote

It would help if you could tell us WHY you don't want to use #serial. Is there some problem with it you would like to avoid?
_________________
The search for better is endless. Instead simply find very good and get the job done.
cfernandez



Joined: 18 Oct 2003
Posts: 145

View user's profile Send private message

PostPosted: Sun May 06, 2007 11:07 pm     Reply with quote

The problem is the following:

We have use 4 software serial device, and each one can have different speed. Using #serial I need to use 4*n, where n is the different speed. Now we use 2 speed for each one, then we need 4*2 = 8 different configuration of #serial and 8 case for getch, putch, etc... This is a lot of code for make the same.

I want to reduce this code using serial bit-banging function, with this I need ONLY one function and this only have to know the pin TX & RX and bit delay for the different speed, and this I solve using a array with TX,RX & Delay information.

You are understand the problem, now?

Thank you very much!
Ttelmah
Guest







PostPosted: Mon May 07, 2007 3:11 am     Reply with quote

For low data rates, this is not too hard. If you do a search on the forum here, functions have been posted to perform a _variable_ driven delay_us. Then all you have to do, is look for the falling edge on the data line (marks the edge of the start bit), and wait for 1.5 bit times, then sample the first bit. Then repeat seven more times, delaying by one bit time between each.
Similarly, there have been variable based input and output bit functions posted.
The problem though, is that the error term (introduced by the handling of the bit, shifting it into the register (or out for transmission), will become unacceptably large at higher data rates. It'll be very hard indeed to tweak the required values correctly, for each possible time. The code is likley to be a _lot_ larger than than the standard functions. If you were handling only perhaps 2 or 3 channels, I'd guess that there would probably be no savings at all!. However on your four channel example, it is probable that savings could be achieved.
Now, the code I am posting her, _won't_ work. I have just 'guessed' at values for the tweak timings needed on the loops, and have not tested any of the functions involved. However they should represent about 80%of what is needed to make this work....
Code:

#include "C:\Program Files\PICC\am4\testsoftser.h"
int16 bit_time;
int16 half_bit_time;
#define RX_BIT_TWEAK (30)
//total 'guess' for the tweak needed between bits on RX
#define TX_BIT_TWEAK (30)
//similar for TX. Will need adjustment using the simulator, or instruction
//counting to get the values right.
int16 times[] = {1041,2083,4167,8333};
//usec delays needed for 960,4800,2400, & 1200bps
#define B9600 (0)
#define B4800 (1)
#define B2400 (2)
#define B1200 (3)

int8 rate=0;  //global value to set the baud rate
int16 TXPIN;
int16 RXPIN;
#define set_baudrate(x) { rate=x;\
   bit_time=times[rate];\
   half_bit_time=bit_time/2; }
#define set_TXPIN(x) TXPIN=x
#define set_RXPIN(x) RXPIN=x
#define soft_kbhit() (do_pin_io(RXPIN,2)==0)

//PCM_programmers variable based PIN I/O routine for an 18F. 16F version
//will need to be substituted if working on these chips.
#define SET_LOW   0
#define SET_HIGH  1
#define READ_PIN  2

int8 do_pin_io(int16 ccs_pin, int8 action)
{
int16 io_port;
int16 io_tris;

int8 bitmask;
int8 retval;
int8 const bitmask_table[] =
   {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};

retval = 0;
io_port = ccs_pin >> 3;   // Get the i/o port address     
bitmask = bitmask_table[ccs_pin & 7];   // get mask
io_tris = io_port + 0x12;  // Get TRIS register address

if(action < 2)  // Is this an output action ?
  {
   *(io_tris) &= ~bitmask; // Set TRIS = output

   if(action)
     {
      *io_port |= bitmask;    // Set pin = 1
     }
   else
     {
      *io_port &= ~bitmask;   // Set pin = 0
     }
  }
else  // If not, we will read the pin  (action = 2)
  {
   *io_tris |= bitmask;     // Set TRIS = input
   retval = !!(*io_port & bitmask);  // Read pin (ret. 0 or 1)
  }

return(retval);   // This only has meaning if action = 2
}

void delay16(int16 n) { // permit a 16bit num for delayus time
   int8 i,j;
   n-=20; //tweak for the duration of this maths, and the call/return
   //will need adjustment for accuracy...
   j = make8(n, 1); // number of 256uSec delays required
   i = make8(n,0); //extra time

   while(j--) delay_us(250); //shortened to adjust for loop time. Will
   //depend on clock rate of chip.
   delay_us(i);
}

int8 read_rx(void) {
   int16 per_bit_time;
   int8 input_byte=0;
   int8 ctr=8; //8bit data
   per_bit_time=bit_time-RX_BIT_TWEAK; //tweak loop time to allow
   //for overheads
   
   while (!soft_kbhit()) ;
   //now serial line has fallen
   delay16(bit_time);
   delay16(half_bit_time);
   while (ctr--) {
      input_byte<<=1;
      input_byte=input_byte|do_pin_io(RXPIN,2);
      delay16(per_bit_time);
   }
   return (input_byte);
}

void send_tx(int8 val_to_send) {
   int16 per_bit_time;
   int8 ctr=8; //again 8bit data
   per_bit_time=bit_time-TX_BIT_TWEAK;
   
   do_pin_io(TXPIN, 0); //start bit
   delay16(per_bit_time);
   while (ctr--) {
      do_pin_io(TXPIN, ((val_to_send & 0x80)==0x80));
      val_to_send<<=1;
      delay16(per_bit_time);
   }
   do_pin_io(TXPIN,1); //stop bit
   delay16(per_bit_time);
}

void main()
{
   int8 val;
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF|ADC_TAD_MUL_0);
   setup_wdt(WDT_OFF);
   setup_timer_0(RTCC_INTERNAL);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
   setup_oscillator(False);
   //How the setup functions work
   set_TXPIN(PIN_B0);
   set_RXPIN(PIN_B1);
   set_baudrate(B9600);
   //Now, if timings are right - untested....
   send_tx("Test string");
   //Now switch to a different rate
   set_baudrate(B1200);
   while (!soft_kbhit()) ; //wait for serial data
   val=read_rx();
   //Now another output pin
   set_TX_PIN(PIN_B2);
   send_TX("1200bps on B2");
   
   while(TRUE) ;
}

Now this may give you a starting point for what you want...

Best Wishes
libor



Joined: 14 Dec 2004
Posts: 288
Location: Hungary

View user's profile Send private message

PostPosted: Mon May 07, 2007 4:30 am     Reply with quote

If you have a spare timer and edge (change) detecting pins:

In an application I needed a second, software UART, I had to make it so, that the PIC does not wait for the bits in a loop doing nothing else, but rather works in the 'background' more closely emulating a hw usart.
I used INT_EXTx (B0, B1, B2 pins can be used in the new PICs) for detecting the start bit's first edge (high-to-low transition) and INT_TIMER0 8 times/byte after it to sample the each bit in the middle. This solution spends 10-15% time in the interrupts with a PIC running at 20Mhz receiving 19200bps. (the sending I have done as usual, though I think it is also possible to make it in timer-based interrupts.)
To avoid timing problems this works in half-duplex mode only of course, no simultaneous sending and receive.

some code parts as a clue:
Code:
void main()   {
...
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_4 | RTCC_8_BIT);     //1.25Mhz = 0.8usec  with  20 Mhz crystal
ext_int_edge(1, H_TO_L);
clear_interrupt(INT_EXT1);
enable_interrupts(INT_EXT1);    //enable interrupt for the first byte's startbit edge
enable_interrupts(GLOBAL);
...

...
#INT_EXT1
void int1_isr() {
   set_timer0(~60);  //fire 1.5 bit later to sample the first bit in the middle
   disable_interrupts(INT_EXT1);    //no more edge detection during bit sampling
   bitcounter=0;  //number of received bits to know when to finish
   recbyte = 0;   //init receive byte buffer to zero
   clear_interrupt(INT_TIMER0);
   enable_interrupts(INT_TIMER0);  //switch to timer mode for sampling databits
}
...
...
#INT_TIMER0
void timer0_isr() {
   set_timer0(~53);   //fire 1 bit later
   if (input(PIN_RX)) bit_set(recbyte,7); //sample and store the bit
   bitcounter++;
   if (bit_test(bitcounter,3)) {  //same as ==8, but quicker,  8th bit, one byte arrived
      byte_arrived = TRUE;     //set flag
      disable_interrupts(INT_TIMER0);   //no more sampling
      clear_interrupt(INT_EXT1);
      enable_interrupts(INT_EXT1);  //switch to startbit edge detection to receive next byte
   } else {    //more bits arriving
      recbyte>>=1;  //rotate in received bits
   }
}


The timer values (1,5 bit timing, and 1 bit timing) you will have to fine tune. being lazy Smile I have done it with a logic analyzer to spare counting clock cycles, interrupt latency, etc.
This code has one byte buffer, but it can be easily modified to use a larger buffer.
cfernandez



Joined: 18 Oct 2003
Posts: 145

View user's profile Send private message

PostPosted: Tue May 08, 2007 4:44 pm     Reply with quote

Thank you, I will be test this code and send then my result.

Best Regards,
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group