当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 >
BLE 数据接发流程
时间:2018-08-16作者:华清远见

BLE概述

BLE分为两部分:控制器和主机。对于4.0以前的蓝牙,这两部分是分开的。所有profile(用来定义设备或组件的角色)和应用都建构在GAP或GATT之上。下面由结构图的底层组件开始介绍。

协议梭的实现方式采用分层的思想,控制器部分包括物理层、链路层、主机控制接口层主机部分包括逻辑链路控制及自适应协议层、安全管理层、属性协议层、通用访问配置文件层、通用属性配置文件层;上层可以调用下层提供的函数来实现需要的功能。

PHY层:1Mbps自适应跳频GFSK(高斯频移键控),运行在免证的2.4GHz。

LL层为RF控制器(射频),控制设备处于准备(standby)、广播、监听/扫描(scan)、初始化、连接,这五种状态中任一种。

HCI层:为接口层,向上为主机提供软件应用程序接口(API),对外为外部硬件控制接口,可以通过串口、SPI、USB来实现设备控制。

L2CAP层:提供数据封装服务

SM层(加密);提供配对和密匙分发,实现安全连接和数据交换

ATT层:负责数据检索

GATT层:出纳负责处理向上与应用打交道,而GATT负责向下把检索任务子进程交给ATT层去做,其关键工作是把为检索工作提供合适的profile结构,而profile由检索关键词(characteristics)组成。

GAP层:向上提供应用程序接口,向下管理各层的相应的功能,尤其是指示LL层的五种状态切换,指导SM层做好加密工作。

BLE 启动流程

在IAR 工程的左侧有很多文件夹,如APP 、HAL、OSAL、 PROFILES等,如图下图所示, 这些文件夹下面包含了很多源代码,这种实现方式与蓝牙4.0 BLE 协议的分层思想是相对应的,尽量将实现某些功能的函数放在同一个文件夹下。

图 BLE 4.0 工程列表

下面从main()函数入手,看看main函数都做啦哪些工作。下找到simpleBLECentral_Main.c文件中的main函数入口。代码如下:

int main(void)

{

/* Initialize hardware */

HAL_BOARD_INIT();

// Initialize board I/O

InitBoard( OB_COLD );

/* Initialze the HAL driver */

HalDriverInit();

/* Initialize NV system */

osal_snv_init();

/* Initialize LL */

/* Initialize the operating system */

osal_init_system();

/* Enable interrupts */

HAL_ENABLE_INTERRUPTS();

// Final board initialization

InitBoard( OB_READY );

#if defined ( POWER_SAVING )

osal_pwrmgr_device( PWRMGR_BATTERY );

#endif

/* Start OSAL */

osal_start_system(); // No Return from here

return 0;

}

在main函数中大部分是对CC2540的初始化和对无线网络协议的配置。主要的就是osal_start_system()函数的处理,这是重点,只有执行这个函数,蓝牙BLE的协议栈才是真正的运行。

为下函数为任务启动过程。实现不停的查看事件表,如果有事件发生就调用相应的事件处理函数。

void osal_run_system( void )

{

uint8 idx = 0;

#ifndef HAL_BOARD_CC2538

osalTimeUpdate();

#endif

Hal_ProcessPoll();

do {

if (tasksEvents[idx]) // 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 = tasksEvents[idx];

tasksEvents[idx] = 0; // Clear the Events for this task.

HAL_EXIT_CRITICAL_SECTION(intState);

activeTaskID = idx;

events = (tasksArr[idx])( idx, events );

activeTaskID = TASK_NO_TASK;

HAL_ENTER_CRITICAL_SECTION(intState);

tasksEvents[idx] |= 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

}

事件处理函数

如果有事件调用SimpleBLEPeripheral_ProcessEvent()函数处理相应的消息事件。在simpleBLECentral.c文件中。

uint16 SimpleBLECentral_ProcessEvent( uint8 task_id, uint16 events )

{

VOID task_id; // OSAL required parameter that isn't used in this function

if ( events & SYS_EVENT_MSG )

{

uint8 *pMsg;

if ( (pMsg = osal_msg_receive( simpleBLETaskId )) != NULL )

{

simpleBLECentral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );

// Release the OSAL message

VOID osal_msg_deallocate( pMsg );

}

// return unprocessed events

return (events ^ SYS_EVENT_MSG);

}

if ( events & START_DEVICE_EVT )

{

// Start the Device

VOID GAPCentralRole_StartDevice( (gapCentralRoleCB_t *) &simpleBLERoleCB );

// Register with bond manager after starting device

GAPBondMgr_Register( (gapBondCBs_t *) &simpleBLEBondCB );

//自动开始搜索

if ( !simpleBLEScanning & simpleBLEScanRes == 0 )

{

simpleBLEScanning = TRUE;

simpleBLEScanRes = 0;

GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE,

DEFAULT_DISCOVERY_ACTIVE_SCAN,

DEFAULT_DISCOVERY_WHITE_LIST );

LCD_WRITE_STRING( "Scanning...", HAL_LCD_LINE_1 );

}

else

{

LCD_WRITE_STRING( "No Scan", HAL_LCD_LINE_1 );

}

return ( events ^ START_DEVICE_EVT );

}

if ( events & START_DISCOVERY_EVT )

{

simpleBLECentralStartDiscovery( );

return ( events ^ START_DISCOVERY_EVT );

}

// Discard unknown events

return 0;

}

接收数据

本函数主要是处理无线的数据,如把接收到的数据打印到串口终端。打印函数如以下代码中的红色字体部分。注意红色部分。

static void simpleBLECentralProcessGATTMsg( gattMsgEvent_t *pMsg )

{

if ( simpleBLEState != BLE_STATE_CONNECTED )

{

// In case a GATT message came after a connection has dropped,

// ignore the message

return;

}

if ( ( pMsg->method == ATT_READ_RSP ) ||

( ( pMsg->method == ATT_ERROR_RSP ) &&

( pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ ) ) )

{

if ( pMsg->method == ATT_ERROR_RSP )

{

uint8 status = pMsg->msg.errorRsp.errCode;

LCD_WRITE_STRING_VALUE( "Read Error", status, 10, HAL_LCD_LINE_1 );

}

else

{

// After a successful read, display the read value

uint8 valueRead = pMsg->msg.readRsp.value[0];

LCD_WRITE_STRING_VALUE( "Read rsp:", valueRead, 10, HAL_LCD_LINE_1 );

}

simpleBLEProcedureInProgress = FALSE;

}

else if ( ( pMsg->method == ATT_WRITE_RSP ) ||

( ( pMsg->method == ATT_ERROR_RSP ) &&

( pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ ) ) )

{

if ( pMsg->method == ATT_ERROR_RSP == ATT_ERROR_RSP )

{

uint8 status = pMsg->msg.errorRsp.errCode;

LCD_WRITE_STRING_VALUE( "Write Error", status, 10, HAL_LCD_LINE_1 );

}

else

{

// After a succesful write, display the value that was written and increment value

//LCD_WRITE_STRING_VALUE( "Write sent:", simpleBLECharVal++, 10, HAL_LCD_LINE_1 );

simpleBLEChar6DoWrite = TRUE;

}

simpleBLEProcedureInProgress = FALSE;

}

else if ( simpleBLEDiscState != BLE_DISC_STATE_IDLE )

{

simpleBLEGATTDiscoveryEvent( pMsg );

}

else if ( ( pMsg->method == ATT_HANDLE_VALUE_NOTI ) ) //通知

{

if( pMsg->msg.handleValueNoti.handle == 0x0038) //CHAR7的通知 串口打印

{

if(pMsg->msg.handleValueNoti.value[0]>=15)

{

NPI_WriteTransport(&pMsg->msg.handleValueNoti.value[1],14 );

NPI_WriteTransport("...\n",4 );

}

else

{

NPI_WriteTransport(&pMsg->msg.handleValueNoti.value[1],pMsg->msg.handleValueNoti.value[0] );

}

}

}

}

发送数据

从串口读取数据存入buf[]缓冲区,然后调用协议栈函数GATT_WriteCharValue()发送出去。

NpiSerialCallback()函数为串口回调函数,当串口有数据时就会调用此函数,并且同时会产生串口事件HAL_UART_RX_TIMEOUT。

static void NpiSerialCallback( uint8 port, uint8 events )

{

(void)port;

uint8 numBytes = 0;

uint8 buf[128];

if (events & HAL_UART_RX_TIMEOUT) //串口有数据

{

numBytes = NPI_RxBufLen(); //读出串口缓冲区有多少字节

if(numBytes)

{

if ( ( simpleBLEState == BLE_STATE_CONNECTED ) && ( simpleBLECharHd6 != 0 ) ) //已连接并获取完CHAR6的Handle就写CHAR6

{

if(simpleBLEChar6DoWrite) //写入成功后再写入

{

attWriteReq_t AttReq;

if ( numBytes >= SIMPLEPROFILE_CHAR6_LEN ) buf[0] = SIMPLEPROFILE_CHAR6_LEN-1;

else buf[0] = numBytes;

NPI_ReadTransport(&buf[1],buf[0]); //从串口读出数据

AttReq.handle = simpleBLECharHd6;

ttReq.len = SIMPLEPROFILE_CHAR6_LEN;

AttReq.sig = 0;

AttReq.cmd = 0;

osal_memcpy(AttReq.value,buf,SIMPLEPROFILE_CHAR6_LEN);

GATT_WriteCharValue( 0, &AttReq, simpleBLETaskId );

simpleBLEChar6DoWrite = FALSE;

}

}

else

{

NPI_WriteTransport("Not Ready\n", 10 );

NPI_ReadTransport(buf,numBytes); //释放串口数据

}

}

}

}

BLE Central串口通信协议

在上位机上显示终端的实时信息,必须有相应的传输协议,协议里含有终端的相应信息。

制定协议如下:

( 1 ) 串口打印数据协议信息:

21 B 01 00 54 00 00 01 3E 14 EB

21 协议头’!’ B:BLE 01 00:模块源节点地址 54:类型 00 00 01:数据/设备状态 3E 14:父节点地址 EB:校验

如果第2数据类型为’B’,则代表所连接的设备是BLE。

( 2 ) 协调节点串口发送的信息:

# C B f 00 01 01

“# C”控制终端的协议头 B:BLE 00 01:终端节点地址 01 控制命令(1:开/0:关)


发表评论

全国咨询电话:400-611-6270,双休日及节假日请致电值班手机:15010390966

在线咨询: 曹老师QQ(3337544669), 徐老师QQ(1462495461), 刘老师 QQ(3108687497)

企业培训洽谈专线:010-82600901,院校合作洽谈专线:010-82600350,在线咨询:QQ(248856300)

Copyright 2004-2018 华清远见教育集团 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号