FreeRTOS系统-独立看门狗监测任务执行状态

FreeRTOS系统-独立看门狗监测任务执行状态


日期 作者 版本 说明
2021.02.05 Hxj V1.0 完成主体

提示:以下文章基于FreeRTOS全部移植完成,能够正常运行.


前言

介绍一种使用事件标志组配合独立看门狗监测任务执行状态的方法,为大家提供一种在软件或者硬件死机时,FreeRTOS 系统如何保证系统复位的思路。


一、独立看门狗的使用

独立看门狗的介绍

由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,简单来说,看门狗就是在不停地对我们正在运行的程序进行监视,我们必须在规定的时间的有效的时间内去进行“喂狗”操作,这样看门狗就知道我们的程序正在正常的运行,如果我们程序由于外界原因跑飞了,那么就无法进行喂狗操作,那么看门狗由于饥饿难耐,他就会使我们的单片机进行复位,以便让程序重新开始运行。

独立看门狗是由专门的低速总线进行驱动,即LSI总线(时钟频率40KHz),它可以在主时钟故障的情况下仍然可以工作(或许这就是把这只狗成为独立看门狗的原因吧)。独立看门狗适合应用于需要看门狗作为一个在主程序之外 能够完全独立工作,并且对时间精度要求低的场合。

在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,当计数器值计数到尾值0x000时会产生一个复位信号(IWDG_RESET)。

无论何时,只要在键值寄存器IWDG_KR中写入0xAAAA(通常说的喂狗,但是0XAAAA并不是装入计数器中的值,真正装入计数器中的值是我们根据分频系数和我们需要的超时时间来计算好的), 自动重装载寄存器IWDG_RLR的值就会重新加载到计数器,从而避免看门狗复位。
如果程序异常,就无法正常喂狗,从而系统复位。

/*
 * 设置 IWDG 的超时时间
 * Tout = prv/40 * rlv (s)
 *      prv可以是[4,8,16,32,64,128,256]
 * prv:预分频器值,取值如下:
 *     @arg IWDG_Prescaler_4: IWDG prescaler set to 4
 *     @arg IWDG_Prescaler_8: IWDG prescaler set to 8
 *     @arg IWDG_Prescaler_16: IWDG prescaler set to 16
 *     @arg IWDG_Prescaler_32: IWDG prescaler set to 32
 *     @arg IWDG_Prescaler_64: IWDG prescaler set to 64
 *     @arg IWDG_Prescaler_128: IWDG prescaler set to 128
 *     @arg IWDG_Prescaler_256: IWDG prescaler set to 256
 *
 *        独立看门狗使用LSI作为时钟。
 *        LSI 的频率一般在 30~60KHZ 之间,根据温度和工作场合会有一定的漂移,我
 *        们一般取 40KHZ,所以独立看门狗的定时时间并一定非常精确,只适用于对时间精度
 *        要求比较低的场合。
 *
 * rlv:重装载寄存器的值,取值范围为:0-0XFFF
 * 函数调用举例:
 * IWDG_Config(IWDG_Prescaler_64 ,625);  // IWDG 1s 超时溢出
 *                        (64/40)*625 = 1s
 */
void IWDG_Config(uint8_t prv ,uint16_t rlv)
{
    
    
    // 使能 预分频寄存器PR和重装载寄存器RLR可写
    IWDG_WriteAccessCmd( IWDG_WriteAccess_Enable );
    // 设置预分频器值
    IWDG_SetPrescaler( prv );
    // 设置重装载寄存器值
    IWDG_SetReload( rlv );
    // 把重装载寄存器的值放到计数器中
    IWDG_ReloadCounter();
    // 使能 IWDG
    IWDG_Enable();
}
// 喂狗
void IWDG_Feed(void)
{
    
    
    // 把重装载寄存器的值放到计数器中,喂狗,防止IWDG复位
    // 当计数器的值减到0的时候会产生系统复位
    IWDG_ReloadCounter();
}

使用指南

在程序合适的位置调用IWDG_Config(uint8_t prv ,uint16_t rlv)根据程序来设置看门狗的复位时间Tout = prv/40 * rlv (s),传入合适的参数。在程序的合适位置定时调用IWDG_Feed()定时喂狗,如果程序异常,未进行喂狗程序则会复位。我们必须在这个时间内进行喂狗至少一次(哪怕你一直不停的喂,但是必须至少喂一次,否则它就会使单片机复位)。

二、FreeRTOS事件标志组的使用

事件标志组是实现多任务同步的有效机制之一。任务间事件标志组的实现是指各个任务之间使用事件标志组实现任务的通信或者同步机制。FreeRTOS在event_groups.c/h]文件中提供了事件标志组的具体实现。


1.事件标志组的简介

实际上就是一个标志位数组,通过FreeRTOS提供的API调用。

需要用到的头文件

#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"

创建事件标志组

函数 描述
xEventGroupCreate() 动态创建事件标志组

使用(示例):

扫描二维码关注公众号,回复: 12652802 查看本文章
EventGroupHandle_t xCreatedEventGroup;//声明事件组 
xCreatedEventGroup = xEventGroupCreate();	//创建事件组

设置标志组

函数 描述
xEventGroupSetBits() 任务级设置标志位
xEventGroupSetBitsFromISR() 中断中设置标志位

xEventGroupSetBitsFromISR()xEventGroupSetBits()的中断版本,用于置位事件组中指定的位。置位事件组中的标志位是一个不确定的操作,因为阻塞在事件组的标志位上的任务的个数是不确定的。FreeRTOS 是不允许不确定的操作在中断和临界段中发生的,所以
xEventGroupSetBitsFromISR()给 FreeRTOS 的守护任务发送一个消息,让置位事件组的操作在守护任务里面完成,守护任务是基于调度锁而非临界段的机制来实现的。

使用(示例):

//LED0任务函数
void led0_task(void *pvParameters)
{
    
    
	while(1)
	{
    
    
		MCU_LED=~MCU_LED;
		xEventGroupSetBits(xCreatedEventGroup, TASK_BIT_1);//在一个任务中将标志位组一位置位
		vTaskDelay(500);
	}
} 

等待事件标志位

函数 描述
xEventGroupWaitBits( ) 等待事件标志位
 EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
                                   const EventBits_t uxBitsToWaitFor,
                                   const BaseType_t xClearOnExit,
                                   const BaseType_t xWaitForAllBits,
                                   const TickType_t xTicksToWait ); 

xEventGroup : 指定要等待的事件标志组。

uxBitsToWaitFord: 指定要等待的事件位,比如要等待bit0和(或)bit2的时候此参数就是0X05, 如果要等待 bit0 和(或)bit1 和(或)bit2 的时候此参数就是 0X07,以此类推。

xClearOnExit: 此参数要是为pdTRUE的话,那么在退出此函数之前由参数uxBitsToWaitFor 所设置的这些事件位就会清零。如果设置位 pdFALSE 的话这些事件位就不会改变。

xWaitForAllBits: 此参数如果设置为 pdTRUE 的话,当 uxBitsToWaitFor 所设置的这些事件 位都置 1,或者指定的阻塞时间到的时候函数 xEventGroupWaitBits()才会返回。当此函数为 pdFALSE 的话,只要 uxBitsToWaitFor 所设置的这些事件 位 其 中 的 任 意 一 个 置 1 , 或 者 指 定 的 阻 塞 时 间 到 的 话 函 数xEventGroupWaitBits()就会返回。

xTicksToWait : 设置阻塞时间,单位为节拍数。

返回值:任何值,返回当所等待的事件位置 1 以后的事件标志组的值,或者阻塞时间到。根据这个值我们就知道哪些事件位置 1 了。如果函数因为阻塞时间到而返回 的话那么这个返回值就不代表任何的含义。

三、系统中的看门狗监测任务执行状态

为了保证 FreeRTOS 的所有用户任务都在正常的运行,我们通过独立看门狗的形式来监测,一旦发现有某个任务长时间没有执行,看门狗就会将系统复位。

实现的逻辑思路:

创建一个事件标志组,在重要的几个任务中将事件标志位置一代表任务执行过,将喂狗程序放在最高优先级的任务里面,其它的任务都定期的向最高优先级任务发送事件标志,只有其他任务都发来了事件标志才进行喂狗。

使用指南

创建事件标志组

EventGroupHandle_t xCreatedEventGroup;//声明事件组 
xCreatedEventGroup = xEventGroupCreate();	//创建事件组

在任务中将事件标志组置一

#define TASK_BIT_1    (0x01 << 0)
#define TASK_BIT_2    (0x01 << 1)
#define TASK_BIT_3    (0x01 << 2)
#define TASK_BIT_4    (0x01 << 3)
#define TASK_BIT_5    (0x01 << 4)
#define TASK_BIT_6    (0x01 << 5)
#define TASK_BIT_ALL	( TASK_BIT_1 |TASK_BIT_2 | TASK_BIT_3|TASK_BIT_4)

//LED0任务函数
void led0_task(void *pvParameters)
{
    
    
	while(1)
	{
    
    
		MCU_LED=~MCU_LED;
		xEventGroupSetBits(xCreatedEventGroup, TASK_BIT_1);//标志位置一
		vTaskDelay(500);
	}
}

//modbus 刷新任务
void ModbusPoll_task(void *pvParameters)
{
    
    
	TickType_t ticks = xTaskGetTickCount();
	while (1)
	{
    
    
		eMBPoll();
		//1ms 一个处理周期
		xEventGroupSetBits(xCreatedEventGroup, TASK_BIT_2);//标志位置一
		vTaskDelayUntil( &ticks, 1 );
	}
}

创建一个喂狗任务,任务优先级最高

/*
 * 喂狗任务
 * 在任务均已执行的情况下进行喂狗
 */
void Iwdg_task(void *pvParameters)
{
    
    
	 EventBits_t uxBits;
	 const TickType_t xTicksToWait = 2000 / portTICK_PERIOD_MS; /* 最大延迟2000ms */
	/*
	  开始执行启动任务主函数前使能独立看门狗。
	  设置LSI是64分频,下面函数参数范围0-0xFFF,分别代表最小值2ms和最大值6552ms

	   设置LSI是128分频,下面函数参数范围0-0xFFF,分别代表最小值4ms和最大值13104ms
	   Tout = prv/40 * rlv (s)
	  下面设置的是5s,如果5s内没有喂狗,系统复位。
	*/
	IWDG_Config(IWDG_Prescaler_64 ,625*5);
	while(1)
	{
    
    
		/* 等待所有任务发来事件标志 */
		uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件标志组句柄 */
									 TASK_BIT_ALL,       /* 等待TASK_BIT_ALL被设置 */
									 pdTRUE,             /* 退出前TASK_BIT_ALL被清除,这里是TASK_BIT_ALL都被设置才表示“退出”*/
									 pdTRUE,             /* 设置为pdTRUE表示等待TASK_BIT_ALL都被设置*/
									 xTicksToWait);      /* 等待延迟时间 */
		if((uxBits & TASK_BIT_ALL) == TASK_BIT_ALL)//判断各个任务是否执行
		{
    
    
			IWDG_Feed();
		}
	}
}

推荐在最高优先级任务里面实现喂狗,这样才可以保证其它低优先级任务发来了事件标志后,喂狗任务可以及时的喂狗。 如果放在一个低优先级的任务里面会存在问题,比如所有的任务都已经发送了表示自己正常运行的事件标志,但是此低优先级任务在执行喂狗程序前被其它高优先级的任务抢占了,造成不能及时喂狗,从而导致系统复位,这种误判断会使得系统不能够正常工作。

四、总结

在本文中使用事件标志组配合看门狗,进行对程序异常进行处理使其重新工作。这只能使程序进行异常恢复,不能恢复到异常前的运动状态,后续需要加入后备寄存器进行保存。

猜你喜欢

转载自blog.csdn.net/qq_45531642/article/details/113678634