13.4 实验例程说明(中断方式通信)
13.4.1 STM32F103开发板实验
配套例子:
V4-410_RTX实验_事件标志组(中断方式)
实验目的:
1. 学习RTX的事件标志组(中断方式)
实验内容:
1. K1按键按下,串口打印。
2. K2键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事件标志,设置bit0。
3. K3键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事件标志,设置bit1。
4. 任务AppTaskMsgPro只有接收到bit0和bit1都被设置了才执行串口打印信息。
5. 各个任务实现的功能如下:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务
:消息处理,等待定时器中断发来的事件标志。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
RTX配置向导详情如下:

u Task
Configuration
l Number
of concurrent running tasks
允许创建4个任务,实际创建了如下四个任务
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务
:消息处理,等待定时器中断发来的事件标志。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
l Number
of tasks with user-provided stack
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:

u Event
Value:任务AppTaskMsgPro当前的事件标志数值。
u Event
Mask:任务AppTaskMsgPro等待的事件标志数值。
程序设计:
u 任务栈大小分配:
static uint64_t
AppTaskUserIFStk[512/8];
static uint64_t
AppTaskLEDStk[256/8];
static uint64_t
AppTaskMsgProStk[512/8];
static uint64_t
AppTaskStartStk[512/8];
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
u 系统栈大小分配:

u RTX初始化:
int main (void)
{
bsp_Init();
os_sys_init_user
(AppTaskStart,
4,
&AppTaskStartStk,
sizeof(AppTaskStartStk));
while(1);
}
u RTX任务创建:
static void AppTaskCreate
(void)
{
HandleTaskUserIF
=
os_tsk_create_user(AppTaskUserIF,
1,
&AppTaskUserIFStk,
sizeof(AppTaskUserIFStk));
HandleTaskLED
=
os_tsk_create_user(AppTaskLED,
2,
&AppTaskLEDStk,
sizeof(AppTaskLEDStk));
HandleTaskMsgPro
=
os_tsk_create_user(AppTaskMsgPro,
3,
&AppTaskMsgProStk,
sizeof(AppTaskMsgProStk));
}
u 四个RTX任务的实现:
__task void
AppTaskUserIF(void)
{
uint8_t
ucKeyCode;
while(1)
{
ucKeyCode
= bsp_GetKey();
if
(ucKeyCode != KEY_NONE)
{
switch
(ucKeyCode)
{
case
KEY_DOWN_K1:
printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
break;
case
KEY_DOWN_K2:
printf("K2键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事
件标志\r\n");
bsp_StartHardTimer(1
,50000, (void *)TIM_CallBack1);
break;
case
KEY_DOWN_K3:
printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事
件标志\r\n");
bsp_StartHardTimer(1
,50000, (void *)TIM_CallBack2);
break;
default:
break;
}
}
os_dly_wait(20);
}
}
__task void
AppTaskLED(void)
{
const
uint16_t usFrequency = 200;
os_itv_set(usFrequency);
while(1)
{
bsp_LedToggle(2);
bsp_LedToggle(3);
os_itv_wait();
}
}
__task void
AppTaskMsgPro(void)
{
OS_RESULT
xResult;
const
uint16_t usMaxBlockTime = 500;
while(1)
{
xResult
= os_evt_wait_and (BIT_ALL, usMaxBlockTime);
switch
(xResult)
{
case
OS_R_EVT:
printf("接收到bit0和bit1都被设置的消息\r\n");
break;
case
OS_R_TMO:
bsp_LedToggle(1);
bsp_LedToggle(4);
break;
default:
break;
}
}
}
__task void
AppTaskStart(void)
{
AppTaskCreate();
while(1)
{
bsp_KeyScan();
os_dly_wait(10);
}
}
u 定时器中断回调函数中发送事件标志:
定时器中断的初始化和中断函数在bsp_timer.c文件中实现,这个不是教程的重点,故不作介绍。这里主要关心RTX的事件标志函数在中断服务程序中的使用方法。
static void
TIM_CallBack1(void)
{
isr_evt_set
(BIT_0, HandleTaskMsgPro);
}
static void
TIM_CallBack2(void)
{
isr_evt_set
(BIT_1, HandleTaskMsgPro);
}
13.4.2 STM32F407开发板实验
配套例子:
V5-410_RTX实验_事件标志组(中断方式)
实验目的:
1. 学习RTX的事件标志组(中断方式)
实验内容:
1. K1按键按下,串口打印。
2. K2键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事件标志,设置bit0。
3. K3键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事件标志,设置bit1。
4. 任务AppTaskMsgPro只有接收到bit0和bit1都被设置了才执行串口打印信息。
5. 各个任务实现的功能如下:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务
:消息处理,等待定时器中断发来的事件标志。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
RTX配置向导详情如下:

u Task
Configuration
l Number
of concurrent running tasks
允许创建4个任务,实际创建了如下四个任务
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务
:消息处理,等待定时器中断发来的事件标志。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
l Number
of tasks with user-provided stack
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:

u Event
Value:任务AppTaskMsgPro当前的事件标志数值。
u Event
Mask:任务AppTaskMsgPro等待的事件标志数值。
程序设计:
u 任务栈大小分配:
static uint64_t
AppTaskUserIFStk[512/8];
static uint64_t
AppTaskLEDStk[256/8];
static uint64_t
AppTaskMsgProStk[512/8];
static uint64_t
AppTaskStartStk[512/8];
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
u 系统栈大小分配:

u RTX初始化:
int main (void)
{
bsp_Init();
os_sys_init_user
(AppTaskStart,
4,
&AppTaskStartStk,
sizeof(AppTaskStartStk));
while(1);
}
u RTX任务创建:
static void AppTaskCreate
(void)
{
HandleTaskUserIF
=
os_tsk_create_user(AppTaskUserIF,
1,
&AppTaskUserIFStk,
sizeof(AppTaskUserIFStk));
HandleTaskLED
=
os_tsk_create_user(AppTaskLED,
2,
&AppTaskLEDStk,
sizeof(AppTaskLEDStk));
HandleTaskMsgPro
=
os_tsk_create_user(AppTaskMsgPro,
3,
&AppTaskMsgProStk,
sizeof(AppTaskMsgProStk));
}
u 四个RTX任务的实现:
__task void
AppTaskUserIF(void)
{
uint8_t
ucKeyCode;
while(1)
{
ucKeyCode
= bsp_GetKey();
if
(ucKeyCode != KEY_NONE)
{
switch
(ucKeyCode)
{
case
KEY_DOWN_K1:
printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
break;
case
KEY_DOWN_K2:
printf("K2键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事
件标志\r\n");
bsp_StartHardTimer(1
,50000, (void *)TIM_CallBack1);
break;
case
KEY_DOWN_K3:
printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事
件标志\r\n");
bsp_StartHardTimer(1
,50000, (void *)TIM_CallBack2);
break;
default:
break;
}
}
os_dly_wait(20);
}
}
__task void
AppTaskLED(void)
{
const
uint16_t usFrequency = 200;
os_itv_set(usFrequency);
while(1)
{
bsp_LedToggle(2);
bsp_LedToggle(3);
os_itv_wait();
}
}
__task void
AppTaskMsgPro(void)
{
OS_RESULT
xResult;
const
uint16_t usMaxBlockTime = 500;
while(1)
{
xResult
= os_evt_wait_and (BIT_ALL, usMaxBlockTime);
switch
(xResult)
{
case
OS_R_EVT:
printf("接收到bit0和bit1都被设置的消息\r\n");
break;
case
OS_R_TMO:
bsp_LedToggle(1);
bsp_LedToggle(4);
break;
default:
break;
}
}
}
__task void
AppTaskStart(void)
{
AppTaskCreate();
while(1)
{
bsp_KeyScan();
os_dly_wait(10);
}
}
u 定时器中断回调函数中发送事件标志:
定时器中断的初始化和中断函数在bsp_timer.c文件中实现,这个不是教程的重点,故不作介绍。这里主要关心RTX的事件标志函数在中断服务程序中的使用方法。
static void
TIM_CallBack1(void)
{
isr_evt_set
(BIT_0, HandleTaskMsgPro);
}
static void
TIM_CallBack2(void)
{
isr_evt_set
(BIT_1, HandleTaskMsgPro);
}
13.5总结
本章节就为大家讲解了任务间的通信和同步机制之一,事件标志组,初学者要稍花些时间将其掌握。