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

person user15216933    schedule 18.03.2021    source источник
comment
Я не использую stm32, но делаю то же самое на TI msp432. Как правило, карта должна иметь как буфер чтения, так и буфер передачи (хотя вы, безусловно, можете предоставить свой собственный). При чтении или записи из UART будет установлено прерывание, когда приемный буфер получит символ. Вы можете использовать функцию прерывания для обработки как передачи, так и приема. Вы должны убедиться, что часы вашей подсистемы работают достаточно быстро для одновременной передачи и приема. У меня были проблемы в режиме пониженного энергопотребления с тактовой частотой 3 МГц со скоростью 115 200 бод на карте TI, но с тактовой частотой 12 МГц проблем не возникает.   -  person David C. Rankin    schedule 19.03.2021


Ответы (2)


Сначала я хочу сделать побочное примечание, чтобы его не пропустить: я бы предложил увеличить индексы начала и конца после того, как вы поместили байт в буфер. Итак, при инициализации 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_rx и get_tx, а пользовательский код будет вызывать put_tx и get_rx. Или еще лучше, напишите несколько функций-оболочек, таких как Uart_Send(char), Uart_SendBuffer(const char*, size_t), Uart_TryReceive(char*, size_t), которые будут вызывать put_tx и 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 и попытаюсь дать вам более прямое руководство, но я думаю, что это полезно взять, понять и преобразовать в вашу реализацию.

person Ashley Miller    schedule 19.03.2021
comment
см. мой ответ ниже. Есть ли у вас какие-либо идеи ? - person user15216933; 22.03.2021
comment
когда я добавляю if(USART_SR_RXNE!=1){ перед uint8_t head_temp... и if(USART_SR_TXE!=1){ перед if (uart_tx_circBuff.head == uart_tx_circBuff.tail) ничего не изменилось. Получите xxxxxxx и множество странных символов. - person user15216933; 23.03.2021

Я внимательно изучил исходный код HAL, и похоже, что с помощью функций HAL_xxxx_IT() весь код прерывания уже обрабатывается за вас. Тем не менее, есть много общего с выполнением этого на «голом железе» в вашем приложении, поскольку вы отправляете и получаете только один символ за раз.

Когда вы вызываете __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE), вы говорите микроконтроллеру запускать прерывание, когда регистр данных пуст. Но процедура прерывания HAL не знает, что передавать, поэтому будет отправлять мусор, пока не подумает, что все закончилось.

Я думаю, что другая проблема может быть вызвана прямым доступом к вашему кольцевому буферу в нескольких местах, что приводит к конфликтам. Вам лучше вызывать свои функции HAL с помощью временного буфера (или указателя на один char), который либо берется из вашего циклического буфера, либо сохраняется в нем.


Основная функция

// 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;
}
person Ashley Miller    schedule 23.03.2021
comment
Я загрузил код на доску, пытаясь понять, почему после отправки одного символа «х» получается «хх». Это работает до 7 раз, когда я отправляю x, получаю xx, когда в восьмой раз я отправляю x, я получаю бесконечную строку «xxxxxxxxxxxx ...», которую я должен завершить с помощью сброса. Но когда я отправляю 7 символов за раз «ххххххх». заменяется 8 символами «xxxxxxxx». После отправки 8 символов «x» за раз получается бесконечная строка, которую я должен прервать сбросом. - person user15216933; 24.03.2021
comment
Какую часть исходного кода вы все еще используете? В вашем исходном сообщении у вас есть HAL_Uart_Transmit() внутри вашего обратного вызова RxCplt, так что он будет отправлять обратно каждый полученный символ. У вас также есть __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE) внутри uart_put_char, поэтому он начнет отправлять обратно неопределенный мусор всякий раз, когда вы помещаете что-то в свой буфер TX. - person Ashley Miller; 25.03.2021
comment
Я удалил его. Сейчас я просто анализирую ваш код. - person user15216933; 26.03.2021