[CW32L083系列] CW32L083串口中断+定时器实现不定长数据接收

[复制链接]
 楼主| lulugl 发表于 2023-7-11 22:32 | 显示全部楼层 |阅读模式
#申请原创# #有奖活动#[url=home.php?mod=space&uid=760190]@21小跑堂 [/url]
CW32L083的UART是没有空闲中断的,这样给接收数据帧带来一定的不便。比如我们需要用的AT指令,那么判断接收完一条完整的指令,可以用串口断+结尾\r\n来判断接收完一条完整指令。但是这个方法有一个缺点就是返回两个\r\n 时就会处理起来非常麻烦。
这是我用串口中断+定时器来实现 如空闲中断的接收,而且可以灵活的处理接收最后一个数据后的延时来判是否接收完整一条指令。
【实现方法】
  • 初始化串口5,开配置波特率为115200,开启接收中断。

staticvoidSerialInit(uint32_tBaudRate)
{
    uint32_tPCLK_Freq;
    GPIO_InitTypeDefGPIO_InitStructure= {0};
    UART_InitTypeDefUART_InitStructure= {0};
    PCLK_Freq=SystemCoreClock>>pow2_table[CW_SYSCTRL->CR0_f.HCLKPRS];
    PCLK_Freq>>=pow2_table[CW_SYSCTRL->CR0_f.PCLKPRS];
    // 调试串口使用UART5
    //  PB8->TX
    //  PB9<-RX
    // 时钟使能
    __RCC_GPIOB_CLK_ENABLE();
    __RCC_UART5_CLK_ENABLE();
    // 先设置UART TX RX 复用,后设置GPIO的属性,避免口线上出现毛刺
    PB08_AFx_UART5TXD();
    PB09_AFx_UART5RXD();
    GPIO_InitStructure.Pins=GPIO_PIN_8;
    GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.Pins=GPIO_PIN_9;
    GPIO_InitStructure.Mode=GPIO_MODE_INPUT;
    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
    UART_InitStructure.UART_BaudRate=BaudRate;
    UART_InitStructure.UART_Over=UART_Over_16;
    UART_InitStructure.UART_Source=UART_Source_PCLK;
    UART_InitStructure.UART_UclkFreq=PCLK_Freq;
    UART_InitStructure.UART_StartBit=UART_StartBit_FE;
    UART_InitStructure.UART_StopBits=UART_StopBits_1;
    UART_InitStructure.UART_Parity=UART_Parity_No;
    UART_InitStructure.UART_HardwareFlowControl=UART_HardwareFlowControl_None;
    UART_InitStructure.UART_Mode=UART_Mode_Rx|UART_Mode_Tx;
    UART_Init(CW_UART5, &UART_InitStructure);
    //优先级,无优先级分组
    NVIC_SetPriority(UART2_UART5_IRQn, 0);
    //UARTx中断使能
    NVIC_EnableIRQ(UART2_UART5_IRQn);
}
2. 定义一个结构体来储存串口的数据:
typedefstruct_uartx_infor{
  uint8_trx_cnt;
  uint8_ttx_cnt;
  uint8_trx_buff[UART_LEN_MAX];
  uint8_ttx_buff[UART_LEN_MAX];
  FunctionalState rx_state; //接收状态
} uartx_infor;
3.初始化GTIM1定时器,定义为一次计时,主频为64M,所以配置16分频 预载值为40000,从而实现10毫秒溢出产生中断。
/* 初始化GTIM定时1 创建10ms的中断*/
voidinit_gtim1(void)
{
    GTIM_InitTypeDefGTIM_InitStruct= {0};
    __RCC_GTIM1_CLK_ENABLE();   // GTIM1时钟使能

    __disable_irq();
    NVIC_EnableIRQ(GTIM1_IRQn);
    __enable_irq();
    GTIM_InitStruct.Mode=GTIM_MODE_TIME;
    GTIM_InitStruct.OneShotMode=GTIM_COUNT_ONESHOT; //只运行一次
    GTIM_InitStruct.Prescaler=GTIM_PRESCALER_DIV16;
    GTIM_InitStruct.ReloadValue=40000UL-1;    // PWM频率为 64M/16/ 4M =100Hz
    GTIM_InitStruct.ToggleOutState=DISABLE;
    GTIM_TimeBaseInit(CW_GTIM1, &GTIM_InitStruct);
    GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE);
    //GTIM_Cmd(CW_GTIM1, ENABLE);
}
4. 定义串口中断函数如下,主要的思想就是触发接收中断后,停止定时器的运行,重装ARR值,再开启定时器,同时把接收到的数据放入缓冲区。如果接收大于最大缓冲区,则接收值归零。
/**
* @brief This funcation handles UART2
*/
voidUART2_UART5_IRQHandler(void)
{
    /* USER CODE BEGIN */
    uint8_trx_data;
    if(UART_GetITStatus(CW_UART5, UART_IT_RC) !=RESET)
    {
        uart5_infor.rx_state=DISABLE;
        rx_data=UART_ReceiveData_8bit(CW_UART5);
        if(uart5_infor.rx_cnt<UART_LEN_MAX)
        {
            uart5_infor.rx_buff[uart5_infor.rx_cnt] =rx_data;
            CW_GTIM1->CR0_f.EN=0;
            CW_GTIM1->ARR=40000-1;
            CW_GTIM1->CR0_f.EN=1;
        }
        else
        {
            uart5_infor.rx_cnt=0;
            memset(uart5_infor.rx_buff, 0, UART_LEN_MAX);
        }
        uart5_infor.rx_cnt++;
        UART_ClearITPendingBit(CW_UART5, UART_IT_RC);
    }
    /* USER CODE END */
}
5. 定时器函数为,如果触发中断,清除中断标志,把接收完整数据值置为真:
/**
* @brief This funcation handles GTIM1
*/
voidGTIM1_IRQHandler(void)
{
    /* USER CODE BEGIN */
    if(GTIM_GetITStatus(CW_GTIM1, GTIM_IT_OV))
    {
        GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);
        uart5_infor.rx_state=ENABLE;
    }
    /* USER CODE END */
}
6. 在主函数中,我们判断接收状态是否为真,如果为真则打印出接收的数值,并且重置串口数据。
while (1)
    {
        if(uart5_infor.rx_state==ENABLE)
        {
            printf("uart5 recv cnt:%d\r\n", uart5_infor.rx_cnt);
            printf("uart5 recv cnt:%s\r\n", uart5_infor.rx_buff);
            uart5_infor_init();
        }
        // GPIO_TogglePin(CW_GPIOC, GPIO_PIN_2);
        // rt_thread_mdelay(500);
        rt_thread_mdelay(10);
    }
【实验效果
我们发送不定长数据,正确的从串口返回数据:
48fe6ae349bb00fc592e4babc88aa014
5152fe373432eef0bc873f5f5ca4637d
【总结】
用定时器与串口中断结合起来,完美的实现了串口的不定长接收。在接AT指令方面非常有用。

2004222 发表于 2023-7-19 23:36 | 显示全部楼层
很好很好啊
 楼主| lulugl 发表于 2023-7-20 07:22 | 显示全部楼层

感谢大佬的关注,与肯定,我会继续努力的!
tpgf 发表于 2023-8-10 16:29 | 显示全部楼层
如何判断一个包里边的数据已经接收完成了呢
八层楼 发表于 2023-8-10 17:19 | 显示全部楼层
当数据完成接收时候会有相应的标志位吗
观海 发表于 2023-8-10 17:44 | 显示全部楼层
可不可以采取等待一定时间后停止接收呢
 楼主| lulugl 发表于 2023-8-10 17:54 | 显示全部楼层
八层楼 发表于 2023-8-10 17:19
当数据完成接收时候会有相应的标志位吗

voidGTIM1_IRQHandler(void)
{
    /* USER CODE BEGIN */
    if(GTIM_GetITStatus(CW_GTIM1, GTIM_IT_OV))
    {
        GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);
        uart5_infor.rx_state=ENABLE;
    }
    /* USER CODE END */
}
 楼主| lulugl 发表于 2023-8-10 17:55 | 显示全部楼层
观海 发表于 2023-8-10 17:44
可不可以采取等待一定时间后停止接收呢

这个就是采用这个原理,接收完以后超过设定时间就视为没有数据发过来了。
guanjiaer 发表于 2023-8-10 17:59 | 显示全部楼层
还有一个方式就是可以自己定制协议里边的具体内容
 楼主| lulugl 发表于 2023-8-10 18:12 | 显示全部楼层
guanjiaer 发表于 2023-8-10 17:59
还有一个方式就是可以自己定制协议里边的具体内容

那个要一个一个判断,也可以,各有所长。对于一些特定的场景需要不定的接收,然后再做间判断。
heimaojingzhang 发表于 2023-8-10 18:18 | 显示全部楼层
数据可以按照一包一包的来发送吗?
 楼主| lulugl 发表于 2023-8-10 19:06 | 显示全部楼层
heimaojingzhang 发表于 2023-8-10 18:18
数据可以按照一包一包的来发送吗?

这里是说接收,发送的话会一个包发送完。
keaibukelian 发表于 2023-8-11 09:08 | 显示全部楼层
当一个包数据发送的时候在包尾的时候串口会自动添加结束标志吗
 楼主| lulugl 发表于 2023-8-12 08:11 | 显示全部楼层
keaibukelian 发表于 2023-8-11 09:08
当一个包数据发送的时候在包尾的时候串口会自动添加结束标志吗

这个需要看看串口的通讯协议。发送一个数据后,会有一个停止位。
chenqianqian 发表于 2023-8-12 12:36 来自手机 | 显示全部楼层
中断发送和接收 效率最高。
 楼主| lulugl 发表于 2023-8-12 18:03 | 显示全部楼层
chenqianqian 发表于 2023-8-12 12:36
中断发送和接收 效率最高。

对的,关键占用CPU时间少。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

180

主题

830

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部