OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 1724|回复: 0

《STM32F407 探索者开发指南》第七十章 UCOSII实验2-信号量和邮箱

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4667
金钱
4667
注册时间
2019-5-8
在线时间
1224 小时
发表于 2023-10-11 16:16:13 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-10-11 16:15 编辑

第七十章 UCOSII实验2-信号量和邮箱

1)实验平台:正点原子探索者STM32F407开发板

2) 章节摘自【正点原子】STM32F407开发指南 V1.1


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32f407_explorerV3.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)STM32技术交流QQ群:151941872

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

上一章,我们学习了如何使用UCOSII,学习了UCOSII的任务调度,但是并没有用到任务间的同步与通信,本章我们将学习两个最基本的任务间通讯方式:信号量和邮箱。
本章分为如下几个小节:
70.1 UCOSII信号量和邮箱简介
70.2 硬件设计
70.3 程序设计
70.4 下载验证

70.1 UCOSII信号量和邮箱简介
系统中的多个任务在运行时,经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的限制和制约,才保证任务的顺利进行。因此,操作系统必须具有对任务的运行进行协调的能力,从而使任务之间可以无冲突、流畅地同步运行,而不导致灾难性的后果。

例如,任务A和任务B共享一台打印机,如果系统已经把打印机分配给了任务A,则任务B因不能获得打印机的使用权而应该处于等待状态,只有当任务A把打印机释放后,系统才能唤醒任务B,使其获得打印机的使用权。如果这两个任务不这样做,那么会造成极大的混乱。

任务间的同步依赖于任务间的通信。在UCOSII中,是使用信号量、邮箱(消息邮箱)和消息队列,这些被称作事件的中间环节来实现任务之间的通信的。这里我们仅介绍信号量和邮箱,消息对列将会在下一章介绍。

事件
两个任务通过事件进行通讯的示意图如图70.1.1所示:
image002.png
图70.1.1.1两个任务使用事件进行通信的示意图

在上图中任务1是发信方,任务2是收信方。任务1负责把信息发送到事件上,这项操作叫做发送事件。任务2通过读取事件操作对事件进行查询:如果有信息则读取,否则等待。读事件操作叫做请求事件。

为了把描述事件的数据结构统一起来,UCOSII使用叫做事件控制块(ECB)的数据结构来描述诸如信号量、邮箱(消息邮箱)和消息队列这些事件。事件控制块中包含等待任务表在内的所有有关事件的数据,事件控制块结构体定义如下:
  1. typedef structos_event {
  2.     INT8U   OSEventType;                   /* 事件的类型*/
  3.     void    *OSEventPtr;                        /* 消息或消息队列的指针*/
  4.     INT16U  OSEventCnt;                       /* 信号量计数器*/
  5.     OS_PRIO OSEventGrp;                       /* 等待事件的任务组*/
  6.     OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* 任务等待表 */
  7. #ifOS_EVENT_NAME_EN > 0u
  8.     INT8U  *OSEventName;                   /* 事件名 */
  9. #endif
  10. }OS_EVENT;
复制代码

信号量
使用信号量的最初目的,是为了给共享资源设立一个标志,该标志表示共享资源的占用情况。这样,当一个任务在访问共享资源之前,就可以先对这个标志进行查询,从而在了解资源被占用的情况之后,再来决定自己的行为。

信号量的实质是一个全局计数器的实现机制,释放信号量的任务使得该计数器的值加1,请求到信号量的任务使得该计数器的值减1。如果计数器的值为0,则请求该信号量的任务将挂起等待,直到别的任务释放该信号量。通过这种方式,使得释放信号量的任务可以控制请求信号量的任务的运行。

信号量的工作原理如图70.1.2所示:
image004.png
图70.1.2信号量工作原理图

信号量可以分为两种:一种是二值型信号量,另外一种是N值信号量。

UCOSII将二值型信号量也称为互斥型信号量,将N值信号量称之为计数型信号量,也就是普通的信号量。

信号量相关的主要操作有:创建信号量OSSemCreate、请求信号量OSSemPend、释放信号量OSSemPost和删除信号量OSSemDel。后面再对这几个函数进行讲解。

邮箱
在多任务操作系统中,常常需要在任务与任务之间通过传递一个数据(这种数据叫做“消息”)的方式来进行通信。为了达到这个目的,可以在内存中创建一个存储空间作为该数据的缓冲区。这个缓冲区称之为消息缓冲区,这样在任务间传递数据(消息)的最简单的办法就是传递消息缓冲区的指针。我们把用来传递消息缓冲区指针的数据结构叫做邮箱(消息邮箱)。消息邮箱的工作情况如图70.1.3所示:
image006.png
图70.1.3 消息邮箱工作情况图

从上图可知,只有任务才能请求消息,消息里仅能存放一条消息,如果释放消息的速度比请求消息的速度快,则释放的消息将会丢失。可以通过广播的方式,使得释放的消息传递给所有请求该消息邮箱的任务。如果当前邮箱为空,且有某一任务2正在请求邮箱,则当另一任务1向邮箱中释放消息时,释放的消息将直接发送给任务2,而不用经过邮箱中转。

在UCOSII中,我们通过事件控制块的OSEventPrt来传递消息缓冲区指针,同时使事件控制块的成员OSEventType为常数OS_EVENT_TYPE_MBOX,则该事件控制块就叫做消息邮箱。

与消息邮箱相关的主要操作有:创建邮箱函数OSMboxCreate、向邮箱发送消息函数OSMboxPost、请求邮箱函数OSMboxPend、查询邮箱状态函数OSMboxQuery和删除邮箱函数OSMboxDel。后面再对这几个函数进行讲解。

70.2 硬件设计
1. 例程功能
在UCOSII里面创建6个任务(不包含统计任务和空闲任务):开始任务、LED0任务、LED1任务,触摸屏任务、按键扫描任务和主任务。开始任务用于创建信号量、创建邮箱、初始化统计任务以及其他任务的创建,之后挂起;LED0任务用于DS0控制,提示程序运行状况;LED1任务用于测试信号量,通过请求信号量函数,每得到一个信号量,DS1就亮一下;触摸屏任务用于在屏幕上画图,可以用于测试CPU使用率;按键扫描任务用于按键扫描,优先级最高,将得到的键值通过消息邮箱发送出去;主任务则通过查询消息邮箱获得键值,并根据键值执行信号量发送(DS1控制)、触摸区域清屏和触摸屏校准等控制。

2. 硬件资源
1)LED灯
    LED0 – PF9
LED1 – PF10
2)独立按键
       KEY0 –PE4
       KEY1 –PE3
       WK_UP –PA0
3)正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)

70.3 程序设计
70.3.1 UCOSII相关函数介绍
信号量函数
在这里对本实验用到的UCOSII信号量函数进行介绍,相关代码存放在os_sem.c中。

1. OSSemCreate函数
创建信号量函数,其声明如下:
  1. OS_EVENT  *OSSemCreate (INT16U cnt)
复制代码
l  函数描述:
用于创建一个信号量

l  函数形参:
cnt是信号量计数器(OSEventCnt)的初始值

l  函数返回值:
已创建的信号量的指针

2. OSSemPend函数
请求信号量函数,其声明如下:
  1. void  OSSemPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr)
复制代码
l  函数描述:
请求信号量

l  函数形参:
pevent:被请求信号量的指针                 
timeout:等待时限
perr:错误信息     
OS_ERR_NONE:调用成功,信号量不为零
OS_ERR_TIMEOUT :信号量没有在指定数目的时钟周期内被设置
OS_ERR_PEND_ABOUT:取消对信号量的等待
OS_ERR_EVENT_TYPE:没有传递信号量的指针
OS_ERR_PEND_ISR:从中断调用该函数时错误
OS_ERR_PEVENT_NULL:pevent是一个空指针
OS_ERR_PEND_LOCKED:调度器上锁了

l  函数返回值:

l  注意事项:
为了防止任务因得不到信号量而处于长期的等待状态,函数OSSemPend允许用参数timeout设置一个等待时间的限制,当任务等待的时间超过timeout时可以结束等待状态而进入就绪状态。如果参数timeout被设置为0,则表明任务的等待时间为无限长。

3. OSSemPost函数
发送信号量函数,其声明如下:
  1. INT8U  OSSemPost (OS_EVENT *pevent)
复制代码
l  函数描述:
用于发送信号量或者称为释放信号量

l  函数形参:
pevent:被请求信号量的指针

l  函数返回值:
OS_ERR_NONE:函数调用成功,信号量被成功地设置
OS_ERR_SEM_OVF:信号量的值溢出
OS_ERR_EVENT_TYPE:pevent不是指向信号量的指针
OS_ERR_PEVENT_NULL:pevent是一个空指针

4.OSSemDel函数
删除信号量函数,其声明如下:
  1. OS_EVENT  *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *perr)
复制代码
l  函数描述:
用于删除信号量并准备挂起所有任务

l  函数形参:
pevent:要删除的信号量指针
opt:删除条件选项
OS_DEL_NO_PEND:在没有任务挂起时删除信号量
OS_DEL_ALWAYS:删除信号量,即使任务正在等待。
perr:错误信息     
OS_ERR_NONE:函数调用成功,成功删除信号量
OS_ERR_DEL_ISR:尝试在中断中删除信号量
OS_ERR_INVALID_OPT:指向一个无效的选项
OS_ERR_TASK_WAITING:一个或多个任务在等待这个信号量
OS_ERR_EVENT_TYPE:没有传递一个指向信号量的指针
OS_ERR_PEVENT_NULL:pevent是一个空指针

l  函数返回值:
pevent :存在错误             (OS_EVENT*)0 : 该信号量被成功删除。

消息邮箱函数
在这里对本实验用到的UCOSII消息邮箱函数进行介绍,相关代码存放在os_mbox.c中

1. OSMboxCreate函数
创建邮箱函数,其声明如下:
  1. OS_EVENT  *OSMboxCreate (void *pmsg)
复制代码
l  函数描述:
用于创建邮箱函数

l  函数形参:
pmsg:消息的指针

l  函数返回值:
消息邮箱的指针

l  注意事项:
调用OSMboxCreate前,需先定义msg的初始值。在一般情况下,这个初始值为NULL。但也可以事先定义一个邮箱,然后把这个邮箱的指针作为参数传递到函数OSMboxCreate中,使得一开始就指向一个邮箱。

2. OSMboxPost函数
向邮箱发送消息函数,其声明如下:
  1. INT8U  OSMboxPost (OS_EVENT  *pevent, void *pmsg)
复制代码
l  函数描述:
用于向消息邮箱发送消息

l  函数形参:
pevent:消息邮箱的指针           
pmsg :消息指针

l  函数返回值:
OS_ERR_NONE:消息发送成功
OS_ERR_MBOX_FULL:不能向满邮箱再发送消息
OS_ERR_EVENT_TYPE:指定的事件不是消息邮箱类型
OS_ERR_PEVENT_NULL:不能向不存在的消息邮箱发送消息
OS_ERR_POST_NULL_PTR:消息缓冲区不能为空

3. OSMboxPend函数
请求邮箱函数,其声明如下:
  1. void  *OSMboxPend (OS_EVENT  *pevent, INT32U timeout, INT8U *perr)
复制代码
l  函数描述:
请求消息邮箱,就是等待一个消息传送到消息邮箱或取得一个消息数据

l  函数形参:
pevent:消息邮箱的指针      
timeout:等待时限      
perr:错误信息
OS_ERR_NONE:函数调用成功,接收到消息
OS_ERR_TIMEOUT:未在“超时”时间内接收到消息
OS_ERR_PEND_ABORT:终止邮箱的等待
OS_ERR_EVENT_TYPE:无效事件类型
OS_ERR_PEND_ISR:从ISR中调用该函数,导致任务挂起
OS_ERR_PEVENT_NULL:pevent是一个空指针
OS_ERR_PEND_LOCKED:调度器上锁了

l  函数返回值:
NULL:未得到消息          !NULL:预期消息的指针

4. OSMboxQuery函数
查询邮箱状态函数,其声明如下:
  1. UINT8U  *OSMboxQuery (OS_EVENT  *pevent, OS_MBOX_DATA*p_mbox_data)
复制代码
l  函数描述:
获取消息邮箱的相关信息

l  函数形参:
pevent:消息邮箱的指针         
p_mbox_data:存放邮箱信息的结构

l  函数返回值:
OS_ ERR_NONE:调用成功      
OS_ERR_EVENT_TYPE:pevent不是指向消息邮箱的指针
OS_ERR_PEVENT_NULL:不能向不存在的消息邮箱发送消息
OS_ERR_PDATA_NULL:p_mbox_data是一个空指针

l  注意事项:
必须先建立消息邮箱,然后使用

5.OSMboxDel函数
删除邮箱函数,其声明如下:
  1. OS_EVENT  *OSMboxDel (OS_EVENT *pevent, INT8U opt, INT8U *perr)
复制代码
l  函数描述:
对一个不再使用的消息邮箱及时删除以释放资源

l  函数形参:
pevent:要删除的邮箱指针
opt:删除条件选项
OS_DEL_NO_PEND:在没有任务挂起时删除邮箱
OS_DEL_ALWAYS:无条件删除邮箱,所有等待该事件的任务转到就绪态。
perr:错误信息
OS_ERR_NONE:函数调用成功,成功删除邮箱
OS_ERR_DEL_ISR:不支持在中断中删除邮箱
OS_ERR_INVALID_OPT:指向一个无效的选项
OS_ERR_TASK_WAITING:一个或多个任务在等待这个信号量
OS_ERR_EVENT_TYPE:没有传递一个指向邮箱的指针
OS_ERR_PEVENT_NULL:pevent是一个空指针

l  函数返回值:
pevent :存在错误             (OS_EVENT *)0 : 该邮箱被成功删除

70.3.2 程序流程图
image008.png
图70.3.2.1 UCOSII信号量和邮箱实验

程序我们按流程图的设计来实现本节的功能代码。我们通过start_task创建其他任务,包括:
LED任务,成功请求信号量时LED灯闪烁一次,否则阻塞;
蜂鸣器任务,成功请求信号量时蜂鸣器响一次,否则阻塞;
按键扫描函数,根据按键发送不同的消息;
main_task用于创建定时器任务,并根据获取的队列消息得到按键值并投递给其他任务,控制部分任务运行或挂起,重绘界面等。

70.3.3 程序解析
1. main.c代码
在main.c文件下,除了main函数之外,还有UCOSII任务的一些配置以及6个任务函数。我们先看一下UCOSII任务的一些宏定义,如下代码所示:
  1. /* UCOSII任务设置 */
  2. /* START 任务 配置
  3. * 包括: 任务优先级 堆栈大小 等
  4. */
  5. #defineSTART_TASK_PRIO                 10      /* 开始任务的优先级设置为最低 */
  6. #defineSTART_STK_SIZE                  128     /* 堆栈大小 */
  7. OS_STKSTART_TASK_STK[START_STK_SIZE];       /* 任务堆栈 */
  8. voidstart_task(void *pdata);                  /* 任务函数 */
  9. /* 触摸屏任务 任务 配置
  10. * 包括: 任务优先级 堆栈大小 等
  11. */
  12. #defineTOUCH_TASK_PRIO                 7       /* 优先级设置(越小优先级越高) */
  13. #defineTOUCH_STK_SIZE                  128    /* 堆栈大小 */
  14. OS_STKTOUCH_TASK_STK[TOUCH_STK_SIZE];      /* 任务堆栈 */
  15. voidtouch_task(void *pdata);               /* 任务函数 */
  16. /* LED 任务 配置
  17. * 包括: 任务优先级 堆栈大小 等
  18. */
  19. #defineLED_TASK_PRIO                   6       /* 优先级设置(越小优先级越高) */
  20. #defineLED_STK_SIZE                    128     /* 堆栈大小 */
  21. OS_STKLED_TASK_STK[LED_STK_SIZE];          /* 任务堆栈 */
  22. voidled_task(void *pdata);                 /* 任务函数 */
  23. /* 蜂鸣器 任务 配置
  24. * 包括: 任务优先级 堆栈大小 等
  25. */
  26. #defineBEEP_TASK_PRIO                  5        /* 优先级设置(越小优先级越高) */
  27. #defineBEEP_STK_SIZE                   128     /* 堆栈大小 */
  28. OS_STKBEEP_TASK_STK[BEEP_STK_SIZE];         /* 任务堆栈 */
  29. voidbeep_task(void *pdata);                    /* 任务函数 */
  30. /* 主 任务 配置
  31. * 包括: 任务优先级 堆栈大小 等
  32. */
  33. #defineMAIN_TASK_PRIO                  4       /* 优先级设置(越小优先级越高) */
  34. #defineMAIN_STK_SIZE                   128     /* 堆栈大小 */
  35. OS_STKMAIN_TASK_STK[MAIN_STK_SIZE];         /* 任务堆栈 */
  36. voidmain_task(void *pdata);                    /* 任务函数 */
  37. /* 按键扫描 任务 配置
  38. * 包括: 任务优先级 堆栈大小 等
  39. */
  40. #defineKEY_TASK_PRIO                   3       /* 优先级设置(越小优先级越高) */
  41. #defineKEY_STK_SIZE                    128     /* 堆栈大小 */
  42. OS_STKKEY_TASK_STK[KEY_STK_SIZE];          /* 任务堆栈 */
  43. voidkey_task(void *pdata);                    /* 任务函数 */
  44. /**
  45. * @brief      开始任务
  46. * @param      pdata : 传入参数(未用到)
  47. * @retval     无
  48. */
  49. voidstart_task(void *pdata)
  50. {
  51.     OS_CPU_SR cpu_sr = 0;
  52.     pdata = pdata;
  53.     msg_key =OSMboxCreate((void *)0);  /* 创建消息邮箱 */
  54.     sem_beep =OSSemCreate(0);          /* 创建信号量 */
  55.    
  56.     OSStatInit();                          /* 开启统计任务 */
  57.     OS_ENTER_CRITICAL();                  /* 进入临界区(关闭中断) */
  58.    
  59.     /* 触摸任务 */
  60.     OSTaskCreateExt((void(*)(void *) )touch_task,
  61.                     (void *          )0,
  62.                     (OS_STK*        )&TOUCH_TASK_STK[TOUCH_STK_SIZE- 1],
  63.                     (INT8U           )TOUCH_TASK_PRIO,
  64.                     (INT16U          )TOUCH_TASK_PRIO,
  65.                     (OS_STK*        )&TOUCH_TASK_STK[0],
  66.                     (INT32U          )TOUCH_STK_SIZE,
  67.                     (void *          )0,
  68.                     (INT16U          )OS_TASK_OPT_STK_CHK| OS_TASK_OPT_STK_CLR |
  69.                                   OS_TASK_OPT_SAVE_FP);
  70.     /* LED任务 */
  71.     OSTaskCreateExt((void(*)(void *) )led_task,
  72.                     (void *          )0,
  73.                     (OS_STK*        )&LED_TASK_STK[LED_STK_SIZE- 1],
  74.                     (INT8U           )LED_TASK_PRIO,
  75.                     (INT16U          )LED_TASK_PRIO,
  76.                     (OS_STK*        )&LED_TASK_STK[0],
  77.                     (INT32U          )LED_STK_SIZE,
  78.                     (void *          )0,
  79.                     (INT16U          )OS_TASK_OPT_STK_CHK| OS_TASK_OPT_STK_CLR |
  80.                                   OS_TASK_OPT_SAVE_FP);
  81.     /* 蜂鸣器任务 */
  82.     OSTaskCreateExt((void(*)(void *) )beep_task,
  83.                     (void *          )0,
  84.                     (OS_STK*        )&BEEP_TASK_STK[BEEP_STK_SIZE- 1],
  85.                     (INT8U           )BEEP_TASK_PRIO,
  86.                     (INT16U          )BEEP_TASK_PRIO,
  87.                     (OS_STK*        )&BEEP_TASK_STK[0],
  88.                     (INT32U          )BEEP_STK_SIZE,
  89.                     (void *          )0,
  90.                     (INT16U          )OS_TASK_OPT_STK_CHK| OS_TASK_OPT_STK_CLR |
  91.                                   OS_TASK_OPT_SAVE_FP);
  92.     /* 主任务 */
  93.     OSTaskCreateExt((void(*)(void *) )main_task,
  94.                     (void *          )0,
  95.                     (OS_STK*        )&MAIN_TASK_STK[MAIN_STK_SIZE- 1],
  96.                     (INT8U           )MAIN_TASK_PRIO,
  97.                     (INT16U          )MAIN_TASK_PRIO,
  98.                     (OS_STK*        )&MAIN_TASK_STK[0],
  99.                     (INT32U          )MAIN_STK_SIZE,
  100.                     (void *          )0,
  101.                     (INT16U          )OS_TASK_OPT_STK_CHK| OS_TASK_OPT_STK_CLR |
  102.                                   OS_TASK_OPT_SAVE_FP);
  103.     /* 按键任务 */
  104.     OSTaskCreateExt((void(*)(void *) )key_task,
  105.                     (void *          )0,
  106.                     (OS_STK*        )&KEY_TASK_STK[KEY_STK_SIZE- 1],
  107.                     (INT8U           )KEY_TASK_PRIO,
  108.                     (INT16U          )KEY_TASK_PRIO,
  109.                     (OS_STK*        )&KEY_TASK_STK[0],
  110.                     (INT32U          )KEY_STK_SIZE,
  111.                     (void *          )0,
  112.                     (INT16U          )OS_TASK_OPT_STK_CHK| OS_TASK_OPT_STK_CLR |
  113.                                   OS_TASK_OPT_SAVE_FP);
  114.     OS_EXIT_CRITICAL();                /* 退出临界区(开中断) */
  115.     OSTaskSuspend(START_TASK_PRIO); /* 挂起开始任务 */
  116. }
  117. /**
  118. * @brief      LED任务
  119. * @param      pdata : 传入参数(未用到)
  120. * @retval     无
  121. */
  122. voidled_task(void *pdata)
  123. {
  124.     uint8_t t;
  125.     pdata = pdata;
  126.     while (1)
  127.     {
  128.         t++;
  129.         delay_ms(10);
  130.         if (t == 8)LED0(1); /*LED0灭 */
  131.         if (t == 100)        /* LED0亮 */
  132.         {
  133.             t = 0;
  134.             LED0(0);
  135.         }
  136.     }
  137. }
  138. /**
  139. * @brief      蜂鸣器任务
  140. * @param      pdata : 传入参数(未用到)
  141. * @retval     无
  142. */
  143. voidbeep_task(void *pdata)
  144. {
  145.     uint8_t err;
  146.     pdata = pdata;
  147.     while (1)
  148.     {
  149.         OSSemPend(sem_beep, 0, &err); /* 请求信号量 */
  150.         BEEP(1);                          /* 打开蜂鸣器 */
  151.         delay_ms(60);
  152.         BEEP(0);                         /* 关闭蜂鸣器 */
  153.         delay_ms(940);
  154.     }
  155. }
  156. /**
  157. * @brief      触摸屏任务
  158. * @param      pdata : 传入参数(未用到)
  159. * @retval     无
  160. */
  161. voidtouch_task(void *pdata)
  162. {
  163.     uint32_t cpu_sr;
  164.     uint16_t lastpos[2];    /* 最后一次的数据 */
  165.     pdata = pdata;
  166.     while (1)
  167.     {
  168.         tp_dev.scan(0);
  169.         if (tp_dev.sta &TP_PRES_DOWN)  /* 触摸屏被按下 */
  170.         {
  171.             if (tp_dev.x[0] <lcddev.width && tp_dev.y[0] <lcddev.height &&
  172.               tp_dev.y[0] > 120)
  173.             {
  174.                 if (lastpos[0] == 0XFFFF)
  175.                 {
  176.                     lastpos[0] =tp_dev.x[0];
  177.                     lastpos[1] =tp_dev.y[0];
  178.                 }
  179.               OS_ENTER_CRITICAL();/* 进入临界段,防止其他任务,打断LCD操作,导致液晶乱序 */
  180.               lcd_draw_bline(lastpos[0],lastpos[1],tp_dev.x[0],tp_dev.y[0],2,RED);          /* 画线 */
  181.               OS_EXIT_CRITICAL();
  182.               lastpos[0] =tp_dev.x[0];
  183.               lastpos[1] =tp_dev.y[0];
  184.             }
  185.         }
  186.         else
  187.         {
  188.             lastpos[0] = 0XFFFF;
  189.             delay_ms(10);   /* 没有按键按下的时候 */
  190.         }
  191.     }
  192. }
  193. /**
  194. * @brief      主任务
  195. * @param      pdata : 传入参数(未用到)
  196. * @retval     无
  197. */
  198. voidmain_task(void *pdata)
  199. {
  200.     uint32_t key = 0;
  201.     uint8_t err;
  202.     uint8_t semmask = 0;
  203.     uint8_t tcnt = 0;
  204.     pdata = pdata;
  205.     while (1)
  206.     {
  207.         key = (uint32_t)OSMboxPend(msg_key, 10, &err);
  208.         switch (key)
  209.         {
  210.             caseKEY0_PRES: /* 控制DS1,并清除触摸区域 */
  211.                 LED1_TOGGLE();
  212.                 lcd_fill(0, 121,lcddev.width - 1,lcddev.height - 1, WHITE);
  213.                 break;
  214.             caseKEY1_PRES: /* 发送信号量 */
  215.                 semmask = 1;
  216.                 OSSemPost(sem_beep);
  217.                 break;
  218.             caseWKUP_PRES:                         /* 校准 */
  219.                 OSTaskSuspend(TOUCH_TASK_PRIO); /* 挂起触摸屏任务 */
  220.                 if ((tp_dev.touchtype& 0X80) == 0)
  221.                 {
  222.                     tp_adjust();
  223.                 }
  224.                 OSTaskResume(TOUCH_TASK_PRIO); /* 解挂 */
  225.                 ucos_load_main_ui();             /* 重新加载主界面 */
  226.                 break;
  227.             }
  228.             if (semmask|| sem_beep->OSEventCnt) /* 需要显示sem */
  229.             {
  230.                 lcd_show_xnum(192, 50,sem_beep->OSEventCnt, 3, 16, 0X80, BLUE);               /* 显示信号量的值 */
  231.                 if (sem_beep->OSEventCnt== 0)
  232.                     semmask = 0; /* 停止更新 */
  233.             }
  234.             if (tcnt == 10) /*0.6秒更新一次CPU使用率 */
  235.             {
  236.                 tcnt = 0;
  237.                 lcd_show_xnum(192,30,OSCPUUsage,3,16,0,BLUE); /* 显示CPU使用率 */
  238.             }
  239.         tcnt++;
  240.         delay_ms(10);
  241.     }
  242. }
  243. /**
  244. * @brief      按键扫描任务
  245. * @param      pdata : 传入参数(未用到)
  246. * @retval     无
  247. */
  248. voidkey_task(void *pdata)
  249. {
  250.     uint32_t key;
  251.     pdata = pdata;
  252.     while (1)
  253.     {
  254.         key =key_scan(0);
  255.         if (key)OSMboxPost(msg_key, (void *)key); /* 发送消息 */
  256.         delay_ms(10);
  257.     }
  258. }
复制代码
上面就是对创建的start_task、led0_task、touch_task、led1_task、main_task和key_task等6个任务的参数进行配置,例如优先级&#65380;堆栈大小和任务函数的声明及定义。这6个任务做的事情在前面程序流程图里面已经有说明,这里就不多说了。

下面看一下main主函数的代码:
  1. OS_EVENT *msg_key;      /* 按键邮箱事件块指针 */
  2. OS_EVENT *sem_beep;     /* 蜂鸣器信号量指针 */
  3. int main(void)
  4. {
  5.     HAL_Init();                                 /* 初始化HAL库 */
  6.     sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz*/
  7.     delay_init(168);                            /* 延时初始化 */
  8.     usart_init(115200);                        /* 串口初始化为115200 */
  9.     led_init();                                 /* 初始化LED*/
  10.     lcd_init();                                 /* 初始化LCD*/
  11.     key_init();                                 /* 初始化按键 */
  12.     beep_init();                                /* 初始化蜂鸣器 */
  13.     tp_dev.init();                              /* 触摸屏初始化 */
  14.     ucos_load_main_ui();                       /* 加载主界面 */
  15.     OSInit();                                    /* UCOS初始化 */
  16.     OSTaskCreateExt((void(*)(void *) )start_task,         /* 任务函数 */
  17.                        (void *          )0,                      /* 传递给任务函数的参数 */
  18.                     (OS_STK*)&START_TASK_STK[START_STK_SIZE- 1], /* 任务堆栈栈顶 */
  19.                     (INT8U           )START_TASK_PRIO,        /* 任务优先级 */
  20.                     (INT16U)START_TASK_PRIO,         /* 任务ID,这里设置为和优先级一样 */
  21.                     (OS_STK*        )&START_TASK_STK[0],    /* 任务堆栈栈底 */
  22.                     (INT32U          )START_STK_SIZE,         /* 任务堆栈大小 */
  23.                     (void *          )0,                         /* 用户补充的存储区 */
  24.                     (INT16U          )OS_TASK_OPT_STK_CHK| OS_TASK_OPT_STK_CLR |
  25.                  OS_TASK_OPT_SAVE_FP);/* 任务选项,所有任务都保存浮点寄存器的值 */
  26.     OSStart();  /* 开始任务 */
  27. }
复制代码
这一章的main函数的运行流程比上一章复杂了一些,我们创建了消息邮箱msg_key,用于按键任务和主任务之间的数据传输(传递键值)。另外创建了信号量sem_led1,用于LED1任务和主任务之间的通信。

在代码中,我们使用了UCOSII提供的CPU统计服务,通过OSStatInit初始化CPU统计任务,然后在主任务中显示CPU使用率。

另外,在主任务中,我们用到了任务的挂起和恢复函数,在执行触摸校准的时候,我们必须先将触摸屏任务挂起,待校准完成之后,再恢复触摸屏任务。这是因为触摸屏校准和触摸屏任务都用到了触摸屏和TFTLCD,而这两个东西是不支持多任务占用的,所有必须采用独占的方式使用,否则可能导致数据错乱。

70.4 下载验证
将程序下载到开发板后,可以看到LCD显示界面如图70.4.1所示:     
image009.png
图70.4.1 初始化界面

从上图可以看到,默认情况下,CPU使用率仅为1%。此时通过在触摸区域(Touch Area)画图,可以看到CPU使用率飙升,这说明触摸屏任务是一个很占CPU的任务;通过按KEY0,可以控制LED1的亮灭;同时,可以在LCD上看到信号量的当前值;通过按下KEY1可以清屏;通过按下WK_UP可以进入校准程序,对触摸屏进行校准。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 07:08

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表