nrf52840藍牙學習(定時器的應用)

和其他 MCU 處理器一樣,在 nrf52840 中定時器的功能是十分強大的。其內部包含了 5 個定時
TIMER 模塊 :TIMER0 TIMER1 TIMER2 TIMER3 TIMER4 ,如下表 10.1 所示。

?

1. 時鐘源
首先定時器 TIMER 工作在高頻時鐘源( HFLCK )下,同時包含了一個 4bit 1 / 2X )的分頻

?

出。
計數模式下,每次觸發 COUNT 任務時, TIMER 的內部計數器 Counter 寄存器都會遞增 1 ,同
時,計數器模式下是不使用定時器的頻率和預分頻器, COUNT 任務在定時器模式下無效。通過設
定一個 CAPTURE Task ,捕獲的計數器的值存儲到 CC[n] 寄存器內,然后對 CC[n] 寄存器進行讀取計
數的值。
5. 任務延遲和優先級
任務延遲: TIMER 啟動后, CLEAR 任務, COUNT 任務和 STOP 任務將保證在 PCLK16M 的一
個時鐘周期內生效。
任務優先級:如果同時觸發 START 任務和 STOP 任務,即在 PCLK16M 的同一時段內,則優先
執行 STOP 任務。
下表是定時器的寄存器列表,詳細說明如下 10.5 表示所示:

?

定時器首先需要設置的三個參數,分別為:定時器的模式、定時器的位寬、定時器的時鐘頻率。
本例中需要進行定時操作,因此還需要設置比較寄存器里的值。如果初始化完成后,定時器就開始
定時,當定時時間的值跟 CC[n] 寄存器的值相等時,將觸發一個 COMPARE [n] event ,這時候我們
關掉定時器定時。這樣根據 CC[n] 寄存器的值就是實現了一個指定的時間長度的。

文件一:

/******************** (C) COPYRIGHT 2023 青風電子 ********************* 文件名  :main* 出品論壇 :www.qfv8.com        * 實驗平臺:青云nRF52840藍牙開發板* 描述    :定時器定時* 作者    :青風* 店鋪    :qfv5.taobao.com
**********************************************************************/#include <stdbool.h>
#include <stdint.h>
//#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "led.h"
#include  "time.h"int main(void)
{// LED_Init();while (1){LED1_Toggle();//使用定時器0產生1s定時nrf_timer_delay_ms(TIMER0, TIMER_DELAY_MS);LED2_Toggle();// 使用定時器1產生1s定時nrf_timer_delay_ms(TIMER1, TIMER_DELAY_MS);LED3_Toggle();// 使用定時器2產生1s定時nrf_timer_delay_ms(TIMER2, TIMER_DELAY_MS);} 
}

?文件二:

#ifndef __LED_H
#define	__LED_H#include "nrf52840.h"#define LED_0          NRF_GPIO_PIN_MAP(0,13)
#define LED_1          NRF_GPIO_PIN_MAP(0,14)
#define LED_2          NRF_GPIO_PIN_MAP(0,15)
#define LED_3          NRF_GPIO_PIN_MAP(0,16)void LED_Init(void);
void LED1_Open(void);
void LED1_Close(void);
void LED1_Toggle(void);
void LED2_Open(void);
void LED2_Close(void);
void LED2_Toggle(void);
void LED3_Open(void);
void LED3_Close(void);
void LED3_Toggle(void);#endif /* __LED_H */

文件 三:

#include "nrf52840.h"
#include "led.h"
#include "nrf_gpio.h"
#include "time.h"
#include <stdbool.h>
#include <stdint.h>/*** @brief Function for timer initialization.*/
static volatile NRF_TIMER_Type * timer_init(timer_t timer)
{volatile NRF_TIMER_Type * p_timer;// 開始16 MHz晶振.NRF_CLOCK->EVENTS_HFCLKSTARTED  = 0;NRF_CLOCK->TASKS_HFCLKSTART     = 1;// 等待外部振蕩器啟動while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {// Do nothing.}switch (timer){case TIMER0:p_timer = NRF_TIMER0;break;case TIMER1:p_timer = NRF_TIMER1;break;case TIMER2:p_timer = NRF_TIMER2;break;default:p_timer = 0;break;}return p_timer;
}/** @brief Function for using the peripheral hardware timers to generate an event after requested number of milliseconds.** @param[in] timer Timer to be used for delay, values from @ref p_timer* @param[in] number_of_ms Number of milliseconds the timer will count.* @note This function will power ON the requested timer, wait until the delay, and then power OFF that timer.*/void nrf_timer_delay_ms(timer_t timer, uint_fast16_t volatile number_of_ms)
{volatile NRF_TIMER_Type * p_timer = timer_init(timer);if (p_timer == 0) {while(true) {// Do nothing.}}p_timer->MODE           = TIMER_MODE_MODE_Timer;        // 設置為定時器模式p_timer->PRESCALER      = 10;                            // Prescaler 9 produces 31250 Hz timer frequency => 1 tick = 32 us.p_timer->BITMODE        = TIMER_BITMODE_BITMODE_16Bit;  // 16 bit 模式.p_timer->TASKS_CLEAR    = 1;                            // 清定時器.// With 32 us ticks, we need to multiply by 31.25 to get milliseconds.p_timer->CC[0]          = number_of_ms * 31;p_timer->CC[0]         += number_of_ms / 4; p_timer->TASKS_START    = 1;                    // Start timer.while (p_timer->EVENTS_COMPARE[0] == 0){// Do nothing.}p_timer->EVENTS_COMPARE[0]  = 0;p_timer->TASKS_STOP         = 1;                // Stop timer.
}
/** @} */

?文件四:

#include "nrf52840.h"
#include "nrf_gpio.h"
#include "led.h"void LED_Init(void)
{// Configure LED-pins as outputsnrf_gpio_cfg_output(LED_0);nrf_gpio_cfg_output(LED_1);nrf_gpio_cfg_output(LED_2);
}void LED1_Open(void)
{nrf_gpio_pin_clear(LED_0);}void LED1_Close(void)
{nrf_gpio_pin_set(LED_0);}
void LED1_Toggle(void)
{nrf_gpio_pin_toggle(LED_0);
}void LED2_Open(void)
{nrf_gpio_pin_clear(LED_1);}void LED2_Close(void)
{nrf_gpio_pin_set(LED_1);}
void LED2_Toggle(void)
{nrf_gpio_pin_toggle(LED_1);
}void LED3_Open(void)
{nrf_gpio_pin_clear(LED_2);}void LED3_Close(void)
{nrf_gpio_pin_set(LED_2);}
void LED3_Toggle(void)
{nrf_gpio_pin_toggle(LED_2);
}


第一節:?nrf_timer_delay_ms函數

void nrf_timer_delay_ms(timer_t timer, uint_fast16_t volatile number_of_ms)
{volatile NRF_TIMER_Type * p_timer = timer_init(timer);if (p_timer == 0) {while(true) {// Do nothing.}}p_timer->MODE           = TIMER_MODE_MODE_Timer;        // 設置為定時器模式p_timer->PRESCALER      = 10;                            // Prescaler 9 produces 31250 Hz timer frequency => 1 tick = 32 us.p_timer->BITMODE        = TIMER_BITMODE_BITMODE_16Bit;  // 16 bit 模式.p_timer->TASKS_CLEAR    = 1;                            // 清定時器.// With 32 us ticks, we need to multiply by 31.25 to get milliseconds.p_timer->CC[0]          = number_of_ms * 31;p_timer->CC[0]         += number_of_ms / 4; p_timer->TASKS_START    = 1;                    // Start timer.while (p_timer->EVENTS_COMPARE[0] == 0){// Do nothing.}p_timer->EVENTS_COMPARE[0]  = 0;p_timer->TASKS_STOP         = 1;                // Stop timer.
}

這段代碼實現了基于 nRF 系列 MCU 定時器的毫秒級延時功能,下面從多個維度進行詳細解析:

函數接口與參數

void nrf_timer_delay_ms(timer_t timer, uint_fast16_t volatile number_of_ms);

  • timer:指定使用的定時器編號(如 TIMER0~TIMER4)
  • number_of_ms:延時毫秒數,使用uint_fast16_t確保最快處理速度
  • volatile 修飾:告知編譯器不優化該變量,確保每次都從內存讀取

定時器初始化與配置

volatile NRF_TIMER_Type * p_timer = timer_init(timer);
if (p_timer == 0) { while(1); } // 初始化失敗則進入死循環

  • timer_init():底層初始化函數,返回定時器寄存器基址
  • 錯誤處理:初始化失敗時進入無限循環,防止后續代碼異常

定時器核心配置

p_timer->MODE = TIMER_MODE_MODE_Timer;      // 定時器模式
p_timer->PRESCALER = 10;                    // 預分頻=1024
p_timer->BITMODE = TIMER_BITMODE_BITMODE_16Bit; // 16位計數
p_timer->TASKS_CLEAR = 1;                   // 清除定時器計數

  • 時鐘頻率:假設系統時鐘 16MHz,分頻后為 15625Hz (64μs/tick)
  • 計數范圍:0~65535,最大延時約 4.2 秒

延時計算原理與誤差分析

p_timer->CC[0] = number_of_ms * 31;
p_timer->CC[0] += number_of_ms / 4; 

  • 計算公式分解
    1. 理論上 1ms 對應 15.625 個 tick (15625Hz)
    2. 代碼實現:CC [0] = ms×31 + ms/4 ≈ ms×31.25
    3. 實際換算:31.25×64μs = 2000μs = 2ms
  • 誤差結論:實際延時是設定值的 2 倍,屬于代碼實現錯誤

正確計算方式

// 正確換算公式
uint32_t ticks = number_of_ms * 15625 / 1000;
if (ticks > 65535) ticks = 65535; // 防止溢出
p_timer->CC[0] = ticks;

  • 原理:將毫秒轉換為定時器 tick 數
  • 溢出處理:限制最大延時時間不超過 4.2 秒

延時實現機制

p_timer->TASKS_START = 1;               // 啟動定時器
while (p_timer->EVENTS_COMPARE[0] == 0) // 等待比較事件
{ /* 忙等待 */ }
p_timer->EVENTS_COMPARE[0] = 0;         // 清除事件標志
p_timer->TASKS_STOP = 1;                // 停止定時器

  • 同步機制:基于忙等待的阻塞式延時
  • 事件處理:比較事件觸發后清除標志位
  • 資源管理:延時結束后停止定時器,降低功耗

性能與優化建議

  1. 時間精度

    • 理論精度:±1 個 tick (64μs)
    • 實際誤差:約 100%(因計算錯誤導致)
  2. 優化方向

    // 低功耗優化版本
    while (!p_timer->EVENTS_COMPARE[0]) {__WFI(); // 進入等待中斷模式,CPU休眠
    }
    
  3. 功能增強

    // 非阻塞式延時接口
    bool nrf_timer_delay_start(timer_t timer, uint16_t ms);
    bool nrf_timer_delay_check(timer_t timer);
    

潛在問題與修復

  1. 計算錯誤:修正 CC [0] 計算公式
  2. 溢出風險:添加輸入參數驗證
  3. 資源沖突:多任務環境需要互斥訪問
  4. 功耗問題:長時間延時建議使用 RTC 替代

修正后的完整代碼示例:

void nrf_timer_delay_ms(timer_t timer, uint16_t ms)
{NRF_TIMER_Type * p_timer = timer_init(timer);if (!p_timer) while(1); // 錯誤處理// 配置定時器p_timer->MODE = TIMER_MODE_MODE_Timer;p_timer->PRESCALER = 10; // 15625Hzp_timer->BITMODE = TIMER_BITMODE_BITMODE_16Bit;p_timer->TASKS_CLEAR = 1;// 正確計算tick數uint32_t ticks = ms * 15625 / 1000;if (ticks > 65535) ticks = 65535;p_timer->CC[0] = ticks;// 啟動并等待p_timer->TASKS_START = 1;while (!p_timer->EVENTS_COMPARE[0]) {__SEV(); // 維持事件信號__WFE(); // 等待事件}// 清理資源p_timer->EVENTS_COMPARE[0] = 0;p_timer->TASKS_STOP = 1;
}

這種基于硬件定時器的延時方案比軟件循環延時更精確,適用于對時間精度要求較高的場合,如通信協議時序控制、PWM 信號生成等。



?NRF_TIMER_Type結構體

這個結構體定義了 Nordic nRF 系列微控制器的定時器外設寄存器映射。下面是對該結構體的詳細解析:

寄存器組織概述

typedef struct {__OM  uint32_t  TASKS_START;                  // 啟動定時器任務__OM  uint32_t  TASKS_STOP;                   // 停止定時器任務__OM  uint32_t  TASKS_COUNT;                  // 計數遞增任務(計數器模式)__OM  uint32_t  TASKS_CLEAR;                  // 清除定時器任務__OM  uint32_t  TASKS_SHUTDOWN;               // 停用定時器任務(已棄用)__IM  uint32_t  RESERVED[11];__OM  uint32_t  TASKS_CAPTURE[6];             // 捕獲當前計數值到CC[n]__IM  uint32_t  RESERVED1[58];__IOM uint32_t  EVENTS_COMPARE[6];            // 比較事件寄存器組__IM  uint32_t  RESERVED2[42];__IOM uint32_t  SHORTS;                       // 短連接配置寄存器__IM  uint32_t  RESERVED3[64];__IOM uint32_t  INTENSET;                     // 中斷使能寄存器__IOM uint32_t  INTENCLR;                     // 中斷禁用寄存器__IM  uint32_t  RESERVED4[126];__IOM uint32_t  MODE;                         // 定時器模式選擇__IOM uint32_t  BITMODE;                      // 位寬配置__IM  uint32_t  RESERVED5;__IOM uint32_t  PRESCALER;                    // 預分頻器配置__IM  uint32_t  RESERVED6[11];__IOM uint32_t  CC[6];                        // 捕獲/比較寄存器組
} NRF_TIMER_Type;

核心功能模塊詳解

  1. 任務寄存器組 (Tasks)

    • TASKS_START: 啟動定時器計數
    • TASKS_STOP: 停止定時器計數
    • TASKS_CLEAR: 清零當前計數值
    • TASKS_CAPTURE[n]: 將當前計數值捕獲到 CC [n] 寄存器
  2. 事件寄存器組 (Events)

    • EVENTS_COMPARE[n]: 當計數值與 CC [n] 匹配時觸發事件
    • 可用于實現 PWM、定時中斷等功能
  3. 配置寄存器

    • MODE: 選擇工作模式 (定時器 / 計數器)
    • BITMODE: 配置計數位寬 (8/16/24/32 位)
    • PRESCALER: 配置預分頻值 (2^n,n=0~9)
  4. 捕獲 / 比較寄存器組

    • CC[0-5]: 6 個 16/24/32 位寄存器
    • 支持捕獲當前計數值或設置比較值
    • 用于 PWM 生成、定時觸發等功能
  5. 中斷控制

    • INTENSET: 使能特定中斷
    • INTENCLR: 禁用特定中斷
    • 支持比較事件中斷、溢出中斷等
  6. 短連接功能

    • SHORTS: 配置事件與任務之間的直接連接
    • 例如:比較事件發生時自動清除計數器

典型應用場景

  1. 延時功能實現

    // 基于CC[0]比較事件的延時函數
    p_timer->CC[0] = current_value + delay_ticks;
    p_timer->TASKS_START = 1;
    while(p_timer->EVENTS_COMPARE[0] == 0); // 等待比較事件
    p_timer->EVENTS_COMPARE[0] = 0; // 清除事件標志
    
  2. PWM 信號生成

    • 使用 SHORTS 自動實現周期循環
    // 配置PWM周期和占空比
    p_timer->CC[0] = period;        // 周期值
    p_timer->CC[1] = duty_cycle;    // 占空比值
    p_timer->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk; // 比較事件0觸發計數器清零
    
  3. 輸入捕獲功能

    // 捕獲外部信號的周期
    p_timer->TASKS_CAPTURE[0] = 1;  // 手動觸發一次捕獲
    // 當外部事件發生時,可通過SHORTS自動觸發捕獲
    p_timer->SHORTS = TIMER_SHORTS_COMPARE1_CAPTURE0_Msk;
    

設計亮點分析

  1. 任務 - 事件架構

    • 任務驅動操作,避免直接寫寄存器
    • 事件標志統一管理,簡化狀態檢測
  2. 靈活的預分頻配置

    • 支持 2^0~2^9 預分頻
    • 適配不同時鐘頻率和應用場景
  3. 多通道捕獲 / 比較

    • 6 個獨立 CC 通道
    • 可同時實現多個 PWM 輸出或定時功能
  4. 短連接機制

    • 硬件級事件 - 任務連接
    • 減少 CPU 干預,提高實時性

這種寄存器映射方式充分體現了 Nordic MCU 定時器的靈活性和高性能,為嵌入式系統提供了強大的定時、計數和 PWM 控制能力。



?NRF_CLOCK_Type結構體

typedef struct {__OM  uint32_t  TASKS_HFCLKSTART;    // 啟動高頻晶體振蕩器__OM  uint32_t  TASKS_HFCLKSTOP;     // 停止高頻晶體振蕩器__OM  uint32_t  TASKS_LFCLKSTART;    // 啟動低頻時鐘__OM  uint32_t  TASKS_LFCLKSTOP;     // 停止低頻時鐘__OM  uint32_t  TASKS_CAL;           // 啟動LFRC校準__OM  uint32_t  TASKS_CTSTART;       // 啟動校準定時器__OM  uint32_t  TASKS_CTSTOP;        // 停止校準定時器__IM  uint32_t  RESERVED[57];__IOM uint32_t  EVENTS_HFCLKSTARTED; // 高頻時鐘啟動完成事件__IOM uint32_t  EVENTS_LFCLKSTARTED; // 低頻時鐘啟動完成事件__IM  uint32_t  RESERVED1;__IOM uint32_t  EVENTS_DONE;         // LFRC校準完成事件__IOM uint32_t  EVENTS_CTTO;         // 校準定時器超時事件__IM  uint32_t  RESERVED2[5];__IOM uint32_t  EVENTS_CTSTARTED;    // 校準定時器啟動事件__IOM uint32_t  EVENTS_CTSTOPPED;    // 校準定時器停止事件__IM  uint32_t  RESERVED3[117];__IOM uint32_t  INTENSET;            // 中斷使能寄存器__IOM uint32_t  INTENCLR;            // 中斷禁用寄存器__IM  uint32_t  RESERVED4[63];__IM  uint32_t  HFCLKRUN;            // 高頻時鐘運行狀態__IM  uint32_t  HFCLKSTAT;           // 高頻時鐘狀態__IM  uint32_t  RESERVED5;__IM  uint32_t  LFCLKRUN;            // 低頻時鐘運行狀態__IM  uint32_t  LFCLKSTAT;           // 低頻時鐘狀態__IM  uint32_t  LFCLKSRCCOPY;        // 低頻時鐘源復制寄存器__IM  uint32_t  RESERVED6[62];__IOM uint32_t  LFCLKSRC;            // 低頻時鐘源選擇__IM  uint32_t  RESERVED7[3];__IOM uint32_t  HFXODEBOUNCE;        // 高頻晶體去抖時間配置__IM  uint32_t  RESERVED8[3];__IOM uint32_t  CTIV;                // 校準定時器間隔配置__IM  uint32_t  RESERVED9[8];__IOM uint32_t  TRACECONFIG;         // 跟蹤端口時鐘配置__IM  uint32_t  RESERVED10[21];__IOM uint32_t  LFRCMODE;            // LFRC模式配置
} NRF_CLOCK_Type;

1. 時鐘控制任務寄存器(Tasks)
  • 高頻時鐘控制
    • TASKS_HFCLKSTART:啟動 HFXO(高頻晶體振蕩器),通常用于需要高精度時鐘的場景(如射頻通信)。
    • TASKS_HFCLKSTOP:停止 HFXO 以降低功耗。
  • 低頻時鐘控制
    • TASKS_LFCLKSTART:啟動低頻時鐘(可選擇 LFRC 或 LFXO),用于低功耗定時器(如 RTC)。
    • TASKS_LFCLKSTOP:停止低頻時鐘。
  • 校準任務
    • TASKS_CAL:觸發 LFRC(低頻 RC 振蕩器)校準,提高時鐘精度。
    • TASKS_CTSTART/CTSTOP:控制校準定時器的啟停,用于 LFRC 校準過程的時間基準。
2. 時鐘事件寄存器(Events)
  • 狀態事件
    • EVENTS_HFCLKSTARTED:HFXO 啟動完成標志(需軟件清零)。
    • EVENTS_LFCLKSTARTED:LFCLK 啟動完成標志。
  • 校準事件
    • EVENTS_DONE:LFRC 校準完成標志。
    • EVENTS_CTTO:校準定時器超時標志,用于校準失敗處理。
3. 時鐘狀態寄存器
  • 高頻時鐘狀態
    • HFCLKRUN:只讀,指示 HFXO 是否已啟動(由 TASKS_HFCLKSTART 觸發)。
    • HFCLKSTAT:包含 HFXO 的穩定狀態等信息。
  • 低頻時鐘狀態
    • LFCLKRUN:指示 LFCLK 是否運行。
    • LFCLKSTAT:包含 LFCLK 的當前源(LFRC/LFXO)及穩定狀態。
    • LFCLKSRCCOPY:記錄 LFCLK 啟動時的時鐘源配置。
4. 時鐘配置寄存器
  • 低頻時鐘源選擇
    • LFCLKSRC:配置 LFCLK 的時鐘源(0=LFRC,1=LFXO,2 = 外部 32.768kHz)。
  • 高頻晶體去抖
    • HFXODEBOUNCE:設置 HFXO 啟動時的去抖時間,防止噪聲干擾。
  • 校準定時器配置
    • CTIV:設置校準定時器的時間間隔,用于 LFRC 校準過程。
  • LFRC 模式配置
    • LFRCMODE:配置 LFRC 的工作模式(如低功耗模式或高精度模式)。
5. 中斷控制寄存器
  • INTENSET/INTENCLR:用于使能或禁用時鐘相關事件的中斷(如 HFXO 啟動完成、LFRC 校準完成)。

典型應用場景

1. 高頻時鐘啟動流程
// 啟動HFXO并等待穩定
NRF_CLOCK->TASKS_HFCLKSTART = 1;         // 觸發啟動任務
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0); // 等待啟動完成
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;       // 清除事件標志
2. 低頻時鐘配置(以 LFXO 為例)
// 配置LFCLK為LFXO源并啟動
NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_LFXO; // 設置時鐘源
NRF_CLOCK->TASKS_LFCLKSTART = 1;              // 啟動LFCLK
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0);  // 等待啟動完成
3. LFRC 校準流程
// 校準LFRC以提高精度
NRF_CLOCK->CTIV = 100;                        // 設置校準定時器間隔
NRF_CLOCK->TASKS_CAL = 1;                      // 啟動校準
while (NRF_CLOCK->EVENTS_DONE == 0);          // 等待校準完成
uint32_t cal_value = NRF_CLOCK->LFCLKCAL;      // 獲取校準值(假設結構體包含此寄存器)

設計亮點分析

  1. 低功耗時鐘管理
    • 獨立控制高頻 / 低頻時鐘的啟停,支持精細化功耗優化(如僅在需要時啟動 HFXO)。
  2. 時鐘源靈活切換
    • LFCLKSRC 支持多種低頻時鐘源,平衡精度與功耗(LFXO 精度高但功耗大,LFRC 功耗低但精度低)。
  3. 自動校準機制
    • 通過 TASKS_CAL 和校準定時器自動優化 LFRC 精度,減少外部晶振依賴。
  4. 硬件去抖設計
    • HFXODEBOUNCE 寄存器防止晶體振蕩器啟動時的抖動干擾,提高時鐘穩定性。

關鍵寄存器映射表

寄存器名稱地址偏移訪問權限功能描述
TASKS_HFCLKSTART0x0000只寫啟動高頻晶體振蕩器
EVENTS_HFCLKSTARTED0x0100讀寫高頻時鐘啟動完成事件
LFCLKSRC0x0518讀寫低頻時鐘源選擇
HFXODEBOUNCE0x0528讀寫高頻晶體去抖時間配置
LFRCMODE0x05B4讀寫LFRC 工作模式配置

該時鐘系統架構充分體現了嵌入式系統對功耗、精度和靈活性的平衡需求,尤其適合藍牙低功耗(BLE)等對時鐘管理要求嚴格的場景。



nrfx_time.c文件解讀

#include <nrfx.h>#if NRFX_CHECK(NRFX_TIMER_ENABLED)#if !(NRFX_CHECK(NRFX_TIMER0_ENABLED) || NRFX_CHECK(NRFX_TIMER1_ENABLED) || \NRFX_CHECK(NRFX_TIMER2_ENABLED) || NRFX_CHECK(NRFX_TIMER3_ENABLED) || \NRFX_CHECK(NRFX_TIMER4_ENABLED))
#error "No enabled TIMER instances. Check <nrfx_config.h>."
#endif#include <nrfx_timer.h>#define NRFX_LOG_MODULE TIMER
#include <nrfx_log.h>/**@brief Timer control block. */
typedef struct
{nrfx_timer_event_handler_t handler;void *                     context;nrfx_drv_state_t           state;
} timer_control_block_t;static timer_control_block_t m_cb[NRFX_TIMER_ENABLED_COUNT];nrfx_err_t nrfx_timer_init(nrfx_timer_t const * const  p_instance,nrfx_timer_config_t const * p_config,nrfx_timer_event_handler_t  timer_event_handler)
{timer_control_block_t * p_cb = &m_cb[p_instance->instance_id];
#ifdef SOFTDEVICE_PRESENTNRFX_ASSERT(p_instance->p_reg != NRF_TIMER0);
#endifNRFX_ASSERT(p_config);NRFX_ASSERT(timer_event_handler);nrfx_err_t err_code;if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED){err_code = NRFX_ERROR_INVALID_STATE;NRFX_LOG_WARNING("Function: %s, error code: %s.",__func__,NRFX_LOG_ERROR_STRING_GET(err_code));return err_code;}/* Warning 685: Relational operator '<=' always evaluates to 'true'"* Warning in NRF_TIMER_IS_BIT_WIDTH_VALID macro. Macro validate timers resolution.* Not necessary in nRF52 based systems. Obligatory in nRF51 based systems.*//*lint -save -e685 */NRFX_ASSERT(NRF_TIMER_IS_BIT_WIDTH_VALID(p_instance->p_reg, p_config->bit_width));//lint -restorep_cb->handler = timer_event_handler;p_cb->context = p_config->p_context;uint8_t i;for (i = 0; i < p_instance->cc_channel_count; ++i){nrf_timer_event_clear(p_instance->p_reg,nrf_timer_compare_event_get(i));}NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg),p_config->interrupt_priority);NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));nrf_timer_mode_set(p_instance->p_reg, p_config->mode);nrf_timer_bit_width_set(p_instance->p_reg, p_config->bit_width);nrf_timer_frequency_set(p_instance->p_reg, p_config->frequency);p_cb->state = NRFX_DRV_STATE_INITIALIZED;err_code = NRFX_SUCCESS;NRFX_LOG_INFO("Function: %s, error code: %s.",__func__,NRFX_LOG_ERROR_STRING_GET(err_code));return err_code;
}void nrfx_timer_uninit(nrfx_timer_t const * const p_instance)
{NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));#define DISABLE_ALL UINT32_MAXnrf_timer_shorts_disable(p_instance->p_reg, DISABLE_ALL);nrf_timer_int_disable(p_instance->p_reg, DISABLE_ALL);#undef DISABLE_ALLnrfx_timer_disable(p_instance);m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_UNINITIALIZED;NRFX_LOG_INFO("Uninitialized instance: %d.", p_instance->instance_id);
}void nrfx_timer_enable(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_INITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_POWERED_ON;NRFX_LOG_INFO("Enabled instance: %d.", p_instance->instance_id);
}void nrfx_timer_disable(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_SHUTDOWN);m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_INITIALIZED;NRFX_LOG_INFO("Disabled instance: %d.", p_instance->instance_id);
}bool nrfx_timer_is_enabled(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);return (m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_POWERED_ON);
}void nrfx_timer_resume(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);NRFX_LOG_INFO("Resumed instance: %d.", p_instance->instance_id);
}void nrfx_timer_pause(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_STOP);NRFX_LOG_INFO("Paused instance: %d.", p_instance->instance_id);
}void nrfx_timer_clear(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_CLEAR);
}void nrfx_timer_increment(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);NRFX_ASSERT(nrf_timer_mode_get(p_instance->p_reg) != NRF_TIMER_MODE_TIMER);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_COUNT);
}uint32_t nrfx_timer_capture(nrfx_timer_t const * const p_instance,nrf_timer_cc_channel_t     cc_channel)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);NRFX_ASSERT(cc_channel < p_instance->cc_channel_count);nrf_timer_task_trigger(p_instance->p_reg,nrf_timer_capture_task_get(cc_channel));return nrf_timer_cc_read(p_instance->p_reg, cc_channel);
}void nrfx_timer_compare(nrfx_timer_t const * const p_instance,nrf_timer_cc_channel_t     cc_channel,uint32_t                   cc_value,bool                       enable_int)
{nrf_timer_int_mask_t timer_int = nrf_timer_compare_int_get(cc_channel);if (enable_int){nrf_timer_event_clear(p_instance->p_reg, nrf_timer_compare_event_get(cc_channel));nrf_timer_int_enable(p_instance->p_reg, timer_int);}else{nrf_timer_int_disable(p_instance->p_reg, timer_int);}nrf_timer_cc_write(p_instance->p_reg, cc_channel, cc_value);NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",p_instance->instance_id,cc_value,cc_channel);
}void nrfx_timer_extended_compare(nrfx_timer_t const * const p_instance,nrf_timer_cc_channel_t     cc_channel,uint32_t                   cc_value,nrf_timer_short_mask_t     timer_short_mask,bool                       enable_int)
{nrf_timer_shorts_disable(p_instance->p_reg,(TIMER_SHORTS_COMPARE0_STOP_Msk  << cc_channel) |(TIMER_SHORTS_COMPARE0_CLEAR_Msk << cc_channel));nrf_timer_shorts_enable(p_instance->p_reg, timer_short_mask);nrfx_timer_compare(p_instance,cc_channel,cc_value,enable_int);NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",p_instance->instance_id,cc_value,cc_channel);
}void nrfx_timer_compare_int_enable(nrfx_timer_t const * const p_instance,uint32_t                   channel)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);NRFX_ASSERT(channel < p_instance->cc_channel_count);nrf_timer_event_clear(p_instance->p_reg,nrf_timer_compare_event_get(channel));nrf_timer_int_enable(p_instance->p_reg,nrf_timer_compare_int_get(channel));
}void nrfx_timer_compare_int_disable(nrfx_timer_t const * const p_instance,uint32_t                   channel)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);NRFX_ASSERT(channel < p_instance->cc_channel_count);nrf_timer_int_disable(p_instance->p_reg,nrf_timer_compare_int_get(channel));
}static void irq_handler(NRF_TIMER_Type        * p_reg,timer_control_block_t * p_cb,uint8_t                 channel_count)
{uint8_t i;for (i = 0; i < channel_count; ++i){nrf_timer_event_t event = nrf_timer_compare_event_get(i);nrf_timer_int_mask_t int_mask = nrf_timer_compare_int_get(i);if (nrf_timer_event_check(p_reg, event) &&nrf_timer_int_enable_check(p_reg, int_mask)){nrf_timer_event_clear(p_reg, event);NRFX_LOG_DEBUG("Compare event, channel: %d.", i);p_cb->handler(event, p_cb->context);}}
}#if NRFX_CHECK(NRFX_TIMER0_ENABLED)
void nrfx_timer_0_irq_handler(void)
{irq_handler(NRF_TIMER0, &m_cb[NRFX_TIMER0_INST_IDX],NRF_TIMER_CC_CHANNEL_COUNT(0));
}
#endif#if NRFX_CHECK(NRFX_TIMER1_ENABLED)
void nrfx_timer_1_irq_handler(void)
{irq_handler(NRF_TIMER1, &m_cb[NRFX_TIMER1_INST_IDX],NRF_TIMER_CC_CHANNEL_COUNT(1));
}
#endif#if NRFX_CHECK(NRFX_TIMER2_ENABLED)
void nrfx_timer_2_irq_handler(void)
{irq_handler(NRF_TIMER2, &m_cb[NRFX_TIMER2_INST_IDX],NRF_TIMER_CC_CHANNEL_COUNT(2));
}
#endif#if NRFX_CHECK(NRFX_TIMER3_ENABLED)
void nrfx_timer_3_irq_handler(void)
{irq_handler(NRF_TIMER3, &m_cb[NRFX_TIMER3_INST_IDX],NRF_TIMER_CC_CHANNEL_COUNT(3));
}
#endif#if NRFX_CHECK(NRFX_TIMER4_ENABLED)
void nrfx_timer_4_irq_handler(void)
{irq_handler(NRF_TIMER4, &m_cb[NRFX_TIMER4_INST_IDX],NRF_TIMER_CC_CHANNEL_COUNT(4));
}
#endif#endif // NRFX_CHECK(NRFX_TIMER_ENABLED)
版權和許可信息部分

/*** Copyright (c) 2015 - 2021, Nordic Semiconductor ASA** All rights reserved.** Redistribution and use in source and binary forms, with or without modification,* are permitted provided that the following conditions are met:** 1. Redistributions of source code must retain the above copyright notice, this*    list of conditions and the following disclaimer.*    ...(中間條件省略)...* 5. Any software provided in binary form under this license must not be reverse*    engineered, decompiled, modified and/or disassembled.** THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. *    ...(后續免責聲明省略)...*/

  • 這部分是標準的版權聲明和許可協議,規定了代碼的使用、修改和分發條件
  • 明確了 Nordic Semiconductor ASA 對代碼的版權所有
  • 列出了 5 個 redistribution 條件,包括保留版權聲明、限制二進制形式的使用等
  • 包含免責聲明,說明軟件按 "原樣" 提供,不提供任何明示或暗示的保證
頭文件和條件編譯部分

c

#include <nrfx.h>#if NRFX_CHECK(NRFX_TIMER_ENABLED)#if !(NRFX_CHECK(NRFX_TIMER0_ENABLED) || NRFX_TIMER1_ENABLED || \NRFX_CHECK(NRFX_TIMER2_ENABLED) || NRFX_CHECK(NRFX_TIMER3_ENABLED) || \NRFX_CHECK(NRFX_TIMER4_ENABLED))
#error "No enabled TIMER instances. Check <nrfx_config.h>."
#endif#include <nrfx_timer.h>#define NRFX_LOG_MODULE TIMER
#include <nrfx_log.h>

  • #include <nrfx.h>:包含 nRFx 框架的主要頭文件
  • #if NRFX_CHECK(NRFX_TIMER_ENABLED):檢查定時器功能是否在配置中啟用
  • 嵌套的條件編譯檢查是否有任何定時器實例 (NRFX_TIMER0 到 NRFX_TIMER4) 被啟用,若無則拋出編譯錯誤
  • #include <nrfx_timer.h>:包含定時器驅動的頭文件
  • #define NRFX_LOG_MODULE TIMER:定義日志模塊為 TIMER,用于 nrfx_log.h 的日志輸出
  • #include <nrfx_log.h>:包含 nRFx 日志模塊的頭文件
結構體定義和全局變量部分

c

/**@brief Timer control block. */
typedef struct
{nrfx_timer_event_handler_t handler;    // 定時器事件處理函數指針void *                     context;    // 上下文指針,傳遞給事件處理函數nrfx_drv_state_t           state;      // 驅動狀態,記錄定時器當前狀態
} timer_control_block_t;static timer_control_block_t m_cb[NRFX_TIMER_ENABLED_COUNT];

  • 定義了timer_control_block_t結構體,作為定時器的控制塊
    • handler:事件處理函數指針,用于處理定時器事件
    • context:上下文指針,通常用于傳遞用戶數據
    • state:定時器驅動狀態,可能的值包括未初始化、已初始化、已啟動等
  • m_cb數組:靜態全局數組,存儲每個定時器實例的控制塊,大小由啟用的定時器數量決定
nrfx_timer_init 函數實現

c

nrfx_err_t nrfx_timer_init(nrfx_timer_t const * const  p_instance,nrfx_timer_config_t const * p_config,nrfx_timer_event_handler_t  timer_event_handler)
{timer_control_block_t * p_cb = &m_cb[p_instance->instance_id];
#ifdef SOFTDEVICE_PRESENTNRFX_ASSERT(p_instance->p_reg != NRF_TIMER0);
#endifNRFX_ASSERT(p_config);NRFX_ASSERT(timer_event_handler);nrfx_err_t err_code;if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED){err_code = NRFX_ERROR_INVALID_STATE;NRFX_LOG_WARNING("Function: %s, error code: %s.",__func__,NRFX_LOG_ERROR_STRING_GET(err_code));return err_code;}/* Warning 685處理 *//*lint -save -e685 */NRFX_ASSERT(NRF_TIMER_IS_BIT_WIDTH_VALID(p_instance->p_reg, p_config->bit_width));//lint -restorep_cb->handler = timer_event_handler;p_cb->context = p_config->p_context;uint8_t i;for (i = 0; i < p_instance->cc_channel_count; ++i){nrf_timer_event_clear(p_instance->p_reg, nrf_timer_compare_event_get(i));}NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg), p_config->interrupt_priority);NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));nrf_timer_mode_set(p_instance->p_reg, p_config->mode);nrf_timer_bit_width_set(p_instance->p_reg, p_config->bit_width);nrf_timer_frequency_set(p_instance->p_reg, p_config->frequency);p_cb->state = NRFX_DRV_STATE_INITIALIZED;err_code = NRFX_SUCCESS;NRFX_LOG_INFO("Function: %s, error code: %s.",__func__,NRFX_LOG_ERROR_STRING_GET(err_code));return err_code;
}

  • 函數功能:初始化定時器實例
  • 參數:
    • p_instance:定時器實例指針,包含寄存器地址和通道數等信息
    • p_config:定時器配置指針,包含模式、位寬、頻率等配置
    • timer_event_handler:定時器事件處理函數
  • 實現步驟:
    1. 獲取對應實例的控制塊指針
    2. 在軟設備存在時,斷言定時器寄存器不是 NRF_TIMER0(可能與軟設備沖突)
    3. 斷言配置指針和事件處理函數不為空
    4. 檢查定時器狀態,若未初始化則繼續,否則返回錯誤
    5. 檢查位寬有效性(抑制 lint 警告)
    6. 保存事件處理函數和上下文
    7. 清除所有通道的比較事件
    8. 設置中斷優先級并啟用中斷
    9. 配置定時器模式、位寬和頻率

  1. 設置狀態為已初始化并返回成功
nrfx_timer_uninit 函數實現

c

void nrfx_timer_uninit(nrfx_timer_t const * const p_instance)
{NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));#define DISABLE_ALL UINT32_MAXnrf_timer_shorts_disable(p_instance->p_reg, DISABLE_ALL);nrf_timer_int_disable(p_instance->p_reg, DISABLE_ALL);#undef DISABLE_ALLnrfx_timer_disable(p_instance);m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_UNINITIALIZED;NRFX_LOG_INFO("Uninitialized instance: %d.", p_instance->instance_id);
}

  • 函數功能:反初始化定時器,釋放資源
  • 實現步驟:
    1. 禁用定時器中斷
    2. 定義宏 DISABLE_ALL 為 UINT32_MAX,禁用所有短接功能和中斷
    3. 調用 nrfx_timer_disable 函數禁用定時器
    4. 將控制塊狀態設置為未初始化
    5. 記錄日志
定時器控制函數(enable/disable/is_enabled 等)

c

void nrfx_timer_enable(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_INITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_POWERED_ON;NRFX_LOG_INFO("Enabled instance: %d.", p_instance->instance_id);
}void nrfx_timer_disable(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_SHUTDOWN);m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_INITIALIZED;NRFX_LOG_INFO("Disabled instance: %d.", p_instance->instance_id);
}bool nrfx_timer_is_enabled(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);return (m_cb[p_instance->instance_id].state == NRFX_DRV_STATE_POWERED_ON);
}void nrfx_timer_resume(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_START);NRFX_LOG_INFO("Resumed instance: %d.", p_instance->instance_id);
}void nrfx_timer_pause(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_STOP);NRFX_LOG_INFO("Paused instance: %d.", p_instance->instance_id);
}

  • 這些函數提供了定時器的基本控制功能:
    • nrfx_timer_enable:啟用定時器,觸發 START 任務,更新狀態
    • nrfx_timer_disable:禁用定時器,觸發 SHUTDOWN 任務,更新狀態
    • nrfx_timer_is_enabled:檢查定時器是否啟用,通過狀態判斷
    • nrfx_timer_resume:恢復定時器運行,觸發 START 任務
    • nrfx_timer_pause:暫停定時器,觸發 STOP 任務
定時器操作函數(clear/increment/capture 等)

c

void nrfx_timer_clear(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_CLEAR);
}void nrfx_timer_increment(nrfx_timer_t const * const p_instance)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);NRFX_ASSERT(nrf_timer_mode_get(p_instance->p_reg) != NRF_TIMER_MODE_TIMER);nrf_timer_task_trigger(p_instance->p_reg, NRF_TIMER_TASK_COUNT);
}uint32_t nrfx_timer_capture(nrfx_timer_t const * const p_instance,nrf_timer_cc_channel_t     cc_channel)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);NRFX_ASSERT(cc_channel < p_instance->cc_channel_count);nrf_timer_task_trigger(p_instance->p_reg, nrf_timer_capture_task_get(cc_channel));return nrf_timer_cc_read(p_instance->p_reg, cc_channel);
}

  • 這些函數提供了定時器的高級操作功能:
    • nrfx_timer_clear:清除定時器計數,觸發 CLEAR 任務
    • nrfx_timer_increment:手動增加定時器計數(要求非 TIMER 模式)
    • nrfx_timer_capture:捕獲指定通道的計數值,觸發捕獲任務并返回值
定時器比較功能函數

c

void nrfx_timer_compare(nrfx_timer_t const * const p_instance,nrf_timer_cc_channel_t     cc_channel,uint32_t                   cc_value,bool                       enable_int)
{nrf_timer_int_mask_t timer_int = nrf_timer_compare_int_get(cc_channel);if (enable_int){nrf_timer_event_clear(p_instance->p_reg, nrf_timer_compare_event_get(cc_channel));nrf_timer_int_enable(p_instance->p_reg, timer_int);}else{nrf_timer_int_disable(p_instance->p_reg, timer_int);}nrf_timer_cc_write(p_instance->p_reg, cc_channel, cc_value);NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",p_instance->instance_id,cc_value,cc_channel);
}void nrfx_timer_extended_compare(nrfx_timer_t const * const p_instance,nrf_timer_cc_channel_t     cc_channel,uint32_t                   cc_value,nrf_timer_short_mask_t     timer_short_mask,bool                       enable_int)
{nrf_timer_shorts_disable(p_instance->p_reg,(TIMER_SHORTS_COMPARE0_STOP_Msk  << cc_channel) |(TIMER_SHORTS_COMPARE0_CLEAR_Msk << cc_channel));nrf_timer_shorts_enable(p_instance->p_reg, timer_short_mask);nrfx_timer_compare(p_instance, cc_channel, cc_value, enable_int);NRFX_LOG_INFO("Timer id: %d, capture value set: %lu, channel: %d.",p_instance->instance_id,cc_value,cc_channel);
}void nrfx_timer_compare_int_enable(nrfx_timer_t const * const p_instance,uint32_t                   channel)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);NRFX_ASSERT(channel < p_instance->cc_channel_count);nrf_timer_event_clear(p_instance->p_reg, nrf_timer_compare_event_get(channel));nrf_timer_int_enable(p_instance->p_reg, nrf_timer_compare_int_get(channel));
}void nrfx_timer_compare_int_disable(nrfx_timer_t const * const p_instance,uint32_t                   channel)
{NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);NRFX_ASSERT(channel < p_instance->cc_channel_count);nrf_timer_int_disable(p_instance->p_reg, nrf_timer_compare_int_get(channel));
}

  • 這些函數處理定時器的比較功能:
    • nrfx_timer_compare:設置比較值,可選擇啟用中斷
    • nrfx_timer_extended_compare:擴展比較功能,支持短接功能配置
    • nrfx_timer_compare_int_enable/disable:單獨控制比較中斷的啟用和禁用
中斷處理函數部分

c

static void irq_handler(NRF_TIMER_Type        * p_reg,timer_control_block_t * p_cb,uint8_t                 channel_count)
{uint8_t i;for (i = 0; i < channel_count; ++i){nrf_timer_event_t event = nrf_timer_compare_event_get(i);nrf_timer_int_mask_t int_mask = nrf_timer_compare_int_get(i);if (nrf_timer_event_check(p_reg, event) &&nrf_timer_int_enable_check(p_reg, int_mask)){nrf_timer_event_clear(p_reg, event);NRFX_LOG_DEBUG("Compare event, channel: %d.", i);p_cb->handler(event, p_cb->context);}}
}#if NRFX_CHECK(NRFX_TIMER0_ENABLED)
void nrfx_timer_0_irq_handler(void)
{irq_handler(NRF_TIMER0, &m_cb[NRFX_TIMER0_INST_IDX],NRF_TIMER_CC_CHANNEL_COUNT(0));
}
#endif// 類似地處理TIMER1到TIMER4的中斷處理函數(代碼結構相同)

  • irq_handler函數:通用中斷處理函數,處理指定定時器的所有通道事件
    • 遍歷所有通道,檢查是否有比較事件發生且中斷已啟用
    • 清除事件標志,記錄日志,并調用注冊的事件處理函數
  • 各個定時器實例的中斷處理函數(如 nrfx_timer_0_irq_handler):
    • 條件編譯檢查對應定時器是否啟用
    • 調用通用中斷處理函數,傳遞對應的寄存器、控制塊和通道數
結尾條件編譯

c

#endif // NRFX_CHECK(NRFX_TIMER_ENABLED)

  • 結束最外層的條件編譯,確保只有在定時器功能啟用時才包含這些代碼

總結

這段代碼是 nRF5 SDK 中定時器驅動的核心實現,主要功能包括:

  1. 定時器的初始化與反初始化
  2. 定時器的啟用、禁用、暫停和恢復
  3. 定時器的計數清除、手動遞增和值捕獲
  4. 定時器的比較功能配置和中斷處理
  5. 各個定時器實例的中斷處理函數

代碼采用了控制塊 (control block) 的設計模式,通過結構體數組管理每個定時器實例的狀態和回調函數。大量使用了斷言 (assert) 來確保輸入參數的有效性,并通過日志模塊記錄關鍵操作,便于調試。條件編譯的使用使得代碼可以根據配置靈活啟用不同的定時器實例,提高了代碼的可配置性和可維護性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/87202.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/87202.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/87202.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Bluedroid】藍牙啟動之BTM_reset_complete源碼解析

當藍牙控制器完成硬件重置后,協議棧需通過一系列初始化操作恢復各模塊狀態。本文深入分析BTM_reset_complete核心函數及其調用鏈,詳解 L2CAP 連接清理、安全模塊重置、掃描參數恢復、BLE 隱私功能初始化等關鍵流程,揭示藍牙設備在重置后如何通過標準化狀態恢復確保互操作性、…

containerd 項目主要目錄簡要說明

containerd 項目結構清晰&#xff0c;核心代碼分布在若干主目錄下。以下是 client、cmd、core、internal、pkg、plugins 這六個主要包/目錄的簡要作用說明&#xff1a; 1. client 作用&#xff1a;封裝與 containerd 守護進程通信的 Go 客戶端 API&#xff0c;主要基于 gRPC。…

有線轉無線工具,輕松創建WiFi熱點

軟件介紹 今天為大家推薦一款實用的無線網絡共享工具——MyPublicWiFi。這款軟件能夠將電腦的有線網絡轉換為無線WiFi&#xff0c;方便其他設備連接使用。 安裝與設置 該軟件為安裝版程序&#xff0c;安裝完成后會自動識別當前電腦的IP地址。用戶可在軟件界面中自定義設…

Linux下,通過標準I2C驅動讀取Sensor ID

sensor型號&#xff1a;OS04L10&#xff0c;sensor引腳以及時鐘要先配置好&#xff0c;源碼如下&#xff1a; #include <fcntl.h> #include <linux/i2c-dev.h> #include <linux/i2c.h> #include <stdint.h> #include <stdio.h> #include <sy…

人工智能基石:SVM支持向量機全解析(附Python實戰)

大家好&#xff01;今天我們來深入探討支持向量機&#xff08;Support Vector Machine, SVM&#xff09;——這個在??圖像識別、文本分類??等領域廣泛應用的強大算法。既能處理分類問題&#xff0c;又能解決回歸任務&#xff0c;甚至在非線性數據面前也能游刃有余。本文將帶…

mysql查看數據庫

在 MySQL 中查看當前數據庫的創建語句&#xff0c;使用 SHOW CREATE DATABASE 命令&#xff0c;以下是詳細操作指南&#xff1a; 1. 查看當前數據庫的創建語句 SHOW CREATE DATABASE database_name; 替換 database_name 為你的數據庫名使用反引號 包裹特殊名稱或保留字 2.…

ArrayList剖析

大家天天在用List&#xff0c;ArrayList一般來講應該是程序員用的最多的集合類了。 我們今天研究一下ArrayList。 總體來講&#xff0c;從底層數據結構或者源碼的角度看&#xff0c;List比Map或者Set要簡單。 底層數據結構 ArryList其實就是可變長數組。 初始化的時候&…

回顧JAVA中的鎖機制

Java中的鎖機制 在Java中&#xff0c;鎖機制是多線程編程里保障數據一致性與線程安全的關鍵技術。 1. 內置鎖&#xff1a;synchronized關鍵字 synchronized是Java的內置鎖機制&#xff0c;能夠保證在同一時刻&#xff0c;只有一個線程可以執行被其修飾的代碼塊或方法。 用法…

YOLOv11: AN OVERVIEW OF THE KEY ARCHITECTURAL ENHANCEMENTS目標檢測論文精讀(逐段解析)

YOLOv11: AN OVERVIEW OF THE KEY ARCHITECTURAL ENHANCEMENTS目標檢測論文精讀&#xff08;逐段解析&#xff09; 論文地址&#xff1a;https://www.arxiv.org/abs/2410.17725 Rahima Khanam and Muhammad Hussain Ultralytics公司發布 CVPR 2024 論文寫的比較簡單&#xff…

【Erdas實驗教程】025:遙感圖像輻射增強(霧霾去除)

文章目錄 一、霧霾去除原理二、霧霾去除案例一、霧霾去除原理 遙感影像霧霾去除的核心原理是消除大氣散射對電磁波的干擾,恢復地物真實反射信息。Haze Reduction 工具的原理: 該工具基于暗目標法(Dark Object Subtraction, DOS),適用于去除因大氣散射(霧霾本質是大氣顆…

Language Models are Unsupervised Multitask Learners :語言模型是無監督的多任務學習者

摘要 自然語言處理任務&#xff0c;如問答、機器翻譯、閱讀理解和摘要&#xff0c;通常通過在特定任務的數據集上進行監督學習來解決。我們展示了語言模型在訓練于一個包含數百萬網頁的新數據集——WebText——時&#xff0c;可以無需任何顯式監督就開始學習這些任務。當模型以…

SQL語句全攻略:從基礎到進階的編程之旅

目錄 一、引言二、SQL 基礎語法2.1 SQL 語句寫法順序2.2 關聯查詢2.3 數據處理常用函數和運算符 三、數據庫和表的基本操作3.1 創建數據庫3.2 使用數據庫3.3 創建表 四、基礎增刪改查操作4.1 插入數據&#xff08;增&#xff09;4.2 查詢數據&#xff08;查&#xff09;4.3 更新…

Kafka的下載安裝

目錄 一、前期準備 1、查看網卡&#xff1a; 2、配置靜態IP 3、設置主機名 4、配置IP與主機名映射 5、關閉防火墻 6、配置免密登錄 二、JDK的安裝 三、Zookeeper的安裝 四、Kafka的安裝 1、Kafka的下載安裝 2、修改配置文件 4、分發文件 5、修改其他節點broker.i…

opencv入門(6) TrackBar調整圖片和鍵盤響應

文章目錄 1 創建trackbar2 使用userdata傳入函數3 鍵盤響應 1 創建trackbar 1.trackbar名稱 2.創建在哪個窗口上 3.拖動trackbar改變的值 4.trackBar的最大值 5.trackbar改變時的回調函數 6. 帶入回調函數的數據&#xff0c;可以不用帶,是一個void指針 createTrackbar(“Value …

QT<33> 修改窗口標題欄背景顏色

前言&#xff1a; 在做項目或者開發QT軟件時&#xff0c;如果想要修改窗口標題欄背景顏色&#xff0c;發現沒有代碼可以直接設置&#xff0c;目前有兩種方法可以設置。 第一種&#xff0c;自定義一個界面類&#xff0c;用QLabelQWidget實現&#xff0c;QLabel當作標題欄。 第…

JavaEE-博客系統項目

項目介紹 準備工作 創建數據表 創建項目 添加依賴 創建對應目錄 除了基本的數據層mapper&#xff0c;業務層service&#xff0c;交互層controller&#xff0c;還創建了公共類的層common&#xff0c;枚舉類層enums&#xff0c;異常類層&#xff0c;和實體類層pojo。 配置項目配…

Java項目:基于SSM框架實現的軟件工程項目管理系統【ssm+B/S架構+源碼+數據庫+畢業論文+開題報告】

摘 要 現代經濟快節奏發展以及不斷完善升級的信息化技術&#xff0c;讓傳統數據信息的管理升級為軟件存儲&#xff0c;歸納&#xff0c;集中處理數據信息的管理方式。本項目管理系統就是在這樣的大環境下誕生&#xff0c;其可以幫助管理者在短時間內處理完畢龐大的數據信息&am…

[按鍵手機安卓/IOS腳本插件開發] 按鍵插件調試與判斷循環結構輔助工具

實現按鍵插件的核心原理 通過一個table類型的QMPlugin變量實現按鍵精靈調用Lua函數&#xff0c;例如 -- Lua代碼 -- 實現兩數相加求和 function QMPlugin.Add(a, b) return a b end 將以上代碼保存成.lua文件&#xff0c;例如test.lua后&#xff0c;放入按鍵精靈手機助手的p…

提示詞框架(9)--CARE

提示詞框架不止是AI的框架&#xff0c;也可以是我們的思考框架&#xff0c;拆解問題的方法&#xff01;&#xff01;&#xff01; CARE框架是一種用于優化提示詞設計的方法&#xff0c;它幫助用戶更有效地與AI進行交互&#xff0c;特別是在需要獲取特定信息或實現某些任務時。…

uniapp+vue2 input不顯示明文密碼,點擊小眼睛顯示或隱藏密碼

<u-input placeholder"請輸入密碼" prefixIcon"lock" :password"showPassword" v-model"formData.password"prefixIconStyle"font-size: 25px;color: #3C9CFF" border"none"><template slot"suffix…