<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

FreeRTOS串口中斷接收不定長的數據與二值信號量的使用

碼農愛學習 ? 來源:碼農愛學習 ? 作者:碼農愛學習 ? 2022-09-26 09:02 ? 次閱讀

基礎知識點

串口中斷種類

串口中斷屬于STM32本身的資源,不涉及到FreeRTOS,但可與FreeRTOS配合使用。

串口接收中斷

中斷標志為:USART_IT_RXNE,即rx none empty,串口只要接收到數據就觸發中斷,如果是接收一個字符串,則每接收到一個字符就觸發一次中斷。

串口空閑中斷

中斷標志為:USART_IT_IDLE,idle即空閑的意思,串口空閑時觸發的中斷,當然也不是說串口空閑時就一直觸發中斷,而實在每個連續的接收完成后,觸發中斷,如果是接收一個字符串,則接收完整個字符串后,觸發一次中斷。

所以,這兩個中斷可以配合使用,串口接收中斷實時接收數據,接受完一串數據后,空閑中斷被觸發,就可以對接收的一串數據分析處理了。這種方式不需要知道每次字符串的具體長度,因而可以接收不定長的串口數據。

信號

FreeRTOS中的信號量是一種任務間通信的方式,信號量包括:二值信號量、互斥信號量、計數信號量,本次只使用二值信號量。

二值信號量

二值信號量只有兩種狀態,可以先通俗的理解為它就是個標志,0或1。信號量用于任務間的同步,FreeRTOS是多任務系統,不同任務間可能需要某種同步關系,如串口中斷接收完數據后,數據分析處理任務才能拿到數據進行分析,這就是一種同步。

信號量的基本操作有獲取信號量釋放信號量,例如:數據分析處理任務需要處理串口數據時,可先嘗試獲取信號量,若獲取不到,也就是信號量是0,則先進入阻塞等待,等待超時可先跳出,之后繼續嘗試獲取信號量。串口空閑中斷接受完一串數據后,可執行釋放信號量操作,這時,數據分析處理任務就可以獲取到信號量,進而可以處理串口數據了,實現了串口數據接收與數據處理的同步。

接下來的程序思路如下:

pYYBAGMwYwaAMgtaAAD5P19tOnw458.png

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:標記退出此函數后是否需要進行任務切換

返回值:

同上

編程要點

串口中斷與釋放信號量

串口配置時記得開啟兩個中斷。

//=======================================
//初始化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;
	}
} 

獲取信號量

編寫一個任務來實現串口數據的獲取,該任務不斷嘗試獲取信號量,獲取成功后,對數據進行處理。

獲取信號量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的不同內容執行不同的小任務

		}
	}
}

一個小應用

結合之前文章介紹的字符串操作的相關知識:,可以對“命令+參數”型的字符串數據進行處理。

//先判斷指令名稱
char *cmd;//表示命令
char *paras;//表示命令后的參數
cmd = strtok_r((char*)buf, " ", ?s);//這里有點小問題,不帶參數的命令,后面需要一個空格

char *ret;
int i;
for (i = 0; i < N;i++)
{
    ret = strstr(struct_dostr1[i].name, cmd);
    if(ret!=NULL)
    {
//        printf("find cmd in funname[%d]rn", i);
        break;
    }
}
if(i==N)
{
    printf("can't find cmd in funname[]rn");
}
else
{				
    //是有效的指令,繼續判斷后續參數
    char* para[4]={0};//限定最多接收4個參數
    para[0] = strtok(paras, " ");
    int j= 1;
    while(paras != NULL)//這里有點小問題,不可以提前結束
    {
        para[j++] = strtok(NULL, " ");
        if(j==4)
            break;
    }

    //執行對應的函數
    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;
}

實驗結果

通過串口發送helloled 80 5,可以看到想要的處理結果:

receive:hello  

hello~~~~~~~~~~

receive:led 80 5

get led: 80, 5

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 串口
    +關注

    關注

    14

    文章

    1492

    瀏覽量

    74580
  • FreeRTOS
    +關注

    關注

    12

    文章

    473

    瀏覽量

    61402
收藏 人收藏

    評論

    相關推薦

    FreeRTOS串口DMA收發不定長數據

    FreeRTOS例程,介紹串口DMA收發不定長數據
    的頭像 發表于 09-26 09:08 ?3717次閱讀
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>串口</b>DMA收發<b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    CW32L083串口中斷+定時器實現不定長數據接收

    CW32L083 用串口中斷加定時器中斷實現串口不定長數據接收,特別適用于AT指令的
    的頭像 發表于 07-12 09:00 ?1124次閱讀
    CW32L083<b class='flag-5'>串口中斷</b>+定時器實現<b class='flag-5'>不定長</b><b class='flag-5'>數據</b><b class='flag-5'>接收</b>

    freertos串口接收數據后如何發送給任務?

    最大長度? 如果用信號量,是不是就等于是中斷程序接收完后發送信號量通知任務處理數組?這樣的
    發表于 04-18 06:36

    stm32串口是如何實現接收不定長度數據的呢

    stm32串口是如何實現接收不定長度數據的呢?串口接收數據一般會采用
    發表于 08-11 08:18

    不定長數據接收的原理是什么?怎么實現串口數據不定長接收?

    不定長數據接收的原理是什么?怎么實現串口數據不定長接收
    發表于 11-16 08:11

    如何利用IDLE中斷進行串口不定長數據接收

    利用IDLE中斷進行串口不定長數據接收有何優勢?如何利用IDLE中斷進行
    發表于 12-08 07:04

    如何利用串口空閑中斷接收不定長數據

    在上一篇文章STM32單片機串口空閑中斷接收不定長數據中介紹了利用串口空閑
    發表于 02-22 07:34

    Freertos串口中斷中釋放信號量,線程捕捉不到是為什么?

    Freertos串口中斷中釋放信號量,線程捕捉不到
    發表于 10-15 10:40

    stm32 串口接收不定長度數據及黏包處理 + 串口DMA接收

    ,那么stm32串口是如何實現接收不定長度數據的呢? 串口接收數據一般會采用
    發表于 12-23 19:09 ?26次下載
    stm32 <b class='flag-5'>串口</b><b class='flag-5'>接收</b><b class='flag-5'>不定長度數據</b>及黏包處理 + <b class='flag-5'>串口</b>DMA<b class='flag-5'>接收</b>

    STM32 DMA串口接收不定長數據

    STM32 DMA串口接收不定長數據
    發表于 12-24 18:50 ?40次下載
    STM32  DMA<b class='flag-5'>串口</b><b class='flag-5'>接收</b><b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    STM32之串口DMA接收不定長數據

    在使用stm32或者其他單片機的時候,會經常使用到串口通訊,那么如何有效地接收數據呢?假如這段數據不定長的有如何高效
    發表于 12-24 19:03 ?30次下載
    STM32之<b class='flag-5'>串口</b>DMA<b class='flag-5'>接收</b><b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    STM32單片機串口空閑中斷+DMA接收不定長數據

    在上一篇文章STM32單片機串口空閑中斷接收不定長數據中介紹了利用串口空閑
    發表于 12-27 19:24 ?18次下載
    STM32單片機<b class='flag-5'>串口</b>空閑<b class='flag-5'>中斷</b>+DMA<b class='flag-5'>接收</b><b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    單片機接收不定長數據,最優解是DMA+串口空閑中斷

    如果單片機不支持串口空閑中斷和DMA,可以參考之前寫的,串口只用接收中斷,完成不定長的分包。這里
    發表于 12-28 19:26 ?22次下載
    單片機<b class='flag-5'>接收</b><b class='flag-5'>不定長</b>的<b class='flag-5'>數據</b>,最優解是DMA+<b class='flag-5'>串口</b>空閑<b class='flag-5'>中斷</b>

    使用UART IDLE中斷接收不定長數據

    在本文中,將介紹使用該中斷來進行不定長串口數據接收的辦法。通過該中斷,可以省卻用于檢測數據傳輸是
    發表于 02-08 15:29 ?4次下載
    使用UART IDLE<b class='flag-5'>中斷</b><b class='flag-5'>接收</b><b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    使用UART IDLE中斷接收不定長數據

    使用UART IDLE中斷接收不定長數據
    的頭像 發表于 09-18 15:41 ?632次閱讀
    使用UART IDLE<b class='flag-5'>中斷</b><b class='flag-5'>接收</b><b class='flag-5'>不定長</b><b class='flag-5'>數據</b>
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>