本讲教程还是会基于 9.0 sdk中的uart例子。 在该例子上使用 sdk中自带的软件定时器来实现一些功能
Uart例子在sdk 中如下目录
XXX\Keil_v5\ARM\Pack\NordicSemiconductor\nRF_Examples\9.0.0\ble_peripheral\ble_app_uart
首先 了解一下相关API
Sdk中的app timer(软件定时器) 是用51822中的RTC来模拟出的一个定时器队列来实现通过一个硬件RTC来模拟多个软件定时器。
使用app timer模块之前需要调用
uint32_t app_timer_init | ( | uint32_t | prescaler, |
uint8_t | max_timers, | ||
uint8_t | op_queues_size, | ||
void * | p_buffer, | ||
evt_schedule_func | |||
) |
来初始化模块,通常都不直接使用该函数,而是使用SDK中提供的宏。
这个宏会根据帮你的参数申请合适的buff然后再调用app_timer_init函数
#define APP_TIMER_INIT | ( |
| PRESCALER, |
| MAX_TIMERS, | ||
| OP_QUEUES_SIZE, | ||
| SCHEDULER_FUNC | ||
) |
Value:
do \
{ \
static uint32_t APP_TIMER_BUF[CEIL_DIV(APP_TIMER_BUF_SIZE((MAX_TIMERS), \
(OP_QUEUES_SIZE) + 1), \
sizeof(uint32_t))]; \
uint32_t ERR_CODE = app_timer_init((PRESCALER), \
(MAX_TIMERS), \
(OP_QUEUES_SIZE) + 1, \
APP_TIMER_BUF, \
SCHEDULER_FUNC); \
APP_ERROR_CHECK(ERR_CODE); \
} while (0)
然后就可以创建自己的定时器了。
uint32_t app_timer_create | ( | p_timer_id, | |
mode, | |||
timeout_handler | |||
) |
可以通过mode模式来选择是只运行一次还是周期性的定时器。
APP_TIMER_MODE_SINGLE_SHOT,
APP_TIMER_MODE_REPEATED
Timeout_handler为注册的回调函数,在定时到期后执行
定时器创建完成后需要主动调用开始函数
uint32_t app_timer_start(app_timer_id_t timer_id,uint32_t timeout_ticks,void * p_context )
定时时间 timeout_ticks 需要通过宏 APP_TIMER_TICKS(MS, PRESCALER) 来或得,MS为需要定时的ms数, PRESCALER就是上面的 初始化模块时的参数PRESCALER。
P_context 为传递给回调函数的参数。 在定时到期执行定时函数时这个参数会作为他的参数
下面我们在uart工程的基础上创建一个定时器,这个定时器在手机使能了rx特征值的notify功能后启动,然后周期性的递增发送一个数据给手机
Uart工程中 app timer模块的初始化已经有了,我们只要创建一个定时器然后在手机使能notify时开启他就行了。
添加如下变量和函数。
app_timer_id_t my_test_timer;
uint8_t g_send_data = 0;
void my_timer_handler(void *params){
ble_nus_string_send(&m_nus, &g_send_data, 1);
g_send_data++;
}
修改main函数,初始化最后创建定时器。
int main(void)
{
uint32_t err_code;
bool erase_bonds;
uint8_t start_string[] = START_STRING;
// Initialize.
APP_TIMER_INIT(APP_TIMER_PRESCALER,APP_TIMER_MAX_TIMERS,APP_TIMER_OP_QUEUE_SIZE, false); //原工程中已经初始化了app timer模块
uart_init();
buttons_leds_init(&erase_bonds);
ble_stack_init();
gap_params_init();
services_init();
advertising_init();
conn_params_init();
printf("start");
err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
//添加创建定时器代码
err_code = app_timer_create(&my_test_timer,
APP_TIMER_MODE_REPEATED,
my_timer_handler);
printf("create timere err_code:%d\r\n",err_code);
// Enter main loop.
for (;;)
{
power_manage();
}
}
然后 在 ble_nus.c 中 找到on_write函数添加红色部分
extern app_timer_id_t my_test_timer; //声明下变量
static void on_write(ble_nus_t * p_nus, ble_evt_t * p_ble_evt)
{
int err_code;
ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if ( (p_evt_write->handle == p_nus->rx_handles.cccd_handle)&&(p_evt_write->len == 2))
{
if (ble_srv_is_notification_enabled(p_evt_write->data))
{
p_nus->is_notification_enabled = true;
//定时1s, PRESCALER直接填 0
app_timer_start(my_test_timer, APP_TIMER_TICKS(1000, 0), NULL);
}
else
{
p_nus->is_notification_enabled = false;
}
}
else if ((p_evt_write->handle == p_nus->tx_handles.value_handle)
&&(p_nus->data_handler != NULL)
)
{
……………………………………….
}
else
{
// Do Nothing. This event is not relevant for this service.
}
}
现在下载程序后,手机连接,然后点使能Notify后就能周期收到数据了。
如果创建timer出错。是允许创建的定时器已经达到最大值了,可以修改一下宏。
Main.c中的
#define APP_TIMER_MAX_TIMERS (3 + BSP_APP_TIMERS_NUMBER)
将原来的2 改为3 如果想创建更多的定时器,同样也要改大这里的值