【教程】nrf51822实例代码解析及修改实例
http://www.eeboard.com/bbs/thread-42757-1-1.html
說在前面:此說明用于nrf51822的主從機的實例代碼解析,通過講解主從機的雙向通訊來說明如何添加自己的服務及特征。此說明為個人理解,可能與原意不同。
目錄
主機模塊...?3
從main說起...?3
app_trace_init()?3
ble_stack_init();?4
client_handling_init()?6
device_manager_init.?7
scan_start.?7
從機模塊...?7
從main說起...?7
gap_params_init();?8
advertising_init();?8
services_init();...?8
advertising_start();?10
以串口的收發為例...?10
從機端:...?10
主機端:...?11
--------------------------------main-------------------------------------------------------------------------
int main(void)
{
? ? //Initialization of various modules.
? ?app_trace_init();
? ?leds_init();
? ?buttons_init();
? ? ble_stack_init();
? ?client_handling_init();
? ?device_manager_init();
? ? // Startscanning for devices.
? ?scan_start();
? ? for (;;)
? ? {
? ?? ? power_manage();
? ? }
}
--------------------------------main-------------------------------------------------------------------------
接下來,具體看看各函數
app_trace_init() 初始化追蹤應用,既初始化串口。
具體就一條函數,設置相應的引腳。
simple_uart_config(RTS_PIN_NUMBER,TX_PIN_NUMBER,CTS_PIN_NUMBER,RX_PIN_NUMBER,HWFC);
如果需要在串口設置輸入中斷,需要添加如下代碼:NRF_UART0->INTENSET= UART_INTENSET_RXDRDY_Enabled << UART_INTENSET_RXDRDY_Pos;
NVIC_SetPriority(UART0_IRQn,APP_IRQ_PRIORITY_LOW);// APP_IRQ_PRIORITY_HIGH
? ???NVIC_EnableIRQ(UART0_IRQn);
并在main函數中添加中斷處理函數的實現:void UART0_IRQHandler(void){}注意:名字必須是這個。這個底層系統調用時已經定義好名字了。無須申明,只需要具體實現即可。??
值得注意的是 simple_uart_config 具體函數實現中有可以設置串口波特率,所有的波特率均有宏定義,只需直接更改宏即可。
NRF_UART0->BAUDRATE=(UART_BAUDRATE_BAUDRATE_Baud38400<<UART_BAUDRATE_BAUDRATE_Pos);
ble_stack_init(); 藍牙協議棧的初始化
--------------------------------ble_stack_init()-------------------------------------------------------------------------
uint32_t err_code;
? ? // Initialize the SoftDevicehandler module.
? ? SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM,false);
? ? // Register with theSoftDevice handler module for BLE events.
? ? err_code =softdevice_ble_evt_handler_set(ble_evt_dispatch);
? ? APP_ERROR_CHECK(err_code);
? ? // Register with theSoftDevice handler module for System events.
? ? err_code =softdevice_sys_evt_handler_set(sys_evt_dispatch);
? ? APP_ERROR_CHECK(err_code);
--------------------------------ble_stack_init()-------------------------------------------------------------------------
根據注釋可以知道大概每個函數是做什么的。
2.1)SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);
初始化SD處理模塊,用于初始化時鐘,內存等信息。需要關注的是
NRF_CLOCK_LFCLKSRC_XTAL_20_PPM 這是時鐘參數,跟設置波特率一樣,換相應的宏即可。
2.2) softdevice_ble_evt_handler_set(ble_evt_dispatch);
設置藍牙事件處理,后面細講。
2.3)softdevice_sys_evt_handler_set(sys_evt_dispatch);
設置系統事件處理。
接下來,看下 ble_evt_dispatch 具體的內容:
2.2.1)dm_ble_evt_handler(p_ble_evt);<----模塊管理
? ?? ?? 2.2.2)client_handling_ble_evt_handler(p_ble_evt); <---注冊的服務處理? ?? ??? 2.2.3) on_ble_evt(p_ble_evt); <--藍牙事件處理
大概的流程基本一致,就是事件處理,只是不同函數關注和處理的數據不同。以 on_ble_evt(p_ble_evt) 為例。
--------------------------------on_ble_evt(p_ble_evt)-----------------------------------------------------------------
switch (p_ble_evt->header.evt_id)
? ? {
? ?? ???case BLE_GAP_EVT_ADV_REPORT:
? ?? ???{
? ?? ?? ?? ?data_t adv_data;
? ?? ?? ?? ?data_t type_data;
? ?? ?? ?? ?// Initializeadvertisement report for parsing.
? ?? ?? ?? ?adv_data.p_data =p_ble_evt->evt.gap_evt.params.adv_report.data;
? ?? ?? ?? ?adv_data.data_len =p_ble_evt->evt.gap_evt.params.adv_report.dlen;
? ?? ?? ?? ?err_code =adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? &adv_data,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? &type_data);
? ?? ?? ?? ?if (err_code != NRF_SUCCESS)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? // Compare shortlocal name in case complete name does not match.
? ?? ?? ?? ?? ? err_code =adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???&adv_data,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???&type_data);
? ?? ?? ?? ?}
? ?? ?? ?? ?// Verify if short orcomplete name matches target.
? ?? ?? ?? ?if ((err_code ==NRF_SUCCESS) &&
? ?? ?? ?? ?? ?(0 ==memcmp(TARGET_DEV_NAME,type_data.p_data,type_data.data_len)))
? ?? ?? ?? ?{
? ?? ?? ?? ?? ? err_code =sd_ble_gap_scan_stop();
? ?? ?? ?? ?? ? if (err_code !=NRF_SUCCESS)
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ? APPL_LOG("[APPL]: Scan stop failed, reason %d\r\n", err_code);
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? err_code =sd_ble_gap_connect(&p_ble_evt->evt.gap_evt.params.adv_report.\
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?peer_addr,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?&m_scan_param,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?&m_connection_param);
? ?? ?? ?? ?? ? if (err_code !=NRF_SUCCESS)
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ? APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n",err_code);
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?}
? ?? ?? ?? ?break;
? ?? ???}
? ?? ???case BLE_GAP_EVT_TIMEOUT:
? ?? ?? ???if(p_ble_evt->evt.gap_evt.params.timeout.src ==BLE_GAP_TIMEOUT_SRC_SCAN)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ?APPL_LOG("[APPL]: Scan Timedout.\r\n");
? ?? ?? ?? ?}
? ?? ?? ?? ?else if(p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ?APPL_LOG("[APPL]: Connection Request Timedout.\r\n");
? ?? ?? ?? ?}
? ?? ?? ?? ?break;
? ?? ???default:
? ?? ?? ?? ?break;
? ? }
--------------------------------on_ble_evt(p_ble_evt)-----------------------------------------------------------------
就是一個switch事件,根據不同的事件類型處理做不同的事情。
如: BLE_GAP_EVT_ADV_REPORT
只要有設備進行廣播,并且廣播是可見的,而自身是掃描狀態的話,就會進入這個事件。具體是將接收到的數據進行匹配,一旦匹配成功就關閉掃描并發送連接請求。其中,需要關注的是: BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME 這個宏。這是主從設備廣播的數據匹配類型,在主從兩端都需要進行設置。
此處再列舉幾個常見的宏定義,并加以大致的解釋。(詳細可以參考協議棧初始化中的其他函數。)
BLE_GAP_EVT_TIMEOUT : 超時處理。
BLE_GATTC_EVT_WRITE_RSP : 寫請求回復。每當主機發送一次數據給從機,從機會回一個寫事件的回復,當主機收到寫事件的回復之后才能再次發送數據。
BLE_GATTC_EVT_HVX : 收到數據。這個事件表示從機發送了數據到主機。
? ?? ?? ? BLE_GAP_EVT_CONNECTED : 連接成功。
? ?? ?? ? BLE_GAP_EVT_DISCONNECTED : 斷開連接。
BLE_GAP_EVT_SEC_PARAMS_REQUEST : 從機請求連接參數
BLE_GAP_EVT_CONN_SEC_UPDATE: 從機請求更改連接參數
client_handling_init() 初始化客戶端處理
--------------------------------client_handling_init()-----------------------------------------------------------------
uint32_t err_code;
? ? uint32_t i;
? ? ble_uuid128_t base_uuid = MULTILINK_PERIPHERAL_BASE_UUID;
? ? err_code =sd_ble_uuid_vs_add(&base_uuid, &m_base_uuid_type);
? ? APP_ERROR_CHECK(err_code);? ? nrf_gpio_range_cfg_output(8, 15);
? ? for (i = 0; i < MAX_CLIENTS; i++)
? ? {
? ?? ???m_client.state??= IDLE;
? ? }
? ? m_client_count = 0;
? ? db_discovery_init();
? ? // Register with discovery module for thediscovery of the service.
? ? ble_uuid_t uuid;? ? uuid.type = m_base_uuid_type;
? ? uuid.uuid =MULTILINK_PERIPHERAL_SERVICE_UUID;
? ? err_code =ble_db_discovery_register(&uuid,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? db_discovery_evt_handler);
? ? APP_ERROR_CHECK(err_code);--------------------------------client_handling_init()-----------------------------------------------------------------
這個函數用于注冊服務以及特征,以及服務的處理。
其中 sd_ble_uuid_vs_add(&base_uuid, &m_base_uuid_type);用于16bit注冊服務。
SIG規定了一套標準的uuid的規則,具體的uuid的規則和類型可以參考實例代碼。
uuid.uuid= MULTILINK_PERIPHERAL_SERVICE_UUID;添加4bit服務,相當于起別名。
ble_db_discovery_register(&uuid,db_discovery_evt_handler);注冊服務發現處理函數。
device_manager_init?設備管理處理化這個函數用于初始化一些設備參數,如(掃描時間,掃描間隔,接收數據的大小等);不需要手動更改,相應的參數修改請在main中的宏定義中修改。
scan_start? ?啟動掃描
? ?? ?? ?這函數用于將前一個函數初始化的數據填入掃描函數中,并開始掃描,不需要手動更改。
至此,全部準備工作完畢,接來下主機就會進入循環的監聽模式,當有相應的事件到來就會自動調用協議棧中相應的模塊進行調度。
從機模塊 從main說起以\nrf51822\Board\nrf6310\s120\experimental\ble_app_multilink_peripheral為例。與主機的工作流程類似(從main看起):初始化了參數之后,開啟廣播,然后就進入循環。只是從機需要初始化的東西有部分與主機會有些不同,其中led,buttons,gpio的內容非開發所需,此處忽略。此外,由于主從設備是配合使用的。ble_stack_init(); device_manager_init();這兩個的初始化與主機的初始化基本一致,只是相應的處理方式和參數名定義會不同。需要查看差異的話,可以對比查看。(比如,從機會有廣播包的事情處理,而主機則沒有。)這里只對其他函數進行說明。
--------------------------------main-------------------------------------------------------------------------
int main(void)
{
? ?ble_stack_init();
? ?leds_init();
? ?timers_init();
? ?gpiote_init();
? ?buttons_init();
? ?device_manager_init();
? ?gap_params_init();
? ?advertising_init();
? ?services_init();
? ?advertising_start();
? ?for (;;)
? ? {
? ?? ? power_manage();
}
--------------------------------main-------------------------------------------------------------------------
接下來,具體看看各函數
gap_params_init();這個函數用于初始化所有的需要的GAP參數,如(設備名稱,連接時間,超時時間等)。同樣的,這個函數不需要修改,相應的參數需要在宏定義中修改。
advertising_init();這個函數用于初始化廣播包。需要注意的有如下參數:
Flag:設備廣播的可見范圍,如有限可見,常可見等設置,詳見宏定義的說明。
advdata.name_type:廣播包中包含的設備名稱類型,與主機端配套設置。如全名稱,短名稱,和不顯示名稱。
services_init();這個函數用于初始化服務,與主機端的client_handing對應。
-------------------------------- ?services_init()-----------------------------------------------------------
uint32_t? ?? ?? ?? ?err_code;
? ? ble_uuid_t? ?? ?? ? uuid;
? ? ble_gatts_char_md_t char_md;
? ? ble_gatts_attr_t? ? attr;
? ? ble_gatts_attr_md_t attr_md;
? ? ble_gatts_attr_md_t cccd_md;
? ? ble_gatts_attr_md_t char_ud_md;
? ? uint16_t? ?? ?? ?? ?svc_test;
? ? static uint8_t multilink_peripheral_data;
? ? static uint8_t multilink_peripheral_ud[] ="Modifiable multilink_peripheral Data";
? ? ble_uuid128_t base_uuid =MULTILINK_PERIPHERAL_BASE_UUID;
? ? err_code =sd_ble_uuid_vs_add(&base_uuid, &m_base_uuid_type);
? ? APP_ERROR_CHECK(err_code);
? ? uuid.type = m_base_uuid_type;
? ? uuid.uuid = MULTILINK_PERIPHERAL_SERVICE_UUID;
? ? err_code =sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &uuid,&svc_test);
? ? APP_ERROR_CHECK(err_code);
? ? uuid.uuid = MULTILINK_PERIPHERAL_CHAR_UUID;
? ? memset(&attr, 0,sizeof(ble_gatts_attr_t));
? ? attr.p_uuid? ? = &uuid;
? ? attr.p_attr_md = &attr_md;
? ? attr.max_len? ?= 1;
? ? attr.p_value? ?= &multilink_peripheral_data;
? ? attr.init_len??= sizeof(multilink_peripheral_data);
? ? memset(&attr_md, 0,sizeof(ble_gatts_attr_md_t));
? ? BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
? ?BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&attr_md.write_perm);
? ? attr_md.vloc = BLE_GATTS_VLOC_STACK;
? ? attr_md.vlen = 0;
? ? memset(&cccd_md, 0,sizeof(ble_gatts_attr_md_t));
? ?BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
? ? BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&cccd_md.write_perm);
? ? cccd_md.vloc = BLE_GATTS_VLOC_STACK;
? ? memset(&char_md, 0,sizeof(ble_gatts_char_md_t));
? ? char_md.p_cccd_md? ?? ?? ?? ?? ?= &cccd_md;
? ? char_md.char_props.notify? ?? ? = 1;
? ? char_md.char_props.indicate? ???= 1;
? ? char_md.char_props.read? ?? ?? ?= 1;
? ? char_md.char_props.write? ?? ???= 1;
? ? char_md.char_ext_props.wr_aux? ?= 1;
? ? char_md.p_user_desc_md? ?? ?? ? = &char_ud_md;
? ? char_md.p_char_user_desc? ?? ???= multilink_peripheral_ud;
? ? char_md.char_user_desc_size? ???= (uint8_t)strlen((char*)multilink_peripheral_ud);
? ? char_md.char_user_desc_max_size =(uint8_t)strlen((char *)multilink_peripheral_ud);
? ? memset(&char_ud_md, 0,sizeof(ble_gatts_attr_md_t));
? ? char_ud_md.vloc = BLE_GATTS_VLOC_STACK;
? ? char_ud_md.vlen = 1;
? ?BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_ud_md.read_perm);
? ?BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_ud_md.write_perm);
? ? err_code =sd_ble_gatts_characteristic_add(BLE_GATT_HANDLE_INVALID,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???&char_md,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? &attr,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? &m_char_handles);
? ? APP_ERROR_CHECK(err_code);
-------------------------------- ?services_init()-----------------------------------------------------------
需要注意如何添加一個服務,特征。
首先,添加一個服務到服務表中。這于主機的是一樣的。
sd_ble_uuid_vs_add(&base_uuid,&m_base_uuid_type);
其次,將該服務設置為主服務列表當中。sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,&uuid, &svc_test);
再者,設置服務之后,需要在這個服務中添加相應的特征。sd_ble_gatts_characteristic_add(BLE_GATT_HANDLE_INVALID,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? &char_md,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? &attr,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? &m_char_handles);
需要說明的是,無論是服務,還是特征都有自己對應的屬性,在注冊之前都需要設置其對應的屬性,這點在實例代碼中已經有了很好的模板。有需要添加自己的服務或者特征都可以通過這個模板進行修改。
advertising_start();開始廣播,這與主機的開始掃描是相對應的。設置廣播類型,時間,超時時間等參數后開始廣播。同樣的,這個函數也不需要修改,修改的部分都在宏定義處修改。
至此,全部準備工作完畢,接來下從機就會進入循環的廣播模式,當有相應的事件到來就會自動調用協議棧中相應的模塊進行調度。
以串口的收發為例接下來,將以串口的收發為例。講述具體數據的發送和接收。
Nrf51822的實例代碼優點就是所有的函數均是模塊化的。因此可以不加修改整個拷貝即可使用。
從機端:從機端的代碼需要參考\Board\pca10001\s110\experimental\ble_app_uart的代碼。
1)? ?? ?調通串口。 發現從機端是沒有設置串口代碼的。因此需要先在從機端添加相應的串口初始化代碼。這里需要用到的uart_init()代碼。直接復制即可。
2)? ?? ?串口中斷函數 直接復制串口中斷處理函數的代碼void UART0_IRQHandler(void)。
看下中斷處理函數做了些什么事情
-------------------------------- voidUART0_IRQHandler(void)---- -------------------------------------
{? ? static uint8_tdata_array[BLE_NUS_MAX_DATA_LEN];
? ? static uint8_t index = 0;? ? uint32_t err_code;
? ? /**@snippet [Handling the data receivedover UART] */
? ? data_array[index] = simple_uart_get();? ? index++;
? ? if ((data_array[index - 1] == '\n') ||(index >= (BLE_NUS_MAX_DATA_LEN - 1)))
? ? {? ?? ???err_code = ble_nus_send_string(&m_nus,data_array, index + 1);
? ?? ???if (err_code !=NRF_ERROR_INVALID_STATE)
? ?? ???{? ?? ?? ?? ?APP_ERROR_CHECK(err_code);
? ?? ???}? ?? ?
? ?? ???index = 0;? ? }
? ? /**@snippet [Handling the data receivedover UART] */
}-------------------------------- voidUART0_IRQHandler(void)---- -------------------------------------
接收串口中的消息,當遇到回車或者字符數夠了之后,將數據發送出去。ble_nus_send_string(&m_nus,data_array, index + 1);這條語句中的m_nus的參數就是數據發送的句柄。會發現根本就沒有對這個參數進行任何的設置,數據是發不出去的。因此要對這個參數進行相應的處理。這里就需要用到services_init()
3)? ?? ?services_init() 看下參考代碼和從機的代碼中services_init()的差異,會發現參考代碼對該函數又進行了進一步的封裝。其中就有對m_nus的操作。同樣的。整個復制過來。(包括內部實現的函數)
這里需要記住注冊了的服務的uuid和BLE_UUID_NUS_SERVICE。
同時注冊的特征id:BLE_UUID_NUS_TX_CHARACTERISTIC,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?BLE_UUID_NUS_RX_CHARACTERISTIC也需要記住。
這樣發送的部分就已經做完了。
那么接收函數呢?
其實也已經做好了。在復制services_init()函數時, ? nus_init.data_handler = nus_data_handler;??
把接收數據的處理函數也已經添加進來了。這里只是把數據通過串口打印出來而已。并未做其他操作。
主機端:主機端的代碼需要參考\Board\nrf6310\s120\experimental\ble_app_hrs_c的代碼
串口中斷函數。同樣的。將參考代碼的串口中斷函數復制過來。同樣是缺乏m_nus這個發送的句柄,因此這里也同樣需要添加服務。此外,由于主從機發送數據所調用的函數不同。這里還需要修改ble_nus_send_string(&m_nus,data_array, index + 1)這個函數。修改后的內容如下,(參考\Board\nrf6310\s120\experimental\ble_app_uart_c中的tx_send()的代碼。)
----------tx_send(ble_ecg_c_t* p_ble_ecg_c,char *str ,unsigned char len)------------
void tx_send(ble_ecg_c_t *p_ble_ecg_c,char *str ,unsigned char len){
? ? tx_message_t * p_msg;? ? if (len > WRITE_MESSAGE_LENGTH) {
? ?? ???return ;
? ? }? ?? ?? ???
? ? p_msg? ?? ?? ?? ???=&m_tx_buffer[m_tx_insert_index++];
? ? m_tx_insert_index &= TX_BUFFER_MASK;? ? strncpy(p_msg->req.write_req.gattc_value,str,len);
? ? p_msg->req.write_req.gattc_params.handle? ?= p_ble_ecg_c->ecg_rx_handle;? ?p_msg->req.write_req.gattc_params.len? ?? ?= len;
? ?p_msg->req.write_req.gattc_params.p_value = (uint8_t*)p_msg->req.write_req.gattc_value;
? ? p_msg->req.write_req.gattc_params.offset? ?= 0;? ?p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ;
? ? p_msg->conn_handle? ?? ?? ?? ?? ?? ?? ?? ? =p_ble_ecg_c->conn_handle;
? ? p_msg->type? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???= WRITE_REQ;? ? tx_buffer_process();
}
----------tx_send(ble_ecg_c_t* p_ble_ecg_c,char *str ,unsigned char len)------------
1)? ?? ?參考hrs_c_init()函數添加新的服務。 -------------------------------- hrs_c_init()-----------------------------------------
static voidhrs_c_init(void)
{? ? ble_hrs_c_init_t hrs_c_init_obj;
? ? hrs_c_init_obj.evt_handler =hrs_c_evt_handler;
? ? uint32_t err_code = ble_hrs_c_init(&m_ble_hrs_c,&hrs_c_init_obj);
? ? APP_ERROR_CHECK(err_code);}
-------------------------------- hrs_c_init()-----------------------------------------
修改部分:
1.(建議,非必須)將所有hrs相關的單詞(包括文件名,宏定義,函數名,變量名等)改掉。
2. hrs_c_evt_handler這個函數是數據接收的實現函數。
3. ble_hrs_c_init(&m_ble_hrs_c, &hrs_c_init_obj)中m_ble_hrs_c這個參數的名稱必須與串口中斷中的m_nus這個參數必須是同一參數,具體起什么名字自己定一個全局變量即可。
4.修改ble_hrs_c_init()
---------------------------------- ble_hrs_c_init()------------------------------------------------
uint32_tble_hrs_c_init(ble_hrs_c_t * p_ble_hrs_c, ble_hrs_c_init_t * p_ble_hrs_c_init)
{? ? if ((p_ble_hrs_c == NULL) ||(p_ble_hrs_c_init == NULL))
? ? {? ?? ???return NRF_ERROR_NULL;
? ? }
? ? ble_uuid_t hrs_uuid;
? ? hrs_uuid.type? ?? ?? ?? ?? ???= BLE_UUID_TYPE_BLE;
? ? hrs_uuid.uuid? ?? ?? ?? ?? ???= BLE_UUID_HEART_RATE_SERVICE;
? ? mp_ble_hrs_c? ?? ?? ?? ?? ?? ?= p_ble_hrs_c;
? ? mp_ble_hrs_c->evt_handler? ???= p_ble_hrs_c_init->evt_handler;
? ? mp_ble_hrs_c->conn_handle? ???= BLE_CONN_HANDLE_INVALID;
? ? mp_ble_hrs_c->hrm_cccd_handle =BLE_GATT_HANDLE_INVALID;
? ? returnble_db_discovery_register(&hrs_uuid,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? db_discover_evt_handler);}
-------------------------------- ble_hrs_c_init()-----------------------------------------
會發現,這里并沒有添加服務到服務表中。這里因為心率服務在SIG中已經定義好了。無需用戶再進行添加。只需要根據需要再添加特征即可。
這里需要添加一個服務uuid。添加或修改如下代碼:
ble_uuid128_t? ?nus_base_uuid = {0x9E, 0xCA, 0xDC, 0x24,0x0E, 0xE5, 0xA9, 0xE0,
? ?? ?? ?? ?? ?? ?? ?? ? 0x93,0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E};
? ?? ?? ?? ? mp_ble_hrs_c? ?? ?? ?? ?? ?? ?= p_ble_hrs_c;? ?? ?? ?? ?? ?? ?? ? err_code =sd_ble_uuid_vs_add(&nus_base_uuid, &m_base_uuid_type);
? ? if (err_code != NRF_SUCCESS)? ? {
? ?? ???return err_code;
? ? }
? ? ble_uuid_t hrs_uuid;
? ? hrs_uuid.type? ?? ?? ?? ?? ???= m_base_uuid_type;
? ?? ?? ? hrs_uuid.uuid? ?? ?? ?? ?? ???= BLE_UUID_NUS_SERVICE;
需要注意的是,uuid必須與從機的uuid一致。BLE_UUID_NUS_SERVICE與從機的一致。這樣才能確保主機能找到從機的服務。
再修改 db_discover_evt_handler 服務發現處理函數。
-------------------------------- db_discover_evt_handler-----------------------------------------
static voiddb_discover_evt_handler(ble_db_discovery_evt_t * p_evt)
{? ? // Check if the Heart Rate Service wasdiscovered.
? ? if (p_evt->evt_type ==BLE_DB_DISCOVERY_COMPLETE
? ?? ???&&? ?? ? p_evt->params.discovered_db.srv_uuid.uuid ==BLE_UUID_HEART_RATE_SERVICE
? ?? ???&&? ?? ? p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE)
? ? {? ?? ???mp_ble_hrs_c->conn_handle =p_evt->conn_handle;
? ?? ???// Find the CCCD Handle of the HeartRate Measurement characteristic.
? ?? ???uint8_t i;? ?? ???for (i = 0; i <p_evt->params.discovered_db.char_count; i++)
? ?? ???{? ?? ?? ?? ?if(p_evt->params.discovered_db.charateristics.characteristic.uuid.uuid
? ?? ?? ?? ?? ? ==? ?? ?? ?? ?? ?BLE_UUID_HEART_RATE_MEASUREMENT_CHAR)
? ?? ?? ?? ?{? ?? ?? ?? ?? ? // Found Heart Ratecharacteristic. Store CCCD handle and break.
? ?? ?? ?? ?? ?mp_ble_hrs_c->hrm_cccd_handle =
? ?? ?? ?? ?? ?? ?? ?? ? p_evt->params.discovered_db.charateristics.cccd_handle;
? ?? ?? ?? ?? ?mp_ble_hrs_c->hrm_handle=? ?? ?? ?? ?? ???p_evt->params.discovered_db.charateristics.characteristic.handle_value;
? ?? ?? ?? ?? ? break;? ?? ?? ?? ?}
? ?? ???}
? ?? ???LOG("[HRS_C]: Heart Rate Servicediscovered at peer.\r\n");
? ?? ???ble_hrs_c_evt_t evt;? ?? ???evt.evt_type =BLE_HRS_C_EVT_DISCOVERY_COMPLETE;
? ?? ? mp_ble_hrs_c->evt_handler(mp_ble_hrs_c, &evt);
? ? }}
-------------------------------- db_discover_evt_handler-----------------------------------------
這里是發現服務后的處理函數。先確保找到的是正確的服務。然后將服務里的特征與主機定義的特征進行匹配,匹配成功后獲取相應的參數值。具體修改如下:
將 BLE_UUID_HEART_RATE_SERVICE換成 BLE_UUID_NUS_SERVICE
將 BLE_UUID_TYPE_BLE換成 m_base_uuid_type
將 BLE_UUID_HEART_RATE_MEASUREMENT_CHAR換成 BLE_UUID_NUS_TX_CHARACTERISTIC,并仿照這塊代碼,添加一個 BLE_UUID_NUS_RX_CHARACTERISTIC的特征發現。
最后在藍牙協議棧的初始化中,添加服務的處理事件。這里可以參考串口服務ble_nus_on_ble_evt中的代碼。
--------------------------------ble_nus_on_ble_evt-----------------------------------------
void ble_nus_on_ble_evt(ble_nus_t * p_nus, ble_evt_t * p_ble_evt)
{
? ? if ((p_nus == NULL) ||(p_ble_evt == NULL))
? ? {
? ?? ???return;
? ? }
? ? switch(p_ble_evt->header.evt_id)
? ? {
? ?? ???caseBLE_GAP_EVT_CONNECTED:
? ?? ?? ?? ?on_connect(p_nus,p_ble_evt);
? ?? ?? ?? ?break;
? ?? ???case BLE_GAP_EVT_DISCONNECTED:
? ?? ?? ?? ?on_disconnect(p_nus,p_ble_evt);
? ?? ?? ?? ?break;
? ?? ???case BLE_GATTS_EVT_WRITE:
? ?? ?? ?? ?on_write(p_nus,p_ble_evt);
? ?? ?? ?? ?break;
? ?? ?? ???caseBLE_GATTC_EVT_WRITE_RSP:? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?
? ?? ?? ?? ?? ?? ?? ?on_write_rsp(p_nus, p_ble_evt);
? ?? ?? ?? ?break;??
? ?? ? default:
? ?? ?? ?? ?// No implementationneeded.
? ?? ?? ?? ?break;
? ? }
}-------------------------------ble_nus_on_ble_evt-----------------------------------------
當有通知到來時,將會觸發BLE_GATTS_EVT_WRITE,可以在這里將收到的數據通過串口打印出來。
至此,nrf51822的實例代碼解析,以及如何添加自己的一個服務及特征,主從機的雙向通訊全部解釋完畢。當中的參數定義并沒有加以補充。復制不同模塊的函數時,要同時將參數、申明、宏定義等相關內容也一塊復制過來。當然復制代碼并不是直接復制,黏貼。而是在理解后參考著原代碼修改成為自己的內容。
總結
以上是生活随笔為你收集整理的【教程】nrf51822实例代码解析及修改实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CC2540、nRF51822应用开发比
- 下一篇: BLE-NRF51822教程1-常用概念