背景

  • 年前一个项目中,由于串口的资源紧张,硬件使用的是主MCU-STM32F030C8T6,其两个硬件串口都被使用了,而另外一个器件间的通信也需要串口通信,故不得不采用模拟串口的方式,该器件的通信要求是:4800bps ,8,E,1
    对,是采用偶校验的方式。之前一直习惯无校验方式,一下子说要采用偶校验,所以不得不深度查阅和了解串口的相关信息。

  • 以下即是所了解的 校验位信息的解读说明:
  • 设置为奇校验,先看发送方将要发送的一帧数据中有几个1,如果是偶数个1则校验位置1,保证1的个数是奇数。如果是奇数就置0。
  • 设置为偶校验,先看发送方将要发送的一帧数据中有几个1,如果是奇数个1则校验位置1,如果是偶数就置0。保证1的个数是偶数。
  • 若当前校验位电平为0且校验接收"1"的个数为偶数个,则通过偶校验(即满足1的个数为偶数个)
  • 若当前校验位电平为1且校验接收"1"的个数为奇数个,则通过偶校验
  • 若当前校验位电平为1且校验接收"1"的个数为偶数个,则通过奇校验(即满足1的个数为奇数个)
    若当前校验位电平为0且校验接收"1"的个数为奇数个,则通过奇校验

了解完这些信息之后,就开始写驱动框架:整个采用 外部中断 + 定时器 来接收处理数据

  • 外部中断: 用来处理数据bit位的状态
  • 定时器 : 用来处理bit数据接收合成一个字节数据
/*模拟串口逻辑实现框架*/
typedef enum
{	
	VUART1=0,
	VUART2,	
}vuart_em;

//函数指针
typedef unsigned char(*byte_read)(vuart_em);
typedef void(*vuart_bsp)(vuart_em);
typedef void(*vuart_start)(vuart_em,FunctionalState);
typedef void(*data_timer)(FunctionalState);

/*
偶校验:就是发送的8个数据位的1的个数为偶数时,TB8=0;为奇数时,TB8=1;
奇校验:与偶校验相反的TB8。
*/
typedef enum
{
	STATE_START=0, /* startbit*/
	STATE_BIT0,
	STATE_BIT1,
	STATE_BIT2,
	STATE_BIT3,
	STATE_BIT4,
	STATE_BIT5,
	STATE_BIT6,
	STATE_BIT7,
	STATE_BITC,/*校验位*/	
	STATE_STOP /* stopbit*/
}rxsta_em;//数据枚举状态位

typedef enum
{
  NONE=0,//无
  ODD, //奇校验
  EVEN,//偶校验  
}chk_em;//校验选择枚举

typedef struct
{
  unsigned short int baud;  //波特率	
  chk_em  chk_type;//校验类型
}config_st; //串口信息配置结构体

typedef struct
{
  unsigned char byte_cont; //数据'1'的个数	
  unsigned char byte_value; //一字节数据
  unsigned char byte_chkok; //数据校验成功标志
}gdata_st;//帧数据结构体

typedef struct
{
  rxsta_em  state; //
}bit_rx_st;//数据位结构体

typedef struct
{
  bit_rx_st   bit_rx;
  gdata_st    data;
  config_st   config;
	data_timer  bit_timer;
  byte_read   byte_get;  //数据接收
  vuart_bsp   hw_init;   //硬件端口初始化
  vuart_start open;
}vuart_st;//虚拟串口信息结构体

#define VUART1_MAX_SIZE (32) //虚拟串口最大接收缓存字节数
POWER MONITOR///
#define PWR_MONITOR_PCC       RCC_AHBPeriph_GPIOC
#define PWR_MONITOR_PORT      GPIOC
#define PWR_MONITOR1_PIN      GPIO_Pin_13
#define PWR_MONITOR2_PIN      GPIO_Pin_14

#define PWR_MONITOR_EXT_PSP   EXTI_PortSourceGPIOC
#define PWR_MONITOR1_EXT_PS   EXTI_PinSource13
#define PWR_MONITOR1_EXT_LINE EXTI_Line13
#define PWR_MONITOR2_EXT_PS   EXTI_PinSource14
#define PWR_MONITOR2_EXT_LINE EXTI_Line14
#define PWR_MONITOR_EXT_IRQ   EXTI4_15_IRQn

#define PWR_MONITOR1_GET()  GPIO_ReadInputDataBit(PWR_MONITOR_PORT,PWR_MONITOR1_PIN)
#define PWR_MONITOR2_GET()  GPIO_ReadInputDataBit(PWR_MONITOR_PORT,PWR_MONITOR2_PIN)
//------------------------虚拟串口驱动----------------
unsigned char VUART1_RX_SIZE=0;
unsigned char VUART1_RX_BUF[VUART1_MAX_SIZE]={0};
vuart_st vuart1,vuart2;

/*
**虚拟串口1的GPIO配置
*/
void vuart1_bsp_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;  
    NVIC_InitTypeDef NVIC_InitStructure; 
	
   	RCC_AHBPeriphClockCmd(PWR_MONITOR_PCC, ENABLE); 
	
	GPIO_InitStructure.GPIO_Pin = PWR_MONITOR1_PIN; 	
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;	
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP; 	
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_Init(PWR_MONITOR_PORT, &GPIO_InitStructure); 

    NVIC_InitStructure.NVIC_IRQChannel=PWR_MONITOR_EXT_IRQ;    
    NVIC_InitStructure.NVIC_IRQChannelPriority=1;    
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;    
    NVIC_Init(&NVIC_InitStructure);		
}
/*
**虚拟串口2的GPIO配置
*/
void vuart2_bsp_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;  
    NVIC_InitTypeDef NVIC_InitStructure; 
	
   	RCC_AHBPeriphClockCmd(PWR_MONITOR_PCC, ENABLE); 

	  GPIO_InitStructure.GPIO_Pin = PWR_MONITOR2_PIN; 	
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;	
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP; 	
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(PWR_MONITOR_PORT, &GPIO_InitStructure); 

    NVIC_InitStructure.NVIC_IRQChannel=PWR_MONITOR_EXT_IRQ;    
    NVIC_InitStructure.NVIC_IRQChannelPriority=0;    
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;    
    NVIC_Init(&NVIC_InitStructure);		
}

/*
**虚拟串口选择
*/
void vuart_bsp_init(vuart_em vuartx)
{
	if(VUART1==vuartx)
	{
      vuart1_bsp_init();  		
	}
    else if(VUART2==vuartx)
	{
      vuart2_bsp_init();  		
	}	
}

/*
**虚拟串口打开
*/
void vuart_open(vuart_em vuartx,FunctionalState fsta)
{
	EXTI_InitTypeDef EXTI_InitStructure;  
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
	if(VUART1==vuartx)
	{
      SYSCFG_EXTILineConfig(PWR_MONITOR_EXT_PSP,PWR_MONITOR1_EXT_PS); 		
      EXTI_InitStructure.EXTI_Line=PWR_MONITOR1_EXT_LINE;  		
	}
    else if(VUART2==vuartx)
	{
      SYSCFG_EXTILineConfig(PWR_MONITOR_EXT_PSP,PWR_MONITOR2_EXT_PS);		
      EXTI_InitStructure.EXTI_Line=PWR_MONITOR2_EXT_LINE;  		
	}
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;    
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; 
	EXTI_InitStructure.EXTI_LineCmd=fsta;    
	EXTI_Init(&EXTI_InitStructure); 
	if(VUART1==vuartx)
	{
       EXTI_ClearFlag(PWR_MONITOR1_EXT_LINE);  		
	}
    else if(VUART2==vuartx)
	{
       EXTI_ClearFlag(PWR_MONITOR2_EXT_LINE);  		
	}	
}

/*
**数据位bit定时器
*/
void bit_timer(FunctionalState fsta)
{
  TIM_Cmd(TIM14,fsta);
}

/*
**字节数据读取
*/
unsigned char byte_get(vuart_em vuart)
{
	unsigned char ret=0;
	if(VUART1==vuart)
	{
		ret= PWR_MONITOR1_GET();
	}
	if(VUART2==vuart)
	{
		ret= PWR_MONITOR2_GET();		
	}
	return  ret;
}

/
**模拟串口配置初始化
*/
void vuart_init(vuart_st *const vuart_me,vuart_em vuartx,unsigned short int baud,chk_em chk_type)
{
   vuart_me->data.byte_cont=0; 
   vuart_me->data.byte_value=0;
   vuart_me->data.byte_chkok=0;
   vuart_me->bit_rx.state=STATE_STOP;//停止位
   vuart_me->config.baud=baud;
   vuart_me->config.chk_type=chk_type;   
   vuart_me->open=vuart_open;        //串口开闭函数指针
   vuart_me->hw_init=vuart_bsp_init; //端口硬件初始化函数指针
   vuart_me->byte_get=byte_get;      //端口数据读取函数指针   
   vuart_me->bit_timer=bit_timer;    //bit数据时基器
   vuart_me->hw_init(vuartx);
	 switch(baud)
	 {
		 case 2400:
			 	 timer14_init(417);//---bit/417us
			 break;
		 case 4800:
			 	 timer14_init(210);//---bit/209us
			 break;
		 case 9600:
			 	 timer14_init(106);//---bit/106us			 
			 break;		 
	 }
   vuart_me->bit_timer(DISABLE);//使能数据位计时器
   vuart_me->open(vuartx,ENABLE);//使能模拟串口接收
}

/*
**数据位状态切换
*/
__inline void vuart_state_handle(vuart_st *const vuart_me,vuart_em vuartx)//外部中断脉冲
{
    //下降沿中断
	if(0==vuart_me->byte_get(vuartx))//检测到低电平
	{	
		if(STATE_STOP<=vuart_me->bit_rx.state)//已超过停止位
		{
		   vuart_me->data.byte_cont=0;//重置
		   vuart_me->data.byte_value=0;
		   vuart_me->bit_rx.state=STATE_START;//重置为起始位
		   vuart_me->bit_timer(ENABLE);//开启bit数据计时器
		}
	}
}

/*
**字节数定时接收
*/
__inline void vuart_read_handle(vuart_st *const vuart_me,vuart_em vuartx)//定时读取
{	
	static unsigned char i;
//波特率:4800bps  
//--START_BIT->BIT0->BIT1->BIT2->BIT3->BIT4->BIT5->BIT6->BIT7->BITC->STOP_BIT->START_BIT	
	
  vuart_me->bit_rx.state++;//状态位切换
	switch(vuart_me->config.chk_type)//---是否有校验	
	{
		case NONE://无校验
			if(STATE_BITC==vuart_me->bit_rx.state)//当前已至校验位
			{
				 vuart_me->bit_rx.state=STATE_STOP;//将校验位直接当作停止位处理	
			}			
			break;
		case EVEN://偶校验
			if(STATE_BITC==vuart_me->bit_rx.state)//当前为校验位
			{
				 //校验规则判断
					if((0!=vuart_me->byte_get(vuartx)&&(0==vuart_me->data.byte_cont%2))||\
					((0==vuart_me->byte_get(vuartx))&&(0!=vuart_me->data.byte_cont%2)))
					{
						vuart_me->data.byte_chkok=0;//校验不通过
					}	
					else
					{
						vuart_me->data.byte_chkok=1;			   
					}		  
			 }			 
			break;
		case ODD://奇校验
			if(STATE_BITC==vuart_me->bit_rx.state)//当前为校验位
			{
				 //校验规则判断
					if((0!=vuart_me->byte_get(vuartx)&&(vuart_me->data.byte_cont%2))||\
					(0==vuart_me->byte_get(vuartx)&&(0==vuart_me->data.byte_cont%2)))
					{
						vuart_me->data.byte_chkok=0;//校验不通过
					}
					else
					{
						vuart_me->data.byte_chkok=1;			   
					}				 
			 }			
			break;
		default:break;		
	}
	//---停止位		
	if(STATE_STOP==vuart_me->bit_rx.state) //定时读取209us/bit
	{
		vuart_me->bit_timer(DISABLE);//关闭计数时基器
		if(vuart_me->data.byte_chkok)//数据位检验通过,一个字节完成
		{
			if(VUART1_MAX_SIZE>VUART1_RX_SIZE)//未     
			{
			  VUART1_RX_BUF[VUART1_RX_SIZE++]=vuart_me->data.byte_value;//接收串口数据送入缓存区				
			}
      vuart_me->data.byte_chkok=0;//清除本次校验通过标志			
		}
		else //当前字节数据校验不通过,则放弃之前收到的数据
		{
		  VUART1_RX_SIZE=0;
		  vuart_me->data.byte_value=0;
		  memset(&VUART1_RX_BUF,0x00,VUART1_MAX_SIZE);
		}
		return;		
	}	
	//---有效数据位[--介于起始位与校验位间--]
	if(STATE_START<vuart_me->bit_rx.state&&STATE_BITC>vuart_me->bit_rx.state)
	{
		i=vuart_me->bit_rx.state;
		if(0!=vuart_me->byte_get(vuartx))//高电平
		{
		  vuart_me->data.byte_cont++;
		  vuart_me->data.byte_value|=(1<<(i-1));
		}
		else//低电平
		{
		  vuart_me->data.byte_value&=~(1<<(i-1));
		}
	}
}

/*
**读取串口数据
*/
void vuart_data_read(vuart_st *const vuart_me,vuart_em vuartx,unsigned char *vuart_data,unsigned char vuart_data_len)
{
	  #define WAIT_DATA_TIME (2)                //(需要根据字节数以及调用该函数的时基作相应的时间调整),本例程调用的时基为:10ms
	  static unsigned char vuartx_recv_timing=0;//接收DATA_MAX_LEN帧长度数据的时间控制值
  	vuart_me->open(vuartx,ENABLE);//打开串口(开启外部中断,开始接收数据)
	
/*
PS:当时在没有加入这个时间量的控制时,调试时发现数据一致不能正常抓取,数据虽没有乱码(误码),
但是总是抓头不抓尾,像断帧,百思不得其解,后来参考同事的驱动,
才意识到是没有考虑保证接收一个完整帧数据的时间,
于是就加入了这个vuartx_recv_timing变量进行接收控制,于是问题完美解决。
*/		
	if(vuart_data_len<=VUART1_RX_SIZE&&WAIT_DATA_TIME<vuartx_recv_timing++)//已达到协议长度,且已超过完整帧接收时长
	{
		for(unsigned char i=0;i<vuart_data_len;i++)
		{
			vuart_data[i]=VUART1_RX_BUF[i];
		}
		VUART1_RX_SIZE=0;//接收地址重置
		vuartx_recv_timing=0;
	//	memset(&VUART1_RX_BUF[0],0x00,VUART1_MAX_SIZE);//销毁本次数据		
	}		
}

/*
**模拟串口硬件外部中断位处理
*/
void EXTI4_15_IRQHandler(void)//
{
  if((EXTI_GetITStatus(PWR_MONITOR1_EXT_LINE)!= RESET)||(EXTI_GetFlagStatus(PWR_MONITOR1_EXT_LINE))!= RESET)
  {	
  //vuart_state_handle(&vuart1,VUART1);
	EXTI_ClearFlag(PWR_MONITOR1_EXT_LINE);
	EXTI_ClearITPendingBit(PWR_MONITOR1_EXT_LINE);  
  }
  if((EXTI_GetITStatus(PWR_MONITOR2_EXT_LINE)!= RESET)||(EXTI_GetFlagStatus(PWR_MONITOR2_EXT_LINE))!= RESET)
  {	
    vuart_state_handle(&vuart2,VUART2);
  	EXTI_ClearFlag(PWR_MONITOR2_EXT_LINE);
  	EXTI_ClearITPendingBit(PWR_MONITOR2_EXT_LINE);  
  } 
}

/*
**数据位定时中断接收里
*/
void TIM14_IRQHandler(void) //
{
 if((TIM_GetFlagStatus(TIM14, TIM_FLAG_Update)!= RESET)||(TIM_GetITStatus(TIM14, TIM_IT_Update)!= RESET) )
 {	
   // vuart_read_handle(&vuart1,VUART1);
    vuart_read_handle(&vuart2,VUART2);
	  TIM_ClearITPendingBit(TIM14,TIM_IT_Update);
	  TIM_ClearFlag(TIM14,TIM_FLAG_Update);		
 }
}
  • 虚拟串口配置初始化
/*
**配置初始化
*/
 vuart_init(&vuart2,VUART2,4800,EVEN);//4800--偶校验
  • 接口调用例程
hlw8302_un hlw8302_a,hlw8302_b;
void hlw8032_data_handle(hlw8302_un *const hlw8302_me,vuart_st *const vuart_me,vuart_em vuartx)
{
	unsigned short int crc_data=0;

	vuart_data_read(vuart_me,vuartx,hlw8302_me->data,DATA_MAX_LEN);//提取串口数据
		
	for(unsigned char j=3;j<DATA_MAX_LEN-3;j++)//有效长度
	{
		crc_data=+hlw8302_me->data[j];           //求取校验和值
	}   
	//无效数据
	if(0xAA==hlw8302_me->reg.state_data||((unsigned char)crc_data!=hlw8302_me->reg.checksum_data))
	{
		memset(&hlw8302_me->data[0],0x00,DATA_MAX_LEN);//舍弃本次数据	   
	//  memset(&VUART1_RX_BUF,0x00,VUART1_MAX_SIZE);	   
	}	
}

/*
**放入定时器中断中
*/
void hlw8302_thread(void)
{
	hlw8032_data_handle(&hlw8302_b,&vuart2,VUART2);
}
  • 最后用串口助手测试,完美接收。