c语言 - 中断上的STM32F411E-DISCO Uart循环缓冲区



我想接收和传输将放入循环缓冲区的数据。我有插入字符和读取字符的功能。

如何使用这些功能,应该将它们放在哪里?目前,我发送和接收发送到终端的内容,并且工作正常。

我只是在学习,请给我一些建议

#define UART_RX_BUF_SIZE 7
#define UART_TX_BUF_SIZE 7
int8_t uart_put_char(char data);
int8_t uart_get_char(char *data);
volatile char uart_rxBuff[UART_RX_BUF_SIZE];
volatile char uart_txBuff[UART_TX_BUF_SIZE];
void uart_put_string(char *s);
typedef struct {
volatile char *const buffer;
uint8_t head;
uint8_t tail;
} circ_buffer_t;
volatile circ_buffer_t uart_rx_circBuff = {uart_rxBuff, 0, 0};
volatile circ_buffer_t uart_tx_circBuff = {uart_txBuff, 0, 0};
uint8_t received_char;
int8_t uart_put_char(char data) {
uint8_t head_temp = uart_tx_circBuff.head + 1;
if (head_temp == UART_TX_BUF_SIZE)
head_temp = 0;
if (head_temp == uart_tx_circBuff.tail)
return 0;
uart_tx_circBuff.buffer[head_temp] = data;
uart_tx_circBuff.head = head_temp;
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);
return 1;                                                                                                                                                                                                                                                                    
}                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                         
int8_t uart_get_char(char *data) {                                                                                                                                                                                                                                             
                                                                                                                                                                                                         
if (uart_rx_circBuff.head == uart_rx_circBuff.tail)                                                                                                                                                                                                                          
return 0;                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                         
uart_rx_circBuff.tail++;                                                                                                                                                                                                                                                     
                                                                                                                                                                                                         
if (uart_rx_circBuff.tail == UART_RX_BUF_SIZE)                                                                                                                                                                                                                               
uart_rx_circBuff.tail = 0;                                                                                                                                                                                                                                                 
                                                                                                                                                                                                         
*data = uart_rx_circBuff.buffer[uart_rx_circBuff.tail];                                                                                                                                                                                                                      
                                                                                                                                                                                                         
return 1;                                                                                                                                                                                                                                                                    
}                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                         
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {                                                                                                                                                                                                                      
if (huart->Instance == USART1) {                                                                                                                                                                                                                                             
// uart_put_char(&received_char);                                                                                                                                                                                                                                          
                                                                                                                                                                                                         
HAL_UART_Transmit(&huart1, &received_char, 1, 100);                                                                                                                                                                                                                        
// uart_get_char(received_char);
HAL_UART_Receive_IT(&huart1, &received_char, 1);
}
}

我想先放一个旁注,这样就不会错过: 我建议在将字节放入缓冲区后增加头尾索引。因此,在初始化时,头部0。当你调用put_tx时,字节将存储在索引0然后递增到1。当中断调用get_tx时,尾巴仍然0,所以你会得到第一个字符,然后它会递增到1。这并不重要,但我认为如果你以这种方式思考,编写干净的代码会更容易。 此外,这完全是一种微优化,但请考虑将缓冲区大小设置为 2 的幂。这样,您无需if (head==BUF_SIZE) head=0;就可以head &= BUF_SIZE-1;,并且可以节省几个时钟周期,而无需进行测试。;-)


设置起来肯定很痛苦,但如果你能绕过多个步骤,那就非常值得了。HAL 可能会为您处理其中的大部分问题,但我对此了解不够。

  1. 您需要一个用于 UART 事件的中断处理程序。
  2. 您需要告诉UART在发送字符时发出RXNE(接收不为空)和TXE(传输为空)的中断。
  3. 您需要告诉 NVIC 启用 UART 中断。
  4. 我绝对建议为这两个缓冲区使用推送和弹出(或放置和获取)函数。中断将调用put_rxget_tx,而用户代码将调用put_txget_rx。或者更好的是,编写一些包装函数,例如Uart_Send(char)Uart_SendBuffer(const char*, size_t)Uart_TryReceive(char*, size_t)将调用put_txget_rx

如果 HAL 像我认为的那样聪明,您可能可以将步骤 1-3 合并为一个步骤,然后只需实现HAL_UART_RxCpltCallback(就像您所做的那样)并HAL_UART_TxCpltCallback

我不知道HAL是如何工作的,足以给你一个精确的解决方案(我所有的STM32工作都是建立在我自己制作的标题上,然后我才意识到CMSIS的存在 - 哎呀!)所以这是我如何在低级代码中做到这一点。

void USART1_IRQHandler() __attribute__((interrupt))
{
// This would probably be handled in HAL_UART_RxCpltCallback
if (USART1->SR.RXNE)           // We've received a byte!
uart_push_rx(USART1->DR);  // Push the received byte onto the back
// of the RX buffer.
// This would probably be handled in HAL_UART_TxCpltCallback
if (USART1->SR.TXE) // Transmit buffer is empty - send next byte
{
char nextByte;
if (uart_pop_tx(&nextByte)) // Get the next byte in the buffer and
USART1->DR = nextByte;  // shove it in the UART data register.
else                       // No more data in the circular buffer,
USART1->CR1.TXEIE = 0; // so disable the TXE interrupt or we'll
// end up stuck in a loop.
}
if (USART1->SR.TC) // There's also a flag for 'transmit complete'. This
{ } // is different from TXE in that TXE means "Okay, I've started this
// one, tell me what'll come next," whereas TC says "Okay, I've
// finished sending everything now. Anything else, boss?"
// We won't use it, but be aware of the terminology. The HAL might
// try and confuse you with its words.
}
void InitialiseUart()
{
HAL_UART_Configure(&huart1, baud, stopBits, andStuff, probably, etc);
HAL_UART_Enable(&huart1);
// You probably don't need to worry about anything below here,
// if the HAL is smart. But I've included it for completeness,
// so you can understand more of what the MCU is doing.
// Enable RXNE (receive not empty) interrupt
USART1->CR1.RXNEIE = 1;
// Don't enable TXE (transmit empty) interrupt yet. Only when you send 
// a character, or the interrupt will fire immediately.
// Enable UART interrupts at the system level
NVIC_EnableIRQ(USART1_IRQn);
}

如果你需要我,我会研究 HAL 代码并尝试给你一些更直接的指导,但我认为接受它、理解它并将其转化为你的实现是有价值的。

我仔细查看了 HAL 源代码,看起来通过使用HAL_xxxx_IT()函数,所有中断代码都已经为您处理完毕。也就是说,在应用程序中使用裸机有很多相似之处,因为您一次只发送和接收一个字符。

当您调用__HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE)时,您告诉微控制器在数据寄存器为空时触发中断。但是 HAL 中断例程不知道要传输什么,所以它会发送垃圾,直到它认为它已经完成。

我认为另一个问题可能是由于在多个地方直接访问您的循环缓冲区而导致的,从而导致冲突。您最好使用临时缓冲区(或指向单个char的指针)调用 HAL 函数,该缓冲区取自循环缓冲区或存储在循环缓冲区中。

<小时 />

主要功能

// Entry point
int main(void)
{
// Initialise system and peripherals
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM10_Init();
// Enable interrupts
HAL_NVIC_EnableIRQ(USART1_IRQn);
// Prepare reception of the first character
HAL_UART_Receive_IT(&huart1, &received_char, 1);
while (1)
{
char downloaded;
if (UartReceiveChar(&downloaded) && downloaded == 'x')
UartSendChar(downloaded);
}
}

UART包装器

// Function declarations
int8_t UartSendChar    (char  data);
void   UartSendString  (char* str);
int8_t UartReceiveChar (char* data);
// Variables
int8_t  isTransmitting = 0;
uint8_t sendingChar;
uint8_t receivedChar;

// Function definitions
// Callback function for when a character has finished sending by the HAL
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
// The HAL has just sent the tail byte from the TX buffer
// If there is still data in the buffer, we want to send the
// next byte in the buffer.
if (buffer_pop_tx(&sendingChar))
HAL_UART_Transmit_IT(huart, &sendingChar, 1);
else
isTransmitting = 0;
}
// Callback function for when a character is received by the HAL
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// The HAL has received a character into 'receivedChar'
// All we need to do is push it onto our circular buffer
buffer_push_rx(receviedChar);
// and prepare to receive the next character
HAL_UART_Receive_IT(huart, &receivedChar, 1);
}
// Send a character
int8_t UartSendChar(char data)
{
// Push the character onto the buffer
int8_t returnValue = buffer_push_tx(data);
// Start sending the buffer, if we're not already transmitting
if (!isTransmitting)
{
sendingChar = data;
isTransmitting = 1;
HAL_UART_Transmit_IT(&huart1, &sendingChar, 1);
}
return returnValue;
}
// Send a null-terminated string
int8_t UartSendString(char* str)
{
// Iterate through all the non-null characters
while (*str)
{
// Send the character; Wait if the buffer is full
while (!UartSendChar(*str)) ;
++str;
}
return 1;
}
// Receive a character
int8_t UartReceiveChar(char* data)
{
// Just a wrapper for buffer_pop_rx
return buffer_pop_rx(data);
}

缓冲区实现

// I've changed your circular buffer size for optimisation purposes
#define UART_RX_BUF_SIZE 8
#define UART_TX_BUF_SIZE 8
// Buffer type definition
typedef struct
{
volatile char *const buffer;
uint8_t head;
uint8_t tail;
uint8_t isFull : 1;
uint8_t isEmpty : 1;
uint8_t hasOverflowed : 1;  // Overflow and underflow are only relevant if we choose to
uint8_t hasUnderflowed : 1; // allow pushing and popping with an empty/full buffer
} circ_buffer_t;

// Function declarations
int8_t buffer_push_tx  (char  data);
int8_t buffer_push_rx  (char  data);
int8_t buffer_pop_tx   (char* data);
int8_t buffer_pop_rx   (char* data);

// Variables
volatile char uart_rxBuff[UART_RX_BUF_SIZE];
volatile char uart_txBuff[UART_TX_BUF_SIZE];
volatile circ_buffer_t uart_rx_circBuff = {uart_rxBuff, 0, 0};
volatile circ_buffer_t uart_tx_circBuff = {uart_txBuff, 0, 0};

// Function definitions
// Push a character onto the transmit buffer
int8_t buffer_push_tx(char data)
{
if (uart_tx_circBuff.isFull) // buffer is full
{
// uart_tx_circBuff.hasOverflowed = 1; // Nasty things can happen if we allow overflows. But in some special cases, it may be necessary.
return 0;
}
// Put the character at the head position and increment the head index
uart_tx_circBuff.buffer[uart_tx_circBuff.head++] = data;
uart_tx.circBuff.head &= (UART_TX_BUF_SIZE - 1); // don't use &= if the buffer size isn't a power of 2
// mark the buffer as full if the head and tail indices are the same
uart_tx_circBuff.isFull = (uart_tx_circBuff.head == uart_tx_circBuff.tail);
uart_tx_circBuff.isEmpty = 0;
// OK
return 1;
}
// Push a character onto the receive buffer
int8_t buffer_push_rx(char data)
{
if (uart_rx_circBuff.isFull) // buffer is full
{
// uart_rx_circBuff.hasOverflowed = 1;
return 0;
}
// Put the character at the head position and increment the head index
uart_rx_circBuff.buffer[uart_rx_circBuff.head++] = data;
uart_rx.circBuff.head &= (UART_RX_BUF_SIZE - 1); // don't use &= if the buffer size isn't a power of 2
// mark the buffer as full if the head and tail indices are the same
uart_rx_circBuff.isFull = (uart_rx_circBuff.head == uart_rx_circBuff.tail);
uart_rx_circBuff.isEmpty = 0;
// OK
return 1;
}
// Try to get a character from the receive buffer.
int8_t uart_pop_rx(char *data)
{
if (uart_rx_circBuff.isEmpty) // buffer is empty
{
// uart_rx_circBuff.hasUnderflowed = 1;
return 0;
}

// Put the character from the tail position of the buffer into 'data' and increment the tail index
*data = uart_rx_circBuff.buffer[uart_rx_circBuff.tail++];
uart_rx_circBuff.tail &= (UART_RX_BUF_SIZE - 1); // // don't use &= if the buffer size isn't a power of 2
// mark the buffer as full if the head and tail indices are the same
uart_rx_circBuff.isEmpty = (uart_rx_circBuff.head == uart_rx_circBuff.tail);
uart_rx_circBuff.isFull = 0;
// OK
return 1;
}
// Try to get a character from the transmit buffer.
int8_t uart_pop_rx(char *data)
{
if (uart_tx_circBuff.head == uart_tx_circBuff.tail) // buffer is empty
{
// uart_tx_circBuff.hasUnderflowed = 1;
return 0;
}

// Put the character from the tail position of the buffer into 'data' and increment the tail index
*data = uart_tx_circBuff.buffer[uart_tx_circBuff.tail++];
uart_tx_circBuff.tail &= (UART_TX_BUF_SIZE - 1); // don't use &= if the buffer size isn't a power of 2
// mark the buffer as full if the head and tail indices are the same
uart_tx_circBuff.isEmpty = (uart_tx_circBuff.head == uart_tx_circBuff.tail);
uart_tx_circBuff.isFull = 0;
// OK
return 1;
}

最新更新