Interfaz serial con buffer circular para MSP430

Anuncio
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
Descargar