当前位置:   article > 正文

Protothreads实现STM32多线程处理_stm32 多线程

stm32 多线程

在学习嵌入式操作系统之前,我注意到了这个能够实现轻量线程环境的函数库(或者可以说是最最简单的操作系统),于是想动手尝试能不能在STM32工程中浅用一下。

一、将Protothreads加入keil5工程

1.将Protothreads直接复制进工程文件中(源码下载链接Protothreads - download);

2.由于 Protothreads全部由头文件构成,在Keil工程设置中直接include即可。

二、使用Protothreads编写代码

1.仿照Malc编写一个程序:LED1灭一秒亮一秒,同时LED2灭五秒亮五秒。代码如下:

  1. #include <stm32f10x.h>
  2. #include "Led_Key.h"
  3. #include "bsp_exti.h"
  4. #include "bsp_SysTick.h"
  5. #include "bsp_iwdg.h"
  6. #include "bsp_wwdg.h"
  7. #include "bsp_uart.h"
  8. #include "bsp_dma.h"
  9. #include "bsp_adc.h"
  10. #include "bsp_tim2.h"
  11. #include "bsp_rtc.h"
  12. #include <pt.h>//Protothreads头文件,必须包括
  13. static int counter1,counter2;
  14. static int protothread1(struct pt *pt)
  15. {
  16. PT_BEGIN(pt);//线程开始
  17. while(1)
  18. {
  19. PT_WAIT_UNTIL(pt, counter1 == 1);//如果时间满1秒继续执行,否则记录运行点并退出线程1
  20. GPIOA->ODR ^= GPIO_Pin_1;//灯1状态反转
  21. counter1 = 0;
  22. }
  23. PT_END(pt);//线程结束
  24. }
  25. static int protothread2(struct pt *pt)
  26. {
  27. PT_BEGIN(pt);//线程开始
  28. while(1)
  29. {
  30. PT_WAIT_UNTIL(pt, counter2 == 5);//如果时间满5秒继续执行,否则记录运行点并退出线程2
  31. GPIOA->ODR ^= GPIO_Pin_2;//灯2状态反转
  32. counter2 = 0;
  33. }
  34. PT_END(pt);//线程结束
  35. }
  36. static struct pt pt1, pt2;
  37. int main(void)
  38. {
  39. PT_INIT(&pt1);//线程1初始化
  40. PT_INIT(&pt2);//线程2初始化
  41. SysTick_Configuration();
  42. Led_Configuration();
  43. while(1)
  44. {
  45. protothread1(&pt1);//执行线程1
  46. protothread2(&pt2);//执行线程2
  47. Delay_us(1000000);
  48. counter1++;
  49. counter2++;
  50. }
  51. }

 2.编译Keil5,此时提示User\main.c(18): warning:  #550-D: variable "PT_YIELD_FLAG" was set but never used,不用管,烧录进stm32,观察现象。

 三、Protothreads进阶——信号量

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计时器记录时间(该文件其他宏定义同样可以修改);

  1. #define PT_TIMER_DELAY(pt,time) \
  2. do { \
  3. (pt)->t = millis(); \
  4. PT_WAIT_UNTIL((pt),((pt_timer)(millis()-(pt)->t)>=(time)));\
  5. }while(0)

修改前 

  1. #define PT_TIMER_DELAY(pt,time) \
  2. do { \
  3. (pt)->t = RTC_GetCounter(); \
  4. PT_WAIT_UNTIL((pt),((pt_timer)(RTC_GetCounter()-(pt)->t)>=(time)));\
  5. }while(0)

修改后

 3.修改rtc时钟配置(改为rtc每震荡一次经过一毫秒),并编写中断处理函数;

  1. int RTC_Configuration_ms(void)
  2. {
  3. RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);
  4. PWR_BackupAccessCmd(ENABLE);
  5. if(BKP_ReadBackupRegister(BKP_DR1) != 0x1234)
  6. {
  7. BKP_DeInit();
  8. RCC_LSEConfig(RCC_LSE_ON);
  9. while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)!=SET)
  10. {
  11. }
  12. RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
  13. RCC_RTCCLKCmd(ENABLE);
  14. RTC_WaitForLastTask();
  15. RTC_WaitForSynchro();
  16. RTC_SetPrescaler(32);//(32 + 1) / 32768 ≈ 0.001(s)
  17. RTC_WaitForLastTask();
  18. BKP_WriteBackupRegister(BKP_DR1, 0x1234);
  19. return 0;
  20. }
  21. return 1;
  22. }
  1. void USART1_IRQHandler(void)
  2. {
  3. while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
  4. {
  5. rdata = USART_ReceiveData(USART1);//此处rdata为全局变量,以便被main.c使用
  6. }
  7. }

4.使用Protothreads编写main.c文件,通过信号量实现要求;

  1. #include <stm32f10x.h>
  2. #include "Led_Key.h"
  3. #include "bsp_exti.h"
  4. #include "bsp_SysTick.h"
  5. #include "bsp_iwdg.h"
  6. #include "bsp_wwdg.h"
  7. #include "bsp_uart.h"
  8. #include "bsp_dma.h"
  9. #include "bsp_adc.h"
  10. #include "bsp_tim2.h"
  11. #include "bsp_rtc.h"
  12. #define PT_USE_TIMER//定时器库
  13. #define PT_USE_SEM//信号量库
  14. #include <pt.h>
  15. static struct pt_sem sem_LED;
  16. unsigned char i;
  17. static int protothread1(struct pt *pt)
  18. {
  19. PT_BEGIN(pt);//线程开始
  20. while(1)
  21. {
  22. PT_SEM_WAIT(pt, &sem_LED); //等待LED信号量可用
  23. GPIOA->ODR ^= GPIO_Pin_1;//灯1状态反转
  24. PT_TIMER_DELAY(pt, 1000);//留一秒
  25. PT_SEM_SIGNAL(pt, &sem_LED);//信号量用完了
  26. PT_YIELD(pt);//让给其他线程(此处很重要,否则会卡在while循环里出不来)
  27. }
  28. PT_END(pt);//线程结束
  29. }
  30. static int protothread2(struct pt *pt)
  31. {
  32. PT_BEGIN(pt);//线程开始
  33. while(1)
  34. {
  35. PT_WAIT_UNTIL(pt, rdata == 0xaa);
  36. PT_SEM_WAIT(pt, &sem_LED);//等待LED信号量可用
  37. for(i = 0; i < 5; i++)
  38. {
  39. GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
  40. PT_TIMER_DELAY(pt, 200);//留0.2秒
  41. GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
  42. PT_TIMER_DELAY(pt, 200);//留0.2秒
  43. }
  44. rdata = 0;//防止再次while
  45. PT_SEM_SIGNAL(pt, &sem_LED); //信号量用完了
  46. }
  47. PT_END(pt);//线程结束
  48. }
  49. static struct pt pt1, pt2;
  50. int main(void)
  51. {
  52. PT_INIT(&pt1);
  53. PT_INIT(&pt2);
  54. PT_SEM_INIT(&sem_LED,1); //初始化信号量为1,即没人用
  55. SysTick_Configuration();
  56. Led_Configuration();
  57. Uart1_Configuration();
  58. Uart1_NVIC_Init();
  59. RTC_Configuration_ms();//配置RTC时钟,作为定时器库的依托
  60. while(1)
  61. {
  62. protothread1(&pt1);
  63. protothread2(&pt2);
  64. }
  65. }

 5.编译Keil5,烧录进stm32,观察现象。

可以看到, 即使在慢速闪烁(线程1)的过程中,只要接收到串口发来的0xaa,立马转换为快闪(线程2),快闪五次后回到慢闪(线程1),实现了多线程处理。

四、总结

Protothreads已经能为程序设计提供轻量线程环境,解决裸机系统无法处理的多线程问题。后续笔者将尝试接触uC/OS III,正式学习嵌入式操作系统。

参考文章:

1.Arduino教程 ProtoThreads在Arduino中的应用#多任务处理#

2.玩儿大了~给arduino上操作系统了~!

 开发板来源:嵌入式技术公开课

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/353623
推荐阅读
相关标签
  

闽ICP备14008679号