【正点原子STM32连载】 第六十二章 UCOSII实验2-信号量和邮箱 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

2023-05-16

1)实验平台:正点原子MiniPro H750开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-336836-1-1.html
4)对正点原子STM32感兴趣的同学可以加群讨论:879133275

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

上一章,我们学习了如何使用UCOSII,学习了UCOSII的任务调度,但是并没有用到任务间的同步与通信,本章我们将学习两个最基本的任务间通讯方式:信号量和邮箱。
本章分为如下几个小节:
62.1 UCOSII信号量和邮箱简介
62.2 硬件设计
62.3 程序设计
62.4 下载验证
62.1 UCOSII信号量和邮箱简介
系统中的多个任务在运行时,经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的限制和制约,才保证任务的顺利运行。因此,操作系统必须具有对任务的运行进行协调的能力,从而使任务之间可以无冲突、流畅地同步运行,而不导致灾难性的后果。
例如,任务A和任务B共享一台打印机,如果系统已经把打印机分配给了任务A,则任务B因不能获得打印机的使用权而应该处于等待状态,只有当任务A把打印机释放后,系统才能唤醒任务B,使其获得打印机的使用权。如果这两个任务不这样做,那么会造成极大的混乱。
任务间的同步依赖于任务间的通信。在UCOSII中,是使用信号量、邮箱(消息邮箱)和消息队列,这些被称作事件的中间环节来实现任务之间的通信的。这里我们仅介绍信号量和邮箱,消息队列将会在下一章介绍。
事件
两个任务通过事件进行通讯的示意图如图62.1.1所示:
在这里插入图片描述

图62.1.1 两个任务使用事件进行通信的示意图
在上图中任务1是发信方,任务2是收信方。任务1负责把信息发送到事件上,这项操作叫做发送事件。任务2通过读取事件操作对事件进行查询:如果有信息则读取,否则等待。读事件操作叫做请求事件。
为了把描述事件的数据结构统一起来,UCOSII 使用叫做事件控制块(ECB)的数据结构来描述诸如信号量、邮箱(消息邮箱)和消息队列这些事件。事件控制块中包含包括等待任务表在内的所有有关事件的数据,事件控制块结构体定义如下:

typedef struct os_event {
    INT8U    OSEventType;                     		/* 事件的类型 */
    void    *OSEventPtr;                        		/* 消息或消息队列的指针 */
    INT16U   OSEventCnt;                      		/* 信号量计数器 */
    OS_PRIO  OSEventGrp;                      		/* 等待事件的任务组 */
    OS_PRIO  OSEventTbl[OS_EVENT_TBL_SIZE];		/* 任务等待表 */
#if OS_EVENT_NAME_EN > 0u
    INT8U   *OSEventName;				     	/* 事件名 */
#endif
} OS_EVENT;

信号量
使用信号量的最初目的,是为了给共享资源设立一个标志,该标志表示该共享资源的占用情况。这样,当一个任务在访问共享资源之前,就可以先对这个标志进行查询,从而在了解资源被占用的情况之后,再来决定自己的行为。
信号量的实质是一个全局计数器的实现机制,释放信号量的任务使得该计数器的值加1,请求到信号量的任务使得该计数器的值减1。如果计数器得值为0,则请求该信号量得任务将挂起等待,直到别的任务释放该信号量。通过这种方式,使得释放信号量的任务可以控制请求信号量的任务的运行。
信号量的工作原理如图62.1.2所示:
在这里插入图片描述

图62.1.2信号量工作原理图
信号量可以分为两种:一种是二值型信号量,另外一种是N 值信号量。
UCOSII 将二值型信号量称之为也叫互斥型信号量,将 N 值信号量称之为计数型信号量, 也就是普通的信号量。
信号量相关的主要操作有:创建信号量OSSemCreate、请求信号量OSSemPend、释放信号量OSSemPost和删除信号量OSSemDel。后面再对这几个函数进行讲解。
邮箱
在多任务操作系统中,常常需要在任务与任务之间通过传递一个数据(这种数据叫做“消息”)的方式来进行通信。为了达到这个目的,可以在内存中创建一个存储空间作为该数据的缓冲区。如果把这个缓冲区称之为消息缓冲区,这样在任务间传递数据(消息)的最简单办法就是传递消息缓冲区的指针。我们把用来传递消息缓冲区指针的数据结构叫做邮箱(消息邮箱)。消息邮箱的工作情况如图62.1.3所示:
在这里插入图片描述

图62.1.3 消息邮箱工作情况图
从上图可知道,只有任务才能请求消息,邮箱里仅能存放一条消息,如果释放消息的速度比请求消息的速度快,则释放的消息将会丢失。可以通过广播的方式,使得释放的消息传递给所有请求该消息邮箱的任务。如果当前邮箱为空,且有某一任务2正在请求邮箱,则当另一任务1向邮箱中释放消息时,释放的消息将直接发送给任务2,而不用经过邮箱中转。
在UCOSII中,我们通过事件控制块的OSEventPrt来传递消息缓冲区指针,同时使事件控制块的成员OSEventType为常数OS_EVENT_TYPE_MBOX,则该事件控制块就叫做消息邮箱。
与消息邮箱相关的主要操作有:创建邮箱函数OSMboxCreate、向邮箱发送消息函数OSMboxPost、请求邮箱函数OSMboxPend、查询邮箱状态函数OSMboxQuery和删除邮箱函数OSMboxDel。后面再对这几个进行函数进行讲解。
62.2 硬件设计

  1. 例程功能
    本实验我们在UCOSII里面创建6个任务:开始任务、LED任务、触摸屏任务、蜂鸣器任务、按键扫描任务和主任务,开始任务用于创建信号量、创建邮箱、初始化统计任务以及其他任务的创建,之后挂起;LED任务用于LED0控制,提示程序运行状况;蜂鸣器任务用于测试信号量,是请求信号量函数,每得到一个信号量,蜂鸣器就叫一次;触摸屏任务用于在屏幕上画图,可以用于测试CPU使用率;按键扫描任务用于按键扫描,优先级最高,将得到的键值通过消息邮箱发送出去;主任务则通过查询消息邮箱获得键值,并根据键值执行LED1控制、信号量发送(蜂鸣器控制)、触摸区域清屏和触摸屏校准等控制。
    通过按KEY0,可以控制KEY1的亮灭。
    通过按KEY1,则可以控制蜂鸣器的发声(连续按下多次后,可以看到蜂鸣每隔1秒叫一次)。
    通过按KEY_UP,可以进入校准程序,进行触摸屏校准(注意,电容触摸屏不需要校准,所以如果是电容屏,
    按KEY_UP,就相当于清屏一次的效果,不会进行校准)

电阻屏校准方法:
1,下载本代码。
2,按KEY_UP。
3,依次点击屏幕上的5个十字架(十字+小圈圈)的最中央(最好用笔尖)
4,直到屏幕提示:"Touch Screen Adjust OK!"字样。
5,校准完成。
2. 硬件资源
1)RGB灯
RED :LED0 - PB4
GREEN :LED1 - PE6
2)独立按键
KEY0 – PA1
KEY1 – PA15
WK_UP – PA0
3) 正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
4)串口1 (PA9/PA10连接在板载USB转串口芯片CH340上面)
5)触摸屏(电阻式/电容式)
6)蜂鸣器(PE4)
62.3 程序设计
62.3.1 UCOSII相关函数介绍
信号量函数
在这里对本实验用到的UCOSII信号量函数进行介绍,相关代码存放在os_sem.c中。

  1. OSSemCreate函数
    创建信号量函数,其声明如下:
    OS_EVENT *OSSemCreate (INT16U cnt)
    函数描述:
    用于创建一个信号量
    函数形参:
    cnt是信号量计数器(OSEventCnt)的初始值
    函数返回值:
    已创建的信号量的指针
  2. OSSemPend函数
    请求信号量函数,其声明如下:
    void OSSemPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr)
    函数描述:
    请求信号量
    函数形参:
    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:调度器上锁了
    函数返回值:

    注意事项:
    为了防止任务因得不到信号量而处于长期的等待状态,函数OSSemPend允许用参数timeout设置一个等待时间的限制,当任务等待的时间超过timeout时可以结束等待状态而进入就绪状态。如果参数timeout被设置为0,则表明任务的等待时间为无限长。
  3. OSSemPost函数
    发送信号量函数,其声明如下:
    INT8U OSSemPost (OS_EVENT *pevent)
    函数描述:
    用于发送信号量或者称为释放信号量
    函数形参:
    pevent:被请求信号量的指针
    函数返回值:
    OS_ERR_NONE:函数调用成功,信号量被成功地设置
    OS_ERR_SEM_OVF:信号量的值溢出
    OS_ERR_EVENT_TYPE:pevent不是指向信号量的指针
    OS_ERR_PEVENT_NULL:pevent是一个空指针
    4.OSSemDel函数
    删除信号量函数,其声明如下:
    OS_EVENT *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *perr)
    函数描述:
    用于删除信号量并准备挂起所有任务
    函数形参:
    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是一个空指针
    函数返回值:
    pevent :存在错误 (OS_EVENT *)0 : 该信号量被成功删除。
    消息邮箱函数
    在这里对本实验用到的UCOSII消息邮箱函数进行介绍,相关代码存放在os_mbox.c中
  4. OSMboxCreate函数
    创建邮箱函数,其声明如下:
    OS_EVENT *OSMboxCreate (void *pmsg)
    函数描述:
    用于创建邮箱函数
    函数形参:
    pmsg:消息的指针
    函数返回值:
    消息邮箱的指针
    注意事项:
    调用OSMboxCreate前,需先定义msg的初始值。在一般情况下,这个初始值为NULL。但也可以事先定义一个邮箱,然后把这个邮箱的指针作为参数传递到函数OSMboxCreate中,使得一开始就指向一个邮箱。
  5. OSMboxPost函数
    向邮箱发送消息函数,其声明如下:
    INT8U OSMboxPost (OS_EVENT *pevent, void *pmsg)
    函数描述:
    用于向消息邮箱发送消息
    函数形参:
    pevent:消息邮箱的指针
    pmsg :消息指针
    函数返回值:
    OS_ERR_NONE:消息发送成功
    OS_ERR_MBOX_FULL:不能向满邮箱再发送消息
    OS_ERR_EVENT_TYPE:指定的事件不是消息邮箱类型
    OS_ERR_PEVENT_NULL:不能向不存在的消息邮箱发送消息
    OS_ERR_POST_NULL_PTR:消息缓冲区不能为空
  6. OSMboxPend函数
    请求邮箱函数,其声明如下:
    void *OSMboxPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr)
    函数描述:
    请求消息邮箱,就是等待一个消息传送到消息邮箱或取得一个消息数据
    函数形参:
    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:调度器上锁了
    函数返回值:
    NULL:未得到消息 !NULL:预期消息的指针
  7. OSMboxQuery函数
    查询邮箱状态函数,其声明如下:
    UINT8U *OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *p_mbox_data)
    函数描述:
    获取消息邮箱的相关信息
    函数形参:
    pevent:消息邮箱的指针
    p_mbox_data:存放邮箱信息的结构
    函数返回值:
    OS_ ERR_NONE:调用成功
    OS_ERR_EVENT_TYPE:pevent不是指向消息邮箱的指针
    OS_ERR_PEVENT_NULL:不能向不存在的消息邮箱发送消息
    OS_ERR_PDATA_NULL:p_mbox_data是一个空指针
    注意事项:
    必须先建立消息邮箱,然后使用
    5.OSMboxDel函数
    删除邮箱函数,其声明如下:
    OS_EVENT *OSMboxDel (OS_EVENT *pevent, INT8U opt, INT8U *perr)
    函数描述:
    对一个不再使用的消息邮箱及时删除以释放资源
    函数形参:
    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是一个空指针
    函数返回值:
    pevent :存在错误 (OS_EVENT *)0 : 该邮箱被成功删除。
    62.3.2 程序流程图
    在这里插入图片描述

图62.3.2.1 UCOSII信号量和邮箱实验
62.3.3 程序解析
main.c代码
在main.c文件下,除了main函数之外,还有UCOSII任务的一些配置以及6个任务函数。我们先看一下UCOSII任务的一些宏定义,如下代码所示:

/*****************************************************************************/
/* UCOSII任务设置 */

/* START 任务 配置
 * 包括: 任务优先级 堆栈大小 等
 */
#define START_TASK_PRIO                 10      	/* 开始任务的优先级设置为最低 */
#define START_STK_SIZE                  128     	/* 堆栈大小 */
	
OS_STK START_TASK_STK[START_STK_SIZE];       	/* 任务堆栈 */
void start_task(void *pdata);                  	/* 任务函数 */

/* 触摸屏任务 任务 配置
 * 包括: 任务优先级 堆栈大小 等
 */
#define TOUCH_TASK_PRIO                 7       	/* 优先级设置(越小优先级越高) */
#define TOUCH_STK_SIZE                  128     	/* 堆栈大小 */

OS_STK TOUCH_TASK_STK[TOUCH_STK_SIZE];       	/* 任务堆栈 */
void touch_task(void *pdata);                  	/* 任务函数 */

/* LED 任务 配置
 * 包括: 任务优先级 堆栈大小 等
 */
#define LED_TASK_PRIO                   6       	/* 优先级设置(越小优先级越高) */
#define LED_STK_SIZE                    128     	/* 堆栈大小 */

OS_STK LED_TASK_STK[LED_STK_SIZE];            	/* 任务堆栈 */
void led_task(void *pdata);                    	/* 任务函数 */

/* 蜂鸣器 任务 配置
 * 包括: 任务优先级 堆栈大小 等
 */
#define BEEP_TASK_PRIO                  5       	/* 优先级设置(越小优先级越高) */
#define BEEP_STK_SIZE                   128     	/* 堆栈大小 */

OS_STK BEEP_TASK_STK[BEEP_STK_SIZE];          	/* 任务堆栈 */
void beep_task(void *pdata);                   	/* 任务函数 */

/* 主 任务 配置
 * 包括: 任务优先级 堆栈大小 等
 */
#define MAIN_TASK_PRIO                  4       	/* 优先级设置(越小优先级越高) */
#define MAIN_STK_SIZE                   128     	/* 堆栈大小 */

OS_STK MAIN_TASK_STK[MAIN_STK_SIZE];          	/* 任务堆栈 */
void main_task(void *pdata);                   	/* 任务函数 */

/* 按键扫描 任务 配置
 * 包括: 任务优先级 堆栈大小 等
 */
#define KEY_TASK_PRIO                   3       	/* 优先级设置(越小优先级越高) */
#define KEY_STK_SIZE                    128     	/* 堆栈大小 */

OS_STK KEY_TASK_STK[KEY_STK_SIZE];            	/* 任务堆栈 */
void key_task(void *pdata);                    	/* 任务函数 */
/*****************************************************************************/

/**
 * @brief       开始任务
 * @param       pdata : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pdata)
{
    OS_CPU_SR cpu_sr = 0;
    pdata = pdata;
    msg_key = OSMboxCreate((void *)0);  /* 创建消息邮箱 */
    sem_beep = OSSemCreate(0);           	/* 创建信号量 */
    
    OSStatInit();             	/* 开启统计任务 */
    OS_ENTER_CRITICAL();    	/* 进入临界区(关闭中断) */
    
    /* 触摸任务 */
    OSTaskCreateExt((void(*)(void *) )touch_task,
                    (void *          )0,
                    (OS_STK *        )&TOUCH_TASK_STK[TOUCH_STK_SIZE - 1],
                    (INT8U          )TOUCH_TASK_PRIO,
                    (INT16U         )TOUCH_TASK_PRIO,
                    (OS_STK *        )&TOUCH_TASK_STK[0],
                    (INT32U         )TOUCH_STK_SIZE,
                    (void *          )0,
                    (INT16U         )OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR 
| OS_TASK_OPT_SAVE_FP);
    /* LED任务 */
    OSTaskCreateExt((void(*)(void *) )led_task,
                    (void *          )0,
                    (OS_STK *        )&LED_TASK_STK[LED_STK_SIZE - 1],
                    (INT8U          )LED_TASK_PRIO,
                    (INT16U         )LED_TASK_PRIO,
                    (OS_STK *        )&LED_TASK_STK[0],
                    (INT32U         )LED_STK_SIZE,
                    (void *          )0,
                    (INT16U         )OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR
 | OS_TASK_OPT_SAVE_FP);
    /* 蜂鸣器任务 */
    OSTaskCreateExt((void(*)(void *) )beep_task,
                    (void *          )0,
                    (OS_STK *        )&BEEP_TASK_STK[BEEP_STK_SIZE - 1],
                    (INT8U          )BEEP_TASK_PRIO,
                    (INT16U         )BEEP_TASK_PRIO,
                    (OS_STK *        )&BEEP_TASK_STK[0],
                    (INT32U         )BEEP_STK_SIZE,
                    (void *          )0,
                    (INT16U         )OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR 
| OS_TASK_OPT_SAVE_FP);
    /* 主任务 */
    OSTaskCreateExt((void(*)(void *) )main_task,
                    (void *          )0,
                    (OS_STK *        )&MAIN_TASK_STK[MAIN_STK_SIZE - 1],
                    (INT8U          )MAIN_TASK_PRIO,
                    (INT16U         )MAIN_TASK_PRIO,
                    (OS_STK *        )&MAIN_TASK_STK[0],
                    (INT32U         )MAIN_STK_SIZE,
                    (void *          )0,
                    (INT16U         )OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR 
| OS_TASK_OPT_SAVE_FP);
    /* 按键任务 */
    OSTaskCreateExt((void(*)(void *) )key_task,
                    (void *          )0,
                    (OS_STK *        )&KEY_TASK_STK[KEY_STK_SIZE - 1],
                    (INT8U          )KEY_TASK_PRIO,
                    (INT16U         )KEY_TASK_PRIO,
                    (OS_STK *        )&KEY_TASK_STK[0],
                    (INT32U         )KEY_STK_SIZE,
                    (void *          )0,
                    (INT16U         )OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR 
| OS_TASK_OPT_SAVE_FP);
    OS_EXIT_CRITICAL();                	/* 退出临界区(开中断) */
    OSTaskSuspend(START_TASK_PRIO); 	/* 挂起开始任务 */
}

/**
 * @brief       LED任务
 * @param       pdata : 传入参数(未用到)
 * @retval      无
 */
void led_task(void *pdata)
{
    uint8_t t;
    while (1)
    {
        t++;
        delay_ms(10);
        if (t == 8)LED0(1); 	/* LED0灭 */
        if (t == 100)       	/* LED0亮 */
        {
            t = 0;
            LED0(0);
        }
    }
}

/**
 * @brief       蜂鸣器任务
 * @param       pdata : 传入参数(未用到)
 * @retval      无
 */
void beep_task(void *pdata)
{
    uint8_t err;
    while (1)
    {
        OSSemPend(sem_beep, 0, &err);   /* 请求信号量 */
        BEEP(1);        /* 打开蜂鸣器 */
        delay_ms(60);
        BEEP(0);        /* 关闭蜂鸣器 */
        delay_ms(940);
    }
}

/**
 * @brief       触摸屏任务
 * @param       pdata : 传入参数(未用到)
 * @retval      无
 */
void touch_task(void *pdata)
{
    uint32_t cpu_sr;
    uint16_t lastpos[2];    /* 最后一次的数据 */
    while (1)
    {
        tp_dev.scan(0);

        if (tp_dev.sta & TP_PRES_DOWN)  /* 触摸屏被按下 */
        {
            if (tp_dev.x[0] < lcddev.width && tp_dev.y[0] < lcddev.height 
&& tp_dev.y[0] > 120)
            {
                if (lastpos[0] == 0XFFFF)
                {
                    lastpos[0] = tp_dev.x[0];
                    lastpos[1] = tp_dev.y[0];
                }
                OS_ENTER_CRITICAL();/*进入临界段,防止其他任务,打断LCD操作,导致液晶乱序*/
                lcd_draw_bline(lastpos[0], lastpos[1], tp_dev.x[0], tp_dev.y[0],
2, RED);   /* 画线 */
                OS_EXIT_CRITICAL();
                lastpos[0] = tp_dev.x[0];
                lastpos[1] = tp_dev.y[0];
            }
        }
        else
        {
            lastpos[0] = 0XFFFF;
            delay_ms(10);   /* 没有按键按下的时候 */
        }
    }
}

/**
 * @brief       主任务
 * @param       pdata : 传入参数(未用到)
 * @retval      无
 */
void main_task(void *pdata)
{
    uint32_t key = 0;
    uint8_t err;
    uint8_t semmask = 0;
    uint8_t tcnt = 0;

    while (1)
    {
        key = (uint32_t)OSMboxPend(msg_key, 10, &err);
        switch (key)
        {
            case KEY0_PRES: /* 控制DS1,并清除触摸区域 */
                LED1_TOGGLE();
                lcd_fill(0, 121, lcddev.width - 1, lcddev.height - 1, WHITE);
                break;
 
            case KEY1_PRES: /* 发送信号量 */
                semmask = 1;
                OSSemPost(sem_beep);
                break;

            case WKUP_PRES: /* 校准 */
                OSTaskSuspend(TOUCH_TASK_PRIO); 	/* 挂起触摸屏任务 */

                if ((tp_dev.touchtype & 0X80) == 0)
                {
                    tp_adjust();
                }
                OSTaskResume(TOUCH_TASK_PRIO);  	/* 解挂 */
                ucos_load_main_ui();            	/* 重新加载主界面 */
                break;
        }

        if (semmask || sem_beep->OSEventCnt)    	/* 需要显示sem */
        {
/* 显示信号量的值 */
            lcd_show_xnum(192, 50, sem_beep->OSEventCnt, 3, 16, 0X80, BLUE);   
            if (sem_beep->OSEventCnt == 0)semmask = 0;  /* 停止更新 */
        }
        if (tcnt == 10) /* 0.6秒更新一次CPU使用率 */
        {
            tcnt = 0;
            lcd_show_xnum(192, 30, OSCPUUsage, 3, 16, 0, BLUE);/* 显示CPU使用率 */
        }
        tcnt++;
        delay_ms(10);
    }
}

/**
 * @brief       按键扫描任务
 * @param       pdata : 传入参数(未用到)
 * @retval      无
 */
void key_task(void *pdata)
{
    uint8_t key;
    while (1)
    {
        key = key_scan(0);
        if (key)OSMboxPost(msg_key, (void *)key); /* 发送消息 */
        delay_ms(10);
    }
}

上面就是对创建的start_task、led0_task、touch_task、led1_task、main_task 和 key_task等6个任务的参数进行配置,例如优先级、堆栈大小和任务函数的声明及定义。
这6个任务做的事情在前面程序流程图里面已经有说明,这里就不多说了。
下面看一下main主函数的代码:

OS_EVENT *msg_key;      	/* 按键邮箱事件块指针 */
OS_EVENT *sem_beep;     	/* 蜂鸣器信号量指针 */

int main(void)
{
    sys_cache_enable();                   	/* 打开L1-Cache */
    HAL_Init();                            	/* 初始化HAL库 */
    sys_stm32_clock_init(240, 2, 2, 4);	/* 设置时钟, 480Mhz */
    delay_init(480);                      	/* 延时初始化 */
    usart_init(115200);                   	/* 串口初始化为115200 */
    mpu_memory_protection();             	/* 保护相关存储区域 */
    led_init();                            	/* 初始化LED */
    lcd_init();                            	/* 初始化LCD */
    key_init();                            	/* 初始化按键 */
    beep_init();                           	/* 初始化蜂鸣器 */
    tp_dev.init();                        	/* 触摸屏初始化 */
    ucos_load_main_ui();                 	/* 加载主界面 */
    OSInit();                              	/* UCOS初始化 */
    OSTaskCreateExt((void(*)(void *) )start_task,     /* 任务函数 */
                    (void *          )0,                     /* 传递给任务函数的参数 */
/* 任务堆栈栈顶 */
                    (OS_STK *        )&START_TASK_STK[START_STK_SIZE - 1], 
                    (INT8U          )START_TASK_PRIO,			/* 任务优先级 */
                    (INT16U         )START_TASK_PRIO,/* 任务ID,这里设置为和优先级一样 */
                    (OS_STK *        )&START_TASK_STK[0],   	/* 任务堆栈栈底 */
                    (INT32U         )START_STK_SIZE,         	/* 任务堆栈大小 */
                    (void *          )0,                       		/* 用户补充的存储区 */
                    (INT16U         )OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR 
| OS_TASK_OPT_SAVE_FP); 
/* 任务选项,为了保险起见,所有任务都保存浮点寄存器的值 */
    OSStart();    /* 开始任务 */
}

这一章的main函数的运行流程比上一章复杂了一些,我们创建了消息邮箱msg_key,用于按键任务和主任务之间的数据传输(传递键值)。另外创建了信号量sem_led1,用于LED1任务和主任务之间的通信。
在代码中,我们使用了UCOSII提供的CPU统计服务,通过OSStatInit初始化CPU统计任务,然后在主任务中显示CPU使用率。
另外,在主任务中,我们用到了任务的挂起和恢复函数,在执行触摸校准的时候,我们必须先将触摸屏任务挂起,待校准完成之后,再恢复触摸屏任务。这是因为触摸屏校准和触摸屏任务都用到了触摸屏和TFTLCD,而这两个东西是不支持多任务占用的,所以必须采用独占的方式使用,否则可能导致数据错乱。
62.4 下载验证
将程序下载到开发板后,可以看到LCD显示界面如图62.4.1所示:
在这里插入图片描述

图62.4.1 初始化界面
从上图可以看到,默认情况下,CPU使用率仅为1%。此时通过在触摸区域(Touch Area)画图,可以看到CPU使用率飙升,这说明触摸屏任务是一个很占CPU的任务;通过按KEY0,可以控制LED1的亮灭;通过按KEY1则可以控制蜂鸣器的发声(连续按下多次后,可以看到蜂鸣每隔1秒叫一次),同时,可以在LCD上面看到信号量的当前值;通过按下WK_UP可以进入校准程序,对触摸屏进行校准(注意,电容触摸屏不需要校准,所以如果是电容屏,按KEY_UP,就相当于清屏一次的效果,不会进行校准)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【正点原子STM32连载】 第六十二章 UCOSII实验2-信号量和邮箱 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1 的相关文章

  • 字符串的定义及何时添加‘\0‘问题

    定义字符串的六种形式 xff1a char arr 61 34 hello world 34 原理 xff1a 字符串常量的值本质上是第一个字符的地址 char arr arr 61 34 hello world 34 的改写 char a
  • 什么是云技术?

    云技术是指在广域网或局域网内将硬件 软件 网络等系列资源统一起来 xff0c 实现数据的计算 储存 处理和共享的一种托管技术
  • 深度学习神经网络归一化方法及MATLAB函数

    归一化方法及 MATLAB函数 数据归一化方法是神经网络预测前对数据常做的一种处理方法 数据归一化处理把所有数据都转化为 xff3b 0 xff0c 1 xff3d 之间的数 xff0c 其目的是取消各维数据间数量级差别 xff0c 避免因
  • 2022数模国赛B题无人机第一题第一小问的简单编程

    前言 2022年国赛B题是关于无人机定位的抽象模型 xff0c 总体难度不大 接下来简单介绍一下第一题第一小问的程序实现 xff0c 当时国赛仓促 xff0c 写的比较简略 xff0c 仅供参考 背景介绍 无源定位 第一个关键词是无源定位
  • Linux基础之网络编程

    网络编程篇 一 网络编程概述二 字节序三 socket编程步骤四 socket服务端和客户端代码的初步实现五 简单的ftp项目实现的功能有哪些 xff1f 六 ftp项目服务器和客户端代码实现 一 网络编程概述 1 进程间通信 xff1a
  • F450无人机组装与调试

    文章目录 认识无人机零部件机架图片 电机电调螺旋桨飞控套件 包括飞控 LED信号灯 xff0c GPS模块 xff0c 电源管理模块 遥控器及遥控器接收机电池护桨 确认工具清单组装过程1 组装机架2 组装电机判断电机正反选择螺丝组装电机连接
  • Openstack-mitaka安装部署

    openstack 一台controller xff0c 一台compute 且两台均为双网卡 xff0c ens33为主网卡 xff0c ens36不需要配置IP 主机名称 网卡名称 网卡类型 网卡IP ens33 controllere
  • Vue history模式路由 部署到二级目录 配置

    1 nginx配置 https xxxx com cms gt C static server cms location cms root C static server 项目部署资源所在磁盘目录 index index html try
  • 【Docker】将本地镜像推送到远程库/私有库

    前言 这里记录如何将本地镜像推送到远程库和私有库 区别 xff0c 一个是存放到阿里云 xff0c 同一个团队可以登录到同一个阿里云仓库 xff0c 去拉取镜像 一个是存放到本地私有库 xff0c 同一个团队可以连接同一个私有库 xff0c
  • HTTP协议--几种数据传输方式

    1 xff09 无状态 http协议是一种自身不对请求和响应之间的通信状态进行保存的协议 xff0c 即无状态协议 这种设置的好处是 xff1a 更快的处理更多的请求事务 xff0c 确保协议的可伸缩性 不过随着web的不断发展 xff0c
  • PDU 超链接

    http blog sina com cn s blog 453226290102wvnu html http blog sina com cn s blog 453226290102wvnv html http blog sina com
  • Canal监控MySQL数据到Kafka详细步骤(jdk+zookeeper+kafka+canal+mysql)

    目录 一 前言二 环境准备三 安装JDK四 安装zookeeper五 安装Kafka六 安装MySQL七 安装canal服务端 xff08 canal监控mysql数据发送到kafka xff09 八 测试是否可以监控到数据九 结语 一 前
  • 流媒体服务器搭建

    本文介绍如何在阿里云 腾讯云等云主机上搭建流媒体服务器 xff0c 包括如何选择云主机配置 如何选择带宽和流媒体服务器软件选择等 流媒体服务器是支撑视频播出的基础系统 xff0c 具有视频直播 视频点播的播出能力 xff0c 有些使用场景下
  • 什么是BGP

    文章目录 1 基本概念什么是BGPBGP路由协议的特点IBGP水平分割规则BGP的路由器号 Router ID BGP工作原理BGP分类 1 基本概念 自治系统 xff0c 指的是在同一个组织管理下 使用相同策略的设备的集合 xff1b 不
  • Web服务------Nginx域名重定向(Location匹配与Rewrite重写)

    目录 一 常用的Nginx正则表达式二 location1 location 的分类2 location 常用的匹配规则3 location 优先级4 location 示例说明5 优先级总结6 匹配规则 二 rewrite1 rewrit
  • 华为云-计算云服务介绍

    前言 相信很多小伙伴在刚开始接触各类云产品的时候 xff0c 被各种各样的云产品类如规格 型号 价格 适用场景等问题所困扰 本文就给大家介绍一下华为云常见云产品的规格区别和适用场景 帮助大家选择合适的云产品 文章目录 前言一 计算云服务1
  • 数据资源 | 八大板块!数据公开下载渠道

    本文综合整理自网站企研 中国学术大数据平台 xff08 https r qiyandata com xff09 来源 xff1a 企研 中国学术大数据平台 公共数据资源 目录 三农 地理信息 生态环境 碳中和 调查数据 省级统计局 省级政府
  • [C++] std::vector

    std vector template lt class T class Alloc 61 allocator lt T gt gt class vector generic template vector是表示可以改变大小的数组的序列容器
  • centos克隆

    下一步 下一步 选中创建完整克隆 启动克隆的系统 编辑 xff1a vi etc udev rules d 70 persistent net rules 修改之后 修改端口号和attr 修改完 执行 重启 xff1a reboot pin
  • 用pyhon下载jupyter遇到的问题(pip版本过低)

    首先安装jupyter 的前提是需要提前安装python 3 3版本及以上 然后输入 pip install jupyter 出现下面问题 python V 在命令行窗口输入 xff0c 验证python的版本 pip install ju

随机推荐

  • ubuntu利用usb_cam打开摄像头

    一 安装摄像头驱动usb cam 想要标定多个相机 xff0c 首先得把相机打开吧 xff0c usb cam是针对usb摄像头的ros驱动包 xff0c 简单来说就是得有这个功能包 xff0c 才能在ros中把摄像头打开 首先打开终端 x
  • Jmeter性能测试(1)---基础介绍

    jmeter xff08 一 xff09 基础介绍 参考书籍 xff1a 段念 软件性能测试与案例剖析 第二版 推荐一本书 零成本实现web性能测试 基于Apache jmeter xff0c 主要内容是一些关于jmeter的实战使用 xf
  • 利用Opencv实现物体的跟踪(1)

    目前感觉 xff0c 利用opencv实现的物体追踪 xff0c 关键要设置好你所检测对象的area xff0c 不然很容易出现混乱 本人也是自学 xff0c 敬请批评指正 import cv2 定义运算的核算子 BLUR RADIUS 6
  • C语言连连看秒杀辅助

    图像处理第一课 连连看秒杀辅助 项目效果 直接使用C语言 xff0c 实现 连连看 最强辅助 项目分析 项目的技术核心 不是逆向 xff0c 而是图像处理 图像处理 xff0c 更高维度的技术手段 电影中的图像处理应用 无人机战争 电影 完
  • 推荐10款适合C/C++开发人员的IDE

    IDE是程序员用于编程的应用程序或软件 IDE主要包括三部分 xff0c 即源代码编辑器 xff0c 构建自动化工具 xff08 编译器 xff09 和调试器 源代码编辑器是程序员可以编写代码的地方 xff0c 而程序员使用构建自动化工具来
  • C++五子棋人机对战

    目录 本教程配套视频 1 项目目标 2 效果演示 3 创建项目 4 项目框架设计 4 1 设计项目框架 4 2 根据设计框架创建类 5 给类添加主要接口 5 1 设计棋盘类Chess的主要接口 5 2 设计AI类的主要接口 5 3 设计Ma
  • VSCode这13款插件也太好用了

    又见VsCode Visual Studio Code xff08 简称VS Code xff09 是一个由微软开发 xff0c 同时支持Windows Linux 和 macOS 等操作系统的免费代码编辑器 xff0c 在2019年的St
  • C语言和C++的区别和联系,大多数人都说错了

    前言 C语言和C 43 43 到底是什么关系 xff1f 首先C 43 43 和C语言本来就是两种不同的编程语言 xff0c 但C 43 43 确实是对C语言的扩充和延伸 xff0c 并且对C语言提供后向兼容的能力 对于有些人说的C 43
  • C++之父做决定了:内部自救!

    进入2023年 xff0c 技术圈都在围观大洋彼岸的聊天机器ChatGPT xff0c 但对于编程圈而言 xff0c 没有什么比内存安全更能引起热议 近期美国国家安全局 xff08 NSA xff09 点名批评C 43 43 xff0c 建
  • 使用数组实现:约瑟夫环问题

    使用数组实现约瑟夫环问题的写法 xff1a include lt iostream gt include lt vector gt using namespace std vector lt int gt josephus int n in
  • 何谓春招?

    一 什么是春招 xff1a 春招是指一年中在春季进行的毕业生招聘活动 xff0c 很多公司在这个时间开始准备新一年的业务计划和预算 春招可以理解为是秋招的大型补招 xff0c 规模是秋招的60 左右 xff0c 抓住校园招聘的最后机会上岸心
  • Jmeter性能测试(2)---脚本录制

    jmeter xff08 二 xff09 录制脚本 对大多数刚开始接触性能测试的人来说 xff0c 代码功力可能不是太好 xff0c 我们可以通过工具 xff0c 录制脚本来进行测试 xff0c 以达到我们的目的 一般来讲 xff0c 录制
  • 为什么越来越多的IT人考软考?

    近几年随着国家计算机与软件技术的发展 xff0c 每年报名参加软考考试的人也越来越多 据工信部新闻发布会消息 xff0c 计算机软件与通信专业技术人员职业资格考试累计报考人数超过485万 xff0c 2022年报考人数129万人 01 为什
  • javascript里裁切图片插件库

    第三方库 cropperjs 适用于JS Vue Jquery 安装 npm install cropperjs 按照格式要求配置如下 lt Wrap the image or canvas element with a block ele
  • Ubuntu安装ROS系统

    昨天安装ros系统用了一天时间 xff0c 所以安装ros前一定要做好心理准备 希望我的经验能够有所帮助 以下方式是安装ros系统的一种方法 xff0c 不是具体的安装步骤 Ubuntu安装ROS系统方法 xff1a 一 Bilibili观
  • ROS系统下完成TCP通信 C语言编程

    ROS系统下完成TCP通信 xff0c 服务端和客户端C语言编程 服务端 xff08 Server xff09 xff1a span class token macro property span class token directive
  • 基于图神经网络强化学习解决车辆路径规划问题

    一 实验要求 复现以下论文的方法和结果 Duan L Zhan Y Hu H Gong Y Wei J Zhang X Xu Y Efficiently solving the practical vehicle routing probl
  • ERROR: Cannot uninstall ‘PyYAML‘. It is a distutils installed project and thus we cannot

    pip install upgrade nni ignore installed i https pypi tuna tsinghua edu cn simple 成功
  • 【正点原子MP157连载】第四章 STM32初体验-摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南

    1 xff09 实验平台 xff1a 正点原子STM32MP157开发板 2 xff09 购买链接 https item taobao com item htm amp id 61 629270721801 3 xff09 全套实验源码 4
  • 【正点原子STM32连载】 第六十二章 UCOSII实验2-信号量和邮箱 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1 xff09 实验平台 xff1a 正点原子MiniPro H750开发板 2 xff09 平台购买地址 xff1a https detail tmall com item htm id 61 677017430560 3 xff09 全