基于STM32系列的模拟串口(非阻塞式)

2023-05-16

    STM32单片机一般少则3个串口,多则5个,而我这次的项目还偏偏5个硬件串口还是不够用.

至于不够用的原因,哎,是项目做到后面有定制,随便哪个串口都省不得,没得办法,只能另想法子咯~

    板子上有几个预留IO口,可以用来模拟串口. 模拟串口一般都选9600,速度最快试了也才19200,

所以限制还是较多的,一般不得以情况下才会用到.

    在写程序之前我也是参考了前辈们,觉得写的不咋地,照抄他的代码,那我的系统啥事情也

不要干了,就在那里delay吧!!! 模拟串口分收和发:收比较难,发送比较容易,那就先将接收这块吧。

    接收:

        有2种思路: 一种是第一个下降沿开始启动定时器,每个bit都去采样Rx电平;

                           从第一个边沿开始统计时间戳,接收完10个bit后再解析.

        我这边碰巧选择的是第二种方法,具体实现思路是:

        ① 启动定时器2用来做背景时间,定时器分频后的计数频率为1MHz,那么对于9600bps

            来说1个bit就是104.16us,我们取整数104就可以了;

        ② 再同时启动一个定时器3,定时时间为104*9.5,这个定时器中断发生的时候表示

            一个字节接收完毕了;

        

我们以0x37串口通讯时序图为例, ,传输的时候是LSB 1st, Rx引脚选择边沿触发, 第1次边沿到的时候

启动定时器2,同时启动定时器3, 第2个边沿的时候将定时器2的计数值存到数组里,此时是104,第3个

边沿触发的时候将其计数104*5计数值,同理将4,5,6三个边沿对应的TIM2值存到缓存里,最终

是 0,104,416,520,728,936,除以104变成0,1,4,5,7,9;

如何将0,1,4,5,7,9解析成0x37,看似简单的问题其实还是有点麻烦的~ 给个思路,先将0和9这样的

数字去掉,变成1,4,5,7;

然后从1开始数到8,跟数组里元素是否有匹配,如果匹配就将状态取反,没有就维持之前的状态:

    1有,2,3没有,4有,5有,6没有,7有,8没有-->11101100->倒序后就是0x37了;

再来分析几个处理后的值:4,5,7: 1,2,3没有,4有,5有,6没有,7有,8没有-->00010011->逆序后11001000->0xC8

1,2: 1有,2有,3,4,5,6,7,8没有->10000000->逆序后00000001->0x01

1,2,3,4,6,7,8:1有,2有,3有,4有,5没有,6有,7有,8有->10100101->逆序后10100101->0xA5

7,8:1,2,3,4,5,6没有,7有,8有->00000010->逆序后01000000->0x40

总结下如果第一个是1开头的那么得出的bit就是1,如果第一个是其他数字开头的,则 "1到其他数字" 之间用0来填充;

下面我们就根据总结出来的规律编写解析代码:

int process_byte(int nums)
{
	int i;
	u8 a=0;
	u8 byte=0;
	u8 new_array[8];
	memset(new_array,0,sizeof(new_array));
	
	for(i=0;i<=nums;i++) //1...7
	{		
		timerecode[i]+=ONE_BIT_TIME/2;
		timerecode[i]/=ONE_BIT_TIME;		
	}
	
	if(timerecode[nums]>=9)	//计算下标是从1开始的,并且去掉了9,所以剩下的也不多了
		nums--;
	
	if(nums<=0)	//全0特殊处理
		return 0;
	
	find_max=nums; //去掉第一个0
	/*	
	假设收到的是0,2,4,9
	经过处理后就剩下2,4
	将2放到new_array下标为1的地方,4放到下标为3的地方
	*/
	for(i=0;i<find_max;i++)
	{
		find_array[i]=timerecode[i+1];
		if(find_array[i]!=0)
		{
			new_array[find_array[i]-1]=find_array[i];
		}
	}
	
	/*
	然后在for(i=find_array[0]-1;i<8;i++)
	这个循环里找,如果对应的位有边沿改变,就改变a的值
	找不到就复制a的值,这里有点要注意,i循环是从高电平
	开始的
	*/
	for(i=find_array[0]-1;i<8;i++)
	{
		byte>>=1;
		if(i==new_array[i]-1)
			a=!a;		

		if(a)
			byte|=0x80;
	}
	
	return byte;
}

对于上图0x37的例子,当定时器3中断时,就调用process_byte(nums-1),其中nums=6,就

是边沿次数,来解析我们接收到的byte啦.

下面的截图是我用模拟串口编写的收发例子:



收发48个任意字节都正常,没有哪个是解析错的.

其他一些函数都比较简单,建议大家自己动手! 有感兴趣的,我会接着写

模拟中断发送!今天到此为止,希望各位喜欢~


发送部分:这部分完全依赖定时器7了,定时周期104us。

发送的原理很简单,我这里没有使用delay方式来发送,而是在定时器里实现发送的;

#define VIR_TXBUFF_SIZ	128
#define VIR_RXBUFF_SIZ	128

typedef struct {
//---------Rx------------
	u8 rxov;
	u8 rxlen;
	u8 rxbuff_idx;
	u8 rx_decode_flag;
	u8 RxBuff[VIR_RXBUFF_SIZ];//大小一定要是2的次方关系
	u8 RXREG;
//---------Tx------------
	u8 send_flag;
	u8 send_max;
	u8 send_cnt;
	u8 send_mode;	//0-阻塞式发送  1-中断发送(放到sendbuff里,指定send_max即可)
	u8 sendbuff[VIR_TXBUFF_SIZ];
	u8 TXREG;
	
}VIRTUAL_UART_t;
static u8 send_a_byte(u8 dat)
{
	if(VirtualUart.send_flag)
		return 1;
	
	VirtualUart.TXREG=dat;
	TIM7->CR1 |= TIM_CR1_CEN;
	Tx_Pin=0;
	VirtualUart.send_flag=1;
	
	return 0;
}

static void send_remain_byte(void)
{
	if(VirtualUart.send_cnt>=VirtualUart.send_max)
	{
		VirtualUart.send_flag=0;	//发送完毕
	}
	else
	{
		VirtualUart.TXREG=VirtualUart.sendbuff[VirtualUart.send_cnt++];
		Tx_Pin=0; 			//产生START信号
	}
}

//TIM7定时器里调用
static u8 tim_send_byte(void (*Callback)(void))
{
	static int sendidx=0;
	sendidx++;
	
	if(sendidx<=8)					//DATA 1,2,3...8
	{
		Tx_Pin=VirtualUart.TXREG&0x01;
		VirtualUart.TXREG>>=1;	
	} else if(sendidx==9) { 			//STOP
		Tx_Pin=1;
	} 
	else if(sendidx==10) {				//STOP的发送完毕了
		sendidx=0;
				
		if(Callback!=NULL)			//有多个字节要发送,调用回调函数继续发送下一个字节
			Callback();
		else
			VirtualUart.send_flag=0;	//已经发送完毕了
		
		return 0;
	}
	
	return 1;	//1-busy
}

//阻塞式发送
void vu_send_string(u8 *s)
{
	VirtualUart.send_mode=0;
	while(*s)
	{
		while(VirtualUart.send_flag!=0);
		send_a_byte(*s);
		s++;
	}
}
//阻塞式发送
void vu_send_len(u8 *s,int len)
{
	VirtualUart.send_mode=0;
	while(len--)
	{
		while(VirtualUart.send_flag!=0);
		send_a_byte(*s);
		s++;
	}
}
//中断发送
int vu_send_some_byte_noblock(int len)
{
	if(VirtualUart.send_flag)
		return -1;
	if(len>VIR_TXBUFF_SIZ)
		return -2;
	
	VirtualUart.send_mode=1;
	VirtualUart.TXREG=VirtualUart.sendbuff[0];
	TIM7->CR1 |= TIM_CR1_CEN;
	Tx_Pin=0;						//产生START信号
	VirtualUart.send_flag=1;
	VirtualUart.send_max=len;
	VirtualUart.send_cnt=1;
	return 0;
}


//主要发送部分代码就在这儿了
void TIM7_IRQHandler(void)
{
	TIM7->SR = (uint16_t)~TIM_IT_Update;
	
	if(VirtualUart.send_mode) {
		if(tim_send_byte(send_remain_byte)==0) {
			if(VirtualUart.send_flag==0)
				TIM7->CR1 &= ~TIM_CR1_CEN;
			TIM7->CNT=0;
		}
	}else {
		if(tim_send_byte(NULL)==0) {
			TIM7->CR1 &= ~TIM_CR1_CEN;
			TIM7->CNT=0;
		}
	}
}


        

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

基于STM32系列的模拟串口(非阻塞式) 的相关文章

  • 代码注释怎么写

    注释怎么写 注释的作用是什么 我认为注释最终作用无非就两个 1 和伪代码一样的作用 xff0c 为接下来要实现的功能写出一个指导性的算法思路 只是没有伪代码详细 但是也指出了完成此功能的大体算法思路 2 给看代码的人一个解释性说明 注意看代
  • 原始 socket 编程

    原始套接字简介 普通 socket 的权力和原始 socket 权力对比 1 原始 socket 可以和内核一样直接对所有层进行操作 除了物理层 可以更改 mac 更改 ip 更改端口 so dos 攻击就可以通过原始 socket 编程来
  • EventLoop execute() 怎么保证线程安全

    EventLoop 的 execute 是在 SingleThreadEventLoop 中实现的 64 Override public void execute Runnable task if task 61 61 null throw
  • EventLoop execute() 怎么保证线程安全

    EventLoop 的 execute 是在 SingleThreadEventLoop 中实现的 64 Override public void execute Runnable task if task 61 61 null throw
  • 关于 find grep xargs 命令总结

    1 定义 amp 区别 xff1a 1 find命令是根据文件的属性进行查找 xff0c 如文件名 xff0c 文件大小 xff0c 所有者 xff0c 所属组 xff0c 是否为空 xff0c 访问时间 xff0c 修改时间等 2 gre
  • ubuntu如何在当前文件夹下打开终端

    1 打开终端 xff0c 执行以下命令 xff1a sudo apt get install nautilus open terminal 2 在终端中执行下列命令 xff0c 重新加载文件管理器 nautilus q 3 执行以上指令后在
  • 解决libssl.so.1.0.0: 找不到的问题

    安装了多个版本的openssl后 xff0c 导致adb或svn无法使用 xff0c 出现以下错误 xff1a libssl so 1 0 0 no version information available 记录下最后的解决方法 xff0
  • JS对象—5.文档对象(document)

    document对象 一 span class token punctuation span 元数据 span class token number 1 span characterSet span class token operator
  • Keil5及芯片包的安装以及 Keil5打开Keil4工程编译出错解决方法

    一 温馨提示 1 安装路径不能带中文 xff0c 必须是英文路径 2 安装目录不能跟 51 的 KEIL 或者 KEIL4 冲突 xff0c 三者目录必须分开 3 KEIL5 的安装比起 KEIL4 多了一个步骤 xff0c 必须添加 MC
  • 上下文切换

    上下文切换是操作系统比较重要的一部分 xff0c 提到它 xff0c 我们第一时间想到的会是使用时间片轮转方式调度的os中涉及的进程之间上下文切换 再问还有没有其他 xff0c 也许会想到中断时 xff0c 上下文似乎也要切换 这两种切换是
  • Android系统 应用图标显示未读消息数(BadgeNumber) 桌面app图标的角标显示

    http www 51itong net android badgenumber 9789 html
  • smbclient介绍

    smbclient介绍 1 Smbclient介绍1 1 SMB 协议介绍 2 Smbclient命令使用技巧2 1 Smbclient 功能说明2 2 语法2 3 参数2 4 使用举例 1 Smbclient介绍 Smbclient sa
  • 关于void指针的地址问题

    如图 void指针指向其他类型的数据时对其地址加1 它可指向几乎任何数据 xff0c 且指针加1时相当于字符指针加1 xff0c 利用这一特性可将void指针作为形参类型 xff0c 可传入任意数据且对形参操作时可类比字符指针 xff0c
  • Ubuntu apt update无论使用什么源都出现类似的错误

    下面是清华源执行后的情况 Ign 1 https mirrors tuna tsinghua edu cn ubuntu jammy InRelease Ign 2 https mirrors tuna tsinghua edu cn ub
  • 从源码分析C++中forward完美转发和move移动语义的本质区别

    完美转发可以看做一种能够按照原来类型转发到另一个地方 xff08 函数 xff09 的方法 xff08 废话 xff09 咱不如直接上源代码 xff08 move h xff09 xff1a template lt typename Tp
  • 实现对单链表的倒置

    我们知道数组的倒置比较简单 xff0c 只需要知道数组的头 xff0c 和数组的尾 xff0c 将其数据互换 xff0c 再将第二个和倒数第二个互换 xff0c 一直这样操作下去 xff0c 数组就实现倒置了 那么单链表也可以通过这样的方法
  • 立创开源|18650锂电池四路充电器

    该工程为18650四路电池充电器 xff0c 支持DC Micro USB USB typec输入 xff0c 同时四路充电 充电芯片采用价格便宜的TP4056 xff0c 电池盒采用直插电池盒 每节电池充电电流为1A xff0c 由于电流
  • 无法连接上 archive.ubuntukylin.com:10006 (120.79.211.60),连接超时

    无法连接上 archive ubuntukylin com 10006 120 79 211 60 xff0c 连接超时解决方法 问题描述 执行sudo apt get update时出现如下错误 xff1a 错误 12 http arch
  • 【C++】模板与泛型编程

    泛型编程 泛型编程最初诞生于C 43 43 中 xff0c 由Alexander Stepanov 2 和David Musser 3 创立 目的是为了实现C 43 43 的STL xff08 标准模板库 xff09 其语言支持机制就是模板
  • LXC(Linux containers)常用命令介绍

    lxc version 用于显示系统LXC的版本号 xff08 可以通过此命令判断系统是否安装了lxc xff09 用法 xff1a lxc version 例如 lxc version lxc checkconfig 用于判断linux内

随机推荐

  • Docker 大势已去,Podman 万岁

    前言 郑重声明 xff1a 本文不是 Podman 的入门篇 xff0c 入门请阅读这篇文章 xff1a 再见 Docker xff0c 是时候拥抱下一代容器工具了 Podman 原来是 CRI O 项目的一部分 xff0c 后来被分离成一
  • 使用node开发一个解放双手的小工具《二》- vsCode插件包装

    背景 xff1a 此篇是上篇 使用node开发一个解放双手的小工具 的后续 xff0c 前边遗留了一些问题 xff0c 最近完善了一下 因为发现实际使用起来 xff0c 有很多的不方便 xff1a 使用上 xff0c 需要配置对应的根目录
  • 使用网络调试助手时,踩坑

    在使用网络调试助手时 xff0c 踩坑很多 xff1a 1 作为tcp的客户端 xff0c 或者服务端 xff0c 没有注意发送设置以ASCII还是HEX 导致目标环境接收解析数据异常出现段错误 2 xff1a 作为UDP作为客户端 xff
  • Win7 32 不能安装STM32 虚拟串口驱动解决方法

    1 对于一些精简过的系统 xff0c 无法安装虚拟串口 xff0c 是由于系统缺少mdmcpq inf和usbser sys文件所造成的 只需要将文件下载下来放在相应的文件夹下面就行了 文件对应的目录如下 xff1a mdmcpq inf在
  • gitee使用教程

    目录 版本控制 1 Gitee上注册账户 2 新建一个远程代码仓 xff0c 点击右上方的加号 3 修改远程仓为开源的 编辑 4 本地下载git 5 克隆远程仓到本地 xff0c 复制地址 6 创建代码并提交到远程仓库 7 将本地代码提交到
  • Telnet 接口的使用(一):Telnet的安装与开启

    什么是 Telnet 简介 telnet 用于远程登录 xff0c Telnet的工作方式为 服务器 客户端 方式 xff0c 它提供了从一台设备 xff08 Telnet客户端 xff09 远程登录到另一台设备 xff08 Telnet服
  • Android系统四层体系架构

    Android体系架构分为四层 xff1a 应用层 应用框架层 库层 内核层 xff08 盗用一张图 xff09 一 应用层 应用层包括手机上的所有APP xff0c 无论是系统自带的还是用户开发的 他们都是基于第二层应用框架层开发的 二
  • 芯片的开发板和评估板的区别

    评估版一般都是半导体生产厂家所提供的 xff0c 用于器件性能评估用 xff0c 大公司不用他来赚钱 xff1b 开发板大多说是通过板子赚钱的 xff0c 就是很多的网友工程师做的 xff1b 目标板是在开发产品的过程中 xff0c 相对于
  • 增量式pid+位置式PID(电机位置闭环控制)

    ps xff1a 2022更新 pid详细解释 一般很少用增量式 xff0c 都是用位置式 xff0c 下文增量式可以不看 本文分为几个部分 xff1a 1 编码器 2 定时器输入捕获 xff08 把定时器初始化为编码器模式 xff09 3
  • Vmware 扩容磁盘

    Vmware 扩容磁盘 第一步 xff0c 首先添加一块磁盘 xff0c 进行分区第二步 xff0c 创建物理卷加入组第三步 xff0c 进行扩容 目录第四步 xff0c 刷新逻辑卷 第一步 xff0c 首先添加一块磁盘 xff0c 进行分
  • 思科模拟器中的交换机使用方法

    思科模拟器中的交换机使用方法 思科模拟器中有较多种交换机型号 xff0c 这里只是简单的介绍一下二层交换机2960 xff0c 以及三层交换机中的3560 1 关于交换机的原理 xff1a 交换机是通过其保存的MAC地址表来进行工作的 xf
  • 计算机发展15件重大事件(图说)

    链接 xff1a https vision xitek com famous 201001 28 36559 html 一 1946年 xff0c 第一台电子计算机 埃尼阿克 问世 二 1976年 xff0c 苹果电脑Apple I现世 三
  • vsftp登录报530 Login incorrect无法登录问题解决

    vsftp登录报530 Login incorrect无法登录问题解决 今天在搭建FTP服务器的时候遇到了530 Login incorrect这个问题 通过修改配置文件解决了 总结了一下在搭建FTP服务器的时要注意的地方大概有下面这几点
  • Linux上SMB挂载提示mount: block device //xxx.xxx.xx.xx/xx is write-protected, mounting read-only时解决办法

    当在Linux上挂载SMB服务器时候有时会提示如下错误 xff1a root 64 test mount o username 61 lisi 192 168 23 32 smb test mount block device 192 16
  • Linux下的LAMP环境搭建时访问PHP页面时变成下载页面的原因

    在搭建LAMP环境的时候遇到了配置完PHP环境后测试访问PHP页面的时候却变成下载的情况 xff0c 主要的影响有一下两个方面 1 PHP的配置问题 2 主要原因是httpd conf配置文件内容出错大致有下面几个地方 xff1a Load
  • 服务器肉鸡/入侵被恶意利用的排查和优化方案

    排查方法 xff1a 1 账户方面 xff1a Windows xff1a xff08 1 xff09 检查服务器内是否有异常的账户 xff0c 查看下服务器内是否有非系统和用户本身创建的账户 xff0c 一般黑客创建的账户账户名 后会有
  • Opensuse如何安装桌面环境

    安装必须的范式 xff1a zypper install t pattern kde kde plasma 编辑 etc sysconfig displaymanager 文件并设定 DISPLAYMANAGER 61 kdm xff0c
  • 怎么用谷歌学术检索下载外文文献

    谷歌学术是一个可以免费搜索外文学术文章的搜索引擎 xff0c 包括了世界上绝大部分出版的学术期刊 xff0c 可广泛搜索学术文献 谷歌学术可了解有关某一领域的学术文献 xff1b 了解某一作者的著述 xff0c 并提供书目信息 xff08
  • 英文文献去哪里查找,8个超强英文文献查找网站建议收藏

    英文文献去哪里查找 xff1f 找对方向用对工具可大幅提升学习和研究效率 xff01 下面详细介绍8个查找英文文献非常好用的网站 一 文献党下载器 xff08 wxdown org xff09 xff1a 该网站几乎整合汇聚了所有文献数据库
  • 基于STM32系列的模拟串口(非阻塞式)

    STM32单片机一般少则3个串口 多则5个 而我这次的项目还偏偏5个硬件串口还是不够用 至于不够用的原因 哎 是项目做到后面有定制 随便哪个串口都省不得 没得办法 只能另想法子咯 板子上有几个预留IO口 可以用来模拟串口 模拟串口一般都选9