【RTT-Studio】详细使用教程十四:软硬件定时器

一、简介

  硬件定时器:在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;
}

五、测试验证

  通过串口助手测试,基本上都可以实现一个定时的功能,具体效果如下所示:
在这里插入图片描述
在这里插入图片描述


  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值