这个就是当有上位机向下位机写东西时,会调用上面这个函数。在这个函数中,会有下列: 先将接收到的长度值打出来:
NPI_PrintValue( \然后执行这个: VOID osal_memcpy( pAttr->pValue, pValue, len ); simpleProfileChar6Len = len;
notifyApp = SIMPLEPROFILE_CHAR6;
注意到这里这个pValue 及pAttr->pValue这两个参数,都要求大于传输的字节长度。这里担心的是这两个是否足够长,先看pValue,要搞清这个,先看谁在调用它,发现它是一个内部调用的函数,其长度定义无法修改之。我估计不会留这么长的空间,要不就是动态分配的还好办一点。
然后看这个pAttr->pValue,看这个pAttr会留多长呢?好象有一个说明是最大为512个octets。这个是什么含义呢?没有搞明白,先试吧。 在最后,它会调用:
simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp ); ///告诉某人,profile变化了
初始化时,注册了这个函数:
VOID SimpleProfile_RegisterAppCBs( &simpleBLEPeripheral_SimpleProfileCBs ); 也就是将bStatus_t SimpleProfile_RegisterAppCBs( simpleProfileCBs_t *appCallbacks ) {
simpleProfile_AppCBs = appCallbacks; }
static simpleProfileCBs_t simpleBLEPeripheral_SimpleProfileCBs = {
simpleProfileChangeCB // Charactersitic value change callback };
绕来绕去,原来最后调用的是
simpleProfileChangeCB(uint8 paramID)就是最前面的一个,就是在这里调用的。 原来是这样的:
1 当上位机有数据发下来时,就会调用:
static bStatus_t simpleProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset ) 2.在这个函数的最后,它会调用:
simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp );
而这个simpleProfile_AppCBs,它simpleProfile_AppCBs就是simpleProfileChangeCB 其实就是调用simpleProfileChangeCB。
这个simpleProfile_AppCBs在开始时,它只是指向一个NULL。在初始化时,它被赋给: simpleBLEPeripheral_SimpleProfileCBs,见上文中的兰色部分
而这个simpleBLEPeripheral_SimpleProfileCBs,它本身就是simpleProfileChangeCB 请见上文中的紫色部分
于是下位机先这么着。然后看上位机,如何发100个字节下来。
26
上位机要发100个字节,首先看这个UP键中有关发送部分。有一个语句: req.len = 10; ///原先为1 改为10 说明这个req很重要。这个如果限死了哪也没办法发100个数了。
我们在att.h中,看到这个定义最多为23-3=20个。感觉好象不行了。我们重新定义一下看 在att.h中,将这个 typedef struct {
uint16 handle; //!< Handle of the attribute to be written (must be first field) uint8 len; //!< Length of value
uint8 value[ATT_MTU_SIZE-3]; //!< Value of the attribute to be written
uint8 sig; //!< Authentication Signature status (not included (0), valid (1), invalid (2))
uint8 cmd; //!< Command Flag } attWriteReq_t;
红色部分改为定义成100看一下。如果不行,再将这个ATT_MTU_SIZE 定成103看 再不行就将这个L2CAP_MTU_SIZE 定义成103试一试看,L2CAP_MTU_SIZE原来定义为23的。
这样做的结果是上位机和下位机都死机了。可能不能超过23个字节吧。
我们改为20个字节看一下。还可以没死机。下位机没有改。看来下位机死机的问题能否改到不死呢?估计不可能了。因此我们暂时修改一下协议。 下位机能否主动发呢?
原来是从机收到读命令后,向上位机回复一个HELLO。现在想改成,从机收到数据后,就立即回复,而主机收到任何数据都将它打印出来,看这个可以吗?
修改的方法是:
首先,主机在收到后要立即打印出来收到东西了,这个要在回调函数中进行。 从机要能主动发数据过来。在收到20个字符后,主动发一个出去。
先看一下从机吧,原来从机是在收到读命令后去发送的。现在要收到数据后主自动回一个,这个有些区别的。看原来是怎么发的。
原来是收到读命令后,会调用这个simpleProfile_ReadAttrCB(。。。)在这个过程中,它会调用下列函数:
*pLen = simpleProfileReadConfig(uuid, newValue); VOID osal_memcpy( pValue, newValue, *pLen );
在这里,这个uuid就是SIMPLEPROFILE_CHAR6_UUID。于是我们可以模拟这个发一个下去看。事实上在这个simpleProfileReadConfig,只是处理了下列: len = osal_strlen(\
osal_memcpy(newValue,\
显然这样做行不通,因为上位机没有读,这个回调函数不会被执行。 只能用Note方式来向主机发数据。但这个Note如何调的呢? 由于没有例子可参考,所以先搜一下notification这个 发现有一个电池服务中调用了这个 有下列定义:
27
static void battNotifyCB( linkDBItem_t *pLinkItem ) {
if ( pLinkItem->stateFlags & LINK_CONNECTED ) {
uint16 value = GATTServApp_ReadCharCfg( pLinkItem->connectionHandle, battLevelClientCharCfg ); if ( value & GATT_CLIENT_CFG_NOTIFY ) {
attHandleValueNoti_t noti;
noti.handle = battAttrTbl[BATT_LEVEL_VALUE_IDX].handle; noti.len = 1;
noti.value[0] = battLevel;
GATT_Notification( pLinkItem->connectionHandle, ¬i, FALSE ); } } }
向一个连接的句柄,发送,发送中还要指明电池句柄,长度,和一个值,问题是这个电池的子句柄是怎么回事呢?
于是先在网上找到一个例子,已放到收藏夹中了。 先看一下这个例子再做吧。
看到这个庞大的配置表 注意到它从0x2800开始
static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] = 。。。
它由3部分组成: char1 的配置表声明 char1 配置表的值
char配置表的用户声明
从char6来看,比其它的多了一个0x2902 多出来的 客户特征配置UUID
而按照网上博客的设想,也是多出来一个0x2902 多出来的 客户特征配置UUID,这个我们已经有了,故就不用再添加了。 有这么一句话:
需要主机的client端对它进行打开通知操作后,从机才能notification通知到主机client. 这个函数也调用了:GATTServApp_InitCharCfg(connHandle,simpleProfileChar6Config); 它是在void simpleProfile_HandleConnStatusCB( uint16 connHandle, uint8 changeType ) 这个回调中初始化的。
这个回调函数是当这个连接状态改变时,这个函数被调用,注意到其中一个参数是连接句柄。 这个连接句柄是连接时得到的吧。找一下看,不是,没有找到。暂不管它。
看代码时,偶然看到按下S1键会产生一个Notification,赶紧转去看一下。结果很失望,估计这个程序是AMO从某个TI的上面拷过来的,将这部分删除了。哪我们看一下TI中有没有这个呢?没看到,看是否防丢器上面有这个呢?按一个键,产生一个Note呢?没有找到。
28
再看:simpleprofile_addservice(uint32 services)这个函数在哪里调用了。 这个在初始化时确实调用了。
后面在这个simpleprofile_setparameter()中也已经在char6中,加入了Notification部分的代码。
我们再看一下原来是怎么发的?原先是在上位机读时,下位机会调用这个 static uint8 simpleProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen ) 在这个过程中,当碰到的是char6时,就会有:
uint8 newValue[120]; ///这里定义了一个20意味着最多可以有20个字符过去。
extern uint8 simpleProfileReadConfig(uint16 uuid, uint8 *newValue); 这个实际上是准备数据 *pLen = simpleProfileReadConfig(uuid, newValue); VOID osal_memcpy( pValue, newValue, *pLen ); 于是就湖里湖涂地传上去了。 看到后来,发现好象只要
bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value )就可以将数据主动发上去了。Param的值为SIMPLEPROFILE_CHAR6 len为要发过去的长度,而*value为要发过去的值。
于是我们可以在收到上位机发过来的helloworld里而,加上这个
bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value )即可以了。
好的,我们就修改一下,只改了这么一点地方,看程序看了半天多。
SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR6, 15, \回复Noti
下面看上位机如何处理这个消息吧。下位机发了这么一个上来,上位机在哪里处理呢?它怎么知道呢?
调试中发现,打印语句NPI_PrintValue()调用时,如果不加include”npi.h”则编译时会有警告,然后运行时居然会出错,奇怪啊! 希望的事情没有发生。
发现一个错误,CHAR6的CCC的长度只能是一个字节,而我们却搞了很多字节过去。 我们将它改为一个字节看一下。已改。
下面看一下在主机端,这个让从机Noti允许部分在何处进行的,应该是在连接建立时进行的吗?是的。
现在发现读char3,4的句柄都会失败 其它的都是成功的。char1,2,5,6都成功.不知为什么?
发现读5,6的句柄时,也会调用:simpleProfileReadConfig()
一时搞不出来暂时到此为止,以后有机会再做吧。
现在我们改为全部都是主机进行控制。主机可以往从机中写一片数据,这部分只进行一次。然后从机就从主机中读出来所有的原来写进去的数据。最后再读出从机的几个测量值,它的值是温度,湿度,电压,测量值
29
现在正式开始编写主机程序:
先编上位机的程序,按下发送键后,就发送20个字符出去。在接下来的等发送成功了,就继续发送20个,直到发送完成。
先要组帧。先发第一帧。我们在按键中先发送第一帧。然后,在收到这个帧的响应中再发其它的帧。如果帧发完了,我们就将这个帧计数清零。而在响应中,如果看到这个帧号为0,它将不会再继续往下发的。因为这个表示已经发送完成了。看一下发送结束后的响应在哪里呢?
现在已经写好发送3帧的例子了,接下来再编写从机的部分。从机首先要将主机发过来的数据进行拚接。然后检查是否正确,再次就是写入到FLASH中。
如何检查是否正确呢?先看帧中的内容,即帧号,如果帧号>128,即0x80。则证明是最后一帧了。在每次收到帧时当然先要检查是不是FF,如果是,则检查第2个字节是不是0,如果也是,则传输结束。本机将断开连接。
其实,收到每一帧,都将要拚接的。先要看帧的次序是不是正确,第一次帧号计数器总是为0,收到的是第0帧。后面收到的都要和它比较。还有这个帧号计数器是一个全局变量,要注意在收到错误帧时,将帧计数器清0。现在有个问题就是下位机完全是被动的,连一个应答信息也发不了。现在也没办法,先不管吧。等以后再说。 先将收到的几帧将它拚接起来。
再将它写到FLASH中去。再从中读出来,都可以了。注意到读出来的字节中,最后一个字节为BCC的值。
接下来,就是要采集有关的数据了,有关的数据有下列:
电池电压 测量电压 温度 湿度
然后我们要将些个数据拚起来。同样,我们可以用到下载的数组来作为应答。因为下载与采集不会同时发生,下载一生可能只发生一次(刚开始配置时)。而上载则会多次发生。 先看测电池电压 这个在连接时是不是就读进来呢?建立连接时的回调函数,我们看是不是可以读进来。但是读温湿度可能不妥。这个还是在任务中去解决吧。我们在连接后,发一个事件给主任务。由主任务去进行这些操作吧。
电池电压如何读,看原来的例子直接借用,测出来电池电压为168*0。02=3.36V 可能是正常的。
接下来,测一下这个测量电压的值, 已经看完了。
接下来,本来想读温度和湿度,或暂时交小江去做。
我们还是先将协议部分实现先做一下。将数据组装,由上位机将它读走再说。 NOTI还要搞通才行。不然无法应答,只能被动接收。
30
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库OSAL解读笔记(6)在线全文阅读。
相关推荐: