背景

  • 年前一个项目中,由于串口的资源紧张,硬件使用的是主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模拟串口驱动(带校验位)相关推荐

  1. STM32模拟IIC驱动sht30温湿度传感器

    最近有在使用sht30这个传感器,相比于新手常用的dht11传感器,sht30更精确,自己花了半小时调好了 所以拿出来分享给大家. sht30外观 驱动不是自己写的, 是采用CSDN上的一位朋友的 , ...

  2. STM32模拟IIC驱动OLED屏 原理+源码

    处理器和芯片间的通信可以形象的比喻成两个人讲话:1.你说的别人得能听懂:双方约定信号的协议.2.你的语速别人得能接受:双方满足时序要求. 一.IIC总线的信号类型 1.开始信号:处理器让SCL时钟保持 ...

  3. ADS1118 STM32模拟spi驱动(HAL)

    #ifndef _ADS1118_SPI_H #define _ADS1118_SPI_H#define ADS1118CS_AIN0 0xC38B //量程0-4.096v #define ADS1 ...

  4. 基于HAL库STM32串口驱动不定长数据接收

    STM32串口驱动不定长数据接收带环形缓冲区 最新框架代码 使用方法 源码 串口接口文件 环形缓冲区接口文件 移植图示 使用涉及4个文件, UART_Port.c UART_Port.h Circul ...

  5. STM32 IO口模拟串口通讯

    转自:http://ziye334.blog.163.com/blog/static/224306191201452833850647 前阵子,调项目时需要用到低波特率串口通讯(300的波特率),才发 ...

  6. K_A12_014 基于STM32等单片机驱动S12SD紫外线传感器模块 串口与OLED0.96双显示

    K_A12_014 基于STM32等单片机驱动S12SD紫外线传感器模块 串口与OLED0.96双显示 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 IIC地址/采集通道选择/时序 对应程 ...

  7. K_A11_002 基于STM32等单片机驱动DS18B20串口与OLED0.96双显示

    K_A11_002 基于STM32等单片机驱动DS18B20 串口与OLED0.96双显示 一.资源说明 二.基本参数 1.参数 2.引脚说明 三.驱动说明 时序 对应程序: 四.部分代码说明 1.接 ...

  8. K_A11_008 基于STM32等单片机驱动SHT30和SHT31 串口与OLED0.96双显示

    K_A11_008 基于STM32等单片机驱动SHT30和SHT31 串口与OLED0.96双显示 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 时序 对应程序: 四.部分代码说明 1.接 ...

  9. K_A16_001 基于STM32等单片机驱动HX711称重模块 串口与OLED0.96双显示

    K_A16_001 基于STM32等单片机驱动HX711称重模块 串口与OLED0.96双显示 一.资源说明 二.基本参数 参数 引脚说明 三.驱动说明 对应程序: 四.部分代码说明 1.接线引脚定义 ...

最新文章

  1. python什么时候用类_python中什么时候使用自定义类
  2. python的语法类似php_PHP实现类似python__mian__=__name__来单独执行类文件
  3. boost::safe_numerics模块实现检测到的数据类型溢出的测试程序
  4. Lambda表达式替代匿名方法
  5. [摘自MSDN] ASP.Net2.0学习 [1] 母版页 2 : 创建和使用 ASP.NET 母版页
  6. 15 款Python编辑器的优缺点,别再问我“选什么编辑器”啦!
  7. java 常见 错误_JAVA几个常见错误简析
  8. POJ1279 Art Gallery 多边形的核
  9. 力扣908.最小差值Ⅰ
  10. [EMNLP18]用序列标注来进行成分句法分析
  11. 汇编程序16位带符号变量计算
  12. HDU2072 tri树/map/set/字符串hash
  13. Linux加密框架 crypto 哈希算法说明 同步哈希shash_alg | 异步哈希 ahash_alg | 通用部分抽象 hash_alg_common
  14. 工程师应培养哪些基本功?听美团技术大咖怎么说
  15. insmod与modprobe命令的区别及其相关命令
  16. Android~快捷方式兼容适配
  17. 午餐不知道吃什么?用 Python 爬美团App评论选餐!
  18. bug生命周期的状态
  19. 猫猫学iOS之微博国际版的一个关于线程调用的异常修复Main Thread Checker: UI API called on a background thread 异常
  20. CJSON的介绍以及使用

热门文章

  1. 计算机组成原理(二) 计算机算术
  2. horspool算法C语言代码,sfa3
  3. 浙江大华嵌入式软件工程师笔试题
  4. Python和numpy下载安装方法
  5. MIPS汇编 汇编程序简介
  6. ADI Blackfin DSP处理器-BF533的开发详解22:SD卡的设计和实现原理及应用(含源码)
  7. 用scratch实现网上游戏“球球情侣2”
  8. MCAFEE按访问扫描被禁用,卸载MCAFEE方法!
  9. 第十六届全国大学生信息安全竞赛创新实践能力赛(CISCN)
  10. mete-learning