1 基础知识点

1.1 串口中断种类

串口中断属于STM32本身的资源,不涉及到FreeRTOS,但可与FreeRTOS配合使用。

串口接收中断

中断标志为:USART_IT_RXNE,即rx none empty,串口只要接收到数据就触发中断,如果是接收一个字符串,则每接收到一个字符就触发一次中断。

串口空闲中断

中断标志为:USART_IT_IDLE,idle即空闲的意思,串口空闲时触发的中断,当然也不是说串口空闲时就一直触发中断,而实在每个连续的接收完成后,触发中断,如果是接收一个字符串,则接收完整个字符串后,触发一次中断。

所以,这两个中断可以配合使用,串口接收中断实时接收数据,接受完一串数据后,空闲中断被触发,就可以对接收的一串数据分析处理了。这种方式不需要知道每次字符串的具体长度,因而可以接收不定长的串口数据。

1.2 信号量

FreeRTOS中的信号量是一种任务间通信的方式,信号量包括:二值信号量、互斥信号量、计数信号量,本次只使用二值信号量。

二值信号量

二值信号量只有两种状态,可以先通俗的理解为它就是个标志,0或1。信号量用于任务间的同步,FreeRTOS是多任务系统,不同任务间可能需要某种同步关系,如串口中断接收完数据后,数据分析处理任务才能拿到数据进行分析,这就是一种同步。

信号量的基本操作有获取信号量和释放信号量,例如:数据分析处理任务需要处理串口数据时,可先尝试获取信号量,若获取不到,也就是信号量是0,则先进入阻塞等待,等待超时可先跳出,之后继续尝试获取信号量。串口空闲中断接受完一串数据后,可执行释放信号量操作,这时,数据分析处理任务就可以获取到信号量,进而可以处理串口数据了,实现了串口数据接收与数据处理的同步。

接下来的程序思路如下:

1.3 API函数

创建二值信号量xSemaphoreCreateBinary()

函数原型(tasks.c中):

SemaphoreHandle_t xSemaphoreCreateBinary( void )

返回值:SemaphoreHandle_t:创建成功的二值信号量句柄,失败返回NULL

释放信号量xSemaphoreGive()

函数原型(tasks.c中):

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )

参数:xSemaphore:要释放的信号量句柄

返回值:释放成功返回pdPASS,失败返回errQUEUE_FULL

释放信号量(中断函数中)xSemaphoreGiveFromISR()

BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,

BaseType_t* pxHigherPriorityTaskWoken)

参数:xSemaphore:同上

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

返回值:同上

获取信号量xSemaphoreTake()

函数原型(tasks.c中):

BaseType_t xSemaphoreTake( SemaphoreHandle_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 < size)//收到的数据长度在size范围内 {

//void *memcpy(void *str1, const void *str2, size_t n) //从存储区 str2 复制 n 个字节到存储区 str1。 memcpy(buf,Recv,rx_cnt);//有几个复制几个 count=rx_cnt;

//printf("%s\r\n", buf); }

else//收到的数据长度太长了 {

memcpy(buf,Recv,size);//只复制size个 count=size;

}

rx_cnt=0;

}

if(count>0)

{

count=0;

printf("receive:%s",buf);

//------------------------------------------------------------------------------ //这里可以继续对buf进行分析和处理,比如根据buf的不同内容执行不同的小任务

}

}

}

2.3 一个小应用

结合之前文章介绍的字符串操作的相关知识:码农爱学习:C语言字符串相关函数使用示例 strtok_r strstr strtok atoi​zhuanlan.zhihu.com

可以对“命令+参数”型的字符串数据进行处理。

//先判断指令名称char *cmd;//表示命令char *paras;//表示命令后的参数cmd = strtok_r((char*)buf, " ", &paras);

char *ret;

int i;

for (i = 0; i < N;i++)

{

ret = strstr(cmd, struct_dostr1[i].name);

if(ret!=NULL)

{

// printf("find cmd in funname[%d]\r\n", i); break;

}

}

//printf("i:%d\r\n",i);//printf("cmd:%s]\r\n", cmd);//printf("paras:%s\r\n", paras);if(i==N)

{

printf("can't find cmd in funname[]\r\n");

}

else

{

//是有效的指令,继续判断后续参数 char* para[4]={0};//限定最多接收4个参数

int j= 0;

while((para[j]=strtok(paras," "))!= NULL)//改成这样就可以了 {

j++;

paras=NULL;

if(j==4)

break;

}

printf("paras nums:%d\r\n",j);

if(j>0)printf("para[0]:%s\r\n", para[0]);

if(j>1)printf("para[1]:%s\r\n", para[1]);

if(j>2)printf("para[2]:%s\r\n", para[2]);

if(j>3)printf("para[3]:%s\r\n", para[3]);

//执行对应的函数 struct_dostr1[i].fun(para);

}

最后的函数执行,是通过定义一个结构体,将字符命令与函数指针对应起来:

#define N 2typedef 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​github.com

xsemaphoretake返回_FreeRTOS例程3-串口中断接收不定长的数据与二值信号量的使用相关推荐

  1. bufferedreader接收不到数据_FreeRTOS例程3-串口中断接收不定长的数据与二值信号量的使用

    1 基础知识点 1.1 串口中断种类 串口中断属于STM32本身的资源,不涉及到FreeRTOS,但可与FreeRTOS配合使用. 串口接收中断 中断标志为:USART_IT_RXNE,即rx non ...

  2. stm32串口空闲中断接收不定长数据

    串口空闲中断接收不定长数据 空闲中断是接受数据后出现一个byte的高电平(空闲)状态,就会触发空闲中断.并不是空闲就会一直中断,准确的说应该是上升沿(停止位)后一个byte,如果一直是低电平是不会触发 ...

  3. STM32使用串口空闲中断接收不定长数据帧-USART_IT_IDLE使用(不使用DMA方式)

    STM32使用串口空闲中断接收不定长数据帧-USART_IT_IDLE使用(不使用DMA方式) 前言 串口空闲中断介绍 清中断方法 串口中断处理函数 串口中断用到的全局变量定义 串口初始化(使能接收中 ...

  4. STM32单片机串口空闲中断接收不定长数据

    在使用单片机的串口通信功能时,常用的接收数据方法是通过固定的字节数来判断一帧数是否发送完成,或者是通过固定的结束标志位来表示一帧数据发送完成.但是有时候会遇到发送的数据长度不固定,也没有固定的结束标志 ...

  5. android 串口一直打开_STM32之串口DMA接收不定长数据

    STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...

  6. 第九章 AT32F403A基于V2库串口 dma接收不定长数据

    目录 概述 硬件 DMA 软件 流程 初始化 初始化代码: 中断服务函数: DMA1通道5设置函数:(重新使能通道) DMA1通道4发送函数:(设置dma长度和内存地址) 测试 最后 概述 本文主要是 ...

  7. STM32 HAL库 串口DMA接收不定长数据

    STM32 HAL库 串口DMA接收不定长数据 整体思路:我是用的CUBEMX软件生成的工程,使能了两个串口,串口2用来接收不定长的数据,串口1用来发送串口2接收到的数据:串口2我找了一个UBLOX卫 ...

  8. STM32LL库——USART中断接收不定长信息

    通用同步异步收发器(USART)既是串口,可以与电脑上的串口助手连接,是调试单片机的重要方式.双向通信至少需要2个引脚:接受数据输入(RX)和发送数据输出(TX). 首先在STM32CubeMX中配置 ...

  9. STM32H7 DMA USART空闲中断接收不定长数据

    1.关键设置 2.代码 2.1 bsp_usart.h /*********************************************************************** ...

  10. STM32单片机串口空闲中断+DMA接收不定长数据

    在上一篇文章STM32单片机串口空闲中断接收不定长数据中介绍了利用串口空闲中断接收不定长数据,这种方式有一个问题就是串口每接收到一个字节就会进入一次中断,如果发送的数据比较频繁,那么串口中断就会不停打 ...

最新文章

  1. c++学习笔记之构造函数
  2. 前端教程分享:HTTP请求Content-Type你知道是做什么的吗?
  3. 用小程序·云开发打造运动圈小程序丨实战
  4. Docker中级篇|深入探究Docker
  5. docker Harbor2.3.4 https 搭建镜像仓库
  6. hadoop使用combiner合并操作
  7. [Bootstrap]bootstrap的简单原理
  8. python中日志logging模块和异常捕获traceback模块的使用
  9. php 快速安装,PHP - 快速配置及安装
  10. 如何对xgboost模型进行优化
  11. sql数据库如何分离
  12. 攻击者利用7号信令(SS7)中的漏洞从德国银行偷取钱财
  13. 阿里云异步word转PDF
  14. sshv2 mitm工具jmitm2的使用记录
  15. 哗啦啦收银系统故障收集
  16. oracle语法基础
  17. 冬季冷色调效果Lr预设
  18. Layui页面元素之导航
  19. Unity:编辑器Editor常用命令
  20. daimayuan每日一题#812 互质

热门文章

  1. libubox组件(1)——usock
  2. 2014年6月5日 深圳 IBM 安全解决方案会议通知
  3. const VS readonly
  4. unity快速将旋转向量转换为四元数
  5. VS C#生成dll,C#和unity工程调用
  6. VS生产dll把双目追踪四个圆点计算的物体位姿给unity,在unity中实时变化
  7. Atitit 2018 技术趋势与没落技术总结 目录 1. 2018 技术雷达 1 1.1. HOSTED IDENTITY MANAGEMENT AS A SERVICE (SaaS)身份管理
  8. piap.windows io 监测attilax总结
  9. paip.XXListener is already configured监听器已经被配置的解决
  10. paip.手机ROOT过程总结