如何使用硬件定时器及计数器

您所在的位置:网站首页 个十百千万计数器软件是什么 如何使用硬件定时器及计数器

如何使用硬件定时器及计数器

2023-12-14 18:51| 来源: 网络整理| 查看: 265

前言

我相信在嵌入式领域的各位大佬,不管在什么样的项目或多或少都是要用到定时器吧,基本上大部分项目均会涉及到定时。然而,计数或许用得会比较少。那么今天小编就给大伙讲讲如何配置使用Nordic 52840的定时器以及计数器。

开发环境 操作系统Windows10 SDKnRF5_SDK_15.2.0_9412b96 IDESEGGER Embedded Studio for ARM 4.12 Source code

Basic tutorial—hardware timer

原理

小编这个人有个习惯,面对新的物种或者技术我均会先去了解下原理。最终才会去写代码。我个人认为写代码是最不费时间的,最费时间的是写代码前期的了解以及思路构思。如果前期准备的不好,那务必会导致你把大部分时间花在了写bug或者在改bug的路上。既然要说原理,那么最靠谱的资料当然是规格书了,先让我们看看Nordic 52840的硬件定时器的diagram.

从上图中可以看出,硬件TIMER的时钟来自于HCLK。因此,如果想要做低功耗的话就直接放弃吧,因为在低功耗模式下,HCLK是要被关闭的。从这个框图看,硬件定时器的结构还是比较简单清晰的。

时钟

PCLK1M

当fTIMER小于等于1Mz时,就选择这个时钟,应用层不用去处理它,这个操作是自动进行的

PLCK16M

当fTIMER大于1Mhz时,就会选择这个时钟,同样也是自动选择的,应用层只需要关注fTIMER即可。

从这里我们可以知道,硬件定时器的时钟最大就是16Mhz。

Task&&Event

这里一共有5种任务以及1种事件,具体如下所示:

COUNTSTOPSTARTCLEARCAPTURECOMPARE(事件)

从命名我们就很清楚地知道它们的含义分别为计数、停止、开始、清零、捕捉、比较,而且我们从箭头的方向也知道,输入的是任务输出的是事件。

从以上我们分析的结果可知,要想Nordic 52840的硬件定时器工作起来,就必须要使能HCLK->选择工作模式->输入COUT、START、STOP、CLEAR、CAPTURE、COMPARE中的任意一个信号

如何使用

Nordic 52840的定时器有两个功能,一个是定时器另外一个是计数器。总共有5个TIMER,分别为TIMER0-TIMER4。 有一点还是要强调一下的,不管是定时器还是计数器它们是共用这5个TIMER的。 例如TIMER0已经做为定时器了,那么此时计数器就只能从TIMER1~TIMER4中选择一个了。

定时器

通过上面的大体原理讲解,我想大家应该有个相对的了解吧,接下来我们看看如何初始化定时器。废话不多说,直接上源码?。

/** * 初始化timer0,用于定时 * @param[in] NULL * @retval NULL * @par 修改日志 * Ver0.0.1: Helon_Chan, 2018/10/23, 初始化版本\n */ static void user_app_timer0_init(void) { nrfx_err_t err_code; uint32_t timer_ms_tick; /* 使用NRFX_TIMER_DEFAULT_CONFIG配置hardware timer0为默认的配置参数 */ nrf_drv_timer_config_t m_hardware_timer0_config = NRF_DRV_TIMER_DEFAULT_CONFIG; /* 初始化hardware timer0 */ err_code = nrf_drv_timer_init(&hardware_timer0, &m_hardware_timer0_config, user_nrf_timer_evt_handler); NRF_LOG_INFO("nrf_drv_timer0_init is %d\n", err_code); /* 1.设置hardware timer0在NRF_TIMER_CC_CHANNEL0通道每一秒产生event,并触发clear task 2.每一秒就回调一次用户自定义的回调处理函数user_nrf_timer_evt_handler,当前false不调用 */ timer_ms_tick = nrf_drv_timer_ms_to_ticks(&hardware_timer0, 1000); NRF_LOG_INFO("nrf_drv_timer_ms_to_ticks is %d\n", timer_ms_tick); nrf_drv_timer_extended_compare(&hardware_timer0, NRF_TIMER_CC_CHANNEL0, timer_ms_tick, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false); /* 使能hardware timer0 */ nrf_drv_timer_enable(&hardware_timer0); }

更多的就不再细述了,基本上每一行代码我都有有注释了。其中m_hardware_timer0_config可以在sdk_config.h头文件中配置,至于配置的方法此次不再详述。感兴趣的可以查看如何搭建Nordic 52840开发环境->如何使能GUI配置界面

计数器

其实计数器,我觉得用的人会比较少。因为多数应该会定义一个变量来存放计数,但是在一些特殊的场合还是需要用到计数器的,例如需要一些计量的应用场景。

/** * 初始化timer1,用于计数 * @param[in] NULL * @retval NULL * @par 修改日志 * Ver0.0.1: Helon_Chan, 2018/10/23, 初始化版本\n */ static void user_app_timer1_init(void) { nrfx_err_t err_code; /* 设置hardware timer1的长度为32bit的计数器 */ nrf_drv_timer_config_t m_hardware_timer1_config = NRF_DRV_TIMER_DEFAULT_CONFIG; m_hardware_timer1_config.mode = NRF_TIMER_MODE_COUNTER; m_hardware_timer1_config.bit_width = NRF_TIMER_BIT_WIDTH_32; /* 初始化hardware timer1 */ err_code = nrf_drv_timer_init(&hardware_timer1, &m_hardware_timer1_config, user_nrf_timer_evt_handler); NRF_LOG_INFO("nrf_drv_timer1_init is %d\n", err_code); /* 使能hardware timer1*/ nrf_drv_timer_enable(&hardware_timer1); } PPI串联

上面讲到的是定时器以及计数器的初始化过程,现在为了让定时器和计数器真正应用起来,小编使用了TIMER0用于定时开关LED灯,TIMER1用于按揵的按下的计数。但是,在实现这个功能之前,我们需要用到PPI,将这些串联起来方可完成这个功能。

/** * 初始化gpiote * @param[in] NULL * @retval NULL * @par 修改日志 * Ver0.0.1: Helon_Chan, 2018/10/23, 初始化版本\n */ static void user_app_gpiote_init(void) { nrfx_err_t err_code; nrf_drv_gpiote_in_config_t m_gpiote_in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true); nrf_drv_gpiote_out_config_t m_gpiote_out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true); /* 初始化gpiote */ err_code = nrf_drv_gpiote_init(); NRF_LOG_INFO("nrf_drv_gpiote_init is %d\n", err_code); /* 设置按键对应的GPIO口由高电平变低电平时,产生ENVENT */ err_code = nrf_drv_gpiote_in_init(KEY_NUMBER, &m_gpiote_in_config, user_nrf_drv_gpiote_evt_handler); NRF_LOG_INFO("nrf_drv_gpiote_in_init is %d\n", err_code); /* 设置LED灯对应的GPIO口被hardware timer0 compare event控制,且每次有EVENT触发GPIO OUT TASK时,取反当前的LED灯 */ err_code = nrf_drv_gpiote_out_init(LED_NUMBER,&m_gpiote_out_config); NRF_LOG_INFO("nrfx_gpiote_out_init is %d\n",err_code); /* 使能gpiote的in event,并触发user_nrf_drv_gpiote_evt_handler回调函数 */ nrf_drv_gpiote_in_event_enable(KEY_NUMBER, true); /* 使能gpiote的out task */ nrf_drv_gpiote_out_task_enable(LED_NUMBER); }

以上的代码,就是将与LED连接的GPIO设置成GPIO的Task,将按键相连的GPIO设置为GPIO的Event。

/** * 初始化ppi * @param[in] NULL * @retval NULL * @par 修改日志 * Ver0.0.1: Helon_Chan, 2018/10/23, 初始化版本\n */ static void user_app_ppi_init(void) { nrfx_err_t err_code; nrf_ppi_channel_t m_nrf_ppi_channel1,m_nrf_ppi_channel2; uint32_t gpiote_evt_addr,gpiote_task_addr; uint32_t hardware_timer1_capture_task_addr,hardware_timer0_compare_evt_addr; /* 初始化ppi */ NRF_LOG_INFO("nrf_drv_ppi_init is %d\n", nrf_drv_ppi_init()); /* 选择未使用的PPI通道 */ err_code = nrf_drv_ppi_channel_alloc(&m_nrf_ppi_channel1); NRF_LOG_INFO("nrf_drv_ppi_channel1_alloc is %d\n", err_code); /* 选择未使用的PPI通道 */ err_code = nrf_drv_ppi_channel_alloc(&m_nrf_ppi_channel2); NRF_LOG_INFO("nrf_drv_ppi_channel2_alloc is %d\n", err_code); /* 获取event和task的地址 */ gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(LED_NUMBER); hardware_timer0_compare_evt_addr = nrf_drv_timer_compare_event_address_get(&hardware_timer0, NRF_TIMER_CC_CHANNEL0); gpiote_evt_addr = nrf_drv_gpiote_in_event_addr_get(KEY_NUMBER); hardware_timer1_capture_task_addr = nrf_drv_timer_task_address_get(&hardware_timer1, NRF_TIMER_TASK_COUNT); /* 将event和task通过选中的ppi_channel连接起来 */ err_code = nrf_drv_ppi_channel_assign(m_nrf_ppi_channel1, hardware_timer0_compare_evt_addr, gpiote_task_addr); NRF_LOG_INFO("nrf_drv_ppi_channel1_assign is %d\n", err_code); err_code = nrf_drv_ppi_channel_assign(m_nrf_ppi_channel2, gpiote_evt_addr, hardware_timer1_capture_task_addr); NRF_LOG_INFO("nrf_drv_ppi_channel2_assign is %d\n", err_code); /* 使能选中的PPI的通道 */ err_code = nrf_drv_ppi_channel_enable(m_nrf_ppi_channel1); NRF_LOG_INFO("nrf_drv_ppi_channel1_enable is %d\n", err_code); err_code = nrf_drv_ppi_channel_enable(m_nrf_ppi_channel2); NRF_LOG_INFO("nrf_drv_ppi_channel2_enable is %d\n", err_code); }

以上代码就是把TIMER0的Event与GPIO的Task通过PPI通道连接在一起,把GPIO的Event与TIMER1的Task同样通过通道连接在一起。如初始化的过程中所讲,TIMER0每秒就会产生一个Event,所以与其相连接的Task就会随之执行一次。同理,与按键相接的GPIO每产生一次Event就会自动触发TIMER1的Task用于计数。

完整的工程实现

其中上面的几个章节,基本上把使用的功能都讲到了,下面只要将这些拼装起来即可。

/** * 初始化使用到的外设 * @param[in] NULL * @retval NULL * @par 修改日志 * Ver0.0.1: Helon_Chan, 2018/10/23, 初始化版本\n */ void user_app_init(void) { user_app_gpiote_init(); user_app_timer0_init(); user_app_timer1_init(); user_app_ppi_init(); } /** * @file main.c * @brief 用户应用程序入口 * @details 用户应用程序的入口文件,用户所有要实现的功能逻辑均是从该文件开始或者处理 * @author Helon_Chan * @par Copyright (c): * 红旭无线开发团队 * @par History: * Ver0.0.1: Helon_Chan, 2018/06/09, 初始化版本\n */ int main(void) { /* log模块初始化 */ log_init(); user_app_init(); NRF_LOG_INFO("/******************************************************************************/\n"); NRF_LOG_INFO(" Welcome to wireless-tech hardware timer demo project \n"); NRF_LOG_INFO(" website :bbs.wireless-tech.cn \n"); NRF_LOG_INFO(" QQ Group:671139854 \n"); NRF_LOG_INFO("/******************************************************************************/\n"); } 最后

至此,Nordic 52840的硬件定时器以及计数器就讲完了,基本上还是比较简单的,Nordic已经将大部分的库都完成了,我们唯一要做的就是在了解原理的情况下,如何使用现在的API。

本文原创,转载请注明出处



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3