STM32模拟串口驱动(带校验位)
背景
- 年前一个项目中,由于串口的资源紧张,硬件使用的是主MCU-STM32F030C8T6,其两个硬件串口都被使用了,而另外一个器件间的通信也需要串口通信,故不得不采用模拟串口的方式,该器件的通信要求是:4800bps ,8,E,1。
对,是采用偶校验的方式。之前一直习惯无校验方式,一下子说要采用偶校验,所以不得不深度查阅和了解串口的相关信息。
另外可以看我另一篇基于此框架在读取机制(采用IDLE空闲信号机制)上做了进一步的优化的文章:博文地址
- 以下即是所了解的 校验位信息的解读说明:
- 设置为奇校验,先看发送方将要发送的一帧数据中有几个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/417usbreak;case 4800:timer14_init(210);//---bit/209usbreak;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) //(需要根据字节数以及调用该函数的时基作相应的时间调整),本例程调用的时基为:10msstatic 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);
}
- 最后用串口助手测试,完美接收。
STM32模拟串口驱动(带校验位)相关推荐
- STM32模拟IIC驱动sht30温湿度传感器
最近有在使用sht30这个传感器,相比于新手常用的dht11传感器,sht30更精确,自己花了半小时调好了 所以拿出来分享给大家. sht30外观 驱动不是自己写的, 是采用CSDN上的一位朋友的 , ...
- STM32模拟IIC驱动OLED屏 原理+源码
处理器和芯片间的通信可以形象的比喻成两个人讲话:1.你说的别人得能听懂:双方约定信号的协议.2.你的语速别人得能接受:双方满足时序要求. 一.IIC总线的信号类型 1.开始信号:处理器让SCL时钟保持 ...
- ADS1118 STM32模拟spi驱动(HAL)
#ifndef _ADS1118_SPI_H #define _ADS1118_SPI_H#define ADS1118CS_AIN0 0xC38B //量程0-4.096v #define ADS1 ...
- 基于HAL库STM32串口驱动不定长数据接收
STM32串口驱动不定长数据接收带环形缓冲区 最新框架代码 使用方法 源码 串口接口文件 环形缓冲区接口文件 移植图示 使用涉及4个文件, UART_Port.c UART_Port.h Circul ...
- STM32 IO口模拟串口通讯
转自:http://ziye334.blog.163.com/blog/static/224306191201452833850647 前阵子,调项目时需要用到低波特率串口通讯(300的波特率),才发 ...
- K_A12_014 基于STM32等单片机驱动S12SD紫外线传感器模块 串口与OLED0.96双显示
K_A12_014 基于STM32等单片机驱动S12SD紫外线传感器模块 串口与OLED0.96双显示 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 IIC地址/采集通道选择/时序 对应程 ...
- K_A11_002 基于STM32等单片机驱动DS18B20串口与OLED0.96双显示
K_A11_002 基于STM32等单片机驱动DS18B20 串口与OLED0.96双显示 一.资源说明 二.基本参数 1.参数 2.引脚说明 三.驱动说明 时序 对应程序: 四.部分代码说明 1.接 ...
- K_A11_008 基于STM32等单片机驱动SHT30和SHT31 串口与OLED0.96双显示
K_A11_008 基于STM32等单片机驱动SHT30和SHT31 串口与OLED0.96双显示 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 时序 对应程序: 四.部分代码说明 1.接 ...
- K_A16_001 基于STM32等单片机驱动HX711称重模块 串口与OLED0.96双显示
K_A16_001 基于STM32等单片机驱动HX711称重模块 串口与OLED0.96双显示 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 对应程序: 四.部分代码说明 1.接线引脚定义 ...
最新文章
- python什么时候用类_python中什么时候使用自定义类
- python的语法类似php_PHP实现类似python__mian__=__name__来单独执行类文件
- boost::safe_numerics模块实现检测到的数据类型溢出的测试程序
- Lambda表达式替代匿名方法
- [摘自MSDN] ASP.Net2.0学习 [1] 母版页 2 : 创建和使用 ASP.NET 母版页
- 15 款Python编辑器的优缺点,别再问我“选什么编辑器”啦!
- java 常见 错误_JAVA几个常见错误简析
- POJ1279 Art Gallery 多边形的核
- 力扣908.最小差值Ⅰ
- [EMNLP18]用序列标注来进行成分句法分析
- 汇编程序16位带符号变量计算
- HDU2072 tri树/map/set/字符串hash
- Linux加密框架 crypto 哈希算法说明 同步哈希shash_alg | 异步哈希 ahash_alg | 通用部分抽象 hash_alg_common
- 工程师应培养哪些基本功?听美团技术大咖怎么说
- insmod与modprobe命令的区别及其相关命令
- Android~快捷方式兼容适配
- 午餐不知道吃什么?用 Python 爬美团App评论选餐!
- bug生命周期的状态
- 猫猫学iOS之微博国际版的一个关于线程调用的异常修复Main Thread Checker: UI API called on a background thread 异常
- CJSON的介绍以及使用