|
|
View previous topic :: View next topic |
Author |
Message |
mkr
Joined: 08 Aug 2006 Posts: 49
|
Simple RS232 interrupt routine |
Posted: Mon Aug 14, 2006 9:59 am |
|
|
Good Morning All,
I am trying to dabble a simple RS232 routine. I am sending a byte 0x68 from PC, I read this byte and send back the same byte to the PC.
The problem is I am recieving the byte so the RDA interrupt is working, but some how I am finding it diffcult to transmit this byte back to PC. I have a simple COM port monitoring app. running on the PC.
Here is the code I am working on, could some body help me out, whats wrong. The direction pin is PIN_J4.
//this is the header file
#include <18F8722.h>
#device ICD=TRUE
#device adc=8
#use delay(clock=16000000)
#fuses NOWDT,WDT128,HS, NOPROTECT, IESO, BROWNOUT, BORV25, NOPUT, NOCPD, STVREN, NODEBUG, LVP, NOWRT, NOCPB, NOEBTRB, NOEBTR, NOWRTD, NOWRTC, NOWRTB, FCMEN, LPT1OSC, MCLR, XINST, MCU
#use rs232(baud=19200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream = COM1)
char c=0;
char senddata=0;
int8 count=0;
void send(char c);
// end of header file
//start of interrupt and main
#int_RDA
RDA_isr()
{
c = fgetc(COM1);
output_high(PIN_J4);
}
#int_TBE
TBE_isr()
{
fputc(c, COM1);
}
void main()
{
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF|ADC_TAD_MUL_0);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_INTERNAL);
// setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
setup_timer_4(T4_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
// enable_interrupts(INT_TIMER1);
enable_interrupts(INT_RDA);
enable_interrupts(INT_TBE);
disable_interrupts(INT_TBE);
enable_interrupts(GLOBAL);
for(;;){
if(c==0x68){
send(c);
}
}
}
void send(char c){
senddata = c;
output_low(PIN_J4);
enable_interrupts(INT_TBE);
}
// program end _________________ Thanks
mkr |
|
|
Ttelmah Guest
|
|
Posted: Mon Aug 14, 2006 10:21 am |
|
|
You can't use TBE, the way you are trying to do it. The problem is that TBE, will continuously occur, when the transmit is 'empty'.
Start with TBE _disabled_ in the main code. Then with just the single byte, use:
Code: |
#int_RDA
RDA_isr() {
c = fgetc(COM1);
output_high(PIN_J4);
enable_interrupts(INT_TBE);
}
#int_TBE
TBE_isr() {
fputc(c, COM1);
disable_interrupts(INT_TBE);
}
|
Now as shown, there is no advantage, or reason to use TBE at all, since you can just putc the character, in the receive interrupt. Where 'TBE', will come into it's own, is when you have multiple characters to send, and you then only disable the interrupt, when the 'buffer' is empty, and re-enable it, when you have characters waiting to send, and the chip is already busy.
Best Wishes |
|
|
mkr
Joined: 08 Aug 2006 Posts: 49
|
i still cannot get it working |
Posted: Mon Aug 14, 2006 1:28 pm |
|
|
Thanks for the reply. I still cannot get it working. I am not using tx interrupt, and trying to rx byte and send the same byte back from the rx interupt routine. I dont know where i am going wrong.
What should I do with the direction pin. Should I make it high, when recieving byte and make the pin low when transmitting byte. Is this right...
here is the sample code...
#int_RDA
RDA_isr()
{
c = fgetc(COM1);
// output_high(PIN_J4);
enable_interrupts(INT_TBE);
// output_low(PIN_J4);
fputc(c,COM1);
disable_interrupts(INT_TBE);
}
/*
#int_TBE
TBE_isr()
{
fputc(senddata, COM1);
disable_interrupts(INT_TBE);
}
*/
Could somebody show an simple interrupt routine with recieving and transmitting data. _________________ Thanks
mkr |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Aug 14, 2006 1:39 pm |
|
|
There are no nested interrupts allowed. They are not supported.
That's why your code doesn't work. You're hoping that a TBE interrupt
will occur in the middle of processing the RDA isr. But it can't.
I need to modify the comment above. It's true for your code, as posted.
But for the 18F series you can do a high priority interrupt. See this
thread.
http://www.ccsinfo.com/forum/viewtopic.php?t=26085
But I don't think you need to do it in your test program. |
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Mon Aug 14, 2006 2:14 pm |
|
|
Do not use the TBE_isr. An interrupt, for the TBE_isr will happen when the Transmit buffer is Empty. So, if you're not transmitting, the buffer will be empty. Therefore, an interrupt will be created and the TBE_isr will be entered. Now, since you're not transmitting anything, again, it will create an interrupt and enter the ISR again. This will hog ALL of your CPU time and nothing will get done.
Simply enable the RDA interrupt and put your fgetc() inside it and then have a fputc() right after it. This will, in effect, echo whatever character the serial port receives.
Code: |
#int_RDA
RDA_isr()
{
c = fgetc(COM1);
fputc(c,COM1);
}
|
Ronald |
|
|
Ttelmah Guest
|
|
Posted: Mon Aug 14, 2006 2:34 pm |
|
|
The problem is that the 'point'of using interrupts, is to allow the communication to be asynchronous to the main program flow. As such serial interrupt routines, won't be designed to handle single characters, but will be buffered I/O routines, and will be more complex than you seem to be looking for. Your original code would work as shown here.
Code: |
#int_RDA
RDA_isr() {
c = fgetc(COM1);
output_high(PIN_J4);
}
#int_TBE
TBE_isr() {
fputc(senddata, COM1);
disable_interrupts(INT_TBE);
}
//With main having this after the initialisation:
enable_interrupts(int_rda);
enable_interrupts(global);
for(;;){
if(c=='h') send();
}
//Then change 'send' to:
void send(){
senddata=c;
output_low(PIN_J4);
enable_interrupts(INT_TBE);
}
|
This will send back the character 'h', if typed (I think it makes it much easier to understand what you are doing, if you use the text form of the character, rather than the hex version), but ignore everything else.
What happens is this:
When an incoming character is seen, 'int_rda' occurs.
This calls the interrupt handler, which retrieves the character.
In the 'main', if the character is 'h', the 'send' function is called.
Now, note that since 'c' is a global register, you should not use it seperately in 'send'. Declaring it like this results in a second 'local' copy of c being generated. It should work, but is wasteful, since you create another copy in the routine.
In C, enabling the interrupt, then results in the TBE routine immediately being called. Pointless, but should then send the character, and disable the interrupt.
Best Wishes |
|
|
mkr
Joined: 08 Aug 2006 Posts: 49
|
Thanks for the explaination. |
Posted: Mon Aug 14, 2006 6:14 pm |
|
|
I have two rs232 ports to be serviced. The first one using the 1st internal interrupt rda/tbe and the second using internal interrupt rda2/tbe2. The first interrupt has to be serviced as fast as possible then the second interrupt. Do I need to set priority for these interrupts to be serviced. If so how to do it.
I found the previous reply very usefull, but where could I find an article or write up of interrupt handling using CCS or for any micro controller application. I read many books but but nothing seems to help with an practical approach in implementing the interrupt handlers.
Your suggestion appreicated. _________________ Thanks
mkr |
|
|
Ttelmah Guest
|
|
Posted: Tue Aug 15, 2006 9:33 am |
|
|
First, interrupts are serviced when they occur, with the global handler, checking the interrupts in the order they are defined (or in the order set in the #priority statement, if one is present). On chips with dual UARTs (which will be 18F chips), it is possible to have one serial using the high priority interrupt, but this will probably bring more complexity than it possibly warrants (it depends on what other interrupts are being used).
So, unless the two UART's interrupt nearly simultaneously (relatively unlikely for asynchronous events), whichever interrupts first, will normally be serviced first. Remember that you always hve at least a whole character time to service the interrupt, so normally, unless the rates are very high, pioritisation becomes uneccessary.
Now you give no idea about the data rates involved, but I'll attach a modified set of 'generic'
interrupt driven set of RS232 handlers, which should give a start.
Code: |
#define TXSIZE (32)
#define RXSIZE (8)
char rx_buffer[2][RXSIZE],tx_buffer[2][TXSIZE];
int8 ip[2],op[2];
//Remember that the TX buffer should be large enough to hold any
//likely 'outgoing' message. The RX buffer only needs to be large enough
//to handle the period between a character arriving, and the main code
//dealing with the data.
//Buffer tests and handling
int ibtemp,btemp;
#define incr(x,size) x=((x+1)&(size-1))
#define isempty(buff,in,out,size) (in==out)
#define tobuff(buff,in,out,size,chr) {buff[in]=chr;\
incr(in,size);\
if (in==out) incr(out,size);}
//Note that this throws away the oldest character if the buffer overflows.
#define ifrombuff(buff,in,out,size) (ibtemp=buff[out],\
incr(out,size),\
ibtemp)
#define frombuff(buff,in,out,size) (btemp=buff[out],\
incr(out,size),\
btemp)
//Note that sizes must be a binary multiple (2,4,8,16 etc.), to work
//with 'incr' as given. For other sizes, the % operator will have to be
//used instead, but this is slower...
#define RX2 (char *)(&rx_buffer[1][0])
#define RX1 (char *)(&rx_buffer[0][0])
#define TX2 (char *)(&tx_buffer[1][0])
#define TX1 (char *)(&tx_buffer[0][0])
//Byte definitions for the interrupt bits - check with data sheet
#byte PIR1=0xF9E
#byte PIR3=0xFA4
#byte TBE2_IF=PIR3.4
#byte TBE1_IF=PIR1.4
//Get character from UART2 receive
#INT_RDA2
void rx2_int(void) {
tobuff(RX2,ip[1],op[1],RXSIZE,fgetc(COM2));
}
//Get character from UART1
#INT_RDA
void rx1_int(void) {
tobuff(RX1,ip[0],op[0],RXSIZE,fgetc(COM1));
}
//Transmit buffer is empty - output a character if one is waiting,
//or turn off the interrupt handling
#INT_TBE2
void tx2_int(void) {
fputc(COM2,ifrombuff(TX2,i[1],op[1],TXSIZE));
if (isempty(TX2,ip[1],op[1],TXSIZE))
disable_interrupts(INT_TBE2);
}
//Same for UART1
#INT_TBE
void tx1_int(void) {
fputc(COM1,ifrombuff(TX1,i[0],op[0],TXSIZE));
if (isempty(TX1,ip[0],op[0],TXSIZE))
disable_interrupts(INT_TBE);
}
//'putc' command. Will transfer the character to the hardware UART
//if the buffer is empty, otherwise store in a software buffer
void iputc1(char c) {
disable_interrupts(INT_TBE);
if (TBE1_IF) fputc(COM1,c);
else {
tobuff(TX1,ip[0],op[0],TXSIZE);
enable_interrupts(INT_TBE);
}
}
void iputc2(char c) {
disable_interrupts(INT_TBE2);
if (TBE2_IF) fputc(COM2,c);
else {
tobuff(TX2,ip[1],op[1],TXSIZE);
enable_interrupts(INT_TBE2);
}
}
//Check if a character is waiting in an input buffer
#define ikbhit1 !isempty(TX1,ip[0],op[0],TXSIZE)
#define ikbhit2 !isempty(TX2,ip[1],op[1],TXSIZE)
//Fetch a character from the input buffer
int8 igetc(int portno,int1 wait) {
if (portno==1) {
if (wait) while (!ikbhit1);
return frombuff(RX1,ip[0],op[0],RXSIZE);
}
if (wait) while (!ikbhit2);
return frombuff(RX2,ip[1],op[1],RXSIZE);
}
|
Now 'no guarantees', these have been assembled from some standard code and modified to handle your two ports, rather than the two port enviroment they are normally used in.
In the main, you 'putc', to 'iputc1' or 'iputc2', with the character to send, according to which port you want to send to.
ikbhit1, and ikbhit2, retrn 'true', if a character is waiting.
Igetc, requires the port number, with a flag, which if 'true', makes it wait for a character, otherwise it assumes you have already tested, and know a character is waiting. It'll return the value fom port2, for any input value other than '1'.
Best Wishes |
|
|
|
|
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
|