1. 程式人生 > >nRF52832——基於SDK15.2 加入串列埠透傳服務

nRF52832——基於SDK15.2 加入串列埠透傳服務

【背景】:專案需求,需要手機和裝置進行藍芽雙向資料傳輸,需要在原工程基礎上加入此通訊工程,使用的是SDK15.2,而官方原始碼已經帶有使用的藍芽串列埠透傳例程,所以參照此例程,移植到現有的工程中即可。但是移植過程卻遇到非常蛋疼的事。

【移植要點】:下面主要提及要點,與SDK12.3的差異;

1、加入串列埠驅動相關

關鍵的是串列埠事件處理回撥函式當串列埠收到資料後,BLE傳送給主機,即手機

//uart事件處理回撥函式
void uart_event_handle(app_uart_evt_t * p_event)
{	
		static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
		static uint8_t index = 0;
		uint32_t       err_code;

    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:  //串列埠接收資料事件
            UNUSED_VARIABLE(app_uart_get(&data_array[index]));
            index++;
//判斷資料是否接收完成,判斷條件:資料長度達到20位元組或者接收到"\n"
            if ((data_array[index - 1] == '\n') ||
                (data_array[index - 1] == '\r') ||
                (index >= m_ble_nus_max_data_len))
            {
                if (index > 1)
                {

                    do
                    {
                        uint16_t length = (uint16_t)index;
                        err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
                        if ((err_code != NRF_ERROR_INVALID_STATE) &&
                            (err_code != NRF_ERROR_RESOURCES) &&
                            (err_code != NRF_ERROR_NOT_FOUND))
                        {
                            APP_ERROR_CHECK(err_code);
                        }
                    } while (err_code == NRF_ERROR_RESOURCES);
                }

                index = 0;
            }
            break;

        case APP_UART_COMMUNICATION_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        default:
            break;
    }
			
}
/****************************************************************************************
 * 描  述 : uart初始化配置函式。波特率115200bps,流控關閉。
 * 入  參 : 無
 * 返回值 : 無
 ***************************************************************************************/ 
void uart_config(void)
{
    uint32_t err_code;
	    app_uart_comm_params_t const comm_params =
    {
        .rx_pin_no    = RX_PIN_NUMBER,
        .tx_pin_no    = TX_PIN_NUMBER,
        .rts_pin_no   = RTS_PIN_NUMBER,
        .cts_pin_no   = CTS_PIN_NUMBER,
        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity   = false,
#if defined (UART_PRESENT)
        .baud_rate    = NRF_UART_BAUDRATE_115200
#else
        .baud_rate    = NRF_UARTE_BAUDRATE_115200
#endif
    };

    //初始化app uart,註冊uart事件回撥函式
    APP_UART_FIFO_INIT(&comm_params,
                         UART_RX_BUF_SIZE,
                         UART_TX_BUF_SIZE,
                         uart_event_handle,
                         APP_IRQ_PRIORITY_LOWEST,
                         err_code);

    APP_ERROR_CHECK(err_code);	
}

2、加入串列埠透傳服務和特徵

1)所需的工程檔案:...components\ble\ble_services目錄下的ble_nus資料夾(注意帶_c的表示主機),將其中的.c   .h檔案加入到自己的工程中,另外SDK15.2 的ble_nus.c中還使用到了ble_link_ctx_manager,在目錄...\components\ble\ble_link_ctx_manager下,因此也需要加入到工程中;

2)程式中加入的要點:

a、定義結構體變數m_nus,即串列埠透傳的例項,在應用程式中通過m_us就可以操作相關透傳服務,這個定義方法和SDK12.3中不一樣,SDK15.2中,nus的事件處理函式ble_nus_on_ble_evt在定義m_nus實體的時候就相當於註冊了,SDK15.2中沒有ble_evt_dispatch這樣的事件派發函式,不需要往這個函式中單獨加nus的事件處理函式,這個還是很方便的;

BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT);                   /**< BLE NUS service instance. */
b、初始化服務

在main.c中 services_init函式中加入串列埠透傳服務,這裡要注意的是該服務屬於自定義服務,需要定義UUID,這個服務中包含兩個特徵 傳送和接收,所以,定義串列埠透傳服務UUID 0X0001,兩個特徵:

Rx:有notify屬性,裝置收到串列埠資料後,將資料傳送給主機,UUID 0X0002;

Tx:有write屬性,主機通過write該特徵值將資料發給裝置,UUID 0X0003;

這裡注意,新增自定義服務後,還需要在sdk_config.h修改NRF_SDH_BLE_VS_UUID_COUNT的值,加幾個自定義服務,這個值就是多少,同時加後若程式執行提示mem空間不足,需要更改target下的ram空間起始地址,一個service 多佔用0x10空間;

static void services_init(void)
{
    ret_code_t         err_code;
    ble_dis_init_t     dis_init;
    nrf_ble_qwr_init_t qwr_init = {0};
	ble_nus_init_t     nus_init;
    uint8_t            body_sensor_location;

    // Initialize Queued Write Module.
    qwr_init.error_handler = nrf_qwr_error_handler;

    err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
    APP_ERROR_CHECK(err_code);

    // Initialize Device Information Service.
    memset(&dis_init, 0, sizeof(dis_init));

    ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, (char *)MANUFACTURER_NAME);

    dis_init.dis_char_rd_sec = SEC_OPEN;

    err_code = ble_dis_init(&dis_init);
    APP_ERROR_CHECK(err_code);
		
		
	 // Initialize NUS.
    memset(&nus_init, 0, sizeof(nus_init));

    nus_init.data_handler = nus_data_handler;

    err_code = ble_nus_init(&m_nus, &nus_init);
    APP_ERROR_CHECK(err_code);
		
}

c、編寫nus_data_handler事件處理函式:

由上節中可以看到,初始化串列埠透傳服務,需要提供一個事件控制代碼,用於處理BLE接收的資料,此處編寫該控制代碼函式,將接受的資料通過串列埠列印:

static void nus_data_handler(ble_nus_evt_t * p_evt)
{

    if (p_evt->type ==BLE_NUS_EVT_RX_DATA )    //  p_evt->type ==BLE_NUS_EVT_COMM_STARTED
    {
        uint32_t err_code;

        NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
        NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);

        for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
        {
            do
            {
                err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
                if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
                {
                    NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
                    APP_ERROR_CHECK(err_code);
                }
            } while (err_code == NRF_ERROR_BUSY);
        }
        if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r')
        {
            while (app_uart_put('\n') == NRF_ERROR_BUSY);
        }
    }

}

d、因SDK15.2支援長包傳輸,若想加入此功能,在gatt_init();中設定MTU,NRF_SDH_BLE_GATT_MAX_MTU_SIZE最大可設定為247,單次傳輸 實際資料長度244位元組;

/**@brief Function for handling events from the GATT library. */
void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
{
    if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED))
    {
        m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
        NRF_LOG_INFO("Data len is set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len);
    }
    NRF_LOG_DEBUG("ATT MTU exchange completed. central 0x%x peripheral 0x%x",
                  p_gatt->att_mtu_desired_central,
                  p_gatt->att_mtu_desired_periph);
}

/**@brief Function for initializing the GATT module. */
static void gatt_init(void)
{
    ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
    APP_ERROR_CHECK(err_code);
	    err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
    APP_ERROR_CHECK(err_code);
	
}

e、非常關鍵的一點,在main.c中,加入自定義串列埠透傳服務 services_init(); 一定要放在 advertising_init();前面,否則程式會出現意想不到的錯誤~現在原理我也沒弄明白, 在NORDIC官網上發了帖子,還有得到回覆,有了解的兄弟歡迎留言討論。