草庐IT

STM32学习笔记 IO口模拟串口 (接收采用定时器方式)

Hard_Yao 2024-03-08 原文

一、概述

        硬件:基于STM32F407VET6编写

        软件:使用两个GPIO口,一个用作串口发送TX,一个用作串口接收RX,采用的是定时器模拟                     时序。

二、串口简介

        要模拟串口,首先肯定是需要了解串口的协议,根据协议来编写程序。

        

        UART的通信方式是由1个起始位,8个数据位,包含一个奇偶校验位,和结束位构成 。在本次的设计中默认为波特率为9600,停止位为1位,8位数据位,无奇偶校验位。

        先介绍起始位,从高电平跳变为低电平,表示通信开始。再来简单介绍下波特率,单位时间内传送码元符号的个数,波特率9600,也就是1s内传送9600个bit,一个bit所需要的时间为 1000000us / 9600 = 104.166 us,也就是104us。

三、程序实现

3.1 头文件参数定义

#ifndef _S_UART_H_
#define _S_UART_H_

#include "sys.h"
#include "delay.h"


//定义通信波特率
#define BaudRate_9600	104		//1000000us/9600=104.1666	发送1个位所需要的时间


//GPIO定义
#define S_Uart_Tx PCout(3)    //模拟串口TX端
#define S_Uart_Rx PCin(4)     //模拟串口RX端


typedef enum{
	State_State = 0,        //起始状态
	State_transfer,         //传输状态
	State_Stop,	            //停止状态
}UartState;

#define SUartLength		200            //模拟串口缓冲区长度
extern u8 SUartCnt;                    //模拟串口缓冲区位置
extern u8 SUartBuff[SUartLength];      //模拟串口缓冲区


void S_Uart_GPIO_Init(void);
void S_Uart_Send_Buff(u8 *buff,u8 length);
u8 S_Uart_Rx_Handler(u8 *buf,u8 *length);

#endif

3.2 GPIO的初始化        

//模拟串口GPIO初始化
void S_Uart_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	EXTI_InitTypeDef EXTI_InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	//开时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);		
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);

	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;				//TX	GPIOC3
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;		//推挽输出
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;		
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;			//上拉
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOC, &GPIO_InitStruct);
	S_Uart_Tx = 1;

	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;				//RX	GPIOC4
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;			//上拉输入
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOC,&GPIO_InitStruct);	
}

3.3 模拟串口发送        

//模拟串口发送一个字节
void S_Uart_One_Tx(u8 Data)
{
	u8 i;

	S_Uart_Tx=0;		//起始位
	delay_us(BaudRate_9600);

	for(i=0; i<8; i++)
	{
		if(Data & 0x01)			//串口协议 先发LSB
			S_Uart_Tx = 1;
		else 
			S_Uart_Tx = 0;

		delay_us(BaudRate_9600);
		Data >>= 1;
	}
	
	S_Uart_Tx = 1;
	delay_us(BaudRate_9600);
}

//模拟串口发送数据
void S_Uart_Send_Buff(u8 *buff,u8 length)
{
	for(u8 i=0; i<length; i++)
	{
		S_Uart_One_Tx(buff[i]);	
	}
}

  3.4 模拟串口接收

        需要开启一个定时器中断,对模拟串口的电平进行监视,废话不多说,直接上代码。

void Time4_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef Tim4_TimeBaseStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);

	Tim4_TimeBaseStruct.TIM_Period = arr;			//重载值
	Tim4_TimeBaseStruct.TIM_Prescaler = psc;		//预分频值
	Tim4_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
	Tim4_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM4,&Tim4_TimeBaseStruct);
	
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
	TIM_Cmd(TIM4,ENABLE);
	
	NVIC_InitStruct.NVIC_IRQChannel = TIM4_IRQn;	
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStruct);
}

void TIM4_IRQHandler(void)
{		
	static u8 value=0;
	static UartState M_State = State_State;
	static u8 bit_cnt;
	
	if(TIM_GetFlagStatus(TIM4,TIM_FLAG_Update) != RESET)
	{
		TIM_ClearITPendingBit(TIM4,TIM_FLAG_Update);		
		
		if(S_Uart_Rx==0 && M_State == State_State)			//起始位 下降沿
		{
			M_State = State_transfer;			//接收到起始位,状态为传输中
			bit_cnt=0;
		}
		else if(M_State == State_transfer)
		{
			bit_cnt++;						
			if(S_Uart_Rx)
			{
				value |= (1<<(bit_cnt-1));
			}
			else 
			{
				value &= ~(1<<(bit_cnt-1));
			}
			if(bit_cnt >= 8)
				M_State = State_Stop;
		}
		else if(S_Uart_Rx && M_State==State_Stop)
		{
			bit_cnt=0;
			if(SUartCnt < SUartLength)
				SUartBuff[SUartCnt++] = value;			//存入缓冲区
			else 
				SUartCnt = 0;
			M_State = State_State;                      //状态回到起始状态,坐等下一帧数据
		}		
	}
}

//接收串口数据处理
u8 S_Uart_Rx_Handler(u8 *buf,u8 *length)
{
	*length = 0;
	if(SUartCnt > 0)			//模拟串口缓冲区不为空
	{
		*length = SUartCnt;			
		memcpy(buf,SUartBuff,*length);		//
		
		SUartCnt = 0;
	}	
	return *length;
}

3.5 主函数调用

int main(void)
{  
	u16 i;
	u8 len;
	//u8 buff[5]={0,1,2,3,4};
	u8 buff[200];
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);		//延时初始化 
	LED_Init();		  		//初始化与LED连接的硬件接口  

	S_Uart_GPIO_Init();	//模拟串口
	Time4_Init(BaudRate_9600,84-1);			//104us	
	
	while(1)
	{		
		
		if(S_Uart_Rx_Handler(buff,&len))			
		{
			S_Uart_Send_Buff(buff,len);					//将接收到的数据发送出去
		}
		
		
		if(++i %100 == 0)
		{
			i=0;
			LED0 = !LED0;
			//S_Uart_Send_Buff(buff,5);
		}
		delay_ms(10);
	}
}

四、程序执行效果

单片机将模拟串口接收到的数据用模拟串口发送出去。 

 好了,内容到此结束了,制作不易,如果对大家有帮助麻烦点个赞,谢谢!

有关STM32学习笔记 IO口模拟串口 (接收采用定时器方式)的更多相关文章

  1. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  4. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  5. ruby-on-rails - 如何使用 Rack 接收 JSON 对象 - 2

    我有一个非常简单的RubyRack服务器,例如:app=Proc.newdo|env|req=Rack::Request.new(env).paramspreq.inspect[200,{'Content-Type'=>'text/plain'},['Somebody']]endRack::Handler::Thin.run(app,:Port=>4001,:threaded=>true)每当我使用JSON对象向服务器发送POSTHTTP请求时:{"session":{"accountId":String,"callId":String,"from":Object,"headers":

  6. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  7. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  8. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  9. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

  10. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

随机推荐