本講教程還是會基於 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 如果想創建更多的定時器,同樣也要改大這裏的值