
梅科尔工作室-看鸿蒙设备开发实战14天笔记(三) 原创
小熊派HarmonyOS学习笔记(三)
信号量
信号量的概念
信号量(Semaphore)是一种实现任务间通信的机制,实现任务之间同步或临界资源的互斥访问。常用于协助一组相互竞争的任务来访问临界资源。
信号量的功能
信号量功能为各任务之间提供同步或互斥实现临界资源的保护
以同步为目的的信号量和以互斥为目的的信号量的不同之处
- 用作互斥时,信号量创建后记数是满的,在需要使用临界资源时,先取信号量,使其变空,这样其他任务需要使用临界资源时就会因为无法取到信号量而阻塞,从而保证了临界资源的安全。
- 用作同步时,信号量在创建后被置为空,任务1取信号量而阻塞,任务2在某种条件发生后,释放信号量,于是任务1得以进入READY或RUNNING态,从而达到了两个任务间的同步。
信号量的运作机制
- 信号量初始化,为配置的N个信号量申请内存(N值可以由用户自行配置,受内存限制),并把所有的信号量初始化成未使用,并加入到未使用链表中供系统使用。
- 信号量创建,从未使用的信号量链表中获取一个信号量资源,并设定初值。
- 信号量申请,若其计数器值大于0,则直接减1返回成功。否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾。
- 信号量释放,若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。
- 信号量删除,将正在使用的信号量置为未使用信号量,并挂回到未使用链表。
- 信号量允许多个任务在同一时刻访问同一资源,但会限制同一时刻访问此资源的最大任务数目。访问同一资源的任务数达到该资源的最大数量时,会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。
cmsis_os2的API信号量接口
接口名 | 功能描述 |
---|---|
osSemaphoreNew | 创建信号量 |
osSemaphoreAcquire | 获取信号量 |
osSemaphoreRelease | 释放信号量 |
osSemaphoreDelete | 删除信号量 |
创建互斥锁
osSemaphoreNew (uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr);
获取互斥锁
osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout);
释放互斥锁
osSemaphoreRelease (osSemaphoreId_t semaphore_id);
删除互斥锁
osMutexDelete (osMutexId_t mutex_id);
实验代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
osSemaphoreId_t sem1;
void Thread_Semaphore1(void)
{
osStatus_t status;
while (1)
{
//释放两次sem1信号量,使得Thread_Semaphore2和Thread_Semaphore3能同步执行
status = osSemaphoreRelease(sem1);
if(status != osOK)
{
printf("Thread_Semaphore1 Release Semap failed\n");
}
else
{
printf("Thread_Semaphore1 Release Semap success\n");
}
// //此处若只释放一次信号量,则Thread_Semaphore2和Thread_Semaphore3会交替运行。
// osSemaphoreRelease(sem1);
osDelay(100);
}
}
void Thread_Semaphore2(void)
{
osStatus_t status;
while (1)
{
//申请sem1信号量
status= osSemaphoreAcquire(sem1, 50U);
if(status != osOK)
{
printf("Thread_Semaphore2 get Semap failed\n");
}
else
{
printf("Thread_Semaphore2 get Semap success\n");
}
}
}
void Thread_Semaphore3(void)
{
osStatus_t status;
while (1)
{
//申请sem1信号量
status = osSemaphoreAcquire(sem1, osWaitForever);
if(status != osOK)
{
printf("Thread_Semaphore3 get Semap failed\n");
}
else
{
printf("Thread_Semaphore3 get Semap success\n");
}
osDelay(1);
}
}
void Semaphore_example(void)
{
osThreadAttr_t attr;
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 4;
attr.priority = 24;
attr.name = "Thread_Semaphore1";
if (osThreadNew((osThreadFunc_t)Thread_Semaphore1, NULL, &attr) == NULL)
{
printf("Falied to create Thread_Semaphore1!\n");
}
attr.name = "Thread_Semaphore2";
if (osThreadNew((osThreadFunc_t)Thread_Semaphore2, NULL, &attr) == NULL)
{
printf("Falied to create Thread_Semaphore2!\n");
}
attr.name = "Thread_Semaphore3";
if (osThreadNew((osThreadFunc_t)Thread_Semaphore3, NULL, &attr) == NULL)
{
printf("Falied to create Thread_Semaphore3!\n");
}
sem1 = osSemaphoreNew(4, 0, NULL);
if (sem1 == NULL)
{
printf("Falied to create Semaphore1!\n");
}
}
APP_FEATURE_INIT(Semaphore_example);
事件管理
事件概念
事件是一种实现任务间通信的机制,可用于实现任务间的同步,但事件通信只能是事件类型的通信,无数据传输。一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。事件集合用32位无符号整型变量来表示,每一位代表一个事件。
事件的运作机制
读事件时,可以根据入参事件掩码类型uwEventMask读取事件的单个或者多个事件类型。事件读取成功后,如果设置LOS_WAITMODE_CLR会清除已读取到的事件类型,反之不会清除已读到的事件类型,需显式清除。可以通过入参选择读取模式,读取事件掩码类型中所有事件还是读取事件掩码类型中任意事件。
写事件时,对指定事件写入指定的事件类型,可以一次同时写多个事件类型。写事件会触发任务调度。
清除事件时,根据入参事件和待清除的事件类型,对事件对应位进行清0操作。
cimsis_os2的API事件接口
接口名 | 功能描述 |
---|---|
osEventFlagsNew | 创建事件标记对象 |
osEventFlagsSet | 设置事件标记 |
osEventFlagsWait | 等待事件标记触发 |
osEventFlagsDelete | 删除事件标记对象 |
创建事件标记对象
osEventFlagsNew (const osEventFlagsAttr_t *attr);
设置事件标记
osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags);
等待事件标记触发
osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout);
删除事件标记对象
osEventFlagsDelete (osEventFlagsId_t ef_id);
实验代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#define FLAGS_MSK1 0x00000001U
osEventFlagsId_t evt_id; // event flags id
/***** 发送事件 *****/
void Thread_EventSender(void *argument)
{
(void)argument;
while (1)
{
osEventFlagsSet(evt_id, FLAGS_MSK1);
osEventFlagsSet(evt_id, FLAGS_MSK2);
osEventFlagsSet(evt_id, FLAGS_MSK3);
//suspend thread
osThreadYield();
osDelay(100);
}
}
/***** 接收事件 *****/
void Thread_EventReceiver(void *argument)
{
(void)argument;
uint32_t flags;
while (1)
{
flags = osEventFlagsWait(evt_id, FLAGS_MSK1|FLAGS_MSK2|FLAGS_MSK3, osFlagsWaitAll, osWaitForever);
printf("Receive Flags is %d\n", flags);
}
}
/***** 创建事件 *****/
static void Event_example(void)
{
evt_id = osEventFlagsNew(NULL);
if (evt_id == NULL)
{
printf("Falied to create EventFlags!\n");
}
osThreadAttr_t attr;
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 4;
attr.priority = 25;
attr.name = "Thread_EventSender";
if (osThreadNew(Thread_EventSender, NULL, &attr) == NULL)
{
printf("Falied to create Thread_EventSender!\n");
}
attr.name = "Thread_EventReceiver";
if (osThreadNew(Thread_EventReceiver, NULL, &attr) == NULL)
{
printf("Falied to create Thread_EventReceiver!\n");
}
}
APP_FEATURE_INIT(Event_example);
互斥锁
概念
- 互斥锁又称互斥型信号量,是一种特殊的二值性信号量,用于实现对共享资源的独占式处理。
- 任意时刻互斥锁的状态只有两种:开锁或闭锁。
- 当有任务持有时,互斥锁处于闭锁状态,这个任务获得该互斥锁的所有权。
- 当该任务释放时,该互斥锁被开锁,任务失去该互斥锁的所有权。
- 当一个任务持有互斥锁时,其他任务将不能再对该互斥锁进行开锁或持有。
- 多任务环境下往往存在多个任务竞争同一共享资源的应用场景,互斥锁可被用于对共享资源的保护从而实现独占式访问。另外,互斥锁可以解决信号量存在的优先级翻转问题。
LiteOS互斥锁的特点
通过优先级继承算法,解决优先级翻转问题。
运作机制
用互斥锁处理非共享资源的同步访问时,如果有任务访问该资源,则互斥锁为加锁状态。此时其他任务如果想访问这个公共资源则会被阻塞,直到互斥锁被持有该锁的任务释放后,其他任务才能重新访问该公共资源,此时互斥锁再次上锁,如此确保同一时刻只有一个任务正在访问这个公共资源,保证了公共资源操作的完整性。
cmsis_os2的API互斥锁接口
接口名 | 功能描述 |
---|---|
osMutexNew | 创建互斥锁 |
osMutexAcquire | 获取互斥锁 |
osMutexRelease | 释放互斥锁 |
osMutexDelete | 删除互斥锁 |
创建互斥锁
osMutexNew (const osMutexAttr_t *attr);
获取互斥锁
osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout);
释放互斥锁
osMutexRelease (osMutexId_t mutex_id);
删除互斥锁
osMutexDelete (osMutexId_t mutex_id);
实验代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
osMutexId_t mutex_id;
void HighPrioThread(void)
{
// wait 1s until start actual work
osDelay(100U);
osStatus_t status;
while (1)
{
// try to acquire mutex
status = osMutexAcquire(mutex_id, osWaitForever);
if(status != osOK)
{
printf("acquire mutex failed\r\n");
}
else
{
printf("acquire mutex success\r\n");
}
printf("HighPrioThread is runing.\r\n");
osDelay(300U);
status = osMutexRelease(mutex_id);
if(status != osOK)
{
printf("release mutex failed\r\n");
}
else
{
printf("release mutex success\r\n");
}
}
}
void MidPrioThread(void)
{
// wait 1s until start actual work
osDelay(100U);
while (1)
{
printf("MidPrioThread is runing.\r\n");
osDelay(100);
}
}
void LowPrioThread(void)
{
osStatus_t status;
while (1)
{
status = osMutexAcquire(mutex_id, osWaitForever);
printf("LowPrioThread is runing.\r\n");
if(status != osOK)
{
printf("acquire mutex failed\r\n");
}
else
{
printf("acquire mutex success\r\n");
}
// block mutex for 3s
osDelay(300U);
status = osMutexRelease(mutex_id);
if(status != osOK)
{
printf("release mutex failed\r\n");
}
else
{
printf("release mutex success\r\n");
}
}
}
void Mutex_example(void)
{
osThreadAttr_t attr;
osStatus_t status;
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 4;
attr.name = "HighPrioThread";
attr.priority = 24;
if (osThreadNew((osThreadFunc_t)HighPrioThread, NULL, &attr) == NULL)
{
printf("Falied to create HighPrioThread!\n");
}
attr.name = "MidPrioThread";
attr.priority = 25;
if (osThreadNew((osThreadFunc_t)MidPrioThread, NULL, &attr) == NULL)
{
printf("Falied to create MidPrioThread!\n");
}
attr.name = "LowPrioThread";
attr.priority = 26;
if (osThreadNew((osThreadFunc_t)LowPrioThread, NULL, &attr) == NULL)
{
printf("Falied to create LowPrioThread!\n");
}
mutex_id = osMutexNew(NULL);
if (mutex_id == NULL)
{
printf("Falied to create Mutex!\n");
}
status = osMutexDelete(mutex_id);
if(status != osOK)
{
printf("delete mutex failed\r\n");
}
else
{
printf("delete mutex success\r\n");
}
status = osMutexDelete(mutex_id);
if(status != osOK)
{
printf("delete mutex failed\r\n");
}
else
{
printf("delete mutex success\r\n");
}
}
APP_FEATURE_INIT(Mutex_example);
消息队列
消息队列的概念
消息队列,是一种常用于任务间通信的数据结构,实现了接收来自任务或中断的不固定长度的消息,并根据不同的接口选择传递消息是否存放在自己空间。任务能够从队列里面读取消息,当队列中的消息是空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。
运作机制
创建队列时,根据用户传入队列长度和消息节点大小来开辟相应的内存空间以供该队列使用,返回队列ID。在队列控制块中维护一个消息头节点位置Head和一个消息尾节点位置Tail来表示当前队列中消息存储情况。Head表示队列中被占用消息的起始位置。Tail表示队列中被空闲消息的起始位置。刚创建时Head和Tail均指向队列起始位置。
写队列时,根据Tail找到被占用消息节点末尾的空闲节点作为数据写入对象。
读队列时,根据Head找到最先写入队列中的消息节点进行读取。
删除队列时,根据传入的队列ID寻找到对应的队列,把队列状态置为未使用,释放原队列所占的空间,对应的队列控制头置为初始状态。
cmsis_os2的API消息队列接口
接口名 | 功能描述 |
---|---|
osMessageQueueNew | 创建消息队列 |
osMessageQueuePut | 发送消息 |
osMessageQueueGet | 获取消息 |
osMessageQueueDelete | 删除消息队列 |
创建消息队列
osMutexNew (const osMutexAttr_t *attr);
发送消息
osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout);
获取消息
osMutexRelease (osMutexId_t mutex_id);
删除消息队列
osMutexDelete (osMutexId_t mutex_id);
实验代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
//number of Message Queue Objects
#define MSGQUEUE_OBJECTS 16
typedef struct
{
//object data type
char *Buf;
uint8_t Idx;
} MSGQUEUE_OBJ_t;
MSGQUEUE_OBJ_t msg;
//message queue id
osMessageQueueId_t mid_MsgQueue;
void Thread_MsgQueue1(void *argument)
{
(void)argument;
uint8_t num = 0;
//do some work...
msg.Buf = "Hello BearPi-HM_Nano!";
while (1)
{
msg.Idx = num;
osMessageQueuePut(mid_MsgQueue, &msg, 0U, 0U);
num++;
//suspend thread
osThreadYield();
osDelay(100);
}
}
void Thread_MsgQueue2(void *argument)
{
(void)argument;
osStatus_t status;
uint32_t count;
while (1)
{
//Insert thread code here...
count = osMessageQueueGetCount(mid_MsgQueue);
printf("message queue get count: %d\r\n",count);
if(count == MSGQUEUE_OBJECTS)
{
osMessageQueueDelete(mid_MsgQueue);
}
//wait for message
status = osMessageQueueGet(mid_MsgQueue, &msg, NULL, 0U);
if (status == osOK)
{
printf("Message Queue Get msg idx:%d buf:%s\n", msg.Idx,msg.Buf);
}
osDelay(300);
}
}
static void Message_example(void)
{
mid_MsgQueue = osMessageQueueNew(MSGQUEUE_OBJECTS, 100, NULL);
if (mid_MsgQueue == NULL)
{
printf("Falied to create Message Queue!\n");
}
osThreadAttr_t attr;
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 10;
attr.priority = 25;
attr.name = "Thread_MsgQueue1";
if (osThreadNew(Thread_MsgQueue1, NULL, &attr) == NULL)
{
printf("Falied to create Thread_MsgQueue1!\n");
}
attr.name = "Thread_MsgQueue2";
if (osThreadNew(Thread_MsgQueue2, NULL, &attr) == NULL)
{
printf("Falied to create Thread_MsgQueue2!\n");
}
}
APP_FEATURE_INIT(Message_example);
GPIO
wifiiot_gpio.h接口
接口名 | 功能描述 |
---|---|
GpioInit | 初始化GPIO |
GpioDeinit | 取消初始化GPIO |
GpioSetDir | 设置GPIO引脚方向 |
GpioGetDir | 获取GPIO引脚方向 |
GpioSetOutputVal | 设置GPIO引脚输出电平值 |
GpioGetOutputVal | 获取GPIO引脚输出电平值 |
wifiiot_gpio_ex.h接口
接口名 | 功能描述 |
---|---|
IoSetPull | 设置GPIO引脚上拉 |
IoGetPull | 获取GPIO引脚上拉 |
IoSetFunc | 设置GPIO引脚功能 |
IoGetFunc | 获取GPIO引脚功能 |
IOSetDriverStrength | 设置GPIO驱动能力 |
IOGetDriverStrength | 获取GPIO驱动能力 |
控制LED灯的闪烁
LED对应的GPIO引脚是GPIO2通过控制GPIO2输出的的电平信号来实现LED灯的闪烁。
- 高电平时点亮LED灯。
- 低电平时熄灭LED灯。
实验代码
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifiiot_gpio.h"
#include "wifiiot_gpio_ex.h"
static void LedTask(void)
{
//初始化GPIO
GpioInit();
//设置GPIO_2的复用功能为普通GPIO
IoSetFunc(WIFI_IOT_IO_NAME_GPIO_2, WIFI_IOT_IO_FUNC_GPIO_2_GPIO);
//设置GPIO_2为输出模式
GpioSetDir(WIFI_IOT_GPIO_IDX_2, WIFI_IOT_GPIO_DIR_OUT);
WifiIotGpioDir val = {0};
GpioGetDir(WIFI_IOT_GPIO_IDX_2,&val);
printf("GPIO_2 Dir is %d\r\n",val);
WifiIotGpioValue OutputVal = {0};
while (1)
{
//设置GPIO_2输出高电平点亮LED灯
GpioSetOutputVal(WIFI_IOT_GPIO_IDX_2, 1);
GpioGetOutputVal(WIFI_IOT_GPIO_IDX_2,&OutputVal);
printf("GPIO_2 OutputVal is %d\r\n",OutputVal);
//延时1s
usleep(1000000);
//设置GPIO_2输出低电平熄灭LED灯
GpioSetOutputVal(WIFI_IOT_GPIO_IDX_2, 0);
GpioGetOutputVal(WIFI_IOT_GPIO_IDX_2,&OutputVal);
printf("GPIO_2 OutputVal is %d\r\n",OutputVal);
//延时1s
usleep(1000000);
}
}
static void LedExampleEntry(void)
{
osThreadAttr_t attr;
attr.name = "LedTask";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 512;
attr.priority = 25;
if (osThreadNew((osThreadFunc_t)LedTask, NULL, &attr) == NULL)
{
printf("Falied to create LedTask!\n");
}
}
APP_FEATURE_INIT(LedExampleEntry);
