bufferedreader接收不到数据_FreeRTOS例程3-串口中断接收不定长的数据与二值信号量的使用
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, " ", ¶s);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 實驗結果
通過串口發送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總結
以上是生活随笔為你收集整理的bufferedreader接收不到数据_FreeRTOS例程3-串口中断接收不定长的数据与二值信号量的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我国东风导弹摧毁预警机标靶
- 下一篇: 辽宁号航母出海一次,要花多少钱?