首页 > 自考资讯 > 自考知识

正点原子stm32开发板原理图,正点原子stm32f1

头条共创 2024-07-05

1)实验平台:Alienek NANO STM32F411 V1开发板

2)摘自《正点原子STM32F4 开发指南(HAL 库版》:更多信息请关注我们的官方微信:正点原子

9d1edd66723f4966b44d3cab502c055f~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720787602&x-signature=i426MvIAwdmWJm%2FuVdmj2kYbBLA%3D第37 章UCOSII 实验2 - 信号量和邮箱

在上一章中,您学习了如何使用UCOSII及其任务调度,但没有使用任务。

任务之间的同步和通信。在本章中,您将学习任务间通信的两种最基本的方法:信号量和邮箱。本章分为:

接下来的几部分:

37.1 UCOSII信号量和邮箱概述

37.2 硬件设计

37.3 软件设计

37.4 下载验证

37.1 UCOSII信号量和邮箱概述

当多个任务在系统中运行时,各个任务往往需要访问相同的共享资源而不发生冲突,或者相互支持和依赖,在某些情况下,各个任务能够顺利运行,甚至可能会相互施加必要的限制和限制。以确保正常运行。因此,操作系统必须具有协调任务执行的能力,使它们能够同步而不发生冲突,顺利运行,而不会造成灾难性的后果。

例如,如果任务A和任务B共享一台打印机,则只有当任务A释放打印机时,任务B才会处于等待状态,因为它无法获得使用打印机的权限。系统可以启动任务B来获取打印机的使用权。如果这两个任务不这样做,就会出现大混乱。

任务之间的同步依赖于任务之间的通信。在UCOSII中,任务之间的通信是通过信号量、邮箱(消息邮箱)、消息队列等称为事件的中间链路来实现的。本章仅介绍信号量和邮箱。消息队列将在下一章介绍。

事件

两个任务通过事件进行通信的示意图如图37.1.1所示。

7ac66502494f47fc8e891c14633d913f~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720787602&x-signature=sTcbhX%2FCUN6x8TPYmsoe2nM1EOs%3D图37.1.1 两个任务使用事件进行通信的示意图

在图37.1.1 中,任务1 是发送者,任务2 是接收者。任务1 负责向事件发送信息。此操作称为发送事件。任务2 通过事件读取操作查询事件。如果有信息,则读取;如果没有信息,则等待。读取事件的行为称为请求事件。为了统一描述事件的数据结构,UCOSII使用了一种称为事件控制块(ECB)的数据结构来描述信号量、邮箱(消息邮箱)、消息队列等事件。事件控制块包含所有与事件相关的数据,包括待处理任务的列表。事件控制块的结构定义如下。

类型定义结构

{

INT8UOS事件类型;

//事件类型

INT16UOSEventCnt;

//信号量计数器

无效*OSEventPtr;

//指向消息或消息队列的指针

INT8UOSEventGrp;

//任务组等待事件

INT8U OSEventTbl[OS_EVENT_TBL_SIZE];//任务等待表

#if OS_EVENT_NAME_EN 0u

INT8U

*操作系统事件名称;

//事件名称

万一

操作系统_事件;

信号

信号量是一种事件。使用信号量的主要目的是标记共享资源。

表示共享资源的使用情况。这样,当任务访问共享资源时,它可以首先更改此标志。

通过运行查询,您可以了解资源占用情况并决定您的操作。

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

二进制信号量就像家里的固定电话:在任何给定时间只有一个人可以使用它。 N 值信号量就像公共信号量

共有1个电话亭可供多人(N)同时使用。

在UCOSII中,二进制信号量称为互斥信号量,N值信号量称为计数信号量。

换句话说,它是一个常规信号量。本章介绍常规信号量和互斥信号量。请参阅嵌入。

“UCOSII实时操作系统原理与应用”第5.4节。

接下来我们看一下UCOSII中一些与信号量相关的函数(未全部列出,如下)。

1)创建信号量函数

在使用信号量之前,您必须使用函数OSSemCreate 创建该函数的原型。

为了:

OS_EVENT *OSSemCreate (INT16U cnt);

该函数的返回值是指向创建的信号量的指针,参数cnt是信号量计数器(OSEventCnt)。

初始值。

2)请求信号量函数

任务调用函数OSSemPend 来请求信号量。函数原型为:

void OSSemPend (OS_EVENT *pevent, INT16U 超时, INT8U *err);

参数pevent是指向请求信号量的指针,timeout是等待时限,err是错误信息。

为了防止任务长时间等待而无法获取信号量,函数OSSemPend 接受参数。

timeout设置等待时间,当任务等待时间超过timeout时退出等待状态。

进入就绪状态。如果参数timeout设置为0,则表示任务可以无限期等待。

3)传输信号量函数

任务必须获取信号量,并在访问共享资源后释放信号量。也称为“信号量释放”。

要发送信号量,需要通过OSSemPost 函数发送信号。 OSSemPost 函数对信号量进行计数

在对信号量进行操作之前,请检查是否有任何任务正在等待该信号量。如果没有,设置信号量计数器

OSEventCnt 加一。如果存在,则调用调度程序OS_Sched() 来运行等待任务中优先级最高的任务。

任务。 OSSemPost函数原型如下:

INT8U OSSemPost(OS_EVENT *pevent);

其中,pevent是信号量指针,如果函数调用成功,返回值为OS_ON_ERR。

根据具体错误返回OS_ERR_EVENT_TYPE、OS_SEM_OVF。

4)信号量函数的删除

当您的应用程序不再需要信号量时,您可以调用函数OSSemDel将其删除。

该函数的原型是:

OS_EVENT *OSSemDel (OS_EVENT *pevent,INT8U opt, INT8U *err);

其中,pevent为要删除的信号量指针,opt为删除条件选项,err为错误信息。

邮政

多任务操作系统经常需要在任务之间传输数据(这种数据称为“消息”)。

为了达到这个目的,我们可以在内存中创建一个存储空间作为数据(以“信息”的形式进行通信)。

缓冲。该缓冲区称为消息缓冲区,是在任务之间传输数据(消息)的最简单方法。

传递的是一个指向消息缓冲区的指针。用于传输消息缓冲区指针的数据结构称为邮箱(消息邮箱)。

在UCOSII中,通过事件控制块的OSEventPrt传递消息缓冲区指针,同时创建事件控制块。

如果控制块成员OSEventType 是常量OS_EVENT_TYPE_MBOX,则事件控制块称为消息邮箱。

接下来我们看一下UCOSII中与消息邮箱相关的一些功能。

1)创建邮箱功能

邮箱的创建是通过OSMboxCreate函数实现的。该函数的原型是:

OS_EVENT *OSMboxCreate (void *msg);

函数参数msg是指向消息的指针,函数返回值是指向消息邮箱的指针。

要调用OSMboxCreate函数,必须首先为msg定义一个初始值。通常,该初始值为:

NULL; 但是,您也可以预先定义一个邮箱,并将指向该邮箱的指针作为参数传递给函数。

OSMboxCreate从一开始就指向邮箱。

2)发送消息到邮箱的功能

任务可以通过调用OSMboxPost函数向消息邮箱发送消息。该函数的原型是:

INT8U OSMboxPost (OS_EVENT *pevent,void *msg);

其中pevent是消息邮箱指针,msg是消息指针。

3)请求邮箱功能

如果任务请求邮箱,它必须调用OSMboxPend 函数。该功能主要作用是显示邮箱。

框指针OSEventPtr 是否为NULL。如果不为NULL,则将邮箱中的消息指针返回给调用函数。

当运行任务时邮箱指针出错时,使用OS_NO_ERR通过函数参数通知任务消息已成功检索。

如果OSEventPtr为NULL,则任务进入等待状态并触发任务的调度。

OSMboxPend函数的原型是:

void *OSMboxPend (OS_EVENT *pevent, INT16U 超时, INT8U *err);

其中pevent是请求邮箱指针,timeout是等待时限,err是错误信息。

4)邮箱状态查询功能

任务可以通过调用函数OSMboxQuery来查询邮箱的当前状态。函数原型为:

INT8U OSMboxQuery(OS_EVENT *pevent,OS_MBOX_DATA *pdata);

其中,pevent是消息邮箱指针,pdata是存储邮箱信息的结构体。

5) 邮箱删除功能

当某个邮箱不再使用时,可以通过调用函数OSMboxDel将其删除。

数值原型为:

OS_EVENT *OSMboxDel(OS_EVENT *pevent,INT8U opt,INT8U *err);

其中pevent是消息邮箱指针,opt是删除选项,err是错误消息。

UCOSII 信号量和邮箱的介绍就到此结束。有关更详细的介绍,请参阅嵌入式实时操作。

UCOSII 系统原理与应用》第5 章。

37.2 硬件设计

本节实验功能介绍:本章我们在UCOSII中创建6个任务:启动任务、LED0任务和LED1任务。

数码管显示任务、按键扫描任务和主任务分别用于创建信号量、创建邮箱和初始化。

统计任务和其他任务的创建,以及挂起的LED0任务,用于DS0控制来通知程序执行状态。

LED1 任务用于测试信号量。这将导致DS1 编号在每次获取信号量时闪烁。

码管显示任务用于测试数码管显示,按键扫描任务用于按键扫描,具有最高优先级检索按键值。

通过邮箱发送消息。主要任务是查询消息邮箱获取键值,并根据键值进行信号量发送。

(DS1控制),控制数码管显示变化。

使用的硬件资源如下。

1)指示灯DS0、DS1

2)两个按键(KEY0/KEY1)

3)数码管

我们已经在之前的研究中介绍过这些。

37.3 软件设计

本章我们将修改第17章的实验(实验12)。具体方法与上一章完全相同,本章不再详细介绍。

添加UCOSII代码后,只需修改main.c函数,输入以下代码:

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

//启动任务

//设置任务优先级

#定义START_TASK_PRIO

10 //设置启动任务优先级为最低

//设置任务栈大小

#定义START_STK_SIZE

64

//任务栈

OS_STK START_TASK_STK[START_STK_SIZE];

//任务函数

无效start_task(无效* pdata);

//LED0任务

//设置任务优先级

#定义LED0_TASK_PRIO

7

//设置任务栈大小

#定义LED0_STK_SIZE

64

//任务栈

OS_STK LED0_TASK_STK[LED0_STK_SIZE];

//任务函数

无效led0_task(无效*pdata);

//乳腺导管显示任务

//设置任务优先级

定义#SMG_TASK_PRIO

6

//设置任务栈大小

定义#SMG_STK_SIZE

64

//任务栈

OS_STK SMG_TASK_STK[SMG_STK_SIZE];

//任务函数

无效smg_task(无效* pdata);

//LED1任务

//设置任务优先级

#定义LED1_TASK_PRIO

//设置任务栈大小

#定义LED1_STK_SIZE

128

//任务栈

OS_STK LED1_TASK_STK[LED1_STK_SIZE];

//任务函数

无效led1_task(无效*pdata);

//主线任务

//设置任务优先级

#定义MAIN_TASK_PRIO

//设置任务栈大小

#定义MAIN_STK_SIZE

128

//任务栈

OS_STK MAIN_TASK_STK[MAIN_STK_SIZE];

//任务函数

无效main_task(无效* pdata);

//按键扫描任务

//设置任务优先级

定义#KEY_TASK_PRIO

3

//设置任务栈大小

#定义KEY_STK_SIZE

64

//任务栈

OS_STK KEY_TASK_STK[KEY_STK_SIZE];

//任务函数

无效key_task(无效* pdata);

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

OS_EVENT * msg_key;

//按钮邮箱事件块指针

OS_EVENT * sem_led1;

//LED1信号量指针

//公共负数数组

//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,全部销毁

u8

smg_num[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6,0xee,0x3e,0x9c,0x7a,0x9e,0x8e,0

x01,0x00};

u8 smg_duan=0;//选择乳腺导管段

int 主函数(无效)

{

HAL_Init();

//初始化HAL库

Stm32_Clock_Init(96,4,2,4);

//设置时钟,96Mhz

延迟初始化(96);

//初始化延迟函数

LED_Init();

//初始化连接LED的硬件接口

KEY_Init();

//初始化按钮

LED_SMG_Init();

//初始化数码管

OSInit();

OSTaskCreate(start_task,(无效

*)0,(OS_STK

*)START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建启动任务

操作系统启动();

}

//启动任务

无效启动任务(无效*pdata)

{

OS_CPU_SR cpu_sr=0;

pdata=pdata;

msg_key=OSMboxCreate((void*)0); //创建消息邮箱

sem_led1=OSSemCreate(0);

//创建信号量

OSStatInit();

//统计任务初始化有大约1秒的延迟。

OS_ENTER_CRITICAL();

//进入临界区(不能被中断打断)

OSTaskCreate(led0_task,(void *)0,

(OS_STK*)LED0_TASK_STK[LED0_STK_SIZE-1], LED0_TASK_PRIO);

OSTaskCreate(smg_task,(void *)0,

(OS_STK*)SMG_TASK_STK[SMG_STK_SIZE-1],SMG_TASK_PRIO);

OSTaskCreate(led1_task,(void *)0,

(OS_STK*)LED1_TASK_STK[LED1_STK_SIZE-1], LED1_TASK_PRIO);

OSTaskCreate(main_task,(void *)0,

(OS_STK*)MAIN_TASK_STK[MAIN_STK_SIZE-1],MAIN_TASK_PRIO);

OSTaskCreate(key_task,(void *)0,

(OS_STK*)KEY_TASK_STK[KEY_STK_SIZE-1],KEY_TASK_PRIO);

OSTaskSuspend(START_TASK_PRIO); //暂停已启动的任务。

OS_EXIT_CRITICAL();

//退出临界区(可能会被中断中断)

}

//LED0任务

无效led0_task(无效*pdata)

{

u8t;

同时(1)

{

t++;

延迟毫秒(10);

if(t==8)LED0=1; //LED0 熄灭。

如果(t==100)

//LED0点亮

{

t=0;

LED0=0;

}

}

}

//LED1任务

无效LED1_task(无效*pdata)

{

u8错误;

同时(1)

{

OSSemPend(sem_led1,0,错误);

LED1=0;

延迟毫秒(200);

LED1=1;

延迟毫秒(800);

}

}

//乳腺导管显示任务

无效smg_task(无效* pdata)

{

同时(1)

{

LED_Write_Data(smg_num[smg_duan],7);//乳腺管显示

LED_Refresh();//更新显示

延迟毫秒(10);

}

}

//主线任务

无效主任务(无效*pdata)

{

u32 键=0;

u8错误;

同时(1)

{

key=(u32)OSMboxPend(msg_key,10,err);

开关(钥匙)

{

case KEY0_PRES://发送信号量

OSSemPost(sem_led1);

休息;

case KEY1_PRES://数码管显示加1

smg_段++;

如果(smg_duan==17)smg_duan=0;

休息;

}

延迟毫秒(10);

}

}

//按键扫描任务

无效key_task(无效* pdata)

{

u8键;

同时(1)

{

键=KEY_Scan(0);

if(key)OSMboxPost(msg_key,(void*)key);//发送消息

延迟毫秒(10);

}

}

在这部分代码中,我们创建了六个任务:start_task、led0_task、smg_task、led1_task 和main_task。

key_task 和key_task 的优先级分别为10 和7-3,堆栈大小除main_task 为128 外均为64。

运行这个程序的过程比上一章稍微复杂一些。我为按键任务创建了一个消息邮箱msg_key。

还为LED1 任务和导向器创建了一个信号量sem_led1,用于主任务之间的数据传输(传递键值)。

服务之间的通信。

这里我们介绍一下软件设计部分。

37.4 下载验证

代码编译成功后,可以通过将代码下载到NANO STM32F4 V1来查看数码管显示。

如图37.4.1所示:

1cfb41c72c3b4dbba5ab3ef53b89c01f~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720787602&x-signature=Ok%2Fu5ZSQQ3UaU%2FE0dJoG0O2KmQU%3D 图37.4.1 初始界面

数码管显示0,同时按下KEY1按键,DS0闪烁,按下KEY0按键更新显示。

DS0 键将闪烁一次。如果多次按DS0,该键将闪烁多次。

版权声明:本文由今日头条转载,如有侵犯您的版权,请联系本站编辑删除。

猜你喜欢