本文最后修改时间:2022年01月25日 10:19
一、本节简介
本节以simple_peripheral工程为例,介绍CC2640R2F如何使用定时器每30ms,发4个数据包(每个数据20字节),共发送80字节给app。
二、实验平台
1)CC2640R2F平台
①协议栈版本:CC2640R2 SDK v1.40.00.45
②编译软件:CCS7.3.0.00019
③硬件平台:香瓜CC2640R2F开发板、HC42的蓝牙5模块(作为主机使用)
④仿真器:香瓜XDS100V3下载器
2)手机平台
①手机型号:小米10至尊版
②安卓版本:安卓11
③安卓app:BLE Scanner
3)电脑平台
①HC-T串口助手(汇承公司自己的串口助手,买HC42模块时带的资料)
三、版权声明
1)作者:甜甜的大香瓜
2)声明:喝水不忘挖井人,转载请注明出处。
3)纠错/业务合作:897503845@qq.com
4)香瓜BLE之CC2640R2F群:557278427
5)本文出处:原创连载资料《简单粗暴学蓝牙5》
6)完整开源资料下载地址(电脑端打开):
四、实验前提
1、在进行本文步骤前,请先阅读以下章节:
1)《简单粗暴学蓝牙5》的“第一章至第四章”章节。
2)《简单粗暴学蓝牙5》的《香瓜CC2640R2F之连接参数》。
2、在进行本文步骤前,请先实现以下章节:
1)《简单粗暴学蓝牙5》的“第三章 软件的安装及使用”章节。
2)《简单粗暴学蓝牙5》的《香瓜CC2640R2F之指令读写IO》。
五、基础知识
1、本文的通信协议是?
答:
图片有点小,放大看。
2、本文的连接间隔修改为多少?
答:
本文的连接间隔范围设置为10ms~10ms,定时器定时每30ms发送一次。
要注意这个连接间隔不符合IOS连接参数要求,也不符合部分安卓手机的连接参数要求。本文是牺牲兼容性的前提下,来提升通信速率。如果要能兼容更多的手机,只能降低速率,自行修改。
3、本文的速率是多少?
答:每30ms发送4包(每包20字节,共80字节)。
由于每秒有(1000/30)次通信,所以通信速率=(1000/30)*80=2666.7字节/秒
4、本文的速率是极限值吗?
答:不是极限值,不同手机有不同极限值。如果是像两个CC2640R2F主从机都确定的通信,即可以实际去改至极限值,再在极限值基础上留一点余量。
另外也可以使用蓝牙5的大数据包通信,pdf第一章有资料链接。理应比这种蓝牙4.0就有的通信方式快。
六、硬件原理
暂无
七、实验步骤
一、修改连接参数(simple_peripheral.c中)
#ifndef FEATURE_OAD // Minimum connection interval (units of 1.25ms, 80=100ms) for automatic // parameter update request #define DEFAULT_DESIRED_MIN_CONN_INTERVAL 8 //10ms // Maximum connection interval (units of 1.25ms, 800=1000ms) for automatic // parameter update request #define DEFAULT_DESIRED_MAX_CONN_INTERVAL 8 //10ms #else // FEATURE_OAD // Increase the the connection interval to allow for higher throughput for OAD // Minimum connection interval (units of 1.25ms, 8=10ms) for automatic // parameter update request #define DEFAULT_DESIRED_MIN_CONN_INTERVAL 8 // Maximum connection interval (units of 1.25ms, 8=10ms) for automatic // parameter update request #define DEFAULT_DESIRED_MAX_CONN_INTERVAL 8 #endif // FEATURE_OAD // Slave latency to use for automatic parameter update request #define DEFAULT_DESIRED_SLAVE_LATENCY 0 // Supervision timeout value (units of 10ms, 1000=10s) for automatic parameter // update request #define DEFAULT_DESIRED_CONN_TIMEOUT 100 //连接超时时间,连接IOS时此参数必须小于6S // After the connection is formed, the peripheral waits until the central // device asks for its preferred connection parameters #define DEFAULT_ENABLE_UPDATE_REQUEST GAPROLE_LINK_PARAM_UPDATE_INITIATE_BOTH_PARAMS//GAPROLE_LINK_PARAM_UPDATE_WAIT_REMOTE_PARAMS // Connection Pause Peripheral time value (in seconds) #define DEFAULT_CONN_PAUSE_PERIPHERAL 1 //有时当从机断开连接时,主机端过10S才会断开,此时将此数值改小可让主机快速发现断开连接 |
修改了上述红色字体的五处。注意如果连接间隔范围是10ms~20ms,香瓜实测有丢包现象,所以后来改成10ms~10ms。
2、定义数据通信事件
1)定义数据通信的宏(simple_peripheral.c中)
//GUA #define SBP_GUA_PERIODIC_EVT Event_Id_02 //香瓜周期事件 #define SBP_GUA_UART_EVT Event_Id_03 //香瓜串口处理事件 #define SBP_GUA_DATA_EVT Event_Id_04 //香瓜数据通信事件 #define SBP_GUA_ALL_EVENTS SBP_GUA_PERIODIC_EVT | SBP_GUA_UART_EVT | SBP_GUA_DATA_EVT //所有事件的集合 //GUA |
SBP_GUA_DATA_EVT是新定义的,其他是香瓜其他例程定义的,注意添加定义的位置。
2)添加周期事件的处理(simple_peripheral.c的SimpleBLEPeripheral_taskFxn中)
//GUA //香瓜数据通信事件 if (events & SBP_GUA_DATA_EVT) { GUA_U16 nGUA_ConnHandle; //再次启动定时器 Util_startClock(&GUA_DataClock); //获取连接句柄 GAPRole_GetParameter(GAPROLE_CONNHANDLE, &nGUA_ConnHandle); //数据通信处理 //第一包 for(GUA_U8 i=0; i<=19; i++) { gaGUA_Data[i++] = gnGUA_Num >> 8; //高8位 gaGUA_Data[i] = (GUA_U8)(gnGUA_Num); //低8位 if(++gnGUA_Num > 10000) { gnGUA_Num = 0; } } GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, gaGUA_Data, 20); //发送数据 //第二包 for(GUA_U8 i=0; i<=19; i++) { gaGUA_Data[i++] = gnGUA_Num >> 8; //高8位 gaGUA_Data[i] = (GUA_U8)(gnGUA_Num); //低8位 if(++gnGUA_Num > 10000) { gnGUA_Num = 0; } } GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, gaGUA_Data, 20); //发送数据 //第三包 for(GUA_U8 i=0; i<=19; i++) { gaGUA_Data[i++] = gnGUA_Num >> 8; //高8位 gaGUA_Data[i] = (GUA_U8)(gnGUA_Num); //低8位 if(++gnGUA_Num > 10000) { gnGUA_Num = 0; } } GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, gaGUA_Data, 20); //发送数据 //第四包 for(GUA_U8 i=0; i<=19; i++) { gaGUA_Data[i++] = gnGUA_Num >> 8; //高8位 gaGUA_Data[i] = (GUA_U8)(gnGUA_Num); //低8位 if(++gnGUA_Num > 10000) { gnGUA_Num = 0; } } GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, gaGUA_Data, 20); //发送数据 } //GUA |
此处发送4包数据,每包20字节。内容是0~10000,当超过10000时又会从0开始。每个数字是16位的,占用两个字节空间,所以一包20字节的可以发10个16位的数据。
此处连续调用4次发送函数,会被传到底层缓存区,在下一次连接事件(本文的每次连接事件为10ms~10ms)到来时发送出去。
3、初始化定时器
1)定义定时器结构体数据(simple_peripheral.c中)
//GUA static Clock_Struct GUA_DataClock; //GUA |
2)设置定时时间(simple_peripheral.c中)
//GUA #define SBP_GUA_DATA_EVT_PERIOD 30 //香瓜定时时间 //GUA |
这里设置为30ms。
3)初始化定时事件(simple_peripheral.c的SimpleBLEPeripheral_init函数末尾中)
//GUA //初始化定时器 Util_constructClock(&GUA_DataClock, SimpleBLEPeripheral_clockHandler, SBP_GUA_DATA_EVT_PERIOD, 0, false, SBP_GUA_DATA_EVT); //GUA |
注意:这里仅仅是对定时器初始化函数,还需要一个“Util_startClock”函数(启动定时器函数)。必须两个都有,定时器才能被启动起来。
本文中的“Util_startClock”函数在特征值回调函数中被调用。
4、定义全局变量(simple_peripheral.c中)
//GUA GUA_U8 gaGUA_Data[20] = {0}; //特征值的20字节缓存区 GUA_U16 gnGUA_Num = 0; //用于计数数据内容0~10000的变量 //GUA |
5、添加手机app指令处理函数
(替换simple_peripheral.c的GUA_Profile_CharValueChangeEvt)
//********************************************************************** //name: GUA_Profile_CharValueChangeEvt //introduce: 香瓜服务的应用层处理函数 //parameter: nGUA_ParamID:特征值ID //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜BLE之CC2640R2F(557278427) //shop: opengua.taobao.com //changetime: 2021.12.27 //********************************************************************** static void GUA_Profile_CharValueChangeEvt(GUA_U8 nGUA_ParamID) { GUA_U16 nGUA_ConnHandle; GUA_U8 naGUA_Buf[20] = {0}; GUA_U8 *pGUA_Value = naGUA_Buf; //判断是哪个特征值 switch(nGUA_ParamID) { //特征值1 case GUAPROFILE_CHAR1: { //获取连接句柄 GAPRole_GetParameter(GAPROLE_CONNHANDLE, &nGUA_ConnHandle); //获取读到的数据 GUAProfile_GetParameter(GUAPROFILE_CHAR1, naGUA_Buf); //根据读到的数据头字节判断是读IO还是写IO,0x00是读IO,0x01是写io switch(naGUA_Buf[0]) { //读IO case 0x00: { //根据第二个字节,判断是读第几个IO switch(naGUA_Buf[1]) { //第0个IO(DIO15,LEFT键) case 0x00: { //判断该IO是高还是低 if(PIN_getInputValue(IOID_15) == 0) { naGUA_Buf[2] = 0x00; //低电平 } else { naGUA_Buf[2] = 0x01; //高电平 } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, naGUA_Buf, 3); break; } //第1个IO(DIO18,RIGHT键) case 0x01: { //判断该IO是高还是低 if(PIN_getInputValue(IOID_18) == 0) { naGUA_Buf[2] = 0x00; //低电平 } else { naGUA_Buf[2] = 0x01; //高电平 } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, naGUA_Buf, 3); break; } //第2个IO(DIO19,UP键) case 0x02: { //判断该IO是高还是低 if(PIN_getInputValue(IOID_19) == 0) { naGUA_Buf[2] = 0x00; //低电平 } else { naGUA_Buf[2] = 0x01; //高电平 } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, naGUA_Buf, 3); break; } //第3个IO(DIO12,DOWN键) case 0x03: { //判断该IO是高还是低 if(PIN_getInputValue(IOID_12) == 0) { naGUA_Buf[2] = 0x00; //低电平 } else { naGUA_Buf[2] = 0x01; //高电平 } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, naGUA_Buf, 3); break; } //其他 default:break; } break; } //写IO case 0x01: { //根据第二个字节,判断是写第几个IO switch(naGUA_Buf[1]) { //第0个IO(DIO25,LED1) case 0x00: { //根据第2个字节的高低来写IO if(naGUA_Buf[2] == 0) { GUA_Led_Set(GUA_LED_NO_1, GUA_LED_MODE_OFF); //拉低 } else { GUA_Led_Set(GUA_LED_NO_1, GUA_LED_MODE_ON); //拉高 } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, naGUA_Buf, 3); break; } //第1个IO(DIO27,LED2) case 0x01: { //根据第2个字节的高低来写IO if(naGUA_Buf[2] == 0) { GUA_Led_Set(GUA_LED_NO_2, GUA_LED_MODE_OFF); //拉低 } else { GUA_Led_Set(GUA_LED_NO_2, GUA_LED_MODE_ON); //拉高 } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, naGUA_Buf, 3); break; } //第2个IO(DIO7,LED3) case 0x02: { //根据第2个字节的高低来写IO if(naGUA_Buf[2] == 0) { GUA_Led_Set(GUA_LED_NO_3, GUA_LED_MODE_OFF); //拉低 } else { GUA_Led_Set(GUA_LED_NO_3, GUA_LED_MODE_ON); //拉高 } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, naGUA_Buf, 3); break; } //第3个IO(DIO0,LED4) case 0x03: { //根据第2个字节的高低来写IO if(naGUA_Buf[2] == 0) { GUA_Led_Set(GUA_LED_NO_4, GUA_LED_MODE_OFF); //拉低 } else { GUA_Led_Set(GUA_LED_NO_4, GUA_LED_MODE_ON); //拉高 } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, naGUA_Buf, 3); break; } //其他 default:break; } break; } //数据通信控制 case 0x02: { //根据第二个字节,判断是停止数据通信,还是开始数据通信控制 switch(naGUA_Buf[1]) { //停止数据通信 case 0x00: { //停止定时器 Util_stopClock(&GUA_DataClock); //清除数据 gnGUA_Num = 0; break; } //开始数据通信 case 0x01: { //启动定时器 Util_startClock(&GUA_DataClock); //清除数据 gnGUA_Num = 0; break; } //其他 default:break; } Break; } default:break; } break; } //其他 default: break; } } //GUA |
这里大部分代码都是前例程的代码,真正添加的是“case 0x02”的数据通信部分。
可通过0x0201来打开定时器,启动数据通信事件;可通过0x0200来关闭定时器,停止数据通信事件。
八、注意事项
1、手机可能缓存了之前固件的数据(在更新过固件之后,都需要清除手机端的缓存!!!),因此要清除缓存,清除缓存的方法如下:
方法一:关闭app、关闭蓝牙总开关、打开蓝牙总开关、打开app。
方法二:手机重启。
九、实验结果
仿真并全速运行,手机app连接并找到0xffe1的香瓜特征值,需要在app上打开该通道的通知开关。
1、开始数据通信
写入“0x0201”,即可看到快速收到数据。
2、停止数据通信
写入“0x0200”,即可停止收到数据。
不过手机app测试无法看清楚实际速率和是否丢包。
使用HC42模块进行速度实测:
正常连接时(详细连接过程看HC42说明书),发送0201开始通信、计时10秒后,发送0200关闭通信,可看到27340个字节,也就是2.734K字节/秒。任意选取一段数据查看都没有问题。
因此实验成功。
标签:gnGUA,GUA,CC2640R2F,naGUA,Num,30ms,IO,80,Buf From: https://blog.csdn.net/feilusia/article/details/145207634