信号量基本概念
信号量(
Semaphore
)是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。
抽象的来讲,信号量是一个非负整数,所有获取它的任务都会将该整数减一(获取它
当然是为了使用资源),当该整数值为零时,所有试图获取它的任务都将处于阻塞状态。
通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其
值的含义分两种情况:
0
:表示没有积累下来的释放信号量操作,且有可能有在此信号量上阻塞的任务。
正值,表示有一个或多个释放信号量操作。
1.二值信号量
二值信号量既可以用于临界资源访问也可以用于同步功能。
二值信号量和互斥信号量(以下使用互斥量表示互斥信号量)非常相似,但是有一些细
微差别:互斥量有优先级继承机制,二值信号量则没有这个机制。这使得二值信号量更偏
向应用于同步功能(任务与任务间的同步或任务和中断间同步),而互斥量更偏向应用于
临界资源的访问。
用作同步时,信号量在创建后应被置为空,任务
1
获取信号量而进入阻塞,任务
2
在
某种条件发生后,释放信号量,于是任务
1
获得信号量得以进入就绪态,如果任务
1
的优
先级是最高的,那么就会立即切换任务,从而达到了两个任务间的同步。同样的,在中断
服务函数中释放信号量,任务
1
也会得到信号量,从而达到任务与中断间的同步。
还记得我们经常说的中断要快进快出吗,在裸机开发中我们经常是在中断中做一个标
记,然后在退出的时候进行轮询处理,这个就是类似我们使用信号量进行同步的,当标记
发生了,我们再做其他事情。在
FreeRTOS
中我们用信号量用于同步,任务与任务的同步,
中断与任务的同步,可以大大提高效率。
可以将二值信号量看作只有一个消息的队列,因此这个队列只能为空或满(因此称为二
值),我们在运用的时候只需要知道队列中是否有消息即可,而无需关注消息是什么。
2.计数信号量
顾名思义,计数信号量肯定是用于计数的,在实际的使用中,我们常将计数信号量用
于事件计数与资源管理。每当某个事件发生时,任务或者中断将释放一个信号量(信号量
计数值加
1
),当处理被事件时(一般在任务中处理),处理任务会取走该信号量(信号
量计数值减
1
),信号量的计数值则表示还有多少个事件没被处理。此外,系统还有很多
资源,我们也可以使用计数信号量进行资源管理,信号量的计数值表示系统中可用的资源
数目,任务必须先获取到信号量才能获取资源访问权,当信号量的计数值为零时表示系统
没有可用的资源,但是要注意,在使用完资源的时候必须归还信号量,否则当计数值为
0
的时候任务就无法访问该资源了。
计数型信号量允许多个任务对其进行操作,但限制了任务的数量。比如有一个停车场,
里面只有
100
个车位,那么能停的车只有
100
辆,也相当于我们的信号量有
100
个,假如
一开始停车场的车位还有
100
个,那么每进去一辆车就要消耗一个停车位,车位的数量就
要减一,对应的,我们的信号量在使用之后也需要减一,当停车场停满了
100
辆车的时候,
此时的停车位为
0
,再来的车就不能停进去了,否则将造成事故,也相当于我们的信号量
为
0
,后面的任务对这个停车场资源的访问也无法进行,当有车从停车场离开的时候,车
位又空余出来了,那么,后面的车就能停进去了,我们信号量的操作也是一样的,当我们
释放了这个资源,后面的任务才能对这个资源进行访问。
3.互斥信号量
互斥信号量其实是特殊的二值信号量,由于其特有的优先级继承机制从而使它更适用
于简单互锁,也就是保护临界资源
用作互斥时,信号量创建后可用信号量个数应该是满的,任务在需要使用临界资源时,
(临界资源是指任何时刻只能被一个任务访问的资源),先获取互斥信号量,使其变空,
这样其他任务需要使用临界资源时就会因为无法获取信号量而进入阻塞,从而保证了临界
资源的安全。
在操作系统中,我们使用信号量的很多时候是为了给临界资源建立一个标志,信号量
表示了该临界资源被占用情况。这样,当一个任务在访问临界资源的时候,就会先对这个
资源信息进行查询,从而在了解资源被占用的情况之后,再做处理,从而使得临界资源得
到有效的保护。
4 .递归信号量
递归信号量,见文知义,递归嘛,就是可以重复获取调用的,本来按照信号量的特性,
每获取一次可用信号量个数就会减少一个,但是递归则不然,对于已经获取递归互斥量的
任务可以重复获取该递归互斥量,该任务拥有递归信号量的所有权。任务成功获取几次递
归互斥量,就要返还几次,在此之前递归互斥量都处于无效状态,其他任务无法获取,只
有持有递归信号量的任务才能获取与释放。
5.API函数
1.动态 创建二值信号量 SemaphoreHandle_txSemaphoreCreateBinary(void)
在 使 用函 数 xSemaphoreTake()获取之前必须先调用函数 xSemaphoreGive()释放后才可以获取。
使用一个类型
SemaphoreHandle_t 的句柄接收xSemaphoreCreateBinary()的返回值。
计数值初始值为 0。
即使发送任务连续释放多个信号量,也只能成功
1
次。释放、获 得信号量是一一对应的。
2. 动态创建计数信号量
SemaphoreHandle_t x xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount)
uxMaxCount: 最大计数值
uxInitialCount: 初始计数值
3.删除 void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
Semaphore: 信号量句柄,你要删除哪个信号量
4.give/take
在任务中使用
|
在 ISR 中使用
| |
take
|
xSemaphoreTake
|
xSemaphoreTakeFromISR
|
give
|
xSemaphoreGive
|
xSemaphoreGiveFromISR
|
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
传入参数为信号量句柄
返回值:
pdTRUE 表示成功,
如果二进制信号量的计数值已经是 1,再次调用此函数则返回失 败;
如果计数型信号量的计数值已经是最大值,再次调用此函数则返回失败
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken );
xSemaphore 信号量句柄,释放哪个信号量
pxHigherPriorityTaskWoken 如果释放信号量导致更高优先级的任务变为就绪态,
则*pxHigherPriorityTaskWoken = pdTRUE
返回值
pdTRUE 表示成功,
如果二进制信号量的计数值已经是 1,再次调用
此函数则返回失败;
如果计数型信号量的计数值已经是最大值,再
次调用此函数则返回失败
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
xSemaphore 信号量句柄,获取哪个信号量
xTicksToWait 如果无法马上获得信号量,阻塞一会: 0:不阻塞,马上返回
portMAX_DELAY: 一直阻塞直到成功
其他值: 阻塞的 Tick 个数,可以使用
pdMS_TO_TICKS()
来指定
阻塞时间为若干 ms
返回值
pdTRUE 表示成功
BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken );
xSemaphore 信号量句柄,获取哪个信号量
xTicksToWait 如果无法马上获得信号量,阻塞一会: 0:不阻塞,马上返回
portMAX_DELAY: 一直阻塞直到成功
其他值: 阻塞的 Tick 个数,可以使用
pdMS_TO_TICKS()
来指定
阻塞时间为若干 ms
返回值
pdTRUE 表示成功
6.示例
使用二进制信号量来显示小车
实验现象是第三个小车先运行,到第1个,最后到第二个。
/*
* Project: N|Watch
* Author: Zak Kemble, contact@zakkemble.co.uk
* Copyright: (C) 2013 by Zak Kemble
* License: GNU GPL v3 (see License.txt)
* Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cmsis_os.h"
#include "FreeRTOS.h" // ARM.FreeRTOS::RTOS:Core
#include "task.h" // ARM.FreeRTOS::RTOS:Core
#include "event_groups.h" // ARM.FreeRTOS::RTOS:Event Groups
#include "semphr.h" // ARM.FreeRTOS::RTOS:Core
#include "draw.h"
#include "resources.h"
#include "driver_lcd.h"
#include "driver_ir_receiver.h"
#include "driver_rotary_encoder.h"
#include "driver_mpu6050.h"
#define NOINVERT false
#define INVERT true
#define CAR_COUNT 3
#define CAR_WIDTH 12
#define CAR_LENGTH 15
#define ROAD_SPEED 6
static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;
SemaphoreHandle_t car;
SemaphoreHandle_t MuxSem_Handle;
struct car {
int x;
int y;
int control_key;
};
struct car g_cars[3] = {
{0, 0, IR_KEY_1},
{0, 17, IR_KEY_2},
{0, 34, IR_KEY_3},
};
static const byte carImg[] ={
0x40,0xF8,0xEC,0x2C,0x2C,0x38,0xF0,0x10,0xD0,0x30,0xE8,0x4C,0x4C,0x9C,0xF0,
0x02,0x1F,0x37,0x34,0x34,0x1C,0x0F,0x08,0x0B,0x0C,0x17,0x32,0x32,0x39,0x0F,
};
static const byte clearImg[30] ={0};
static const byte roadMarking[] ={
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
};
#if 0
void car_test(void)
{
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
draw_bitmap(0, 0, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(0, 0, 15, 16);
draw_bitmap(0, 16, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(0, 16, 8, 1);
while (1);
}
#endif
static void ShowCar(struct car *pcar)
{
draw_bitmap(pcar->x, pcar->y, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(pcar->x, pcar->y, 15, 16);
}
static void HideCar(struct car *pcar)
{
draw_bitmap(pcar->x, pcar->y, clearImg, 15, 16, NOINVERT, 0);
draw_flushArea(pcar->x, pcar->y, 15, 16);
}
static void CarTask1(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
vTaskDelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
static void CarTask2(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
vTaskDelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
static void CarTask3(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
vTaskDelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
void car_game(void)
{
int x;
int i, j;
xSemaphoreGive(MuxSem_Handle);
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
/*创建一个计数信号量空间为3,现有1*/
car =xSemaphoreCreateCounting(3,1);
/* 画出路标 */
for (i = 0; i < 3; i++)
{
for (j = 0; j < 8; j++)
{
draw_bitmap(16*j, 16+17*i, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(16*j, 16+17*i, 8, 1);
}
}
/* 创建3个汽车任务 */
xTaskCreate(CarTask1, "car1", 128, &g_cars[0], osPriorityNormal, NULL);
xTaskCreate(CarTask2, "car2", 128, &g_cars[1], osPriorityNormal, NULL);
xTaskCreate(CarTask3, "car3", 128, &g_cars[2], osPriorityNormal, NULL);
}
使用计数量来显示小车
实验现象是第三个小车和第1个先运行,最后到第二个。
/*
* Project: N|Watch
* Author: Zak Kemble, contact@zakkemble.co.uk
* Copyright: (C) 2013 by Zak Kemble
* License: GNU GPL v3 (see License.txt)
* Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cmsis_os.h"
#include "FreeRTOS.h" // ARM.FreeRTOS::RTOS:Core
#include "task.h" // ARM.FreeRTOS::RTOS:Core
#include "event_groups.h" // ARM.FreeRTOS::RTOS:Event Groups
#include "semphr.h" // ARM.FreeRTOS::RTOS:Core
#include "draw.h"
#include "resources.h"
#include "driver_lcd.h"
#include "driver_ir_receiver.h"
#include "driver_rotary_encoder.h"
#include "driver_mpu6050.h"
#define NOINVERT false
#define INVERT true
#define CAR_COUNT 3
#define CAR_WIDTH 12
#define CAR_LENGTH 15
#define ROAD_SPEED 6
static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;
SemaphoreHandle_t car;
SemaphoreHandle_t MuxSem_Handle;
struct car {
int x;
int y;
int control_key;
};
struct car g_cars[3] = {
{0, 0, IR_KEY_1},
{0, 17, IR_KEY_2},
{0, 34, IR_KEY_3},
};
static const byte carImg[] ={
0x40,0xF8,0xEC,0x2C,0x2C,0x38,0xF0,0x10,0xD0,0x30,0xE8,0x4C,0x4C,0x9C,0xF0,
0x02,0x1F,0x37,0x34,0x34,0x1C,0x0F,0x08,0x0B,0x0C,0x17,0x32,0x32,0x39,0x0F,
};
static const byte clearImg[30] ={0};
static const byte roadMarking[] ={
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
};
#if 0
void car_test(void)
{
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
draw_bitmap(0, 0, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(0, 0, 15, 16);
draw_bitmap(0, 16, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(0, 16, 8, 1);
while (1);
}
#endif
static void ShowCar(struct car *pcar)
{
draw_bitmap(pcar->x, pcar->y, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(pcar->x, pcar->y, 15, 16);
}
static void HideCar(struct car *pcar)
{
draw_bitmap(pcar->x, pcar->y, clearImg, 15, 16, NOINVERT, 0);
draw_flushArea(pcar->x, pcar->y, 15, 16);
}
static void CarTask1(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
vTaskDelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
static void CarTask2(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
vTaskDelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
static void CarTask3(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
vTaskDelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
void car_game(void)
{
int x;
int i, j;
/*创建一个二进制信号量*/
// xSemaphoreGive(MuxSem_Handle);
/*创建一个计数信号量*/
MuxSem_Handle= xSemaphoreCreateMutex();
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
/*创建一个计数信号量空间为3,现有2*/
car =xSemaphoreCreateCounting(3,2);
/* 画出路标 */
for (i = 0; i < 3; i++)
{
for (j = 0; j < 8; j++)
{
draw_bitmap(16*j, 16+17*i, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(16*j, 16+17*i, 8, 1);
}
}
/* 创建3个汽车任务 */
#if 0
for (i = 0; i < 3; i++)
{
draw_bitmap(g_cars[i].x, g_cars[i].y, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(g_cars[i].x, g_cars[i].y, 15, 16);
}
#endif
xTaskCreate(CarTask1, "car1", 128, &g_cars[0], osPriorityNormal, NULL);
xTaskCreate(CarTask2, "car2", 128, &g_cars[1], osPriorityNormal, NULL);
xTaskCreate(CarTask3, "car3", 128, &g_cars[2], osPriorityNormal, NULL);
}
3.使用互斥量显示小车
实验现象是第三个小车先运行,到第1个,最后到第二个。
/*互斥信号量*/
void cartask(void *params)
{
BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
while(1)
{
//获取二值信号量 xSemaphore,没获取到则一直等待
xReturn = xSemaphoreTake(MuxSem_Handle,/* 二值信号量句柄 */
portMAX_DELAY); /* 等待时间 */
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
//vTaskDelay(50);
xReturn = xSemaphoreGive( MuxSem_Handle );//互斥量释放函数 给出二值信号量
if (pcar->x == g_xres - CAR_LENGTH)
{
vTaskDelete(NULL);
}
}
}
}
void car_game(void)
{
int x;
int i, j;
/*创建一个互斥量*/
MuxSem_Handle= xSemaphoreCreateBinary( );
/*创建一个二进制信号量*/
// xSemaphoreGive(MuxSem_Handle);
/*创建一个计数信号量*/
MuxSem_Handle= xSemaphoreCreateMutex();
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
if (MuxSem_Handle!= NULL ) {
/* 互斥量创建成功 */
}
/*创建一个计数信号量空间为3,现有2*/
car =xSemaphoreCreateCounting(3,2);
/* 画出路标 */
for (i = 0; i < 3; i++)
{
for (j = 0; j < 8; j++)
{
draw_bitmap(16*j, 16+17*i, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(16*j, 16+17*i, 8, 1);
}
}
/* 创建3个汽车任务 */
#if 0
for (i = 0; i < 3; i++)
{
draw_bitmap(g_cars[i].x, g_cars[i].y, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(g_cars[i].x, g_cars[i].y, 15, 16);
}
#endif
xTaskCreate(cartask, "car1", 128, &g_cars[0], osPriorityNormal, NULL);
xTaskCreate(cartask, "car2", 128, &g_cars[1], osPriorityNormal, NULL);
xTaskCreate(cartask, "car3", 128, &g_cars[2], osPriorityNormal, NULL);
}
7.特殊情况,优先级反转
优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行
优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。
在使用二值信号量的时候,经常会遇到优先级翻转的问题。
高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)
示例
/*
* Project: N|Watch
* Author: Zak Kemble, contact@zakkemble.co.uk
* Copyright: (C) 2013 by Zak Kemble
* License: GNU GPL v3 (see License.txt)
* Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cmsis_os.h"
#include "FreeRTOS.h" // ARM.FreeRTOS::RTOS:Core
#include "task.h" // ARM.FreeRTOS::RTOS:Core
#include "event_groups.h" // ARM.FreeRTOS::RTOS:Event Groups
#include "semphr.h" // ARM.FreeRTOS::RTOS:Core
#include "draw.h"
#include "resources.h"
#include "driver_lcd.h"
#include "driver_ir_receiver.h"
#include "driver_rotary_encoder.h"
#include "driver_mpu6050.h"
#define NOINVERT false
#define INVERT true
#define CAR_COUNT 3
#define CAR_WIDTH 12
#define CAR_LENGTH 15
#define ROAD_SPEED 6
static uint32_t g_xres, g_yres, g_bpp;
static uint8_t *g_framebuffer;
SemaphoreHandle_t car;
SemaphoreHandle_t MuxSem_Handle;
struct car {
int x;
int y;
int control_key;
};
struct car g_cars[3] = {
{0, 0, IR_KEY_1},
{0, 17, IR_KEY_2},
{0, 34, IR_KEY_3},
};
static const byte carImg[] ={
0x40,0xF8,0xEC,0x2C,0x2C,0x38,0xF0,0x10,0xD0,0x30,0xE8,0x4C,0x4C,0x9C,0xF0,
0x02,0x1F,0x37,0x34,0x34,0x1C,0x0F,0x08,0x0B,0x0C,0x17,0x32,0x32,0x39,0x0F,
};
static const byte clearImg[30] ={0};
static const byte roadMarking[] ={
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
};
#if 0
void car_test(void)
{
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
draw_bitmap(0, 0, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(0, 0, 15, 16);
draw_bitmap(0, 16, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(0, 16, 8, 1);
while (1);
}
#endif
static void ShowCar(struct car *pcar)
{
draw_bitmap(pcar->x, pcar->y, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(pcar->x, pcar->y, 15, 16);
}
static void HideCar(struct car *pcar)
{
draw_bitmap(pcar->x, pcar->y, clearImg, 15, 16, NOINVERT, 0);
draw_flushArea(pcar->x, pcar->y, 15, 16);
}
static void CarTask1(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
vTaskDelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
static void CarTask2(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
vTaskDelay(1000);
//xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
//vTaskDelay(50);
mdelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
static void CarTask3(void *params)
{
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
vTaskDelay(5000);
xSemaphoreTake(car,portMAX_DELAY);
while(1)
{
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
//vTaskDelay(50);
mdelay(50);
if (pcar->x == g_xres - CAR_LENGTH)
{
/* 释放信号量 */
xSemaphoreGive(car);
vTaskDelete(NULL);
}
}
}
}
/*互斥信号量*/
void cartask(void *params)
{
BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
struct car *pcar = params;
struct ir_data idata;
ShowCar(pcar);
while(1)
{
//获取二值信号量 xSemaphore,没获取到则一直等待
xReturn = xSemaphoreTake(MuxSem_Handle,/* 二值信号量句柄 */
portMAX_DELAY); /* 等待时间 */
if (pcar->x < g_xres - CAR_LENGTH)
{
/* 隐藏汽车 */
HideCar(pcar);
/* 调整位置 */
pcar->x += 1;
if (pcar->x > g_xres - CAR_LENGTH)
{
pcar->x = g_xres - CAR_LENGTH;
}
/* 重新显示汽车 */
ShowCar(pcar);
//vTaskDelay(50);
xReturn = xSemaphoreGive( MuxSem_Handle );//互斥量释放函数 给出二值信号量
if (pcar->x == g_xres - CAR_LENGTH)
{
vTaskDelete(NULL);
}
}
}
}
void car_game(void)
{
int x;
int i, j;
/*创建一个互斥量*/
MuxSem_Handle= xSemaphoreCreateBinary( );
/*创建一个二进制信号量*/
// xSemaphoreGive(MuxSem_Handle);
/*创建一个计数信号量*/
MuxSem_Handle= xSemaphoreCreateMutex();
g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp);
draw_init();
draw_end();
if (MuxSem_Handle!= NULL ) {
/* 互斥量创建成功 */
}
/*创建一个计数信号量空间为3,现有2*/
car =xSemaphoreCreateCounting(3,1);
/* 画出路标 */
for (i = 0; i < 3; i++)
{
for (j = 0; j < 8; j++)
{
draw_bitmap(16*j, 16+17*i, roadMarking, 8, 1, NOINVERT, 0);
draw_flushArea(16*j, 16+17*i, 8, 1);
}
}
/* 创建3个汽车任务 */
#if 0
for (i = 0; i < 3; i++)
{
draw_bitmap(g_cars[i].x, g_cars[i].y, carImg, 15, 16, NOINVERT, 0);
draw_flushArea(g_cars[i].x, g_cars[i].y, 15, 16);
}
#endif
xTaskCreate(CarTask1, "car1", 128, &g_cars[0], osPriorityNormal, NULL);
xTaskCreate(CarTask2, "car2", 128, &g_cars[1], osPriorityNormal+2, NULL);
xTaskCreate(CarTask3, "car3", 128, &g_cars[2], osPriorityNormal+3, NULL);
//vTaskStartScheduler();
}