STM32 IO模拟实现软件串口

发布者:老卫最新更新时间:2020-06-30 来源: eefocus关键字:STM32  IO模拟  软件串口 手机看文章 扫描二维码
随时随地手机看文章

最近项目中STM32的串口资源紧张,于是使用IO口进行模拟串口,现进行整理记录。


实现思路

IO口模拟串口的思路也比较简单,一切按照串口协议进行操作即可。

对于发送,计算好不同波特率对应的延时时间进行数据发送。


对于接收,稍微复杂。通过外部中断检测接收管脚的下降沿,检测到起始信号后开启定时器,定时器按照波特率设定好时间,每隔一段时间进入定时器中断接收数据,完成一个字节后关闭定时器。


测试Demo说明

TXD : PC13

RXD : PB14

波特率:9600 ,1-8-N


Demo功能

接收11个数据,然后把接收到的数据发送出去。


程序实现


#define OI_TXD PCout(13)

#define OI_RXD PBin(14)


#define BuadRate_9600 100


u8 len = 0; //接收计数

u8 USART_buf[11];  //接收缓冲区


enum{

COM_START_BIT,

COM_D0_BIT,

COM_D1_BIT,

COM_D2_BIT,

COM_D3_BIT,

COM_D4_BIT,

COM_D5_BIT,

COM_D6_BIT,

COM_D7_BIT,

COM_STOP_BIT,

};


u8 recvStat = COM_STOP_BIT;

u8 recvData = 0;


void IO_TXD(u8 Data)

{

u8 i = 0;

OI_TXD = 0;  

delay_us(BuadRate_9600);

for(i = 0; i < 8; i++)

{

if(Data&0x01)

OI_TXD = 1;  

else

OI_TXD = 0;

delay_us(BuadRate_9600);

Data = Data>>1;

}

OI_TXD = 1;

delay_us(BuadRate_9600);

}

void USART_Send(u8 *buf, u8 len)

{

u8 t;

for(t = 0; t < len; t++)

{

IO_TXD(buf[t]);

}

}

 void IOConfig(void)

 {

GPIO_InitTypeDef  GPIO_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

  EXTI_InitTypeDef EXTI_InitStruct;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PC端口时钟 

 

//SoftWare Serial TXD

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;     

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz  

  GPIO_Init(GPIOC, &GPIO_InitStructure);  

  GPIO_SetBits(GPIOC,GPIO_Pin_13);

 

 

//SoftWare Serial RXD

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);  


GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);

EXTI_InitStruct.EXTI_Line = EXTI_Line14;

EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;

EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断

EXTI_InitStruct.EXTI_LineCmd=ENABLE;

EXTI_Init(&EXTI_InitStruct);



NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn ; 

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; 

NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;  

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;  

NVIC_Init(&NVIC_InitStructure);  

}

 

void TIM4_Int_Init(u16 arr,u16 psc)

{

  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能

//定时器TIM4初始化

TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);

TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断


//中断优先级NVIC设置

NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  //TIM4中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级1级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //从优先级1级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能

NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器  

}

 

 

 int main(void)

 {

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级

delay_init();

IOConfig();

  TIM4_Int_Init(107, 71); //1M计数频率

 

  while(1)

{

if(len > 10)

{

len = 0;

USART_Send(USART_buf,11);

}

}

}


void EXTI15_10_IRQHandler(void)

{

if(EXTI_GetFlagStatus(EXTI_Line14) != RESET)

{

if(OI_RXD == 0) 

{

if(recvStat == COM_STOP_BIT)

{

recvStat = COM_START_BIT;

TIM_Cmd(TIM4, ENABLE);

}

}

EXTI_ClearITPendingBit(EXTI_Line14);

}

}


void TIM4_IRQHandler(void)

{  

if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) != RESET)

{

TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);

recvStat++;

if(recvStat == COM_STOP_BIT)

{

TIM_Cmd(TIM4, DISABLE);

USART_buf[len++] = recvData;

return;

}

if(OI_RXD)

{

recvData |= (1 << (recvStat - 1));

}else{

recvData &= ~(1 << (recvStat - 1));

}

  }

}


Demo下载

Demo工程下载地址github。

https://github.com/TonyIOT/SoftWareSerial.git

关键字:STM32  IO模拟  软件串口 引用地址:STM32 IO模拟实现软件串口

上一篇:关于STM32 利用IO口模拟串口实现数据通信
下一篇:stm32 浮点数问题

推荐阅读最新更新时间:2024-11-22 23:56

STM32 USART 输入输出C库函数重定向理解
重定向:是指用户可以自己重写c的库函数,当连接器检查到用户编写了与C库函数相同名字的函数时,优先采用用户编写的函数,这样用户就可以实现对库的修改了。 为了实现重定向printf()函数,我们需要重写fputc()这个c标准库函数,因为printf()在c标准库函数中实质是一个宏,最终调用了fputc()这个函数。 例如 用户有一个I/O设备,如USART。本来库函数 fputc()是把字符输出到调试器控制窗口上去,但用户要把数据通过USART输出到串口助手上去,这样一来,用到的基于fputc()函数的printf()系列函数的输出都需要被重定向到USART端口上去。 所以要想使用USART功能,用户必须自己重定向fpu
[单片机]
STM32的IIC应用详解1
概要 IIC(IIC,inter-Integrated circuit),两线式串行总线,用于MCU和外设间的通信。 IIC只需两根线:数据线SDA和时钟线SCL。以半双工方式实现MCU和外设之间数据传输,速度可达400kbps。 多主机I2C总线结构 注意SDA和SCL两根总线需要上拉,使总线处于空闲状态。 IIC协议 空闲状态 协议规定,SDA和SCL同时为高电平时,总线处于空闲状态。上拉电阻保证电平处于高电平。 起始信号和停止信号 起始信号:SCL为高电平时,SDA电平发生高到低的跳变 停止信号:SCL为高电平时,SDA电平发生低到高的跳变 应答信号 发送器每发送完一个字节(8个脉冲),在第9个脉
[单片机]
<font color='red'>STM32</font>的IIC应用详解1
STM32_ ADC单通道单次采集
今天的软件工程下载地址(360云盘): https://yunpan.cn/cPGrE6DLHX24R 访问密码 de4f STM32F10x的资料可以在我360云盘下载: https://yunpan.cn/crBUdUGdYKam2 访问密码 ca90 关于“STM32F103ADC单通道单次采集” 我把重要的几点在下面分别讲述,若不明白,请关注微信公众号“EmbeddDeveloper”查阅或留言。 一、RCC时钟配置 该函数位于在bsp.c文件下面; 使能RCC时钟:RCC_APB2Periph_ADC1 二、引脚配置 该函数位于在adc.c文件下面; 对AD通道2所使用的引脚进行配置。 问题: 细心的人可能
[单片机]
STM32_ ADC单通道单次采集
023_STM32之PID算法原理及应用
(一)PID控制算法(P:比例     I:积分    D:微分) (二)首先先说明原理,使用的是数字PID算法,模拟PID算法在计算机这样的系统中是不能够直接使用的,数字PID算法又分为位置式PID控制算法和增量式PID控制算法,那么下面从原理上说明这两种算法 (三)原理分析如图 (四)从上面图中我们可以得到定义 定义变量 用户设定值: SV 当前值(实际值): PV 偏差: E = SV - PV (五)如果我们在一段时间内就从传感器读取一个值,那么我们就可以得到一个实际值的数据序列,,那么我们也会得到一个偏差值的序列 读取时间:  t(1) t(2)    ------  t(k
[单片机]
023_STM32之PID算法原理及应用
stm32学习之七
USART串口学习: 本篇主要是stm32板子与PC机器的连接,由于是初学,花了很长的时间思考的实验,才成功的配置成功串口的通信,煞费脑筋,所以自成一篇博客。 其实在买板子的时候,应该想到有能实现通信功能的连接线,但是主要是笔记本电脑,为了稳妥起见,不敢夸张的实验。 步骤: 1、有一个USB转(串口线)com(不专业的说法,但是清晰的可以看到板子上面有com1,就这样称呼)连接线,然后就有一根连接开发板的com线,这样可以实现PC机器和stm32的连接。如果电脑上显示驱动没有安装成功,那么这个时候就要想到驱动的安装问题了。可以网上找,或者......你知道的。 2、打开设备管理器,可以看到电脑上
[单片机]
STM32 NVIC学习
阅读nvic:系统中断管理。 我的理解——管理系统内部的中断,负责打开和关闭中断。 基础应用1,中断的初始化函数,包括设置中断向量表位置,和开启所需的中断两部分。所有程序中必须的。 用法: void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure;//中断管理恢复默认参数 #ifdef VECT_TAB_RAM //如果C/C++ Compiler\Preprocessor\Defined symbols中的定义了VECT_TAB_RAM(见程序库更改内容的表格) NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
[单片机]
STM32-(34):DMA传输控制(理论)
直接存储器传送(Direct Memory Access-DMA) 将外设的数据不经过CPU直接送入内存储器,或者,从内存储器不经过CPU直接送往外部设备 一次DMA传送只需要执行一个DMA周期(相当于一个总线读/写周期),因而能够满足高速外设数据传输的需要。 Direct Memory Access (存储器直接访问)。这是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过 CPU ,也不需要 CPU 干预。整个数据传输操作在一个称为 DMA 控制器 的控制下进行的。 CPU 除了在数据传输开始和结束时做一点处理外,在传输过程中 CPU 可以进行其他的工作。这样,在大部分时间里,CPU 和输入输出都处于
[单片机]
STM32-(34):DMA传输控制(理论)
STM32单片机硬件关键基础精华及注意事项
   STM32简单介绍   一、背景   如果你正为项目的处理器而进行艰难的选择:一方面抱怨16位单片机有限的指令和性能,另一方面又抱怨32位处理器的高成本和高功耗,那么,基于 ARM Cortex-M3内核的STM32系列处理器也许能帮你解决这个问题。使你不必在性能、成本、功耗等因素之间做出取舍和折衷。   即使你还没有看完STM32的产品手册,但对于这样一款融合ARM和ST技术的“新生儿”相信你和我一样不会担心这款针对16位MCU应用领域 的32位处理器的性能,但是从工程的角度来讲,除了芯片本身的性能和成本之外,你或许还会考虑到开发工具的成本和广泛度;存储器的种类、规模、性能和容 量;以及各种软件获得的难易,我相信你看
[单片机]
<font color='red'>STM32</font>单片机硬件关键基础精华及注意事项
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多往期活动

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved