I am trying to generate an interrupt when a byte is received via the serial port of an Atmel AT89C51. I am not having much luck. Here is my code:
#include <reg51.h> #include <CTYPE.H> #include <STDIO.H> #include <INTRINS.H> int big_d, count, dogg, i, j; void rcv (void) interrupt 4 { big_d = _getkey(); count++; dogg = 1; RI = 0; } void main(void) { SCON = 0x50; /* SCON: mode 1, 8-bit UART, enable rcvr */ TMOD |= 0x20; /* TMOD: timer 1, mode 2, 8-bit reload */ TH1 = 253; /* TH1: reload value for 9600 baud @ 11.0592MHz */ TR1 = 1; /* TR1: timer 1 run */ TI = 1; /* TI: set TI to send first char of UART */ ES = 1; /* enable serial interrupts */ dogg = 0; count = 0x30; do{ putchar(count); for(j=0; j<255; j++) { for(i=0; i<255; i++) { _nop_(); _nop_(); _nop_(); _nop_(); } } if(dogg == 1) { putchar(count); printf("\n"); putchar(big_d); printf("\n\n\n"); dogg = 0; } }while(1); }
There are several problems with this program. 1. Global interrupts are not enabled. You must set the EA bit to enable them. 2. You set the TI bit initially to allow transmission of characters, however, this triggers an interrupt. Your interrupt routine does not handle sending characters. 3. You interrupt calls _getkey to retrieve the character from the SBUF. The problem with this is that _getkey clears the TI bit. Your interrupt ALSO clears the TI bit. So, if a serial character is receive between these times, it will be lost. 4. I can only assume that this program is designed to receive serial characters in the interrupt and to transmit serial characters in a polled fashion. This is BAD. A serial I/O example program already exists for the 8051. Take a look at http://www.keil.com/download/docs/intsio.zip.asp for a working example. Jon
Thanks for the info. Do I need to send my characters in my interrupt subroutine as well? I would like to just use the interrupt to receive characters so I can increment a counter everytime I receive a byte. Jake
Do I need to send my characters in my interrupt subroutine as well? Well, technically speaking, no, you don't need to send characters from the interrupt. However, if you enable the serial interrupt, you will receive an interrupt when TI is set or when RI is set. And, you will have to do a lot of work to get around this. The example link that I provided already handles serial transmit and receive in the interrupt, so you don't have to write anything. Jon
"The example link that I provided already handles serial transmit and receive in the interrupt, so you don't have to write anything." Yes, I have used the example code that Jon cited and can confirm that it really does work - just like it says on the tin!
"I would like to just use the interrupt to receive characters so I can increment a counter everytime I receive a byte." Increment the counter only if (RI) Erik
No, polling of only the transmitter is common (and I would argue "good") practice. Since there is no bit for "Transmitter Register Empty" (TRE), you can use the transmitter interrupt to implement TRE. I'd suggest that you consider using JBC when polling TRE to avoid potential design problems in case you ever decide to send characters from the receive interrupt. This might happen if you needed XON XOFF flow control.
_1: JBC TRE, _2 JMP _1 _2:
Do you understand that there is no transmit interrupt vector? The 8051 has a combined serial interrupt vector that either the RI or TI flag or both cause a vector to. You cannot have a serial receive interrupt vector service routine that does not check RI and TI or at least ignore TI. Polling for TI is silly if you want to have some level of hardware muti-tasking. You fill up a packet buffer, set TI and let the UART ISR send out the entire packet without further intervention from the background task or main() loop. Going further, you can ring buffer both the xmit and recv so that getchar pulls from the recv ring and putchar puts into the xmit ring. The serial ISR puts and pulls from the opposite rings. It all works swimmingly. The only caveat is that at very high baud rates, slower 8051's cannot get the next byte into/from the Tx/Rx buffer in time with interrupt overhead. - Mark
The example code cited earlier by Jon implements the ring buffer technique just described by Mark.
View all questions in Keil forum