STM32systick的妙用

1、此次博客的灵感由来:

        最近在短视频平台刷到关于状态机的视频,然后STM32中有一个滴答计时器叫systick,恰好他又有中断功能,那么是不是可以用systick做一个任务状态机,来让任务实现最基本的“挂起态,就绪态,执行态”,从而摆脱平日使用传统delay()函数造成的阻塞问题,提高系统响应效率。

2、什么是状态机

        状态机按照我的理解就是,检测某一样东西他当前的状态,可以是按键的按下与抬起,也可以是RTOS中任务函数的等待与执行,又或者是中断函数等待中断信号出现的状态。

3、在stm32平台上实现状态机

        由于此次状态机的实现涉及到了RTOS任务调度的基础概念,觉得阅读困难的可以去稍微了解一下关于这方面的知识点。

        在平日对systick的使用,大部分都是将他用于定时功能,其实systick的全称是“滴答定时计数器”,而我们用的时候大多都忽略他的本质是个计数器。本次项目我们需要将他配置为定时1ms的计数功能,看到这个1ms是不是想起了RTOS中的心跳时钟,所以结合systick+RTOS任务调度的基本概念,就可以实现状态机。

        3.1 systick配置

static uint8_t us = 0;
static uint16_t ms = 0;

void SysTickConfig(void)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
	
	us = SystemCoreClock / 8000000;
	
	ms = (uint16_t )us *1000;
}

void osTickIinit(void)
{
	SysTick->LOAD 	 = 1 * ms;
	SysTick->VAL	 = 0x00;
	SysTick->CTRL	|= SysTick_CTRL_TICKINT_Msk;
	SysTick->CTRL	|= SysTick_CTRL_ENABLE_Msk;	
}

        3.1.1 时钟源选择

        默认SysTick_CLKSource_HCLK_Div8即可

        3.1.2 主要配置

        自动重装载值赋予systick走完1ms所需的值,然后打开计数完成中断,并使能systick。即systick将会每 1ms 触发一次中断,这我喜欢称他为 状态机的心跳时钟。

        3.2 状态机配置

       

#define MAX_TASK 10  //任务最大堆栈

uint32_t sysTickCount = 0;

typedef void (*osTaskHandle)(void* parm);

typedef struct {
	char* taskName;		//任务函数名字
	uint8_t firstPriority;	//优先级
	uint32_t funcTick;	//任务心跳
	uint32_t lastFunckTick;	//上一次心跳时间
	void* parm;		//参数
	osTaskHandle func;	//需要执行函数
}_systemSM;

_systemSM taskStack[MAX_TASK]={0};


//这个函数放在.h文件里面
__inline uint32_t getOsTickHeart()
{
	return sysTickCount;
}

uint8_t osTaskCreat(char* name,uint8_t fp, uint32_t delay, osTaskHandle func, void* parm)
{
	_systemSM systemSM;
	if(fp < MAX_TASK)
	{
		systemSM.taskName = name;
		systemSM.firstPriority = fp;
		systemSM.funcTick = delay;
		systemSM.func = func;
		systemSM.lastFunckTick = getOsTickHeart();
		systemSM.parm = parm;
		taskStack[fp] = systemSM;
	}
	
	return 1;
}

void osTaskPolling(void)
{
	static uint32_t tickNow;
	
	tickNow = getOsTickHeart();
	
	for(uint8_t i=0;i<MAX_TASK;i++)
	{
		if(taskStack[i].taskName != 0)    //检测任务函数区块是否有效
		{
			if(tickNow-taskStack[i].funcTick == taskStack[i].lastFunckTick)//检测当前系统心跳是否大于上一次任务执行心跳+任务延时
			{
				taskStack[i].lastFunckTick = tickNow;//更新任务执行心跳
				taskStack[i].func(taskStack[i].parm);//执行任务
			}
		}
	}
}

void GPIO_Toggle_Bit(void* parm)
{
	static uint8_t status;
	status =~status;
	PCout(13) = status;
}

void printout1(void* parm)
{
	STM_LOG(parm,"task in 1");

}

void printout2(void* parm)
{
	STM_LOG(parm,"task in 2");
}


int main (void)
{
	SysTickConfig();
	osTickIinit();
	usart_init(115200);
	LedConfig();
	
    //清除挂起
	SCB->ICSR = SCB_ICSR_PENDSVCLR_Msk;
    //将pendSV配置最低优先级
	NVIC_SetPriority(PendSV_IRQn,0xFF);
	
	osTaskCreat("led",2,500,GPIO_Toggle_Bit,NULL);
	osTaskCreat("p1",0,1000,printout1,"printout1");
	osTaskCreat("p2",1,1000,printout2,"printout2");
	
	while(1);
	
}

void SysTick_Handler(void)
{
	sysTickCount+=1;
    //检测pendSV是否挂起状态 这很重要
	if(!(SCB->ICSR & SCB_ICSR_PENDSVSET_Msk))
		SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}


void PendSV_Handler(void)
{
	osTaskPolling();
}

        业务逻辑:通过systick每一秒产生的中断去软件触发pendSV中断,让pendSV中断函数来轮询检测当前是否有任务到达用户设定的延时时长,如果有则执行。当同一时间内有多个任务同时满足延时时长,则按任务初始化时的优先级按顺序执行。

        3.2.1 任务初始化

        uint8_t osTaskCreat(char* name,uint8_t fp, uint32_t delay, osTaskHandle func, void* parm)

        name:任务名字,用于任务区块检测

        fp:优先级,同一优先级只能配置一个任务

        delay:任务延时,即任务每delay多少ms执行一次

        func:任务执行函数

        parm:传递参数

        3.3 实现效果

        

        3.4 pendSV

        可能有些人会好奇,为什么非要通过pendSV中断来实现轮询查表,而不是直接在systick中断里面直接轮询,其实这与stm32的中断优先级有关,在中断向量表中SysTick_Handler的优先级是要高于外设中断的,比如说按键中断,串口中断等等,而systick中断又关乎于整个系统心跳稳定性的函数,一但因为某个函数执行时间>1ms,这对于整个系统来说是灾难性的。而为什么不在main函数while循环中执行,我个人的见解就是,在嵌入式开发中,能用中断尽量用中断,能用回调尽量用回调,什么都往main里面塞,代码执行效率不高。而且我用pendSV轮询的话,while循环中又可以在加一个函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

河狸子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值