低压电量采集平台DW710C与PC的通信

基于MFC和STR711的串口通信与电表数据采集
本文详细介绍了如何使用MFC编写上位机串口通信软件与运行在采集器中的STR711程序实现电表数据的实时采集。包括上位机软件的设置、发送与接收数据流程,以及采集器端程序的初始化、与PC通信的设置和数据交互逻辑。

采集器的一个485接口与RS-485与RS-232转换模块的485端相连,RS-485与RS-232转换模块的232端通过串口线与PC的232串口相连,我们通过编写上位机串口通信软件和运行在采集器中的程序实现二者的通信。

1)上位机软件:

采用MFC,主要利用串口函数SetCommState、WriteFile和ReadFile进行串口设置、发送和接收。具体程序如下:

void CComplDlg::OnReseved() 
{
	DWORD length=0;
	unsigned char Com_Recv_Buf[256];

	ReadFile(hCom,Com_Recv_Buf,20,&length,NULL);    //读取串口内容

	m_sReseved = 150;    //电量
    //电表地址,十六进制
	m_sAddr1.Format("0x%02x", Com_Recv_Buf[4]);
    m_sAddr2.Format("0x%02x", Com_Recv_Buf[5]); 
	m_sAddr3.Format("0x%02x", Com_Recv_Buf[6]);
	m_sAddr4.Format("0x%02x", Com_Recv_Buf[7]); 
	m_sAddr5.Format("0x%02x", Com_Recv_Buf[8]);
	m_sAddr6.Format("0x%02x", Com_Recv_Buf[9]); 

	UpdateData(false);
	SetCommMask(hCom,EV_TXEMPTY);
	UpdateData(false);
}

void CComplDlg::OnSend() 
{
	UpdateData(true); 
	
	DWORD length=0;
	unsigned char Com_Send_Buf[10];
	Com_Send_Buf[0]=m_sSend;
	if(WriteFile(hCom,Com_Send_Buf,1,&length,NULL))
	{
		m_cReseved.EnableWindow(true);
    }
    else
	{
		MessageBox(TEXT("数据发送失败!请重试!"),TEXT("提示"),MB_OK);
	}
}


void CComplDlg::OnSetupcom() 
{
	UpdateData(true); 
	
	SetupComm(hCom,1024,1024);
	COMMTIMEOUTS Timeouts;
	//DCB dcb
	GetCommState(hCom,&dcb);
	dcb.BaudRate=38400;//m_nSetupbt;
	dcb.ByteSize=8;
	dcb.StopBits=ONESTOPBIT;
	dcb.Parity=NOPARITY;
	SetCommState(hCom,&dcb);
	UpdateData(false);
	m_cSend.EnableWindow(true);
	m_cSend.SetFocus();
	m_cSetupcomm.EnableWindow(false);
	m_cEditsend.SetFocus();
}

2)采集器端程序:
void main(void)
{
	Str711_Init();	//对主芯片STR711进行初始化
    
	/*设置初始的与PC通信的波特率*/
	Base_ParaMeter.Baud_to_Pc = BAUD_UART_PC_ORDER_38400;
    /*因为更改了初始的与PC通信的波特率所以再将数据重新写回到SPI_Flash中去*/
	SPI_Write_161d(BASE_ADDR_BASE_PARA,(u8*)&Base_ParaMeter,sizeof(Base_ParaMeter));	
    /*配置与PC通信的UART*/
	UART_Config(UART_PC, BAUD_UART_PC_38400, UART_EVEN_PARITY, UART_1_StopBits, UARTM_8D_P);

    //主循环
	while(1)
	{
		WDG_CntRefresh();				//刷新看门狗的计数器值
		//*****************************************************************************/
						/*发送一个数据包过去,请求读电量*/
                       /*  if(Global_Task_Flag &TASK_FLAG_BEGIN_LUNXUN)
                          {
                              Global_Task_Flag &=(~TASK_FLAG_BEGIN_LUNXUN);
                              if((Global_Task_Flag&TASK_FLAG_LUNXUN_ING)==0)
                              {    
                                      WDG_CntRefresh();//刷新看门狗的计数器值
                                      LunXun_Start();  //开始轮询
                                      
                               }			
                          }*/
        //*****************************************************************************/
		if(Global_Task_Flag&TASK_FLAG_RX_PC_BIT_OK)
		{//串口PC的有效数据帧被收到, 执行上位机的相关命令
			Global_Task_Flag &= (~TASK_FLAG_RX_PC_BIT_OK);
			WDG_CntRefresh();				//刷新看门狗的计数器值
			
            Send_Ack_Or_Data_To_Host_Uart_PC();
		} 
        
		if(Global_Task_Flag&TASK_FLAG_25MS_TASK)
		{//25MS执行一次的任务
			Global_Task_Flag &=(~TASK_FLAG_25MS_TASK);
			WDG_CntRefresh();				//刷新看门狗的计数器值
			Task_2();
		} 
        WDG_CntRefresh();   //刷新看门狗的计数器值
		Task_3();	
	}
}

/*********************************************************************************************
*任务函数:  Task2()
*功能:      25ms 执行一次的任务
*********************************************************************************************/
void 	Task_2()					
{
	WDG_CntRefresh();		//刷新看门狗的计数器值 
 	Parse_Com_Data(3);      //串口PC
}
/*********************************************************************************************
*任务函数:  Task3()
*功能:	    判断串口数据的发送是否完成,如果完成将状态转换为接收状态
*********************************************************************************************/
void	Task_3()
{
	//判断串口PC的发送是否完成,如果完成,则将状态转换为接收状态
	if(Com_Task_Flag& TASK_FLAG_COM_PC_SEND_COMPLETE)
	{
		Com_Task_Flag &= (~TASK_FLAG_COM_PC_SEND_COMPLETE);
		Com_PC_Send_Total_Len		=0;
		Com_PC_Send_Pos			=0;	
	}
}

/*********************************************************************************************
*函数名称:  Send_Ack_Or_Data_To_Host_Uart_PC()
*功能描述:  对收到串口PC 的上位机命令进行应答
*********************************************************************************************/
void Send_Ack_Or_Data_To_Host_Uart_PC()
{
	u32 i;
	u8 ch;
    
	/*自定义返回格式与内容,参照DLT 645-1997多功能电能表通信规约的通信协议*/
    for(int index=0; index<10; index++)
        Com_PC_Send_Buf[index] = index;
        
	//向上位机发送应答数据帧
    Com_PC_Send_Data_Len = 3;
	Com_PC_Send_Check_Sum = 0;
	
	//对数据域加 0x33
	for(i=0;i<Com_PC_Send_Data_Len;i++)
	{
		Com_PC_Send_Buf[10+i] +=0X33;
	}
	for(i=0;i<10+Com_PC_Send_Data_Len;i++)
	{
		Com_PC_Send_Check_Sum += Com_PC_Send_Buf[i];
	}
	Com_PC_Send_Buf[i]= Com_PC_Send_Check_Sum;
	Com_PC_Send_Buf[i+1] = 0x16;
	Com_PC_Send_Buf[i+2] = 0x16;
	Com_PC_Send_Buf[i+3] = 0x16;
	
	Com_PC_FE_Number =0;
	
	//关闭接收中断,取得发送的总长度
	Com_PC_Send_Total_Len = Com_PC_Send_Data_Len +14;	//包括校验和以及0X16
	Com_PC_Send_Pos =0;
	ch = 0XFE;
	PC_TX_ENABLE;	//PC发送使能	
	
 	UART_ByteSend(UART_PC,&ch );
 	UART_ByteSend(UART_PC,&ch );
 	UART_ByteSend(UART_PC,&ch );
 	UART_ByteSend(UART_PC,&ch );

	do
	{
		if(Com_PC_Send_Pos<Com_PC_Send_Total_Len)
		{
		 	UART_ByteSend(UART_PC,&Com_PC_Send_Buf[Com_PC_Send_Pos]);
			Com_PC_Send_Pos++;
		}
		else
		{
			break;
		}
	}while (!(UART_FlagStatus(UART_PC) & UART_TxFull));
	
	Com_Task_Flag  |= TASK_FLAG_COM_PC_SEND_TIME;
 	UART_ItConfig(UART_PC,UART_TxEmpty|UART_TxHalfEmpty,ENABLE);		//发送中断使能
 	UART_ItConfig(UART_PC,UART_RxHalfFull|UART_TimeOutNotEmpty,DISABLE);		//接收中断禁止	
		
}

/***************************************************************************************************
* FunctionName : Parse_Com_Data
* Description  : 解析COM口是否有一个完整的数据帧收到                  
* Parameter(s) :                 
*                Com_Number : 是哪一个COM口 2为下行的485口有;3为上行的PC口
*
* Return       : void
***************************************************************************************************/
void	Parse_Com_Data(u8 Com_Number)
{
	u8* Com_Recv_Buf;			/*指向串口接收缓冲区的指针*/
	u8	Com_Data_Len;	        /*记录数据帧的数据域长度*/ 
	u8*	Com_Recv_Buf_Ptr_W;		/*串口接收缓冲区的写指针*/
	u8*	Com_Recv_Buf_Ptr_R;		/*串口接收缓冲区的读指针*/
	u16 COM_RECV_BUF_SIZE;      /*接收缓冲区的大小*/
	u32	TASK_FLAG_COM_RX_OK;    /*接收到一个完整的帧的标志位*/
	u8*	Com_Process_Buf;        /*如果接收的帧完整则将这一帧数据转存到这个处理缓冲区中为后面处理做准备*/
	u16 i=0;
    
	switch(Com_Number)
	{							
		case	3:
			Com_Recv_Buf 					  = Com_PC_Recv_Buf;
			Com_Recv_Buf_Ptr_W 				  = &Com_PC_Recv_Buf_Ptr_W;
			Com_Recv_Buf_Ptr_R 				  = &Com_PC_Recv_Buf_Ptr_R;
			Com_Process_Buf   				  = Com_PC_Process_Buf;
			COM_RECV_BUF_SIZE 				  = COM_RECV_BUF_SIZE_HW_PC;
			TASK_FLAG_COM_RX_OK 			  = TASK_FLAG_RX_PC_BIT_OK;
			break;
		default:
			return;
	}
	
	/*若发过来的数据是0x99,则视为可以通信,进行应答*/
    if(Com_Recv_Buf[0] == 0x99)
    {
		Com_Process_Buf[0]=Com_Recv_Buf[0];
		//清除缓冲区中所有的数据. 
		memset(Com_Recv_Buf,0,COM_RECV_BUF_SIZE);
		//读写指针清零也可以. 暂时先不清零吧
		*Com_Recv_Buf_Ptr_R = 0;
		*Com_Recv_Buf_Ptr_W = 0;

		Global_Task_Flag |= TASK_FLAG_COM_RX_OK;
    }	//设置收到串口1有效数据帧标志
        
	return;


}

在采集器端25ms进行一次串口数据读取,通过判断接收到的数据是否为0x99,决定是否进行应答。通过UART_ByteSend函数进行发送应答信息。

减少库的使用,解决那些需要小代码,但苦恼于没有简易的字符串处理函数的郁闷 char *itoa_private(int val, char *buf, unsigned radix);//整数转字符串 int my_isdigit(int ch);//判断字符是否为数字 long long StrToInt(const char *s,int sign);//字符串转数字 int atoi_32(const char *s);//将字符串str转换为32位整型,其正数的最值为0x7FFFFFFF,负数的最小值为0x80000000,考虑数字会溢出的情况 void LettersChange(unsigned char*str, unsigned char font);//大小写字母变成大小写字母 signed int st_strlen(unsigned char* str);//获取字符串长度 signed int st_strcmp(unsigned char *s,unsigned char *t);//比较两个字符串大小 unsigned char *st_strcpy(unsigned char *t,unsigned char *s);//字符串拷贝 signed int st_strncmp(unsigned char *dest,unsigned char *src,unsigned int Len);//可变长度字符串大小比较 signed char st_toupper(unsigned char c);//小写字符转化为大写字符 unsigned char *st_strcat(unsigned char *s,unsigned char *t);//字符串连接 signed int st_memcpy(unsigned char *dest, unsigned char *src, unsigned int Len);//可变长度内存拷贝 signed int st_memset(unsigned char *dest,unsigned char byte, unsigned int Len);//设置长度为Len的内存块为byte signed int st_memcmp(unsigned char* dest, unsigned char* src, unsigned int Len);//内存段内数据比较 int gsmInvertNumbers(const char* pSrc, char* pDst, int nSrcLength);//正常顺序的字符串转换为两两颠倒的字符串,若长度为奇数,补'F'凑成偶数 int gsmSerializeNumbers(const char* pSrc, char* pDst, int nSrcLength);//两两颠倒的字符串转换为正常顺序的字符串 int gsmEncode7bit(const char* pSrc, unsigned char* pDst, int nSrcLength);//7bit编码 int gsmDecode7bit(const unsigned char* pSrc, char* pDst, int nSrcLength);//7bit解码 int gsmString2Bytes(const char* pSrc, unsigned char* pDst, int nSrcLength);//可打印字符串转换为字节数据 int gsmBytes2String(const unsigned char* pSrc, char* pDst, int nSrcLength);//字节数据转换为可打印字符串
内容概要:本文介绍了一个基于多传感器融合的定位系统设计方案,采用GPS、里程计和电子罗盘作为定位传感器,利用扩展卡尔曼滤波(EKF)算法对多源传感器数据行融合处理,最终输出目标的滤波后位置信息,并提供了完整的Matlab代码实现。该方法有效提升了定位精度稳定性,尤其适用于存在单一传感器误差或信号丢失的复杂环境,如自动驾驶、移动采用GPS、里程计和电子罗盘作为定位传感器,EKF作为多传感器的融合算法,最终输出目标的滤波位置(Matlab代码实现)机器人导航等领域。文中详细阐述了各传感器的数据建模方式、状态转移观测方程构建,以及EKF算法的具体实现步骤,具有较强的工程实践价值。; 适合人群:具备一定Matlab编程基础,熟悉传感器原理和滤波算法的高校研究生、科研人员及从事自动驾驶、机器人导航等相关领域的工程技术人员。; 使用场景及目标:①学习和掌握多传感器融合的基本理论实现方法;②应用于移动机器人、无人车、无人机等系统的高精度定位导航开发;③作为EKF算法在实际工程中应用的教学案例或项目参考; 阅读建议:建议读者结合Matlab代码逐行理解算法实现过程,重点关注状态预测观测更新模块的设计逻辑,可尝试引入真实传感器数据或仿真噪声环境以验证算法鲁棒性,并一步拓展至UKF、PF等更高级滤波算法的研究对比。
内容概要:文章围绕智能汽车新一代传感器的发展趋势,重点阐述了BEV(鸟瞰图视角)端到端感知融合架构如何成为智能驾驶感知系统的新范式。传统后融合前融合方案因信息丢失或算力需求过高难以满足高阶智驾需求,而基于Transformer的BEV融合方案通过统一坐标系下的多源传感器特征融合,在保证感知精度的同时兼顾算力可行性,显著提升复杂场景下的鲁棒性系统可靠性。此外,文章指出BEV模型落地面临大算力依赖高数据成本的挑战,提出“数据采集-模型训练-算法迭代-数据反哺”的高效数据闭环体系,通过自动化标注长尾数据反馈实现算法持续化,降低对人工标注的依赖,提升数据利用效率。典型企业案例一步验证了该路径的技术可行性经济价值。; 适合人群:从事汽车电子、智能驾驶感知算法研发的工程师,以及关注自动驾驶技术趋势的产品经理和技术管理者;具备一定自动驾驶基础知识,希望深入了解BEV架构数据闭环机制的专业人士。; 使用场景及目标:①理解BEV+Transformer为何成为当前感知融合的主流技术路线;②掌握数据闭环在BEV模型迭代中的关键作用及其工程实现逻辑;③为智能驾驶系统架构设计、传感器选型算法优化提供决策参考; 阅读建议:本文侧重技术趋势分析系统级思考,建议结合实际项目背景阅读,重点关注BEV融合逻辑数据闭环构建方法,并可延伸研究相关企业在舱泊一体等场景的应用实践。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值