由浅入深-蓝牙4.0BLE协议栈开发攻略大全(3).docx_第1页
由浅入深-蓝牙4.0BLE协议栈开发攻略大全(3).docx_第2页
由浅入深-蓝牙4.0BLE协议栈开发攻略大全(3).docx_第3页
由浅入深-蓝牙4.0BLE协议栈开发攻略大全(3).docx_第4页
由浅入深-蓝牙4.0BLE协议栈开发攻略大全(3).docx_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

本系列教程将结合TI推出的CC254x SoC 系列,讲解从环境的搭建到蓝牙4.0协议栈的开发来深入学习蓝牙4.0的开发过程。教程共分为六部分,本文为第三部分:第三部分知识点:第十一节 串口通信第十二节 Flash的读写第十三节 BLE协议栈简介第十四节 OSAL工作原理第十五节 BLE蓝牙4.0协议栈启动分析有关TI 的CC254x芯片介绍,可点击下面链接查看:主流蓝牙BLE控制芯片详解(1):TI CC2540同系列资料推荐:由浅入深,蓝牙4.0/BLE协议栈开发攻略大全(1)由浅入深,蓝牙4.0/BLE协议栈开发攻略大全(2)有关本文的工具下载,大家可以到以下这个地址:朱兆祺ForARM第十一节 串口通信在软件开发过程中调试是一个很关键的过程,而调试用的最多的手段就是打印Log,嵌入式平台很少有显示设备,所以我们需要将信息通过串口打印到PC端。MT254xboard上已经通过RS232芯片将UART0连接到DB9,我们只需要将DB9连接到电脑即可,UART0 对应的外部设备 IO 引脚关系为:P0_2-RX,P0_3-TX。我们需要将这两个IO配置为复用功能,CC2540的USART可以配置为SPI模式或者异步UART模式,这里我们需要配置为异步UART模式。首先配置IO为UART模式:PERCFG &= 0x01; / 配置UART为位置 1P0SEL = 0x3c; / P0_2,P0_3,P0_4,P0_5用作串口功能P2DIR &= 0XC0; / P0 优先作为UART0配置UART0寄存器,将UART0配置为8N1模式,波特率为115200。U0CSR |= 0x80; / UART 方式U0GCR |= 11; / U0GCR与U0BAUD配合U0BAUD |= 216; / 波特率设为115200UTX0IF = 0; / 清除中断标志U0CSR |= 0X40; / 允许接收IEN0 |= 0x84; / 开总中断,接收中断这里采用中断方式来接收串口数据,并在中断中回调应用层的接收处理函数。#pragma vector = URX0_VECTOR_interrupt void UART0_ISR(void)uint8 ch;URX0IF = 0; / 清中断标志ch = U0DBUF;if ( NULL != RecvCb ) / 调用回调函数RecvCb(ch);为了测试串口的通讯功能,这里我们通过串口接收命令的方式来控制LED的亮灭和蜂鸣器的响和停止,并且显示当前的状态。根据串口输出提示,发送对应字符可以实现相应功能,并且显示状态。第十二节 Flash的读写嵌入式系统中需要存储数据,而片内的Flash资源很匮乏,所以我们经常需要使用SpiFlash来存储数据,MT254xboard中板载了一个 512Kbyte的Flash,下面我们来驱动此Flash。上一小节中我们用SPI的方式驱动了LCD12864,这节我们继续用SPI来驱动板载的 Flash,详细的说明了如何驱动这片Flash,在此不做累述,我们复制LCD12864工程,重命名为SpiFlash,在此工程中添加GD25Q40的两个驱动文件。下面我们来检测这个Flash,检测的方法为,全部写入0xAA,然后再读出,对比是否为0xAA,如果是,那Flash是没有问题的,否则Flash可能已经有坏块。具体的代码见例程,这个过程所需要的时间取决于我们需要检测的区域大小,如果完全检测,则可能需要几分钟的时间。int main(void)SysStartXOSC();LCD12864_Init(); / LCD初始化GD25Q40_Init(); / Flash初始化LCD12864_DisStr(0, “Flash Check.。”);sprintf(LCDBuf, “Flash ID :%04X”, GD25Q40_ReadID(); / 读取器件IDLCD12864_DisStr(1, LCDBuf);GD25Q40_EraseChip(); / 擦除整片Flash 大约需要10SLCD12864_DisStr(2, “Erase Chip Complete”);uint32 iCnt = 0;/ 全部写入0xAAconst uint8 Write = 0xAA;for(iCnt=0; iCnt CHECK_ADDR_RANGE; iCnt+)GD25Q40_Write(&Write, iCnt, 1); / 写入0xAA/ 读取Flash内部的值,与写入的值对比uint8 Read;for(iCnt=0; iCnt = CHECK_ADDR_RANGE)LCD12864_DisStr(3, “Flash Check Success”);GD25Q40_EraseChip(); / 再次擦除while(1);return 0;MT254X蓝牙4.0开发板Flash效果:第十三节 BLE协议栈简介TI的协议栈分为两部分:控制器和主机。对于4.0以前的蓝牙,这两部分是分开的。所有profile和应用都建构在GAP或GATT之上。根据这张图,我们从底层开始介绍。TI的这款CC2540器件可以单芯片实现BLE蓝牙协议栈结构图的所有组件,包括应用程序。1.1.1 PHY层1Mbps自适应跳频GFSK(高斯频移键控),运行在免证的2.4GHz。1.1.2 LL层LL层为RF控制器,控制设备处于准备(standby)、广播、监听/扫描(scan)、初始化、连接,这五种状态中一种。五种状态切换描述为:未连接时,设备广播信息,另外一个设备一直监听或按需扫描,两个设备连接初始化,设备连接上了。发起聊天的设备为主设备,接受聊天的设备为从设备,同一次聊天只能有一个意见领袖,即主设备和从设备不能切换。1.1.3 HCI层HCI层为接口层,向上为主机提供软件应用程序接口(API),对外为外部硬件控制接口,可以通过串口、SPI、USB来实现设备控制。1.1.4 L2CAP层L2CAP层提供数据封装服务,允许逻辑上的点对点通讯。1.1.5 SM层SM层提供配对和密匙分发,实现安全连接和数据交换。1.1.6 ATT层ATT层负责数据检索,允许设备向另外一个设备展示一块特定的数据称之为属性,在ATT环境中,展示属性的设备称之为服务器,与它配对的设备称之为客户端。链路层的主机从机和这里的服务器、客服端是两种概念,主设备既可以是服务器,也可以是客户端。从设备毅然。1.1.7 GATT层GATT层定义了使用 ATT 的服务框架和配置文件(profiles)的结构。BLE 中所有的数据通信都需要经过 GATT。GATT负责处理向上与应用打交道,其关键工作是把为检索工作提供合适的profile结构,而profile由检索关键词(characteristics)组成。1.1.8 GAP层GAP直接与应用程序或配置文件(profiles)通信的接口,处理设备发现和连接相关服务。另外还处理安全特性的初始化。对上级,提供应用程序接口,对下级,管理各级职能部门,尤其是指示LL层控制室五种状态切换,指导保卫处做好机要工作。1.2 TI协议栈源码介绍在第二章我们讲解了源码的安装,这里我们就来剖析源码的结构。打开协议栈目录我们可以看到下图:BLE源码:目录名内容说明Accessories一些工具和已经编译好的Hex文件此文件夹中有Btool的安装包、USB-CDC的驱动。ComponentsHal驱动,OSAL源码、协议栈通用源码此文件夹是OSAL各层组件的实现Documents帮助文档协议栈说明文档,这是学习BLE最好的资料。Projects工程文件这里有一些TI的Demo,我们开发一般是在Demo的基础上进行这里TI给出了很多Demo,这些例程都是经过了SIG评审的,ble 文件夹中有很多工程文件,有些是具体的应用,例如BloodPressure、GlucoseCollector 、GlucoseSensor 、 HeartRate 、HIDEmuKbd 等都为传感器的实际应用,有相应标准的 Profile。其中有4种角色: SimpleBLEBroadcaster 、 SimpleBLECentral 、SimpleBLEObserver、SimpleBLEPeripheral。他们都有自己的特点。1.Broadcaster 广播员 非连接性的信号装置2.Observer 观察者 扫描得到,但不能链接3.Peripheral 从机 可链接,在单个链路层链接中作为从机4.Central 主机 扫描设备并发起链接,在单链路层或多链路层中作为主机。我们的讲解将围绕这主机和从机进行。因为其它的设备都是基于这两种设备扩展开来的。第十四节 OSAL工作原理蓝牙为了实现同多个设备相连,或实现多功能,也实现了功能扩充,这就产生了调度问题。因为,虽然软件和协议栈可扩充,但终究最底层的执行部门只有一个。为了实现多事件和多任务切换,需要把事件和任务对应的应用,并起一个名字OSAL操作系统抽象层。OSAL管理的实现如果实现软件和硬件的低耦合,使软件不经改动或很少改动即可应用在另外的硬件上,这样就方便硬件改造、升级、迁移后,软件的移植。HAL硬件抽象层正是用来抽象各种硬件的资源,告知给软件。其作用类似于嵌入式系统设备驱动的定义硬件资源的h头文件。BLE低功耗蓝牙系统架构:OSAL作为调度核心,BLE协议栈、profile定义、所有的应用都围绕它来实现。OSAL不是传统大家使用的操作系统,而是一个允许软件建立和执行事件的循环。软件功能是由任务事件来实现的,创建一个任务事件需要以下工作:1. 创建task identifier任务ID;2. 编写任务初始化(task initialization routine)进程,并需要添加到OSAL初始化进程中,这就是说系统启动后不能动态添加功能;3. 编写任务处理程序;4. 如有需要提供消息服务。BLE协议栈的各层都是以OSAL任务方式实现,由于LL控制室的时间要求最为迫切,所以其任务优先级最高。为了实现任务管理,OSAL通过消息处理(messageprocess),存储管理,计时器定时等附加服务实现。系统启动流程:为了使用OSAL,在main函数的最后要启动一个名叫osal_start_system的进程,该进程会调用由特定应用决定的启动函数 osalInitTasks(来启动系统)。osalInitTasks逐个调用BLE协议栈各层的启动进程来初始化协议栈。随后,设置一个任务的 8bit任务ID(task ID),跳入循环等待执行任务,系统启动完成。1. 任务优先级决定于任务ID,任务ID越小,优先级越高2. BLE协议栈各层的任务优先级比应用程序的高3. 初始化协议栈后,越早调入的任务,任务ID越高,优先级越低,即系统倾向于处理新到的任务每个事件任务由对应的16bit事件变量来标示,事件状态由旗号(taskflag)来标示。如果事件处理程序已经完成,但其旗号并没有移除,OSAL会认为事情还没有完成而继续在该程序中不返回。比如,在SimpleBLEPeripheral实例工程中,当事件START_DEVICE_EVT发生,其处理函数SimpleBLEPeripheral_ProcessEvent就运行,结束后返回16bit事件变量,并清除旗语 SBP_START_DEVICE_EVT。每当OSAL事件检测到了有任务事件,其相应的处理进程将被添加到由处理进程指针构成的事件处理表单中,该表单名叫taskArr(taskarray)。taskArr中各个事件进程的顺序和osalInitTasks初始化函数中任务ID的顺序是对应的。有两种,最简单的方法是使用osal_set_event函数(函数原型在OSAL.h文件中),在这个函数中,用户可以像定义函数参数一样设置任务ID 和事件旗语。第二种方法是使用osal_start_timerEx函数(函数原型在OSAL_Timers.h文件中),使用方法同 osal_set_event函数,而第三个以毫秒为单位的参数osal_start_timerEx则指示该事件处理必须要在这个限定时间内,通过定时器来为事件处理计时。类似于Linux嵌入式系统内存分配C函数mem_alloc,OSAL利用osal_mem_alloc提供基本的存储管理,但osal_mem_alloc只有一个用于定义byte数的参数。对应的内存释放函数为osal_mem_free。不同的子系统通过OSAL的消息机制通信。消息即为数据,数据种类和长度都不限定。消息收发过程描述如下:接收信息,调用函数osal_msg_allocate创建消息占用内存空间(已经包含了osal_mem_alloc函数功能),需要为该函数指定空间大小,该函数返回内存空间地址指针,利用该指针就可把所需数据拷贝到该空间。发送数据,调用函数osal_msg_send,需为该函数指定发送目标任务,OSAL通过旗语SYS_EVENT_MSG告知目标任务,目标任务的处理函数调用osal_msg_receive来接收发来的数据。建议每个OSAL任务都有一个消息处理函数,每当任务收到一个消息后,通过消息的种类来确定需要本任务做相应处理。消息接收并处理完成,调用函数osal_msg_deallocate来释放内存(已经包含了osal_mem_free函数功能)。为了实现更好的移植性,协议栈将硬件层抽象出了一个HAL硬件抽象层,当新的硬件平台做好后,只需修改HAL,而不需修改HAL之上的协议栈的其他组件和应用程序。第十五节 BLE蓝牙4.0协议栈启动分析TI的这款CC2540/CC2541器件可以单芯片实现BLE蓝牙协议栈结构图的所有组件,包括应用程序。从这章开始我们来剖析协议栈源码,我们选用 SimpleBLEPeripheral工程开刀,这是一个从机的例程,基本的工作是对外广播,等待主机来连接,读写展示的属性。首先打开工程文件,打开后可以看到整个工程的结构。我们按照系统的启动顺序来一步一步走,我们都知道在C代码中,一般启动的首个函数为main,这个函数在 SimpleBLEPeripheral_Main.c中,打开文件,可以看到这个文件只有一个main函数和一个函数的申明,我们暂时不理会那个申明的函数,先看main都做了些什么工作:Int main(void)/* Initialize hardware */HAL_BOARD_INIT(); / 硬件初始化/ Initialize board I/OInitBoard( OB_COLD ); / 板级初始化/* Initialze the HAL driver */HalDriverInit(); / Hal驱动初始化/* Initialize NV system */osal_snv_init(); / Flash存储SNV初始化/* Initialize LL */* Initialize the operating system */osal_init_system(); / OSAL初始化/* Enable interrupts */HAL_ENABLE_INTERRUPTS(); / 使能总中断/ Final board initializationInitBoard( OB_READY ); / 板级初始化#if defined ( POWER_SAVING )osal_pwrmgr_device( PWRMGR_BATTERY ); / 低功耗管理#endif/* Start OSAL */osal_start_system(); / No Return from here 启动OSALreturn 0;通过代码我们可以看到,系统启动的过程,主要是做了一些初始化,如果开启了低功耗,则还需要开启低功耗管理。我们先不去理会初始化做了什么,但是我们知道在main函数的最后启动了OSAL,那么我们就进去看看OSAL是如何运作的。在IAR中如果需要跳转到某个函数或变量的定义,可以在此函数名中右击然后选择Go To Definition就可以调到相应的定义。void osal_start_system( void )#if !defined ( ZBIT ) & !defined ( UBIT )for(;) / Forever Loop#endifosal_run_system();这里看到我们进入了一个死循环,并且一直调用osal_run_system(),那我们再进入此函数。void osal_run_system( void )uint8 idx = 0;#ifndef HAL_BOARD_CC2538osalTimeUpdate(); / 定时器更新#endifHal_ProcessPoll(); / Hal层信息处理do if (tasksEventsidx) / Task is highest priority that is ready.break; while (+idx tasksCnt); / 检查每个人任务是否有事件if (idx tasksCnt) / 有事件发生uint16 events;halIntState_t intState;HAL_ENTER_CRITICAL_SECTION(intState); / 进入临界区events = tasksEventsidx;tasksEventsidx = 0; / Clear the Events for this task. 清除事件标志HAL_EXIT_CRITICAL_SECTION(intState); / 退出临界区activeTaskID = idx;events = (tasksArridx)( idx, events ); / 执行事件处理函数activeTaskID = TASK_NO_TASK;HAL_ENTER_CRITICAL_SECTION(intState); / 进入临界区tasksEventsidx |= events; / Add back unprocessed events to the current task.HAL_EXIT_CRITICAL_SECTION(intState); / 退出临界区#if defined( POWER_SAVING ) / 没有事件发生,并且开启了低功耗模式else / Complete pass through all task events with no activity? / 系统进入低功耗模式osal_pwrmgr_powerconserve(); / Put the processor/system into sleep#endif/* Yield in case cooperative scheduling is being used. */#if defined (configUSE_PREEMPTION) & (configUSE_PREEMPTION = 0)osal_task_yield();#endif在这里可以看到这个OSAL的核心,整个OSAL通过检测每个任务是否有事件发生,如果有则执行相应的任务,处理相应的事件。如果没有事件需要处理并且开启了低功耗模式,则系统就会进入低功耗模式。这里有一个很关键的地方,OSAL是如何知道哪个事件需要哪个任务来处理呢?events = (tasksArridx)( idx, events ); / 执行事件处理函数我们看这里有一个很关键的数组tasksArr,很显然,这是一个函数指针数组,我们看看它的定义。const pTaskEventHandlerFn tasksArr =LL_ProcessEvent, / task 0Hal_ProcessEvent, / task 1HCI_ProcessEvent, / task 2#if defined ( OSAL_CBTIMER_NUM_TASKS )OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), / task 3#endifL2CAP_ProcessEvent, / task 4GAP_ProcessEvent, / task 5GATT_ProcessEvent, / task 6SM_ProcessEvent, / task 7GAPRole_ProcessEvent, / task 8GAPBondMgr_ProcessEvent, / task 9GATTServApp_ProcessEvent, / task 10SimpleBLEPeripheral_ProcessEvent / task 11;可以看到在这个数组的定义中,每个成员都是任务的执行函数,按照任务的优先级排序,并且在osalInitTasks中初始化的时候,我们可以看到每个任务都有一个对应的初始化函数,并且传递了一个taskID,此ID从0开始自增,这里有一点非常重要,初始化的顺序和任务数组的定义顺序是一样的,这就保证了我们给任务发生消息或事件时能够准确的传递到相应的任务处理函数。void osalInitTasks( void )uint8 taskID = 0;tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt);/* LL Task */LL_Init( taskID+ );/* Hal Task */Hal_Init( taskID+ );/* HCI Task */HCI_Init( taskID+ );#if defined ( OSAL_CBTIMER_NUM_TASKS )/* Callback Timer Tasks */osal_CbTimerInit( taskID );taskID += OSAL_CBTIMER_NUM_TASKS;#endif/* L2CAP Task */L2CAP_Init( taskID+ );/* GAP Task */GAP_Init( taskID+ );/* GATT Task */GATT_Init( taskID+ );/* SM Task */SM_Init( taskID+ );/* Profiles */GAPRole_Init( taskID+ );GAPBondMgr_Init( taskID+ );GATTServApp_Init( taskID+ );/* Application */SimpleBLEPeripheral_Init( taskID );应用层的初始化

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论