基于CH579实现蓝牙(ble4.2)串口透传

2021-10-09 14:25:46 浏览数 (1)

点击上方[物联网思考],点击关注,第一时间查看物联网精彩分享!

1、整体框图

实现思路:CH579作为外部设备(Peripheral),串口接收(RX)来自外部mcu的数据,通过蓝牙(ble4.2)发送到中心设备(Center)(上图中是手机),通过蓝牙接收来自中心设备的数据,然后通过串口发送(TX)到外部mcu。

2、串口透传服务

ble是通过特征值传输数据的,因此串口透传服务至少需要两个特征值;一个用于发送数据,一个用于接收数据;为了提高数据吞吐量,发送和接收都不使用ack;因此CH579主动发送数据到手机,可以用notify,手机发送数据到CH579,可以用write no rsp的方式。

3、应用串口

这里使用串口3,用接收中断和时间超时的方法接收串口不定长数据。

代码语言:javascript复制
  UART3_ByteTrigCfg( UART_1BYTE_TRIG );
  UART3_INTCfg( ENABLE, RB_IER_RECV_RDY|RB_IER_LINE_STAT );
  NVIC_EnableIRQ( UART3_IRQn );
  NVIC_SetPriority(UART3_IRQn,5);

由于CH579串口设计上使用的是FIFO的形式,所以设置为1字节触发。

4、串口透传属性表

代码语言:javascript复制
gattAttribute_t uarttransAttrTb[]=
{
	//uart服务
	{
		{ ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
        GATT_PERMIT_READ,                         /* permissions */
        0,                                        /* handle */
        (uint8 *)&uarttransService                /* pValue */
	},
	//rx特征
	{
		{ATT_BT_UUID_SIZE,characterUUID},
		GATT_PERMIT_READ,
		0,
		&uartrxProps
	},
	//rx特征值
	{
		{ATT_BT_UUID_SIZE,uartrxUUID},
		GATT_PERMIT_WRITE,
		0,
		&uartrxchar
	},
	//tx特征
	{
		{ATT_BT_UUID_SIZE,characterUUID},
		GATT_PERMIT_READ,
		0,
		&uarttxProps
	},
	//tx特征值
	{  
		{ATT_BT_UUID_SIZE,uarttxUUID},
		0,
		0,
		&uarttxchar
	},
	//cccd
	{  
		{ATT_BT_UUID_SIZE,clientCharCfgUUID},
		GATT_PERMIT_READ|GATT_PERMIT_WRITE,
		0,
		(uint8 *)uarttxcharConfig	
	},
};

可以看出属性表里面,包含6条属性,声明了一个服务、一个tx特征、tx特征值、rx特征、rx特征值、以及一个客户特征配置(用于客户端配置是否接收服务器的notify)。

5、RX特征实现

属性写回调函数,将蓝牙RX的特征值传到应用层。

代码语言:javascript复制
uint8 uarttrans_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                           uint8 *pValue, uint16 len, uint16 offset,
                                           uint8 method )
{
	uint8 status = SUCCESS;
	
	if(pAttr->type.len==ATT_BT_UUID_SIZE)
	{
		uint16 uuid= BUILD_UINT16(pAttr->type.uuid[0],pAttr->type.uuid[1]);
		
		switch(uuid)
		{
			case GATT_CLIENT_CHAR_CFG_UUID:
				status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,offset,GATT_CLIENT_CFG_NOTIFY);
					
				break;
			case UART_RX_CHAR_UUID:			
				if(UartTransChangeCBs)
				{
					UartTransChangeCBs(0,pValue,len);
					//将蓝牙接收到的数据,传向应用层,应用层再通过串口3发送到外部mcu
				}
				break;
			default:
			  status=ATT_ERR_ATTR_NOT_FOUND;
				break;
		}
	}
	else
	{
		status= ATT_ERR_INVALID_HANDLE;
	}
	
	return status;
}

6、TX特征实现

CH579接收到外部mcu的串口数据后,通过notify的方式发送出去。

代码语言:javascript复制
#define SBP_DATA_READY_EVT                     0x0008
#define SBP_DATA_TX_EVT                        0x0010
#define SBP_RX_TIMEOUT_EVT                     0x0020

定义了三个事件,SBP_DATA_READY_EVT用于检测串口接收数据是否就绪,SBP_RX_TIMEOUT_EVT用于串口接收超时,SBP_DATA_TX_EVT用于蓝牙发送。

代码语言:javascript复制
static void PollingTx(void)
{
	uint8 result=0xff;	
	attHandleValueNoti_t noti;
	uint16 i=0;
	static uint8 retry=0;
	
	if(AppTxCount>=0)
	{
		uint8 mtulen = ATT_GetMTU(peripheralConnList.connHandle)-3;
	//获取当前MTU的大小,实际能发送的数据长度,还要减去notify的数据头
		if(AppTxCount>mtulen)
		{
			noti.len=mtulen;

			noti.pValue=GATT_bm_alloc(peripheralConnList.connHandle,ATT_HANDLE_VALUE_NOTI,noti.len,NULL,0);
			tmos_memcpy(noti.pValue,AppTxData,noti.len);
			
			result=UartTrans_Notify(peripheralConnList.connHandle, noti);
			
			if(result != SUCCESS)
			{
				GATT_bm_free( (gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI );
				
				retry  ;
				if(retry>=3)
				{
					retry=0;
					AppTxCount=0;
					tmos_stop_task(Peripheral_TaskID,SBP_DATA_TX_EVT);
					tmos_start_task(Peripheral_TaskID,SBP_DATA_READY_EVT,SBP_DATA_READY_EVT_PERIOD);
										
					PRINT("Send fail...rn");

					return;
				}
			}
			else
			{
				retry=0;
				
				AppTxCount=AppTxCount-mtulen;
				
			#if 1	
				for( i=0;i<AppTxCount;i  )
				{
					AppTxData[i]=AppTxData[i mtulen];
				}
			#else	
				tmos_memcpy(AppTxData,&AppTxData[mtulen],AppTxCount);
			#endif
			}
							
			tmos_start_task(Peripheral_TaskID,SBP_DATA_TX_EVT,SBP_DATA_TX_EVT_PERIOD);
		}
		else
		{
			noti.len=AppTxCount;

			noti.pValue=GATT_bm_alloc(peripheralConnList.connHandle,ATT_HANDLE_VALUE_NOTI,noti.len,NULL,0);
			tmos_memcpy(noti.pValue,AppTxData,noti.len);
			
			result=UartTrans_Notify(peripheralConnList.connHandle, noti);
						
			if(result!=SUCCESS)
			{
				GATT_bm_free( (gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI );
				
				retry  ;
				if(retry>=3)
				{
					retry=0;
					AppTxCount=0;
					tmos_stop_task(Peripheral_TaskID,SBP_DATA_TX_EVT);
					tmos_start_task(Peripheral_TaskID,SBP_DATA_READY_EVT,SBP_DATA_READY_EVT_PERIOD);
					
					PRINT("Send fail...rn");
					
					return;
				}
				else
				{
					tmos_start_task(Peripheral_TaskID,SBP_DATA_TX_EVT,SBP_DATA_TX_EVT_PERIOD);
				}
			}
			else
			{
				retry=0;
				AppTxCount=0;
				
				tmos_stop_task(Peripheral_TaskID,SBP_DATA_TX_EVT);
				tmos_start_task(Peripheral_TaskID,SBP_DATA_READY_EVT,SBP_DATA_READY_EVT_PERIOD);
			}
		}
	}	
}

这里要注意,当前MTU的大小,如果需要发送的数据大于MTU的大小,则需要分包发送;还加入了重发,如果3次都失败,则打印“Send fail…”。

代码语言:javascript复制
uint8 UartTrans_Notify(uint16 connhandle,attHandleValueNoti_t noti)
{	
	uint16 value=GATTServApp_ReadCharCfg(connhandle,uarttxcharConfig);
//获取CCCD配置
	if(value & GATT_CLIENT_CFG_NOTIFY)
	{
		noti.handle=uarttransAttrTb[4].handle;
		
		return GATT_Notification(connhandle,&noti,FALSE);
	}
	return bleIncorrectMode;
}

notify发送,需要先获取客户端是否使能了发送,如果使能了则发送,否则返回错误。

7、主函数

上述增加的事件都是基于TMOS的,因此只需要在主函数在初始化应用串口即可,

代码语言:javascript复制
int main( void ) 
{
#if (defined (HAL_SLEEP)) && (HAL_SLEEP == TRUE)
  GPIOA_ModeCfg( GPIO_Pin_All, GPIO_ModeIN_PU );
  GPIOB_ModeCfg( GPIO_Pin_All, GPIO_ModeIN_PU );
#endif
	
#ifdef DEBUG
  GPIOA_SetBits(bTXD1);
  GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);
  UART1_DefInit( );
#endif   
	
  PRINT("%sn",VER_LIB);
  CH57X_BLEInit( );
  HAL_Init( );
  GAPRole_PeripheralInit( );
		
  AppUartInit();//应用串口
  Peripheral_Init( ); 
	while(1){
		TMOS_SystemProcess( );//TMOS运行
	}
}

8、运行测试

8.1、使用ble调试助手连接,如下:

可以看到自定义的串口透传服务,在串口透传服务下有两个特征,一个支持Write No Response(写),一个支持Notify(通知)。

8.2、接收测试

点击Write No Response右边的箭头,手机发送数据到CH579,CH5789通过串口打印出来,如下:

这里为了方便测试,在手机端周期发送,可以看出CH579也周期性的接收到了数据。

8.3发送测试

CH579发送数据到手机,如下:

篇幅限制,文中只列出了部分代码,如需完整工程,后台联系。

——————END——————

相关推荐:

专辑->蓝牙BLE4.2

专辑->玩转ESP32

专辑->从0到1搭建LoRa物联网

专辑->mcu系列

0 人点赞