NRF51822蓝牙服务(1)——LED读写
前言
上篇已經簡單分析了一下BLE協議棧的基本知識,今天就廢話少說,直接先從最基礎的點燈開始BLE藍牙服務的開發。
實例分析
本文直接采用官方SDK的led例程修改,所以首先我們先來看看開發板的硬件連接:
從這里可以看出P0.18~P0.20分別對應LED0~LED2,當芯片輸出高電平時LED將被點亮;
這時候我們來看下官方的LED驅動代碼:
#define LEDS_OFF(leds_mask) do { NRF_GPIO->OUTSET = (leds_mask) & (LEDS_MASK & LEDS_INV_MASK); \NRF_GPIO->OUTCLR = (leds_mask) & (LEDS_MASK & ~LEDS_INV_MASK); } while (0)#define LEDS_ON(leds_mask) do { NRF_GPIO->OUTCLR = (leds_mask) & (LEDS_MASK & LEDS_INV_MASK); \NRF_GPIO->OUTSET = (leds_mask) & (LEDS_MASK & ~LEDS_INV_MASK); } while (0)這里可以知道官方的設置與我們實際是相反的,所以代碼改為:
#define LEDS_OFF(leds_mask) do { NRF_GPIO->OUTCLR = (leds_mask) & (LEDS_MASK & LEDS_INV_MASK); \NRF_GPIO->OUTSET = (leds_mask) & (LEDS_MASK & ~LEDS_INV_MASK); } while (0)#define LEDS_ON(leds_mask) do { NRF_GPIO->OUTSET = (leds_mask) & (LEDS_MASK & LEDS_INV_MASK); \NRF_GPIO->OUTCLR = (leds_mask) & (LEDS_MASK & ~LEDS_INV_MASK); } while (0)接著,我們需要新建服務的驅動文件,如:ble_lbs.c和ble_lbs.h文件,這里我們直接分析官方的文件即可:
ble_lbs.h:
#define LBS_UUID_BASE {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, \0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00} #define LBS_UUID_SERVICE 0x1523 #define LBS_UUID_LED_CHAR 0x1525這里宏定義了私有服務的基礎UUID和服務的UUID以及特征值的UUID;
問:這些UUID怎么來的呢?
答:使用nRFgo Studio生成基礎UUID,例如生成的基礎UUID是0000xxxx-1212-EFDE-1523-785FEABCD123,并且由于nrf51822是小端存儲格式,所以定義時要倒置;
/** @brief LED Button Service init structure. This structure contains all options and data needed for* initialization of the service.*/ typedef struct {ble_lbs_led_write_handler_t led_write_handler; /**< Event handler to be called when the LED Characteristic is written. */ } ble_lbs_init_t;/**@brief LED Button Service structure. This structure contains various status information for the service. */ struct ble_lbs_s {uint16_t service_handle; /**< Handle of LED Button Service (as provided by the BLE stack). */ble_gatts_char_handles_t led_char_handles; /**< Handles related to the LED Characteristic. */ble_gatts_char_handles_t button_char_handles; /**< Handles related to the Button Characteristic. */uint8_t uuid_type; /**< UUID type for the LED Button Service. */uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack). BLE_CONN_HANDLE_INVALID if not in a connection. */ble_lbs_led_write_handler_t led_write_handler; /**< Event handler to be called when the LED Characteristic is written. */ };緊接著我們看到定義了兩個結構體,一個是回調句柄結構體,一個是服務參數結構體;
/**@brief Function for initializing the LED Button Service.** @param[out] p_lbs LED Button Service structure. This structure must be supplied by* the application. It is initialized by this function and will later* be used to identify this particular service instance.* @param[in] p_lbs_init Information needed to initialize the service.** @retval NRF_SUCCESS If the service was initialized successfully. Otherwise, an error code is returned.*/ uint32_t ble_lbs_init(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init);/**@brief Function for handling the application's BLE stack events.** @details This function handles all events from the BLE stack that are of interest to the LED Button Service.** @param[in] p_lbs LED Button Service structure.* @param[in] p_ble_evt Event received from the BLE stack.*/ void ble_lbs_on_ble_evt(ble_lbs_t * p_lbs, ble_evt_t * p_ble_evt);最后,聲明了兩個API供上層應用調用。
ble_lbs.c:
/**@brief Function for handling the Connect event.** @param[in] p_lbs LED Button Service structure.* @param[in] p_ble_evt Event received from the BLE stack.*/ static void on_connect(ble_lbs_t * p_lbs, ble_evt_t * p_ble_evt) {p_lbs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle; }/**@brief Function for handling the Disconnect event.** @param[in] p_lbs LED Button Service structure.* @param[in] p_ble_evt Event received from the BLE stack.*/ static void on_disconnect(ble_lbs_t * p_lbs, ble_evt_t * p_ble_evt) {UNUSED_PARAMETER(p_ble_evt);p_lbs->conn_handle = BLE_CONN_HANDLE_INVALID; }/**@brief Function for handling the Write event.** @param[in] p_lbs LED Button Service structure.* @param[in] p_ble_evt Event received from the BLE stack.*/ static void on_write(ble_lbs_t * p_lbs, ble_evt_t * p_ble_evt) {ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;if ((p_evt_write->handle == p_lbs->led_char_handles.value_handle) &&(p_evt_write->len == 1) &&(p_lbs->led_write_handler != NULL)){p_lbs->led_write_handler(p_lbs, p_evt_write->data[0]);} }void ble_lbs_on_ble_evt(ble_lbs_t * p_lbs, ble_evt_t * p_ble_evt) {switch (p_ble_evt->header.evt_id){case BLE_GAP_EVT_CONNECTED:on_connect(p_lbs, p_ble_evt);break;case BLE_GAP_EVT_DISCONNECTED:on_disconnect(p_lbs, p_ble_evt);break;case BLE_GATTS_EVT_WRITE:on_write(p_lbs, p_ble_evt);break;default:// No implementation needed.break;} }這里我們可以看到,文件中定義了4個API函數。分別是連接時,斷開連接時以及寫空中操作時調用的函數,我們主要留意下寫操作怎么關聯到處理回調函數即可。而ble_lbs_on_ble_evt這個函數則是作為應用層調用的接口,在事件派發函數中被調用。
接下來,我們還需要就是定義服務初始化函數以及為服務添加特征值的函數。
uint32_t ble_lbs_init(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init) {uint32_t err_code;ble_uuid_t ble_uuid;// Initialize service structure.p_lbs->conn_handle = BLE_CONN_HANDLE_INVALID;p_lbs->led_write_handler = p_lbs_init->led_write_handler;// Add service.ble_uuid128_t base_uuid = {LBS_UUID_BASE};err_code = sd_ble_uuid_vs_add(&base_uuid, &p_lbs->uuid_type);if (err_code != NRF_SUCCESS){return err_code;}ble_uuid.type = p_lbs->uuid_type;ble_uuid.uuid = LBS_UUID_SERVICE;err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_lbs->service_handle);if (err_code != NRF_SUCCESS){return err_code;}// Add characteristics.err_code = button_char_add(p_lbs, p_lbs_init);if (err_code != NRF_SUCCESS){return err_code;}err_code = led_char_add(p_lbs, p_lbs_init);if (err_code != NRF_SUCCESS){return err_code;}return NRF_SUCCESS; } static uint32_t led_char_add(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init) {ble_gatts_char_md_t char_md;ble_gatts_attr_t attr_char_value;ble_uuid_t ble_uuid;ble_gatts_attr_md_t attr_md;memset(&char_md, 0, sizeof(char_md));char_md.char_props.read = 1;char_md.char_props.write = 1;char_md.p_char_user_desc = NULL;char_md.p_char_pf = NULL;char_md.p_user_desc_md = NULL;char_md.p_cccd_md = NULL;char_md.p_sccd_md = NULL;ble_uuid.type = p_lbs->uuid_type;ble_uuid.uuid = LBS_UUID_LED_CHAR;memset(&attr_md, 0, sizeof(attr_md));BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);attr_md.vloc = BLE_GATTS_VLOC_STACK;attr_md.rd_auth = 0;attr_md.wr_auth = 0;attr_md.vlen = 0;memset(&attr_char_value, 0, sizeof(attr_char_value));attr_char_value.p_uuid = &ble_uuid;attr_char_value.p_attr_md = &attr_md;attr_char_value.init_len = sizeof(uint8_t);attr_char_value.init_offs = 0;attr_char_value.max_len = sizeof(uint8_t);attr_char_value.p_value = NULL;return sd_ble_gatts_characteristic_add(p_lbs->service_handle,&char_md,&attr_char_value,&p_lbs->led_char_handles); }到這里,就基本完成服務驅動的代碼。
應用層:
/**@brief Function for initializing services that will be used by the application.*/ static void services_init(void) {uint32_t err_code;ble_lbs_init_t init;init.led_write_handler = led_write_handler;err_code = ble_lbs_init(&m_lbs, &init);APP_ERROR_CHECK(err_code); }既然我們創建了私有服務,那么必然要在服務初始化里面將服務注冊進去。需要注意的是,我們前面提到的寫空中操作的處理回調函數也是要在這里注冊,但現在我們還沒完成回調函數的代碼。所以,我們先把代碼加上:
static void led_write_handler(ble_lbs_t * p_lbs, uint8_t led_state) {if (led_state){LEDS_ON(LEDBUTTON_LED_PIN);}else{LEDS_OFF(LEDBUTTON_LED_PIN);} }緊接著,就是將LED服務的事件處理函數添加到應用層的事件派發函數,這樣當進行空中操作時就會觸發事件派發,從而執行回調處理函數:
static void ble_evt_dispatch(ble_evt_t * p_ble_evt) {on_ble_evt(p_ble_evt);ble_conn_params_on_ble_evt(p_ble_evt);ble_lbs_on_ble_evt(&m_lbs, p_ble_evt); }最后,雖然我們已經完成了服務的所有驅動代碼以及應用層的注冊,但是我們必須在廣播里面加入服務的UUID。所以在廣播初始化函數里面需要把服務的UUID加進去:
ble_uuid_t adv_uuids[] = {{LBS_UUID_SERVICE, m_lbs.uuid_type}};到這里,才真正完成任務。
結果驗證
- 手機連接藍牙,我們可以看到有一個新增的UUID為1523的服務,在其下包含一個UUID為1525的特制值;
- 1525特征值里包含了read和write兩個屬性;
- 點擊write屬性,往藍牙寫入0x01,LED0亮;往藍牙寫入0x00,LED0熄滅;
總結
通過這個實驗,我們可以了解到如何添加私有服務。其他類似按鍵、串口等服務都可以按照這種方法添加,但如果工作中用到其實我們可以直接在官方的例程上修改即可,但原理我們還是要清楚。
總結
以上是生活随笔為你收集整理的NRF51822蓝牙服务(1)——LED读写的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年需要学习的5个最受欢迎的编程语
- 下一篇: 并查集详解 ——图文解说,简单易懂(转)