请选择 进入手机版 | 继续访问电脑版
点击联系客服
客服QQ:509006671 客服微信:mengfeiseo

兰州老站长

 找回密码
 立即注册
查看: 62|回复: 1

FreeRTOS例程3-使用串行端口中断接收长度不确定的数据和二进制信号量

[复制链接]

1

主题

1

帖子

-7

积分

限制会员

积分
-7
发表于 2021-3-6 12:26:07 | 显示全部楼层 |阅读模式
http://www  . Sina.com/http://www  . Sina.com/

串行端口中断是STM32本身的资源,不包含FreeRTOS,但可以与FreeRTOS一起使用。

1 基础知识点

中断标志为USART_IT_RXNE,即rx  none  empty。串行端口每次接收数据时都会触发中断,如果接收字符串,则每次接收字符时都会触发中断。

1.1 串口中断种类

中断标志:USART_IT_IDLE,IDLE处于空闲状态。串行端口空闲时触发的中断,以及串行端口空闲时并不总是触发中断。实际上,每次连续接收完成后都会触发中断,收到字符串后,会在收到整个字符串后触发中断。

因此,这两个中断可以同时使用。串行端口接收中断,实时接收数据,接受一系列数据后,将触发空闲中断,分析收到的一系列数据。该方法不需要知道每个字符串的确切长度,因此可以接收长度不确定的串行端口数据。

串口接收中断

FreeRTOS的信号量是任务之间的通信方法,包括二值信号量、互斥信号量、计数信号量等。这次只使用二值信号量。

串口空闲中断

二进制信号量只有两种状态,首先可以通俗地理解它是标志,0或1。信号量用于任务之间的同步,FreeRTOS可能需要在多个任务之间建立一种同步关系。例如,串行端口停止接收数据后,数据分析处理任务可以获取数据进行分析。这就是同步。

信号量的基本工作是获取信号量和解除信号量。例如,如果数据分析处理操作需要处理串行端口数据,则可以先尝试获得信号量。否则,即信号量为0时,可能会先进入阻塞大气,等待超时会先突出来。然后继续尝试获得信号量。串行端口空闲中断接受一系列数据后,可以执行信号量解除操作。这样就可以在数据分析处理操作中获得信号量,从而处理串行端口数据,3358www.sina.com/。

以下过程如下:





http://www  . Sina.com/http://www  . Sina.com/

函数原型(tasks.c中)3360

semaphore  handle  _ t  xsemaphorecreatebinary(void)

返回值:

SemaphoreHandle_t:创建成功的二进制信号器句柄。失败返回空值

1.2 信号量

函数原型(tasks.c中)3360

base  type  _ t  xsemaphoregive(semaphore  handle  _ t  xsemaphore)

参数:

XSemaphore:要关闭的信号量句柄

返回值:

释放成功时返回pdPASS,失败时返回errQUEUE_FULL

二值信号量

base  type  _ t  xsemaphoregivefromisr(semaphore  handle  _ t  xsemaphore,

base  type  _ t  * pxhigherprioritytaskwoken)

参数:

XSemaphore:如上所述

PxHigherPriorityTaskWoken:标记退出此函数后是否需要切换操作

返回值:

同一本书

实现了串口数据接收与数据处理的同步

函数原型(tasks.c中)3360

base  type  _ t  xsemaphoretake(semaphore  handle  _ t  xsemaphore,
       TickType_t xBlockTime)
参数:

  • xSemaphore:要释放的信号量句柄
  • xBlockTime:阻塞时间

    返回值:
  • 获取成功返回pdTRUE,失败返回pdFALSE

    获取信号量(中断函数中)xSemaphoreTakeFromISR()

    BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore,
                                     BaseType_t* pxHigherPriorityTaskWoken)
    参数:

  • xSemaphore:同上
  • pxHigherPriorityTaskWoken:标记退出此函数后是否需要进行任务切换

    返回值:
  • 同上

    2 编程要点
    2.1 串口中断与释放信号量
    串口配置时记得开启两个中断。
    //=======================================
    //初始化IO 串口1
    //bound:波特率
    //=======================================
    void uart_init(u32 bound)
    {
        //GPIO端口设置
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
        //串口1对应引脚复用映射
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
        //USART1端口配置
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度50MHz
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
        GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
        //USART1 初始化设置
        USART_InitStructure.USART_BaudRate = bound;//波特率设置
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
        USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
        USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
        USART_Init(USART1, &USART_InitStructure); //初始化串口1
        USART_Cmd(USART1, ENABLE);  //使能串口1
        USART_ClearFlag(USART1, USART_FLAG_TC);
    #if EN_USART1_RX
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
        USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
        //Usart1 NVIC 配置
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=8;//抢占优先级8
        NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;       //子优先级0
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能
        NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
    #endif
    }
    中断服务函数的串口空闲中断,清除标志位只能通过先读SR寄存器,再读DR寄存器清除!
    中断中使用信号量释放要使用ISR结尾的函数xSemaphoreGiveFromISR,否则程序就卡住了。

    //=======================================
    //串口1中断服务程序
    //=======================================
    void USART1_IRQHandler(void)                    
    {
        uint8_t data;//接收数据暂存变量
        BaseType_t xHigherPriorityTaskWoken;
        if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
        {
            data =USART_ReceiveData(USART1);            
            Recv[rx_cnt++]=data;//接收的数据存入接收数组
            USART_ClearITPendingBit(USART1,USART_IT_RXNE);
        }
        if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//空闲中断
        {
            if(uartSemaphore!=NULL)
            {
                //释放二值信号量
                xSemaphoreGiveFromISR(uartSemaphore,&xHigherPriorityTaskWoken); //释放二值信号量
            }
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
            data = USART1->SR;//串口空闲中断的中断标志只能通过先读SR寄存器,再读DR寄存器清除!
            data = USART1->DR;
            //USART_ClearITPendingBit(USART1,USART_IT_IDLE);//这种方式无效
            //rx_cnt=0;
         }
    }
    2.2 获取信号量
    编写一个任务来实现串口数据的获取,该任务不断尝试获取信号量,获取成功后,对数据进行处理。
    获取信号量xSemaphoreTake,阻塞(等待时间)10ms,获取不到信号量则向下执行,每个任务都是一个死循环,马上又会进行信号量获取。

    //打印任务函数
    void print_task(void *pvParameters)
    {
        int count=0;
        BaseType_t err = pdFALSE;
        int size=50;
        uint8_t buf[64];//最多只取前64个数据
        //清空本地接收数组
        memset(buf,0,size);
        while(1)
        {
            err=xSemaphoreTake(uartSemaphore,10);   //获取信号量
            if(err==pdTRUE)                         //获取信号量成功
            {  
                //printf("%s",Data);
                if(rx_cnt 0)
            {
                count=0;
                printf("receive:%s",buf);
                //------------------------------------------------------------------------------
                //这里可以继续对buf进行分析和处理,比如根据buf的不同内容执行不同的小任务
            }
        }
    }
    2.3 一个小应用
    结合之前文章介绍的字符串操作的相关知识:C语言字符串相关函数使用示例 strtok_r strstr strtok atoi,可以对“命令+参数”型的字符串数据进行处理。
    //先判断指令名称
    char *cmd;//表示命令
    char *paras;//表示命令后的参数
    cmd = strtok_r((char*)buf, " ", &paras);//这里有点小问题,不带参数的命令,后面需要一个空格
    char *ret;
    int i;
    for (i = 0; i
    最后的函数执行,是通过定义一个结构体,将字符命令与函数指针对应起来:
    #define N 2
    typedef struct struct_dostr
    {
    char name[32];
    int (*fun)(char *argv[]);
    }struct_dostr;
    struct_dostr struct_dostr1[N]={
    {"hello",hello},
    {"led",  led},  
    };
    int hello(char* p[])
    {
        printf("hello~~~~~~~~~~\r\n");
        return 0;
    }
    int led(char* p[])
    {
        int p0,p1;
        p0=atoi(p[0]);
        p1=atoi(p[1]);
        printf("get led: %d, %d\r\n",p0,p1);
        return 0;
    }
    3 实验结果
    通过串口发送hello或led 80 5,可以看到想要的处理结果:

    receive:hello  
    hello~~~~~~~~~~
    receive:led 80 5
    get led: 80, 5
    完整工程代码已保存至GitHub:  
    https://github.com/xxpcb/FreeRTOS-STM32F407-examples
  • 回复

    使用道具 举报

    0

    主题

    69

    帖子

    -52

    积分

    限制会员

    积分
    -52
    发表于 2021-3-6 21:17:40 | 显示全部楼层
    LZ说的很不错
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|无图版|手机版|小黑屋|兰州@IT精英团

    GMT+8, 2021-4-14 19:14 , Processed in 0.083694 second(s), 25 queries .

    Powered by Discuz! X3.4

    Copyright © 2021, Tencent Cloud.

    快速回复 返回顶部 返回列表