48 STM32普通IO模拟usart串口

铁头山羊-STM32(为串口初始化IO引脚) 本文介绍了在STM32单片机上配置USART串口引脚的方法。主要内容包括:1)通过数据手册查找USART默认引脚(PA9为TX,PA10为RX)及其重映射选项(PB6/PB7);2)详细说明引脚配置模式,TX引脚应设为复用输出推挽,RX引脚推荐设为输入上拉;3)提供两种配置代码示例(默认引脚和重映射引脚),强调需先开启AFIO时钟再进行重映射;4)总结了引脚查找方法、重映射功能和模式选择原则等关键知识点。 阅读详情
AI摘要

本文详细介绍了如何利用STM32的普通IO和定时器,模拟实现9600波特率的串口通信,包括发送和接收原理,以及关键代码片段。适合学习者理解串口协议并实践基础硬件编程。

1.引言

本次实验的原因是因为最近接触到一款单片机,只有一个串口,但项目中要用到至少2个串口,所以一个串口好少啊,没办法,只能另寻出路,通过普通IO来模拟usart串口了。然后,经过一番资料搜索,代码搬运,终于成功,组合出一个io模拟串口的模块,该模块成功实现了9600-8-N的串口数据收发,为方便记忆以及学习,特此记录!

 

2.普通IO模拟串口原理

普通io模拟串口,也需要严格的遵循串口协议规则,具体的规则可百度一下。

当波特率是115200时,发送1bit数据需要 1/115200 = 8.68us;所以,根据协议每一位数据传输电平持续8.68us

当波特率是9600时,发送1bit数据需要 1/9600= 104us;所以,根据协议每一位数据传输电平持续104us

这里要注意,在115200的时候,因为stm32默认的delay_us的不精准,会导致模拟串口的数据收发错误,在此可以建议用定时器延时,以达到精准操作。

在接收的地方,通过中断来触发启动,然后启动一个定时器,根据波特率的电平持续时间,在电平的中间去检测,是高电平还是低电平,从而确定bit的值。所以,检测到下降沿后延时一下,再启动定时器,定时去判断IO的高低。

STM32 IO口模拟串口通讯 - ziye334 - ziye334的博客

 

3.代码实现 

本次使用stm32f103c8t6实验板,所用到的硬件资源如下:

1.GPIO两个

2.外部中断1路

3.定时器两个

io模拟串口头文件:iousart.h

#ifndef __IOUSART_H_
#define	__IOUSART_H_

#include "stm32f10x.h"

//对应波特率的1个电平持续时间
//(1/9600) = 104us
#define IO_USART_SENDDELAY_TIME 	104	

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,
};


void iouart1_SendByte(uint8_t uByte);
void iouart1_init(void);

#endif /* __LED_H */

io模拟串口源文件:iousart.c

#include <stdio.h>
#include "iousart.h"
#include "delay.h"

//////////////////////////////////////////
//io模拟串口-9600-8-N
//定时器2+定时器4+外部中断1路+2个GPIO
//////////////////////////////////////////
u8 recvData = 0;
u8 recvStat = COM_STOP_BIT;
//////////////////////////////////////////
//用以模拟串口延时
void TIM2_Int_Init(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

  	TIM_TimeBaseStructure.TIM_Period = 65535 - 1;
  	TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;	//设置了用来作为 TIM3 时钟频率除数的预分频值
 	TIM_TimeBaseStructure.TIM_ClockDivision = 0;	//时钟分割为0
  	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM3 向上计数模式

  	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
	TIM_Cmd(TIM2,ENABLE);
}

//用以模拟io串口数据接收计时
void TIM4_Int_Init(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

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

	//定时器TIM4初始化
	TIM_TimeBaseStructure.TIM_Period = IO_USART_SENDDELAY_TIME; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; //设置用来作为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 = 3;  //先占优先级1级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //从优先级1级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器			 
}

//模拟串口1初始化
void iouart1_init(void)	
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	EXTI_InitTypeDef EXTI_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB,PC端口时钟 

	//SoftWare Serial TXD
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;	    
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz	 
	GPIO_Init(GPIOB, &GPIO_InitStructure);	  				
	GPIO_SetBits(GPIOB, GPIO_Pin_8); 						

	 
	//SoftWare Serial RXD
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		
	GPIO_Init(GPIOB, &GPIO_InitStructure);	 

	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9);
	EXTI_InitStruct.EXTI_Line = EXTI_Line9;
	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= EXTI9_5_IRQn ; 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;  
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;  
	NVIC_Init(&NVIC_InitStructure);  

	TIM2_Int_Init();
	TIM4_Int_Init();
}


void iouart1_TXD(uint8_t option)
{
	if(1 == option)
	{
		GPIO_SetBits(GPIOB, GPIO_Pin_8);
	}
	else
	{
		GPIO_ResetBits(GPIOB, GPIO_Pin_8);	
	}
}

uint8_t iouart1_RXD(void)
{
	return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_9);
}

/***************************************
*	函 数 名: Delay_Ms
*	功能说明: 延时
*	形    参:nTime,单位为uS
*	返 回 值: 无
****************************************/
void iouart1_delayUs(volatile u32 nTime)
{ 

	u16 tmp;

	tmp = TIM_GetCounter(TIM2);		//获得 TIM3 计数器的值
	
	if(tmp + nTime <= 65535)
		while( (TIM_GetCounter(TIM2) - tmp) < nTime );
	else
	{
		TIM_SetCounter(TIM2, 0);//设置 TIM3 计数器寄存器值为0
		while( TIM_GetCounter(TIM2) < nTime );
	}
}

/*****************************************
*	函 数 名: iouart1_SendByte
*	功能说明: 模拟串口发送一字节数据
*	形    参:无
*	返 回 值: 无
******************************************/
void iouart1_SendByte(u8 datatoSend)
{
	u8 i, tmp;

	// 开始位
	iouart1_TXD(0);	//将TXD的引脚的电平置低
	iouart1_delayUs(IO_USART_SENDDELAY_TIME);	

	for(i = 0; i < 8; i++)
	{
		tmp	= (datatoSend >> i) & 0x01;

		if(tmp == 0)
		{
			iouart1_TXD(0);
			iouart1_delayUs(IO_USART_SENDDELAY_TIME);	//0		
		}
		else
		{
			iouart1_TXD(1);
			iouart1_delayUs(IO_USART_SENDDELAY_TIME);	//1		
		}
	}
	
	// 结束位
	iouart1_TXD(1);//将TXD的引脚的电平置高
	iouart1_delayUs(IO_USART_SENDDELAY_TIME);	
}


void EXTI9_5_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line9) != RESET)
	{
		if(iouart1_RXD() == 0) 
		{
			if(recvStat == COM_STOP_BIT)
			{
				recvStat = COM_START_BIT;
				//这里要延时一下
				iouart1_delayUs(50);
				TIM_Cmd(TIM4, ENABLE);
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line9);
	}
}


void TIM4_IRQHandler(void)
{  
	if(TIM_GetITStatus(TIM4, TIM_FLAG_Update) != RESET)
	{
		TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);	
		 recvStat++;
		if(recvStat == COM_STOP_BIT)
		{
			TIM_Cmd(TIM4, DISABLE);
			//到这里就接收到完整的一个字节数据
			recvData = recvData;
			
			
			return;
		}
		if(iouart1_RXD())
		{
			recvData |= (1 << (recvStat - 1));
		}else{
			recvData &= ~(1 << (recvStat - 1));
		}	
  }		
}

以上代码,测试过是可以的。over!

 

GPIO模拟串口TX与RX,波特率115200 使用单片机的GPIO口去模拟串口的TX与RX进行数据的发送和接收处理,波特率可使用115200 阅读详情

相关推荐

【基于STM32的串口通信----用软件模拟】

基于STM32的串口通信----用软件模拟

嵌入式学习之路 7461

使用GPIO来模拟UART

本文使用32的两个IO口来模拟串口,可以缓解芯片串口外设紧张的问题。

weixin_56646002的博客 8910

期末速成STM32复习

总线基址和外设基址/*外设基址*/Block2/*总线基址*//*GPIO外设基址*//*寄存器基址,以GPIOB为例*//*外设声明*//*引脚定义*/#define GPIO_Pin_0 ((uint16_t)0x0001)选择pin0(1

m0_73264516的博客 2004

STM32之IO模拟串口篇

硬件基于stm32f767igt6水星板开发;软件使用了RT-Thread实时操作系统,两个空闲IO口。PE2用作串口TX端,搭配一个定时器控制发送时序;PE3用作串口RX端,并使能外部中断,触发外部中断则开启一个接收定时器。GitHub代码下载地址空闲位当uart处于空闲状态(线路没有数据传输)时,TX、RX线都处于高电平状态(逻辑“1”),一般需要把相应IO口配置为上拉。起始位由高电平跳变为低电平,且持续一个位宽度,表示触发起始信号。数据位发送状态分为空闲和发送中;...

Qurry的博客 6875

STM32F411固件库开发实战:USART多引脚映射与重定向配置详解

本文详细解析了STM32F411固件库开发中USART多引脚映射与重定向配置的实战要点。文章以UFQFPN48封装为例,澄清了芯片封装对实际可用串口数量的影响,并逐步讲解了从GPIO复用配置、USART参数设置到printf/scanf重定向的完整流程,提供了灵活的引脚重映射方案和实用的调试避坑指南,帮助开发者高效配置串口通信。

weixin_29183637的博客 241

基于STM32的姿态传感器电路设计与使用

文章目录引言程序设计描述编写串口通信的驱动程序编写系统延时程序对于MPU6050姿态传感器进行初始化编写定时中断程序电路设计描述具体设计分析代码部分详细说明设计实现效果MPU6050内部自带DMP解算姿态角 引言 设计采用的芯片是STM32F103C8T6,采用的传感器是MPU6050姿态传感器,自主进行电路设计,通过串口传输,完成MPU6050姿态传感器原始数据的读取与显示,以及依靠其内部的DMP模块对原始传感器数据进行滤波、融合处理,得到解算后的姿态角,再通过串口通信的方式,将数据输出到电脑上位机上。

共同进步! 1万+

STM32 普通IO模拟串口UART(含代码示例-已编译验证)

点击下载-STM32 普通IO模拟串口UART(完整代码-已编译验证)欢迎纠正,(づ ̄3 ̄)づ╭❤~ღ( ´・ᴗ・` ) 比心。

小肥丸 5226

STM32基础篇:引脚 × 复用 × 重映射

IO复用:指IO引脚身兼数职的现象。对于STM32芯片的一些引脚,其不仅能被GPIO模块直接控制(当做普通IO引脚使用),还能被其他模块使用。名称通用复用PA9普通IOPA10普通IO以PA9和PA10为例,除了被GOIO使用,用于输出或检测高低电平外,还可以被串口使用,当做串口的发送引脚或接收引脚来使用;也可以被定时使用,作为定时器的通道3和4来使用。

qq_74114817的博客 1830

STM32 DMA USART PROG

这篇文章主要介绍了基于STM32的DMA(直接内存访问)数据传输实验。代码实现了通过DMA通道将内存数据发送到串口外设的功能,包含DMA初始化配置、数据传输控制以及进度显示等关键部分。实验通过按键触发DMA传输,利用LCD实时显示传输进度,并在传输完成后提示完成状态。核心函数包括DMA通道配置(MYDMA_Config)和传输使能(MYDMA_Enable),展示了如何通过DMA实现高效数据传输而不占用CPU资源。

czhaii的专栏 381

STM32F401CCU6三链路实战:USB CDC、USART与IIC OLED联调指南

嵌入式开发中,USB通信、串口收发与IIC显示是常见外设接口。USB CDC通过批量传输端点实现虚拟串口,需正确配置时钟树与描述符;USART依赖波特率与中断管理;IIC则基于地址寻址与显存映射驱动OLED。掌握这些原理可提升多外设协同能力,广泛应用于调试交互、数据可视化等场景。本文以STM32F401CCU6为例,详解USB CDC、USART与IIC OLED的配置与联调,助你避开常见陷阱。

weixin_42520573的博客 231

HK32F030/STM32F030 IO口模拟串口:软串口收发实现与避坑指南

在嵌入式开发中,GPIO模拟串口(软串口)是一种解决硬件外设不足的通用技术。其原理是通过精确延时控制GPIO翻转,模拟异步串行通信的起始位、数据位和停止位,实现波特率可配置的串口收发。该技术不依赖硬件USART,可灵活映射到任意IO,适用于传感器扩展、调试口复用等场景。本文以HK32F030和STM32F030为例,详细讲解软串口的延时计算、发送与接收代码实现、中断处理及常见问题排查,并给出可配置模块与回环自测方法,帮助开发者快速移植并稳定运行。

weixin_33314040的博客 191

STM32端口复用和重映射

参考正点原子视频 端口复用 STM32有很多内置外设,这些外设的外部引脚都是与GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做复用 简单来说就是 咱们的板子上有很多的引脚,这些引脚本来是你作为普通的输入输出使用的(就是你自己这是这个引脚的功能),但是咱们芯片的内置外设也需要与外界进行信息交流啊啊,但是引脚就这么多(比如咱们mini板子只有51个外接引脚),如果每个内置外设都要占用引脚的话,普通的引脚就不够用了,所以这个时候伟大的人类想到

xuexiwd的博客 4334

HK32F030 IO口模拟串口收发与STM32F030兼容性实战

在嵌入式开发中,硬件串口资源不足时,GPIO模拟串口成为常见解决方案。其原理是通过精确控制IO口翻转时序,模拟异步串口协议,实现数据收发。技术价值在于节省硬件成本,提升设计灵活性,尤其适用于HK32F030等国产MCU替换STM32F030的场景。典型应用包括多传感器通信、调试接口扩展等。然而,IO模拟串口对时序要求苛刻,需处理波特率校准、中断响应延迟等挑战。本文深入探讨HK32F030与STM32F030在GPIO翻转速度、中断向量表等方面的差异,提供参数配置、避坑指南及可复用模块,帮助工程师实现稳定兼容

weixin_26742753的博客 267

STM32双串口同时工作实战:配置、中断接收与调试技巧

串口是嵌入式系统中最基础也最常用的通信方式,但当设备需要同时对接蓝牙、GPS等多个模块时,单串口往往捉襟见肘。此时,双串口技术成为解决多设备并发通信的关键。STM32F103系列内部集成了多个独立的USART外设,通过合理的引脚复用、时钟配置和中断机制,可以实现真正意义上的数据通道分离,互不干扰。本文基于HAL库与STM32CubeMX,从CubeMX图形化配置讲起,深入拆解双串口中断接收的代码实现,包括按行接收、环形缓冲区以及发送封装,并总结双串口调试中常见的“收不到数据”“数据串线”“偶发丢失”等问题的

weixin_34239592的博客 574

stm32f103zet6普通IO口模拟串口

由于STM32串口有限,于是试着将普通IO口模拟成串口使用,本来想着挺难,后来只要搞清楚串口的时序其实就很简单了,测试成功,附上MDK工程。

STM32(普通IO 模拟串口).rar_IO模拟fsmc_IO模拟串口_STM32 IO模拟_io 串口_nonegem

利用STm32普通IO口实现串口功能,可以弥补串口不足的缺点

利用STM32驱动串口字符型LCD:操作指南(含代码)

详解如何使用STM32控制串口字符型lcd,提供清晰的接线方式与实用代码示例,帮助开发者快速实现屏幕通信与数据展示,提升嵌入式项目交互体验。

weixin_33298352的博客 285

STM32 普通IO口 模拟串口

这两天一直在调试用普通IO口来承担串口的角色,再次做个笔记。当然广泛参考广大网友的代码在此感谢网友。首先串口的最最最基本的数据格式是由10位数据组成,注意是最最最基本的当然要有些带各种校验的那些暂时不考虑毕竟要先会走才能飞嘛,首先,第一位开始位,其次是八个数据位,然后一个停止位,数据位的时间长度由你的波特率决定的,我模拟的串口最实现了115200波特率当然偶尔有错位,这个就是接下来校验的工作了。

上一篇: 47 stm32f0 IAP跳转APP后中断无法进入
下一篇: 49 QT打包应用程序
Chasing_Chasing
博客等级 码龄10年 233粉丝 87原创
评论 18
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值