STM32 IO口模拟串口

2023-05-16

红叶何时落水

什么是串口呢?简单来说它是一种通信协议;

串口是一个泛称,UART、TTL、RS232、RS485都遵循类似的通信时序协议,因此都被通称为串口。

而对于单片机来说,串口通信遵守TTL电平标准

高电平 1 2.4V~5V

低电平 0 0V~0.5V

它可以实现两个设备之间的通信,一般来说,它是全双工通信,需要接三根线。

 便可以实现双向通信

既然他是一种通信协议,那么它有哪些协议呢?

 

1.波特率

我们知道在二进制中只有01两种状态,那么我们的发送设备发送一串二进制数00010110,之后,

接受设备接收到了这一段波形,那么该如何将其中的信息解析出来呢?

在串口中,波特率就是用来约束两者之间的协议之一。比如说,我发送了一段0和1构成的波形,这段波形我希望他有8个二进制的信息,那么我让每一个二进制信息都保持一秒钟,然后在发生变化。也就是说,00010110是一段时长为8秒的波形。前三秒为低电平,代表了000,4秒为高电平,代表了1,后面类似。

我希望这段波形中有八个信息。那么如果我想让别人知道其中的信息,那么我就得告诉别人我这段信息中,每一秒代表一个信息。那么别人在获取我这段时长为8秒的波形后,就会一秒一秒的数八次,依次找出每个二进制信息。

那么,每一秒有一个信息,这就是波特率。1秒一个信息,波特率为1;1秒9600个信息,波特率为9600;

2.起始信号与终止信号

那么,我们如果想要连续发好多个信号,我们不能把所有信息连到一起发送出去,就像我们写英语作文,不能把所有单词的字母挨得特别紧,一点空也不留。Iloveyou 与 I love you; 效果上来看后一个更好。而起始信号和终止信号就像单词之间的空格。

我们规定起始信号就是一个下降沿,即由高电平下降到低电平,后面的八位数据就是一个单词。八位数据写完后,我们将电平变为高电平,告诉别人这个单词结束了。这个高电平可由0.5、1、1.5或2个逻辑1的数据位表示,只要双方约定一致即可。

3.数据大小

通常我们规定你一次只能发送八个二进制数据,别人也只数八个,多出来的就不数了。

4.数据校验

我们如何保证我们将这段信息发送出去后,信息在传输的路上没有发生变化呢?

由于我们的信息中只有1和0两种状态,那么这就意味着1的个数可以是0~8,不是奇数就是偶数。

那么,我们可以选择奇校验 当这八个数据里由0或2或4...个1时,我们设置校验位为1,那么这九个数据里就有奇数个1。如果八个数据里1的个数本来就为奇数个,那么,校验位设置为0.保证数据里1的个数为奇数。接受者数一数1的的个数,就大概知道数据有没有被破坏。

偶校验同理。当然,我们也可以选择不校验。(反正没啥用)

知道了这些,我们便可以利用单片机IO口来模拟一个usart

打字打不动了,直接放代码吧。写一点注释算了

#include "stm32f10x.h"                  // Device header
#include "Timer.h"
#include "Delay.h"
extern u8 data;
extern u8 re_data;
int flag3 = 0;
int flag2 = 0;
int flag = 0;
void GPIO_init() {//两个IO口初始化
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	PA0 = 1;
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;//PA1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //  
	GPIO_Init(GPIOA, &GPIO_InitStructure);//
	PA1 = 1;
}
void Timer_Init(u32 psc, u32 arr)//定时器2初始化,注意定时器2的主频为72M,原因去看时钟树
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = arr - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//通用定时器没有这个功能,高级定时器可以实现,2的64次方计数
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//这里的名字加上Handler,就是中断函数名
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	GPIO_init();
	TIM_Cmd(TIM2, ENABLE);//使能定时器
	
}

void send_data() {
		if(flag && flag < 10) {
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update);	

			PA0 = (data >> (flag - 1)) & 0x01;//发送八位数据
			flag++;
		} else if(!flag) {
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update);	
			PA0 = 0;//制造起始位
			flag++;
		} else {
			PA0 = 1;//制造结束位
			flag = 0;
			TIM_Cmd(TIM2, DISABLE);
			TIM_ITConfig(TIM2,TIM_IT_Update,DISABLE);
			Delay_ms(1);//这里要加一个延时,否者会连续发送两个字节
			EXTI->IMR |= EXTI_Line1;//开启外部中断
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update);	
		}
}

void recive_data() {
		int i = 0;
		int counter = 0;
		if(flag2 && flag2 < 17) {
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update);	
			for(i = 0; i < 9; i++) 
				counter += GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);//获取引脚口状态
			re_data = (!(flag2 % 2) && flag2 < 16) ? ((counter > 3) ? (re_data|0x80)>>1 : re_data >> 1) : (flag2 == 16 && counter > 3) ? (re_data|0x80) : re_data;//通过位操作,串行八位数据
			flag2++;
		} else if(!flag2) {
			re_data = 0;
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update);	
			for(i = 0; i < 3; i++) {
				if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)) {//判断起始位
					TIM_Cmd(TIM2, DISABLE);
					EXTI->IMR |= EXTI_Line1;
					break;
				}
			} 
			
			if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1))
				flag2++;
		} else {
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update);	
			flag2 = 0;	
			flag3 = 1;
			PA1 = 1;
			TIM2->ARR = 750;		//寄存器改变定时器频率				
		}
}


void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		if(!flag3)
			recive_data();
		else 
			send_data();
	}
}


void EXTIX_Init(void) {
 	  EXTI_InitTypeDef EXTI_InitStructure;
 	  NVIC_InitTypeDef NVIC_InitStructure;
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//ÍⲿÖжϣ¬ÐèҪʹÄÜAFIOʱÖÓ
	
    //GPIOA.1	  
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);

   	EXTI_InitStructure.EXTI_Line=EXTI_Line1;
  	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 = EXTI1_IRQn;			
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;					
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								
  	NVIC_Init(&NVIC_InitStructure);  	 

}
void EXTI1_IRQHandler(void)
{
	TIM2->ARR = 375;
	flag3 = 0;
	flag2 = 0;
	TIM_Cmd(TIM2, ENABLE);//使能定时器
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启定时器中断
	EXTI->IMR &= ~(EXTI_Line1);//寄存器关闭外部中断
	EXTI_ClearITPendingBit(EXTI_Line1);  
}

注意的点

寄存器操作改变引脚口状态,三种不同方式的优缺点

定时器主频

中断的开启与关闭

数据的|与&以及<<操作

寄存器改变定时器频率

定时器重复技术计数功能

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32 IO口模拟串口 的相关文章

  • git仓库与vscode关联

    git仓库与vscode关联 git安装完后 xff0c 会提示输入用户信息 a 设置用户名 xff1a git config global user name 39 你再github上注册的用户名 39 b 设置用户邮箱 xff1a gi
  • python修改全局变量

    span class token comment 全局变量 span num span class token operator 61 span span class token number 10 span span class toke
  • python函数不能修改全局变量

    span class token comment 全局变量 span num span class token operator 61 span span class token number 10 span span class toke
  • FreeRTOS笔记(六)互斥量mutex

    概念 互斥量是二进制信号量的一个变种 xff0c 开启互斥量需要在头文件FreeRTOSConfig h 设置configUSE MUTEXES 为1 互斥量和信号量的主要区别如下 互斥量用于保护资源时必须要被返还 信号量用于数据同步时不需
  • 完爆面试官!spring可能带来的一个深坑

    4步套路 xff0c 解决动态规划问题 1 确定问题状态 提炼最后一步的问题转化 2 转移方程 xff0c 把问题方程化 3 按照实际逻辑设置初始条件和边界情况 4 确定计算顺序并求解 结合实例感受下 xff1a 你有三种硬币 xff0c
  • 树莓派Raspberry Pi 2B在Kali上使用TightVNCServer灰屏

    1 将 root vnc xstartup改为 span class token shebang important bin sh span unset SESSION MANAGER unset DBUS SESSION BUS ADDR
  • STM32——UCOSIII 简介

    目录 UCOSIII简介 UCOSIII中的任务 组成 任务堆栈 任务控制块 任务函数 任务函数模板 UCOSIII系统任务 组成 空闲任务 时钟节拍任务 统计任务 定时任务 中断服务管理任务 UCOSIII任务状态 组成及状态概念 UCO
  • ARM —— 寄存器的封装

    目录 SFR 直接一对一封装 结构体封装寄存器 SFR 全称 xff1a 特殊功能寄存器 xff08 Special Function Register xff09 作用 xff1a 用于 控制片内外设 xff0c 存放 相应功能部件的 控
  • Ubuntu给Pix2.4.8刷Ardupilot固件

    全文基于waf编译器使用 waf命令 xff0c APM官网对于waf的使用描述 xff1a https github com ArduPilot ardupilot blob master BUILD md 前提 xff1a 已经在ubu
  • 程序猿面试经验总结(经验篇)

    开篇序 金九银十大家都知道吧 xff0c 的确九十月份都是跳槽旺季与招聘旺季 xff0c 无论是找工作的 招聘的单位都是特别特别的多 xff0c 多的你有时候看都看不过来 xff0c 以至于让你有时候很难选择 xff0c 其实选择对应自己的
  • STM32 HAL库和标准库的原理区别

    STM32 HAL库和标准库的原理区别 HAL简介 HAL库 xff0c HAL是Hardware Abstraction Layer的缩写 xff0c 中文名称是 xff1a 硬件抽象层 xff0c 是st公司为了更方便地进行stm32之
  • 【超详细~】js的三大定时器:setTimeout、setInterval、requestAnimationFrame

    setTimeout xff08 表达式 xff0c 时间 xff09 61 gt 是指延迟指定时间后才调用函数 xff0c 调用次数仅一次 xff1b setInterval xff08 表达式 xff0c 时间 xff09 61 gt
  • FreeRTOS的学习(一)——实时操作系统和多任务的介绍

    目录 1 初识FreeRTOS 2 FreeRTOS 任务的状态 3 FreeRTOS 的任务 任务的创建和删除 1 xTaskCreate xff1a 使用静态的方法创建一个任务 2 xTaskCreateStatic xff1a 使用静
  • FreeRTOS的学习(四)—— 信号量之间的优先级翻转问题和互斥信号量

    目录 优先级翻转现象 什么是优先级翻转 互斥信号量 1 互斥信号量简介 2 创建互斥信号量 1 函数 xSemaphoreCreateMutex 2 函数 xSemaphoreCreateMutexStatic 3 释放互斥信号量 4 获取
  • STC15单片机 固定翼无人机/航模 飞控程序

    stc15单片机 固定翼无人机 飞控程序 硬件 stcf2k60s22 4g无线通信模块nrf24l01mg90s数字舵机摇杆无刷电机电调 用到的单片机资源 pwmad转换 程序结构 利用ADC转换读取摇杆值将摇杆数值转换为16进制通过2
  • ESP8266 WIFI 模块使用说明

    ESP8266是ai thinker公司推出的一款无线WIFI模块 xff0c 可以通过配置 xff0c 和单片机上的串口进行通信 xff0c 利用WIFI传输数据 1 AT指令简介 同许多通信模块一样 xff0c 我们需要对WIFI模块利
  • Ubuntu18更新软件源、安装python3.8和安装pip

    目录 一 更新软件源二 安装python3 8三 安装pip 一 更新软件源 1 xff09 首先 xff0c 打开sources list文件 sudo vim etc apt sources list 若没有vim xff0c 则需要进
  • Python 迭代器 与 异常处理

    文章目录 迭代介绍可迭代对象迭代器对象迭代器对象取值 方法的简写for循环内部原理异常处理异常的分类异常的类型异常的处理try except 语句try except else 语句try except finally 语句try exce
  • linux系统进程 exec函数族的使用(fork之后)

    1 2 exec 函数族 头文件 unistd h 3 exec 函数可用在fork 函数之后 xff0c 直接执行一个程序而省略子进程复制父进程的资源 4 调用exec不会创建一个新的pid 5 6 path xff1a 可执行文件的路径
  • Collecting package metadata (current_repodata.json): failed的问题解决

    解决方法 1 删除 condarc文件 2 关闭VPN 3 https的锅 xff0c 详情看下面的博客 环境配置 Collecting package metadata current repodata json failed的问题解决

随机推荐

  • 作业1:CAN数据库配置(DBC)

    目录 一 首先如何创建dbc文件 二 创建 三 创建节点NODE 一 其它 xff08 共1题 xff0c 100分 xff09 1 其它 使用vector candb editor软件 xff0c 按给定的信号矩阵配置CAN xff0c
  • Visual Studio 2022 C++下载及配置

    下载地址 xff1a https visualstudio microsoft com zh hans vs 之后点击右下角的安装 xff1b 如果下载速度一直为0 xff0c 那么解决方法为 xff1a 修改电脑的DNS服务器地址为8 8
  • app测试和web测试的区别

    1 功能方面 xff1a 在流程和功能测试上是没有区别的 xff0c 系统测试和一些细节可能会不一样 那么我们就要先来了解 xff0c web和app的区别 xff1a web项目 xff0c 一般都是b s架构 xff0c 基于浏览器的
  • Java网络编程实现

    前言 计算机网路实现了多个网络终端的互联 xff0c 彼此之间能够进行数据交流 而网络应用程序就是在已连接的不同终端设备上运行的程序 xff0c 这些网络程序相互之间可以进行数据交互 网络程序的数据交互则依赖于TCP IP协议 xff0c
  • java对象转JSONObject、JSONObject转java对象及String转JSONObject

    JSONObject jo 61 JSONObject JSONObject toJSON javaBean Student stu 61 JSONObject parseObject jo Student class JSONObject
  • redis设置密码

    设置密码有两种方式 1 命令行设置密码 运行cmd切换到redis根目录 xff0c 先启动服务端 gt redis server exe 另开一个cmd切换到redis根目录 xff0c 启动客户端 gt redis cli exe h
  • 【Java】Java四舍五入保留1位小数、2位小数

    方法一 xff1a 使用字符串格式化实现四舍五入 支持float和double类型 double data 61 3 02 利用字符串格式化的方式实现四舍五入 保留1位小数 String result 61 String format 34
  • ROS: [xxx.launch] is neither a launch file in package

    在ROS执行launch文件的过程中 xff0c 我经常碰见这个问题 xff0c 比如最近在安装ARBOTIX仿真器的时候 sudo apt get install ros indigo arbotix rospack profile 安装
  • SpringBoot文件上传

    文件上传 Spring MVC对文件上传做了简化 xff0c 在Spring Boot中对此做了更进一步的简化 xff0c 文件上传更为方便 Java中的文件上传一共涉及两个组件 xff0c 一个是CommonsMultipartResol
  • Django-图书管理系统(含源码)

    前段时间翻文件发现了以前学习python和django时做的一个系统 xff0c 当时的想法是将这玩意做出来应付web开发大作业 课程设计作业甚至是毕设用的 xff0c 实际上也确实应付了课程设计 xff0c 功能虽然不算多 xff0c 但
  • Android Studio 实现登录注册-源代码 (连接MySql数据库)

    Android Studio 实现登录注册 源代码 xff08 连接MySql数据库 xff09 Android Studio 实现登录注册 源代码 二 xff08 Servlet 43 连接MySql数据库 xff09 Android S
  • MySQL8重置root账户密码图文教程

    root账户为MySQL的超级管理员用户 xff0c 拥有MySQL提供的所有权限 我们登录了root账户可以重置其它创建的所有用户的密码 xff0c 那么root账户的密码忘记了怎么办呢 xff1f 由于版本和安装环境的不同 xff0c
  • docker执行权限问题Got permission denied while trying to connect to the Docker daemon socket

    安装docker之后 xff0c 执行docker images等命令 xff0c 提示权限问题 xff0c 报错如下 xff1a Got permission denied while trying to connect to the D
  • @ServletComponentScan和@ComponentScan 注解

    springboot 项目启动类中我们经常见到这两个注解 64 ServletComponentScan和 64 ComponentScan 下面我们就言简意赅的介绍一下这两个注解的作用 一 64 ServletComponentScan
  • MySQL中使用序列-Sequence

    在Oracle数据库中若想要一个连续的自增的数据类型的值 xff0c 可以通过创建一个sequence来实现 而在MySQL数据库中并没有sequence 通常如果一个表只需要一个自增的列 xff0c 那么我们可以使用MySQL的auto
  • SpringMVC获取请求参数

    CSDN话题挑战赛第1期 活动详情地址 https marketing csdn net p bb5081d88a77db8d6ef45bb7b6ef3d7f 大家好 我是郭尕 SpringMVC获取请求参数 一 前言 二 通过控制器方法的
  • 搞定Redis(三)消息的发布、订阅和新增数据类型

    一 Redis的发布和订阅 1 什么是发布和订阅 Redis 发布订阅 pub sub 是一种消息通信模式 xff1a 发送者 pub 发送消息 xff0c 订阅者 sub 接收消息 Redis 客户端可以订阅任意数量的频道 2 Redis
  • 智能控制——模糊数学及控制

    一 模糊控制概况 模糊逻辑控制 Fuzzy Logic Control 简称模糊控制 Fuzzy Control xff0c 是以模糊集合论 模糊语言变量和模糊逻辑推理为基础的一种计算机数字控制技术 1965年 xff0c 美国的L A Z
  • CMakeList 将CPP代码编译成可执行文件示例

    一 本次编译 CMakeList中用到的语法详解 1 设置cmake 版本 xff1a cmake minimum required VERSION 3 5 2 set the project name project Agent Test
  • STM32 IO口模拟串口

    红叶何时落水 什么是串口呢 xff1f 简单来说它是一种通信协议 xff1b 串口是一个泛称 xff0c UART TTL RS232 RS485都遵循类似的通信时序协议 xff0c 因此都被通称为串口 而对于单片机来说 xff0c 串口通