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复用为USART1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1//USART1端口配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度50MHzGPIO_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); //初始化串口1USART_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;//抢占优先级8NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;       //子优先级0NVIC_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("%srn", 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]rn", i);break;}
}
//printf("i:%drn",i);
//printf("cmd:%s]rn", cmd);
//printf("paras:%srn", paras);
if(i==N)
{printf("can't find cmd in funname[]rn");
}
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:%drn",j);if(j>0)printf("para[0]:%srn", para[0]);if(j>1)printf("para[1]:%srn", para[1]);if(j>2)printf("para[2]:%srn", para[2]);if(j>3)printf("para[3]:%srn", para[3]);//执行对应的函数struct_dostr1[i].fun(para);
}

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

#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~~~~~~~~~~rn");return 0;
}int led(char* p[])
{int p0,p1;p0=atoi(p[0]);p1=atoi(p[1]);printf("get led: %d, %drn",p0,p1);return 0;
}

3 实验结果

通过串口发送helloled 80 5,可以看到想要的处理结果:

receive:hello
hello~~~~~~~~~~
receive:led 80 5
get led: 80, 5

完整工程代码已保存至GitHub:

https://github.com/xxpcb/FreeRTOS-STM32F407-examples​github.com

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

  1. STM32使用串口中断接收HWT101的数据

    因研究生阶段项目需求,需要采用HWT101给单片机提供角度信息. HWT101是维特智能公司下的一款角度传感器,它可以通过计算角速度测量绕Z轴旋转的水平方向的偏航角,具有高精度.几乎无漂移.不受磁场干 ...

  2. STM32G070RBT6基于STM32CubeMX创建串口中断接收数据

    STM32G070RBT6基于STM32CubeMX创建串口中断接收数据

  3. STM32串口中断接收HAL库超详解析

    之前一直用的固件库跑c8t6和zet6,现在有幸接触到stm32H743用到HAL库,学习嘛都是踩坑,csdn,然后...理解底层库的代码 本人有幸遇到了H7的串口中断接收只进去一次的Bug,于是马上 ...

  4. STM32 HAL库 串口DMA(收发)和STM32串口中断接收(接收时间管理机制)+ESP8266 wifi模组通信问题

    一.HAL库 串口 DMA+ESP8266模组通信问题 用STM32 HAL库串口的DMA发送和空闲中断接收处理数据,单片机发送AT指令给ESP8266 wifi模组问题:单片机连续几次给wifi模组 ...

  5. stc 串口收发 c语言,STC12C5A60S2 串口中断接收程序

    原标题:STC12C5A60S2 串口中断接收程序 #define UART0_BUF_LEN 32 int UART1_Recv_count; //接收计数 bit UART1_Overflow_F ...

  6. STM32串口中断接收实验

    STM32串口中断接收实验的详细说明 准备 代码实现 总结 准备 材料:STM32F407ZGT6最小系统板,串口1通过跳线帽连接到了CH340上. 需求:从电脑向板子的串口1发送一个字符串(以回车和 ...

  7. 多串口接收数据并显示-自定义串口通信类-调用委托显示数据-保存数据-实例:10串口接收数据并显示加保存

    当时搜了很久,没咋都到合适的思路,就自己整理一份吧 大致实现的功能: 通过串口接收数据,进行解析,然后显示,或者保存数据: 主要:多个串口接收显示都能用: 大致思路: 1.新建一个类SerialPor ...

  8. STM32串口中断接收帧数据并返回给上位机总结(配合MAX3483)

    一.前言 这是我的第一篇CSDN,记录一些代码总结,一方面与大家分享交流,另一方面方便以后再次使用能够快速回忆,再就是提高自身写作水平.如有错误之处,欢迎各位大佬批评指正. 二.所涉及的芯片 1.ST ...

  9. python串口连续数据_Python代码从串口连续接收可变数据

    通常,您与微通信所做的工作是将单个字符用于轻量级或创建通信协议.基本上你有一个开始标志,结束标志和某种校验和,以确保数据正确传输.有很多方法可以做到这一点. 以下代码适用于Python 3.您可能必须 ...

最新文章

  1. python将一个数组纵向切割_python – 对如何切割numpy数组感到困惑
  2. Oracle中dbms_job包的使用
  3. 牛客网 在线编程 之字形矩阵打印
  4. log4net异步写入日志_微信支付万亿日志在Hermes中的实践
  5. 阿里云Link TEE获得全球首款GlobalPlatform TEE全配置安全认证
  6. A quick presentation of the Visual Studio 2010 editions per role
  7. Linux下 RabbitMQ的安装与配置
  8. jqueryMobile模块整理—按钮(buttons)
  9. Android Studio的Project有某个项目,Build Variants却没有,如何添加物已有项目?
  10. Python 项目打包
  11. 三菱plc控制步进电机实例_三菱FX3U的plc通过手摇轮,如何手动控制步进电机
  12. 关于被隐藏的文件夹无法去掉隐藏的属性
  13. lattice planner 规划详解
  14. 基础类库,注释,异常,集合,泛型
  15. 谷歌浏览器如何给长网页截图?
  16. Adguard Home最低DNS处理时间配置
  17. DL在地球物理中的应用及发展趋势
  18. 当地图与绘画结合,竟然能迸发出这样的精彩
  19. 叭叭一下Servlet的虚拟路径的映射
  20. 为什么要学好数学,计算机与数学的关系,学习数学的过程

热门文章

  1. java判断点与线与面的关系_VC++开发GIS系统(280)判断点与面的拓扑关系
  2. Java反射基础:获取Class对象的三种方式
  3. 【收藏】Vue中ref和$refs的介绍及使用
  4. npm查看指定包的所有版本
  5. golang管道channel的基本使用及注意事项:往管道中存取数据
  6. Scala中的/,%,++,--
  7. Linux tail命令
  8. MySQL列转行sql语句
  9. Spring SpEL表达式
  10. FFmpeg windows下载安装