Software UART Using PCA
Software UART Using PCA
If the on-chip UART is being used as an inter-processor link, the PCA can be used to interface the microcontroller to additional asynchronous lines. This application note uses several different Compare/Capture modes available on the PCA to receive or transmit bytes of data. It is assumed the reader is familiar with the PCA and C programming language. For more information on the PCA refer to PCA chapter of the data sheet or design guide listed in Section 5.
2. Description
The Figure 1 shows the format of a standard 10-bit asynchronous frame: 1 start bit (0), 8 data bits, and 1 stop bit (1). The start bit is used to synchronize the receiver to the transmitter; at the leading edge of the start bit the receiver must set up its timing logic to sample the incoming line in the center of each bit. Following the start bit are eight data bits which are transmitted least significant bit first. The stop bit is set to the opposite state of the start bit to guarantee that the leading edge of the start bit will cause a transition on the line. It also provides a dead time on the line so that the receiver can maintain its synchronization. The provided software can be easily adapted to support other data format ; for example 9 data bits can be supported by changing the constant NBR_OF_DATA_BITS.
Mode 1 D0 D1 D2 D3 D4 D5 D6 D7
Figure 1. Data Frames Two of the Compare/Capture modes on the PCA are used in receiving and transmitting data bits. When receiving, the Negative-Edge Capture mode allows the PCA to detect the start bit. Then using the Software Timer mode, interrupts are generated to sample the incoming data bits. This same mode is used to clock out bits when transmitting. Two kinds of reception errors can be reported : overrun and frame error.
ANM079
3. Hardware
PCA pin CEX0 is used as RxD and pin CEX1 as TxD.
4. Software
This Application Note contains six sections of code:
Macro and constants definitions Main program PCA initialization routine PCA interrupt handler Receive routine Transmit routine.
A complete listing of the routines and the test loop which was used to verify their operation is found in the Section 6
ANM079
4.4. PCA interrupt handler
Section 6.1.3 contains the PCA interrupt handler. According to the received interrupt (channel 0 and channel 1), it calls either the receive or transmit routine.
IDLE
ANM079
State : IDLE State :WAITING_FOR_MID_START_BIT
State : WAITING_FOR_DATA_BIT
State : WAITING_FOR_STOP_BIT
Section 6.1.4 shows the code of the receive routine. The main routine must have a way to know that a byte has been received. With the on-chip UART, the RI (Receive Interrupt) bit is set whenever a byte has been received. For the software serial port, it can pool the receive_flag_0 as in the provided example or use any unused interrupt vector to generate an interrupt when a byte has been received.
ANM079
The register, transmit_byte_1, is used to pass the bits which must transmitted. Finally, one counter registers, nbr_of_rxd_bits_left keep track of how many bits are still to be transmitted. Flag transmit_flag_1 is set when transmission is started and reset when all bits have been transmitted. The first time through, the start bit is transmitted. At each successive interrupt one bit is transmitted and nbr_of_rxd_bits_left is decremented. When nbr_of_rxd_bits_left equals zero, the stop bit is transmitted.
The transmit routine can be in four different states: IDLE : Initialize transmission. SENDING_DATA_BIT : Send data bit and shift transmit register. SENDING_STOP_BIT : Force begin of the stop bit. STOP_SEND_BIT : Disable PCA and reset flag The state diagram for the transmit routine is given in Figure 4. The flowchart for each state is given in Figure 5. Section 6.1.5 shows the code of the transmit routine.
IDLE
ANM079
State : IDLE State :SENDING_DATA_BIT
State : SENDING_STOP_BIT
State : STOP_BIT_SENT
4.7. Conclusion
The software routines in the Section 6 can be altered to vary the baud rate and number of channels to fit a particular application. The number of channels which can be implemented is limited by the CPU time required to service the PCA interrupt. At higher baud rates, fewer channels can be run. The test program verifies the simultaneous operation the on-chip full-duplex channel at 9600 Baud, leaving about 80% CPU resource free at 16 Mhz. 6 Rev A - 13 March 2001
ANM079
5. Bibliography
[1] TSC80251G1 Design Guide [2] TS80C51Rx2 data sheet [3] T89C51RD2 data sheet
ANM079
6. Appendix A: Software
6.1. MAIN.C
/**************************************************************************** Project: Soft UART for Atmel W&M microcontrollers Module: MAIN.C Version: 1.1 Original ver: 970902 Updated: 970904 Updated: 20000112/JD Written by: Jan Gripsborn *****************************************************************************/ #include "def.h" macros */ void init_pca(void); void receive0(void); void transmit1(void); bit /* Include register declaration, local definitions and
/* Init the PCA block */ /* Receive routine called from PCA interrupt */ /* Transmit routine called from PCA interrupt */
unsigned char receive_status_0 = 0;/* Reveive status for channel 0 Bit 4 = OverRun Bit 5 = FrameError */ data_type received_byte_0; /* The received byte on channel 0 The definition of "data_type" depends on the number of data bits used */ bit transmit_flag_1 = FALSE; transmit_byte_1; /* Flag indicating transmission on on channel 1 */ /* Byte to tranmit */
data_type
ANM079
} } }
ANM079
Performs reception of a byte on channel 0. receive_flag _0 is set to TRUE when reception is finished and the received byte is stored in received_byte_0. If RXD_STATUS is enabled (see DEF.h) receive_status_0 will contain the error information: Bit 4 = OverRun Bit 5 = FrameError The user has to clear receive_flag_0 when a byte has been read. Othervise an overRun will be generated on the next received byte (If RXD_STATUS is used). ---------------------------------------------------------------------------*/ void receive0(void) { enum {WAITING_FOR_DATA_BITS, /* States used by the variable state */ WAITING_FOR_MID_START_BIT, WAITING_FOR_STOP_BIT, IDLE}; static unsigned char state= IDLE; static unsigned char nbr_of_rxd_bits_left;/* Indicates number of bits left to transmit */ static data_type tmp_received_byte = 0; /* The received byte is stored here */ union countertype /* tmp holds the CCAP0 counter value */ { unsigned int tmp; char byte[2]; }; static union countertype counter;
switch (state) { case IDLE: /* State = IDLE, No reception is going on */ counter.byte[1] = CCAP0L; /* Store the present counter value */ counter.byte[0] = CCAP0H; CCAP0L= (counter.tmp+=HALF_BIT_TIME); /* Increase module 0 counter with 1/2 bit time */ CCAP0H= counter.byte[0]; CCAPM0 = S_W_TIMER; /* Set up the Module 0 for Software Timer mode */ state = WAITING_FOR_MID_START_BIT; /* Wait for the start bit */ break; case WAITING_FOR_MID_START_BIT: if (!RXD_PIN0) /* Check if we have a correct start bit */ { CCAP0L= (counter.tmp+=ONE_BIT_TIME); /* Increase module 0 counter with 1 bit time */ CCAP0H= counter.byte[0]; /* -" */ nbr_of_rxd_bits_left = NBR_OF_DATA_BITS; /* Init variable */ state = WAITING_FOR_DATA_BITS; /* Wait for the data bits */ } else { CCAPM0 = NEG_EDGE; /* Set-up Module 0 for Negative Edge capture mode */ state = IDLE; /* Exit reception */ } break; case WAITING_FOR_DATA_BITS:
10
ANM079
tmp_received_byte = (tmp_received_byte>>1); /* Shift the received byte */ if (RXD_PIN0) /* Add the new bit */ tmp_received_byte += (1<<(NBR_OF_DATA_BITS-1)); CCAP0L= (counter.tmp+=ONE_BIT_TIME); /* Increase module 0 counter with 1 bit time */ CCAP0H= counter.byte[0]; if (!--nbr_of_rxd_bits_left) /* Are we finshed? */ state = WAITING_FOR_STOP_BIT; /* Yes, wait for the stop bit */ break; case WAITING_FOR_STOP_BIT: #if RXD_STATUS == TRUE /* See DEF.h for definition */ if (receive_flag_0) /* Check for overRun */ receive_status_0 = OverRun; if (!RXD_PIN0) /* Check the stop bit. */ receive_status_0 += FrameError; /* If no stop bit a FrameError is generated */ #endif receive_flag_0 = TRUE; /* Set flag to indicate a received byte */ received_byte_0 = tmp_received_byte; /* Store the received byte */ CCAPM0 = NEG_EDGE; /* Set-up Module 0 for Negative Edge capture mode */ state = IDLE; /* Exit reception */ break; } }
11
ANM079
CCAP1L= (counter.tmp+=ONE_BIT_TIME); /* Increase module 1 counter with 1 bit time */ CCAP1H= counter.byte[0]; /* -" */ CCAPM1 = S_W_TIMER; /* Set-up Module 1 for Software Timer mode */ nbr_of_txd_bits_left = NBR_OF_DATA_BITS; /* Init variable */ transmit_flag_1 = TRUE; /* Set flag to indicate transmission */ state = SENDING_DATA_BITS; break; case SENDING_DATA_BITS: TXD_PIN1 = transmit_byte_1; /* Send data bit */ transmit_byte_1 = transmit_byte_1 >> 1; /* Shift out the sent bit */ if (!(--nbr_of_txd_bits_left)) /* Are we finished with data bits? */ state = SENDING_STOP_BIT; /* Yes! */ CCAP1L= (counter.tmp+=ONE_BIT_TIME); /* Increase module 1 counter with 1 bit time */ CCAP1H= counter.byte[0]; /* -" */ break; case SENDING_STOP_BIT: TXD_PIN1 = 1; /* Send stop bit */ CCAP1L= (counter.tmp+=ONE_BIT_TIME); /* Increase module 1 counter with 1 bit time */ CCAP1H= counter.byte[0]; /* -" */ state = STOP_BIT_SENT; break; case STOP_BIT_SENT: CCAPM1 = 0; /* Disable PCA Module 1 */ transmit_flag_1=FALSE; /* Clear flag and indicate transmission is over */ state = IDLE; /* Exit tranmission */ break; } }
12
ANM079
6.2. def.h
#include "reg51f.h" /* Include register declarations */ #define NBR_OF_DATA_BITS 8 /* Number of data bits used for reception and transmission */ #define XTAL 16 /* Crystal frequency, MHz */ #define BAUD_RATE 9600 /* Baudrate of transmission and reception */ #define ONE_BIT_TIME (((long int) XTAL*1000000)/((long int) 12*BAUD_RATE)) #define HALF_BIT_TIME ONE_BIT_TIME/2 #define TRUE 1 #define FALSE 0 #define RXD_STATUS #define #define #define #define #define #define RXD_PIN0 CEX0 TXD_PIN1 CEX1 NEG_EDGE 0x11 S_W_TIMER 0x49 OverRun FrameError 0x04 0x08
TRUE
/* TRUE if "receive_status" should be active */ /* CEX0 Pin used for reception */ /* CEX1 Pin used for transmission */
/* Sets up PCA Module for Negative Edge Capture Mode */ /* Sets up PCA Module for Software Timer Mode */ /* Bit used in receive_status_0 */ /* Bit used in receive_status_0 */
#if (NBR_OF_DATA_BITS > 8) /* The type used for the byte to transmit and receive */ #define data_type unsigned int /* (int or char) depends on the number of bits used */ #else #define data_type unsigned char #endif #define TRANSMIT_1(A) transmit_byte_1 = (A); /* Macro used for transmitting a byte */\ CCAPM1 = NEG_EDGE; /* Enable the interrupt */ \ TXD_PIN1 = 0; /* Send startbit and generate an interrupt */
13