目录

  • 前言:
  • 资料:
  • 一、main函数:
  • 二、app任务初始化:
  • 三、app 任务中的事件处理:
    • 3.1、事件
    • 3.2、任务处理
    • 3.3、任务间的消息
    • 3.4、发送到消息队列
    • 3.5、任务内部事件
    • 3.6、回调函数:
  • 四、蓝牙:
    • 4.1、发送蓝牙数据:
      • 4.1.1、主机向从机发送数据:
      • 4.1.2、从机向主机发送数据:
    • 4.2、接收蓝牙数据
      • 4.2.1、从机接受
      • 4.2.2、主机接受
    • 4.3、蓝牙profile:
      • 4.3.1、simpleprofile:
        • 1、定义特征属性表,在属性表中,设定特征和属性相关内容。
        • 2、定义特征值设置和读取函数:
        • 3、定义特征和属性的 读写过程
        • 4、设置回调函数
    • 4.4、ICALL BLE5模块:
  • 五、用户数据存储:
  • 六、CCFG
  • 七、动态内存:

本系列文章由江山(csdn名:补不补布)(github:jianggogogo)自己写成,当中用到引用时都已经标记出来,如果出现版权问题,请直接联系我修改。当然,技术在于分享,欢迎大家转载,不过请注明出处。最后,如果出现有错误的地方欢迎大家指正。

前言:

本篇文章,基于谷雨的开发手册,主要是在原来的基础上加入自己的理解:
看一看cc2640R2f的代码框架,从大体上来看函数到底是怎么样子一个结构。

资料:

1、蓝牙的笔记

一、main函数:

作为入口函数,首先找到位置,位于startup下面的main.c:

/*******************************************************************************int main()
{/*注册断言回调函数*/RegisterAssertCback(AssertHandler);/*初始化硬件gpio口*/PIN_init(BoardGpioInitTable);/* Initialize ICall module *//*初始化软件模块,用于app和stack之间的通信*/ICall_init();/*创建任务*/ICall_createRemoteTasks();/*GAPRole管理蓝牙设备角色等事务,*/GAPRole_createTask();/*最后创建的任务是app ,也就是 SimpleBLEPeripheral Task*/SimpleBLEPeripheral_createTask();/* enable interrupts and start SYS/BIOS */BIOS_start();return 0;
}

二、app任务初始化:

/*********************************************************************
static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1)
{//app任务初始化函数,为任务分配服务SimpleBLEPeripheral_init();// Application main loopfor (;;){//一个用于任务的循环
}
}

App 任务初始化函数一般需要做下列几件事情:
第 65 页 / 共 177 页
 ICall_registerApp注册,必须首先调用
 设置 GAP 层 ,例如广播间隔等参数
 设置 GAPRole ,例如是否开启广播等、连接参数等
 设置绑定管理器,例如是否开启绑定,以及绑定模式。
 添加 Profile ,并注册 Profile 回调函数
 启动设备
 其他硬件配置

三、app 任务中的事件处理:

3.1、事件

uint32_t events;
// 阻塞在这里,等待事件返回
events = Event_pend(syncEvent, Event_Id_NONE, SBP_ALL_EVENTS, ICALL_TIMEOUT_FOREVER);
//不断去处理到达的事件
if (events) { }

3.2、任务处理

当蓝牙协议栈通过
触发一个 RTOS 任务 ,开始任务处理 过程 。任务处理过程一般分为三个
部分。
 协议栈消息,例如发送 AttRsp 确认消息 ,协议栈内部消息 GATT_MSG_EVENT 等
 RTOS 消息队列,通过消息队列缓存的延期待执行的数据,例如缓存的 Profile 特征值
数据等。
 自定义 EVENT ,例如 app 自定义的周期性时间,每个 5s 发送一次 notify 等。

3.3、任务间的消息

蓝牙协议栈通过 ICall 将消息传递给应用程序任务,例如上一小节的 GATT_MSG_EVENT 消除处理函数 SimpleBLEPeripheral_processStackMsg 。

3.4、发送到消息队列

这些消息使用
SimpleBLEPeripheral_enqueueMsg 函数入队 。消息被添加到队列中,并且
按照添加的顺序进行消息处理。 发送消息和 处理消息的代码片段如下 。

  • 当然我们最后还要使用TI 再次封装的实用工具: Util_enqueueMsg(appMsgQueue, syncEvent, (uint8*)pMsg);,将数据添加到消息队列中去。 注意第二个参数是 syncEvent,当前任务的同步事件句柄。
    Util_enqueueMsg函数如下:
uint8_t Util_enqueueMsg(Queue_Handle msgQueue, Event_Handle event, uint8_t *pMsg){ ... pRec->pData = pMsg; // 队列是原子操作 Queue_put(msgQueue, &pRec->_elem); // Wake up the application thread event handler. if (event) {Event_post(event, UTIL_QUEUE_EVENT_ID); } ... return FALSE; }

3.5、任务内部事件

在app 任务内部,可以创建 16 个自定义事件,可以通过定时 或其他方式 来异步执行事件

3.6、回调函数:

例如、蓝牙状态回调:

static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState) { switch ( newState ) {case GAPROLE_STARTED: {//协议栈启动运行 GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress); } break;case GAPROLE_ADVERTISING: //设备正在广播break;case GAPROLE_CONNECTED: { //设备已连接 } break;

注意,所有的回调函数的代码处理主体应该在任务上下文中进行,也就是通过
RTOS 消息
队列的方式,先缓存起来,然后待任务空闲时处理,例如刚才的函数,是在回调函数中 push
到消息队列中。

四、蓝牙:

4.1、发送蓝牙数据:

蓝牙数据的发送就是分为两种情况:一种是主机向从机发送数据,另一种是从机向主机发送数据:

4.1.1、主机向从机发送数据:

主机向从机发送数据
GATT_WriteCharValue ()(),该函数的代码片段如下

//申请用于发送蓝牙数据的内存
req.pValue = GATT_bm_alloc(connHandle, ATT_WRITE_REQ, 1, NULL); if ( req.pValue != NULL ){ //从机特征值uuid对应的handle,主机发起连接时获得。 req.handle = charHdl; req.len = 1; //待发送的数据 req.pValue[0] = charVal; req.sig = 0;req.cmd = 0; //开始发送status = GATT_WriteCharValue(connHandle, &req, selfEntity); if ( status != SUCCESS ) { GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ); } }

4.1.2、从机向主机发送数据:

GATT_Notification ()()

//申请用于发送蓝牙数据的内存
noti.pValue = GATT_bm_alloc(connHandle, ATT_HANDLE_VALUE_NOTI, 2, NULL); if (noti.pValue != NULL){ //通知特征值的句柄,可以通过GATT属性表得到 noti.handle = pAttr->handle; noti.len = 2; //待发送的数据 noti.pValue[0] = LO_UINT16(blkNum); noti.pValue[1] = HI_UINT16(blkNum); //发送数据 if (GATT_Notification(connHandle, &noti, FALSE) != SUCCESS) { GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);} }

4.2、接收蓝牙数据

和发送一样,蓝牙接受数据也是区分主机和从机两个方向。

4.2.1、从机接受

对应GATT_WriteCharValue函数。

第一步,注册

//Profile 回调函数,用来接收特征值事件。
// Register callback with SimpleGATTprofile
SimpleProfile_RegisterAppCBs(&SimpleBLEPeripheral_simpleProfileCBs);

第二步,在回调函数中调用
SimpleProfile_GetParameter读取 数据

static void SimpleBLEPeripheral_processCharValueChangeEvt(uint8_t paramID) {
switch(paramID)
{ case SIMPLEPROFILE_CHAR1: //读取数据
SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);//打印到显示屏上Display_print1(dispHandle, 4, 0, "Char 1: %d", (uint16_t)newValue); break; ... }

4.2.2、主机接受

第一步,注册接收
notify 消息
// 注册接收Indications/Notifications消息
GATT_RegisterForInd(selfEntity);
第二步,
在 GATT_MSG_EVENT 消息处理函数 SimpleBLECentral_processGATTMsg增
加 ATT_HANDLE_VALUE_NOTI 处理代码 。

static void SimpleBLECentral_processGATTMsg(gattMsgEvent_t *pMsg) { if (state == BLE_STATE_CONNECTED) {// See if GATT server was unable to transmit an ATT response if (pMsg->hdr.status == blePending) else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT) ... //handle notifications after initializationelse if (pMsg->method == ATT_HANDLE_VALUE_NOTI){ //数据内容为:pMsg->msg.handleValueNoti.pValue //数据长度为:pMsg->msg.handleValueNoti.len UartWrite(pMsg->msg.handleValueNoti.pValue,
pMsg->msg.handleValueNoti.len);} ... }

4.3、蓝牙profile:

蓝牙Profile 可以理解为主从双方通信过程中的格式化数据,并存储在蓝牙从机中,作为服务端,而主机作为客户端,客户端可以来获取服务端的数据或者属性,这个数据就称之为特征值。

Simple_peripheral 从机中的 Profile 为 SimpleProfile ,位于 src profiles simple_profile cc26xx
目录中。 重要的代码片段如下。

4.3.1、simpleprofile:

这个profile中采用的profile为:SimpleProfile。

1、定义特征属性表,在属性表中,设定特征和属性相关内容。

//特征属性表
static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] ={{{ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */GATT_PERMIT_READ, /* permissions */0, /* handle */(uint8 *)&simpleProfileService /* pValue */ },// Characteristic 1 Declaration {{ ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &simpleProfileChar1Props }, // Characteristic Value 1{ {ATT_BT_UUID_SIZE, simpleProfilechar1UUID },GATT_PERMIT_READ | GATT_PERMIT_WRITE,0, &simpleProfileChar1 },// Characteristic 1 User Description{ { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ,0, simpleProfileChar1UserDesp }, ... };

UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,这里设置为0xFFF1。

CONST uint8 simpleProfilechar1UUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(SIMPLEPROFILE_CHAR1_UUID), HI_UINT16(SIMPLEPROFILE_CHAR1_UUID)
};

用户可读的描述::

// Simple Profile Characteristic 1 User Descriptionstatic uint8 simpleProfileChar1UserDesp[17]= "Characteristic 1";

特征值为一个字节的数据:

// Characteristic 1 Value
static uint8 simpleProfileChar1 = 0;

2、定义特征值设置和读取函数:

  • 特征值设置:
bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value )
{
switch
( param )
//特征值1设置case SIMPLEPROFILE_CHAR1: if ( len == sizeof ( uint8 ) ) { simpleProfileChar1 = *((uint8*)value);}else { ret = bleInvalidRange;} break; //特征值2设置case SIMPLEPROFILE_CHAR2: ... break; }
  • 读取函数
bStatus_t SimpleProfile_GetParameter( uint8 param, void *value )
{ s
witch ( param ){ case SIMPLEPROFILE_CHAR1: *((uint8*)value) = simpleProfileChar1; break;case SIMPLEPROFILE_CHAR2:break; ...}

3、定义特征和属性的 读写过程

主机读取特征值

static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method)
{
if ( pAttr->type.len == ATT_BT_UUID_SIZE ){ // 16-bit UUIDuint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); switch ( uuid ) { //主机读特征值策略case SIMPLEPROFILE_CHAR1_UUID: *pLen = 1; pValue[0] = *pAttr->pValue;break; case SIMPLEPROFILE_CHAR5_UUID:*pLen = SIMPLEPROFILE_CHAR5_LEN; VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR5_LEN ); break;

主机写入特征值

static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method){ if ( pAttr->type.len == ATT_BT_UUID_SIZE ){ // 16-bit UUID uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);switch ( uuid ) { //主机写入数据 case SIMPLEPROFILE_CHAR1_UUID: ... uint8 *pCurValue = (uint8 *)pAttr->pValue; *pCurValue = pValue[0]; notifyApp = SIMPLEPROFILE_CHAR1; // 调用app注册的Profile回调函数 ... simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp );...

4、设置回调函数

bStatus_t SimpleProfile_RegisterAppCBs( simpleProfileCBs_t *appCallbacks ){if ( appCallbacks ) { //保存回调函数 simpleProfile_AppCBs = appCallbacks; ...

4.4、ICALL BLE5模块:

ICall是一种软件模块,可为应用程序与协议栈提供通信服务, app 中调用的协议栈 API 函数,大多来自 ICall 模块,另外 ICall 还提供 RTOS 的一些线程同步、动态内存等服务。 ICall 使得 app 和 stack 在统一的 RTOS 环境中高效运行,共享资源。

五、用户数据存储:

ccdc2640R2提供的存储管理叫做SNV,snv主要用于协议栈的绑定管理器存储。

开发者无需初始化SNV ,直接使用 SNV 提供的 Read/Writ e 函数即可(初始化函数已在 stack中调用,无需开发者干预)。在使用 SNV 服务之前,需要设置 stack 子工程的预处理宏定义:OSAL_SNV=1或 OSAL_SNV=2 1 或 2 表示使用的 Flash Page 4K )数量。 若 OSAL_SNV=0 ,则表示禁用 SNV 存储:

1、保存数据:
uint8 osal_snv_write( osalSnvId_t id, osalSnvLen_t len, void *pBuf)
2、读取数据:
uint8 osal_snv_read( osalSnvId_t id, osalSnvLen_t len, void *pBuf)
3、由于SNV 被多个模块共享,例如协议栈的 GapBondMgr 绑定管理器等,因此要小心的定义 snv item ,在 bcomdef.h 中查看 设置了系统占用的 item ,以及开发者可以使用的 item 范围
如下代码:

// Customer NV Items - Range 0x80 - 0x8F - This must match the number of Bonding entries#define BLE_NVID_CUST_START 0x80 //!< Start of the Customer's NV IDs    #define BLE_NVID_CUST_END 0x8F //!< End of the Customer's NV IDs

六、CCFG

CCA Customer Configuration Area 是客户配置取,占用闪存的最后一页。是客户配置取,占用闪存的最后一页,而 CCFG 占用CCA 扇区最后的 86 个字节。默认情况下,连接器会将 CCA 页中未使用的 Flash 空间分配给 app使用。

七、动态内存:

内存可以分为两个部分,堆和栈。函数中申请的局部变量,以及函数嵌套,中断都是使用栈空间。而静态变量和全局变量则是使用的堆空间。动态内存使用的内存也位于堆内存。
由于栈空间分配的数量十分有限,所以不要在函数中使用太大的数组。尽量使用动态内存。

sdk中一般使用 ICall_malloc 申请内存, ICall_free 释放内存 ,代码片段如下:

// 申请长度为len的动态内存uint8 *newValue = (uint8*)ICall_malloc(len); ...
// 释放内存ICall_free(newValue);

除了ICall_malloc 中还有另外集中申请动态内存的函数,分别是:
ICall_allocMsg ,对应 free 函数为 ICall_ free Ms g
GATT_bm_alloc ,对应 free 函数为: GATT_bm_free

三者的主要区别是,
ICall_malloc 用于开发者的一般性内存申请,而 ICall_allocMsg 主要用于 RTOS 消息队列的内存申请
GATT_bm_alloc 用于待发送的蓝牙数据内存申请。

德州CC2640R2f蓝牙芯片学习笔记(二)代码框架相关推荐

  1. Colly 学习笔记(二)——爬虫框架,抓取下载数据(上证A股数据下载)

    Colly 学习笔记(二)--爬虫框架,抓取下载数据(上证A股数据下载) Colly 学习笔记(一)--爬虫框架,抓取中金公司行业市盈率数据 Colly 学习笔记(二)--爬虫框架,抓取下载数据(上证 ...

  2. MonoRail学习笔记二:框架代码下载

    为了更好的学习MonoRail,我准备下载MonoRail的源代码看看. 先从http://www.castleproject.org/castle/download.html 下了一个源代码,结果编 ...

  3. 即时通讯学习笔记003---Tigase代码框架解读

    JAVA技术交流QQ群:170933152 在java下实现的xmpp开源实现,除了openfire外,tigase是另一个牛逼的项目. 实际的实验室压力下,50万人同时在线的单机,tigase的gc ...

  4. java mvc框架代码_JAVA技术学习笔记:SpringMVC框架(内附入门程序开发代码)

    原标题:JAVA技术学习笔记:SpringMVC框架(内附入门程序开发代码) JavaEE体系结构包括四层,从上到下分别是应用层.Web层.业务层.持久层.Struts和SpringMVC是Web层的 ...

  5. Java学习笔记-Day64 Spring 框架(二)

    Java学习笔记-Day64 Spring 框架(二) 一.控制反转IOC和依赖注入DI 1.控制反转IOC 2.依赖注入DI 3.Spring IOC容器 3.1.简介 3.2.实现容器 3.2.获 ...

  6. Spring Boot 框架学习笔记(二)(配置文件与数据注入 yaml基本语法 JSR303数据验证 多环境切换 )

    Spring Boot 框架学习笔记(二) 六.appliaction.properties配置与数据注入 6.1 `@Value`注解 测试注入数据 读取输入流 6.2 读取配置文件数据注入 单文件 ...

  7. GEE(Google Earth Engine) 最基础代码学习笔记二 —— JavaScript 语言

    GEE(Google Earth Engine) 学习笔记二 Javascript 语言 1. 注释 print('Hello World!'); 如果要注释,则在代码前面加//,比如: // pri ...

  8. Hadoop学习笔记—16.Pig框架学习

    Hadoop学习笔记-16.Pig框架学习 一.关于Pig:别以为猪不能干活 1.1 Pig的简介 Pig是一个基于Hadoop的大规模数据分析平台,它提供的SQL-LIKE语言叫Pig Latin, ...

  9. pythonsze_python学习笔记二 数据类型(基础篇)

    Python基础 对于Python,一切事物都是对象,对象基于类创建 不同类型的类可以创造出字符串,数字,列表这样的对象,比如"koka".24.['北京', '上海', '深圳' ...

最新文章

  1. mybaits十:关联查询
  2. Python Generators(生成器)——yield关键字
  3. 成功解决An error ocurred while starting the kernel
  4. 学霸大佬整理,超全 Python 学习路线图(附工具+视频+书籍+面试)
  5. 画质评测|一次关于视频画质的探(zhǎn)讨(xiàn)
  6. Android导航栏ActionBar的具体分析
  7. 计算机应用技术专业标志,计算机应用技术论文
  8. cartographer运行没有map_Cartographer激光SLAM2D源码分析
  9. CTO:不要在代码中写 set/get 方法了,逮一次罚款...
  10. java设计模式在java中的应用
  11. 洛谷.5283.[十二省联考2019]异或粽子(可持久化Trie 堆)
  12. Vue组件动态(异步)传值
  13. 周志明:终于薅住了这位 “社恐”作者的小辫子
  14. 工作经验的Java学习心得
  15. 王垠:完全用Linux工作及其后续
  16. 某短视频的X-Gorgon,X-Ladon等加密
  17. java 判断手机访问_下面java代码判断是手机访问还是PC访问什么地方出错了,手机跳转不到制定页面,等待解答...
  18. 设置line-height无效的解决办法
  19. 三菱4轴控制伺服案例,三菱PLC FX3U加三菱1PG定位模 块控制4个松下伺服,有完整的注释,结构清晰明了。伺服控制程序JOG HOME 定位 全部写成了功能块FB .你可以直接拿过去用
  20. Diffie-Hellman 密钥交换算法

热门文章

  1. Spark 安装与配置 (Spark HA 集群部署)
  2. 推荐 7 个牛哄哄的电商项目
  3. UVA1335-Beijin Guards(二分)
  4. NiFi分享第一期-安全认证(证书认证)
  5. python 曲线拟合(numpy.polyfit、scipy.optimize.curve_fit)
  6. 理解LP Simplex
  7. 树莓派无线鼠标不灵敏问题安装输入法
  8. shell脚本中等待上一条命令执行结束在执行下一条。
  9. 传统企业想要实现数字化转型,主要包含以下几大趋势?
  10. python sqlachemy模糊查询报错