IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> [蓝牙 4.0 CC2541 开发] BLE架构与OSAL -> 正文阅读

[嵌入式][蓝牙 4.0 CC2541 开发] BLE架构与OSAL

[蓝牙 4.0 CC2541 开发] BLE架构与OSAL

本文主要介绍 TI BLE Stack v1.4.1,其只支持 Bluetooth 4.0
(蓝牙5.0 和 蓝牙Mesh 我会努力的(??_?)?)
按照德州仪器公司 CC2541 芯片作为示例,介绍蓝牙4.0以下几个内容

  • OSAL:一种简单的嵌入式系统
  • BLE Stack:低功耗蓝牙协议栈
  • GAP通用访问协议 和 GATT通用属性协议

本次先介绍BLE架构 和 OSAL

BLE 4.0是什么

Bluetooth v4.0 使用两种通讯无线技术,这两种无线技术的速率是不同的 :基本速率/增强数据速率(BR/EDR)、蓝牙低功耗(BLE)。

BLE 就是我们所说的低功耗蓝牙,每次传输比较少的数据,发送后进入休眠,定时唤醒再发送;

而 BR/EDR 功耗比较高,它可以连续传输大包数据,满足更多的场景。

BLE 协议栈架构

一般 BLE 架构如图分为两个部分:控制器和主机。
在这里插入图片描述
为什么要分为两个部分?一个控制器一个主机?

这是由于向下兼容的旧版蓝牙结构,也就是蓝牙2.0、3.0这些老型号。

当时,也就是还没出iPhone的时候 😛,那时候的芯片的性能很弱,不能同时处理 2.4GHz 信号的调制解调,一遍处理封包解包。所以用了两个模块进行协同:控制器和主机。HCI、L2CAP就是用于这样的结构间沟通联系的部分。

但现在大部分的蓝牙芯片都已经集成了控制器和主机,不仅仅一个芯片处理调制解调信号、封包解包,还有另一个完整 MCU 工作。用 CC2541 举例,不仅可以发送无线信号,还可以运行应用。

  • Physical Layer (PHY)层
    指的是一个无线电层。
    主要是一些 1Mbps 自适应高斯频移键控物理层面
  • Link Layer (LL) :
    这一层主要控制 射频天线 的状态
    这种状态可以总结为五种:待命 Standby ; 广播 Advertisers;扫描 Scanning;发起 Initiating;连接 Connected。
  • Host-Controller Interface (HCI):
    主要是兼容旧设备,有蓝牙规范规定的控制器和主机之间通信的接口。旧设备是通过硬件串口链接,现在比如CC系列芯片是通过软件API的。
  • L2CAP:
    为了主机层,将控制器的通信封装,接受和发送。
  • security manager SM :
    配对和密钥分发,安全的交换数据;为其他层和其他设备通信的解密功能
  • 通用访问配置文件层 GAP:
    对接 应用层、配置层,处理怎么发现其他设备、怎么建立低功耗链接、什么时候介入安全层SM;
  • 属性协议 ATT :
    允许什么数据公开给另一台设备;往哪些地方写入数据,那些数据可读
  • 通用属性配置文件层 GATT
    服务于 属性协议,如何使用 ATT。可以往 ATT 中写数据来通知另一个蓝牙设备,也就是传输什么数据

OSAL:Operating System Abstraction Layer

其实是一个**操作系统,**就如同 UCOS、RTOS一样,这是一个由德州仪器深度定制的、专门针对51内核的操作系统

BLE 协议栈、配置文件、应用程序 都在 OSAL 的控制回路中构建。提供消息传递、堆栈管理、ji

主要是:

  • 任务:各个协议层之间的定义
  • 任务事件、事件进程:不同层相互之间的消息传递
  • 堆管理;
  • OSAL消息。

Task

任务的初始化

void osalInitTasks( void )
{
  uint8 taskID = 0;
  //OSAL分配给 tasksEvents 一个16位的任务层数量的数组的指针
  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);   
  // tasksEvents 全部置零
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

  LL_Init( taskID++ );   //LL Task
  Hal_Init( taskID++ );  //Hal Task
  HCI_Init( taskID++ );  //HCI Task
  L2CAP_Init( taskID++ );// L2CAP Task 
  GAP_Init( taskID++ );  // GAP Task
  GATT_Init( taskID++ ); // GATT Task 
  SM_Init( taskID++ );   // SM Task 
  /* Profiles */
  GAPRole_Init( taskID++ );
  GAPBondMgr_Init( taskID++ );
  GATTServApp_Init( taskID++ );
  /* Application */
  SimpleBLEPeripheral_Init( taskID );

每一个协议层都从 osalInitTasks() 中调用初始化例程。

当然,所有任务以物理射频层 LL 为最高优先级 : )

先分配内存池后,再按顺序为每一层分配 taskID ,8 位 taskID 值越高 优先级越低。如 SimpleBLEPeripheral_Init 即为最高数值 taskID 且最低优先级。

还定义了 tasksEvents 的大小,在 osal_run_system 系统运行中可以用户最多增加15个的子事件。 0x8000 被定义位系统消息

task event 和 event process

  • 任务 (task) 可以理解为某协议层
  • 事件 (event) 可以理解为 层和层之间相互传递信息、相互激活函数的处理程序

一般物理层射频天线接到数据后怎么将接到的消息传递给应用层呢?或者 应用层 想要发送一段字符,怎么将指令一层层发送直到 LL层射频呢?

OSAL给出的解决办法是使用 任务事件

在初始化结束后,进入系统主循环,在系统循环中有一个关键的步骤是,轮询检查是否有任务层中有事件发生。如果有,就进入这个事件处理回调函数。↓ 这是框图。
在这里插入图片描述
主要讲一下代码

↓ 这是定义任务事件处理的数组,在 osal_run_system 函数中,轮询的时候会调用这个数组检查每一层的事件。

const pTaskEventHandlerFn tasksArr[] =
{
  LL_ProcessEvent,                                                  // task 0
  Hal_ProcessEvent,                                                 // task 1
  HCI_ProcessEvent,                                                 // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )
  OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ),           // task 3
#endif
  L2CAP_ProcessEvent,                                               // task 4
  GAP_ProcessEvent,                                                 // task 5
  GATT_ProcessEvent,                                                // task 6
  SM_ProcessEvent,                                                  // task 7
  GAPRole_ProcessEvent,                                             // task 8
  GAPBondMgr_ProcessEvent,                                          // task 9
  GATTServApp_ProcessEvent,                                         // task 10
  SimpleBLEPeripheral_ProcessEvent                                  // task 11
};

↓ 是 osal_run_system 的函数,在注释中详细解释

void osal_run_system( void )
{
  uint8 idx = 0;
  Hal_ProcessPoll(); //硬件中断处理 主要是USART接收中断和休眠模式的初始化

  do {
    if (tasksEvents[idx])  // 保证每次处理的都是最高优先级的
			break;
  } while (++idx < tasksCnt);  //递增任务ID值
   
  if (idx < tasksCnt)
  {
    uint16 events;
    halIntState_t intState;

    HAL_ENTER_CRITICAL_SECTION(intState);  //先关闭中断 防止多个中断响应
    events = tasksEvents[idx];             //将事件缓存
    tasksEvents[idx] = 0;                  //清除事件缓存
    HAL_EXIT_CRITICAL_SECTION(intState);   //打开中断

    activeTaskID = idx;                    //缓存当前的任务层ID
    /*触发对应任务层的处理函数 传递对应的任务层ID 和 对应事件值。会返回一个*/ 
    events = (tasksArr[idx])( idx, events );  
    activeTaskID = TASK_NO_TASK;           //清除ID缓存
    
    HAL_ENTER_CRITICAL_SECTION(intState);   //先关闭中断 
    tasksEvents[idx] |= events;             //缓存剩余事件标志
    HAL_EXIT_CRITICAL_SECTION(intState);    //打开中断
  }  
}

各个任务层的事件处理函数 ProcessEventtaskArr[] 指针数组中。

在初始化后 在 OSAL 无限循环中检查是否有任务事件,如果有任务事件则进入对应任务层的 processEvent

软件各个层可以在processEvent 中相互独立的为各自协议层设置 OSAL event。

↓ 可以用以下两个函数,在各自层中的子函数中调用,来激活对应的任务层事件。

  • uint8 osal_set_event( uint8 task_id, uint16 event_flag ) 可以往指定协议层中安排新的事件
  • uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint32 timeout_value ) 类似于osal_set_event()。第三个参数可以设定毫秒计时器超时后激活 event。

Message

OSAL 设置了 SYS_EVENT_MSG 作为任务层之间的接收消息的标志,会调用对应协议层事件处理程序Msg event process

已经有 task_event 为什么还需要 Message?

首先先确定:Message 是一个 task_event,是 OSAL 设定的专用于发送数据的任务事件。而其他是激活对应子程序的。

Msg 是可以将 任何类型数据、任意大小的数组指针发送给指定层。

↓ 发送消息 典型事例的是 OSAL发送按键信息

uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
  keyChange_t *msgPtr;
	//如果案件任务ID没有定义,则不处理,
  if ( registeredKeysTaskID != NO_TASK_ID )
  {
    msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );  //初始化 Msg 内存
    if ( msgPtr ) //如果 Msg 消息申请出错会出现 NULL
    {
      msgPtr->hdr.event = KEY_CHANGE; 
      msgPtr->state = state;
      msgPtr->keys = keys;

      osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr ); //发送给对应的任务层 按键信息
    }
    return ( SUCCESS );
  }
  else
    return ( FAILURE );
}

↓接收消息 这是应用层的事件处理函数

uint16 SimpleBLEPeripheral_ProcessEvent( uint8 task_id, uint16 events )
{
  VOID task_id; 
  if ( events & SYS_EVENT_MSG )
  {
    uint8 *pMsg;
		//判定 simpleBLEPeripheral_TaskID  是否有对应 Msg
    if ( (pMsg = osal_msg_receive( simpleBLEPeripheral_TaskID )) != NULL )
    {
      simpleBLEPeripheral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );  //任务层的处理 Msg 程序
      VOID osal_msg_deallocate( pMsg );  //释放消息内存
    }
    return (events ^ SYS_EVENT_MSG);  //处理结束后 返回消除当前事件的事件标志位
  }
}

Heap Manager

对于系统的内存堆管理,类似于 C标准库 malloc realloc calloc

  • void *osal_mem_alloc( uint16 size ) 做动态内存管理,在任务初始化中就有用过
  • void osal_mem_free(void *ptr) 用于释放分配的内存

CallBack 回调函数

除 Process Event 以外 还可以定义 回调函数 。如 simpleProfileChangeCB()peripheralStateNotificationCB()

CB 只能在任务中做有限的处理,需要密集处理的话,发送 event 在 应用层 中处理。


结束了

接下来还有两篇介绍 多篇实战(? ?_?)?

就酱

在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-07 10:59:11  更:2021-09-07 11:00:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:52:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码