uCOSii中信号量的作用:
在创建信号量时,Sem_Event=OSSemCreate(1)用于分时复用共享资源;
Sem_Event=OSSemCreate(0)用于中断和任务间同步或任务之间的同步。
具体在使用时,需要灵活运用。在访问共享资源时,我喜欢用互斥信号量,爱好不同而已。
一、uCOSii信号量用于对共享资源的保护
1、举例如下:
任务1每隔1小时,将DATA1数据保存到EEPROM。
任务2每隔2小时,将DATA2数据保存到EEPROM。
可见,任务1和任务2都要使用公共资源“写EEPROM”。为了防止使用冲突,我们通过发送信号量和接收信号量来分时操作“写EEPROM”,保证写入正确。
当然也可以放在一个任务里去做,为了了解怎么使用共享资源,这里使用信号量来实现。
2、实现方法:
1)、声明事件指针
OS_EVENT *Sem_Event;//定义一个事件指针
2)、任务1
//函数功能:任务1每隔1小时,将DATA1数据保存到EEPROM。
void SaveDATA1_Task(void *pdata)
{
u16 SaveDATA1_Cnt;
u8 Sem_Err;
while(1)
{
OSTimeDlyHMSM(0,0,0,1000);//延时1秒
SaveDATA1_Cnt++;
if(SaveDATA1_Cnt >3600)//1小时时间到,保存DATA1
{
OSSemPend(Sem_Event,0,&Sem_Err);//等待一个信号量,实现资源保护
EEPROM_U8_Data_Write(DATA1, DATA1_address);
OSSemPost(Sem_Event);//发出一个信号量
SaveDATA1_Cnt =0;
}
}
}
3)、任务2
//函数功能:任务2每隔2小时,将DATA2数据保存到EEPROM。
void SaveDATA2_Task(void *pdata)
{
u16 SaveDATA2_Cnt;
u8 Sem_Err;
while(1)
{
OSTimeDlyHMSM(0,0,0,1000);//延时1秒
SaveDATA2_Cnt++;
if(SaveDATA2_Cnt >7200)//2小时时间到,保存DATA2
{
OSSemPend(Sem_Event,0,&Sem_Err);//等待一个信号量,实现资源保护
EEPROM_U8_Data_Write(DATA2, DATA2_address);
OSSemPost(Sem_Event);//发出一个信号量
SaveDATA2_Cnt =0;
}
}
}
4)、启动任务
void Start_Task(void *pdata)
{
OS_CPU_SR cpu_sr=0;
OS_ENTER_CRITICAL();
//进入临界区(无法被中断打断),需要定义cpu_sr变量
Sem_Event=OSSemCreate(1);
//创建信号量Sem_Event,设置计数器初始值设置为1,即发送了一个信号量。
OSTaskCreate(
SaveDATA1_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&SaveDATA1_Task_STACK[SaveDATA1_Task_STACK_SIZE-1],
/* 指向堆栈任务栈顶的指针*/
SaveDATA1_Task_PRIORITY/* 任务优先级*/
);
OSTaskCreate(
SaveDATA2_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&SaveDATA2_Task_STACK[SaveDATA2_Task_STACK_SIZE-1],
/* 指向堆栈任务栈顶的指针*/
SaveDATA2_Task_PRIORITY/* 任务优先级*/
);
OSTaskDel(OS_PRIO_SELF); //删除自己
OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
}
二、uCOSii使用信号量实现中断和任务之间的同步
1、举例如下:
串口接收一组配置参数,然后将该参数需要保存到EEPROM中。显然,我们不能在中断中写EPPROM,这样会导致串口中断执行时间太长。因此需要分成两个部分实现,一个是串口接收,一个是负责保存参数,同时还要干其他事情。
有人会在某个任务中扫描串口,若收完配置信息立即保存,不用搞这个信号量也实现,实现方法千万种。这里使用信号量实现中断和任务之间的同步,完成中断和任务之间无缝隙对接。
2、实现方法:
1)、声明事件指针
OS_EVENT *Sem_Event;//定义一个事件指针
2)中断
uint8_t UART4HeadFlag;
uint8_t UART4_in; //UART4接收缓冲区的输入下标;
#define UART4_RCV_buffer_Size 100
//定义UART4接收缓冲区的长度100;
uint8_t UART4_RCV_buffer[UART4_RCV_buffer_Size];
//用来存放硬件串口接收到的数据;
//(ID=04661219C1677461)
//函数功能:USART4中断服务函数
void UART4_IRQHandler(void)
{
unsigned char temp;
(void)temp;//不让temp产生警告
if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET)
{
temp=USART_ReceiveData(UART4); //从UART4串口读取一个字节;
if(temp==’(’&& UART4HeadFlag==0)
{
UART4_in =0;
UART4HeadFlag=1;
}
if(UART4HeadFlag==1)
{
UART4_RCV_buffer[UART4_in]=temp;
UART4_in++;
}
if(temp==’)’&& UART4HeadFlag==1)
{
UART4_in =0;
UART4HeadFlag=0;//接收完成
OSSemPost(Sem_Event);//发出一个信号量
}
}
if(USART_GetFlagStatus(UART4,USART_FLAG_PE) != RESET)
{
USART_ReceiveData(UART4);//读串口
USART_ClearFlag(UART4, USART_FLAG_PE);
}
if(USART_GetFlagStatus(UART4,USART_FLAG_ORE) != RESET)
{
USART_ReceiveData(UART4);//读串口
USART_ClearFlag(UART4,USART_FLAG_ORE); //清除溢出中断
}
if(USART_GetFlagStatus(UART4,USART_FLAG_FE) != RESET)
{
USART_ReceiveData(UART4);//读串口
USART_ClearFlag(UART4,USART_FLAG_FE);
}
}
3)、任务1
void Task1(void *pdata)
{
u8 Sem_Err;
while(1)
{
OSSemPend(Sem_Event,0,&Sem_Err);//等待一个信号量,实现无缝对接
{
Save_String_To_EEPROM(UART4_RCV_buffer,UART4_in,ID_address);
}
}
}
void LED1_Task(void *pdata)
{
while(1)
{
LED1=!LED1;//信号有效
OSTimeDlyHMSM(0,0,0,500);//500毫秒闪烁1次
}
}
4)、启动任务
void Start_Task(void *pdata)
{
OS_CPU_SR cpu_sr=0;
OS_ENTER_CRITICAL();
//进入临界区(无法被中断打断),需要定义cpu_sr变量
Sem_Event=OSSemCreate(0);
//创建信号量Sem_Event,设置计数器初始值设置为0,即不发送一个信号量
OSTaskCreate(
Task1,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&Task1_STACK[Task1_STACK_SIZE-1],
/* 指向堆栈任务栈顶的指针*/
Task1_PRIORITY/* 任务优先级*/
);
OSTaskCreate(
LED1_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&LED1_TASK_STACK[LED1_TASK_STACK_SIZE-1],
/* 指向堆栈任务栈顶的指针*/
LED1_TASK_PRIORITY/* 任务优先级*/
);
OSTaskDel(OS_PRIO_SELF); //删除自己
OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
}
三、uCOSii使用信号量实现任务和任务之间的同步
1、举例如下:
任务1为负责按键触发
任务2负责点灯。
任务1中的按键按下,任务2负责点灯
使用信号量实现中断和任务之间的同步,完成中断和任务之间无缝隙对接。
2、实现方法:
1)、声明事件指针
OS_EVENT *Sem_Event;//定义一个事件指针
2)、任务1
//函数功能:任务1负责按键触发。
void Key_Task(void *pdata)
{
u8 Sem_Err;
while(1)
{
OSSemPost(Sem_Event);//发出一个信号量
}
}
3)、任务2
void LED_Task(void *pdata)
{
u8 Sem_Err;
while(1)
{
OSSemPend(Sem_Event,0,&Sem_Err);//等待一个信号量,实现无缝对接
LED1=!LED1;//信号有效
}
}
4)、启动任务
void Start_Task(void *pdata)
{
OS_CPU_SR cpu_sr=0;
OS_ENTER_CRITICAL();
//进入临界区(无法被中断打断),需要定义cpu_sr变量
Sem_Event=OSSemCreate(0);
//创建信号量Sem_Event,设置计数器初始值设置为0,即不发送一个信号量
OSTaskCreate(
LED_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&LED_Task_STACK[LED_Task_STACK_SIZE-1],
/* 指向堆栈任务栈顶的指针*/
LED_Task_PRIORITY/* 任务优先级*/
);
OSTaskCreate(
Key_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&KEY_TASK_STACK[KEY_TASK_STACK_SIZE-1],
/* 指向堆栈任务栈顶的指针*/
KEY_TASK_PRIORITY/* 任务优先级*/
);
OSTaskDel(OS_PRIO_SELF); //删除自己
OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
}