1 起因
笔者在开发蓝牙芯片CC2540,试图取实现大量数据的BLE透传功能,在认真学习了蓝牙4.0协议标准以及TI提供的SDK说明,我总结了两个发送函数,以及两个接收函数的位置。
两个发送函数包括
1 当我的设备处于主机角色的时候,发送数据给从机的函数,此时采用写特征值的方法。
void CC2541_send_as_central(uint8 * buf , uint16 len);
2 当我的设备处于从机角色的时候,发送数据采用notify方法
void CC2541_send_as_periph(uint8 * buf , uint16 len);
理所当然的可以将两个发送函数合并为
CC2541_send(role_t role , uint8* buf , uint16 len);
阅读TI提供的蓝牙协议栈函数,各种机制,函数回调非常复杂,但是通过网上其他人的文章我找到了插入位置。
两个接收的位置
1 做从机的时候,接收数据事件发生在profile里的特征值被修改后发生。
static void TRANSFERProfileChangeCB(uint8 paramID) {
uint8 bBuf[20] = { 0 };
uint8 rltByteNum=0;
//...省略部分
TRANSFERProfile_GetParameter(TRANSFERPROFILE_CHAR1, bBuf,&rltByteNum);
//通过get函数获得值修改后的特征值,存放在bBuf数组里
//在这里得知了获取的数据bBuf 以及长度 rltByteNum
}
2 做主机的时候,接收数据事件发生在GATT的MESSAGE事件中,因为作主机的时候,获得数据实际上就是得到了从机的notify。(从机给主机发数据采用notify,类似中断一样,强行告诉主机,我有数据发给你,主机还有一个read方法可以访问从机的数据,但是不是从机主动发起的)。
static void simpleBLECentralProcessGATTMsg(gattMsgEvent_t *pMsg) {
//...函数里面包含了各种GATT的消息,通过判断pMsg->method来判断这是哪一种消息类型,很明显simpleBLECentralProcessGATTMsg函数是一个系统的回调函数,下文分享笔者对回调的理解,为文章简洁,仅复制相关代码
else if ((pMsg->method == ATT_HANDLE_VALUE_NOTI)) {
//检查profile Handle号,由BTools获得
if (pMsg->msg.handleValueNoti.handle == 0x0025) {
//获得的数据 pMsg->msg.handleValueNoti.value
//获得数据的长度是 pMsg->msg.handleValueNoti.len
}
}
}
笔者想实现的功能是 主从一体的蓝牙透传模块,数据由外部经过UART传入蓝牙芯片,蓝牙芯片通过调制蓝牙信号发送给另一个蓝牙芯片,另一个蓝牙芯片经过UART传给终端设备。
因此,笔者还得研究关于TI提供的UART工具。找到了一个发送函数,以及一个在回调函数中的接收位置。
发送函数
void NPI_WriteTransport(uint8* buffer , uint16 len);
接收位置
static void simpleBLE_NpiSerialCallback(uint8 port, uint8 events) {
if (events & (HAL_UART_RX_TIMEOUT | HAL_UART_RX_FULL)){
uint8 numBytes = 0;
numBytes = NPI_RxBufLen(); //读出串口缓冲区有多少字节
if (numBytes == 0) {
return;
} else{
uint8 *buffer = osal_mem_alloc(numBytes);
if (buffer){
//数据指针是 buffer 长度是 numBytes
osal_mem_free(buffer);
}
}
}
}
回调函数的理解
如果你自己也经常使用函数指针,那么对回调应该很容易理解。
例如我自己是一个非常崇尚代码完美主义的人,我的main函数是这样写的,写的很漂亮。
int main(void){
mySystem_init();
Loop:
mySystem_handle();
User_handle();
goto Loop;
}
注意这里mySystem_handle 和 mySystem_init是我写的代码,User_handle是我预留给用户去写的!
这个main函数的结构就是由三块构成的,mySystem_init 和 mySystem_handle 以及User_handle ,类比蓝牙协议栈,mySystem_init和mySystem_handle实现了蓝牙协议栈的基本功能,我想绝大多数人是不愿意修改这部分代码的,因此不能动这部分代码。
TI的作者工程师也不希望你修改这段代码的代码结构!于是作者们会给你提供User_handle的入口给你。
例如 User_handle会这样写
typedef void * function_pointer(void);// 定义函数指针类型
function_pointer User_app;
void User_handle(){
User_app();
}
void register_user_app(function_pointer ){
User_app = function_pointer ;
}
这样一来,你可以使用register_user_app函数来修改主函数的执行内容,转到你配置的程序流中,但是不修改原有的工程运行结构。
BLE的协议栈很复杂,一般人不应当轻易的改变他的工程结构,所以TI的工程师们选择把回调函数放给开发者。
上文中的simpleBLECentralProcessGATTMsg等函数都是回调函数。
回调函数怎么传值?
还是以app_handle为例
typedef void * function_pointer(int);// 定义函数指针类型
function_pointer User_app;
void User_handle(){
User_app(100);
}
void register_user_app(function_pointer ){
User_app = function_pointer ;
}
void test_task(int a){
printf(“得到的数 %d ”,a);
}
//在主函数中注册
register_user_app(test_task);
运行原有工程结构后,test_task就获得了100这个值。
在BLE的SDK中大部分都是依靠这样的回调来进行传值,包括我之前总结的几个接收数据的位置!
现在遇到的问题是:
1 在回调函数中得到的数据不完整,举个例子来说,外部通过UART传进来的数据,不止一次的进入回调函数,每次进入回调函数获得的数据长度都不同,但是系统能保证的是不会少数据,但是每次进入的次数都不一样,这给最终的数据处理带来麻烦,因为最终数据处理肯定得是一个完整的包。
2 蓝牙发送速度与串口接受速度不一致,有快有慢,会出现数据覆盖的现象。
笔者采用两种方法解决这个问题,
1 通过超时判断
2 通过循环队列缓存
后面慢慢写自己解决问题的过程,分享解决的过程给大家