我是靠谱客的博主 专注铅笔,最近开发中收集的这篇文章主要介绍Protothreads实现STM32多线程处理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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

一、将Protothreads加入keil5工程

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

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

二、使用Protothreads编写代码

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

#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中的应用#多任务处理#

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

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

最后

以上就是专注铅笔为你收集整理的Protothreads实现STM32多线程处理的全部内容,希望文章能够帮你解决Protothreads实现STM32多线程处理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部