基礎(chǔ)知識(shí)點(diǎn)
串口中斷種類
串口中斷屬于STM32本身的資源,不涉及到FreeRTOS,但可與FreeRTOS配合使用。
串口接收中斷
中斷標(biāo)志為:USART_IT_RXNE,即rx none empty,串口只要接收到數(shù)據(jù)就觸發(fā)中斷,如果是接收一個(gè)字符串,則每接收到一個(gè)字符就觸發(fā)一次中斷。
串口空閑中斷
中斷標(biāo)志為:USART_IT_IDLE,idle即空閑的意思,串口空閑時(shí)觸發(fā)的中斷,當(dāng)然也不是說(shuō)串口空閑時(shí)就一直觸發(fā)中斷,而實(shí)在每個(gè)連續(xù)的接收完成后,觸發(fā)中斷,如果是接收一個(gè)字符串,則接收完整個(gè)字符串后,觸發(fā)一次中斷。
所以,這兩個(gè)中斷可以配合使用,串口接收中斷實(shí)時(shí)接收數(shù)據(jù),接受完一串?dāng)?shù)據(jù)后,空閑中斷被觸發(fā),就可以對(duì)接收的一串?dāng)?shù)據(jù)分析處理了。這種方式不需要知道每次字符串的具體長(zhǎng)度,因而可以接收不定長(zhǎng)的串口數(shù)據(jù)。
信號(hào)量
FreeRTOS中的信號(hào)量是一種任務(wù)間通信的方式,信號(hào)量包括:二值信號(hào)量、互斥信號(hào)量、計(jì)數(shù)信號(hào)量,本次只使用二值信號(hào)量。
二值信號(hào)量
二值信號(hào)量只有兩種狀態(tài),可以先通俗的理解為它就是個(gè)標(biāo)志,0或1。信號(hào)量用于任務(wù)間的同步,F(xiàn)reeRTOS是多任務(wù)系統(tǒng),不同任務(wù)間可能需要某種同步關(guān)系,如串口中斷接收完數(shù)據(jù)后,數(shù)據(jù)分析處理任務(wù)才能拿到數(shù)據(jù)進(jìn)行分析,這就是一種同步。
信號(hào)量的基本操作有獲取信號(hào)量和釋放信號(hào)量,例如:數(shù)據(jù)分析處理任務(wù)需要處理串口數(shù)據(jù)時(shí),可先嘗試獲取信號(hào)量,若獲取不到,也就是信號(hào)量是0,則先進(jìn)入阻塞等待,等待超時(shí)可先跳出,之后繼續(xù)嘗試獲取信號(hào)量。串口空閑中斷接受完一串?dāng)?shù)據(jù)后,可執(zhí)行釋放信號(hào)量操作,這時(shí),數(shù)據(jù)分析處理任務(wù)就可以獲取到信號(hào)量,進(jìn)而可以處理串口數(shù)據(jù)了,實(shí)現(xiàn)了串口數(shù)據(jù)接收與數(shù)據(jù)處理的同步。
接下來(lái)的程序思路如下:
API函數(shù)
創(chuàng)建二值信號(hào)量xSemaphoreCreateBinary()
函數(shù)原型(tasks.c中):
SemaphoreHandle_t xSemaphoreCreateBinary( void )
返回值:
SemaphoreHandle_t:創(chuàng)建成功的二值信號(hào)量句柄,失敗返回NULL
釋放信號(hào)量xSemaphoreGive()
函數(shù)原型(tasks.c中):
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )
xSemaphore:要釋放的信號(hào)量句柄
返回值:
釋放成功返回pdPASS,失敗返回errQUEUE_FULL
釋放信號(hào)量(中斷函數(shù)中)xSemaphoreGiveFromISR()
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t* pxHigherPriorityTaskWoken)
參數(shù):
xSemaphore:同上
pxHigherPriorityTaskWoken:標(biāo)記退出此函數(shù)后是否需要進(jìn)行任務(wù)切換
返回值:
同上
獲取信號(hào)量xSemaphoreTake()
函數(shù)原型(tasks.c中):
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,
TickType_t xBlockTime)
參數(shù):
xSemaphore:要釋放的信號(hào)量句柄
xBlockTime:阻塞時(shí)間
返回值:
獲取成功返回pdTRUE,失敗返回pdFALSE
獲取信號(hào)量(中斷函數(shù)中)xSemaphoreTakeFromISR()
BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t* pxHigherPriorityTaskWoken)
參數(shù):
xSemaphore:同上
pxHigherPriorityTaskWoken:標(biāo)記退出此函數(shù)后是否需要進(jìn)行任務(wù)切換
返回值:
同上
編程要點(diǎn)
串口中斷與釋放信號(hào)量
串口配置時(shí)記得開(kāi)啟兩個(gè)中斷。
//=======================================
//初始化IO 串口1
//bound:波特率
//=======================================
void uart_init(u32 bound)
{
//GPIO端口設(shè)置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1時(shí)鐘
//串口1對(duì)應(yīng)引腳復(fù)用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9復(fù)用為USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10復(fù)用為USART1
//USART1端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9與GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復(fù)用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復(fù)用輸出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
//USART1 初始化設(shè)置
USART_InitStructure.USART_BaudRate = bound;//波特率設(shè)置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長(zhǎng)為8位數(shù)據(jù)格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個(gè)停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//無(wú)奇偶校驗(yàn)位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無(wú)硬件數(shù)據(jù)流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發(fā)模式
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);//開(kāi)啟相關(guān)中斷
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=8;//搶占優(yōu)先級(jí)8
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子優(yōu)先級(jí)0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據(jù)指定的參數(shù)初始化VIC寄存器
#endif
}
中斷服務(wù)函數(shù)的串口空閑中斷,清除標(biāo)志位只能通過(guò)先讀SR寄存器,再讀DR寄存器清除!
中斷中使用信號(hào)量釋放要使用ISR結(jié)尾的函數(shù)xSemaphoreGiveFromISR
,否則程序就卡住了。
//=======================================
//串口1中斷服務(wù)程序
//=======================================
void USART1_IRQHandler(void)
{
uint8_t data;//接收數(shù)據(jù)暫存變量
BaseType_t xHigherPriorityTaskWoken;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷
{
data =USART_ReceiveData(USART1);
Recv[rx_cnt++]=data;//接收的數(shù)據(jù)存入接收數(shù)組
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//空閑中斷
{
if(uartSemaphore!=NULL)
{
//釋放二值信號(hào)量
xSemaphoreGiveFromISR(uartSemaphore,&xHigherPriorityTaskWoken); //釋放二值信號(hào)量
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的話進(jìn)行一次任務(wù)切換
data = USART1->SR;//串口空閑中斷的中斷標(biāo)志只能通過(guò)先讀SR寄存器,再讀DR寄存器清除!
data = USART1->DR;
//USART_ClearITPendingBit(USART1,USART_IT_IDLE);//這種方式無(wú)效
//rx_cnt=0;
}
}
獲取信號(hào)量
編寫一個(gè)任務(wù)來(lái)實(shí)現(xiàn)串口數(shù)據(jù)的獲取,該任務(wù)不斷嘗試獲取信號(hào)量,獲取成功后,對(duì)數(shù)據(jù)進(jìn)行處理。
獲取信號(hào)量xSemaphoreTake
,阻塞(等待時(shí)間)10ms,獲取不到信號(hào)量則向下執(zhí)行,每個(gè)任務(wù)都是一個(gè)死循環(huán),馬上又會(huì)進(jìn)行信號(hào)量獲取。
//打印任務(wù)函數(shù)
void print_task(void *pvParameters)
{
int count=0;
BaseType_t err = pdFALSE;
int size=50;
uint8_t buf[64];//最多只取前64個(gè)數(shù)據(jù)
//清空本地接收數(shù)組
memset(buf,0,size);
while(1)
{
err=xSemaphoreTake(uartSemaphore,10); //獲取信號(hào)量
if(err==pdTRUE) //獲取信號(hào)量成功
{
//printf("%s",Data);
if(rx_cnt < size)//收到的數(shù)據(jù)長(zhǎng)度在size范圍內(nèi)
{
//void *memcpy(void *str1, const void *str2, size_t n)
//從存儲(chǔ)區(qū) str2 復(fù)制 n 個(gè)字節(jié)到存儲(chǔ)區(qū) str1。
memcpy(buf,Recv,rx_cnt);//有幾個(gè)復(fù)制幾個(gè)
count=rx_cnt;
//printf("%srn", buf);
}
else//收到的數(shù)據(jù)長(zhǎng)度太長(zhǎng)了
{
memcpy(buf,Recv,size);//只復(fù)制size個(gè)
count=size;
}
rx_cnt=0;
}
if(count>0)
{
count=0;
printf("receive:%s",buf);
//------------------------------------------------------------------------------
//這里可以繼續(xù)對(duì)buf進(jìn)行分析和處理,比如根據(jù)buf的不同內(nèi)容執(zhí)行不同的小任務(wù)
}
}
}
一個(gè)小應(yīng)用
結(jié)合之前文章介紹的字符串操作的相關(guān)知識(shí):,可以對(duì)“命令+參數(shù)”型的字符串?dāng)?shù)據(jù)進(jìn)行處理。
//先判斷指令名稱
char *cmd;//表示命令
char *paras;//表示命令后的參數(shù)
cmd = strtok_r((char*)buf, " ", ?s);//這里有點(diǎn)小問(wèn)題,不帶參數(shù)的命令,后面需要一個(gè)空格
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
{
//是有效的指令,繼續(xù)判斷后續(xù)參數(shù)
char* para[4]={0};//限定最多接收4個(gè)參數(shù)
para[0] = strtok(paras, " ");
int j= 1;
while(paras != NULL)//這里有點(diǎn)小問(wèn)題,不可以提前結(jié)束
{
para[j++] = strtok(NULL, " ");
if(j==4)
break;
}
//執(zhí)行對(duì)應(yīng)的函數(shù)
struct_dostr1[i].fun(para);
}
最后的函數(shù)執(zhí)行,是通過(guò)定義一個(gè)結(jié)構(gòu)體,將字符命令與函數(shù)指針對(duì)應(yīng)起來(lá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~~~~~~~~~~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;
}
實(shí)驗(yàn)結(jié)果
通過(guò)串口發(fā)送hello
或led 80 5
,可以看到想要的處理結(jié)果:
receive:hello
hello~~~~~~~~~~
receive:led 80 5
get led: 80, 5
審核編輯:湯梓紅
-
串口
+關(guān)注
關(guān)注
14文章
1555瀏覽量
76631 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
484瀏覽量
62223
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論