1基本的な知識ポイント
1.1
シリアルポート割り込みの種類シリアルポート割り込みはSTM32独自のリソースに属し、FreeRTOSは含まれませんが、FreeRTOSと組み合わせて使用できます。
シリアルポート受信割り込み
割り込みフラグは次のとおりです。USART_IT_RXNE、つまりrx none empty、シリアルポートはデータを受信している限り割り込みをトリガーします。文字列を受信している場合は、文字を受信するたびに割り込みをトリガーします。
シリアルポートのアイドル割り込み
割り込みフラグは次のとおりです。USART_IT_IDLE、アイドルはアイドルを意味します。シリアルポートがアイドルのときに割り込みがトリガーされます。もちろん、シリアルポートがアイドルのときに常に割り込みがトリガーされるわけではありませんが、連続受信が完了すると、割り込みがトリガーされます。受信した場合、文字列を受信すると、文字列全体を受信した後に割り込みがトリガーされます。
したがって、これら2つの割り込みを併用することができます。シリアルポートは割り込みを受信してリアルタイムでデータを受信します。データの文字列を受信した後、アイドル割り込みがトリガーされ、受信したデータの文字列を分析して処理できます。この方法では、文字列の特定の長さを毎回知る必要がないため、可変長のシリアルポートデータを受信できます。
1.2セマフォ
FreeRTOSのセマフォは、タスク間の通信手段です。セマフォには、バイナリセマフォ、相互に排他的なセマフォ、カウントセマフォが含まれます。今回はバイナリセマフォのみを使用します。
バイナリ
セマフォバイナリセマフォには、0または1の符号として理解できる2つの状態しかありません。セマフォはタスク間の同期に使用されます。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シリアルポートの割り込みとセマフォの解放
シリアルポートを構成するときは、2つの割り込みを有効にすることを忘れないでください。
//=======================================
//初始化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 strtokatoiを使用します。
//先判断指令名称
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]\r\n", i);
break;
}
}
if(i==N)
{
printf("can't find cmd in funname[]\r\n");
}
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~~~~~~~~~~\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に保存されました: