一、简介
硬件定时器
:在RTT中使用硬件定时器,主要是需要配置HWTIMER,以及通过CubeMx来生成定时器的一些初始化代码,便于使用定时器的定时功能,硬件定时器会有一个数目的限制,如果硬件定时器使用完了,那就无法添加定时器的使用,所以,综合考虑还是可以多使用软件定时器来替代硬件定时器。
软件定时器
:是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。RT-Thread 操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是 OS Tick 的整数倍,例如一个 OS Tick 是 10ms,那么上层软件定时器只能是 10ms,20ms,100ms 等,而不能定时为 15ms。RT-Thread 的定时器也基于系统的节拍,提供了基于节拍整数倍的定时能力。
二、RTT时钟配置
在使用定时器的时候,可以使用内部时钟,也可以使用外部时钟,一般情况下可以通过外部晶振来配置外部时钟,配置过程如下:
- 打开RT-Thread Studio软件新建基于芯片的项目,并使用外部时钟系统。
- 在drv_clk.c文件中添加时钟配置函数,并且注释内部时钟的调用。
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
void clk_init(char *clk_source, int source_freq, int target_freq)
{
// system_clock_config(target_freq);
SystemClock_Config();
}
三、硬件定时器使用
1.配置RT-Thread Settings—》组件—》设备驱动程序—》使用HWTIMER设备驱动程序。
2.开启board.h的宏配置
/*-------------------------- HARDWARE TIMER CONFIG BEGIN --------------------------*/
/** if you want to use hardware timer you can use the following instructions.
*
* STEP 1, open hwtimer driver framework support in the RT-Thread Settings file
*
* STEP 2, define macro related to the hwtimer
* such as #define BSP_USING_TIM and
* #define BSP_USING_TIM1
*
* STEP 3, copy your hardwire timer init function from stm32xxxx_hal_msp.c generated by stm32cubemx to the end of board.c file
* such as void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
*
* STEP 4, modify your stm32xxxx_hal_config.h file to support hardwere timer peripherals. define macro related to the peripherals
* such as #define HAL_TIM_MODULE_ENABLED
*
*/
#define BSP_USING_TIM
#ifdef BSP_USING_TIM
#define BSP_USING_TIM3
/*#define BSP_USING_TIM16*/
/*#define BSP_USING_TIM17*/
#endif
/*-------------------------- HAREWARE TIMER CONFIG END --------------------------*/
3.开启stm32f1xx_hal_conf.h的宏配置
4.用stm32cube配置TIM相关驱动内容
这里只使用了定时器基础的计数功能,只需要进行简单的配置即可。得到void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)函数即可,放在board.c后面。
/**
* @brief 硬件定时器初始化
* @param htim_base
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (htim_base->Instance == TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE();
}
}
5.初始化例程
#define HWTIMER_DEV_NAME "timer3" /* 定时器名称 */
/* 定时器超时回调函数 */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
rt_kprintf("this is hwtimer timeout callback fucntion!\n");
rt_kprintf("tick is :%d !\n", rt_tick_get());
return 0;
}
static int hwtimer_sample(void)
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
if (hw_dev == RT_NULL)
{
rt_kprintf("hwtimer sample run failed! can't find %s device!\n", HWTIMER_DEV_NAME);
return RT_ERROR;
}
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
if (ret != RT_EOK)
{
rt_kprintf("open %s device failed!\n", HWTIMER_DEV_NAME);
return ret;
}
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
if (ret != RT_EOK)
{
rt_kprintf("set mode failed! ret is :%d\n", ret);
return ret;
}
/* 设置定时器超时值为3s并启动定时器 */
timeout_s.sec = 3; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
{
rt_kprintf("set timeout value failed\n");
return RT_ERROR;
}
/* 延时3500ms */
rt_thread_mdelay(1000);
/* 读取定时器当前值 */
rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
rt_kprintf("Read: Sec = %d, Usec = %d\n", timeout_s.sec, timeout_s.usec);
return ret;
}
6.main.c
#include <rtthread.h>
#include <drv_common.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
static int hwtimer_sample(void);
int main(void)
{
int count = 1;
hwtimer_sample();
while (count)
{
rt_thread_mdelay(1000);
}
return RT_EOK;
}
四、软件定时器使用
1.查看系统的时钟节拍OS Tick:无特殊需求,使用默认1000HZ,即1ms即可。
2.例程使用:例程会创建两个动态定时器,一个是单次定时,一个是周期性定时并让周期定时器运行一段时间后停止运行
/* 定时器的控制块 */
static rt_timer_t timer1;
static rt_timer_t timer2;
static int cnt = 0;
/* 定时器 1 超时函数 */
static void timeout1(void *parameter)
{
rt_kprintf("periodic timer is timeout %d\n", cnt);
/* 运行第 10 次,停止周期定时器 */
if (cnt++>= 9)
{
rt_timer_stop(timer1);
rt_kprintf("periodic timer was stopped! \n");
}
}
/* 定时器 2 超时函数 */
static void timeout2(void *parameter)
{
rt_kprintf("one shot timer is timeout\n");
}
int timer_sample(void)
{
/* 创建定时器 1 周期定时器 */
timer1 = rt_timer_create("timer1", timeout1, RT_NULL, 1000, RT_TIMER_FLAG_PERIODIC);
/* 启动定时器 1 */
if (timer1 != RT_NULL) rt_timer_start(timer1);
/* 创建定时器 2 单次定时器 */
timer2 = rt_timer_create("timer2", timeout2, RT_NULL, 2500, RT_TIMER_FLAG_ONE_SHOT);
/* 启动定时器 2 */
if (timer2 != RT_NULL) rt_timer_start(timer2);
return 0;
}
3.main.c
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
extern int timer_sample(void);
int main(void)
{
int count = 1;
timer_sample();
while (count)
{
rt_thread_mdelay(1000);
}
return RT_EOK;
}
五、测试验证
通过串口助手测试,基本上都可以实现一个定时的功能,具体效果如下所示: