橙猫猫缺大德 2024-03-29 18:47 采纳率: 50%
浏览 96

stm32G431RBT6的串口接收与回调问题

专家您好,我遇到这样一个问题,这是我的代码出现问题的两个函数(放到了提问的最下面),这是一个蓝桥杯嵌入式省赛会考的知识点,是检测串口传输数据内容是否正确的一个问题,首先我在回调函数里写了flag,每次接收数据后将tim4的cnt归零,并且把接收到的数据放到一个数组里面。然后我在void Usart_Proc(void)里面写了,当flag生效且tim4的计数大于15次(波特率为9600,系统频率为80MHZ,tim4的PSC为8000-1,所以计数15下大概为1.5ms,大于了传输一个字节的时间)的时候说明数据传输完毕,开始对if的内容进行判断,函数的功能是这样的

遇到的问题是:
问题1:你可以看到,我在回调函数里面写了这样两行代码:

    sprintf(str,"%c\n",rx_buffer);
    HAL_UART_Transmit(&huart1, (uint8_t *)str, strlen(str), 50);    //检测数据是否输入正常

这两行代码的意思是串口每接收到一个数据就将其传输出来,我平时需要在串口传输助手里面对其反复调试,这里出现的问题,我如果不开fifo的话,我在串口传输助手里面输入10个字符,程序只能检测出2~3个,而且存储进数组的顺序也经常混乱,于是我试着把这两行代码去掉让回调函数更简洁高效,然后就正常实现功能了,或者我打开fifo模式的情况下加上这两行代码也依然正常。请问这是为什么,是因为不加fifo的情况下,回调函数比较占用时间的时候会导致数据丢失吗??

问题2:你可以注意到,我的代码里用来判断数据传输完成的方法是人造了一个空闲中断,用TIM计数来实现的,理论上只需要计数11次后数据就传输完毕了,所以我设置的阈值是计数15次。但是我在实际操作过程中,我试着将判断数据是否传输完毕这里的TIM -> CNT > 15这里将15改成10甚至是0,那么按理说理想情况应该是由于人造空闲中断计数不够,导致函数无法判断串口的数据是否传输完毕,我在从串口调试助手里面输入“abc”时,它应该先error两次,然后再success对吧,但实际上测试发现,实际效果是大部分情况直接success,小概率情况会出现仅仅一次error,请问这是什么原因导致的呢?是计数器不够精准的原因,还是串口数据传输的中断优先级比主函数while里的函数void Usart_Proc(void)循环优先级高呢还是while循坏的不够快呢?
真心求问,如果有需要的话我可以提供源代码文件

下面的是我写的两个代码和相关变量声明

//串口显示专用变量
uint8_t str[21];
uint8_t rx_buffer=0;
uint8_t rx_val[10000];
int rx_flag=0;
int count_flag = 0;

void Usart_Proc(void)
{
    
        if(rx_flag == 1 && TIM4->CNT >15)    //判断数据是否传输完毕
        {
                if(rx_val[0] == 'a'&&rx_val[1] == 'b'&&rx_val[2] == 'c')
                {
                    sprintf(str,"success\n");
                    HAL_UART_Transmit(&huart1, (uint8_t *)str, strlen(str), 50);  //成功
                }
                else 
                {
                    sprintf(str,"error!\n");
                    HAL_UART_Transmit(&huart1, (uint8_t *)str, strlen(str), 50);  //失败
                }
                rx_flag = 0;
                for(int j = 0;j<=count_flag;j++)     //清空数组
                {
                    rx_val[j] = 0;
                }
                count_flag=0;    
        }
        
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    
    TIM4->CNT = 0;                     //计数器归零
    rx_flag=1;                         //flag
    rx_val[count_flag] = rx_buffer;    //数据存入数组中保存
    count_flag++;

    sprintf(str,"%c\n",rx_buffer);
    HAL_UART_Transmit(&huart1, (uint8_t *)str, strlen(str), 50);    //检测数据是否输入正常
    
    HAL_UART_Receive_IT(&huart1,&rx_buffer,1);
}


  • 写回答

4条回答 默认 最新

  • 码农阿豪@新空间 优质创作者: Java、后端开发技术领域 2024-03-29 18:47
    关注
    让阿豪来帮你解答,本回答参考chatgpt3.5编写提供,如果还有疑问可以评论或留言
    问题1: 代码中的sprint和HAL_UART_Transmit函数都是用来将数据通过串口传输出去的,而且都是在HAL_UART_RxCpltCallback回调函数中被调用的。如果不开启fifo模式,当接收到多个字符的时候,HAL_UART_RxCpltCallback回调函数在处理完第一个字符之后就会立刻被调用,而第二个字符则会被覆盖掉,因此在Usart_Proc函数中,只能检测到部分数据被正确传输,而且存储进数组的顺序会混乱。而开启fifo模式后,回调函数中的所有接收到的数据都会暂时存放在一个叫做“接收缓存区”的地方,直到数组全部填满或者超时之后才会触发HAL_UART_RxCpltCallback回调函数,并且所有的接收数据都会依次被传输出去,因此即使不调用sprint和HAL_UART_Transmit函数,在Usart_Proc函数中也能够正确判断所有数据是否传输完成。 问题2: 关于TIM -> CNT > 15的设置,具体数值是根据时钟频率和波特率算出来的,因此一旦修改时钟频率或者波特率,阈值就需要重新计算。如果将阈值降低,那么也就意味着在同样的计数时间内需要传输更多的数据,因此判断数据是否传输完成的准确性会受到影响。如果将阈值设为0,那么相当于没有判断数据是否传输完成,这样会导致程序无法正确判断串口的数据是否传输完毕,因此出现了只有一次error的情况。而实际上,这个问题可能还和程序中HAL_UART_Receive_IT函数使用的位置有关系。如果该函数在while循环内部调用,那么可能由于循环过快,导致串口接收数据的中断响应不够及时,从而影响了程序的正常运行。 下面是一份可能更优化的代码: #define BUF_SIZE 100 uint8_t rx_buffer; uint8_t rx_val[BUF_SIZE]; int count_flag = 0; int rx_flag = 0; void Usart_Proc(void) {
    if(rx_flag == 1) {
        rx_flag = 0;
        if(strncmp(rx_val, "abc", 3) == 0) { // 判断接收数据是否为"abc"
            HAL_UART_Transmit(&huart1, (uint8_t *)"success\n", 8, 50);
        } else {
            HAL_UART_Transmit(&huart1, (uint8_t *)"error!\n", 7, 50);
        }
        memset(rx_val, 0, sizeof(rx_val)); // 将接收缓存区清空
        count_flag = 0;
    }
    

    } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(count_flag < BUF_SIZE) { rx_val[count_flag++] = rx_buffer; // 将接收到的数据存入接收缓存区 HAL_UART_Receive_IT(&huart1, &rx_buffer, 1); // 开启下一次接收中断 if(count_flag == BUF_SIZE) { rx_flag = 1; // 接收缓存区已满,标记数据已接收完成 } } } int main() { // 初始化串口和计时器 HAL_UART_Receive_IT(&huart1, &rx_buffer, 1); HAL_TIM_Base_Start(&htim4); HAL_TIM_Base_Start_IT(&htim4); while(1) { Usart_Proc(); } return 0; } 这份代码有以下几个修改点: 1. 将接收缓存区的大小设置为BUF_SIZE,并在接收到数据后依次将数据存放在缓存区中。当缓存区已满时,标记数据已经接收完成,从而避免了判断数据是否接收完成的操作。接收到数据后,直接将其存入缓存区,不再对其进行打印输出等操作,从而保证代码的性能。 2. 将判断数据是否接收完成的操作移动到Usart_Proc函数中,每次接收到数据后都会进行判断。如果接收到的数据符合要求,就通过HAL_UART_Transmit函数向外输出"success";如果接收到的数据不符合要求,就通过HAL_UART_Transmit函数向外输出"error!"。 3. 在HAL_UART_RxCpltCallback回调函数中,每次接收到数据后会开启下一次接收中断。当接收缓存区已经满了以后,就标记数据已经接收完成,并不再接收下一次数据。由于标记数据是否接收完成的操作在Usart_Proc函数中进行,因此不需要单独设置阈值来判断数据是否接收完成。 总之,优化代码需要根据具体情况具体分析,结合实际应用场景来进行优化,不断调整和改进,才能使代码更加高效、稳定和可维护。

    评论

报告相同问题?

问题事件

  • 创建了问题 3月29日