|
|
View previous topic :: View next topic |
Author |
Message |
cfernandez
Joined: 18 Oct 2003 Posts: 145
|
software uart routine |
Posted: Sat May 05, 2007 12:35 pm |
|
|
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
|
|
Posted: Sat May 05, 2007 2:36 pm |
|
|
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
|
|
Posted: Sun May 06, 2007 8:10 am |
|
|
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
|
|
Posted: Sun May 06, 2007 11:07 pm |
|
|
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
|
|
Posted: Mon May 07, 2007 3:11 am |
|
|
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
|
|
Posted: Mon May 07, 2007 4:30 am |
|
|
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 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
|
|
Posted: Tue May 08, 2007 4:44 pm |
|
|
Thank you, I will be test this code and send then my result.
Best Regards, |
|
|
|
|
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
|