BLE蓝牙 40 学习笔记_第1页
BLE蓝牙 40 学习笔记_第2页
BLE蓝牙 40 学习笔记_第3页
BLE蓝牙 40 学习笔记_第4页
BLE蓝牙 40 学习笔记_第5页
已阅读5页,还剩33页未读 继续免费阅读

下载本文档

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

文档简介

本文格式为Word版,下载可任意编辑——BLE蓝牙40学习笔记蓝牙4.0BLEcenter与peripheral建立连接绑定过程2蓝牙4.0BLEperipheral广播设置7蓝牙4.0BLE数据传输(一)11蓝牙4.0BLE数据传输(二)12蓝牙4.0BLE数据传输(三)16蓝牙4.0BLE数据传输(四)19蓝牙4.0BLE数据传输(五)23蓝牙4.0BLE程序设计相关问题解答(转载)25蓝牙4.0BLESimpleBLEPeripheral_添加新CHAR值及UUID33蓝牙4.0BLEperipheral广播设置学习笔记(转载)45蓝牙4.0BLEkey处理过程看任务、事件、消息机制50CC254x内部存储结构FLASH53蓝牙4.0BLEFLASH操作58

蓝牙4.0BLEcenter与peripheral建立连接绑定过程

蓝牙主机从机建立连接绑定过程

center与simplePeripheral建立连接过程

center首先进行osal_init_system()初始化各个任务,SimpleBLECentral_Init->osal_set_event(simpleBLETaskId,START_DEVICE_EVT);进入SimpleBLECentral_ProcessEvent()调用VOIDGAPCentralRole_StartDevice((gapCentralRoleCB_t*)//当时始化完成,会发送GAP_DEVICE_INIT_DONE_EVENT由于注册了simpleBLERoleCB函数,因此发送的event由simpleBLERoleCB函数接收staticvoidsimpleBLECentralEventCB(gapCentralRoleEvent_t*pEvent)此时pEvent->gap.opcode=GAP_DEVICE_INIT_DONE_EVENT,相应信息存储于pEvent中typedefunion{

gapEventHdr_tgap;//!deviceInfo.pEvtData,pEvent->deviceInfo.dataLen)){

//HalUARTWrite(0,\数据长度pEvtData数据

simpleBLEAddDeviceInfo(pEvent->deviceInfo.addr,pEvent->deviceInfo.addrType);}}}

break;

此时已经获得了扫描到的设备个数,以及设备地址,还有广告内容等信息。

通过串口发送‘3’来建立连接建立连接:typedefstruct{

uint8taskID;//!

主机主动发起的。而从机若想主动发送数据给主机那么我们就只能用notification的方式发送。我们的主机一半作为GATT层的client,而从机一半作为service。

蓝牙4.0BLE数据传输(三)

上一回我们谈到了数据传输的过程以及如何发送和接收数据,那么今天我们谈谈特征值。首先看一下SIG给出的定义

一个特点是用属性以及如何访问该值的配置信息和关于如何的值是显示或为代表的信息服务中的值。

特征值在GATT的分类,再来看SIG的定义:

在GATT层中,一个特征是由其特性的定义定义的。特征定义包含特性的宣言、特征属性和一个值,并且可能包含描述符描述的值或允许的特点对服务器的配置。我们再看看特征值是如何在协议栈中实现的。首先我们看一下关于特征值定义的一些宏。

//PrimaryServiceUUID主服务的UUID相当于一辆公交车的车牌,公交车里面又有不同的乘客,这些乘客就是主服务携带的信息

CONSTuint8primaryServiceUUID[ATT_BT_UUID_SIZE]={

LO_UINT16(GATT_PRIMARY_SERVICE_UUID),HI_UINT16(GATT_PRIMARY_SERVICE_UUID)};

//SimpleGATTProfileServiceUUID:0xFFF0

CONSTuint8simpleProfileServUUID[ATT_BT_UUID_SIZE]={

LO_UINT16(SIMPLEPROFILE_SERV_UUID),HI_UINT16(SIMPLEPROFILE_SERV_UUID)};

//SimpleProfileServiceattribute

staticCONSTgattAttrType_tsimpleProfileService={ATT_BT_UUID_SIZE,simpleProfileServUUID};

//Profile参数

#defineSIMPLEPROFILE_CHAR10//RWuint8-ProfileCharacteristic1value

//SimpleProfileServiceUUID

#defineSIMPLEPROFILE_SERV_UUID0xFFF0//service的UUID//KeyPressedUUID

#defineSIMPLEPROFILE_CHAR1_UUID0xFFF1

//SimpleProfileCharacteristic1Properties特征值属性(读/写)

staticuint8simpleProfileChar1Props=GATT_PROP_READ|GATT_PROP_WRITE;

//Characteristic1Value特征值的值staticuint8simpleProfileChar1=0;

//SimpleProfileCharacteristic1UserDescription特征值的用户说明staticuint8simpleProfileChar1UserDesp[17]=\

定义了特征值所需要的相关宏以后,我们需要将特征值填写到特征值列表中,

staticgattAttribute_tsimpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED]={

//SimpleProfileService{

{ATT_BT_UUID_SIZE,primaryServiceUUID},

GATT_PERMIT_READ,0,(uint8*)

这样就把特征值添加到列表中了,假使我们需要添加其他的一些特征值,可以直接在Characteristic1后类似的连接Characteristic2Characteristic3Characteristic4等等。是不是很简单啊,照葫芦画瓢,一看就会,是吧?

读取服务的值,需要知道服务的UUID或者Handle;写服务的值,需要知道服务的Hanle;写服务描述符,需要知道该Descriptor的Hanle。如何知道一个服务的Handle?

根据服务的UUID调用API函数GATT_ReadUsingCharUUID协议栈会返回该服务的Handle。特别注意的是,一个服务的Descriptor的Handle总是该服务的Handle+1,如电池电量服务的Handle是0x0025,那么它的Descriptor的Handle是0x0026。获取Handle:

attReadByTypeReq_treq;

simpleBLEDiscState=BLE_DISC_STATE_CHAR;req.startHandle=simpleBLESvcStartHdl;req.endHandle=simpleBLESvcEndHdl;req.type.len=ATT_BT_UUID_SIZE;

req.type.uuid[0]=LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);req.type.uuid[1]=HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);

GATT_ReadUsingCharUUID(simpleBLEConnHandle,

把读到的数据拆包,提取handle的值:simpleBLECharHdl=BUILD_UINT16(pMsg->msg.readByTypeRsp.dataList[0],pMsg->msg.readByTypeRsp.dataList[1]);

写数据:

attWriteReq_treq;

req.handle=simpleBLECharHdl;

req.len=1;

req.value[0]=simpleBLECharVal;req.sig=0;req.cmd=0;status=GATT_WriteCharValue(simpleBLEConnHandle,最终说一下我的理解:

特征值就是蓝牙提供给外设进行数据交互的接口。通过写摸个特征值,就可以把数据传给对方,通过读特征值就能读到对方的数据。

蓝牙4.0BLE数据传输(四)

今天我们做一个简单的数据传输试验,就是用TI提供的lightblue来点亮控制LED

首先我们来看一下用手机APP来控制led,我是用app来控制cc2540这个模块,那么我的模块就是从机,从机一般都是service,那么手机就是主机,是client。

这个很简单,我没有用协议栈里面的hal_led,由于我看了,这个很麻烦,明明两句话的事情非搞得那么繁杂,废话不说直接来分析。开启cc2540协议栈中的例子SimpleBLEPeripheral.eww。

首先我们得把hal_led的宏关了,

添加HAL_LED=FALSE,这样BLE协议栈里面的HAL_LED就算是废了。

接下来看我的,首先对led初始化,这个就简单了,就是和我们的51是一样的,我是在InitBoard函数里面添加的led初始化:P1DIR|=0x02;//设置P1_0,P1_1的io为输出模式P1_0=0;//关闭led1P1_1=0;//关闭led2

就这样初始化搞定了,下面我们就回想一下,client向主机发送数据用的是write,就是手机把数据写进cc2540里面去。在我们的lightblue这个软件里,characteristic3有些的属性。因此我们就可以更改一下源代码。首先我们定义我们的任务事件

#defineMYTASCK_EVENT0x0002;定义完以后我们得触发事件啊对吧?

那我们就在SBP_START_DEVICE_EVT(自己定义的SimpleBLEPeripheral_Init这个事件用的是

osal_set_event(simpleBLEPeripheral_TaskID,SBP_START_DEVICE_EVT);这个函数触发我们的SimpleBLEPeripheral_Init事件)事件里面进行操作。那么我们该如何触发自己定义的mytasckevent事件呢?

1-OSAL.C

*@fnosal_strlen计算字符串的长度。字符串必需空结尾。*@fnosal_memcpy一般内存复制*@fnosal_revmemcpy

//一般的颠倒内存复制。从资源缓存的最终开始,通过使用资源地址的指针移动len字节的,然后指针自减。*@fnosal_memdup

//分派一个缓存(和osal_mem_alloc()),并复制这个资源缓存到刚分派好的控件.*@fnosal_memcmp一般内存比较*@fnosal_memset设置内存缓存值*@fnosal_rand产生随机数

*@fnosal_msg_allocate动态分派一个OSAL消息缓存

*@fnosal_msg_deallocate释放一个OSAL消息缓存(接收任务调用)*@fnosal_msg_send为某个任务发送一个OSAL消息*@fnosal_msg_receive检索接收命令消息。

*@fnosal_msg_find发现在一个OSAL消息匹配任务ID和事件参数。*@fnosal_msg_extract

*//从OSAL队列中间提取和移除一个OSAL消息

*@fnosal_set_event为一个任务设置一个OSAL事件*@fnosal_clear_event清除一个任务的事件标志。

*@fnosal_init_systemOSAL初始化,必需在主函数中被调用*@fnosal_start_system开始运行OSAL主循环

*@fnosal_run_system现至少一个等待事件的第一个任务。

2-OSAL_bufmgr.c

*@fnosal_bm_alloc分派器函数的实现

*@fnosal_bm_free取消分派器函数的实现*@fnosal_bm_adjust_header调整header*@fnosal_bm_adjust_tail调整tail

3-OSAL_cbtimer.c

*@fnosal_CbTimerInit初始化函数

*@fnosal_CbTimerProcessEvent//回调定时器任务事件处理函数*@fnosal_CbTimerStart//当定时器到期,注册调用函数将被调用。*@fnosal_CbTimerUpdate更新一个已经启动的消息定时器*@fnosal_CbTimerStop//中止一个已经启动的定时器。

4-OSAL_ClockBLE.c

*@fnosalTimeUpdate更新OSAL时间和定时器,

*@fnosalClockUpdate//用运行ms更新OSAL时间表,

*@fnosal_setClock//设置新时间。*@fnosal_getClock//得到当前时间。

*@fnosal_ConvertUTCTime转换时间格式*@fnosal_ConvertUTCSecs转换时间格式

5-OSAL_Memory.c

*@fnosal_mem_init内存管理初始化*@fnosal_mem_kick//任务初始化

*@fnosal_mem_alloc执行OSAL动态内存分派函数*@fnosal_mem_free//执行OSAL动态内存释放函数

6-OSAL_PwrMgr.c

*@fnosal_pwrmgr_init//初始化电源管理系统

*@fnosal_pwrmgr_task_state//每一个任务调用函数,来陈述是否这个任务要保存电源*@fnosal_pwrmgr_powerconserve//当没有活动安排这个函数从主循环被调用,不应当从别的地方被调用。

7-OSAL_snv.c

8-OSAL_timers.c

*@fnosalTimerInit初始化OSAL定时器系统*@fnosalAddTimer增加一个定时器到定时器列表*@fnosalFindTimer在定时器列表中发现一个定时器*@fnosalDeleteTimer从一个列表中删除一个定时器*@fnosal_start_timerEx//调用开始一个定时器在nms内*@fnosal_stop_timerEx//中止一个已经启动的定时器。*@fnosalTimerUpdate更新定时器结构

*@fnosal_adjust_timers更新已经过去的定时器结构*@fnosal_GetSystemClock读取本地系统时钟

==================================================================以上是一个BLE应用里的OSAL所用到的函数名字,及其功能,便利以后查询。==================================================================

APP文件夹中(bloodpressure.cVSsimpleblebroadcaster.c)

BLE学习应用笔记2

1.安装D:\\TI资料\\BLE-Stack

安装目录文件夹下C:\\TexasInstruments\\BLE-CC254x-1.2.1\\Projects\\ble下有应用列子程序。

2.安装IAR软件

安装目录D:\\TI资料\\IAREmbeddedWorkbench

3.安装SmartRFFlashProgrammer软件SmartRFPacketSnifferSmartRFStudio7

安装目录D:\\TI资料\\TISoftware\\

4.阅读文件夹BLE-CC254x-1.2.1\\下的例子程序。

5.APP应用层

HAL硬件驱动层OSAL操作系统层

PROFILES配置文件层:

BLE协议栈的结构和配置在这一层设置。GAP提供关于连接的服务设置SMP安全管理

ATT属性结构设置

GATT使用属性协议发现,服务或其他关联属性之间分组信息

BLE学习应用笔记3

BLE学习应用笔记3

1.搭建硬件:按接口连接好硬件:USB->小口USB->CCDebugger->DBG_CONNECTOR->设备1或设备2(usb的插头小块向内/插电池的插头小块向外)

2.搭建之前安装软件:SmartRFStudio和SmartRFFlashProgrammer.

3.设备连接好后,电脑提醒要求安装驱动:选择从指定位置安装:C:\\ProgramFiles\\TexasInstruments\\SmartRFTools\\Drivers\\Cebal\\win_32bit_x86(默认位置)

插入设备后还需安装一个驱动:C:\\TexasInstruments\\BLE-CC254x-1.2.1\\Accessories\\Drivers。之后,重启CCDebugger,绿灯亮后,即可正常调试。

4.开启SmartRFStudio软件。“2.4GHz〞下选择“CC2540〞

5.下载程序:SmartRFFlashProgrammer,选择“SoC〞,选择所下载文件*.hex所在文件夹,“Actions〞中选择“Erase,programandverify〞,下载点击“performactions〞

BLE学习应用笔记4

1,连接好硬件,安装好驱动,USBDongle小是主设备。

2.查看连接的com口。

3.使用BTool软件:

开启BTool,位置在C:\\TexasInstruments\\BLE-CC254x-1.2.1\\Projects\\BTool。

选择com口,其余默认(115200,cts/rts,none,one,8),然后OK。

4.下载程序:FlashProgrammer或者IAR

BLE学习应用笔记5OSAL中断操作:1、允许中断

uint8osal_int_enable(uint8interrupt_id)--interrupt_id:中断标示符2、阻止中断

uint8osal_int_disable(uint8interrupt_id)--interrupt_id:中断标示符3、暂停中断

HAL_ENTER_CRITICAL_SECTION(x)4、重新启用中断

HAL_EXIT_CRITICAL_SECTION(x)OSAL内存操作:1、分派内存

void*osal_mem_alloc(uint16size)--size:分派缓冲区的大小2、释放内存

voidosal_mem_free(void*ptr)--ptr:指向释放的缓冲区OSAL消息传递:1、分派信息缓冲区

byte*osal_msg_allocate(uint16len)--len:信息的长度2、发送信息

byteosal_msg_send(bytedestination_task,byte*msg_ptr)

--destination_task:接收信息任务的标示符--msg_ptr:消息指针3、接收信息

byte*osal_msg_receive(bytetask_id)--task_id:接收信息的任务ID4、释放消息缓冲区

byteosal_msg_deallocate(byte*msg_ptr)

--msg_ptr:消息指针OSAL任务管理:1、任务初始化

byteosal_init_system(void)--要创立的任务列表2、任务开始

voidosal_start_system(void)--系统任务的主循环函数3、获取活动任务IDbyteosal_self(void)

--中断服务子程序中调用将会发生错误4、事件句柄的函数原型

(*pTaskEventHandlerFn)(unsignedchartask_id,unsignedshortevent)

OSAL定时器:1、启动定时器

byteosal_start_timerEx(bytetaskID,UINT16event_id,UINT16timeout_value)--taskID:定时器终止时事件任务的任务ID

--event_id:用户定义的事件,时间终止时通知这个事件--定时器设置前的时间量2、中止定时器

byteosal_stop_timerEx(bytetask_id,UINT16event_id)--task_id:事件任务的任务ID--event_id:用户自定义事件3、读取系统时钟

uint32osal_GetSystemClock(void)--用来读取系统时钟(毫秒级)

BLE学习应用笔记6将\事件\理解成按键,将\任务\理解成蜂鸣器.

也就是说按键被按下蜂鸣器就响起来.对应\事件\任务\然而,具体是哪一个按键被按下了.那么得利用事件附带的\消息\来判定,K1,K2??说白了\消息\就是附带物.

蓝牙4.0BLEkey处理过程看任务、事件、消息机制

一事件机制

每一个任务都会有好多种事件,包括系统消息、任务事件等等。

在这之前我们得介绍一下新的apiosal_start_timerEx

osal_start_timerEx(SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT+(osal_rand()

该函数是每隔5s显示一次,去掉后,节点参与网络后,只显示一次,就是说这个函数但是一个定

时出发的函数,每隔(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT+(osal_rand()

//StartBondManager

VOIDGAPBondMgr_Register(

//Settimerforfirstperiodiceventosal_start_timerEx(simpleBLEPeripheral_TaskID,SBP_PERIODIC_EVT,MY_TASCK_EVT_PERIOD);

return(events^SBP_START_DEVICE_EVT);}

if(events

}

//执行自己的任务mytasck();

return(events^MYTASCK_EVENT);}

voidmytasck(){

uint8valueToCopy;uint8stat;

stat=SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3,if(valueToCopy==0x01){P1_0=1;P1_1=1;}else{P1_0=0;P1_1=0;}}

就这样,编译以后把程序烧到板子里面就ok了,

在这里输入1,那么我们的led就亮了,输入其他的数值我们的led就会熄灭。

蓝牙4.0BLE数据传输(五)

我们的从service给client传递数据有两种方式:

第一种:直接通过GATT_Notification()这个函数来通知client

其次种:通过GATTServApp_ProcessCharCfg()这个函数间接的通知client为什么说GATTServApp_ProcessCharCfg()是一种间接方式呢?

原因是GATTServApp_ProcessCharCfg()再会让主机向slave去发送一个read的请求,然

后调用simpleProfile_ReadAttrCB()函数,然后再执行里面的赋值语句caseSIMPLEPROFILE_CHAR4_UUID:*pLen=1;

pValue[0]=*pAttr->pValue;break;

那么假使notification已经被使能,GATTServApp_ProcessCharCfg()函数内部还是会调用GATT_Notification()函数,所以我就说GATTServApp_ProcessCharCfg()是一种间接发送notification的方式,现在大家明白了吧?

还不懂?那就看代码:

1、GATTServApp_ProcessCharCfg方式:

caseSIMPLEPROFILE_CHAR4:if(len==sizeof(uint8)){

simpleProfileChar4=*((uint8*)value);

GATTServApp_ProcessCharCfg(simpleProfileChar4Config,直接调用这一句就可以了,我们来看一下这里面的参数,@1:特征表配置参数,@2:传递的数据,

@3:判断是否经过省份验证,TUREFALSE@4:属性表

@5:在属性表中属性的数目@6:任务的确认通知

接下来,函数的内部会自动调用simpleProfile_ReadAttrCB()这个函数,这个函数再根据UUID做相应的操作,譬如说赋值:caseSIMPLEPROFILE_CHAR4_UUID:*pLen=1;

pValue[0]=*pAttr->pValue;break;这样,就完成了发送通知,当然我们省略了填写属性表的步骤(这个前面讲过了就不说了)。

2、GATT_Notification方式这个简单,直接上代码

staticattHandleValueNoti_tpReport;//声明attHandleValueNoti_t这个结构体uint16noti_cHandle;//存放handle

pReport.handle=simpleProfileAttrTbl[11].handle;//读取notification对应的handleGAPRole_GetParameter(0x30E,//获取ConnectionHandlepReport.len=1;//数据长度pReport.value[0]=0x03;//赋值

GATT_Notification(noti_cHandle,

这样是不是一目了然?,当然假使我们要用notification的话,建议使用直接调用GATT_Notification函数这种方式。

蓝牙4.0BLE程序设计相关问题解答(转载)

1.问:什么是蓝牙通信?

答:蓝牙通讯最初设计初衷是便利移动电话(手机)与配件之间进行低成本、低功耗无线通信连接,现在已经成为IEEE802.15标准,得到全球上万家厂商支持。

2.问:假使从事蓝牙开发有没有前途?答:严格地说,这不是一个技术问题,而是一个世界观问题。什么是前途?假使单纯是金钱,从事技术是不太可能暴富的(注意比尔.盖茨是个技术商人);假使想用你所能改善世界,这是可能的,终究蓝牙的主要用途是民用。附带说一句,考虑赚钱和改变世界是中国和西方人世界观的主要区别。

3.问:蓝牙有什么优势?

答:首先是低功耗,以BLE4.0为例,一节钮扣电池在静态工作状态可以支持一年;其次是低成本,TI公司的CC2540蓝牙SOC方案芯片出售价仅1美元,可以让人们低廉使用蓝牙技术;再次是开放性,2.4GHz的频段全球开放,没有政府监管;最终是适合时代潮流,现在是手机的时代,蓝牙技术本来就为它而生。

4.问:蓝牙4.0协议和BLE是什么?

答:蓝牙4.0协议是2023年6月由SIG(SpecialInterestGroup)发布的最新标准,它有2种模式:BLE(Bluetoothlowenergy)只能与4.0协议设备通信,适应节能且仅收发少量数据的设备(如家用电子);BR/EDR(BasicRate/EnhancedDataRate),向下兼容(能与3.0/2.1/2.0通信),适应收发数据较多的设备(如耳机)。

5.问:目前支持蓝牙4.0的移动设备有哪些?答:苹果公司的iPhone4S、iPhone5、miniPad和iPad3;小米手机2;三星公司的GalaxySIII和NoteII;HTCONE系列。

6.问:如何开始蓝牙4.0的开发呢?

答:概括地讲至少以下三方面的准备吧。硬件方面,需要购买TI公司蓝牙迷你套件,包括

蓝牙USB电子狗和KeyFob以及CCDebugger传真器;软件方面,安装IARfor8051,TI公司BTool软件;技术知识,《CC2540/41BLESoftwareDeveloper’sGuide1.3》和《CC2540/41User’sGuide》。

7.问:刚开始接触蓝牙如何快速上手?

答:理论联系实践是比较好的学习方法,建议先学习《CC2540/41BLESoftwareDeveloper’sGuide1.3》,然后将SimpleBLEPerepheral工程导入IARfor8051,结合电子狗和BTool,调试蓝牙通讯中的广播/连接/绑定/访问。光看书不动手,空虚;不看书光动手,浅薄。

8.问:IAR调试CC2540时程序导入到了芯片的Flash中了吗?

答:确实。CC2540是SOC(SystemOnChip)芯片,它的内核就是8051,它需要从ROM中取指令,从RAM中取数据来运行。仿真时,CCDebugger会把程序导入芯片Flash中,再执行仿真。

9.问:当IAR调试中出现警告“缺少断点,无法运行到main()〞?

答:出现这个错误的原因是,IARfor8051最多只能设置3个断点,假使设置过多,当程序下载后,将出现些调试警告。解决的方法很简单,去掉一些断点,再重新载入程序。

10.问:为什么IAR调试时有好多变量无法查看它的值?

答:主要的原因是IAR编译器设置了优化功能,函数中的自动变量以及一些静态函数都被优化过了,所以没有生成对应的调试信息,无法查看和设置断点。解决的方法是关闭编译器的优化功能,右键点击工程的Options->C/C++Compiler->Optimizations中的Level设置为None。

11.问:蓝牙协议分层好多且比较繁杂,该如何把握呢?

答:蓝牙协议从应用层到物理层一共分了8层,看上去比较繁杂且API函数好多。首先不必要知道每一层的具体实现,把握与应用紧凑关联GAP/GATT(或者GAPRole和GATTProfiles)层就可以满足大部分设计需要;每一层的软件都是通过OSAL来调用的,因此需要了解OSAL的基本原理:任务/事件/消息/定时器/动态分派内存;最终把蓝牙通讯过程理解,将有助于开发。

12.问:OSAL是一个操作系统吗?

答:OSAL(OperatingSystemAbstractionLayer)操作系统抽象层,它不是一个真正的操作系统(它没有ContextSwitch上下文切换功能),但它巧妙地组织各任务,支持任务优先级,任务之间可以通过事件和消息来通信,为任务提供软定时器和动态内存分派。要避免的陷阱是,应用任务的单个函数运行时间不能太长(如操作大批量数据的Flash写),否则它无法及时调度高优先级的LL(LinkLayer)任务而导致蓝牙通信中断。

13.问:蓝牙节点是如何组成微微网的呢?

答:蓝牙节点组网中,只能存在一个主节点(Central)和多个从节点(Peripheral),从节点是发出信号者,主节点是扫描且发起连接者。

14.问:主节点和从节点通信的过程是怎样的呢?

答:当从节点发出广告信号(包括设备地址和设备名称之类的附加信息);主节点收到此广

告信号后,向从节点发出扫描请求;当从节点回应扫描时,就完成了设备发现过程。接着主节点向从节点发出连接请求(包括连接时隙、从节点待机次数、连接超时值),从节点回应连接,就完成了建立连接。

为了安全起见,一些数据的访问需要认证,它的完成是这样的:一方(可以是主节点,也可以是从节点)向另一方索要6位数字的密码,之后,两个节点彼此交换安全密钥用于加密和认证,此过程称为配对。

认证的过程比较繁琐,BLE协议支持两节点保存认证的安全密钥(一般是非易失性存储器中),以便于两节点下次连接后快速认证,这就是绑定技术。

15.问:蓝牙通信中两个节点如何交换数据?

答:这是蓝牙通信中最让初学者迷惑的地方。大部分通信,特别是TCP/IP,交换数据的婚介是数据包,但蓝牙通信中,工程师找不到数据包访问方式,于是就产生疑问。其实蓝牙最底层也是基于无线数据包交换,只是通过层层封装,交付给工程师的API接口就变成了Client访问Server的方式。

16.问:Client和Server节点是如何定义呢?

答:通俗地说吧,Server(服务器)就是数据中心,Client(客户端)就是访问数据者。特别说明,它与主/从设备是独立的概念:一个主设备既可以充当Server,又可以充当Client;从设备亦然。

17.问:Server是如何提供数据呢?

答:Server首先将一个服务按“属性/句柄/数值/描述〞这种格式予以组织,然后调用API函数GATTServApp_RegisterService将服务数据进行注册。举个实例吧,设提供一个电池电量服务字节,它允许Client读取,数据为一个8比特无符号数(0~100%),它的组织如下:022500192A,这5个数据(小端格式)分别是:0x02=只读属性,0x0025=句柄;0x2A19=服务UUID。

18.问:不明白Server提供服务中的UUID?

答:UUID(UniversalUniqueIdentifier)全球惟一标识符,本来是SIC组织分派给特定蓝牙服务的标识,如分派0x2A25为设备序列号的UUID,这样任意蓝牙设备都可以通过它得到另一个设备的序列号。

打个类比,它就像书名,如《现代操作系统》,所有人一看就知道它是计算机大师AndrewS.Tanenbaum写的书。

19.问:什么是Server提供服务中的句柄呢?

答:句柄(Handle)就是服务数据在数据中心的地址,当所有的服务数据组织起来后,它总得有个先后顺序,某个服务的位置就是它的句柄。还是上面的类比,假使想去图书馆借阅《现代操作系统》,需要查明该书在哪一层楼,哪个房间,这就是该书的Hanle。

20.问:为什么Server提供的服务中有描述?

答:有些服务是有描述(Descriptor)的,它是用于Client配置该服务的功能(通知或者显示)。像某人没有借到《现代操作系统》该书(可能是被别人借光了),他(她)可以打个电话给图书馆工作人员,请求一旦该书可以借阅了给他一个通知,这个过程相当于配置该书的Descriptor。

21.问:服务的属性与描述有区别吗?

答:有区别,服务的属性是Server设置访问权限。就像图书馆的工作人员可以设置《现代操作系统》仅能在阅览室看不能外借(只读),或者即可以看也可以外借(读/写)。22.问:Client如何访问Server的服务呢?

答:大致分三类:读取服务的值,需要知道服务的UUID或者Handle;写服务的值,需要知道服务的Hanle;写服务描述符,需要知道该Descriptor的Hanle。

23.问:如何知道一个服务的Handle?

答:根据服务的UUID调用API函数GATT_ReadUsingCharUUID

协议栈会返回该服务的Handle。特别注意的是,一个服务的Descriptor的Handle总是该服务的Handle+1,如电池电量服务的Handle是0x0025,那么它的Descriptor的Handle是0x0026。

24.问:Server可以访问Client吗?

答:蓝牙通信中,Server不能直接访问(读/写)Client,但是可以通知(Notification)Client,通知的前提是Client通过写Descriptor使能通知功能。例如,某Server发现电池电量已经低于安全阀值,它可以调用GATT_Notification通知所有已连接的Client,但是Client接收后假使处理是它自己的事情。

25.问:假使得知电池容量?答:任何使用电池供电的设备都必需确切监控电池容量,否则设备可以突然断电而中止工作,它的基本原理是通过ADC(模数转换器)计算电池电压。以CC2540芯片用一钮扣电池为例,电池电压从2.0v~3.0v,即电量的0%~100%;CC2540有一10比特的ADC,量程范围为0~511,参考电压为1.25v,最大测量电压为3.75v,以上信息可以得知:(v/3)/1.25*511=adc,则2.0v=273adc,3.0v=409adc,根据下图可以很简单得知ADC转换为电压的公式:

Percentage/(X–273)=100/136=25/34,变换后为:

Percentage=(X-273)*25/34,为四舍五入提高计算精度则有:Percentage=[(X-273)*25+33]/34。

26.问:蓝牙发射信号功率调整会影响通信距离吗?答:会,以TI公司的CC2540为例,它支持4种发射功率选择:4dBm、0dBm、-6dBm和-23dBm,按无线电功率定义:LdBm=10lg(Pwr/1mW),以上4种分贝值换算成瓦特为:2.51mW、1mW、0.251mW和0.005mW,有效通信距离分别为:30米、10米、7米和3米。

27.问:如何知道两个蓝牙通信节点之间的距离?

答:要知道蓝牙通信节点(如手机和蓝牙设备)之间的距离,最简单实现的方法是通过读取接收RSSI(ReceivedSignalStrength

Indication)值来计算。无线通讯中功率与距离的关系如下:

其中A可以看作是信号传输1米远时接收信号的功率,n是传播因子(它受障碍,温度和湿度等影响),r是节点之间的距离。当确定了常数A与n的值后,距离r就可以根据PR(dBm)计算出来。

28.问:如何获取蓝牙节点的接收RSSI值?

答:具体的设备接收RSSI值的方法不一样,以iPhone手机为例,iOS提供API函数获取RSSI值;TI公司的CC2540芯片的BLE协议栈中,首先将读取RSSI值回调函数挂载到

gapRolesRssiRead_t类型的指针下,建立连接后,主设备调用GAPCentralRole_StartRssi(),从设备调用

GAPRole_SetParameter(GAPROLE_RSSI_READ_RATE,??)。这样就可以定时读取接收的RSSI值了。

29.问:如何开展读取RSSI值的试验?

答:读取RSSI值的试验可以这样搭建,主设备固定位置,向从设备发送信号,从设备LED光和Buzzer报警为通信成功,逐次移动从设备,而获取RSSI值随物理距离之间的关系。下图是笔者做试验的数据:

试验器材为2块CC2540芯片,主芯片发射功率为4dBm(2.51mW),Loss是通信节点中失败次数。

30.问:如何将接收RSSI试验数据得到距离计算公式呢?答:最好的工具是EXCEL软件,以上表中的试验数据和EXCEL2023为例。首先选中Distance和RSSI两行,点击“插入->散列图〞,软件会自动生成如下图:

选取其中任意点,点右键,“添加趋势线->对数〞,将会出现下图:

可见RSSI与距离的关系是比较符合指数函数,再点击“显示公式〞

此时得到指数函数公式为:y=-49.53–17.7ln(x),再把自然对数换成10常用对数,则有:y=-49.53–40.71lg(x)。通过以上几步就轻松得到RSSI与距离之间的计算公式。

31问:针对RSSI采样值选用什么样的滤波算法?

答:RSSI采样值遵循以下特点:有个别的脉冲干扰引起极大值和微小值的出现,其他采样数据值沿平均值分布,比较适合的算法是:滑动防脉冲干扰平均滤波法。它的原理是,设有N个单位的队列,用新的采样值覆盖旧的采样值,去除队列中最大值和最小值后,再计算队列中采样数据的平均值。用C语言描述如下:staticINT8SFilter(INT8SchVal){

#defineFIFO_NUM10

INT8SchMinVal,chMaxVal,chTemp;

INT16SnCnt,nSum;

staticINT8Ss_chIx=0,s_chIsFull=FALSE;staticINT8Ss_achBuf[FIFO_NUM];

s_achBuf[s_chIx]=chVal;if(++s_chIx>=FIFO_NUM){

s_chIx=0;

s_chIsFull=TRUE;}

if(!s_chIsFull){

nSum=0;

for(nCnt=0;nCntchMaxVal){

chMaxVal=chTemp;}

elseif(chTemp>8)

//Characteristic6Value//simpleProfileChar6是个5位数组,接收数据后存在这staticuint8simpleProfileChar6[SIMPLEPROFILE_CHAR6_LEN]={0,0,0,0,0};

//SimpleProfileCharacteristic6UserDescription

staticuint8simpleProfileChar6UserDesp[17]=\

3、属性表(ProfileAttributes-Table)最重要,添加了这个才会在lightblue中列表出来

staticgattAttribute_tsimpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED]这里要把数组改为

#defineSERVAPP_NUM_ATTR_SUPPORTED20原来是17(//添加了3组结构体数组CHAR6)

simpleProfileAttrTbl表中,可读可写属性都是3个数组,只有char4的通知是4组,多了个//Characteristic4configuration

并把CHAR6添加进去

//Characteristic6Declaration(声明,没加这个lightblue属性表找不到){

{ATT_BT_UUID_SIZE,characterUUID},GATT_PERMIT_READ,0,

//把要改写的数据写到simpleProfileChar6数组来}else{

ret=bleInvalidRange;}

break;

4、获取参数函数{SimpleProfile_GetParameter(UUID,获取到的值)函数}实际上就是把被新进的值simpleProfileChar6放进value数组

在bStatus_tSimpleProfile_GetParameter(uint8param,void*value)中添加:

caseSIMPLEPROFILE_CHAR6:

VOIDosal_memcpy(value,simpleProfileChar6,SIMPLEPROFILE_CHAR6_LEN);break;

//读取simpleProfileChar6的值放到*value中,char1是单个字节读取,为*((uint8*)value)=simpleProfileChar1;

·6、读写特征值函数(2个回调函数·这个两个是注册到GATT层的回调函数,在GATT初始化的时候注册的.这部分代码封装在库里面.

每当GATT

温馨提示

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

评论

0/150

提交评论