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

#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上操作系统了~!

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

  • 9
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值