小钢炮CANNON
直播中

icode

9年用户 16经验值
擅长:电源/新能源 MEMS/传感技术 嵌入式技术 制造/封装 模拟技术 存储技术 处理器/DSP 控制/MCU RF/无线 电源/新能源 MEMS/传感技术 嵌入式技术 制造/封装 模拟技术 存储技术 处理器/DSP 控制/MCU RF/无线
私信 关注
[经验]

【CANNON试用体验】 - 写自定义的profile

本帖最后由 icode 于 2016-3-7 12:06 编辑

     工欲善其事,必先利其器。所以俺在趁着活动弄了一块板,首先谢谢取码,好了,话不多说,言归正值传!
     如果想了解BLE,当前蓝牙标准是不能不看的,现在最新的蓝牙标准是4.2,可以到度娘搜一下,当然2千多页的英文文档,也挺考验我们的耐心与英文水平,但好在里面的英文都比较简单,而且,你只需要看你需要的部分就可以了,好吧,关于蓝牙标准的问题,就先谈到这吧,如果您对蓝牙标准哪个地方有不了解的可以找我讨论,毕竟,俺还接触过有几年了。
     蓝牙开发中,最基本的就是配置文件profile,有了这个profile,主机从机才能相互打招乎,才能知道怎么个礼尚往来,就像USB协议中的各种枚举类型一样,当然您一时闭会不理解这个profile是什么东西也没关系,等到你跑一两个例程,你就深谙其事了。关于GATT 的profile可以参看core4.2文档中的第3章《Vol3|:Core System Package[Host Volum]》中的partG:Generic Attrivute Profile这一部分有详细的讲解,请您有空了一定要耐着性子去看。
  好吧,正面对着代码慢慢说: 首先建个自己的C文件,例如sample_service.c,sample_service.h,名字可以自己定义,
/********************************************************************************************
文件名称 : ample_service.c
修订信息 : 201634 09:15:18 By : Mark Xu
软件版本 : R00
文件描述 : 自定义的配置文件
Copyright 2005 - 2016 Mark Xu . All Rights Reserved.
-------------------------------------------------------------------------------------------------
注意事项: 1)...
修改记录 : 1)...
********************************************************************************************/
/* ------------------------------------------------------------------------------------------------
*
头文件
*-----------------------------------------------------------------------------------------------*/
#include "sample_service.h"
#include "connection_config.h"
/* ------------------------------------------------------------------------------------------------
*
全局变量
*------------------------------------------------------------------------------------------------
*/
extern BLE_RoleTypeDef BLE_Role;
/*------------------------------------------------------------------------------------------------
* 私有变量
*------------------------------------------------------------------------------------------------
*/
volatile int connected = FALSE;
volatile uint8_t set_connectable = 1;
volatile uint16_t connection_handle = 0;
volatile uint8_t notification_enabled = FALSE;
uint16_t sampleServHandle, TXCharHandle, RXCharHandle;

// 自己定义的服务 UUID
// uuid="7aac6ac0-afca-11e1-9feb-0002a5d5c51b"
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
const uint8_t service_uuid[16] =
{
0x1b,0xc5,0xd5,0xa5,0x02,0x00,0xeb,0x9f,0xe1,0x11,0xca,0xaf,0xc0,0x6a,0xac,0x7a
};

// 通过该 UUID,把数据发送给主机
const uint8_t charUuidTX[16] =
{
// uuid="aed04e80-afc9-11e1-a484-0002a5d5c51b"
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
0x1b,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xa4,0xe1,0x11,0xc9,0xaf,0x80,0x4e,0xd0,0xae
};

// 通过该 UUID接收来自己主机的数据
const uint8_t charUuidRX[16] =
{
// uuid="19575ba0-b20d-11e1-b0a5-0002a5d5c51b"
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
0x1b,0xc5,0xd5,0xa5,0x02,0x00,0xa5,0xb0,0xe1,0x11,0x0d,0xb2,0xa0,0x5b,0x57,0x19
};
#define COPY_UUID_128(uuid_struct,uuid_15, uuid_14, uuid_13, uuid_12, uuid_11, uuid_10, uuid_9, uuid_8, uuid_7,uuid_6, uuid_5, uuid_4, uuid_3, uuid_2, uuid_1, uuid_0)
do {
uuid_struct.uuid128[0] = uuid_0; uuid_struct.uuid128[1] = uuid_1;uuid_struct.uuid128[2] = uuid_2; uuid_struct.uuid128[3] = uuid_3;
uuid_struct.uuid128[4] = uuid_4; uuid_struct.uuid128[5] = uuid_5;uuid_struct.uuid128[6] = uuid_6; uuid_struct.uuid128[7] = uuid_7;
uuid_struct.uuid128[8] = uuid_8; uuid_struct.uuid128[9] = uuid_9;uuid_struct.uuid128[10] = uuid_10; uuid_struct.uuid128[11] = uuid_11;
uuid_struct.uuid128[12] = uuid_12; uuid_struct.uuid128[13] = uuid_13;uuid_struct.uuid128[14] = uuid_14; uuid_struct.uuid128[15] = uuid_15;
}while(0)
此处说一下,BLE支持16位的UUID与128位的UUID,关于UUID,16位的UUID有些已被 标准的配置占用了,比如心率,电池等,这个可以在蓝牙标准组织的网站上可以查看到,128位的UUID可以在网上直接生成,直接在度娘写入关键写“UUID在线生成”,立马会生成一坨(好吧,希望你不是在吃饭时看到该帖子的)……
//设置广播间隔 320*0.625ms = 200ms
#define MIN_DEF_ADVERTISING_INTERVAL 320
#define MAX_DEF_ADVERTISING_INTERVAL 336
理论上广播间隔可以20ms到10.24s之间,但是也要参看手机方面的要求,太快或太慢,手机端都收不到,这个是手机操作系统有关,关于手机方面的要求,可以参看IOS及android方面的BLE编程要求,这里不做讨论,关于蓝牙标准上定义如下 所示,如果你读的蓝牙标准不是4.2.那么你可以在PDF中搜索关键字“Advertising Interval”,在这里,俺设置为200ms:
/* 应答数组
GAP - SCAN RSP data (max size = 31 bytes)
*/
const uint8_t scanRspData[] =
{
// complete name
0x09, // length of first data structure (9 bytes excluding length byte)
AD_TYPE_COMPLETE_LOCAL_NAME, // AD Type = Complete local name 0x09
'L', // -- 1
'O', // -- 2
'V', // -- 3
'E , // -- 4
' ', // -- 5
'B', // -- 6
'L', // -- 7
'E', // -- 8

};

关于应答数组,简单的说就是A机在广播,B机发送扫描请求,A机说可以,于是发给B机一个应答数组,
其最大长度是31个字节,数组的内容,可以是设备的名称,制造商信息,广播强度等,这个没有强制的要求,但对数组的结构有要求,关于数组的结构可以参看蓝牙标准《Vol3:Core System Package[Host volume]》-->--><11 Advertising and Scan Responese Data Formate>,如果蓝牙标准版本不一样,可以在pdf中搜索关键字“Scan data formate”,也希望您在有空的时候仔细把这一章节好好读几遍,书读百篇,其意自现,当然最重要的还是看源码,毕竟应用为主^_^
// 定义 设备名称
const char local_name[] =
{
AD_TYPE_COMPLETE_LOCAL_NAME,
'L', // -- 1
'O', // -- 2
'V', // -- 3
'E , // -- 4
'  ', // -- 5
'B', // -- 6
'L', // -- 7
'E', // -- 8
};
uint8_t manuf_data[]=                        /* 制造商信息,此处为测试用,所以定义为123*/                  
{
  4,                                    //
AD_TYPE_MANUFACTURER_SPECIFIC_DATA,     // 0
0x31,        // 1
  0x32,      // 2
  0x33,      //3                     
};
下面就要创建我们的服务文件:
/**************************************************************************************************
* 文件名称: Add_Sample_Service
*
* 函数功能: 添加指定的文件
*
* 入口参数: None
*
* 出口参数: tBleStatus - BLE_STATUS_SUCCESS 添加 成功 BLE_STATUS_ERROR添加失败
*
* 备 注:
**************************************************************************************************/
tBleStatus Add_Sample_Service(void)
{
tBleStatus ret;

ret = aci_gatt_add_serv(UUID_TYPE_128, service_uuid, PRIMARY_SERVICE, 7,&sampleServHandle);
if (ret != BLE_STATUS_SUCCESS) goto fail;

ret = aci_gatt_add_char(sampleServHandle, UUID_TYPE_128, charUuidTX, 20,(CHAR_PROP_NOTIFY|CHAR_PROP_READ), ATTR_PERMISSION_NONE, 0,
16, 1, &TXCharHandle);
if (ret != BLE_STATUS_SUCCESS) goto fail;

ret = aci_gatt_add_char(sampleServHandle, UUID_TYPE_128, charUuidRX, 20,CHAR_PROP_WRITE|CHAR_PROP_WRITE_WITHOUT_RESP, ATTR_PERMISSION_NONE,GATT_NOTIFY_ATTRIBUTE_WRITE,
16, 1, &RXCharHandle);
if (ret != BLE_STATUS_SUCCESS) goto fail;

PRINTF("Sample Service added.nTX Char Handle %04X, RX Char Handle%04Xn", TXCharHandle, RXCharHandle);
return BLE_STATUS_SUCCESS;

fail:
PRINTF("Error while adding Sample Service.n");
return BLE_STATUS_ERROR ;
}
    关于添加服务的函数“aci_gatt_add_serv”可以在“bluenrg_gatt_aci.h”中找到,也有相应的说明,这里就不啰嗦了
关于添加服务的函数“ aci_gatt_add_char”,是添加character的函数,可以在“bluenrg_gatt_aci.h”中找到,也有相应的说明。
这里提醒一下,关于character的属性,请参考蓝牙标准《Volum3 - Core System Package [Hostvolume]》 -->-- ><3.3.1.1 Characteristic Properties>,此处做为测试,我设置发的UUID的属性为notify,这样,只要主机不需要实时查询,只要从机数据更改,就可以直接发送给主机



回帖(6)

icode

2016-3-7 10:29:44
/**************************************************************************************************
* 函数名称 Make_Connection
*
* 函数功能 建立连接
*
* 入口参数 None
*
* 出口参数 None
*
* 备 注:
**************************************************************************************************/
void Make_Connection(void)
{
tBleStatus ret;

tBDAddr bdaddr = {0xaa, 0x00, 0x00, 0xE1, 0x80, 0x02}; // 自己定义的MAC地址

if(BLE_Role == CLIENT)
{
ret = aci_gap_create_connection(SCAN_P, SCAN_L, PUBLIC_ADDR, bdaddr, PUBLIC_ADDR, CONN_P1, CONN_P2, 0,
SUPERV_TIMEOUT, CONN_L1 , CONN_L2);


if (ret != 0){
PRINTF("Error while starting connection.n");
Clock_Wait(100);
}

}
else
{
    /*
Advertising_Event_Type, Adv_Interval_Min, Adv_Interval_Max, Address_Type, Adv_Filter_Policy,
Local_Name_Length, Local_Name, Service_Uuid_Length, Service_Uuid_List, Slave_Conn_Interval_Min,
Slave_Conn_Interval_Max
*/
ret = aci_gap_set_discoverable(ADV_IND, 0, 0, PUBLIC_ADDR, NO_WHITE_LIST_USE,
                                                   sizeof(local_name), local_name,
                                              0, NULL, 0, 0);
PRINTF("%dn",ret);

hci_le_set_scan_resp_data(sizeof(scanRspData),scanRspData); /* respone data */

ret = aci_gap_update_adv_data(sizeof(manuf_data), manuf_data); /* 把制造商信息也广播出来 */
}
}
这一个函数就是添加应答数据,广播内容,也就是手机端能搜索到的设备的名称,制造商芯片等,到此,如果你在主程序中初始化可以的话,一个profile就OK了,光有 这个还不行,还要能收发数据,所以还要再添加几个函数:
举报

icode

2016-3-7 10:29:45
/**************************************************************************************************
*  函数名称:   receiveData
*
* 函数功能:   接收数据,并处理接收到的数据
*
* 入口参数:   data_buffer - 接收数组的缓存
*
*                      Nb_bytes - 要接收的数据的个数
*
*  出口参数 :None
*
* 备        注:当接收到数据时, 该函数会被调用
**************************************************************************************************/
void receiveData(uint8_t* data_buffer, uint8_t Nb_bytes)
{
BSP_LED_Toggle(LED2);
resolve_rev_data(data_buffer); /* 算是接收到的数据 */
}
如果想发送数据,请调用以下函数|:
/**************************************************************************************************
*  函数名称:   sendData
*
*  函数功能: 发送数据
*
*  入口参数:   data_buffer - 发关数据缓冲数组
*
*                         Nb_bytes - 要发送的数据的个数
*
*  出口参数 :  None
*
*  备      注 :   None
**************************************************************************************************/
void sendData(uint8_t* data_buffer, uint8_t Nb_bytes)
{
if(BLE_Role == SERVER)
{
aci_gatt_update_char_value(sampleServHandle,TXCharHandle, 0, Nb_bytes, data_buffer);
}
else
{
aci_gatt_write_without_response(connection_handle, RX_HANDLE+1, Nb_bytes, data_buffer);
}
}

/**************************************************************************************************
*  函数名称 :   Attribute_Modified_CB
*
*   函数功能 :  当参数有变化时,该函数会被调
*
*    入口参数 :   handle - 连接句柄
*
*                          data_length - 改变的数据长度
*
*                          att_data - 数据缓存
*
*  出口参数 :   None
*
*  备       注 :  
**************************************************************************************************/
void Attribute_Modified_CB(uint16_t handle, uint8_t data_length, uint8_t *att_data)
{
if(handle == RXCharHandle + 1){
receiveData(att_data, data_length);
} else if (handle == TXCharHandle + 2) {
if(att_data[0] == 0x01)
notification_enabled = TRUE;
}
}
/**************************************************************************************************
*  函数名称 :  GAP_ConnectionComplete_CB
*
* 函数功能 :当设备连接上以后,该函数会被回调   
*
*  入口参数 :   addr : 连接的设备的地址
*
*                        handle : 连接句柄
*
*  出口参数  :   None
*
*   备       注  :
**************************************************************************************************/
void GAP_ConnectionComplete_CB(uint8_t addr[6], uint16_t handle)
{
connected = TRUE;
connection_handle = handle;

PRINTF("Connected to device:");
for(int i = 5; i > 0; i--)
{
PRINTF("%02X-", addr[i]);
}
PRINTF("%02Xn", addr[0]);
}
/**************************************************************************************************
*  函数名称  :   GAP_DisconnectionComplete_CB
*
*  函数功能  :   当设备断开以后,该函数会被调用.
*
* 入口参数 :   None
*
*出口参数 :   None
*
* 备  注 :   
**************************************************************************************************/
void GAP_DisconnectionComplete_CB(void)
{
connected = FALSE;

PRINTF("Disconnectedn");
/* 如果是主机,设置成重新连接 */
set_connectable = TRUE;
notification_enabled = FALSE;

/* 重新开始广播,用户也可以不让它广播 */
hci_le_set_advertise_enable(set_connectable);
}
举报

icode

2016-3-7 10:29:46
受到警告
提示: 作者被禁止或删除 内容自动屏蔽
举报

icode

2016-3-7 10:29:47

本帖最后由 icode 于 2016-3-7 11:23 编辑

好吧,至此,一个小小的自定义的profile配置完成了,您可以再写个主函数,把相应的功能初始化一下,就可以通讯了,或者再加一个串口,就可以成为透传模块了。

俺知道,我比较懒,在这个帖子中说的比较少,贴的比较多,但是这个入门性的东西,不是一句两句能讲清楚 的,俺也只是来个抛砖引玉,希望大家多多努力,最后,俺要说,多读,多写,多练,才是学习的关键!
俺知道,我比较懒,在这个帖子中说的比较少,贴的比较多,但是这个入门性的东西,不是一句两句能讲清楚 的,俺也只是来个抛砖引玉,希望大家多多努力,最后,俺要说,多读,多写,多练,才是学习的关键!

本文为Mark Xu,原创文章,转载请注明!
另个,您也可以在群里找 大炮  探讨技术^_^
记得,此人是我^_^
QQ截图20160307103054.png
最后吼一下,俺一惯的口号:
每天进步一点点,开心多一点!
举报

更多回帖

发帖
×
20
完善资料,
赚取积分