模拟串口UART的实现
我所祷告的,就是要你们的爱心,在知识和见识上,多而又多,使你们能分辨是非,做诚实无过的人,直到基督的日子。
——腓立比书【1:9~10】
最近在调的MCU的型号为STM32F030,配置芯片相较之前的MCU都比较简单,功能配置很顺利。但是在写串口程序的时候,发现串口一直不通,使用示波器也没有波形。因为基本的串口通讯线只有Tx和Rx两根线,配置也相对简单,8位数位,1位停止位,9600波特率。协议结构为 起始位(低电平)+8位数据(低位在前)+1位停止位(高电平),例如发送字节0x55,即电平为低 高低高低高低高低 高。电平转换的间隔时间为1s/9600 = 104us
以上均为理论分析过程,检查代码对串口的配置都没有发现错误。最终排查的结果是硬件工程师画原理图和PCB图时将串口的Tx和Rx画反了!由于某些原因板子已经量产了,故只能通过改软件来实现串口的功能,在网上找了一下发现模拟串口可行性可以,故动手写了一下模拟串口。
串口通讯需要模拟两根线(Tx和Rx)的时序,模拟串口的主要思路如下:
发送部分比较简单,按照 起始位(低电平)+8位数据(低位在前)+1位停止位(高电平),间隔时间104us,即可。
接收部分有点复杂,需要配置一个外部中断,用于检测低电平信号,还需要一个定时器,用于读取有效数据。
下面将代码附上:
发送IO口初始化
/*!
* @brief 模拟串口1 TX IO口配置
* @param none
* @return none
* @note Tx(PA10)
*/
void MUSART1_TX_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_10);
}
接收IO口初始化
/*!
* @brief 模拟串口1 RX IO口配置
* @param none
* @return none
* @note Rx(PA9)
*/
void MUSART1_RX_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //!<外部中断时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource9);
EXTI_InitStructure.EXTI_Line=EXTI_Line9;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿中断
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI4_15_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority=0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
定时器初始化
/*!
* @brief 定时器14初始化
* @param
* @return NONE
* @note 103us定时器,用于串口数据采样
*/
void Time14Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimerBaseStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); //!<时钟使能
TIM_DeInit(TIM14); //!<Time1定时器重设缺省值
TIM_TimerBaseStruct.TIM_Period=103; //!<设置重载寄存器初值 (设置为103,即:定时104us)
TIM_TimerBaseStruct.TIM_Prescaler=7; //!<使用内部8M时钟,分频8(7+1),8M/8 = 1000000,故数1000000(999999+1)下,达1秒
TIM_TimerBaseStruct.TIM_ClockDivision=0; //!<不分频
TIM_TimerBaseStruct.TIM_CounterMode=TIM_CounterMode_Up; //!<设置计数器向上计数模式
TIM_TimeBaseInit(TIM14,&TIM_TimerBaseStruct);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM14_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ClearITPendingBit(TIM14, TIM_FLAG_Update);
TIM_ITConfig(TIM14,TIM_IT_Update,ENABLE); //!<使能TIM1中断源
TIM_Cmd(TIM14,DISABLE); //!<禁能TIM1定时器
}
发送数据函数
uint32 delayTime =99; //!<9600,理论值为104但实际测下来99时效果最好
/*!
* @brief 模拟串口1发送一个字节
* @param
* @return none
* @note 数据低位在前高位在后
*/
void MUSART1_SendData(uint8 data)
{
uint8 i = 0;
TX_L(); //!<起始位
delay_us(delayTime);
for(i = 0; i < 8; i++){
if(data & 0x01)
TX_H();
else
TX_L();
delay_us(delayTime);
data >>= 1;
}
TX_H(); //!<停止位
delay_us(delayTime);
}
接收数据,外部中断起始接收
/*!
* @brief 串口接收IO中断处理函数
* @param none
* @return NONE
* @note none
*/
void EXTI4_15_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line9) != RESET){
if(RX_READ() == 0x00){
if(rx_state >= STATE_STOP){
recvData = 0;
rx_state = STATE_START;
delay_us(50);
TIM_Cmd(TIM14, ENABLE); //!<打开定时器,接收数据
}
}
EXTI_ClearITPendingBit(EXTI_Line9);
}
}
接收数据,定时器中断接收数据
/*!
* @brief 定时器1中断处理函数
* @param
* @return NONE
* @note
*/
void TIM14_IRQHandler(void)
{
if(TIM_GetITStatus(TIM14, TIM_IT_Update) != RESET){
rx_state++; //!<改变状态机
if(rx_state == STATE_STOP){
TIM_Cmd(TIM14, DISABLE); //!<关闭定时器
usart_getByte(); //!<接收到停止位之后,处理数据recvData
return; //!<返回
}
if(RX_READ()){
recvData |= (1 << (rx_state - 1));
}else{
recvData &= ~(1 <<(rx_state - 1));
}
TIM_ClearITPendingBit(TIM14, TIM_FLAG_Update);
}
}
其他说明
typedef enum{
STATE_START=0,
STATE_BIT0,
STATE_BIT1,
STATE_BIT2,
STATE_BIT3,
STATE_BIT4,
STATE_BIT5,
STATE_BIT6,
STATE_BIT7,
STATE_STOP
}RX_STATE;
RX_STATE rx_state = STATE_STOP;
uint8 recvData=0; //!<接收的一个字节数据,全局变量
至此,模拟串口的代码及原理均已描述完成。单独的串口通讯并没有问题,但是在实际应用中采取了一种特殊的“总线”形式。
本次写的是从机部分的代码,从机接收数据并没有问题,但是在发送数据时,由于所有的从机Tx都挂载在同一根Tx上,并且从机Tx空闲状态时一直是高电平,导致指定从机的起始信号发不出去。故需要再做以下处理,解决以上问题。
当接收到的数据包中的ID为本从机ID时将Tx拉高,否则拉低,这样能够保证当指定ID的从机发送数据时有且只有一个从机再总线上发送数据(其他从机的Tx主动离线)。
好了就记录这么多。
模拟串口UART的实现的更多相关文章
- [15单片机] STC15F104W开发入门及模拟串口程序
STC15F104W开发入门及模拟串口程序 Saturday, 31. March 2018 09:42AM - beautifulzzzz 前言 最近找到一款51内核的SOP8封装的8脚单片机STC ...
- STM32之模拟串口设计
一.设计用途: 公司PCB制成板降成本,选择的MCU比项目需求少一个串口,为满足制成板成本和项目对串口需求,选择模拟一路串口. 二.硬件电路: 三.设计实现: 工具&软件:STM32F030R ...
- 如何在User版本开启串口(Uart),抓取上层Log,开启输入控制台
[原][FAQ03891] 如何在User版本开启串口(Uart),抓取上层Log,开启输入控制台 2014-11-26阅读1369 评论0 FAQ Content [Description]如何在U ...
- GPIO模拟串口注意是事项
GPIO模拟串口需要注意的事项如下:(程序见我的博客第一篇) 1.由于串口是异步通信,则串口发送必须满足宽度要求. (1)假设串口的波特率是9600bps(1s传输9600个bit),则传输1bit需 ...
- 通用GPIO模拟串口,提供源代码,本人经过测试OK(第一版)
--------------------------serial.h------------------------------------------ #ifndef _SERIAL_H_ #def ...
- 51单片机GPIO口模拟串口通信
51单片机GPIO口模拟串口通信 标签: bytetimer终端存储 2011-08-03 11:06 6387人阅读 评论(2) 收藏 举报 本文章已收录于: 分类: 深入C语言(20) 作者同 ...
- stc15f104w模拟串口使用
stc15f104w单片机体积小,全8个引脚完全够一般的控制使用,最小系统也就是个电路滤波----加上一个47uf电容和一个103电容即可,但因为其是一个5V单片机,供电需要使用5V左右电源. 该款单 ...
- C# 为VB6.0程序模拟串口数据
为VB6.0编写程序模拟数据测试使用. 一.VB6.0 控件MSComm,来发送接收串口数据 CommPort 属性设置并返回通讯端口号,虚拟端口为COM2. Settings 属性设置并返回端口的波 ...
- C# 模拟串口发送接收
一.准备虚拟串口驱动工具 创建俩个虚拟串口,如图: 二.创建两个控制台程序 模拟串口的发送接收数据 1. 接收数据,代码如下: //遍历串行端口名称数组 foreach (string port in ...
随机推荐
- 基于websocket实现的一个简单的聊天室
本文是基于websocket写的一个简单的聊天室的例子,可以实现简单的群聊和私聊.是基于websocket的注解方式编写的.(有一个小的缺陷,如果用户名是中文,会乱码,不知如何处理,如有人知道,请告知 ...
- IDEA注释设置:从当前鼠标位置开始注释快捷键
在写xml或html注释时,经常出现注释出来的时候都是顶格的,前面包含一大段空格,并没有在鼠标位置开始. 可在设置中进行修改,其他代码格式修改方法类似
- python基础语法--字典的遍历
原文链接:https://blog.csdn.net/normang/article/details/55804231 (1)遍历key值 >>> a {'a': '1', 'b': ...
- C++STL(set……)
set 底层实现是用红黑树. set 建立 set<int> s; // 不可重,默认升序 set<int,less> s; // 不可重,升序 set<int,grea ...
- hdu 2199 Can you solve this equation?(二分法求多项式解)
题意 给Y值,找到多项式 8*x^4 + 7*x^3 + 2*x^2 + 3*x + 6 == Y 在0到100之间的解. 思路 从0到100,多项式是单调的,故用二分法求解. 代码 double c ...
- POJ 2584 T-Shirt Gumbo(二分图最大匹配)
题意: 有五种衣服尺码:S,M,L,X,T N个人,每个人都有一个可以穿的衣服尺码的范围,例:SX,意思是可以穿S,M,L,X的衣服. 给出五种尺码的衣服各有多少件. 如果可以满足所有人的要求,输出 ...
- 攻防环境配置大全(iss/apache/nginx/tomcat/jboss/weblogic)
一.IIS/apache/nginx/tomcat 介绍 1.asp aspx 只能在微软系统的iis中间件运行 [asp+IIS+access(扩展名为mdb)].aspx+mssql+iis结合, ...
- Kubernetes Deployment 源码分析(二)
概述startDeploymentController 入口逻辑DeploymentController 对象DeploymentController 类型定义DeploymentController ...
- Mysql教程:(四)连接查询
连接查询 1.左连接查询: mysql> select stu.*,sc.*,maths+sc.chinese+sc.english from student stu left join sco ...
- Pytest使用pytest-html和allure生成测试报告
Pytest-html 1.安装命令pip3 install pytest-html,如下图: 执⾏后,会在当前⽬录下⽣成 ⼀个report.html的⽂件,打开后会展示详细的测试报告,执行该命令py ...