【BLE】CC2541之通信协议

本篇博文最后修改时间:2016年12月12日,10:51。


一、简介

本文以SimpleBLEPeripheral工程为例,介绍如何在一个可读、可写、可通知、20字节长的特征值char6基础上,添加一个香瓜自定义的通信协议,并用app发送指令实现P11口上的LED点灯。


二、实验平台

协议栈版本:BLE-CC254x-1.4.0

编译软件: IAR 8.20.2

硬件平台: Smart RF开发板(主芯片CC2541)

手机型号: 小米4S

安卓版本:安卓5.1

安卓app:TruthBlue


三、版权声明

博主:甜甜的大香瓜

声明:喝水不忘挖井人,转载请注明出处。

原文地址:http://blog.csdn.NET/feilusia

联系方式:897503845@qq.com

香瓜BLE之CC2541群:127442605

香瓜BLE之CC2640群:557278427

香瓜BLE之Android群:541462902

香瓜单片机之STM8/STM32群:164311667
甜甜的大香瓜的小店(淘宝店):https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i

四、 实验前提
1、在进行本文步骤前,请先 阅读 以下博文:
暂无

2、在进行本文步骤前,请先 实现以下博文:
1)《CC2541之添加特征值》:http://blog.csdn.net/feilusia/article/details/48235691


五、基础知识

1、通信协议是什么?

答:通信协议是通信双方的通信规范,有被广泛使用如Modbus协议。

本文使用香瓜项目中最常用的一套自定义通信协议。


2、为什么要通信协议?

答:如果通信双方没有制定通信协议,会导致香瓜从CC2541端发送一个0x38数据给app端,app端不清楚这个数据是表示电机转动、还是温度值、还是电量值、又或是在骂人……

因此通信过程中的数据含义需要在开发之前定义下来,也就是要定义通信协议。


3、本文使用的通信协议是如何的?

答:


简单来说,就是通信过程采取20字节为一包的通信数据包,如果实际数值不满20字节,需要填充补齐上0xFF至20字节。

通信协议规定了指令集一共有两条:开关灯指令、应答错误指令。


4、本文中APP端需发送的指令集

答:

香瓜的测试指令

1、功能码0x00(开关灯)
1)关灯
fe010000ffffffffffffffffffffffffffffffff

2)开灯
fe01000100ffffffffffffffffffffffffffffff
一共两条指令:关灯、开灯。

可自行对照上面的通信协议加强理解该指令。


六、实验步骤

1、编写并添加自定义的通信协议驱动

1)写一个通信协议驱动GUA_RF_Communication.c(存放在“……\BLE-CC254x-1.4.0\Projects\ble\SimpleBLEPeripheral\Source\GUA”路径下)

//******************************************************************************                            
//name:             GUA_RF_Communication.c               
//introduce:        香瓜的通信协议        
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                       
//changetime:       2016.12.08       
//****************************************************************************** 
#include "GUA_RF_Communication.h"
#include <string.h>

/*********************宏定义************************/ 
//通信协议的功能码
#define FUNC_GUA_LED_ON_OFF                             0x00    //led开关的功能码

//应用层事件(从应用层复制过来)
#define SBP_START_DEVICE_EVT                            0x0001
#define SBP_PERIODIC_EVT                                0x0002
#define SBP_GUA_RF_COMMUNICAION_PROCESS_EVT             0x0004  //通信处理事件
#define SBP_GUA_RF_COMMUNICAION_COMMAND_ERR_EVT         0x0008  //通信数据出错事件

#define SBP_GUA_LED_ON_OFF_EVT                          0x0040  //led开关事件

//通信协议的数据包长度
#define GUA_DATA_PACKAGE_LEN                            20      //20字节一包

//******************************************************************************                
//name:             GUA_RF_Communication_Judgment               
//introduce:        RF的通信数据判断            
//parameter:        npGUA_Receive: 接收缓冲区首地址       
//return:           true: 数据包正确
//                  false: 数据包错误             
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                      
//changetime:       2016.12.08                      
//****************************************************************************** 
GUA_U8 GUA_RF_Communication_Judgment(GUA_U8 *npGUA_Receive)
{
  GUA_U8 nGUA_Sof = *npGUA_Receive;
  GUA_U8 nGUA_Len =  *(npGUA_Receive + 1);
  GUA_U8 nGUA_Func =  *(npGUA_Receive + 2);	
  GUA_U8 *npGUA_Data = npGUA_Receive + 3;
  GUA_U8 nGUA_Crc =  *(npGUA_Receive + 3 + nGUA_Len);
  GUA_U8 nGUA_Crc_Count = 0;	
	
  //判断起始位正确性
  if(nGUA_Sof != 0xFE)
  {
    return GUA_RF_COMMUNICATION_JUDGMENT_FALSE;  
  }		

  //接收数据长度
  if(nGUA_Len > 16)
  {
    return GUA_RF_COMMUNICATION_JUDGMENT_FALSE;  
  } 

  //计算校验和
  nGUA_Crc_Count += nGUA_Sof;    
  nGUA_Crc_Count += nGUA_Len;	
  nGUA_Crc_Count += nGUA_Func;
	
  while(nGUA_Len--)
  {
    nGUA_Crc_Count += *(npGUA_Data + nGUA_Len);
  }
	
  //比较校验和
  if(nGUA_Crc != nGUA_Crc_Count)  
  {
    return GUA_RF_COMMUNICATION_JUDGMENT_FALSE;  
  } 	

  //数据包正确
  return GUA_RF_COMMUNICATION_JUDGMENT_TRUE;
}

extern void GUA_SimpleGATTprofile_Char6_Notify(GUA_U16 nGUA_ConnHandle, GUA_U8 *npGUA_Value, GUA_U8 nGUA_Len);
//******************************************************************************                
//name:             GUA_RF_Communication_DataPackage_Send               
//introduce:        RF的通信数据打包并发送(包头+有效数据长度+功能码+有效数据+补齐字节)            
//parameter:        nGUA_Func: 功能码     
//                  npGUA_ValidData: 有效数据首地址
//                  nGUA_ValidData_Len: 要发送的数据长度
//return:           none            
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                      
//changetime:       2016.12.08                      
//****************************************************************************** 
void GUA_RF_Communication_DataPackage_Send(GUA_U16 nGUA_ConnHandle, GUA_U8 nGUA_Func, GUA_U8 *npGUA_ValidData, GUA_U8 nGUA_ValidData_Len)
{
  GUA_U8 nbGUA_DataPackage_Data[GUA_DATA_PACKAGE_LEN]; 
  GUA_U8 nGUA_Num;
  
  //初始化发送缓冲区
  memset(nbGUA_DataPackage_Data, 0xFF, 20);  
  
  //填充数据
  nbGUA_DataPackage_Data[0] = 0xFE;                                                     //包头
  nbGUA_DataPackage_Data[1] = nGUA_ValidData_Len;                                       //有效数据长度  
  nbGUA_DataPackage_Data[2] = nGUA_Func;                                                //功能码
  memcpy(nbGUA_DataPackage_Data + 3, npGUA_ValidData, nGUA_ValidData_Len);              //有效数据

  
  nbGUA_DataPackage_Data[3 + nGUA_ValidData_Len] = 0;                                   //校验和清零  
  for(nGUA_Num = 0; nGUA_Num < (3 + nGUA_ValidData_Len); nGUA_Num++)
  {
    nbGUA_DataPackage_Data[3 + nGUA_ValidData_Len] += nbGUA_DataPackage_Data[nGUA_Num]; //校验和累加   
  } 
  
  //发送数据    
  GUA_SimpleGATTprofile_Char6_Notify(nGUA_ConnHandle, nbGUA_DataPackage_Data, GUA_DATA_PACKAGE_LEN); 
}

//******************************************************************************                
//name:             GUA_RF_Communication_Process               
//introduce:        RF的通信数据处理            
//parameter:        npGUA_Receive: 接收缓冲区首地址     
//                  nGUA_Event: 要启动的事件
//return:           none            
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                      
//changetime:       2016.12.08                      
//****************************************************************************** 
void GUA_RF_Communication_Process(GUA_U8 *npGUA_Receive, GUA_U16 *npGUA_Event)
{
  GUA_U8 nGUA_Func =  *(npGUA_Receive + 2);

  //判断功能码
  switch(nGUA_Func)
  {	
    //led开关的功能码
    case FUNC_GUA_LED_ON_OFF:
    {
      *npGUA_Event = SBP_GUA_LED_ON_OFF_EVT;
      break;      
    }

    //功能码无效
    default:
    {
      *npGUA_Event = SBP_GUA_RF_COMMUNICAION_COMMAND_ERR_EVT;
      break;      
    }           
  }
}
注意本文件的事件要从应用层复制过来,保证事件是一样的。


2)写一个通信协议驱动的头文件 GUA_RF_Communication.h (存放在“……\BLE-CC254x-1.4.0\Projects\ble\SimpleBLEPeripheral\Source\GUA”路径下)

//******************************************************************************                            
//name:             GUA_RF_Communication.h              
//introduce:        香瓜的通信协议的头文件        
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                       
//changetime:       2016.12.08       
//****************************************************************************** 
#ifndef _GUA_RF_COMMUNICATION_H_
#define _GUA_RF_COMMUNICATION_H_

/*********************宏定义************************/ 
#ifndef GUA_U8    
typedef unsigned char GUA_U8;    
#endif    
  
#ifndef GUA_U16    
typedef unsigned short GUA_U16;    
#endif    
  
#ifndef GUA_U32    
typedef unsigned long GUA_U32;    
#endif    

#define GUA_RF_COMMUNICATION_JUDGMENT_FALSE     0
#define GUA_RF_COMMUNICATION_JUDGMENT_TRUE      1


/*********************外部函数声明************************/  
extern GUA_U8 GUA_RF_Communication_Judgment(GUA_U8 *npGUA_Receive);
extern void GUA_RF_Communication_DataPackage_Send(GUA_U16 nGUA_ConnHandle, 
                                                  GUA_U8 nGUA_Func, GUA_U8 *npGUA_ValidData, GUA_U8 nGUA_ValidData_Len);
extern void GUA_RF_Communication_Process(GUA_U8 *npGUA_Receive, unsigned short *npGUA_Event);

#endif

3)工程中添加GUA_RF_Communication.c



4)在IAR设置中添加按键驱动源文件路径

$PROJ_DIR$\..\..\SimpleBLEPeripheral\Source\GUA  

2、应用层中调用

1)添加通信协议驱动头文件(SimpleBLEPeripheral.c中)

#include "GUA_RF_Communication.h"

2)修改应用层的simple服务的回调函数(SimpleBLEPeripheral.c中)

//******************************************************************************                
//name:             simpleProfileChangeCB               
//introduce:        simple服务的回调函数
//parameter:        paramID: 特征值ID 
//return:           none             
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                      
//changetime:       2016.12.08                     
//******************************************************************************  
static void simpleProfileChangeCB( uint8 paramID )
{
  //通过特征值ID来分发特征值处理
  switch( paramID )
  {
    //char6的处理
    case SIMPLEPROFILE_CHAR6:  
    {
      //启动RF通信处理事件
      osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_GUA_RF_COMMUNICAION_PROCESS_EVT, 0 );        
      break;      
    }        
      
    default:break;
  }
}

3)添加“ 通信处理事件 、通信数据出错事件、led开关事件

①定义通信处理事件、通信数据出错事件、led开关事件(SimpleBLEPeripheral.c中)

 //通信处理事件
  if(events & SBP_GUA_RF_COMMUNICAION_PROCESS_EVT)
  {
    uint16 nGUA_Event;
    uint8 nGUA_Ret;    
    uint8 nbGUA_Char6[SIMPLEPROFILE_CHAR6_LEN] = {0};   
    
    //读取特征值6的数值
    SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR6, nbGUA_Char6);
        
    //判断接收到的RF值是否正确
    nGUA_Ret = GUA_RF_Communication_Judgment(nbGUA_Char6);                                 

    //数据正确,则执行对应事件
    if(nGUA_Ret == GUA_RF_COMMUNICATION_JUDGMENT_TRUE)
    {
      //根据功能码判断该执行什么事件
      GUA_RF_Communication_Process(nbGUA_Char6, &nGUA_Event);                               
		
      //定时器执行对应的功能事件
      osal_start_timerEx(simpleBLEPeripheral_TaskID, nGUA_Event, 0);                   
    }
    //数据不正确,则反馈报错    
    else
    {
      //定时器执行RF通信数据出错事件
      osal_start_timerEx(simpleBLEPeripheral_TaskID, SBP_GUA_RF_COMMUNICAION_COMMAND_ERR_EVT, 0);         
    }

    return (events ^ SBP_GUA_RF_COMMUNICAION_PROCESS_EVT);
  }

  //通信数据出错事件
  if (events & SBP_GUA_RF_COMMUNICAION_COMMAND_ERR_EVT)
  { 
    uint16 nGUA_ConnHandle;       
    uint8 nGUA_Func;    
    uint8 nbGUA_ValidData[16]; 
    uint8 nGUA_ValidData_Len;
    
    //获得连接句柄
    GAPRole_GetParameter(GAPROLE_CONNHANDLE, &nGUA_ConnHandle);

    //功能码填充  
    nGUA_Func = 0x80;       

    //有效数据填充
    //nbGUA_ValidData[0] = 0;
    
    //有效数据的长度
    nGUA_ValidData_Len = 0;

    //发送数据   
    GUA_RF_Communication_DataPackage_Send(nGUA_ConnHandle, nGUA_Func, nbGUA_ValidData, nGUA_ValidData_Len);    
    
    return (events ^ SBP_GUA_RF_COMMUNICAION_COMMAND_ERR_EVT);
  } 
    
  //功能码00 led开关事件
  if ( events & SBP_GUA_LED_ON_OFF_EVT )
  {
    uint16 nGUA_ConnHandle;       
    uint8 nGUA_Func;    
    uint8 nbGUA_ValidData[16]; 
    uint8 nGUA_ValidData_Len;
    uint8 nbGUA_Char6[SIMPLEPROFILE_CHAR6_LEN] = {0};  
    
    /*****************处理指令************************/
    //读出RF接收到的数据到缓冲区
    SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR6, nbGUA_Char6);

    //如果为0则关灯
    switch(nbGUA_Char6[3])
    {
      //关灯
      case 0x00:
      {
        P1_1 = 0;               //拉低P11     
        P1SEL &= ~(1 << 1);     //设置P11为IO口    
        P1DIR |= (1 << 1);      //设置P11为输出 
        
        break;
      }
 
      //开灯
      case 0x01:
      {
        P1_1 = 1;               //拉高P11     
        P1SEL &= ~(1 << 1);     //设置P11为IO口    
        P1DIR |= (1 << 1);      //设置P11为输出 
        
        break;
      }
      
      //其它
      default:break;
    }
    
    /*****************应答************************/
    //获得连接句柄
    GAPRole_GetParameter(GAPROLE_CONNHANDLE, &nGUA_ConnHandle);

    //功能码填充  
    nGUA_Func = 0x00;       

    //有效数据填充
    //nbGUA_ValidData[0] = 0;
   
    //有效数据的长度
    nGUA_ValidData_Len = 0;

    //发送数据   
    GUA_RF_Communication_DataPackage_Send(nGUA_ConnHandle, nGUA_Func, nbGUA_ValidData, nGUA_ValidData_Len);     

    return (events ^ SBP_GUA_LED_ON_OFF_EVT);
  } 

②定义“通信处理事件、通信数据出错事件、led开关事件”的宏 (替换SimpleBLEPeripheral.h中原来的事件宏)

// Simple BLE Peripheral Task Events
#define SBP_START_DEVICE_EVT                            0x0001
#define SBP_PERIODIC_EVT                                0x0002
#define SBP_GUA_RF_COMMUNICAION_PROCESS_EVT             0x0004  //通信处理事件
#define SBP_GUA_RF_COMMUNICAION_COMMAND_ERR_EVT         0x0008  //通信数据出错事件

#define SBP_GUA_LED_ON_OFF_EVT                          0x0040  //led开关事件

七、实验结果

仿真并全速运行,手机端使用truthblue连接并使用char6通道进行通信控制。

1、通信指令收发如下图


红框为开灯与开灯应答,蓝框为关灯与关灯应答,紫框为错误指令与报错应答。


2、开发板现象

1)发送关灯指令时



2)发送开灯指令时



实现了指令控制led的亮灭。

因此,实验成功。


  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值