玩转RT-Thread系列教程(2)–软硬件定时器的使用
定时器有硬件定时器和软件定时器之分
一、软件定时器
软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。
RT-Thread 操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是 OS Tick 的整数倍,例如一个 OS Tick 是 10ms,那么上层软件定时器只能是 10ms,20ms,100ms 等,而不能定时为 15ms。RT-Thread 的定时器也基于系统的节拍,提供了基于节拍整数倍的定时能力。
1.软件定时器示例:
功能:创建两个软件定时器,一个是单次定时器,另一个是周期型定时器,分别在定时器超时函数中打印信息。
/* 定时器的控制块 */
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, 10,
RT_TIMER_FLAG_PERIODIC);
/* 启动定时器 1 */
if (timer1 != RT_NULL) rt_timer_start(timer1);
/* 创建定时器 2 单次定时器 */
timer2 = rt_timer_create("timer2", timeout2,
RT_NULL, 30,
RT_TIMER_FLAG_ONE_SHOT);
/* 启动定时器 2 */
if (timer2 != RT_NULL) rt_timer_start(timer2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(timer_sample, timer sample);
2.编译、下载、验证
可以看出,单次定时器在30ms时便完成了超时任务,而周期定时器只有在手动关闭时才会停止。
二、HWTIMER定时器
硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
1.首先打开Cubemx配置下基本定时器,生成代码
2.打开env,输入menuconfig进入配置界面
3.使能基本定时器
经过查看f1的参考手册,我们可以看见TIM2、3、4、5是基本定时器
4.输入命令scons –target=mdk5,生成工程
5.示例代码
测试代码如下,实验功能为:设置定时器3为周期性定时器,设置定时器超时值为1s,每1s将led电平反转一次。
#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());
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
rt_kprintf("LED1 AND LED2 TogglePin!\n");
return 0;
}
static int hwtimer_sample(int argc, char *argv[])
{
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;
}
/* 设置定时器超时值为1s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
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;
}
return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(hwtimer_sample, hwtimer sample);
6.编译、下载、验证
下载到开发板,我们可以观察到led1和led2以1s的频率闪烁了。