搜索
12下一页
返回列表 发新帖本帖赏金 10.00元(功能说明)我要提问

[uCOS/RTOS] 基于FreeRTOS的UART空闲中断框架设计

[复制链接]
736|22
 楼主 | 2021-3-9 21:53 | 显示全部楼层 |阅读模式
本帖最后由 caizhiwei 于 2021-3-9 22:16 编辑

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

  2. /*用于空闲中断判断*/
  3. typedef struct
  4. {
  5. uint16_t uart_cnt;
  6. uint16_t timer_cnt;
  7. }stcUART_Idle;

  8. extern stcUART_Idle UART_Idle;
复制代码

2. 串口部分代码:
  1. /**********************************************************************************************
  2.     *函数功能:初始化UART
  3.     *UARTx:  选择初始化UART端口号
  4.     *Parity: 奇偶校验位
  5.     *说明IO用使用复位模式2,DMA默认是使能
  6. **********************************************************************************************
  7. */
  8. void BSP_UARTx_Init(M0P_UART_TypeDef *UARTx, uint32_t baud, en_uart_mmdorck_t Parity)
  9. {
  10.          if(UARTx == M0P_UART0)
  11.          {                    
  12.                 Uart0_init(baud,Parity);
  13.                 EnableNvic(UART0_IRQn, IrqLevel3, TRUE);       ///<系统中断使能                  
  14.          }
  15.          if(UARTx == M0P_UART1)
  16.          {                        
  17.                 EnableNvic(UART1_IRQn, IrqLevel3, TRUE);           ///<系统中断使能        
  18.          }        
  19. }   


  20. //串口0模块配置
  21. static void Uart0_init(uint32_t baud, en_uart_mmdorck_t Parity)
  22. {   
  23.         stc_gpio_cfg_t stcGpioCfg;
  24.     stc_uart_cfg_t  stcCfg;
  25.     stc_uart_baud_t stcBaud;
  26.         
  27.     DDL_ZERO_STRUCT(stcGpioCfg);
  28.     DDL_ZERO_STRUCT(stcCfg);
  29.     DDL_ZERO_STRUCT(stcBaud);
  30.         
  31.     Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE); //GPIO外设模块时钟使能
  32.    
  33.     stcGpioCfg.enDir = GpioDirOut;
  34.     Gpio_Init(GpioPortA,GpioPin9,&stcGpioCfg);
  35.     Gpio_SetAfMode(GpioPortA,GpioPin9,GpioAf1); //配置PA09 为UART0 TX
  36.     stcGpioCfg.enDir = GpioDirIn;
  37.     Gpio_Init(GpioPortA,GpioPin10,&stcGpioCfg);
  38.     Gpio_SetAfMode(GpioPortA,GpioPin10,GpioAf1);//配置PA10 为UART0 RX

  39.     Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);//UART0外设模块时钟使能
  40.         
  41.         stcCfg.enRunMode = UartMskMode3;     //模式3
  42.     if(Parity == UartMskEven)
  43.         {
  44.                 stcCfg.enMmdorCk = UartMskEven;      //偶校验
  45.         }
  46.         else if(Parity == UartMskOdd)
  47.         {
  48.                 stcCfg.enMmdorCk = UartMskOdd;       //奇校验
  49.         }
  50.         else
  51.         {
  52.                 stcCfg.enRunMode = UartMskMode1;     //模式1,奇偶检验无效
  53.         }
  54.     stcCfg.enStopBit = UartMsk1bit;      //1位停止位
  55.     stcCfg.stcBaud.u32Baud = baud;       //波特率9600
  56.     stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;         //通道采样分频配置
  57.     stcCfg.stcBaud.u32Pclk = Sysctrl_GetPClkFreq();    //获得外设时钟(PCLK)频率值
  58.     Uart_Init(M0P_UART0, &stcCfg);       //串口初始化

  59.     Uart_ClrStatus(M0P_UART0,UartRC);    //清接收请求
  60.     Uart_ClrStatus(M0P_UART0,UartTC);    //清发送请求
  61.     Uart_EnableIrq(M0P_UART0,UartRxIrq); //使能串口接收中断
  62.     //Uart_EnableIrq(M0P_UART0,UartTxIrq); //使能串口发送中断                     
  63.         //使能DMA发送, DMA相关通道使能后,如果Tx Buff为空,会立马启动传输
  64.         Uart_EnableFunc(M0P_UART0,UartDmaTxFunc);  
  65. }
复制代码
3. 编写UART中断函数
386060477e9a94e7d.png

在这里采用了循环数组接收,没有使用队列,可以省点资源,效果差不多,数组处理更方便。

3. Timer定时器,这里选用2ms周期中断,并通过UART中断中启动,在Timer中断中关闭。
  1. #include "bsp_timer.h"

  2. #include "bsp_uart.h"


  3. SemaphoreHandle_t BinSem_UART_Idle;


  4. //Timer3 配置,用于uart0 的空闲中断
  5. void BSP_Timer3_init(uint16_t u16Period)
  6. {
  7.     uint16_t                    u16ArrValue;
  8.     uint16_t                    u16CntValue;
  9.     stc_tim3_mode0_cfg_t     stcTim3BaseCfg;   
  10.     //结构体初始化清零
  11.     DDL_ZERO_STRUCT(stcTim3BaseCfg);
  12.    
  13.     Sysctrl_SetPeripheralGate(SysctrlPeripheralTim3, TRUE); //Base Timer外设时钟使能
  14.    
  15.     stcTim3BaseCfg.enWorkMode = Tim3WorkMode0;              //定时器模式
  16.     stcTim3BaseCfg.enCT       = Tim3Timer;                  //定时器功能,计数时钟为内部PCLK
  17.     stcTim3BaseCfg.enPRS      = Tim3PCLKDiv32;              //PCLK/32
  18.     stcTim3BaseCfg.enCntMode  = Tim316bitArrMode;           //自动重载16位计数器/定时器
  19.     stcTim3BaseCfg.bEnTog     = FALSE;
  20.     stcTim3BaseCfg.bEnGate    = FALSE;
  21.     stcTim3BaseCfg.enGateP    = Tim3GatePositive;
  22.    
  23.     Tim3_Mode0_Init(&stcTim3BaseCfg);                       //TIM3 的模式0功能初始化        
  24.     u16ArrValue = 0x10000 - u16Period ;
  25.     Tim3_M0_ARRSet(u16ArrValue);                            //设置重载值(ARR = 0x10000 - 周期)  
  26.     u16CntValue = 0x10000 - u16Period;   
  27.     Tim3_M0_Cnt16Set(u16CntValue);                          //设置计数初值   
  28.     Tim3_ClearIntFlag(Tim3UevIrq);                          //清中断标志        
  29.     Tim3_Mode0_EnableIrq();                                 //使能TIM3中断(模式0时只有一个中断)        
  30.     EnableNvic(TIM3_IRQn, IrqLevel3, TRUE);                 //TIM3 开中断
  31.         //Tim3_M0_Run();   //TIM3 运行
  32. }

  33. /*去初始化,进低功耗功耗前调用此接口*/
  34. void BSP_Timer3_Deinit(void)
  35. {
  36.         stc_tim3_mode0_cfg_t     stcTim3BaseCfg;   
  37.     DDL_ZERO_STRUCT(stcTim3BaseCfg);                         //结构体初始化清零
  38.         Tim3_Mode0_Init(&stcTim3BaseCfg);   
  39.         Tim3_ClearIntFlag(Tim3UevIrq);                //清中断标志        
  40.     Tim3_Mode0_DisableIrq();
  41.         Tim3_M0_Stop();
  42. }
复制代码
32329604780e2d29bd.png

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

98555604780f137d35.png
实验效果:
98076047810369058.png

源码整理后的附件:
游客,如果您要查看本帖隐藏内容请回复


使用特权

评论回复

打赏榜单

21小跑堂 打赏了 10.00 元 2021-03-10
理由:恭喜通过原创文章审核!请多多加油哦!

相关帖子

 楼主 | 2021-3-9 22:12 | 显示全部楼层
@21小跑堂
申请原创,哈哈

使用特权

评论回复
| 2021-3-9 22:27 | 显示全部楼层
谢谢分享

使用特权

评论回复
| 2021-3-12 09:05 | 显示全部楼层
谢谢!

使用特权

评论回复
| 2021-3-12 09:07 | 显示全部楼层
挺实用的一个帖子

使用特权

评论回复
| 2021-3-12 10:53 | 显示全部楼层
没有IDLE空闲中断的MCU有福了

使用特权

评论回复
| 2021-3-13 16:03 | 显示全部楼层
来学习学习

使用特权

评论回复
| 2021-3-13 20:09 | 显示全部楼层
谢谢楼主

使用特权

评论回复
| 2021-3-14 07:42 | 显示全部楼层
谢谢楼主!

使用特权

评论回复
| 2021-3-14 11:09 | 显示全部楼层
这个方法不错, 学习下

使用特权

评论回复
| 2021-3-14 15:58 | 显示全部楼层
不错。。。。。。。。。。。

使用特权

评论回复
| 2021-3-15 10:36 | 显示全部楼层
来,学习学习

使用特权

评论回复
| 2021-3-15 16:10 | 显示全部楼层
学习使用

使用特权

评论回复
| 2021-3-16 17:56 | 显示全部楼层
let me see !

使用特权

评论回复
| 2021-3-17 09:30 | 显示全部楼层
看看 学习学习

使用特权

评论回复
| 2021-3-17 14:21 | 显示全部楼层
11111111111111111111111

使用特权

评论回复
| 2021-3-17 14:48 | 显示全部楼层
路过,看看

使用特权

评论回复
| 2021-3-21 17:53 | 显示全部楼层
挺好的例子,学习一下

使用特权

评论回复
| 2021-3-23 12:02 | 显示全部楼层

挺好的例子,学习一下

使用特权

评论回复
| 2021-3-23 16:04 | 显示全部楼层
看起来挺有用的

使用特权

评论回复
扫描二维码,随时随地手机跟帖
12下一页
返回列表 发新帖 本帖赏金 10.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 我要提问 投诉建议 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

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