在固定间隔后通过STM32(Bluepill)接收到固定间隔后的角色

发布于 2025-01-30 06:58:04 字数 2600 浏览 5 评论 0原文

我试图通过UART1从Atmega 328p不断地发送一个“ Hello”字符串到Bluepill板。我正在通过中断进行接收。 问题是几次后,它开始在我的串行显示器上给“ Ello”“ Hello”,我无法弄清楚什么问题。 波特率-9600 奇偶校验 - 无 stop bit-1

main.c:

usart_irq在此处称为“ USART代码在此处开始”下:

#include "main.h"
#include "uart.h"
UART_HandleTypeDef huart1;
char *readBuffer;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);

int main(void)
{
 HAL_Init();
 SystemClock_Config();
 MX_GPIO_Init();
  MX_USART1_UART_Init();

 /* USER CODE BEGIN 2 */
  __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE END 2 */

接收到的字符串使用ReadString存储在“ dechBuffer”中,并使用使用该串行监视器打印到序列显示我创建了一个单独的UART库。

while (1)
  {
    /* USER CODE END WHILE */

      readBuffer=serialReadString1();
      writeString1(readBuffer);
      HAL_Delay(500);

    /* USER CODE BEGIN 3 */
  }

usart1_irqhandler

我启用了全局中断,并在那里称为接收函数,并使用了返回,因为我不想使用hal_irq:

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
 receivestring1();
 return;
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

uart.c

写字符串:

#include"main.h"
#include "uart.h"
#define RX_BUFFER_SIZE1 144

char rxBuffer1[RX_BUFFER_SIZE1];
uint16_t rx1ReadPos;


void sendChar1( uint8_t ch)
{
  while(!(USART1->SR & USART_SR_TXE));
  USART1->DR = ch;
}


void writeString1(const char c[])
{
    uint16_t i = 0;
        for (i = 0; i<= strlen(c); i++)
        {
            sendChar1(c[i]);
        }
        memset(rxBuffer1,0,RX_BUFFER_SIZE1);
        rx1ReadPos = 0;
}

用于阅读 :细绳:

 unsigned char receivedChar1(void)
    {
       while(!(USART1->SR & USART_SR_RXNE));//check whether any tx is going on
       return USART1->DR;
    }
    
    void receivestring1()
    {
        while(!(USART1->SR & USART_SR_RXNE));//check whether any tx is going on
        rxBuffer1[rx1ReadPos] = USART1->DR;
    
                if (rxBuffer1[rx1ReadPos] != '\n')
                {
                    rx1ReadPos++;
                    if (rx1ReadPos >= RX_BUFFER_SIZE1)
                    {
                        rx1ReadPos = 0;
                    }
                }
    }
    
    char* serialReadString1()
    {
        return rxBuffer1;
    }

I am trying to send a string "hello" continiously through UART1 from Atmega 328P to the BLUEPILL board. I am doing the reception by by interrupt.
The problem is after few times it starts to give "ello" "hello" alternatively on my serial monitor, I am not able to figure out whats going wrong.
BAUD RATE-9600
parity bit- NONE
STop bit- 1

main.c:

The usart_IRQ is called here under "usart code begin here":

#include "main.h"
#include "uart.h"
UART_HandleTypeDef huart1;
char *readBuffer;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);

int main(void)
{
 HAL_Init();
 SystemClock_Config();
 MX_GPIO_Init();
  MX_USART1_UART_Init();

 /* USER CODE BEGIN 2 */
  __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE END 2 */

The string received is stored in the "readBuffer" using the readstring and the printed to the serial monitor using the writestring forwhich i created a separate UART library.

while (1)
  {
    /* USER CODE END WHILE */

      readBuffer=serialReadString1();
      writeString1(readBuffer);
      HAL_Delay(500);

    /* USER CODE BEGIN 3 */
  }

USART1_IRQHandler

I have enabled the Global interrupt and called the received function there, and used the return because I dont want to use the HAL_irq:

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
 receivestring1();
 return;
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

uart.c

for write string:

#include"main.h"
#include "uart.h"
#define RX_BUFFER_SIZE1 144

char rxBuffer1[RX_BUFFER_SIZE1];
uint16_t rx1ReadPos;


void sendChar1( uint8_t ch)
{
  while(!(USART1->SR & USART_SR_TXE));
  USART1->DR = ch;
}


void writeString1(const char c[])
{
    uint16_t i = 0;
        for (i = 0; i<= strlen(c); i++)
        {
            sendChar1(c[i]);
        }
        memset(rxBuffer1,0,RX_BUFFER_SIZE1);
        rx1ReadPos = 0;
}

for read string:

 unsigned char receivedChar1(void)
    {
       while(!(USART1->SR & USART_SR_RXNE));//check whether any tx is going on
       return USART1->DR;
    }
    
    void receivestring1()
    {
        while(!(USART1->SR & USART_SR_RXNE));//check whether any tx is going on
        rxBuffer1[rx1ReadPos] = USART1->DR;
    
                if (rxBuffer1[rx1ReadPos] != '\n')
                {
                    rx1ReadPos++;
                    if (rx1ReadPos >= RX_BUFFER_SIZE1)
                    {
                        rx1ReadPos = 0;
                    }
                }
    }
    
    char* serialReadString1()
    {
        return rxBuffer1;
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

在风中等你 2025-02-06 06:58:04

我可以在您的代码中发现几个潜在问题:

  1. 您不应该跳过hal_uart_irqhandler()usart1_irqhandler()中的调用。通常需要在中断控制器(NVIC)和/或相应的外围单元(在您的情况下)中中断(已清除)。如果没有这样做,您可能会立即将中断处理程序再次召唤,尽管没有真正的新事件。

  2. while(!(usart1-&gt; sr&amp; usart_sr_rxne)))receed> receed> receed> receed char1() and code> rechivestring1()的开头。不需要必要,因为只有在RX不为空时才能生成中断。您的中断配置看起来正确,因此仅由于问题1(见上文)可能需要这是必要的。

  3. 变量rxbuffer1rx1ReadPosevevesivestring1() and code> writestring1()中都操纵。由于rechivestring1()是从usart1_irqhandler()调用writestring1()main(Main()调用此操作实际上可以并行发生。这可能会成为问题,原因有两个:

    • 编译器可能不知道,rxBuffer1的内容可能会在任何情况下发生变化(例如,在处理 for for witteString1() loop时>)。可以通过将volatile关键字添加到rxbuffer1rx1ReadPos以及writestring1()的参数。这样,编译器将重新评估strlen(c) loop的的每个通过中。
    • 该代码仍然可能尝试更改rx1ReadPos几乎同时,例如,之前的那一刻,当evereivestring1()收到新字符时rx1ReadPoswritestring1()中重置为零,这将导致失去该字符。这称为A 种族条件,只能通过在有限的时间内阻止中断,或者以确保该并行执行不能导致数据损失的方式来避免。一种可能的解决方案可能是使用两个单独的缓冲区 - 一个用于接收和一个用于写作的缓冲区。


  4. writestring1()是通过sendchar1()将字符写入接收数据的同一UART1。这主要不是可以的,但是发送数据是使用相同的慢速9600波特速率。这真的很可能是,writestring1()usart1_irqhandler()中断,即收到其他数据。最好将配置为更高BAUD率的其他UART用于调试目的。

我想您的“ Ello”问题主要由问题3引起。

附录

以检查STM32是否正确接收Atmega的字符,可以通过直接从中断处理程序发送每个接收的字符来简化代码(避免异步缓冲区处理的复杂性)。
这可能是这样的:

int main(void)
{
  [...]
  while(1)
  {
    // everything is done in IRQ handler
  }
}

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  unsigned char newChar;
  newChar = USART1->DR; // read received char
  USART1->DR = newChar; // send it out again right away

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

Addendum#2

通过请求是草图如何改进缓冲区的方法:

char rxBuffer1[RX_BUFFER_SIZE1];
char rxBuffer2[RX_BUFFER_SIZE1];
char* volatile rxBufferPtr = rxBuffer1;
volatile bool SwitchBufferRequest = false;

void writeString1(const char buffer[])
{
    for (int i = 0; i<= strlen(buffer); i++)
    {
        sendChar1(buffer[i]);
    }
    memset(buffer,0,RX_BUFFER_SIZE1);
}

void receivestring1()
{
    static uint16_t rxBufferPos = 0;

    if (SwitchBufferRequest)
    {
        if (rxBufferPtr == rxBuffer1)
        {
            rxBufferPtr = rxBuffer2;
        }
        else
        {
            rxBufferPtr = rxBuffer1;
        }
        rxBufferPos = 0;
        SwitchBufferRequest = false;
    }
    
    rxBufferPtr[rxBufferPos] = USART1->DR;
    
    if (rxBufferPos < (RX_BUFFER_SIZE1 - 2)) // buffer full?
    {
        rxBufferPos++;
    }
}
    
char* serialReadString1()
{
    char* rxBufferWithData = rxBufferPtr;

    SwitchBufferRequest = true;
    while (SwitchBufferRequest == true) {;} // wait for buffer switch
    return rxBufferWithData;
}

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  receivestring1();
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

int main(void)
{
  [...]
  while (1)
  {
    /* USER CODE END WHILE */

      readBuffer=serialReadString1();
      writeString1(readBuffer);
      HAL_Delay(500);

    /* USER CODE BEGIN 3 */
  }
}

注:
此草图试图尽可能少地修改代码。关键想法是:

  1. 使用两个单独的缓冲区(rxbuffer1rxbuffer2) - 一个用于接收数据,一个用于再次将接收到的数据发送。
  2. 切换中需要新数据时,请切换缓冲区
  3. 每次需要在main() serialReadString1()中请求缓冲区 。缓冲区切换本身是在ISR内完成的,以避免任何种族条件。
  4. 注意serialReadString1()中的操作顺序:首先,在switchbufferrequest设置为true 因为rxbufferptr可能会在此后的任何时间更改。
  5. writestring1()仅通过删除rxbufferpos(现在在ISR中完成)的重置并清除Rx Buffer,而不是不是 使用, 。 RX ISR。

I can spot several potential issues in your code:

  1. You should not skip the call of HAL_UART_IRQHandler() in USART1_IRQHandler(). Each interrupt usually needs to be acknowledged (cleared) in either the interrupt controller (NVIC) and/or the respective peripheral unit (the UART in your case). If this is not done, you might end up with the interrupt handler being called again right away, although there has not been a real new event.

  2. The while(!(USART1->SR & USART_SR_RXNE)) at the start of receivedChar1() and receivestring1() should not be necessary, since the interrupt should only be generated if the RX is not empty. Your interrupt configuration looks correct, so this might be only necessary due to issue #1 (see above).

  3. The variables rxBuffer1and rx1ReadPos are manipulated both in receivestring1() and writeString1(). Since receivestring1() is called from USART1_IRQHandler() while writeString1() is called from main() this manipulation can happen virtually in parallel. This can become problematic for two reasons:

    • The compiler might not be aware, the content of rxBuffer1 might change at any (e.g. also while processing the for loop in writeString1()). This can be avoided by adding the volatile keyword to rxBuffer1, rx1ReadPos and also the argument of writeString1(). This way the compiler will re-evaluate strlen(c) on every pass of the for loop.
    • The code might still try to change rx1ReadPos at virtually the same time, e.g. when a new character is received by receivestring1() just in the moment before rx1ReadPos is reset to zero in writeString1(), which would lead to losing that character. This is called a race-condition and can only be avoided by either blocking interrupts for a limited time or by constructing the code in a way that makes sure, that parallel execution can not lead to data-loss. One possible solution could be the use of two separate buffers - one used for receiving and one used for writing.
  4. writeString1() is writing the characters via sendChar1() to the same UART1 that is receiving the data. This is not a principally okay but sending the data is thus using the same slow 9600 baud-rate. This makes it really probable, that writeString1() gets interrupted by USART1_IRQHandler(), i.e. that additional data is received. It might be better to use a different UART configured to a higher baud-rate for debug purposes.

I suppose that your "ello" problem is primarily caused by issue #3.

Addendum

To check whether the stm32 correctly receives the characters from the Atmega you could simplify your code by sending out each received character directly from the interrupt handler (avoiding the complexity of the asynchronous buffer handling).
This could look like this:

int main(void)
{
  [...]
  while(1)
  {
    // everything is done in IRQ handler
  }
}

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  unsigned char newChar;
  newChar = USART1->DR; // read received char
  USART1->DR = newChar; // send it out again right away

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

Addendum #2

By request here is a sketch how the buffer handling could be improved:

char rxBuffer1[RX_BUFFER_SIZE1];
char rxBuffer2[RX_BUFFER_SIZE1];
char* volatile rxBufferPtr = rxBuffer1;
volatile bool SwitchBufferRequest = false;

void writeString1(const char buffer[])
{
    for (int i = 0; i<= strlen(buffer); i++)
    {
        sendChar1(buffer[i]);
    }
    memset(buffer,0,RX_BUFFER_SIZE1);
}

void receivestring1()
{
    static uint16_t rxBufferPos = 0;

    if (SwitchBufferRequest)
    {
        if (rxBufferPtr == rxBuffer1)
        {
            rxBufferPtr = rxBuffer2;
        }
        else
        {
            rxBufferPtr = rxBuffer1;
        }
        rxBufferPos = 0;
        SwitchBufferRequest = false;
    }
    
    rxBufferPtr[rxBufferPos] = USART1->DR;
    
    if (rxBufferPos < (RX_BUFFER_SIZE1 - 2)) // buffer full?
    {
        rxBufferPos++;
    }
}
    
char* serialReadString1()
{
    char* rxBufferWithData = rxBufferPtr;

    SwitchBufferRequest = true;
    while (SwitchBufferRequest == true) {;} // wait for buffer switch
    return rxBufferWithData;
}

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  receivestring1();
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

int main(void)
{
  [...]
  while (1)
  {
    /* USER CODE END WHILE */

      readBuffer=serialReadString1();
      writeString1(readBuffer);
      HAL_Delay(500);

    /* USER CODE BEGIN 3 */
  }
}

Note:
This sketch tries to modify your code as little as possible. The key ideas are:

  1. Use two separate buffers (rxBuffer1 and rxBuffer2) -- one for receiving data and one for sending received data out again.
  2. Buffers are switched each time new data is needed in main()
  3. Buffer switching is requested in serialReadString1() via the flag SwitchBufferRequest. Buffer switching itself is done within the ISR to avoid any race conditions.
  4. Note the order of operations in serialReadString1(): First the pointer to the latest received data is copied in a separate variable before SwitchBufferRequest is set to true because rxBufferPtr might change at any time afterwards.
  5. writeString1() is only modified slightly by removing the reset of rxBufferPos (now done in ISR) and clearing the RX buffer which is not used by the RX ISR.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文