赞
踩
在学习嵌入式操作系统之前,我注意到了这个能够实现轻量线程环境的函数库(或者可以说是最最简单的操作系统),于是想动手尝试能不能在STM32工程中浅用一下。
1.将Protothreads直接复制进工程文件中(源码下载链接Protothreads - download);
2.由于 Protothreads全部由头文件构成,在Keil工程设置中直接include即可。
1.仿照Malc编写一个程序:LED1灭一秒亮一秒,同时LED2灭五秒亮五秒。代码如下:
- #include <stm32f10x.h>
- #include "Led_Key.h"
- #include "bsp_exti.h"
- #include "bsp_SysTick.h"
- #include "bsp_iwdg.h"
- #include "bsp_wwdg.h"
- #include "bsp_uart.h"
- #include "bsp_dma.h"
- #include "bsp_adc.h"
- #include "bsp_tim2.h"
- #include "bsp_rtc.h"
- #include <pt.h>//Protothreads头文件,必须包括
-
- static int counter1,counter2;
-
- static int protothread1(struct pt *pt)
- {
- PT_BEGIN(pt);//线程开始
-
- while(1)
- {
- PT_WAIT_UNTIL(pt, counter1 == 1);//如果时间满1秒继续执行,否则记录运行点并退出线程1
- GPIOA->ODR ^= GPIO_Pin_1;//灯1状态反转
- counter1 = 0;
- }
-
- PT_END(pt);//线程结束
- }
-
- static int protothread2(struct pt *pt)
- {
- PT_BEGIN(pt);//线程开始
-
- while(1)
- {
- PT_WAIT_UNTIL(pt, counter2 == 5);//如果时间满5秒继续执行,否则记录运行点并退出线程2
- GPIOA->ODR ^= GPIO_Pin_2;//灯2状态反转
- counter2 = 0;
- }
-
- PT_END(pt);//线程结束
- }
-
- static struct pt pt1, pt2;
- int main(void)
- {
- PT_INIT(&pt1);//线程1初始化
- PT_INIT(&pt2);//线程2初始化
-
- SysTick_Configuration();
-
- Led_Configuration();
-
- while(1)
- {
- protothread1(&pt1);//执行线程1
- protothread2(&pt2);//执行线程2
-
- Delay_us(1000000);
-
- counter1++;
- counter2++;
- }
- }
2.编译Keil5,此时提示User\main.c(18): warning: #550-D: variable "PT_YIELD_FLAG" was set but never used,不用管,烧录进stm32,观察现象。
zqnchn讲解了信号量的概念,即“得到了一个信号量,任务继续运行,得不到,一边呆着去”。
要求:板载LED以2秒一周期的速率慢速闪烁。当且仅当串口发来0xaa时,快闪5次。
1.将protothreads文件夹下的文件替换为zqnchn针对Arduino优化后的Protothreads版本(地址:https://toscode.gitee.com/changser/changser_pt_for_arduino);
2.修改pt-timer.h中关于PT_TIMER_DELAY的宏定义,此处笔者采用rtc计时器记录时间(该文件其他宏定义同样可以修改);
- #define PT_TIMER_DELAY(pt,time) \
- do { \
- (pt)->t = millis(); \
- PT_WAIT_UNTIL((pt),((pt_timer)(millis()-(pt)->t)>=(time)));\
- }while(0)
修改前
- #define PT_TIMER_DELAY(pt,time) \
- do { \
- (pt)->t = RTC_GetCounter(); \
- PT_WAIT_UNTIL((pt),((pt_timer)(RTC_GetCounter()-(pt)->t)>=(time)));\
- }while(0)
修改后
3.修改rtc时钟配置(改为rtc每震荡一次经过一毫秒),并编写中断处理函数;
- int RTC_Configuration_ms(void)
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);
- PWR_BackupAccessCmd(ENABLE);
- if(BKP_ReadBackupRegister(BKP_DR1) != 0x1234)
- {
- BKP_DeInit();
- RCC_LSEConfig(RCC_LSE_ON);
- while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)!=SET)
- {
- }
- RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
- RCC_RTCCLKCmd(ENABLE);
- RTC_WaitForLastTask();
- RTC_WaitForSynchro();
- RTC_SetPrescaler(32);//(32 + 1) / 32768 ≈ 0.001(s)
- RTC_WaitForLastTask();
- BKP_WriteBackupRegister(BKP_DR1, 0x1234);
- return 0;
- }
- return 1;
- }
- void USART1_IRQHandler(void)
- {
- while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
- {
- rdata = USART_ReceiveData(USART1);//此处rdata为全局变量,以便被main.c使用
- }
- }
4.使用Protothreads编写main.c文件,通过信号量实现要求;
- #include <stm32f10x.h>
- #include "Led_Key.h"
- #include "bsp_exti.h"
- #include "bsp_SysTick.h"
- #include "bsp_iwdg.h"
- #include "bsp_wwdg.h"
- #include "bsp_uart.h"
- #include "bsp_dma.h"
- #include "bsp_adc.h"
- #include "bsp_tim2.h"
- #include "bsp_rtc.h"
-
- #define PT_USE_TIMER//定时器库
- #define PT_USE_SEM//信号量库
- #include <pt.h>
-
- static struct pt_sem sem_LED;
- unsigned char i;
-
- static int protothread1(struct pt *pt)
- {
- PT_BEGIN(pt);//线程开始
-
- while(1)
- {
- PT_SEM_WAIT(pt, &sem_LED); //等待LED信号量可用
- GPIOA->ODR ^= GPIO_Pin_1;//灯1状态反转
- PT_TIMER_DELAY(pt, 1000);//留一秒
- PT_SEM_SIGNAL(pt, &sem_LED);//信号量用完了
-
- PT_YIELD(pt);//让给其他线程(此处很重要,否则会卡在while循环里出不来)
- }
- PT_END(pt);//线程结束
- }
-
- static int protothread2(struct pt *pt)
- {
- PT_BEGIN(pt);//线程开始
-
- while(1)
- {
- PT_WAIT_UNTIL(pt, rdata == 0xaa);
-
- PT_SEM_WAIT(pt, &sem_LED);//等待LED信号量可用
-
- for(i = 0; i < 5; i++)
- {
- GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
- PT_TIMER_DELAY(pt, 200);//留0.2秒
-
- GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
- PT_TIMER_DELAY(pt, 200);//留0.2秒
- }
-
- rdata = 0;//防止再次while
-
- PT_SEM_SIGNAL(pt, &sem_LED); //信号量用完了
- }
-
- PT_END(pt);//线程结束
- }
-
- static struct pt pt1, pt2;
- int main(void)
- {
- PT_INIT(&pt1);
- PT_INIT(&pt2);
- PT_SEM_INIT(&sem_LED,1); //初始化信号量为1,即没人用
-
- SysTick_Configuration();
-
- Led_Configuration();
-
- Uart1_Configuration();
- Uart1_NVIC_Init();
-
- RTC_Configuration_ms();//配置RTC时钟,作为定时器库的依托
-
-
- while(1)
- {
- protothread1(&pt1);
- protothread2(&pt2);
- }
- }
5.编译Keil5,烧录进stm32,观察现象。
可以看到, 即使在慢速闪烁(线程1)的过程中,只要接收到串口发来的0xaa,立马转换为快闪(线程2),快闪五次后回到慢闪(线程1),实现了多线程处理。
Protothreads已经能为程序设计提供轻量线程环境,解决裸机系统无法处理的多线程问题。后续笔者将尝试接触uC/OS III,正式学习嵌入式操作系统。
参考文章:
1.Arduino教程 ProtoThreads在Arduino中的应用#多任务处理#
开发板来源:嵌入式技术公开课
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。