精华内容
下载资源
问答
  • 1_GPIO实验教程、2_定时器实验教程、3_中断实验教程、4_ADC实验教程、5_串口实验教程、6_SPI实验教程、7_I2C实验教程
  • 目录前言:一、蓝牙协议栈简介:二、GAP层,通用访问配置文件:2.1、连接参数:2.2、有效连接间隔:2.3、连接参数注意事项:2.4、多连接下的连接参数限制:2.5、连接参数更新2.6、连接终止:2.7、GAP概述:2.8、配置...

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

    前言:

    基于谷雨cc2640教程

    一、蓝牙协议栈简介:

    对蓝牙有基础的朋友可能知道蓝牙的一些概念,这就是一个典型蓝牙协议栈。那么对于蓝牙新手来说,我们就可以降蓝牙协议栈看作是一个飞机,我们只需要学习如何开飞机就行,至于飞机怎么做,那不是我们现阶段需要考虑的事情。
    在这里插入图片描述

    二、GAP层,通用访问配置文件:

    1. BLE协议栈的GAP负责连接功能,处理的是设备的接入过程
    2. 包括:设备发现,链路建立,链路终止,安全特性的启动和设备配置。

    在这里插入图片描述

    2.1、连接参数:

    1. connection interval连接间隔:在ble方案中,为使用跳频方案,就是两个设备旨在特定的频道上面交流数据,随后会在新的频道上面同行,而这个新的频道在上次的交流中约定。连接间隔的范围从6(7.5ms)到最大值3200(4.0s)
    2. slave latency从机延迟:这个参数是从机跳过多个连接事件的能力:如果外设没有要发送的数据就继续睡眠。当然,这个功能可以选择开启或者不开启。
    3. supervison time-out:监控超时,连词成功连接事件之间的最长时间,如果在这个时间内没有成功,那么设备将终止连接并且返回到未连接状态。以10ms为单位,可以从10(100ms)到3200(32.0s)之间。

    2.2、有效连接间隔:

    有效连接间隔等于两个连接事件之间的时间跨度。当没有数据从从机发送到主机时,从机每500ms 一个 连接事件 交互一次 。

    2.3、连接参数注意事项:

    在这里插入图片描述

    2.4、多连接下的连接参数限制:

    为了有时间能够扫描新设备,可以使用的最小连接间隔为12.5+5*n。n为当前连接的数量。

    2.5、连接参数更新

    外设可以发送连接参数更新请求来请求主机更改连接设置。
    -该请求包含四个参数:最小连接间隔,最大连接间隔,从延迟和
    监管 超时。这些值表外围设备对连接所需的参数(连接间隔作为范围给出)。当 主机 设备接收到该请求时,它可以接受或拒绝新的参数。

    • 根据
      GAPRole ,连接参数更新可以 调用 GAPRole_SendUpdateParam 或GAPCentralRole_UpdateLink 命令异步发送。

    2.6、连接终止:

    主机和从机可以由于任何原因终止连接,一方终止,另一方必须在两台设备退出连接状态之前进行相应。

    2.7、GAP概述:

    应用层可以直接调用GAP APi来执行BLE相关功能,大多数GAP由GAProle task处理。

    在这里插入图片描述

    2.8、配置GAP:

    GAP层功能主要在库代码中定义,这些功能大多由GAPRole调用,不需要应用直接调用。

    • 开始GAPRole 之前可能需要修改的几个参数。这些参数可以通过 GAP_SetParamValue 和GAP_GetParamValue 函数进行设置或获取,包括 广播和扫描间隔、 窗口等(有关更多信息,请参阅 API )。以下是 simple_peripheral_init 中 GAP 层的配置:

    在这里插入图片描述

    三、GAPRole任务

    • 这是一个单独的任务,处理大多数GAP层的功能,并且释放应用层的空间。这个任务由应用程序启用和配置,许多蓝牙低功耗协议栈实际那由GAPRole任务直接处理而不传送到应用程序中。
    • 应用程序可以向GAPRole注册回调函数,用于在应用程序中处理某些事件。
      在这里插入图片描述

    3.1、外设角色 peripheral:

    操作流程:
    1、 初始化GAPRole参数:
    在这里插入图片描述
    2、初始化GAPROle任务,并且将应用程序回调函数传回GAPRole。
    在这里插入图片描述
    在这里插入图片描述
    4、GAPRole任务处理了绝大多数GAP事件,将一些事件转发到应用程序。

    3.2、central主机:

    1、初始化参数:
    在这里插入图片描述
    2、启动任务:
    在这里插入图片描述
    3、从应用程序中发送数据之后,会通过central函数到达stack。
    4、GAPRole任务也处理了一部分事件,然后将一部分事件转发到应用程序。

    四、GATT通用属性配置文件:

    GATT主要用于两个连接设备之间的数据通信。数据以特征值的形式传输和存储。从GATT的角度来看,:

    • gattu服务器:一个包含特性数据库中的设备,可以通过一个GATT客户端写入或者读取。
    • GATT客户端,是向GATT服务器写入或者读取数据的设备。
      在这里插入图片描述

    4.1、GATT:characteristics and attributes特征和属性:

    这两个词我想大多数朋友在看的时候会有一种

    4.2、四个GATT配置文件:

    1、gatt服务
    2、通用属性服务:GATT服务器的信息。
    3、设备信息服务:
    4、simple_gatt_profile 这个是用于测试的profile文件。

    以下是简单Profile 文件属性表的逐行描述,由以下句柄引用。
    0x001F是 simple_gatt_profile 服务声明 。该声明的 UUID 为 0x2800 (蓝牙定义的
    GATT_PRIMARY_SERVICE_UUID )。该声明的值是 simple_gatt_profile (自定义)的 UUID 。
    0x0020是 SimpleProfileChar1 的特征声明 。该声明可以被认为是指向
    SimpleProfileChar1 值的指针。该声明的 UUID 为 0x2803 (蓝牙定义的
    GATT_CHARACTER_UUID )。 5 字节 的特征值如下 (从 MSB 到 LSB
    字节0 :蓝牙规范中定义的 SimpleProfileChar1 的属性(以下是一些相关属性。)
    0x02:允许读取特征值
    0x04:允许写入特征值(无响应
    0x08:允许写入特征值(带响应
    0x10:允许通知特征值(无确认
    0x20:允许通知特征值(带确认 0x0A 的值表示特性可读( 0x02 )和可写 0x08 )。

    字节1 2 SimpleProfileChar1 值的字节反转句柄(句柄 0x0021
    字节3 4 SimpleProfileChar1 值的 UUID (自定义 0xFFF1
    0x0021是 SimpleProfileChar1 值 。该值的 UUID 为 0xFFF1 (自定义)。该值是特征的实际有效载荷数据。如其特征声明(句柄 0x0020 )所示,该值是可读写的。
    0x0022是 SimpleProfileChar1 用户描述 。该描述的 UUID 为 0x2901 (蓝牙定义)。该
    描述的值是描述特征的可读字符串。
    0x00230x002F 是剩下的四个特征描述的 simpleProfileChar1 相同结构的属性。 唯一
    不同的 属性,处理 0x002B ,描述如下。
    0x002B是 SimpleProfileChar4 客户端特征配置 。此配置的 UUID 为 0x2902 (蓝牙定
    义)。通过写入此属性, GATT 服务器可以将 SimpleProfileChar4 配置为通知。

    4.3、GAP GATT Service GGS服务:

    GGS是实主机或者外围设备的低功耗多必须的。当然,使用低功耗的设备也必然包含GGS。GGS的目的实在设备发现和连接启动的过程中进行辅助。

    4.3.1、使用GGS:

    • 首先,我们也应该知道GGS同样也是以lib的形式存在于协议栈中。
      1、包含头文件 $install/src/inc #include "gapgattserver.h"
      2、初始化GGS参数:
    // GAP GATT Attributes
     static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple BLE Peripheral"; GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    

    3、使用GGS初始化应用程序回调。
    4、将GGS添加到GATT服务器。

    4.4、GATT服务:

    该服务提供注册GATT服务的相关信息。

    4.4.1、使用GATT服务:

    这一部分要求用户必须使能GATT可变服务属性,当使用ble的时候。

    4.5、GATT client抽象:

    GATT客户端没有属性表或者profile文件。因为他是收集方。

    4.5.1、使用GATT:

    同样的,GATT的服务也在库中实现:

    • 客户端使用GATT:
      1、初始化客户端:
      VOID GATT_InitClient();
      2、注册接受传入的ATT指示和通知:
      GATT_RegisterForInd(selfEntity);
      3、执行GATT客户端过程:

    在这里插入图片描述
    4、接受处理GATT,当收到事件的时候,最后由库发送数据。
    在这里插入图片描述

    5、GATT客户端可以从GATT服务器接受指示或者数据。

    4.6、GATT服务抽象:

    GATT得大部分配置在:GattServApp

    4.6.1、GattServApp模块:

    GATTServApp存储和管理应用程序范围的属性表。各种 Profile 文件使用此模块将其特性添加到属性表 中 。低功耗蓝牙 协议栈 使用此模块来响应 GATT 客户端的请求。例如, GATT客户端可以发送“发现所有主要特征”消息。 GATT 服务器上的 低功耗蓝牙协议栈 收到此消息,并使用 GATTServApp 查找并发送存储在属性表中的所有主要特性。从 Profile 文件访的GATTServApp 功能在 gattservapp_util.c 中定义。

    1、创建属性表:

    // 初始化GATT属性
    GGS_AddService(GATT_ALL_SERVICES); //添加服务
    GATTServApp_AddService(GATT_ALL_SERVICES); //添加属性服务
    DevInfo_AddService(); // 加入设备信息服务
     #ifndef FEATURE_OAD_ONCHIP 
     SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile #endif //!FEATURE_OAD_ONCHIP
    

    在这里插入图片描述

    4.6.2、profile架构:

    我们可以查到profile的翻译有:轮廓;简介;形象;外形。但是你在蓝牙协议中这个词语究竟是代表什么。其实不是很好定义,不妨我们直接就简单的把这个profile看成字母A,然后我们自己来定义这个定义:

    4.6.2.1、 属性表定义:

    每个GATT属性的服务,都需要一个属性表:
    表的定义:
    static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED]

    typedef struct attAttribute_t { gattAttrType_t type; //!< Attribute type (2 or 16 octet UUIDs)
    uint8 permissions; //!< Attribute permissions 
    uint16 handle; //!< Attribute handle - assigned internally by attribute server 
    uint8* const pValue; //!< Attribute value - encoding of the octet array is defined in //!< the applicable profile. The maximum length of an attribute //!< value shall be 512 octets. } gattAttribute_t;
    

    权限配置:

    在这里插入图片描述

    特征声明:
    在这里插入图片描述
    特征值:
    在这里插入图片描述
    特征值配置:
    在这里插入图片描述

    4.6.2.2、添加服务功能:

    应用程序启动的时候,需要添加所支持的GATT服务:
    1、为客户端特征配置CCC数组分配空间:
    在这里插入图片描述
    2、初始化CCC数组,
    GATTServApp_InitCharCfg(INVALID_CONHANDLE, simpleProfileChar4Config );
    3、使用GATTServApp注册配置文件:

    4.6.2.3、注册应用程序回调函数:

    在这里插入图片描述
    当然,我们必须实现这一个回调函数:
    在这里插入图片描述

    4.6.2.4、读写回调函数:

    1、客户端读取请求:
    当给定属性收到GATT客户端的读取请求的时候,调用回调文件。
    在这里插入图片描述
    2、客户端的写请求:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    4.6.2.5、GET和set函数:

    用来读取和写入配置文件:
    在这里插入图片描述

    4.6.3、排队书写:

    GATT_PARAM_NUM_PREPARE_WRITES

    4.7、GATT过程中的动态内存:

    在这里插入图片描述

    4.8在应用中接受更多GATT事件:

    GATT_RegisterForMsgs可以接收到更多的GATT事件:
    主要有以下三类:
    1、GATT服务器无法发送ATT相应,尝试在下一个连接间隔发送。
    2、ATT流量控制违规,通知应用程序接入的设备违反ATT流量规则。
    3、更新ATT MTU大小。

    4.9、GATT安全:

    4.9.1:认证:

    在客户端通过认证配对方法之前,无法访问需要身份验证的特性。

    4.9.2:授权:

    应用程序需要定义自己的授权请求,所以协议栈将这些特性的读写请求转发到应用程序层。
    1、 注册授权回调:

    CONST gattServiceCBs_t simpleProfileCBs = { 
    simpleProfile_ReadAttrCB, // Read callback function pointer 
    simpleProfile_WriteAttrCB, // Write callback function pointer 
    simpleProfile_authorizationCB // Authorization callback function pointer };
    

    2、调用回调函数:
    不要再这里执行复杂的功能

    static bStatus_t simpleProfile_authorizationCB( uint16 connHandle,gattAttribute_t *pAttr,uint8 opcode ) { 
    //This is just an example implementation, normal use cases would require //more complex logic to determine that the device is authorized
    if(clientIsAuthorized) 
    	return SUCCESS; 
    else return ATT_ERR_INSUFFICIENT_AUTHOR; }
    
    展开全文
  • 目录前言:一、 本系列文章由江山(csdn名:补不补布)(github:jianggogogo)自己写成,当中用到引用时都已经标记出来,如果出现版权问题,请直接联系...本文基于谷雨cc2640r2f教程所编写,是一篇学习笔记。 一、 ...

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

    前言:

    本文基于谷雨cc2640r2f教程所编写,是一篇学习笔记。

    一、简介:

    • 总体上来说,这也就是一个实时操作系统,只不过cc2640芯片协议栈是在这个系统之上运行的。
    • 其次,该系统是实时的抢占式的多线程操作系统。
    • 内核采用SYS/BIOS
      线程级别:
      名称|内容
      —|---
      硬件中断服务程序|硬件事件触发器
      软件中断|32个优先级
      任务|32个优先级,当写数据时停止运行
      空闲中断|单独的优先级种类

    二、任务:

    • 一个并且只有一个任务处于运行状态,包括空闲任务。
    • 在任何情况下面,mcu都会切换到已经准备就绪的最高优先级任务。

    2.1、创建任务:

    任务创建之后,会存在一个自己的运行时间栈空间:
    这是一个示例:可以看到大致流程为:
    1、建立一个任务指针
    2、初始化这个任务
    3、设置栈内存
    4、设置栈内存大小
    5、设置任务优先级
    6、构建任务,传递任务指针
    在这里插入图片描述

    • 当创建任务的时候,将一个任务函数的指针传递给,task_construct函数。

    2.2、定时器:

    这里就是一个定时器,注意,最低分辨率的rtos时钟周期是10微秒
    如果需要更加高的分辨率那么需要通过硬件定时器。
    例子:
    在这里插入图片描述

    2.3、信号量:

    信号量可以分为计数信号量和二进制信号量:
    初始化信号量:
    在这里插入图片描述

    • 等待信号量:
      作用为,任务将在这里被阻塞,直到得到一个信号量
      在这里插入图片描述
    • 发布一个信号量:
      在这里插入图片描述

    2.4、任务队列:

    队列采用先进先出的顺序来处理事件。
    在这里插入图片描述

    2.5、空闲任务

    cc2640中,空闲任务运行电源策略管理器

    2.6、单元管理:

    开发者可以使用pwoer_saving宏定义库来启动和禁止使用低功耗功能。

    2.7、硬件中断:

    在这里插入图片描述

    2.8、软件中断:

    不要在中断中执行阻塞代码。
    在这里插入图片描述

    展开全文
  • 目录前言:一、main函数:二、app任务初始化:三、app 任务中的事件处理:3.1、事件3.2、任务处理3.3、任务间的消息3.4、发送到消息队列3.5、任务内部事件3.6、回调函数:四、蓝牙:4.1、发送蓝牙数据:4.1.1、主机向...

    本系列文章由江山(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 loop
      for (;;)
      {
      //一个用于任务的循环
    }
    }
    

    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 initialization
      else 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 Description
     static 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 UUID
     uint16 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 用于待发送的蓝牙数据内存申请。

    展开全文
  • 一直工作在蓝牙应用的软件开发。 在工作之余,利用手中的开发板做了一些视频, 方便准备学习的开发人员快速入门,更容易上手。 ======================= 需要CSR8670/8675的学习板...

    一直工作在蓝牙应用的软件开发。

    在工作之余,利用手中的开发板做了一些视频,

     方便准备学习的开发人员快速入门,更容易上手。

    此视频 适用于 所用QCC512x系列:QCC5127,5126,5125,5124,5121,5120,

    QCC302x系列:QCC3020,3021,3024,3026,3031,3034,

    =======================

    需要CSR8670/8675的学习板【淘宝店:科飞工作室】。

    高通蓝牙技术交流群:713094735

    科飞开发板支持群:763757182(淘宝订单号入群,可获更多的资源

    =======================
     

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 326
精华内容 130
关键字:

蓝牙芯片学习