关于TI的CC2541的程序解读
更新时间:2024-06-27 20:31:01 阅读量: 综合文库 文档下载
- 关于ti的成语推荐度:
- 相关推荐
A 先粗看了一些东西
如果某一个任务的event被置位了,就处理它。
即 taskEvents[idx]!=0
而这个事件是由中断程序之类的设置的或者由某一任务设置的?
只有有events事件发生的任务才去处理。没有事件发生的任务将跳过。
系统信息,可能会发给本任务,这个用函数: Osal_msg_receve(simpleosal_taskID)就可以得知。
猜想:当系统事件发生时,它会将一个全局变量中的事件数组的某一个与0x8000或运算,到底是哪一个就是我们的任务排列的序号来决定的,例如我们在本例子中有11个任务(后来我自已又加了一个)如果第2个任务要发系统信息运去,就将这个数组置为1。 程序中的这样处理的:
tasksEvents[task_id] |= event_flag; // Stuff the event bit(s)---见osal.c中。
这个信息处理函数(带任务ID参数)它就是读这个全局变量,看这个是发给谁的。
所以它先看有没有系统事件是发给本任务的,
uint8 *osal_msg_receive( uint8 task_id ) {
osal_msg_hdr_t *listHdr; ///队列指针,用于搜索的 osal_msg_hdr_t *prevHdr = NULL; ///前一个
osal_msg_hdr_t *foundHdr = NULL; ///发现的指针处
halIntState_t intState; ///这个不知是干什么用的
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState); ////进入
// Point to the top of the queue
listHdr = osal_qHead; ////指向队列的前部
// Look through the queue for a message that belongs to the asking task while ( listHdr != NULL ) ////只要队列不空,就一直往下走 {
if ( (listHdr - 1)->dest_id == task_id ) ///事件队列中的目标ID是指向本地ID否? {
if ( foundHdr == NULL ) ///第一次发现消息是给自已的 {
// Save the first one
1
foundHdr = listHdr; ////发现一个消息是给自已的 } else {
// Second msg found, stop looking 又发现一个 直接退出了 break; } }
if ( foundHdr == NULL ) {
prevHdr = listHdr; ///当前这个就是第一个 }
listHdr = OSAL_MSG_NEXT( listHdr ); ///指向下一个 }
// Is there more than one? if ( listHdr != NULL ) {
// Yes, Signal the task that a message is waiting osal_set_event( task_id, SYS_EVENT_MSG ); } else {
// No more 如果没有消息就告诉任务,现在没有系统消息 osal_clear_event( task_id, SYS_EVENT_MSG ); }
// Did we find a message?
if ( foundHdr != NULL ) ////将这个消息从中间抽出来让它消失掉,这时就要知道前一个 {
// Take out of the link list
osal_msg_extract( &osal_qHead, foundHdr, prevHdr ); }
// Release interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
return ( (uint8*) foundHdr ); }
B再看一下操作系统,从中断到处理的过程
现在基本理清了按键消息是如何发过来的,有点绕。 首先,在初始化中,允许了中断并配置了回调函数(这个函数在文件onBoard.c中InitBoard()
2
调用了它):
/* Initialize Key stuff */
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE; //OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);---halkey.c中定义 在配置回调函数中,它初始化了两个键盘的中断允许位和上下沿等细节,将中断屏蔽位打开,且将这个中断标志位清除。注意到它将全局变量置为有效OnboardKeyIntEnable=1 而这个变量本来没什么意义的,但发现它可以用来读键盘中断位时,在区分是用中断方式来做还是用查询方式来做时用得着这个变量。 中断函数的定义有点小技巧(宏),它就是将中断矢量放到合适的位置,而且申明了这个函数。它既是定义,也是申明。这个在hal_mcu.h中定义了。(Keil和IAR不一样)
但是这个配置程序中可能有点错,PICTL是用来控制上升沿或下降沿中断的,在多功能键初始化中,即P2.0处理中,它错误将P2IEN的D3位置为1,而不是将PICTL的D3位置1。所以这个键变成了上升沿了。当然这并不影响程序的运行。下面我们看一下是上升沿还是下降沿。先看一下电路图按下去时,它会产生一个上升沿,仿真器调一下看是否是上升沿时中断?果然,在刚按下去,还没抬起来的时候,就捕获到一个中断了。这说明这个中断是上升尚的,证明了程序的配置下降沿是没成功的,看来TI也会出点小错误。
中断响应程序中,最后调用了:----中断函数定义在halkey.c中.
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);将生成一个按键事件。注意到这个Hal_TaskID这个是内核ID的任务。延时一下可抗抖动干扰? 注意到这个函数中调用了 newTimer = osalAddTimer( taskID, event_id, timeout_value ); 而这个加定时器这个函数,当时只是加了一个定时器,后面的事情,就是要延时一点时间后,等时间到了后再去设置它,这个终于发现了(在定时器更新中osalTimerUpdate()) 当定时时间一到,就将事件发送给相应的任务ID
osal_set_event( freeTimer->task_id, freeTimer->event_flag ); 所以是它将这个按键事件是发送给内核这个任务的。
注意到这里的任务ID,就是内核任务ID。而这个事件就是按键事件。
注意到在中断程序中,并不确定按键是哪一个,它是在HalKeyPoll()中检测键的。
而这个事件的处理在什么地方呢?
它在Hal_ProcessEvent( uint8 task_id, uint16 events )中。
这个是一直在调用的,它是11个运行的任务之一(事实上它是第2个任务)
在这个任务中,如果发现有系统事件,它会将这个事件读出来。没做什么事情,也不会有这样的系统事件(在我们这个程序中)
如果是按键事件,则调用HalKeyPoll()在这个函数中将检测这个键值传送到这个回调函数中。(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); 注意到第2个参数它=0 OnBoard_KeyCallback( uint8 keys, uint8 state )注意到这个state=0.
而这个shift的值为多少呢?如果不是SW6,它就是true.如果是SW6,它就是false.
shift = (OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE) ? false : ((keys & HAL_KEY_SW_6) ? true : false); ---如果是单键,它为false,为5键,它就是true.
3
这个回调函数,调用了OnBoard_SendKeys( keys, shift ) 在这个函数中,它调用了: osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
这个注册的任务ID,就是我们在初始化中指定的任务(最后一个任务ID)。而这个信息就含有我们的事件名,状态,键名称。
发送消息的函数调用了osal_msg_enqueue_push( destination_task, msg_ptr, FALSE)在这个函数的开始,将消息推送到消息队列中,osal_msg_enqueue( &osal_qHead, msg_ptr ); 最后调用了osal_set_event( destination_task, SYS_EVENT_MSG );即告诉目标任务ID,你的系统事件到了。
有一个问题是程序是如何按键去抖动的呢?例如由于抖动,发生了N次按键中断。难道就要加N个定时器吗?终于后来又发现了,它在osalAddTimer( uint8 task_id, uint16 event_flag, uint32 timeout )完美地解决了这个问题,这个问题就在这个函数中,当增加定时器时,如果发现同样的事件,同样的任务,已经有一个定时器在运行了,则我们就不会再增加定时器了,而是将这个定时器更新一下就可以了。而如果从来没有定时器用于这个事件和这个任务(两者同时符合,则不生成一个新的定时器)
newTimer = osalFindTimer( task_id, event_flag );
if ( newTimer ) ///如果发现了定时器已经存在了 {
// Timer is found - update it. ///只是将这个定时时间更新一下而已。 newTimer->timeout.time32 = timeout;
return ( newTimer ); }
Else ///如果没有这个定时器存在,就生成一个定时器 {
// New Timer
newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) ); ///分配一段内存 。。。。 以下设置定时器的参数
}
真正的键的最后处理,交给应用层了。因为这个信息已经封装,发送到最未的一个任务了,消息中的事件就是KEY_CHANGE 它的状态就是true or false---这个依赖于是5键还是单个键。 它的键名就是预定义好的6个键,分别为0x01,0x02,0x04....
后面还有注意到
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
分配的是一个2*事件个数的数组 例如11个任务,就分配了一个22个字节的指针处。 它是一个int16的数组,每个位表示一个事件,每个任务最多有16个事件,其中的0x8000表示的是系统事件。即最高位为1的事件。
我们还要看一下这个始终运行的任务 do {
4
if (tasksEvents[idx]) // Task is highest priority that is ready. 注意到这个数组。它代表? {
break; }
} while (++idx < tasksCnt);
在什么地方设置这个呢?tasksEvents[idx]
uint8 osal_set_event( uint8 task_id, uint16 event_flag ) {
if ( task_id < tasksCnt ) {
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts tasksEvents[task_id] |= event_flag; // Stuff the event bit(s)
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts return ( SUCCESS ); } else {
return ( INVALID_TASK ); } }
C自已在工程中试着加任务,传事件和附加消息,还是成功了
下面,我们试着在一个工程中,加一个任务,这个任务让一个LED灯闪。且时间到了,就给主任务发送一条自定义的消息,此消息含一个变量,这个变量每次都++的。通过串口打印一个 时间到了XX出来。
先加一个任务试试。
1.首先在任务组中增加一个函数 MyTestEvent() 这个在OSAL_simpleBLECentral.c中。 2.
A 增加一个 MyTest_Init(taskID );注意到前面一个任务必须将tasdID++;只有最后一个才不要++。 simpleBLECentral.c中,这个是初始化的调用。
B 增加一个定义static uint8 MyTestTaskId; -----simpleBLECentral.c中。
C extern uint16 MyTestEvent( uint8 task_id, uint16 events );----这个在simpleBLECentral.h中要定义,同时要定义的还有 extern void MyTest_Init(task_id) 不然编译会出警告
3.写这个初始化函数及处理函数
A初始化函数部分:它定义在simpleBLECentral.c中
先: HalLedSet( HAL_LED_2, HAL_LED_MODE_ON ); ///没什么事就让一个灯亮一下吧
5
#define START_TEST_EVT 0x01 -------------启动时加个事件试一下simpleBL...h
B处理函数部分:它定义在simpleBLECentral.c中 我们怎样来处理这件事呢?---简单一点
这样行不行?先在初始化中,增加一个定时器事件。
在这个任务中,在收到定时器事件后,让灯反转一下,并重新开启一个定时器事件。这样灯就不停地反转了。不这么做。
复杂一点,就是先调用这个函数:
osal_start_timerEx (Hal_TaskID, HAL_TEST_EVENT, HAL_KEY_DEBOUNCE_VALUE); 我们先将一个事件,发给内核任务去处理
#define HAL_TEST_EVENT 0x1000 -----这个定义在hal_drivers.c中
于是,在内核中就要增加对这个事件的处理。但是这个处理很简单
我们知道,这个HalKeyPoll()总是要在按键被调用的。这个是有按键事件发生时才调用,所以不能将这个事件处理放在这里。
但是我们在Hal_ProcessEvent()中处理这件事情的。于是我们将这个事件放到这里来处理,在这个函数中增加对这个的处理部分。原来键处理是在回调函数中处理的,我们直接。。。 但是有一个要定义一下,就是定义一个时间到事件
#define TIMER_ARRIVED 0xC1 //gxd 2015/11/25
下面我们再总结一下这个过程。
1.初始化时,我们先给这个我们加的这个任务,即其ID号为MyTestTaskId,其运行的任务名为:MyTestEvent的这个任务,发送一个事件:(事件定义在simpleBLECentral.h文件中) osal_set_event( MyTestTaskId, START_TEST_EVT ); 启动测试事件,因为这个任务中也没有什么其它事件,故我们就将这个事任定义为0x01 我们知道它最多有16个事件。一般来说,这个0x8000就是系统事件。注意到每个任务都有自已怕系统事件。系统事件不是唯一的。
2.这个任务一旦运行,马上就会收到这个事件。在收到这个事件后,会向内核中的HAL事件处理程序发送一个延时后的事件
osal_start_timerEx (Hal_TaskID,HAL_TEST_EVENT, HAL_TIMER_DEBOUNCE_VALUE);
3.内核程序是专门处理这个事件的。所以过一段时间,就会收到它,这个时间长度为1000。单位是多少呢?需进一步了解----发现它的单位是ms 看函数定义中的注释中有。
4.内核事件处理程序中,会直接调用 Test_Send( 0, 0 );这个函数。这个函数将调用: osal_msg_send( MyTestTaskId, (uint8 *)msgPtr );即给MyTestTaskId发送一个信息。 注意看到原来键中断处理却不是这样做的,它原来的方法是在中断中,给内核任务发送一个键事件,在这个键事件处理中,调用keyPoll()中将键值得到,然后调用一个回调函数---我们预定定义好的。在这个回调函数中,调用了OnBoard_SendKeys()这个函数,在这个函数中给我们的这个任务发一个系统消息,且将消息设置了一下。 osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
6
而现在的做法是直接给测试任务发一个消息。
5. 于是我们的测试任务就会收到这个消息,而且是个系统消息。收到这个系统消息后,系统消息中的事件是 TIMER_ARRIVED 调用这个 My_TEST_ProcessOSALMsg()这个消息。在处理这个消息过程后,将销户这个消息。
6.在销户了这个消息之后,为了让这个过程不断地进行下去,我们再给内核发送一个测试事件。于是这个过程继续下去。
发现这个延时启动的计时器的单位为ms.故我们将这个时间定为1000。
发现加了这部分程序后,系统就死机了。跟踪发现信息没有发送过去。 后来发现程序将事件名称写错了,拷贝时忘记改引起的,修改后好了。
串口经常少打一个回车。而且是每打印5次就会发生这样的错误一次。这个不知所以了?
D 试着理解一下BLE协议部分,感觉有点乱
下面再继续读一下其它的代码部分
先看系统事件,它处理按键和GATT消息部分。
先看简单点的,消息处理:消息处理就是读写部分,当读或者写指令发出后,它在交给无线部分收发后,完成后会有响应,这个响应有4种情况,读错误,写错误,读成功,写成功。 下面具体看一下这个处理:
如果没有建立连接,则直接返回,这个可理解。 如果是读响应,(读出错不在这里)或读请求则有: uint8 valueRead = pMsg->msg.readRsp.value[0]; 证明这个消息中含有读的值。这个消息格式如下: typedef struct {
osal_event_hdr_t hdr; //!< GATT_MSG_EVENT and status uint16 connHandle; //!< Connection message was received on uint8 method; //!< Type of message
gattMsg_t msg; //!< Attribute protocol/profile message } gattMsgEvent_t;
如果不是读,也不是写,而且正在忙的话,就是在做发现的事情。例如,在接到发现从机事件时,就将这个状态改变成正在发现状态:simpleBLEDiscState = BLE_DISC_STATE_SVC; 此时就不是闲了。而如果有消息过来,要不就是读,要不就是写,要不就是在发现。 所在在收到了系统消息后,一个看这个消息是不是按键消息,一个看消息中附加的事件是不是GATT消息,如果是GATT消息,则进一步看是读还是写还是其它。其它只有一种情况就是发现消息。而这个发现消息也是因为按了UP键引发的。
最后我们还是看一下键处理,尽管这个键处理比较复杂一些。
●先看UP键。如果没建立连接,就调用一个函数去发现从机扫描。如果正在扫描时按下UP键,就终止扫描了。不过除非你连接按两下UP,不然因为连接速度很快,不会出现终止扫描这种情况。
如果已经建立连接了,按UP键的话就是一次是写,一次就是读,一直按一直这样做下去。 ●再看一下左键处理。
7
显示扫描到的设备号。按一下换一个,直到最多8个又回来了。
●再看右键 是连接更新。这个不知在做什么,因为源代码看不到在库里。
●再看中心键,如果选择要连接的从机是空闲状态,还没有进行连接,则进行连接,如果已经是连接状态,则断开连接。
●再看DOWN键,每按一次,是读取RSSI或取消读取RSSI。 注意到扫描和连接是两件事情。扫描只是看下面有多少从机。而连接则是给指定的从机去连接。连的是谁则通过左键去选。做试验时我们只有一个从机,只买了一个。
程序还是没有看完全,还得继续努力去消化一下。只有多花时间多去看,对代码看上去就记住得差不多的时候,这个代码也就看得差不多了。这样下来速度是慢了点,但是比较深入一点。
我们再一次将客户机(中心)这个程序解读一遍:
先看在这个事件处理程序:它处理三种事件,第1种是系统事件,第2种是启动事件,第3种是开始发现事件。
简单点的先看第3种。第3种是一个开始发现事件。我想这个开始发现事件应该是按了UP键后发出的吧。本来也没有必要搞这么一个绕来绕去的,在按键中直接调这个函数simpleBLECentralStartDiscovery( );不就行了吗?我们看一下在什么地方触发了这个事件? static void simpleBLECentralEventCB( gapCentralRoleEvent_t *pEvent )中调用了这个事件。 在GAP_LINK_ESTABLISHED_EVENT事件中,发调用这个 osal_start_timerEx(simpleBLETaskId,START_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY );
照理,我们应该在UP键中会出现这个发现设备事件?
理解错了,原来这个是发现FFF0服务?而不是发现从机。原来是与从机建立连接后,再启动这个发现从机服务的过程,而不是扫描从机。注意到这里发现FFF0服务的服务。下位机如果有N个,则怎样去发现呢?另外每个下位机它的服务都是FFF0吗? 我估计下回还要看一下同时连接3个从机的例子能从中间找点线索吧。 调用了这个发现从机服务后,还要做什么呢?此时应该可以与服务器的特征参数通讯了吧? 只能先跟踪到这里了。过一会再来回顾这部分的处理吧。
接下来我们看到第2种事件,就是启动事件,启动事件由这个任务的init()的。 启动事件中,也做了不少事情的,主要有:
注册了两个函数,其中一个是RSSI事件处理回调函数和一个状态处理回调函数,也就是收到一些事件后,如何处理收到的事件消息。
绑定处理函数,这个可能是处理密码与配对的吧。也是一个回调函数。 于是我们再稍深入一点,看RSSI函数回调到底做了些什么? RSSI回调函数,就做了一件事,将RSSI的值显示在屏幕上。 事件处理函数有点比较复杂,它的目的是处理一些事件:
GAP初始化完成事件,初始化完成后,将信息在屏幕上显示出来。 设备资料事件:在一个给定的广播的服务UUID列表中,发现一个给定的UUID(FFF0吗?) 设备发现事件:在屏幕上显示设备发现,和去选择这个字样。
建立连接事件:它会调用一个osal_start_timerEx( simpleBLETaskId, START_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY );生成一个事件,然后由任务中去开始发现指定的从机。这个有点象是建立连接? 断开连接事件:显示连接被断开。
参数更新事件:就是显示一个para,update在显示屏上。
8
接下来我们看比较麻烦的最后一个,就是系统事件。系统事件含两种消息处理,一种是按键消息处理,一种是GATT消息处理。其中GATT消息处理有三种情况,读,写,不闲着。前两种比较简单一点,就是从自带的消息中将字符取出来。并显示在屏幕上。 后面一个就调用这个函数:simpleBLEGATTDiscoveryEvent( pMsg );
这个函数处理GATT发现函数。这个函数中,处理两种情况,一种是服务发现,一种的特征发现。在特征被发现后就将它进入空闲状态。
服务发现干些什么事呢?当一切顺利时,就将状态置为特径发现,便于下一次来处理。 将FFF1这个值会设置进去,也就是说好象只对FFF1进行操作。 特征发现,保存处理句柄,这个没理解。等待以后理解吧。 最后看一下键盘处理吧:
UP键,是去扫描用的。调用了一个GAPCentralRole_StartDiscovery函数。如果已经连接,则分别去写,读这个相关联的句柄中的某一个字符。但只是进行读写动作,读写完成后自然会在一个回调函数中处理的。simpleBLECentralProcessGATTMsg()会有GATT事件消息产生的。
LEFT键---显示多个下位机设备。每按一次,显示一个。直到。。 右键: 更新连接------不知道这个是做什么用的。
中间键:相当于一个回车键,它是确认它或者是断开连接。
下键: 它不断地显示这个RSSI值。再按一下,就是不再显示了。 初步看完来,再自上而下看一遍代码
看到simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )这个函数理解一下。
这个函数在GATT事件中,当不是读,也不是写时,而且也不是闲时,就会调用这个函数去处理这个消息。我们知道系统消息有两个,一个是按键消息,一个是GATT消息。GATT消息应该是由于操作按键引起的。
当simpleBLEDiscState != BLE_DISC_STATE_IDLE时,表示它的发现状态处理空闲时,但却有系统事件传过来,说明此时就是发现事件要处理了。发现状态有三种状态,一种是空闲,一种是发现了服务,一种是发现了特征值。 建立连接失败了,就将这个置为空闲了。 终止连接了,也将这个标志置为空闲了。
调用发现FFF0的服务,则这个标志置为BLE_DISC_STATE_SVC---这个是唯一一个给值的 发现服务成功了(完成了),就将这个标志置为BLE_DISC_STATE_CHAR 发现特征后,就将它又置为空闲了。
什么时候会调用发现FFF0服务呢?这个在事件处理中,当( events & START_DISCOVERY_EVT )时,就会调用发现FFF0这个服务。 哪这个事件又由谁触发的呢?这个是由建立连接事件引起的。建立连接又是由谁引起的?这个是由按下中间键引起的。于是整个流程就比较清楚了。
E总结很重要,这里有点不乱了
这样总结一下:
●先是按下了UP键,开始扫描下位机。而且将搜到的下位机可以全部显示出来,这部分当前还没有读到。(稍后再继续读下去余下的代码也许就明白了)
●然后是左键可以选择指定的下位机。按左键时有simpleBLEScanIdx++,这个非常重要。
9
这个是选定要连接的从机中的哪一个从机用的。非常重要。
●接下来,再就是按下中间的键,就可以对指定的下位机进行连接。这个指定的从机就是用这个simpleBLEScanIdx数来代表的,这个是序号,又一次很重要。 在对下位机进行连接过程中,会调用下列函数:
GAPCentralRole_EstablishLink( DEFAULT_LINK_HIGH_DUTY_CYCLE, DEFAULT_LINK_WHITE_LIST, addrType, peerAddr );
注意到这个参数地址类型,对方地址。这个是对指定的从机进行连接。 peerAddr = simpleBLEDevList[simpleBLEScanIdx].addr; addrType = simpleBLEDevList[simpleBLEScanIdx].addrType;
下位机的地址及类型,是从一个设备列表中找出来的。而其中有一个比较重要的量就是: simpleBLEScanIdx,这个变量就是我们选定的要连接的下位机的设备序号。而这个序号又是怎么来的呢,是按左键选定得来的。至于它总共有多少个下位机被发现且放到列表数组中这个暂时不考虑。等一会看完所有的代码,也许自然就知道了(又一次轻轻放下它再说,人生需要先放下很多次,不然脑袋就乱了)。
●当调用了上面的“中心角色建立连接”函数后,我估计它会产生一个事件,就是GAP_LINK_ESTABLISHED_EVENT。这个事件是由回调函数处理的。因为我们在一开始启动了一个中心角色的设备的时候,就将这个回调函数附着上去。建立连接后,它会推一个定时器延时的事件产生(当然这里还有很多其它的事件会产生,这只是其中的一个),它就是 osal_start_timerEx(simpleBLETaskId,START_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY );
●因此过一会儿,也就是1秒后,它会给我们的任务发现一个事件,这个事件就是开始去发现服务和特征值。我们的主任务在收到这个事件后就会调用: simpleBLECentralStartDiscovery( ); ///调用发现从机函数 在这个函数中,我们只查找FFF0的服务。
simpleBLEDiscState = BLE_DISC_STATE_SVC; ///现处于发现服务状态 这个重要! // Discovery simple BLE service 这个函数在库里面,其它地方好象找不到 GATT_DiscPrimaryServiceByUUID( simpleBLEConnHandle,
uuid, ////FFF0 ATT_BT_UUID_SIZE, /// 2
simpleBLETaskId ); ///由哪一个任务ID去处理的 这里特别注意到第一个参数:simpleBLEConnHandle,它是在建立连接时被设置的。即按下中心键后,产生了连接事件,在这个事件中被设置的。
于是我们就是处理这个发现过程,而这个发现过程会引起系统事件,并附加有消息,这个消息的处理用这个函数来处理的simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )中间非读,非写的部分。
●调用了这个函数后,就会产生一些系统消息,这个系统消息不是按键消息,它的事件而是GATT_MSG_EVENT。于是我们就要处理这个消息了。这个GATT_MSG_EVENT有三种可能性,一种是读,一种是写,一种就不是读也不是写,它就是去将消息处理一下 if ( simpleBLEDiscState != BLE_DISC_STATE_IDLE ) simpleBLEGATTDiscoveryEvent( pMsg );
●在发现完成后,它会将simpleBLEDiscState = BLE_DISC_STATE_IDLE等于空闲的,后面读或写时就不会跑到发现这个地方去了
●到了这一步,就可以对读写的特征值进行读写了。而对这些特征值的读或者写,不会马上
10
就有结果的,而是要通过空中无线协议栈去完成,完成后有消息返回来的。此时才正式确认读写成功。
F 再回头来从头到尾将这个主任务看一遍,有点看懂了
于是以上过程就基本分析完成了一半多,还有一些分析过程放下了没来得及分析。 在这种情况下,再从头到尾将程序读一遍,以便为未读完的部分做点准备。 █仔细看一下UP键的处理有几个问题:
GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST ); 在初次按下UP键后,会调用上面这个函数 中心角色启动发现,也就是启动扫描。
另一个问题是,在此连接情况下按下UP键,将分别进行读,写操作。这个读写操作的句柄是怎么来的呢?req.handle = simpleBLECharHdl;---这是写时,要得到这个句柄才能写。 另外还有一个通讯句柄,也要的,如下:
status = GATT_WriteCharValue( simpleBLEConnHandle, &req, simpleBLETaskId ); 一个是特征值句柄,一个是通讯句柄。两个都要的。 此外还要注意到这两个函数都是封装在库里的。它必然产生一些消息或事件给我们的函数来处理。因为它是提交给无线栈了,处理完后有信息返回的,一般是成功或失败。 具体的我们先看到这,只是提出问题,后面的函数自然会回答的。
再看一下左键做了些什么?左键看上去好象是选择某一个下位机连接的。它负责的是将这个变量simpleBLEScanIdx++。这个变量是一个非常重要的变量。
右键,似乎更新连接:
GAPCentralRole_UpdateLink( simpleBLEConnHandle,
DEFAULT_UPDATE_MIN_CONN_INTERVAL, DEFAULT_UPDATE_MAX_CONN_INTERVAL, DEFAULT_UPDATE_SLAVE_LATENCY, DEFAULT_UPDATE_CONN_TIMEOUT ); 中间键比较重要,它用于连接或断开与某一个下位机的连接。
如果simpleBLEState == BLE_STATE_IDLE这个状态是空闲的,已连接,正在连接,正在断开4种可能性之一。 将对某一个进行连接:
peerAddr = simpleBLEDevList[simpleBLEScanIdx].addr; addrType = simpleBLEDevList[simpleBLEScanIdx].addrType; simpleBLEState = BLE_STATE_CONNECTING;
GAPCentralRole_EstablishLink( DEFAULT_LINK_HIGH_DUTY_CYCLE, DEFAULT_LINK_WHITE_LIST, addrType, peerAddr );
这里,用到了simpleBLEScanIdx很重要的变量,即已经扫描到的某个下位机。 而DOWN键,则决定是否显示RSSI信号强度。
█接下来的一个函数,是处理系统事件,加上附带的消息。
它是因为操作了键引起的。例如,UP键进行了读写,于是最后会触发到系统消息过来。UP
11
中间键,建立连接,也会有系统消息传过来。
建立连接时,只有当simpleBLEDiscState != BLE_DISC_STATE_IDLE才会去处理,哪么这个simpleBLEDiscState 什么时候给个值呢?原来在
void simpleBLECentralStartDiscovery( void )中,给过这个值
那又是谁在调用这个函数呢?这个在主任务的一个事件发生时,才会调用这个值,哪个这个事件START_DISCOVERY_EVT是谁给的呢?原来的建立连接时给的。哪么为什么会有建立连接这个事情发生呢?肯定是因为按下了中间键引起的。这里就能说得通了,由于按下了中间键,从而调用了一个建立连接的函数,而这个函数就会给一个回调函数发送一个事件,这个事件就是建立连接事件,这个事件中会产生一个开始发现事件给主任务,主任务在这个事件中会调用开始发现这个函数,开始发现这个函数会将这个simpleBLEDiscState 这个变送置为非空闲。非空闲就会去处理simpleBLEGATTDiscoveryEvent(pMsg).而这个函数会发现服务,发现特征量,发现完后会将这个变量置为空闲。发现服务和发现特征量时,会给句柄赋值?居然没发现,哪到底在哪里给这些句柄赋值的呢? 在建立连接时,就将通讯句柄赋值了:
simpleBLEConnHandle = pEvent->linkCmpl.connectionHandle; ///在建立连接事件时,将当前的连接句柄
simpleBLEProcedureInProgress = TRUE; ///设为当前的处理句柄
而在发现特征量时,有下列:
simpleBLECharHdl = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0], pMsg->msg.readByTypeRsp.dataList[1] ); 于是给这个特征量赋值了。
因此,这个回调函数是用来处理这个发现事件的,即发现某一指定的下位机的服务和特征。 它这里又是一个状态机来处理的。发现服务和发现特征是分两步完成的。
█下面仔细看一下这个回调函数,这个回调函数处理的不是主任务的系统事件,也不是主任务的其它事件,而是它内部的一些事件,这些事件,是由于启动了这个主机引起的,即我们在主任务开始时为响应这个events & START_DEVICE_EVT,启动了下列:
VOID GAPCentralRole_StartDevice( (gapCentralRoleCB_t *) &simpleBLERoleCB ); // Register with bond manager after starting device
GAPBondMgr_Register( (gapBondCBs_t *) &simpleBLEBondCB ); ///让绑定自动处理 启动了设备,也注册了绑定过程。
绑定过程在本例子中没做什么事,因为我们将它设为无需密码可直接连接的。
事实上,发密码过去是发一个随机的数过去。下位机我们已配置成无需密码连接了。
这个回调函数有几个事件要处理:
1. 初始化完成事件,此时就将本机的地址显示出来就可以了。什么时候调了一个初始化函数呢?在设置回调函数时就初始化了。
2. GAP设备资料事件 在发现这个下位机资料后,将这个下位机的信息加进去,特别注意到 simpleBLEScanRes++;将这个总共发现了多少个设备++一下。这里可能以后很有用的,因为以后用手机对多个下位机时,已经设置过的我们就不要再去设置它了,而且在扫描到这些下位机时,直接将它跳过去。但是扫描到的下位机怎么识别呢?下位机总有一个地址的,可以供识别的。另外,它还有字符串吗?(名称,不过名称也许是一样的,我们也许只扫描一类名称,别的名称如耳机就不要管它了,是不是可以这样?)另外在哪里发现它的名称呢?这
12
个本该是扫描时的工作吧。每扫描到一个下位机从设备,我们就会出现这个事件。
3. GAP_DEVICE_DISCOVERY_EVENT-----这个有点象设备扫描结束了时出来的这个事件。因为我们看到它将扫描完成后的通道数放到simpleBLEScanIdx = simpleBLEScanRes; 而这个simpleBLEScanRes=pEvent->discCmpl.numDevs; 所以感觉到它是扫描完成了调用的似的。
4.GAP_LINK_ESTABLISHED_EVENT 建立连接事件,这个前面已经分析过了。这里有: simpleBLEState = BLE_STATE_CONNECTED;
simpleBLEConnHandle = pEvent->linkCmpl.connectionHandle; ///在建立连接连接句柄 simpleBLEProcedureInProgress = TRUE; ///设为当前的处理句柄 我们在这个地方,将当前通讯句柄得到了。很重要的一个句柄。 5.GAP_LINK_TERMINATED_EVENT 这个就不多分析了。
6.GAP_LINK_PARAM_UPDATE_EVENT这个没做任何不处理。
G 我只能说,初步的理了一遍,该看的都看过了,基本理解了。
我现在想找到从机名称,看在这个事件中能否找出来,主要看两个事件,最主要是看GAP_DEVICE_DISCOVERY_EVENT和GAP_DEVICE_INFO_EVENT
先看第一个,将断点设在此。但是由于调试器看数据结构很不方便,没有发现这个下位机的名称。但总是会隐在某个地方的。
为此,我们再看一下从机的程序吧。从机是我们真正要做的,主机实际上是手机。要了解主机部分,还需要再看一下BTOOLS运行部分的程序看一看。
从机看过了,没有什么收获。
要不再看一下HostTestRelease 看从中能看出点什么过来。我想这个程序中应该通过串口给主机一些指令,主机随后根据这些指令去执行,应该可以看出点更详细的东西出来。 但是很难看下去。所以又转换一下思路: 看一下这个一拖三的系统处理过程:
1.首先按下UP键,此时将调用下列函数:
GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE, ///启动扫描 DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST ); 这个就是开始扫描,并不是开始连接
2 开始扫描后,所有事件将由这个处理
simpleBLECentralEventCB( gapCentralRoleEvent_t *pEvent ) 每扫到一个设备,就会有这个事件产生:GAP_DEVICE_INFO_EVENT此时我们看到可以打印扫到的这个设备及它的RSSI。正是在这里,本来想在这里将RSSI比较大的扫描的连接上,小的就不要连上了。但是后面发现这样不行,因为在扫描完成后,即GAP_DEVICE_DISCOVERY_EVENT后,它有下列处理:
simpleBLEScanRes = pEvent->discCmpl.numDevs; ///发现了多少下位机 将总数设置一下 osal_memcpy( simpleBLEDevList, pEvent->discCmpl.pDevList,
(sizeof( gapDevRec_t ) * pEvent->discCmpl.numDevs) ); ///全部拷进来了
13
当然,我们可以不管这个,将它删除后看一下是否不处理这个事件会有什么结果。如果可以不管这个,我们就可以将以前处理过的(因为我们已经得到了地址,处理过的保存起来了,我们总是在不停地扫描,看有没有新的进来,有新的进来就给它建立连接,已经处理过的就终止连接)
3.到此为止,程序将什么事也不做直到按下了中心键后,一键连接。
首先调用这个函数:performPeriodicTask_AutoConnect()这个函数主要的工作就是将第一个扫描到的下位机的地址取出来,且执行:
GAPCentralRole_EstablishLink( DEFAULT_LINK_HIGH_DUTY_CYCLE, DEFAULT_LINK_WHITE_LIST, addrType, peerAddr );
注意到取的地址为序号为simpleBLEScanIdx,而这个值在什么地方置0了?检查一下正是在设备扫描完毕后,将这个值置为0了。
4.调用这个函数后,将传一个事件:----自动连接事件,但是这个事件在1.6秒后才发生。 osal_start_timerEx(simpleBLETaskId,SBP_AUTO_CONNECT_EVT, SBP_PERIODIC_EVT_AUTO_CONNECT );
5.此时面临两个事情要做,一个是建立连接,将会产生连接事件,另一个是主任务要去响应这个自动连接事件。看它们是如何处理的?注意到这个自动连接事件延时了1.6秒。比较长,在这种情况下,应该建立连接这个事件早就执行了,故执行连接会被先响应。于是有
GAP_LINK_ESTABLISHED_EVENT事件发生。看在这中间发生了什么?在这里同样用到了关键变量:simpleBLEScanIdx。先将连接的句柄保存下来--这个很重要。然后将特征量句柄清0,这个句柄将在未来的某个地方设置。然后它将发送下列: osal_start_timerEx(simpleBLETaskId,START_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY );
发送一个开始发现事件给任务 注意到这个延时仅为1ms基本上就是立即发送了。注意到在这里,这个变量simpleBLEScanIdx并没有++。
6.主任务在接到这个事件后,即调用simpleBLECentralStartDiscovery()这个函数的作用是 p->simpleBLEDiscState = BLE_DISC_STATE_SVC; ///状态改为正在发现服务 GATT_DiscPrimaryServiceByUUID( p->simpleBLEConnHandle, uuid,
ATT_BT_UUID_SIZE, simpleBLETaskId ); 下面这个函数是一个库函数,表示开始进行服务发现。 7 在服务发现期间,会反复的自动调用:
simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )
这里是这样处理的:先将起始句柄和未了句柄设置一下,这个看上去我们没有用到它: p->simpleBLESvcStartHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].handle;
p->simpleBLESvcEndHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].grpEndHandle; p->simpleBLEDiscState = BLE_DISC_STATE_CHAR1; req.startHandle = p->simpleBLESvcStartHdl; req.endHandle = p->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);
14
GATT_ReadUsingCharUUID( p->simpleBLEConnHandle, &req, simpleBLETaskId );
然后去发现这个特征值1。
后来,得到了特征值1的句柄:
p->simpleBLECharHdl[0] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0], pMsg->msg.readByTypeRsp.dataList[1] );
后面又去发现特征值6,在发现后又得到特征值6的句柄。
此时,第1个设备的连接就完成了。接下来,开始要连接第2个设备。
这时,1.6秒也已经到了,出现事件SBP_AUTO_CONNECT_EVT,于是会出现下列现象,如果simpleBLEScanIdx=simpleBLEScanRes 表明已经连上了所有的扫描到的从机,此时就会退出,再也不会进行连接动作了。
这里我们特别注意到simpleBLEScanRes 这个值,它在扫描事件中被设置了的。这个在扫描完成后设置了,而不是在GAP_DEVICE_INFO_EVENT中进行任何处理,故如果只用这个GAP_DEVICE_INFO_EVENT的话,就会出问题的。除非将这个值进行++处理。 如果没有完成,则会simpleBLEScanIdx++。这个很重要。然后它会: performPeriodicTask_AutoConnect(); // 执行动连接
osal_start_timerEx(simpleBLETaskId,SBP_AUTO_CONNECT_EVT, SBP_PERIODIC_EVT_AUTO_CONNECT );
这时,又会执行一个建立连接的动作。注意到在前面按下中间键时,已经进行了这个动作。 于是最后将所有扫描到的都连接上了。如果在此期间,有连接的失败的,将这个值: p->simpleBLEState = BLE_STATE_CONNECTED;
不会被设置。并且将这个p指向的结构设置为初始值。此后在发数据时就不会乱发了。 注意到断开连接时,这个simpleBLEScanIdx并没有--。即在设备列表中并没有去掉这个值。
8.如果一切顺利,这个设备都连接好了,且连了3个。当然如果设备本来少于3个,就按实际个数连接。
9.接着等按键操作了。先看几个简单的。比如按下down键。把所有的已经建好连接的下位机的RSSI回调函数激活或取消。
GAPCentralRole_StartRssi( p->simpleBLEConnHandle, DEFAULT_RSSI_PERIOD );// 关键 为了达到轮流激活取消的效果,用到了p中的一个变量p->simpleBLERssi来进行判断。
10.如果按下了左键,而且不是在扫描状态,即扫描完成了,就是显示各个下位机的地址。注意此时这个simpleBLEScanIdx++; 所以如果在按下中间键之前,按了左键,这个值就会变掉。到时,如果没有按中间键就去按这个左键,这个程序感觉要出错似的?不过不会了,因为在按下中间键时,会将这个值清0的。
11.如果按下了右键,这个是去更新参数,暂时不管这个了。
12.如果按下了开发板上的S1键,这个才是最复杂的,要开始发数据了。
15
我们发现这个打印老是出错,怎样让这个打印在消息中处理?即我们需要打印时,就发送一个消息给任务,在任务中在有空时才去打印这个消息中的内容,等打完后才去重新打印,即打印完了后,(这个可能要用一个函数来决定,再去打印下一个)这样就能把所有的消息都打印好了。打印队列中还有东西未打完,我们就不会去处理这个打印消息。只有打完了才去处理。这个要用消息队列去处理吧。消息本来就是一个队列的。 好象有点麻烦。
先看一下,我们打印时如何知道缓冲区的内容已经打印完了呢? HalUARTBusyDMA()这个函数也不知是不是?
还有一个办法是每次打印后都加一个延时才去处理下一个。但怎样加延时呢?OSAL中没有这个。只有延时发事件。
我们产生一个周期性的事件,在这个周期性的事件中,去处理打印消息,因为一个周期设为10ms,则10ms我们肯定打印完了。
这样,我们先设计一个周期性的事件,在这个事件中,我们去处理
原来的定时闪灯的方法是这样的,在我们自已定义的任务中,每隔一段时间,就去给
Hal_TaskID这个任务,发送一个事件,这个任务发现这个事件后,就调用一个Test_Send() 在这个Test_Send中,我们给消息分配了一个内存,然后,将这个系统消息发给我们的定义的任务,在我们的任务中,在系统消息处理中,将处理这个消息(闪灯)然后,将这段消息内存删除。
现在,我们要打印时,就生成一个消息,且发送给我们自已定义的任务,在这个任务中,我们处理系统消息,但这个系统消息每次只处理一条
注意到键改变这个事件它是 typedef struct {
osal_event_hdr_t hdr; //事件头,这个很重要,它包含有下列第6行部分 uint8 state; // shift 状态 uint8 keys; // keys 键值 } keyChange_t;
typedef struct {
uint8 event; ///事件,这个与上面的相呼应。 uint8 status; } osal_event_hdr_t;
在系统信息处理中,本来这个我们发过来的是一个指针keyChange_t。这个指针有比较多的成员变量。但是后来在任务中进行处理时,即变更为osal_event_hdr_t类型的指针 simpleBLECentral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );这就说明,系统信息处理中,只处理我们这个指针的前面几个字节,后面的直接忽略了,但是在调用这个处理函数时,可不能忽略,因为按键值是在后面的。
21
这个有点奇怪的是:
从队列中收消息时,它是返回的是 osal_msg_hdr_t 这是一个消息队列。 收到消息后,要读取其中的事件时,这个指针却变成 osal_event_hdr_t 在具体处理函数中,即要将同一个指针变成 keyChange_t 去处理。
以上的原因是keyChange_t的第一个指针,指向的就是osal_event_hdr_t,而消息得到的第一个数处,它是keyChange_t,这个在osal_msg_push()函数中可以看得出来。
了解了以上现象后,我们先仿这个 typedef struct {
osal_event_hdr_t hdr;
uint8 state; // shift uint8 keys; // keys } keyChange_t;
定义一个新的结构 typedef struct {
osal_event_hdr_t hdr; ///不想含这个事件 可以吗? uint8 printf_num; //我们要打印的字节的长度 uint8 printf_data[32]; ///我们最多打印32个字节 }printf_t;
我们发一个消息给它,每次我们只处理一个消息,处理完这个消息后,就将这个消息清除掉。消息中并不含事件可以吗?看来是不行的,因为发送消息时,必须是系统事件才可以。
我们这样做,串口打印发的是系统消息,这个系统消息队列中,如果时间未到,我们并不处理它,只有当时间到了,我们才将这个消息删除掉,这样,这些打印消息都会留在这个系统队列中。等待下一次又会去处理。处理一个消息串口消息就将这个计数器保留下来,看是否相同,如果不同才去处理串口消息。程序不做大的修改。还是每隔一段时间会有时间到的系统消息到来。不过这样做显然不行,因为每次运行到此时,系统消息可能都是哪个串口消息,而这个时间消息却永运等不来了。
只有这样,将这个系统消息给串口独占了。该任务的系统消息只有一个。我们先改一下,将原来的部分删除掉看一下。先让这个任务自已产生一个周期性的调用。然后加一个静态的全局变量作为计数值。
接下来,修改关键的部分,想打印时,我们就生成一个系统消息给我们自已的任务。
显然,我们在打印文档中修改它,先在npi.h中定义这个新结构。 typedef struct {
22
osal_event_hdr_t hdr; ///不想含这个事件 可以吗? uint8 printf_num; //我们要打印的字节的长度 uint8 printf_data[32]; ///我们最多打印32个字节 }printf_t;
然后生成一个新的虚拟打印语句,我们先打的是ASCII部分。如果外部要打印必须先将要打印的东西换成它才可以。而且每次打印不能超过32个字符。 Virtual_printf(char*,uint8 len)
另外写三个函数
Virtual_printf_srting(char*p) Virtual_printf_data(int data)
先简单一点的吧。Virtual_printf(char*,uint8 len)
将结构定义到npi.c中。加上这个函数。构想如下: 处理这个消息,在自已定义的任务中。
每条打印语句,转化为系统事件+消息给我们的任务。这个消息由系统事件引起。我们的任务在得到系统事件后,去找到这个消息去处理,当这个消息存在时,就看是否是打印消息,如果是,则看时间更新到否,如果到就打印,且将此消息清除,如果不到,则该消息还存在消息队列中。只要还有消息,这个系统事件就存在,没有清除掉,如果消息队列为空,则将这个系统事件清除掉。结果发现只打印了第一句就死机了。而且也不再运行到我们的事件处理程序中去了。此时就有可能是消息队列溢出了?
我们只发一条消息试一下。可以打印,但是发2条就不能打印了。
原因是第1条执行后,再执行第2条的消息处理时,开始3次还能进到处理程序中(由于时间没到不处理),但是后面就直接读消息队列时却为空了。没找到原因。暂时放一下。 发现这个从机的广播信息不符合规定的,所以一直找不到下位机,现在可以发现一个下位机了。
现在发现一个设备,可以开始下一步了。现在下位机我们用它的一个特征值
建立连接后,有一个事件会发生,就是建立连接事件会发生,于它在这个回调函数中,会直接会发送一个START_DISCOVERY_EVT给本任务,于是本任务会调用: simpleBLECentralStartDiscovery( );函数,看下位机有什么样的服务。 注意到在建立连接完成后,这个值被得到了:simpleBLEConnHandle 然后,调用发现服务后就去发现服务回调函数中处理一些事宜。
我们打算用CHAR6来做一些事情,上位机会发一个helloworld下去,下位机通过此口回一个0x01 0x02 0x03 0x04回来
怎样得以char6的句柄呢?
在发现服务中的回调函数中,我们发现了simpleBLECharHdl。这个就是通讯特征的句柄。如果要发现多个特征的句柄,估计是在这个子程序中多循环几次。一直得到最后一个。
23
得么这个句柄后,看在UP键中是如何写的?
status = GATT_WriteCharValue( simpleBLEConnHandle, &req, simpleBLETaskId ); 但是怎么没发现simpleBLECharHdl呢?发现了,就在。。。
发现发送后,主机返回一个ERROR 再看从机,它没有设下面这个:
SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR6, SIMPLEPROFILE_CHAR6_LEN, charValue6 );
我们在初始化时将这个设一下看。
设完后,可以打印helloworld了。接下来,我们反向打印一下HELLOWORLD.即上位机发一个读命令,下位机就返回这个HELLOWORLD这10个字符。
先看一下,上位机如何读,我们想通过同一个特征值char6来读一下看。
原来的怎么读的。原来上位机按一个键(手机上按一个键)可以读温度和湿度。现在我们首先在从机中先改动一下。我们知道,原来我们是将传感器的温湿度是每隔一段时间,进行一个周期性的数据采集,放到SensorValue[i]中。 在读配置这个函数中,它将会调用:
uint8 simpleProfileReadConfig(uint16 uuid, uint8 *newValue)特别注意到这个函数返回值为len.如果我们要读10个字符串,则返回的值就是10。于是我们要将这个newValue的定义改为较长才行。还有读配置其实可能并不是一个妥当的办法,因为读配置只能读一次。每次可能只能读23个字符,这样字符多的话就要读好几次。先这样读10个看一下。最好是主动发100来个字符过去。
这里4个字节还没有改,接下来要改的。先没改,先看如何调用这个函数的。看到了,是在 static uint8 simpleProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen ) 中调用这个函数的。
但又是谁在什么时候调这个回调的呢?特别是这个maxLen这个参数好象没有用到。
这样下位机差不多改好了,下面看一下主机该如何改呢?主机要读char6和东西。读过来有多长,再打印出来。 以下是主机部分。
先看读成功后的回调函数,它将读来的东西打印了一下,原来只打印一个字节的。现在要打印多少字节。中间有一个长度,也有一个值,其最长的长度定义为23,我们不要超过它就是了。 接下来,我们怎么知道这个char6 的特征值要读呢?我们按了一个键就是去读下位机的配置的。
在按下键中,我们没有找到有关的部分。因为它是一个外部函数,我们只执行了下列: attReadReq_t req;
req.handle = simpleBLECharHdl; ///这个就是指定的要读的特征值
status = GATT_ReadCharValue( simpleBLEConnHandle, &req, simpleBLETaskId ); 大功初成!双向都可以通过char6来进行了。主机按UP键,发送一个helloworld到下位机,再按一次UP键,则主机发一个读下位机的char6信息,下位机的char6信息它就是大写的HELLOWORLD.来回都正确了。
时间已经指向用了一个多月,从11月25日到12月29日,太长了,也许自已太笨。
24
25
26
正在阅读:
关于TI的CC2541的程序解读06-27
SECCO表格 - 图文03-26
Cprimerplus课后编程练习答案04-30
引水式水电站课程设计 - 图文05-11
我眼中的李白作文500字06-24
失地农民引导性培训教学计划(1)资料03-08
会展财务预算方案与时间进度表05-01
2015年周历表(A4打印版)08-16
家政服务公司创业计划书 - 图文02-26
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 解读
- 程序
- CC2541
- 关于
- 无机高分子絮凝剂项目IPO上市咨询(2013年最新细分市场+募投可研
- 山东省淄博市沂源县中庄乡中学八年级政治上册《依法享有财产权、
- 有效利用身边的资源促进幼儿健康成长
- 关于确保实现首季“开门红”工作开展情况的报告0
- 宁钢1780mm热连轧机总说明书新
- 伦敦大学研究生申请条件 - 图文
- c#串口通信
- 语法完型阅读训练3
- 某高新技术开发区供水管网工程可研 - 图文
- 100测评网初中英语资料-Unit 4 Reading The history of ba
- 一年级语文上册拼音教案
- 花呗支付,支付吧授权函
- 2016年最新急诊剖宫产诊断及治疗标准流程
- 2015年度安徽海盾建材有限公司销售收入与资产数据报告 - 图文
- 香奈儿五号香水案例分析
- 海上船舶远程视频监控系统设计方案
- 北京美太(ME丨DA)飞天集成灶售后维修电话
- 对老年糖尿病患者社区护理干预效果分析
- 我的假日生活
- 地板专业分包合同技术要求10.14