0

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

我怎样才能使用这些功能,我应该把它们放在哪里?目前我发送和接收我发送到终端的内容,它工作正常。

刚学,求指教

#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);
  }
}
4

2 回答 2

4

我想先放一个旁注,以免错过:我建议在将字节放入缓冲区后增加头部和尾部索引。因此,在初始化时, head 是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. 我绝对建议对两个缓冲区都使用 push 和 pop(或 put 和 get)函数。中断将调用put_rxand get_tx,而用户代码将调用put_txand get_rx。或者更好的是,编写一些包装函数,例如Uart_Send(char), Uart_SendBuffer(const char*, size_t)Uart_TryReceive(char*, size_t)它们将调用put_txand get_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 代码并尝试为您提供一些更直接的指导,但我认为接受、理解并将其转化为您的实现是很有价值的。

于 2021-03-19T06:49:34.083 回答
1

我仔细查看了 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;
}
于 2021-03-23T09:26:57.760 回答