背景
- 年前一个项目中,由于串口的资源紧张,硬件使用的是主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);
}
- 最后用串口助手测试,完美接收。