【BLE】CC2541之添加特征值

灰太狼 2022-06-11 03:26 383阅读 0赞

转载:http://blog.csdn.net/feilusia/article/details/48235691

版权声明:喝水不忘挖井人,转载请注明出处,897503845@qq.com

本篇博文最后修改时间:2017年03月21日,15:32。

一、简介

本文以SimpleBLEPeripheral工程为例,介绍如何添加一个可读、可写、可通知、20字节长的特征值char6,并用app实现数据的收发。

二、实验平台

协议栈版本:BLE-CC254x-1.4.0

编译软件: IAR 8.20.2

硬件平台: Smart RF开发板(主芯片CC2541)

手机型号: 小米4S

安卓版本:安卓5.1

安卓app:TruthBlue2_1

三****版权声明

博主:甜甜的大香瓜

声明:喝水不忘挖井人,转载请注明出处。

原文地址:http://blog.csdn[.NET][]/feilusia

联系方式:897503845@qq.com

香瓜BLE之CC2541群:127442605

香瓜BLE之CC2640群:557278427

香瓜BLE之Android群:541462902

香瓜单片机之STM8/STM32群:164311667

香瓜单片机之Linux群:512598061
香瓜单片机之职场交流群:450154342

甜甜的大香瓜的小店(淘宝店):https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i

四、 实验前提

1、在进行本文步骤前,请先 阅读 以下博文:

暂无

2、在进行本文步骤前,请先 实现以下博文:

暂无

五、基础知识

1、特征值是什么?

答:特征值是一个变量或者一个数组,它被定义在从机端,它是主从机之间传输数据的缓冲区。

比如添加一个char6[20],它的值初始化为1、2、3、4、5、6、7、8、9、10、11、12、13、14、15、16、17、18、19、20。

当char6具有读、写属性时,主机可以通过GATT_ReadCharValue、GATT_WriteCharValue进行读、写从机的char6。

当char6具有notify通知属性时,从机可以将char6的值通知给主机。(通知的两种方式可参见本博客的《CC2541的notify》)

六、实验步骤

1、增加char6的宏定义(替换simpleGATTprofile.h中CONSTANTS段部分)

  1. // Profile Parameters
  2. #define SIMPLEPROFILE_CHAR1 0 // RW uint8 - Profile Characteristic 1 value
  3. #define SIMPLEPROFILE_CHAR2 1 // RW uint8 - Profile Characteristic 2 value
  4. #define SIMPLEPROFILE_CHAR3 2 // RW uint8 - Profile Characteristic 3 value
  5. #define SIMPLEPROFILE_CHAR4 3 // RW uint8 - Profile Characteristic 4 value
  6. #define SIMPLEPROFILE_CHAR5 4 // RW uint8 - Profile Characteristic 5 value
  7. #define SIMPLEPROFILE_CHAR6 5 // RW uint8 - Profile Characteristic 6 value //GUA
  8. // Simple Profile Service UUID
  9. #define SIMPLEPROFILE_SERV_UUID 0xFFF0
  10. // Key Pressed UUID
  11. #define SIMPLEPROFILE_CHAR1_UUID 0xFFF1
  12. #define SIMPLEPROFILE_CHAR2_UUID 0xFFF2
  13. #define SIMPLEPROFILE_CHAR3_UUID 0xFFF3
  14. #define SIMPLEPROFILE_CHAR4_UUID 0xFFF4
  15. #define SIMPLEPROFILE_CHAR5_UUID 0xFFF5
  16. #define SIMPLEPROFILE_CHAR6_UUID 0xFFF6 //GUA
  17. // Simple Keys Profile Services bit fields
  18. #define SIMPLEPROFILE_SERVICE 0x00000001
  19. // Length of Characteristic 5 in bytes
  20. #define SIMPLEPROFILE_CHAR5_LEN 5
  21. // Length of Characteristic 6 in bytes
  22. #define SIMPLEPROFILE_CHAR6_LEN 20 //GUA

2、增加char6的UUID(simpleGATTprofile.c的GLOBAL VARIABLES段中)

  1. // Characteristic 6 UUID: 0xFFF6
  2. CONST uint8 simpleProfilechar6UUID[ATT_BT_UUID_SIZE] =
  3. {
  4. LO_UINT16(SIMPLEPROFILE_CHAR6_UUID), HI_UINT16(SIMPLEPROFILE_CHAR6_UUID)
  5. };

将16位的UUID拆成2个字节放到数组里。

3、增加char6的配置属性(simpleGATTprofile.c的Profile Attributes - variables段中)

  1. // Simple Profile Characteristic 6 Properties
  2. static uint8 simpleProfileChar6Props = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_NOTIFY;
  3. // Characteristic 6 Value
  4. static uint8 simpleProfileChar6[SIMPLEPROFILE_CHAR6_LEN] = {0};
  5. // Simple Profile Characteristic 6 Configuration Each client has its own
  6. // instantiation of the Client Characteristic Configuration. Reads of the
  7. // Client Characteristic Configuration only shows the configuration for
  8. // that client and writes only affect the configuration of that client.
  9. static gattCharCfg_t simpleProfileChar6Config[GATT_MAX_NUM_CONN];
  10. // Simple Profile Characteristic 6 User Description
  11. static uint8 simpleProfileChar6UserDesp[17] = "Characteristic 6\0";

由于属性包含GATT_PROP_NOTIFY方式,所以必须要有个通知开关simpleProfileChar6Config。

4、修改属性表

1)修改属性表的大小(simpleGATTprofile.c的CONSTANTS段中)

  1. #define SERVAPP_NUM_ATTR_SUPPORTED 21

增加上面定义的char6的4个属性变量。

2)修改属性表(simpleGATTprofile.c的simpleProfileAttrTbl数组中)

  1. // Characteristic 6 Declaration
  2. {
  3. { ATT_BT_UUID_SIZE, characterUUID },
  4. GATT_PERMIT_READ,
  5. 0,
  6. &simpleProfileChar6Props
  7. },
  8. // Characteristic Value 6
  9. {
  10. { ATT_BT_UUID_SIZE, simpleProfilechar6UUID },
  11. GATT_PERMIT_READ | GATT_PERMIT_WRITE,
  12. 0,
  13. simpleProfileChar6
  14. },
  15. // Characteristic 6 configuration
  16. {
  17. { ATT_BT_UUID_SIZE, clientCharCfgUUID },
  18. GATT_PERMIT_READ | GATT_PERMIT_WRITE,
  19. 0,
  20. (uint8 *)simpleProfileChar6Config
  21. },
  22. // Characteristic 6 User Description
  23. {
  24. { ATT_BT_UUID_SIZE, charUserDescUUID },
  25. GATT_PERMIT_READ,
  26. 0,
  27. simpleProfileChar6UserDesp
  28. },

此处注意两点:

第一点,

读、写属性的只有3个变量,而含有notify属性的特征值会多一个开关config,所以是4个变量。

第二点,

大多数新手搞不清楚特征值属性的“GATT_PROP_READ”与属性表的“GATT_PERMIT_READ”的区别:

打个比方说明,

属性表是一列火车,它有SERVAPP_NUM_ATTR_SUPPORTED这么多节车厢,GATT_PERMIT_READ是每节车厢的钥匙。

此时第18节~21节车厢装的是宝箱char6,GATT_PROP_READ是宝箱char6的钥匙。

虽然两把都是钥匙,但是作用的对象不一样。

实际上GATT_PERMIT_READ是针对属性表使用的,而GATT_PROP_READ是针对特征值使用的。

5、修改特征值的参数函数

1)增加char6的数值可设置的处理(simpleGATTprofile.c的SimpleProfile_SetParameter函数中)

  1. case SIMPLEPROFILE_CHAR6:
  2. if ( len == SIMPLEPROFILE_CHAR6_LEN )
  3. {
  4. VOID osal_memcpy( simpleProfileChar6, value, SIMPLEPROFILE_CHAR6_LEN );
  5. }
  6. else
  7. {
  8. ret = bleInvalidRange;
  9. }
  10. break;

2)增加char6的数值可获取的处理(simpleGATTprofile.c的SimpleProfile_GetParameter函数中)

  1. case SIMPLEPROFILE_CHAR6:
  2. VOID osal_memcpy( value, simpleProfileChar6, SIMPLEPROFILE_CHAR6_LEN );
  3. break;

6、修改特征值的 读写 函数

1)增加char6的数值读取的处理(simpleGATTprofile.c的simpleProfile_ReadAttrCB函数中)

  1. case SIMPLEPROFILE_CHAR6_UUID:
  2. *pLen = SIMPLEPROFILE_CHAR6_LEN;
  3. VOID osal_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR6_LEN );
  4. break;

2) 增加char6的数值写入的处理 simpleGATTprofile.c的simpleProfile_WriteAttrCB函数中

  1. case SIMPLEPROFILE_CHAR6_UUID:
  2. //Validate the value
  3. // Make sure it's not a blob oper
  4. if ( offset == 0 )
  5. {
  6. if ( len != SIMPLEPROFILE_CHAR6_LEN )
  7. {
  8. status = ATT_ERR_INVALID_VALUE_SIZE;
  9. }
  10. }
  11. else
  12. {
  13. status = ATT_ERR_ATTR_NOT_LONG;
  14. }
  15. //Write the value
  16. if ( status == SUCCESS )
  17. {
  18. VOID osal_memcpy( pAttr->pValue, pValue, SIMPLEPROFILE_CHAR6_LEN );
  19. notifyApp = SIMPLEPROFILE_CHAR6;
  20. }
  21. break;

3)**增加通知开关的处理(替换simpleGATTprofile.c的simpleProfile_WriteAttrCB函数中的GATT_CLIENT_CHAR_CFG_UUID部分)**

  1. //通知开关管理
  2. case GATT_CLIENT_CHAR_CFG_UUID:
  3. //CHAR4的通知开关
  4. if(pAttr->handle == simpleProfileAttrTbl[GUA_ATTRTBL_CHAR4_CCC_IDX].handle)
  5. {
  6. status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
  7. offset, GATT_CLIENT_CFG_NOTIFY );
  8. }
  9. //CHAR6的通知开关
  10. else if(pAttr->handle == simpleProfileAttrTbl[GUA_ATTRTBL_CHAR6_CCC_IDX].handle)
  11. {
  12. status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
  13. offset, GATT_CLIENT_CFG_NOTIFY );
  14. }
  15. //其他情况不打开通知开关
  16. else
  17. {
  18. status = ATT_ERR_INVALID_HANDLE;
  19. }
  20. break;

此处非常重要,如果没添加会导致通知开关打不开,以至于从机无法主动发送数据到主机。

4)添加两个通知开关的宏**simpleGATTprofile.c中)**

  1. #define GUA_ATTRTBL_CHAR4_VALUE_IDX 11
  2. #define GUA_ATTRTBL_CHAR4_CCC_IDX 12
  3. #define GUA_ATTRTBL_CHAR6_VALUE_IDX 18
  4. #define GUA_ATTRTBL_CHAR6_CCC_IDX 19

char4与char6为通知属性,因此需要明确该特征值开关的位置。

char4的特征值数据在属性表simpleProfileAttrTbl中的位置为第11位(属性表是数组,从0位开始),因此GUA_ATTRTBL_CHAR4_VALUE_IDX宏定义为11。

char4的特征值开关在属性表simpleProfileAttrTbl中的位置为第12位(属性表是数组,从0位开始),因此GUA_ATTRTBL_CHAR4_CCC_IDX宏定义为12。

char6的特征值数据在属性表simpleProfileAttrTbl中的位置为第18位(属性表是数组,从0位开始),因此GUA_ATTRTBL_CHAR6_VALUE_IDX宏定义为18。

char6的特征值开关在属性表simpleProfileAttrTbl中的位置为第19位(属性表是数组,从0位开始),因此GUA_ATTRTBL_CHAR6_CCC_IDX宏定义为19。

7、**增加char6的通知开关初始化(替换simpleGATTprofile.c的SimpleProfile_AddService函数**)

  1. //******************************************************************************
  2. //name: SimpleProfile_AddService
  3. //introduce: 通过注册GATT属性与GATT服务,从而初始化simple服务
  4. //parameter: services: 服务号
  5. //return: none
  6. //author: 甜甜的大香瓜
  7. //email: 897503845@qq.com
  8. //QQ group 香瓜BLE之CC2541(127442605)
  9. //changetime: 2016.12.08
  10. //******************************************************************************
  11. bStatus_t SimpleProfile_AddService( uint32 services )
  12. {
  13. uint8 status = SUCCESS;
  14. // Initialize Client Characteristic Configuration attributes
  15. GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar4Config );
  16. GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar6Config ); //GUA
  17. // Register with Link DB to receive link status change callback
  18. VOID linkDB_Register( simpleProfile_HandleConnStatusCB );
  19. if ( services & SIMPLEPROFILE_SERVICE )
  20. {
  21. // Register GATT attribute list and CBs with GATT Server App
  22. status = GATTServApp_RegisterService( simpleProfileAttrTbl,
  23. GATT_NUM_ATTRS( simpleProfileAttrTbl ),
  24. &simpleProfileCBs );
  25. }
  26. return ( status );
  27. }

只有通知属性的才需要初始化通知开关属性。

8、增加char6的通知开关初始化的实时更新**(替换simpleGATTprofile.c的simpleProfile_HandleConnStatusCB函数)**

  1. //******************************************************************************
  2. //name: simpleProfile_HandleConnStatusCB
  3. //introduce: simple服务连接状态的改变函数
  4. //parameter: connHandle: 连接句柄
  5. // changeType: 改变类型
  6. //return: none
  7. //author: 甜甜的大香瓜
  8. //email: 897503845@qq.com
  9. //QQ group 香瓜BLE之CC2541(127442605)
  10. //changetime: 2016.12.08
  11. //******************************************************************************
  12. static void simpleProfile_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
  13. {
  14. // Make sure this is not loopback connection
  15. if ( connHandle != LOOPBACK_CONNHANDLE )
  16. {
  17. // Reset Client Char Config if connection has dropped
  18. if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED ) ||
  19. ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&
  20. ( !linkDB_Up( connHandle ) ) ) )
  21. {
  22. GATTServApp_InitCharCfg( connHandle, simpleProfileChar4Config );
  23. GATTServApp_InitCharCfg( connHandle, simpleProfileChar6Config ); //GUA
  24. }
  25. }
  26. }

会根据设备连接状态来改变特征值通知开关的状态。

9、增加char6的发送通知数据的函数

1)定义**char6的发送通知数据的函数**(simpleGATTprofile.c中)

  1. //******************************************************************************
  2. //name: GUA_SimpleGATTprofile_Char6_Notify
  3. //introduce: 发送char6通道的数据
  4. //parameter: nGUA_ConnHandle: 连接句柄
  5. // npGUA_Value: 要通知的数据,范围为0~SIMPLEPROFILE_CHAR6,最多20个字节
  6. // nGUA_Len: 要通知的数据的长度
  7. //return: none
  8. //author: 甜甜的大香瓜
  9. //email: 897503845@qq.com
  10. //QQ group 香瓜BLE之CC2541(127442605)
  11. //changetime: 2016.12.29
  12. //******************************************************************************
  13. void GUA_SimpleGATTprofile_Char6_Notify(uint16 nGUA_ConnHandle, uint8 *pGUA_Value, uint8 nGUA_Len)
  14. {
  15. attHandleValueNoti_t stGUA_Noti;
  16. uint16 nGUA_Return;
  17. //读出CCC的值
  18. nGUA_Return = GATTServApp_ReadCharCfg(nGUA_ConnHandle, simpleProfileChar6Config);
  19. //判断是否打开通知开关,打开了则发送数据
  20. if (nGUA_Return & GATT_CLIENT_CFG_NOTIFY)
  21. {
  22. //填充数据
  23. stGUA_Noti.handle = simpleProfileAttrTbl[GUA_ATTRTBL_CHAR6_VALUE_IDX].handle;
  24. stGUA_Noti.len = nGUA_Len;
  25. osal_memcpy(stGUA_Noti.value, pGUA_Value, nGUA_Len);
  26. //发送数据
  27. GATT_Notification(nGUA_ConnHandle, &stGUA_Noti, FALSE);
  28. }
  29. }

注意,本函数仅适用于协议栈1.3.2和1.4.0版本。

1.4.2版本的attHandleValueNoti_t 结构体发生变化,需要多一条分配发送数据缓冲区的代码。可以参考《CC2640之自定义服务》的notify代码(不一定完全一样):

  1. //分配发送数据缓冲区
  2. stGUA_Noti.pValue = GATT_bm_alloc(nGUA_ConnHandle, ATT_HANDLE_VALUE_NOTI, GUAPROFILE_CHAR6_LEN, NULL);

2)声明 char6的发送通知数据的函数 (simpleGATTprofile.h中)

  1. extern void GUA_SimpleGATTprofile_Char6_Notify(uint16 nGUA_ConnHandle, uint8 *pGUA_Value, uint8 nGUA_Len);

10、应用层修改

1)修改特征值初始化的数值(simpleBLEPeripheral.c的SimpleBLEPeripheral_Init函数中)

  1. // Setup the SimpleProfile Characteristic Values
  2. {
  3. uint8 charValue1 = 1;
  4. uint8 charValue2 = 2;
  5. uint8 charValue3 = 3;
  6. uint8 charValue4 = 4;
  7. uint8 charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };
  8. uint8 charValue6[SIMPLEPROFILE_CHAR6_LEN] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
  9. SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR1, sizeof ( uint8 ), &charValue1 );
  10. SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR2, sizeof ( uint8 ), &charValue2 );
  11. SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR3, sizeof ( uint8 ), &charValue3 );
  12. SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, sizeof ( uint8 ), &charValue4 );
  13. SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5 );
  14. SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR6, SIMPLEPROFILE_CHAR6_LEN, charValue6 );
  15. }

2)修改应用层的回调函数(simpleBLEPeripheral.c的simpleProfileChangeCB函数中)

  1. //******************************************************************************
  2. //name: simpleProfileChangeCB
  3. //introduce: simple服务的回调函数
  4. //parameter: paramID: 特征值ID
  5. //return: none
  6. //author: 甜甜的大香瓜
  7. //email: 897503845@qq.com
  8. //QQ group 香瓜BLE之CC2541(127442605)
  9. //changetime: 2016.12.08
  10. //******************************************************************************
  11. static void simpleProfileChangeCB( uint8 paramID )
  12. {
  13. uint16 nGUA_ConnHandle;
  14. uint8 nbGUA_Char6[20] = {0};
  15. switch( paramID )
  16. {
  17. //char1
  18. case SIMPLEPROFILE_CHAR1:
  19. {
  20. break;
  21. }
  22. //char3
  23. case SIMPLEPROFILE_CHAR3:
  24. {
  25. break;
  26. }
  27. //char6
  28. case SIMPLEPROFILE_CHAR6:
  29. {
  30. //获取连接句柄
  31. GAPRole_GetParameter(GAPROLE_CONNHANDLE, &nGUA_ConnHandle);
  32. //读取char6的数值
  33. SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR6, &nbGUA_Char6);
  34. //发送数据
  35. GUA_SimpleGATTprofile_Char6_Notify(nGUA_ConnHandle, nbGUA_Char6, 20);
  36. break;
  37. }
  38. default:
  39. {
  40. break;
  41. }
  42. }
  43. }

七、注意事项

手机可能缓存了之前的代码(在更新过CC2541的代码之后,都需要清除手机端的缓存!!!),因此要清除缓存,清除缓存的方法如下:

方法一:关闭app、关闭蓝牙总开关、打开蓝牙总开关、打开app。
方法二:手机重启。

八、实验结果

1、仿真并全速运行工程。

2、用安卓手机的TruthBlue2_1扫描并连接设备,可发现新增的特征值char6

20161208122733966

3、数据通信过程如下图

20161208122759252

①红框为app主动读取到的数值,为默认的1~20(hex显示)。

②蓝框为app主动写入cc2541的数值。

③紫框为cc2541接收到app的数值后,再将char6的数值通过通知发送出来,可见当前char6的数值已被app改变。

因此,实验成功。

发表评论

表情:
评论列表 (有 0 条评论,383人围观)

还没有评论,来说两句吧...

相关阅读

    相关 学习CC2541开发笔记

    硬件部分 首先,要熟悉了解现有的开发板模块都有什么功能,硬件其实没有太多问题,各个预留出来的端点知道是干什么的就好。毕竟是已经经过试验之后的成品电路板,本身没有问题。那么

    相关 数字特征值

    数字特征值 题目内容: 对数字求特征值是常用的编码算法,奇偶特征是一种简单的特征值。对于一个整数,从个位开始对每一位数字编号,个位是1号,十位是2号,以此类推。这个整数

    相关 特征值预处理

    一.归一化的概念     1.概念 特点: 通过对原始数据的变换映射到默认为[0,1]之间 目的:是的某一特征值不会对结果造成更大的