nRF52832——基于SDK15.2 加入串口透传服务

版权声明:本文为博主原创文章,未经博主允许不得转载,联系邮箱[email protected] https://blog.csdn.net/w464960660/article/details/85159820

【背景】:项目需求,需要手机和设备进行蓝牙双向数据传输,需要在原工程基础上加入此通信工程,使用的是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;

扫描二维码关注公众号,回复: 4594703 查看本文章

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官网上发了帖子,还有得到回复,有了解的兄弟欢迎留言讨论。

猜你喜欢

转载自blog.csdn.net/w464960660/article/details/85159820