FreeRTOS的基础时钟

在STM32CubeMX启用FreeRTOS后,在导出代码时会出现一个如图4所示的对话框。提示在使用FreeRTOS时,强烈建议将HAL的基础时钟设置为非SysTick定时器。在前面的示例中,我们都是将HAL的基础时钟设置为定时器TIM6,但并未详细说明这么做的原因。

在前一节已经介绍了HAL基础时钟的作用,以及使用SysTick定时器或TIM6定时器作为HAL基础时钟时的工作原理。通过前面章节对FreeRTOS的介绍,也知道了FreeRTOS需要使用SysTick定时器作为其基础时钟,以产生FreeRTOS的嘀嗒信号,在SysTick的定时中断里进行任务状态检查,需要时发出任务调度申请。

图4 在使用FreeRTOS时提示需要使用非SysTick定时器作为HAL基础时钟源

那么,在使用FreeRTOS时,如果在图4的对话框中点击“Yes”,执意使用SysTick作为HAL的基础时钟,生成的代码编译后能否正常运行呢?如果使用了TIM6作为HAL的基础时钟,FreeRTOS是如何对SysTick进行初始化,如何对SysTick的中断进行处理的呢?

对于第一个问题,如果在图4的对话框中点击“Yes”,执意使用SysTick作为HAL的基础时钟,生成的代码编译后是无法正常运行的,即使FreeRTOS只有一个非常简单的任务。这种情况下FreeRTOS就没有基础时钟,无法产生嘀嗒信号,所以无法正常运行,其代码方面的原因在后面解释。

所以,在使用FreeRTOS时,必须为HAL设置一个非SysTick定时器作为HAL的基础时钟,SysTick将自动作为FreeRTOS的基础时钟。这是FreeRTOS的移植决定的,因为SysTick是Cortex-M内核的一个定时器,在整个STM32系列中都是存在的,使用SysTick作为滴答时钟进行移植显然适用性更强,针对不同系列的STM32处理器移植时需要的改动可以最小化。

1. SysTick定时器的初始化

在STM32CubeMX中基于STM32F407ZG创建一个项目,使用TIM6作为HAL的基础时钟,启用FreeRTOS后NVIC的自动设置结果如图5所示。定时器TIM6的抢占优先级为0,SysTick和PendSV中断的优先级都设置为15,而且这3个中断都不能被关闭,不能修改优先级。

图5 启用FreeRTOS,并使用TIM6作为HAL的基础时钟后的NVIC设置

在FreeRTOS中,系统嘀嗒信号的频率由参数configTICK_RATE_HZ决定,默认值是1000Hz。SysTick通过定时中断产生嘀嗒信号,SysTick默认定时周期是1ms。

在main()函数中,执行函数osKernelStart()启动内核时对SysTick定时器进行初始化设置。跟踪函数osKernelStart()的代码,发现最终设置SysTick定时器的是文件port.c中的函数xPortStartScheduler()和vPortSetupTimerInterrupt()。函数xPortStartScheduler()中设置SysTick和PendSV中断的中断优先级,函数vPortSetupTimerInterrupt()设置SysTick的定时周期,代码如下:

	// 设置systick 定时器,产生需要频率的嘀嗒中断
	__attribute__(( weak )) void vPortSetupTimerInterrupt( void )
	{
		/* 计算用于配置嘀嗒中断的需要的常数 */
		#if( configUSE_TICKLESS_IDLE == 1 )     //Tickless低功耗模式,正常情况下为0
		{
			ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
			xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
			ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
		}
		#endif /* configUSE_TICKLESS_IDLE */
	
		/*停止和清除SysTick的控制寄存器和计数值寄存器 */
		portNVIC_SYSTICK_CTRL_REG = 0UL;
		portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
	
		/* 配置SysTick,使其以设定的频率产生中断*/
		portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
		portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
	}

函数vPortSetupTimerInterrupt()的功能是配置SysTick定时器相关的寄存器,使其以设定的频率产生中断。当参数configUSE_TICKLESS_IDLE的值等于1的时候,也就是使用了Tickless低功耗模式的时候会计算几个常数,这几个常数会在Tickless低功耗模式的时候用于嘀嗒计数值的补偿。

函数中用到的portNVIC_SYSTICK_CTRL_REG、portNVIC_SYSTICK_LOAD_REG等宏是SysTick相关寄存器的移植定义,在文件port.c中的定义时:

	#define portNVIC_SYSTICK_CTRL_REG			( * ( ( volatile uint32_t * ) 0xe000e010 ) )
	#define portNVIC_SYSTICK_LOAD_REG			( * ( ( volatile uint32_t * ) 0xe000e014 ) )
	#define portNVIC_SYSTICK_CURRENT_VALUE_REG	( * ( ( volatile uint32_t * ) 0xe000e018 ) )

查阅Cortex-M4内核技术手册会发现,这3个宏对应的就是SysTick的控制和状态寄存器SYST_CSR、重载值寄存器SYST_RVR和当前值寄存器SYST_CVR。

2. SysTick定时器的中断处理

在使用TIM6作为HAL基础时钟,启用了FreeRTOS的项目中,会发现在文件stm32f4xx_it.c中没有SysTick中断ISR函数SysTick_Handler()的代码框架。而FreeRTOS要使用SysTick的定时中断产生嘀嗒信号,必然是要定义ISR函数的。搜索关键字SysTick_Handler,发现在文件FreeRTOSConfig.h中有如下的宏定义:

	/*  将FreeRTOS 移植的中断处理函数映射到CMSIS 的标准ISR函数名  */
	#define vPortSVCHandler     SVC_Handler
	#define xPortPendSVHandler  PendSV_Handler
	
	/* 重要提示: 在使用STM32Cube时,如果HAL的基础时钟被设置为SysTick,下面的定义会被注释,以免覆盖HAL定义的ISR函数SysTick_Handler  */
	#define xPortSysTickHandler SysTick_Handler

通过这3个宏定义,FreeRTOS将自己移植的3个中断的处理函数与CMSIS的标准ISR函数名关联起来。例如,SysTick中断的标准ISR函数名是SysTick_HandlerFreeRTOS移植的函数名就是xPortSysTickHandler

源代码中有英文注释特别强调,如果将HAL的基础时钟设置为SysTick,那么第3个宏定义是会被注释掉的,以免覆盖HAL定义的ISR函数SysTick_Handler。但是,这种情况下FreeRTOS就没有基础时钟了,不会产生嘀嗒信号,所以FreeRTOS就无法正常运行了。

FreeRTOS移植的SysTick中断的处理函数xPortSysTickHandler()是在文件port.c中定义的,其功能是调用函数xTaskIncrementTick()使嘀嗒信号计数值递增,并且检查是否需要进行上下文切换。如果需要进行上下文切换,就将PendSV中断挂起,实际的上下文切换是在PendSV的中断处理程序里执行的。函数xTaskIncrementTick()的代码如下。

	void xPortSysTickHandler( void )
	{
		portDISABLE_INTERRUPTS();
		{
			/* 将RTOS嘀嗒计数值递增,并检查是否需要进行上下文切换 */
			if( xTaskIncrementTick() != pdFALSE )
			{
				/*使PendSV中断挂起,请求上下文切换,上下文切换在PendSV中断里执行 */
				portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
			}
		}
		portENABLE_INTERRUPTS();
	}

其中调用的函数xTaskIncrementTick()是在文件task.c中实现的,文件task.c中定义了一个表示嘀嗒信号当前计数值的全局变量xTickCount,函数xTaskIncrementTick()的一个功能就是每次使xTickCount的值加1,在计算延时的时候就会用到全局变量xTickCount。

前一篇 使用SysTick作为HAL的基础时钟

主题   HAL和FreeRTOS的基础时钟

  • 12
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
FreeRTOS是一个开源的实时操作系统,它专为嵌入式系统设计,提供了任务调度、内存管理、中断处理、时间管理和通信机制等功能。在FreeRTOS中,系统时钟的节拍可以通过配置文件FreeRTOSConfig.h来设置,使用#define configTICK_RATE_HZ来定义时钟节拍的频率,例如#define configTICK_RATE_HZ ((TickType_t) 1000)表示时钟节拍为1毫秒。 FreeRTOS还提供了事件标志组(Event Group)的功能,通过一系列函数可以实现对事件的操作。这些函数包括xEventGroupCreate()、xEventGroupCreateStatic()、vEventGroupDelete()、xEventGroupWaitBits()、xEventGroupSetBits()、xEventGroupSetBitsFromISR()、xEventGroupClearBits()、xEventGroupClearBitsFromISR()、xEventGroupGetBits()、xEventGroupGetBitsFromISR()、xEventGroupSync()等。通过这些函数,可以创建、删除、等待和设置事件标志组,以实现任务间的同步与通信。 在使用FreeRTOS时,一般需要进行一些基本的配置,如时钟配置、串口使能以及勾选任务信息相关的宏定义(方便查看任务信息)。另外,还可以根据需要添加消息队列等组件以满足不同的应用需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [FreeRTOS学习笔记(一)——基础知识体系](https://blog.csdn.net/xingzhewanfu/article/details/85262135)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值