前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于CH579实现蓝牙(ble4.2)串口透传

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

作者头像
ManInRoad
发布2021-10-09 14:25:46
2.4K2
发布2021-10-09 14:25:46
举报
文章被收录于专栏:物联网思考物联网思考

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

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...\r\n");

					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...\r\n");
					
					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("%s\n",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系列

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 物联网思考 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 点击上方[物联网思考],点击关注,第一时间查看物联网精彩分享!
  • 1、整体框图
  • 2、串口透传服务
  • 3、应用串口
  • 4、串口透传属性表
  • 5、RX特征实现
  • 6、TX特征实现
  • 7、主函数
  • 8、运行测试
    • 8.1、使用ble调试助手连接,如下:
      • 8.2、接收测试
        • 8.3发送测试
        相关产品与服务
        物联网
        腾讯连连是腾讯云物联网全新商业品牌,它涵盖一站式物联网平台 IoT Explorer,连连官方微信小程序和配套的小程序 SDK、插件和开源 App,并整合腾讯云内优势产品能力,如大数据、音视频、AI等。同时,它打通腾讯系 C 端内容资源,如QQ音乐、微信支付、微保、微众银行、医疗健康等生态应用入口。提供覆盖“云-管-边-端”的物联网基础设施,面向“消费物联”和 “产业物联”两大赛道提供全方位的物联网产品和解决方案,助力企业高效实现数字化转型。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档