8位MCU串口与电池包通讯

一.前言

   串口,相信大家已经很熟悉这个名字了。作为嵌入式最常用的通讯手段,无疑是许多人对于MCU通信梦开始的地方。先前我有介绍过一个关于485的文章485、CAN通讯、LCD段码屏 电动车仪表测试盒_整车通信485-CSDN博客,它完全就是基于串口的通信方式,只是电平标准不一样。一位刚毕业的小亲戚今天问我串口怎么用,乘此机会在此结合我做过的商业性案例一起详细的介绍一下。

二.串口介绍

       串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
      单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
       便于对底层的理解,我们只介绍8位的MCU自带的串口(UART)。(Universal Asynchronous Receiver Transmitter,通用异步收发器)。关于UART和USART的区别及介绍,大佬的UART与USART区别 (串口同步通信和异步通信)_uart同步和异步的区别-CSDN博客介绍的很详细,这里不做过多描述。

三.硬件电路

 常见的标准TTl :高5V 低0V     RS232: -3~~15V表示高  3~15V表示低    RS485:压差2~6V高  压差-2~-6V表示低(485是差分信号,抗干扰能力强)。

 

四.参数介绍

  1. 波特率(Baud Rate)
    • 定义:波特率是衡量数据传输速率的一个重要指标,它表示单位时间内传输的码元个数。码元是承载信息量的基本信号单位。例如,在二进制通信系统中,一个码元就代表一个二进制位(0 或 1)。波特率的单位是波特(Bd),1 波特意味着每秒传输 1 个码元。
    • 影响因素:较高的波特率可以实现更快的数据传输速度,但对通信线路的质量和设备的性能要求也更高。如果波特率过高,而通信线路存在干扰或者接收设备处理速度跟不上,就可能会导致数据丢失或错误。例如,在一个干扰较大的长距离通信线路中,使用过高的波特率可能会使接收端无法正确解析接收到的信号。
    • 常见值:常见的波特率有 9600、19200、38400、115200 等。在一些对数据传输速度要求不高,且通信环境较为复杂的情况下,如简单的传感器数据采集,9600 波特率可能就足够了。而在需要快速传输大量数据的场景,如计算机与高速外部设备之间的通信,可能会使用 115200 波特率甚至更高。
  2. 数据位(Data Bits)
    • 定义:数据位用于指定每个字节的数据长度。在串口通信中,字节是数据的基本单位。数据位可以设置为 7 位或 8 位。
    • 应用场景:7 位数据位主要用于 ASCII 码通信。ASCII 码是一种字符编码标准,它用 7 位二进制数来表示 128 个字符,包括英文字母、数字、标点符号等。例如,在早期的电传打字机通信中,就广泛使用 7 位 ASCII 码,因为这样可以有效地传输文本信息。而 8 位数据位则可以传输一个完整的字节,它可以用于传输任意二进制数据,包括自定义的协议数据或者非 ASCII 码字符集的数据。
  3. 停止位(Stop Bits)
    • 定义:停止位用于表示一个数据字节传输结束。它是在每个字节的数据位传输完成后发送的,可以是 1 位、1.5 位或 2 位。
    • 作用:停止位的主要作用是让接收设备能够正确识别每个字节的结束位置。在异步串口通信中,接收设备需要通过识别停止位来确定一个字节的数据已经完整接收,从而准备接收下一个字节。例如,当发送端发送一个字节的数据位后,接着发送停止位,接收端会等待停止位接收完成后,才会开始处理这个字节的数据,并准备接收下一个字节的起始位。
  4. 奇偶校验位(Parity Bit)
    • 定义:奇偶校验位是一种简单的错误检测机制。它是在数据位之后添加的一位,用于检查数据在传输过程中是否发生错误。
    • 类型:可以设置为奇校验、偶校验或无校验。在奇校验中,数据位和校验位中 “1” 的个数为奇数。例如,发送的数据位是 0110010,为了使 “1” 的个数为奇数,校验位会被设置为 0,这样数据位和校验位一起(01100100)中 “1” 的个数为奇数。在偶校验中,数据位和校验位中 “1” 的个数为偶数。比如,对于数据位 1010101,校验位会被设置为 1,使得(10101011)中 “1” 的个数为偶数。当接收方收到数据后,会根据设置的奇偶校验方式来检查数据的正确性。如果接收端计算出的数据位和校验位中 “1” 的个数不符合设定的奇偶校验规则,就说明数据在传输过程中可能出现了错误。不过,奇偶校验只能检测出奇数个错误位,对于偶数个错误位可能无法检测出来。

五.案例介绍

      串口还是蛮好理解的,用户只需要配置好基础参数,他就可以按照你给的数据进行收发,话不多说,我们直接看实例。

     由于特殊原因,客户原本使用AD对电池包剩余电量进行采集的方式对结果会有很大的误差。现在采用串口直接和电池包进行通讯,根据电池包返回的SOC值对电量进行校准。

要求:

下面看代码部分

void UART_Init()
{
  AFP1|=0B00010100; //复用管脚选择
  PCKEN|=0B00100000;//使能USART模块时钟
  URLCR|=0B00000001;//8位数据长度,1位停止位,无奇偶校验位
  
  URDLL=104; 
  URDLH=0;     //波特率=Fmaster/(16*{URDLH,URDLL})=9600
  URMCR|=0B00011000;//串口发送,接收使能
  URIER|=0B00100001;  //发送完成,接收buf非空中断 
  
  TCF=1;
  INTCON|=0B10000000; //开全局中断
  INTCON|=0B01000000; //开外设总中断
}

假如你是一个设备,什么时候发送数据完全取决你自己什么时候想发,所以对于发送我们不需要放在中断中去占用资源。相反,我们并不能确定对方什么时候给我们发送东西,假如我们一直在等,就会阻塞单片机的程序,所以接收我们是用中断接收。

void Uart_Send_Byte(unsigned char byte)//串口发送字符
{
  URDATAL= byte;
  while(TCF==0);
  TCF=1;
}

void Uart_Send_Array(unsigned char *str)//发送数据组
{
  SendCnt=0;
  while(SendCnt<5)//我的待发送数据固定长为5个Byte
  {
    Uart_Send_Byte(*str);
    str++;
    SendCnt++;   
  }
}

void Uart_Receive()
{
   if(Uartend==0)
   {
     if(Uart_Head_Flag==0)//判断头帧
     {
        SendBuf3[UartCnt]=URDATAL;
        if(((UartCnt==0)  &&  (SendBuf3[UartCnt]==0xAA)) || ((UartCnt==1)  &&  (SendBuf3[UartCnt]==0x21)) ||((UartCnt==2)  &&  (SendBuf3[UartCnt]==0x18)))
        {
           UartCnt++;
        }
        else
           UartCnt=0;
           
        if(UartCnt==3)
        {
          Uart_Head_Flag=1;
          BMS_Check_Value=0;//
        }
     }
     else
     {                 
        SendBuf3[UartCnt]=URDATAL;
        UartCnt++;         
        if(UartCnt==31)//接收完全部数据
        {
          UartCnt=0;
          Uart_Head_Flag=0;
          Uartend=1;
        }
     }
   }   
   else
   {
     UartCnt=0;
   }
}

void Uart_Check_OK()//校验方式
{
  unsigned char BMS_CheckH=0;
  unsigned char BMS_CheckL=0;
  unsigned char SUM=0;

  if(Uartend)//接收完整数据帧
  {

     for(SUM=1;SUM<28;SUM++)
     {
       BMS_Check_Value=BMS_Check_Value+SendBuf3[SUM];
     }
     BMS_CheckL =BMS_Check_Value%256;
     BMS_CheckH =BMS_Check_Value/256;
     if(BMS_CheckL==SendBuf3[29] && BMS_CheckH==SendBuf3[30])
     {
			Soc_Value=SendBuf3[11];
			Uartend=0;
     }
  }
}

   以上就是发送以及接收数据的处理了,这样就可以根据接收的报文进行SOC数值的提取了。

    这边再强掉一下当开启串口接收中断时,硬件接收Buf就会将接收数据存下,同时将RXNEF置1,读接收Buf里数据时自动清零。

       然后给大家补充一个点,就是有可能单片机是5V的,但是电池BMS板那端的信号是3.3V高,这就大概率会通信不成功,导致MCU可以发送数据给BMS板,但是却接收不了信号,需要在RX端口增加电路。

圈起的电容是考虑到实车上环境对信号的影响。

     近期再给台铃做仪表时,他有个需求是需要转意,代码我放在这边做个记录,有需要的可以直接用

		if(ECU_Meter_Inquiry_F)//信息回复帧发送数组
		{
			ECU_Meter_Inquiry_F=0;				
			//数据转意处理部分
			Buf3_Length=10;//原先数组长度
			for(q=2;q<=8;q++)
			{
				if((Uart_Send_Buf[q]==0xBB) || (Uart_Send_Buf[q]==0xAA) || (Uart_Send_Buf[q]==0x55) )//0xBB 0xAA 0X55 需要转意
				{		
					
					Uart_Send_Buf3[q+(Buf3_Length-10)]=0xBB;//0xBB替代原先数据					
				  Buf3_Length++;发送数组长度++
					for(j=q+(Buf3_Length-10);j<=Buf3_Length-1;j++)//每一位往后挪
					{
					  Uart_Send_Buf3[j]=Uart_Send_Buf[q];
					}					
				}
				else
				{
					Uart_Send_Buf3[q+(Buf3_Length-10)]=Uart_Send_Buf[q];										
				}							
			}
			Uart_Send_Buf3[0]=0xAA;
			Uart_Send_Buf3[1]=0xAA;
			Uart_Send_Buf3[Buf3_Length-1]=0x55;//填充包头包尾
      Uart_Send_Array(Uart_Send_Buf3,Buf3_Length);//发送转意后的数组
		}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值