信号量(Semaphore)是在多线程环境下使用的一种设施,可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。
下面通过RT-Thread的信号量来完成一个简单的应用:LED2和LED3以1Hz的频率交替闪烁。
信号量是进程间通信的媒介,在此定义两个线程thread1和thread2。Thread1首先获取一个信号量,然后点亮LED2,延迟500ms过后熄灭LED2,随即释放一个信号量。Thread2以RT_WAITING_FOREVER的方式一直保持获取信号量,当Thread1释放信号量之后,Thread2获取到一个信号量开始运行,点亮LED3,延迟500ms过后熄灭,随即释放一个信号量。
1. 首先当然是定义两个LED灯啦!在jiezhi320讲解的《15天入门RT-Thread》视频第2课中,LED的定义方式使用了RT-Thread自带的API,我也用此方式初始化两个LED。我使用的STM32开发板以STM32F103ZET6为主控芯片,该芯片为144脚。通过查看电路原理图,得到LED2对应PE5,即芯片的第4脚;LED3对应芯片的PB5,即芯片的第135脚。如下图所示:
2.在rtconfig.h文件中,以具体的名称代替引脚序号:
#define LED_2_PIN 4 //LED2 connect to PE5-the forth pin of stm32f103zet6
#define LED_3_PIN 135 //LED3 connect to PB5-the 135th pin of stm32f103zet6
3.新建一个semaphore_led.c文件,例程的所有源码都将在此文件中编写。semaphore_led.c文件中加入两个头文件
#include //Some important API is defined in this head-file
#include //Devices like PIN/I2C/SDIO can be configured in this head-file
4.将LED_2_PIN和LED_3_PIN设置为输出模式。这儿提一下,jiezhi320的《15天入门RT-Thread》第2课时此处配置有误,引脚的模式应该是OUTPUT而不是INPUT。
void semaphore_led_init(void)
{
rt_pin_mode(LED_2_PIN,PIN_MODE_OUTPUT);
rt_pin_mode(LED_3_PIN,PIN_MODE_OUTPUT);
}
5.编写thread1和thread2的入口函数。Thread1以RT_WAITING_FOREVER的方式获取一个信号量,操作完LED2后释放信号量。此时while循环应该继续运行的,但是由于thread1释放信号量之后,thread2以RT_WAITING_FOREVER的方式获取到了信号量,使信号量的value为0,thread1便停止在result = rt_sem_take(dynamic_sem,RT_WAITING_FOREVER)处。Thread2操作完LED3之后,释放了一个信号量,Thread1获取到该信号量,继续运行。
void rt_thread_entry1(void *parameter)
{
rt_err_t result = RT_NULL;
while(1)
{
result = rt_sem_take(dynamic_sem,RT_WAITING_FOREVER);
if(result != RT_EOK)
{
return;
}
rt_kprintf("thread1 take 1 sem\n");
rt_pin_write(LED_2_PIN,0);
rt_kprintf("led2-on in thread1 \n");
rt_thread_delay(RT_TICK_PER_SECOND/2);//RT_TICK_PER_SECOND = 100;delay 500ms;
rt_pin_write(LED_2_PIN,1);
rt_kprintf("led2-off in thread1 \n");
rt_kprintf("thread1 release 1 sem\n");//if we put the line of code behind the next line
//we won't see anything printed,guess why?
rt_sem_release(dynamic_sem);
}
}
void rt_thread_entry2(void *parameter)
{
rt_err_t result = RT_NULL;
while(1)
{
result = rt_sem_take(dynamic_sem,RT_WAITING_FOREVER);
if(result != RT_EOK)
{
return;
}
rt_kprintf("thread2 take 1 sem\n");
rt_pin_write(LED_3_PIN,0);
rt_kprintf("led3-on in thread2 \n");
rt_thread_delay(RT_TICK_PER_SECOND/2);
rt_pin_write(LED_3_PIN,1);
rt_kprintf("led3-off in thread2 \n");
rt_kprintf("thread2 release 1 sem\n");
rt_sem_release(dynamic_sem);
}
}
6.创建并启动thread1和thread2,并将该例程加入到finsh命令行中;整个程序就完成了。
int semaphore_led_sample_init(void *parameter)
{
semaphore_led_init();
dynamic_sem = rt_sem_create("dsem",1,RT_IPC_FLAG_FIFO);
if(dynamic_sem == RT_NULL)
{
rt_kprintf("Failed to create dynamic semaphore! \n");
return 1;
}
tid1 = rt_thread_create("thread1",rt_thread_entry1,RT_NULL,512,5,10);
if(tid1 == RT_NULL)
{
rt_kprintf("Failed to create thread1\n");
return 1;
}
rt_thread_startup(tid1);
tid2 = rt_thread_create("thread2",rt_thread_entry2,RT_NULL,512,6,10);
if(tid2 == RT_NULL)
{
rt_kprintf("Failed to create thread2\n");
return 1;
}
rt_thread_startup(tid2);
return 0;
}
#if defined (RT_SAMPLES_AUTORUN) && defined(RT_USING_COMPONENTS_INIT)
INIT_APP_EXPORT(semaphore_led_sample_init);
#endif
MSH_CMD_EXPORT(semaphore_led_sample_init, semaphore led sample);
下面给出整个semaphore_led.c文件,添加到《15天入门RT-Thread》工程文件中,编译过后烧录进硬件板中,就可以观察到LED2和LED3交替闪烁并且打印相关信息了。注意:请根据自己的开发板使用的STM32芯片调整LED2和LED3连接的引脚。48脚、64脚、100脚和144脚的引脚定义是不一样的。如有疑问可以查阅Drivers->drv_gpio.c文件。
#include
#include
static rt_sem_t dynamic_sem = RT_NULL;
static rt_thread_t tid1,tid2;
void semaphore_led_init(void)
{
rt_pin_mode(LED_2_PIN,PIN_MODE_OUTPUT);
rt_pin_mode(LED_3_PIN,PIN_MODE_OUTPUT);
}
void rt_thread_entry1(void *parameter)
{
rt_err_t result = RT_NULL;
while(1)
{
result = rt_sem_take(dynamic_sem,RT_WAITING_FOREVER);
if(result != RT_EOK)
{
return;
}
rt_kprintf("thread1 take 1 sem\n");
rt_pin_write(LED_2_PIN,0);
rt_kprintf("led2-on in thread1 \n");
rt_thread_delay(RT_TICK_PER_SECOND/2);//RT_TICK_PER_SECOND = 100;delay 500ms;
rt_pin_write(LED_2_PIN,1);
rt_kprintf("led2-off in thread1 \n");
rt_kprintf("thread1 release 1 sem\n");
rt_sem_release(dynamic_sem);
}
}
void rt_thread_entry2(void *parameter)
{
rt_err_t result = RT_NULL;
while(1)
{
result = rt_sem_take(dynamic_sem,RT_WAITING_FOREVER);
if(result != RT_EOK)
{
return;
}
rt_kprintf("thread2 take 1 sem\n");
rt_pin_write(LED_3_PIN,0);
rt_kprintf("led3-on in thread2 \n");
rt_thread_delay(RT_TICK_PER_SECOND/2);
rt_pin_write(LED_3_PIN,1);
rt_kprintf("led3-off in thread2 \n");
rt_kprintf("thread2 release 1 sem\n");
rt_sem_release(dynamic_sem);
}
}
int semaphore_led_sample_init(void *parameter)
{
semaphore_led_init();
dynamic_sem = rt_sem_create("dsem",1,RT_IPC_FLAG_FIFO);
if(dynamic_sem == RT_NULL)
{
rt_kprintf("Failed to create dynamic semaphore! \n");
return 1;
}
tid1 = rt_thread_create("thread1",rt_thread_entry1,RT_NULL,512,5,10);
if(tid1 == RT_NULL)
{
rt_kprintf("Failed to create thread1\n");
return 1;
}
rt_thread_startup(tid1);
tid2 = rt_thread_create("thread2",rt_thread_entry2,RT_NULL,512,6,10);
if(tid2 == RT_NULL)
{
rt_kprintf("Failed to create thread2\n");
return 1;
}
rt_thread_startup(tid2);
return 0;
}
#if defined (RT_SAMPLES_AUTORUN) && defined(RT_USING_COMPONENTS_INIT)
INIT_APP_EXPORT(semaphore_led_sample_init);
#endif
MSH_CMD_EXPORT(semaphore_led_sample_init, semaphore led sample);