去年Noridc出了一款集成蓝牙4.0和可以跟24L01通信的芯片,片子的封装可以做到很小,而且功耗非常低,非常适合做穿戴设备,于是最近有空掏了一套二手的AK II,又没了一个Becon的板子,先玩了一些基本的外设,现在开始玩了BLE的相关开发。
在官方的SDK目录下,我们可以找到Nordic\nrf51822\Board\pca10001\ble\experimental\ble_app_uart这个工程文件,另外SDK文档中也有简单介绍BLE UART数据转发的应用。
该应用包括一个服务,即 Nordic UART Service (UUID: 0x0001). The UUID of the Nordic UART Service is 6E400001-B5A3-F393-E0A9-E50E24DCCA9E.
该服务包括两个特征,一个用于接收,另一个用于发送:
TX Characteristic (UUID: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E):
When the peer has enabled notification for the Tx Characteristic, the application can send data to the peer as notifications. The application will transmit all data received over UART as notifications.
RX Characteristic (UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E):
Peer can start sending data to the device by writing to the Rx Characteristic of the service. ATT Write Request or ATT Write Command can be used. The data received is sent on the UART interface.
简单介绍下重要的代码:
增加专有服务(proprietary service)和特征(characteristic)
专有服务和特征的初始化操作室在 ble_nus.c,Nordic UART Service 增加到S110 SoftDevice 的过程如下:
err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_nus->uuid_type);
if (err_code !=NRF_SUCCESS)
{
return err_code;
}
ble_uuid.type = p_nus->uuid_type;
ble_uuid.uuid = BLE_UUID_NUS_SERVICE;
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_nus->service_handle);
RX 特征增加到SoftDevice 的过程如下,需要注意的是,特征的read和write权限和它的CCCD被设置为open,这意味着对特征没有安全方面的限制,同时也要注意UUID的类型 (ble_uuid.type)的值是通过调用sd_ble_uuid_vs_add()获取的,TX特征也是用同样的方式增加:
ble_gatts_char_md_t char_md;
ble_gatts_attr_md_t cccd_md;
ble_gatts_attr_t attr_char_value;
ble_uuid_t ble_uuid;
ble_gatts_attr_md_t attr_md;
memset(&cccd_md, 0, sizeof(cccd_md));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
cccd_md.vloc = BLE_GATTS_VLOC_STACK;
memset(&char_md, 0, sizeof(char_md));
char_md.char_props.notify = 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 = &cccd_md;
char_md.p_sccd_md = NULL;
ble_uuid.type = p_nus->uuid_type;
ble_uuid.uuid = BLE_UUID_NUS_RX_CHARACTERISTIC;
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 = 1;
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 = BLE_NUS_MAX_RX_CHAR_LEN;
returnsd_ble_gatts_characteristic_add(p_nus->service_handle,
&char_md,
&attr_char_value,
&p_nus->rx_handles);
UART初始化
所有跟初始化相关,通过BLE和UART数据接收和发送都是在main.c中实现的。
UART初始化操作如下,SDK中提供了UART的驱动的相关代码,可以执行UART配置。需要注意的是simple_uart_config函数的最后一个参数HWFC表示硬件流控使能,也就是说RTS和CTS被使用,接下来,使能了UART中断。
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);
NVIC_EnableIRQ(UART0_IRQn);
处理通过BLE接收到的数据
在services_init()函数中初始化service,应用程序通过nus_data_handler函数用于处理通过BLE接收到的数据,这个时候Nordic UART Service会指示已经存在有效的来自于配对好的设备的数据,这些数据会转发到UART,相关代码如下:
void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length)
{
for (int i = 0; i < length; i++)
{
simple_uart_put(p_data[i]);
}
simple_uart_put(‘\n‘);
}
处理通过UART接收到的数据
来自UART的数据在转发到BLE配对的设备之前,会先通过某些检查,下面的代码是在UART中断函数,通过UART接收到的数据之后都会调用该中断,这些接收到的字符首先会缓存到一个数组中,如果接收到一个‘new line‘(回车符)或者超过了NUS_MAX_DATA_LENGTH的长度,都将调用ble_nus_send_string 通过BLE发送出去。
注意:默认情况下NUS_MAX_DATA_LENGTH被设置为最大的值,这也是notification包最大的长度,例如:BLE_ATT_MTU - 3, 不能再变更长。
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;
}
按键和LED灯分配:
LED0:Advertising,设备的名称可以被配对的设备显示,名称为‘Nordic_UART‘,在未连接之前会一直闪烁。
LED1:Connected,连接后LED0灭,LED1亮
Button 0 :在系统off模式的时候,按下该按键会唤醒系统,开始Advertising
测试的硬件环境:
红米1S电信版,AK II,Jlink。
上几个测试通过的图片:
串口发送和接收:
安卓4.3,nRF UART软件连接,发送接收数据: