caizhiwei 发表于 2021-3-9 21:53

基于FreeRTOS的UART空闲中断框架设计

本帖最后由 caizhiwei 于 2021-3-9 22:16 编辑

#申请原创#
基于FreeRTOS的UART空闲中断框架设计
设计背景:   针对大部分国产低端MCU(ARM-CortexM0)来说,并没有空闲中断,此时就需要一个定时器Timer配合来完成此任务。对于UART接受不定长数据,空闲中断还是非常实用的!知识点:FreeRTOS的二值信号量 Timer UART空闲中断的原理:   IDLE中断叫空闲中断,不叫帧中断。那么什么叫空闲,怎么定义空闲呢?在实际发送数据的时候,比如一串字符串,其实发送的两个字符之间间隔非常短,所以在两个字符之间不叫空闲。空闲的定义是总线上在一个字节的时间内没有再接收到数据,空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。而总线在什么情况时,会有一个字节时间内没有接收到数据呢?一般就只有一个数据帧发送完成的情况,所以串口的空闲中断也叫帧中断。
开发环境:Win10,MDK5.28,HC32L136设计步骤:
    这里不做长篇大论,列举了重要的核心部分讲解,便于大家移植。附件中带有完整的工程代码。首先定义一个结构体和信号量。extern SemaphoreHandle_t AT_RX_Semaphore;

/*用于空闲中断判断*/
typedef struct
{
uint16_t uart_cnt;
uint16_t timer_cnt;
}stcUART_Idle;

extern stcUART_Idle UART_Idle;
2. 串口部分代码:/**********************************************************************************************
    *函数功能:初始化UART
    *UARTx:选择初始化UART端口号
    *Parity: 奇偶校验位
    *说明IO用使用复位模式2,DMA默认是使能
**********************************************************************************************
*/
void BSP_UARTx_Init(M0P_UART_TypeDef *UARTx, uint32_t baud, en_uart_mmdorck_t Parity)
{
         if(UARTx == M0P_UART0)
         {                  
                Uart0_init(baud,Parity);
                EnableNvic(UART0_IRQn, IrqLevel3, TRUE);       ///<系统中断使能                  
         }
         if(UARTx == M0P_UART1)
         {                        
                EnableNvic(UART1_IRQn, IrqLevel3, TRUE);         ///<系统中断使能      
         }      
}   


//串口0模块配置
static void Uart0_init(uint32_t baud, en_uart_mmdorck_t Parity)
{   
      stc_gpio_cfg_t stcGpioCfg;
    stc_uart_cfg_tstcCfg;
    stc_uart_baud_t stcBaud;
      
    DDL_ZERO_STRUCT(stcGpioCfg);
    DDL_ZERO_STRUCT(stcCfg);
    DDL_ZERO_STRUCT(stcBaud);
      
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE); //GPIO外设模块时钟使能
   
    stcGpioCfg.enDir = GpioDirOut;
    Gpio_Init(GpioPortA,GpioPin9,&stcGpioCfg);
    Gpio_SetAfMode(GpioPortA,GpioPin9,GpioAf1); //配置PA09 为UART0 TX
    stcGpioCfg.enDir = GpioDirIn;
    Gpio_Init(GpioPortA,GpioPin10,&stcGpioCfg);
    Gpio_SetAfMode(GpioPortA,GpioPin10,GpioAf1);//配置PA10 为UART0 RX

    Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);//UART0外设模块时钟使能
      
      stcCfg.enRunMode = UartMskMode3;   //模式3
    if(Parity == UartMskEven)
      {
                stcCfg.enMmdorCk = UartMskEven;      //偶校验
      }
      else if(Parity == UartMskOdd)
      {
                stcCfg.enMmdorCk = UartMskOdd;       //奇校验
      }
      else
      {
                stcCfg.enRunMode = UartMskMode1;   //模式1,奇偶检验无效
      }
    stcCfg.enStopBit = UartMsk1bit;      //1位停止位
    stcCfg.stcBaud.u32Baud = baud;       //波特率9600
    stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;         //通道采样分频配置
    stcCfg.stcBaud.u32Pclk = Sysctrl_GetPClkFreq();    //获得外设时钟(PCLK)频率值
    Uart_Init(M0P_UART0, &stcCfg);       //串口初始化

    Uart_ClrStatus(M0P_UART0,UartRC);    //清接收请求
    Uart_ClrStatus(M0P_UART0,UartTC);    //清发送请求
    Uart_EnableIrq(M0P_UART0,UartRxIrq); //使能串口接收中断
    //Uart_EnableIrq(M0P_UART0,UartTxIrq); //使能串口发送中断                     
      //使能DMA发送, DMA相关通道使能后,如果Tx Buff为空,会立马启动传输
      Uart_EnableFunc(M0P_UART0,UartDmaTxFunc);
}3. 编写UART中断函数

在这里采用了循环数组接收,没有使用队列,可以省点资源,效果差不多,数组处理更方便。
3. Timer定时器,这里选用2ms周期中断,并通过UART中断中启动,在Timer中断中关闭。#include "bsp_timer.h"

#include "bsp_uart.h"


SemaphoreHandle_t BinSem_UART_Idle;


//Timer3 配置,用于uart0 的空闲中断
void BSP_Timer3_init(uint16_t u16Period)
{
    uint16_t                  u16ArrValue;
    uint16_t                  u16CntValue;
    stc_tim3_mode0_cfg_t   stcTim3BaseCfg;   
    //结构体初始化清零
    DDL_ZERO_STRUCT(stcTim3BaseCfg);
   
    Sysctrl_SetPeripheralGate(SysctrlPeripheralTim3, TRUE); //Base Timer外设时钟使能
   
    stcTim3BaseCfg.enWorkMode = Tim3WorkMode0;            //定时器模式
    stcTim3BaseCfg.enCT       = Tim3Timer;                  //定时器功能,计数时钟为内部PCLK
    stcTim3BaseCfg.enPRS      = Tim3PCLKDiv32;            //PCLK/32
    stcTim3BaseCfg.enCntMode= Tim316bitArrMode;         //自动重载16位计数器/定时器
    stcTim3BaseCfg.bEnTog   = FALSE;
    stcTim3BaseCfg.bEnGate    = FALSE;
    stcTim3BaseCfg.enGateP    = Tim3GatePositive;
   
    Tim3_Mode0_Init(&stcTim3BaseCfg);                     //TIM3 的模式0功能初始化      
    u16ArrValue = 0x10000 - u16Period ;
    Tim3_M0_ARRSet(u16ArrValue);                            //设置重载值(ARR = 0x10000 - 周期)
    u16CntValue = 0x10000 - u16Period;   
    Tim3_M0_Cnt16Set(u16CntValue);                        //设置计数初值   
    Tim3_ClearIntFlag(Tim3UevIrq);                        //清中断标志      
    Tim3_Mode0_EnableIrq();                                 //使能TIM3中断(模式0时只有一个中断)      
    EnableNvic(TIM3_IRQn, IrqLevel3, TRUE);               //TIM3 开中断
      //Tim3_M0_Run();   //TIM3 运行
}

/*去初始化,进低功耗功耗前调用此接口*/
void BSP_Timer3_Deinit(void)
{
      stc_tim3_mode0_cfg_t   stcTim3BaseCfg;   
    DDL_ZERO_STRUCT(stcTim3BaseCfg);                         //结构体初始化清零
      Tim3_Mode0_Init(&stcTim3BaseCfg);   
      Tim3_ClearIntFlag(Tim3UevIrq);                //清中断标志      
    Tim3_Mode0_DisableIrq();
      Tim3_M0_Stop();
}

UART和Timer如何配合使用,上面的函数已经给出了。最后,中断中已经给出了信号量,后续如何处理呢?用一个任务去接收信号就好了:

实验效果:
源码整理后的附件:**** Hidden Message *****

caizhiwei 发表于 2021-3-9 22:12

@21小跑堂
申请原创,哈哈

zhdm 发表于 2021-3-9 22:27

谢谢分享

〽️〽️〽️ 发表于 2021-3-12 09:05

谢谢!

ccd007 发表于 2021-3-12 09:07

挺实用的一个帖子

gyh974 发表于 2021-3-12 10:53

没有IDLE空闲中断的MCU有福了

wifi99 发表于 2021-3-13 16:03

来学习学习

Thomas717 发表于 2021-3-13 20:09

谢谢楼主

goodfriends2004 发表于 2021-3-14 07:42

谢谢楼主!

lawesly 发表于 2021-3-14 11:09

这个方法不错, 学习下{:handshake:}

lfc315 发表于 2021-3-14 15:58

不错。。。。。。。。。。。

keke 发表于 2021-3-15 10:36

来,学习学习

starsfu 发表于 2021-3-15 16:10

学习使用

brosoon 发表于 2021-3-16 17:56

let me see !

sleaven 发表于 2021-3-17 09:30

看看 学习学习

qj984229477 发表于 2021-3-17 14:21

11111111111111111111111

bbschen 发表于 2021-3-17 14:48

路过,看看

kk的回忆 发表于 2021-3-21 17:53

挺好的例子,学习一下

lcfmax 发表于 2021-3-23 12:02


挺好的例子,学习一下

maryshy 发表于 2021-3-23 16:04

看起来挺有用的
页: [1] 2 3
查看完整版本: 基于FreeRTOS的UART空闲中断框架设计