UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Diseño con Microcontroladopres Interfaz serial con buffer circular para MSP430 La mayor parte de las implementaciones de transmisión de datos vía puerto serial utilizan las siguientes funciones: Transmisión void USART_SendData (unsigned char Data) { UTXBUF = Data; // buffer de transmisión } Recepción unsigned char USART_GetData (void) { unsigned char Data; Data = URXBUF; // buffer de recepción return (Data); } En estos casos, se considera el envío y recepción de sólo un byte. Además, no se realiza un chequeo de los registros de transmisión (UxTXBUF y UxRXBUF) para verificar su disponibilidad. Esto puede generar mensajes corruptos, especialmente en transmisiones que involucran más de un byte. La idea es entonces separar los datos de transmisión y recepción de los registros de hardware. Para esto, se utilizan buffers circulares cuya principal ventaja es que pueden trabajar sin inicializaciones complejas. En la solución propuesta se utiliza un buffer circular para la transmisión y otro para la recepción. Cada buffer está ligado a dos índices (un índice de lectura y otro de escritura). También se considera una variable que guarda la cuenta de bytes remanentes en el buffer. Esta variable es también igual a la distancia entre los índices de lectura y escritura. Declaración de las variables #define TX_INT_ENABLE IE1 |= UTXIE0 #define TX_INT_DISABLE IE1 &= ~UTXIE0 #define RX_INT_ENABLE IE1 |= URXIE0 #define RX_INT_DISABLE IE1 &= ~URXIE0 #define RXBUFSIZE 16 // receive buffer size static volatile unsigned char RXBuffer[RXBUFSIZE]; // receive buffer // receive buffer indexes : Pablo Naveas Farías 08-01-2004 6 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Diseño con Microcontroladopres static volatile unsigned char RXReadIndex, RXWriteIndex; // count of received bytes: static volatile unsigned char RXCharCount; #define TXBUFSIZE 16 // transmit buffer size static volatile unsigned char TXBuffer[TXBUFSIZE]; // transmit buffer // transmit buffer indexes: static volatile unsigned char TXReadIndex, TXWriteIndex; // not yet transmitted bytes: static volatile unsigned char TXCharCount; #define BUFFER_EMPTY 1 // 1: nothing to send static volatile unsigned char TXBufferEmpty; // flag for synchronization Inicialización de índices y del USART0 - RS232Init () : void RS232Init (void) { RXWriteIndex = RXReadIndex = RXCharCount = 0; TXWriteIndex = TXReadIndex = TXCharCount = 0; TXBufferEmpty = BUFFER_EMPTY; // reset empty flag //USART0_INIT BCSCTL1 &= ~DIVA0; // ACLK = XT1 / 4 = MCLK / 4 BCSCTL1 |= DIVA1; UCTL0 = CHAR; //Sart bit, 8 data bits, no parity, 1 stop UTCTL0 = SSEL0; //ACLK is UART clock U0BR0 = 0xd0; //2000000:9600=208 U0BR1 = 0x00; UMCTL0 = 0x00; //no modulation ME1 |= UTXE0 | URXE0; //enable UART modul P3SEL |= 0x30; // P3.4,5 = USART0 TXD/RXD P3DIR |= BIT4; //enable TXD0 as output P3DIR &= ~BIT5; //enable RXD0 as input IE1 |= UTXIE0; // Enable USART0 TX interrupt IE1 |= URXIE0; // Enable USART0 RX interrupt _EINT(); //enable interrupt } Transmisión La rutina que se entrega a continuación sólo escribe el byte a transmitir en la posición indicada por el TXwriteindex en el buffer de transmisión. Luego este índice es incrementado, al igual que la cuenta de los bytes que no han sido transmitidos. Luego, se chequea si existe una transmisión activa. Si es así, no se hace nada más. En caso contrario, se inicia la transmisión. Pablo Naveas Farías 08-01-2004 6 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Diseño con Microcontroladopres Transmisión de datos - RS232TXChar (): void RS232TXChar (char cByte) { TXBuffer[TXWriteIndex++] = cByte; // load byte to buffer and inc index TXWriteIndex &= TXBUFSIZE-1; // adjust index to borders of buffer TX_INT_DISABLE; // disable transmit interrupt (in IE1) TXCharCount++; // new char, inc count TX_INT_ENABLE; // enable interrupt (in IE1) if (TXBufferEmpty && TXCharCount) // buffer had been empty { TXBufferEmpty = !BUFFER_EMPTY; // reset empty flag U0TXBUF = TXBuffer[TXReadIndex++]; // load tx register, inc index TXReadIndex &= TXBUFSIZE-1; // adjust index TXCharCount--; // char sent, dec count } } Rutina de atención de interrupciones para la transmisión - TXInterrupt (): #pragma vector = UART0TX_VECTOR __interrupt void TXInterrupt (void) { _EINT(); if (TXCharCount) { // send if chars are in buffer U0TXBUF = TXBuffer[TXReadIndex++]; // load tx register, inc index TXReadIndex &= TXBUFSIZE-1; // adjust index TXCharCount--; // char sent, dec count } else // buffer empty, nothing to do TXBufferEmpty = BUFFER_EMPTY; // set empty flag } La rutina de atención de interrupciones por transmisión es atendida si el registro de transmisión está vacío. Si esto ocurre la rutina de atención de la interrupción chequea si queda algún byte por enviar. Si es así, el siguiente byte es escrito en el registro de transmisión. El índice de lectura es incrementado y el contador de bytes es decrementado. Si el byte enviado es el último (TXCharCount == 0), la transmisión termina seteando el flag que indica que el buffer está vacío. De este modo, para enviar un byte sólo hay que llamar a la función TXChar pasándole el byte a transmitir. Todo el resto del trabajo se realiza automáticamente en el background, sin interferir con otras partes del firmware. Pablo Naveas Farías 08-01-2004 6 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Diseño con Microcontroladopres Recepción Se utiliza el mismo concepto anterior: un buffer circular, con índices de lectura y escritura, y una variable que lleva la cuenta de los bytes que no han sido recibidos. Sólo la fuente y el destino de los datos cambia: la fuente es la interrupción por recepción. Rutina de atención de interrupciones para la recepción - RXInterrupt (): #pragma vector = UART0RX_VECTOR __interrupt void RXInterrupt (void) { _EINT(); RXBuffer[RXWriteIndex++] = U0RXBUF; // store received byte and // inc receive index RXWriteIndex &= RXBUFSIZE-1; // reset index RXCharCount++; // received, inc count } El contenido de registro de recepción se escribe al buffer de lectura, y se incrementan el índice de lectura y el contador de bytes. El siguiente byte recibido es escrito a la siguiente posición del buffer de lectura, y así sucesivamente. Todos los bytes recibidos son almacenados en el buffer. Se requieren dos funciones para recoger los datos: la primera - RS232RXBufferCount() - chequea si se ha escrito un nuevo dato y la segunda - cRS232GetChar() - recoge el dato. Al leer un byte, el índice de escritura se incrementa para hacer que la siguiente llamada a la rutina lea el siguiente byte disponible desde el buffer. unsigned char RS232RXBufferCount (void) { return (RXCharCount); } char cRS232GetChar (void) { char Byte; if (RXCharCount) { // char still available Byte = RXBuffer[RXReadIndex++]; // get byte from buffer RXReadIndex &= RXBUFSIZE-1; // adjust index RX_INT_DISABLE; // disable rx interrupt (IE2) RXCharCount--; // one char read, dec count RX_INT_ENABLE; // done, enable int (IE2) return (Byte); } else Pablo Naveas Farías 08-01-2004 6 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Diseño con Microcontroladopres return (0); // if there is no new char } Estructura del código necesario para recepción datos desde el puerto serial char cReceiveByte; .... if (RS232RXBufferCount()) // char in buffer? { cReceiveByte = cRS232GetChar(); // yes, get it ... } ... Así, todo lo que el programador debe hacer es asegurarse que los bytes son leídos antes que ocurra un overflow del buffer. Notas: • • Para ajustar los buffers, usar siempre tamaños potencias de dos (16, 32, 64, ...). Los códigos entregados tienen la sintaxis adecuada para el compilador de la versión 2 de IAR. ANEXO (Código para Tx y Rx) void main (void) { InitOsc (); InitPorts (); RS232Init (); InitLCD (); RS232TXChar (CR); RS232TXChar (LF); for (i = 0; i != sizeof(TxMss); i++) // envío del string inicial { Delayx100us(50); RS232TXChar (TxMss[i]); } RS232TXChar (CR); RS232TXChar (LF); Pablo Naveas Farías 08-01-2004 6 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Diseño con Microcontroladopres while (1) // loop de recepción { if ( RS232RXBufferCount() ) // hay caracteres? { RxB = cRS232GetChar(); RS232TXChar (RxB); // echo a pantalla STATUS_LED_ON; if (cntr == 0) // envío al display LCD { SEND_CMD(CLR_DISP); SEND_CMD(DD_RAM_ADDR); } SEND_CHAR(RxB); if(cntr == 15) SEND_CMD(DD_RAM_ADDR2); if(cntr++ == 31) cntr = 0; STATUS_LED_OFF; } } } Pablo Naveas Farías 08-01-2004 6