Передача UART через прерывание на микроконтроллере 8051
Моя платформа - микроконтроллер c8051F120. Я хотел бы отправить (= TX) байтов через UART0 с использованием прерываний. Мой дизайн пока что следующий:
#define UART0_TX_SIZE 16
char UART0_tx[UART0_TX_SIZE];
short UART0_tx_uart = 0;
short UART0_tx_main = 0;
short UART0_tx_available = 0;
void UART0_putChar(char value) {
char SAVE_SFRPAGE;
bit EA_SAVE = EA;
// potentially blocking code
while (UART0_tx_available == UART0_TX_SIZE)
;
// disable interrupts
EA = 0;
EA = 0;
if (UART0_tx_available) {
UART0_tx[UART0_tx_main] = value;
++UART0_tx_main;
if (UART0_tx_main == UART0_TX_SIZE)
UART0_tx_main = 0;
++UART0_tx_available;
} else {
SAVE_SFRPAGE = SFRPAGE;
SFRPAGE = UART0_PAGE;
SBUF0 = value;
SFRPAGE = SAVE_SFRPAGE;
}
// reenable if necessary
EA = EA_SAVE;
}
// (return void works for other interrupts)
void UART0_Interrupt() interrupt (4) {
if (RI0 == 1) {
RI0 = 0;
}
if (TI0 == 1) { // cause of interrupt: previous tx is finished
TI0 = 0; // Q: Should this clear tx interrupt flag be further down?
if (SSTA0 & 0x20) { // Errors tx collision
SSTA0 &= 0xDF;
}
if (UART0_tx_available) { // If buffer not empty
--UART0_tx_available; // Decrease array size
SBUF0 = UART0_tx[UART0_tx_uart]; //Transmit
++UART0_tx_uart; //Update counter
if (UART0_tx_uart == UART0_TX_SIZE)
UART0_tx_uart = 0;
}
}
}
Я вполне уверен, что инициализация относительно регистров UART0 и синхронизации через Timer2 (не часть вышеприведенного кода) является правильной, потому что я могу использовать функцию блокировки:
char putchar_Blocking(char value) {
char SFRPAGE_SAVE = SFRPAGE;
SFRPAGE = UART0_PAGE;
while (!TI0) // while TI0 == 1 wait for transmit complete
;
TI0 = 0;
SBUF0 = value;
SFRPAGE = SFRPAGE_SAVE;
return value;
}
Когда я хочу переключиться на дизайн прерывания, я, конечно, также устанавливаю
ES0 = 1;
Кто-нибудь находит недостаток в моем дизайне, который пытается использовать прерывание? Или у кого-нибудь есть пример кода для этого? Спасибо! И большой привет jszakmeister, который ответил на мой вопрос о чтении реестра TCNT.
2 ответа
Мой коллега Го Сюн обнаружил ошибку: переменная UART0_tx_available не была увеличена и уменьшена в нужном месте. Ниже приведена исправленная и протестированная версия:
#define UART0_TX_SIZE 16
char UART0_tx[UART0_TX_SIZE];
short UART0_tx_uart = 0;
short UART0_tx_main = 0;
short UART0_tx_available = 0;
void UART0_putChar(char value) {
char SAVE_SFRPAGE;
bit EA_SAVE = EA;
// potentially blocking code
while (UART0_tx_available == UART0_TX_SIZE)
;
// disable interrupts
EA = 0;
EA = 0;
if (UART0_tx_available) {
UART0_tx[UART0_tx_main] = value;
++UART0_tx_main;
if (UART0_tx_main == UART0_TX_SIZE)
UART0_tx_main = 0;
} else {
SAVE_SFRPAGE = SFRPAGE;
SFRPAGE = UART0_PAGE;
SBUF0 = value;
SFRPAGE = SAVE_SFRPAGE;
}
++UART0_tx_available;
// reenable if necessary
EA = EA_SAVE;
}
// (return void works for other interrupts)
void UART0_Interrupt() interrupt (4) {
if (RI0 == 1) {
RI0 = 0;
}
if (TI0 == 1) { // cause of interrupt: previous tx is finished
TI0 = 0; // Q: Should this clear tx interrupt flag be further down?
if (SSTA0 & 0x20) { // Errors tx collision
SSTA0 &= 0xDF;
}
--UART0_tx_available; // Decrease array size
if (UART0_tx_available) { // If buffer not empty
SBUF0 = UART0_tx[UART0_tx_uart]; //Transmit
++UART0_tx_uart; //Update counter
if (UART0_tx_uart == UART0_TX_SIZE)
UART0_tx_uart = 0;
}
}
}
Самый большой недостаток, который я вижу, это то, что у вас не должно быть никакой переменной (например, UART0_tx_available), изменяемой основным кодом и кодом прерывания.
Обычно я использую UART, управляемый прерываниями, используя кольцевой буфер и два указателя.
Вот простой пример C для AVR micro. Мой код 8051 полностью собран.
/* size of RX/TX buffers */
#define UART_RX_BUFFER_SIZE 16
#define UART_TX_BUFFER_SIZE 16
#define UART_RX_BUFFER_MASK ( UART_RX_BUFFER_SIZE - 1)
#define UART_TX_BUFFER_MASK ( UART_TX_BUFFER_SIZE - 1)
#if ( UART_RX_BUFFER_SIZE & UART_RX_BUFFER_MASK )
#error RX buffer size is not a power of 2
#endif
#if ( UART_TX_BUFFER_SIZE & UART_TX_BUFFER_MASK )
#error TX buffer size is not a power of 2
#endif
/*
* module global variables
*/
static volatile unsigned char UART_TxBuf[UART_TX_BUFFER_SIZE];
static volatile unsigned char UART_RxBuf[UART_RX_BUFFER_SIZE];
static volatile unsigned char UART_TxHead;
static volatile unsigned char UART_TxTail;
static volatile unsigned char UART_RxHead;
static volatile unsigned char UART_RxTail;
static volatile unsigned char UART_LastRxError;
SIGNAL(UART0_TRANSMIT_INTERRUPT)
/*************************************************************************
Function: UART Data Register Empty interrupt
Purpose: called when the UART is ready to transmit the next byte
**************************************************************************/
{
unsigned char tmptail;
if ( UART_TxHead != UART_TxTail) {
/* calculate and store new buffer index */
tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK;
/* get one byte from buffer and write it to UART */
UART0_DATA = UART_TxBuf[tmptail]; /* start transmission */
UART_TxTail = tmptail;
}else{
/* tx buffer empty, disable UDRE interrupt */
UART0_CONTROL &= ~_BV(UART0_UDRIE);
}
}
/*************************************************************************
Function: uart_putc()
Purpose: write byte to ringbuffer for transmitting via UART
Input: byte to be transmitted
Returns: none
**************************************************************************/
void uart_putc(unsigned char data)
{
unsigned char tmphead;
tmphead = (UART_TxHead + 1) & UART_TX_BUFFER_MASK;
while ( tmphead == UART_TxTail ){
;/* wait for free space in buffer */
}
UART_TxBuf[tmphead] = data;
UART_TxHead = tmphead;
/* enable UDRE interrupt */
UART0_CONTROL |= _BV(UART0_UDRIE);
}/* uart_putc */
Отдельное спасибо Питеру Флери http://jump.to/fleury за библиотеку, из которой вышли эти подпрограммы.