View previous topic :: View next topic |
Author |
Message |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
RS232 Interrupt operation mid-range PICS w/o UART hardware |
Posted: Fri May 15, 2009 4:11 pm |
|
|
The need to poll on non-hardware low end pics makes RS232 can make receiving a bit dicey. Interrupt service for received characters would seem to add a lot of reliability to the process.
has anybody seen/created an RS-232 Receive driver along this line of thought for low end midrange pics w/o UART hardware:
the RX data input pin is connected to a B port edge edge sensitive interrupt pin.
Once the interrupt is taken on leading ( ok falling ) edge of a new character - then the ISR disables the interrupt - and receives the entire character && buffers it circularly like the EX-ISR driver for hardware enabled pics does .
then when the stop bit is IN - the int state is cleared and ints are re-enabled for the next character.
i assume i could code it in short order - if i had access to
the source code that is called with a #USE RS232 - on such devices
but no can find.
any comments ?
or is this a bad idea for some reason i am not getting yet? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri May 15, 2009 4:27 pm |
|
|
1. Set the 'rcv' pin to PIN_B0 (Ext. Int pin) in the #use rs232() statement.
Also connect the incoming signal from the PC to Pin B0 (via a Max232
chip).
2. Create an #int_ext isr and do your getc() in the isr.
3. In main(), configure the External interrupt edge to be H_TO_L,
to catch the leading edge of the start bit
4. Then enable External interrupts. (Clear the external interrupt first). |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri May 15, 2009 6:48 pm |
|
|
i will post the result after doing the hardware over the W.E. |
|
|
Ttelmah Guest
|
|
Posted: Sat May 16, 2009 2:25 am |
|
|
As a further comment, depending on your bps rate, versus CPU clock (if CPU instruction clock is less than perhaps bps * 60), add the option 'SAMPLE_EARLY', to the software UART setup.
The problem is that it takes typically around about 30 instruction times to get 'into' the interrupt handler code, and if the bps rate is relatively high, compared to the instruction clock, this then results in late sampling of the data.
Examples of whether to select this option:
Master clock 4MHz. 9600bps. Master instruction rate = 1MHz, 104 instructions/bit. Should be OK with the standard setup.
Master clock 4Mhz. 19200bps. 52 instructions per bit. This will _need_ to have the sample early option, to have much chance of working right.
Best Wishes |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
got it going now |
Posted: Sat May 16, 2009 2:19 pm |
|
|
the purpose was to update a 16c715 multiple temp reader ( designed by somebody else) - with intermittant failure to reply, using the simple software code for serial. i did as PCM and TTelmah suggested - the packet is always 12 chars - i set up a 16 char buffer and added a timer based receive stream check feature to TRY to avoid receive collisions - since the commands that come in are a burst - i look for a decent gap in reception before attempting the reply. comments to make it better?
Code: |
// using 4.9152mhz clock for zero err baud division
// max safe baud rate 9600
// with 9.83 mhz 19200 MY CTL PACKET IS ONLY 12 CHARS LONG
// SO USING BUFFER OF 16 in this example
// timer0 used to be sure incoming packets are complete
// before attempting reply - to avoid char distortion and loss
// due to interrupt - Thanks to PCM and TTelmah !!
// 16c715.c ultimate target using the same rock
#include <16f818.h>
#include <stdlib.h>
#Fuses EC_IO,NOWDT,PUT, MCLR,BROWNOUT,NOLVP,CPD,NOCPD,WRT,NODEBUG,CCPB3,NOPROTECT
#use delay( clock=4915200)
#use rs232(baud=9600 , xmit=PIN_B1, rcv=PIN_B0, sample_early)
#use fast_io(A)
#use fast_io(B)
#bit T0IF = 0x0B.2 // flag in INTCON reg
//
#define BUFFER_SIZE 16
BYTE RISR_out; // global background newchar from ISR BACKGND buffer
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
#define bkbhit (next_in!=next_out)
void bgetc(void) {
RISR_out=buffer[next_out]; // using GLObal return char variable
next_out=(next_out+1) % BUFFER_SIZE;
}
void SIsr_INIT(void){
clear_interrupt( int_ext );
EXT_INT_EDGE(H_TO_L);
enable_interrupts(int_ext);
enable_interrupts(global);
}
#int_ext
void serial_isr() {
int t;
buffer[next_in]=getc(); t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out) next_in=t; // Buffer full !!
}
//***********************************
void InitHDW ( void ) {
//***********************************
setup_oscillator ( 4915200 ); // same as for rock 8mhz
output_a (0); output_b (0); set_tris_a (0xFF); // - all INPUT
setup_timer_0( RTCC_DIV_64); // set for rollovers ~12 ms holdoff later
// USE RTCC_DIV_128 for 9.93 mhz clock
set_tris_b (0b00000001); output_b (0);
}
//===================================
void main()
{
char v; char t; unsigned int8 a; unsigned int8 b;
InitHDW();
SIsr_INIT();
printf ("*Hello*\r"); // just check output
delay_ms(12000); // delay to let me send a packet in
// now endlessly
while (1) { // now get AND send at same time
a=next_in; // get mark of buffer position
T0IF = 0; b=3; // clear t0if and do 3x12 MS +/-
while(b){ // TIMER0 just rolls over endlessly
if ( T0IF ) { // not using INTS - just watching timer
--b; T0IF=0;
if (!b && a==next_in ){ // unload IF no NU chars R caught
// your (safer) xmit routine here
while (bkbhit){ bgetc(); putc(RISR_out);} //simple test
} // Implied ELSE - keep waiting
} // END : IF t0if
} // END : WHILE B
} // END : while 1
}
|
Last edited by asmboy on Sat May 16, 2009 3:02 pm; edited 1 time in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat May 16, 2009 2:56 pm |
|
|
Quote: | #int_ext
void serial_isr() {
int t;
buffer[next_in]=getc(); t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out) next_in=t; // Buffer full !!
clear_interrupt( int_ext ); // do this LAST
} |
The compiler already puts in code to clear the interrupt flag at the
end of the routine. You don't need that line. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
duh - sorry |
Posted: Sat May 16, 2009 2:59 pm |
|
|
i shoulda looked at the .LST file -
thanks for the pointer - VERY much appreciated
hopefully somebody else who could benefit will stumble across this thread. |
|
|
krasi
Joined: 17 Jun 2009 Posts: 9 Location: Bulgaria
|
'...to avoid receive collisions..." |
Posted: Wed Jun 17, 2009 5:48 am |
|
|
First of all I want to say "hello" to everybody in this useful forum. This is my first post, so I want to excuse me if I'm wrong with something.
I want to say: "Thank you" to everybody who helps the others in getting more comfortable in PIC embedded software and especially to PCM Programmer and Ttelmah. You guys are the best! I learned a lot from your posts. Keep going this way.
Hi asmboy. Thank you for your post. I was stuck, and your code helps me a lot to get me out of this situation. With little modifications for PIC18F2580 with Timer2, I use it for my project. But I have a question about the timer function. You said that this is a "check feature to TRY to avoid receive collisions". Can you (or somebody else) explain me why? What is the purpose? Because I think it's not necessary. The "getc" function receives all bytes without problem (I already checked it.)
Best regards,
Krasi |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
about collisions |
Posted: Wed Jun 17, 2009 8:33 am |
|
|
the problem with collision is ONLY an issue on PICs that don't have actual serial UART hardware. ( so long as you are using said hardware)
On low end parts w/o UART ihardware- the interrupt to receive chars ,can happen during transmit - and will 'break' the outgoing characters.
if you are using a pic with hardware - ( and using the hardware) - there is no issue with transmit failure from the overlap of Xmit and RECV functions |
|
|
krasi
Joined: 17 Jun 2009 Posts: 9 Location: Bulgaria
|
about collisions |
Posted: Wed Jun 17, 2009 9:13 am |
|
|
Yes, it can be happen as you say, but what if we use "DISABLE_INTS" in #use RS232? As I know it is designed just for this case:
Quote: | Will cause interrupts to be disabled when the routines get or put a character. This prevents character distortion for software implemented I/O and prevents interaction between I/O in interrupt handlers and the main program when using the UART. |
Best regards,
Krasi |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Wed Jun 17, 2009 9:33 am |
|
|
you are then making a choice - remember - it is the SOFTWARE based serial code. if you disable receive ints - when transmitting - no problem - you will not hurt your transmit stream
BUT
of course you WILL miss ALL receive serial characters that would have come in during the XMIT cycle. and worse - if when you re-enable ints -
should you be mid character on receive - you will get some trash in your receive stream form that partial character.
ALWAYS USE THE HARDWARE UART WHEN SOLID 2 WAY COMM IS REQUIRED.
if you are trying to add an extra serial channel to a PIC with ONE hardware uart in use already - the only SAFE thing to do is use the software uart for RECEIVE only. Overlap on the software channel will get you eventually if you don't
a typical case would be - say- RECEIVING data passively from another gadget that does not need supervision - and using your hardware based serial channel to do 2 way com with a host |
|
|
krasi
Joined: 17 Jun 2009 Posts: 9 Location: Bulgaria
|
thanks |
Posted: Thu Jun 18, 2009 12:45 am |
|
|
Thank you for your good explanation. It is more clear now. I was forgot for the simultaneously transmitting, that's why I couldn't saw the difference.
I use in my application only half duplex, and that's why I was thinking that there is no problem to use "DISABLE_INTS". But you are right for the full duplex - there is no other choice than the hardware channel if you want solid 2 way.
Thank you again,
Krasi |
|
|
|