SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合用于可穿戴、物联网设备等。具体可咨询:http://www.sydtek.com/
nrf51822学习之定时器的探究
主程序调用的定时器初始化函数:timers_init();
内容如下:
static void timers_init(void)
{
// Initialize timer module, making it use the scheduler
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, true);
/* YOUR_JOB: Create any timers to be used by the application.
Below is an example of how to create a timer.
For every new timer needed, increase the value of the macro APP_TIMER_MAX_TIMERS by
one.
err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
APP_ERROR_CHECK(err_code); */
}
这个定时器初始化函数最终使用的是RTC时钟,作为整个程序的宏观脉搏,为调度器使用,当然用户也可以创建时钟。
static uint32_t APP_TIMER_BUF[CEIL_DIV(APP_TIMER_BUF_SIZE((MAX_TIMERS), \
(OP_QUEUES_SIZE) + 1), \
sizeof(uint32_t))]; \
这是计算要分配的定时器内存区大小,说到底就是分配一个静态的数组,这个数组的类型是uint32_t,细看这个数组的项数,有下面这个函数:
#define CEIL_DIV(A, B) \
/*lint -save -e573 */ \
((((A) - 1) / (B)) + 1) \
/*lint -restore */
可以看到,这个数组是以uint32_t就是4个字节为单位的,具体计算再看:
APP_TIMER_BUF_SIZE((MAX_TIMERS), \
(OP_QUEUES_SIZE) + 1),
这里各个含义如下:
#define APP_TIMER_BUF_SIZE(MAX_TIMERS, OP_QUEUE_SIZE) \
( \
((MAX_TIMERS) * APP_TIMER_NODE_SIZE) \
+ \
( \
APP_TIMER_INT_LEVELS \
* \
(APP_TIMER_USER_SIZE + ((OP_QUEUE_SIZE) + 1) * APP_TIMER_USER_OP_SIZE) \
) \
)
其中传入的变量:
#define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */
#define APP_TIMER_MAX_TIMERS 2 /**< Maximum number of simultaneously created timers. */
#define APP_TIMER_OP_QUEUE_SIZE 4 /**< Size of timer operation queues. */
也就是说 MAX_TIMERS=2 OP_QUEUE_SIZE)=4,再看其他的变量:
#define APP_TIMER_NODE_SIZE 40 /**< Size of app_timer.timer_node_t (only for use inside APP_TIMER_BUF_SIZE()). */
#define APP_TIMER_USER_OP_SIZE 24 /**< Size of app_timer.timer_user_op_t (only for use inside APP_TIMER_BUF_SIZE()). */
#define APP_TIMER_USER_SIZE 8 /**< Size of app_timer.timer_user_t (only for use inside APP_TIMER_BUF_SIZE()). */
#define APP_TIMER_INT_LEVELS 3 /**< Number of interrupt levels from where timer operations may be initiated (only for use inside APP_TIMER_BUF_SIZE()). */
从这里看APP_TIMER_BUF_SIZE函数的功能就是计算出time模块一共需要多少内存区域,然后经过CEIL_DIV函数对齐4字节的计算就知道了要分配多少个数组项给定时器模块
分配了内存区,然后下来的就是初始化rtc时钟了:
uint32_t ERR_CODE = app_timer_init((PRESCALER), \
(MAX_TIMERS), \
(OP_QUEUES_SIZE) + 1, \
APP_TIMER_BUF, \
(USE_SCHEDULER) ? app_timer_evt_schedule : NULL); \
该函数的源代码如下:
uint32_t app_timer_init(uint32_t prescaler,//预分频器
uint8_t max_timers,//最大时间
uint8_t op_queues_size,
void * p_buffer,
app_timer_evt_schedule_func_t evt_schedule_func)
{
int i;
// Check that buffer is correctly aligned检查缓冲区是否正确字对齐
if (!is_word_aligned(p_buffer))
{
return NRF_ERROR_INVALID_PARAM;//无效的参数
}
// 检查空缓冲区
if (p_buffer == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
// Stop RTC to prevent any running timers from expiring (in case of reinitialization)RTC停止
rtc1_stop();
m_evt_schedule_func = evt_schedule_func;//按键调度事件
// Initialize timer node array初始化定时器节点数组
m_node_array_size = max_timers;
mp_nodes = p_buffer;
for (i = 0; i < max_timers; i++)
{
mp_nodes[i].state = STATE_FREE;
mp_nodes[i].is_running = false;
}
// Skip timer node array
p_buffer = &((uint8_t *)p_buffer)[max_timers * sizeof(timer_node_t)];
// Initialize users array
m_user_array_size = APP_TIMER_INT_LEVELS;
mp_users = p_buffer;
// Skip user array
p_buffer = &((uint8_t *)p_buffer)[APP_TIMER_INT_LEVELS * sizeof(timer_user_t)];
// Initialize operation queues
for (i = 0; i < APP_TIMER_INT_LEVELS; i++)
{
timer_user_t * p_user = &mp_users[i];
p_user->first = 0;
p_user->last = 0;
p_user->user_op_queue_size = op_queues_size;
p_user->p_user_op_queue = p_buffer;
// Skip operation queue
p_buffer = &((uint8_t *)p_buffer)[op_queues_size * sizeof(timer_user_op_t)];
}
m_timer_id_head = TIMER_NULL;
m_ticks_elapsed_q_read_ind = 0;
m_ticks_elapsed_q_write_ind = 0;
NVIC_ClearPendingIRQ(SWI0_IRQn);
NVIC_SetPriority(SWI0_IRQn, SWI0_IRQ_PRI);
NVIC_EnableIRQ(SWI0_IRQn);
rtc1_init(prescaler);
m_ticks_latest = rtc1_counter_get();
return NRF_SUCCESS;
}
首先要做的就是检查内存区是否是4字节对齐和是否存在
// Check that buffer is correctly aligned检查缓冲区是否正确字对齐
if (!is_word_aligned(p_buffer))
{
return NRF_ERROR_INVALID_PARAM;//无效的参数
}
// 检查空缓冲区
if (p_buffer == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
然后停止rtc
// Stop RTC to prevent any running timers from expiring (in case of reinitialization)RTC停止
rtc1_stop();
源码如下:
static void rtc1_stop(void)
{
NVIC_DisableIRQ(RTC1_IRQn);
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk; //清除比较器
NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk; //停止比较器使能
NRF_RTC1->TASKS_STOP = 1; //停止RTC模块
nrf_delay_us(MAX_RTC_TASKS_DELAY);
}
然后根据传入进来的参数给全局变量赋值:
m_evt_schedule_func = evt_schedule_func;//按键调度事件
// Initialize timer node array初始化定时器节点数组
m_node_array_size = max_timers;
mp_nodes = p_buffer;
主意这些全局变量,在后面才会使用,在本函数没有使用,只是赋值而已,下面给初始化定时器各个队列,比如设置定时器的使用标志和是否运行标志以及初始化操作队列
for (i = 0; i < max_timers; i++)
{
mp_nodes[i].state = STATE_FREE;
mp_nodes[i].is_running = false;
}
// Skip timer node array
p_buffer = &((uint8_t *)p_buffer)[max_timers * sizeof(timer_node_t)];
// Initialize users array
m_user_array_size = APP_TIMER_INT_LEVELS;
mp_users = p_buffer;
// Skip user array
p_buffer = &((uint8_t *)p_buffer)[APP_TIMER_INT_LEVELS * sizeof(timer_user_t)];
// Initialize operation queues
for (i = 0; i < APP_TIMER_INT_LEVELS; i++)
{
timer_user_t * p_user = &mp_users[i];
p_user->first = 0;
p_user->last = 0;
p_user->user_op_queue_size = op_queues_size;
p_user->p_user_op_queue = p_buffer;
// Skip operation queue
p_buffer = &((uint8_t *)p_buffer)[op_queues_size * sizeof(timer_user_op_t)];
}
m_timer_id_head = TIMER_NULL;
m_ticks_elapsed_q_read_ind = 0;
m_ticks_elapsed_q_write_ind = 0;
然后初始化一个软件中断,这个也是给后面的定时各个函数使用的使用的
NVIC_ClearPendingIRQ(SWI0_IRQn);
NVIC_SetPriority(SWI0_IRQn, SWI0_IRQ_PRI);
NVIC_EnableIRQ(SWI0_IRQn);
然后是rtc时钟的初始化
rtc1_init(prescaler);
这里传入的参数是#define APP_TIMER_PRESCALER 0 下面细看这个函数
static void rtc1_init(uint32_t prescaler)
{
NRF_RTC1->PRESCALER = prescaler;
NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI);
}
主意这里只是设置rtc的分频和中断优先级,优先级不论,说说这个分频,也就是rtc的频率是32768 ,中断不可能以这个频率去跑,那只能够说明定时器模块的频率毕竟和RTC的比较器有关了,或许不同的定时器模块他的比较器的值是不一样的吧。
最后是获取一下计数值,以更新一个全局变量:
m_ticks_latest = rtc1_counter_get();
主意到这里并没有开始定时器
还有这里的定时器指的是RTC而不是51822的定时计数模块
到此初始化是分析完毕了