模拟串口LV2,解决硬件串口资源不足问题!!!!

模拟串口通信 2.0 版本!!

我在前面的文章里面有写了 虚拟串口通信,虽然说能用,但是用过的小伙伴都说 “好!”

优缺点:

先说一点,2.0版本并不适用于同硬件串口的所有场合,仅仅针对自己开发的电子垃圾的主从结构,比如自己的两块板子通信,或者硬件串口没得了又想追加串口不得已的情况。

优点:

1.可以设置任意波特率, 并且从机不可以识别任意波特率数据(只要主时钟频率高就行了)。
2.可以实现不定长度数据收发,并且用户能完成收发标志 1.0版本可不行。
3.减少资源消耗,同比1.0版本,在芯片内部外设资源上面有所减少。
4.同比1.0版本 大大提高收发数据稳定性。反正我实验没有丢过数据。

好下面说缺点:

缺点:

1. 2.0版本相对于1.0版本,硬件接线上面多出了1根时钟线(相当于 一个串口收发需要三根线,三根线,三根线,如果有多路虚拟串口,则每一路仅需要追加 2 根线,笑哭!!! )。
2. 因为是任意波特率所以无法和其他硬件串口做对接 如果非要接 那你就吧波特率规定到和硬件一样。
3. 串口通信点对点,懂得都懂。只不过用的IO模拟 ,有些引脚上面的驱动能力要强那么丢丢,一对多可试试。
好!!到这里有人就会问了:三根线,你走SPI啥!再不济你走I2C,I2C还少根线。你三根线还是模拟的数据玩个鸡毛!!
嗯?问的好!我竟无言以对,但是这是全双工通信,而且没有比较严格的主从关系,而且通信只能主机发起,不能从机发起。

好!!那又有人会问:那和SPI 有啥子区别啥,都是三线全双工通信。

好那就看下面:
在这里插入图片描述
这是一个数据收发的时序图,上面可以看到从主模块出来的TX是在时钟的下降沿发出通信起始位,然后在每个下降沿到达时改变信号状态,在时钟上升沿进行数据采集,而RX则是反过来,在上升沿发起通信并且修改电平状态,下降沿采集状态。所以在两数据通信的时候上升和下降沿都有意义。



首先是主控板 :

下面来看配置情况:

这里我定义的三个通用IO口
IO_CLK :时钟输出脚(GPIO)
IO_RX :接受输入脚(GPIO)
IO_TX :数据输出脚(GPIO)
开一个定时器 使能 更新溢出中断(定时时间自己设置一个就行)

在这里插入图片描述
在这里插入图片描述

以上就是基本的软件配置信息,这一块比较简单大家大概配置一下就行了,剩下的就是代码部分了

/* *****************************************************************
*Copyright (C), 2019-2023, TTT
*Filename:          M_File_Oled.c
*Author  :          ZY
*Created Date :     15/02/2023
*Abstract   :       处理全局参数
*History :
*V1.0.0   : Initial (by ZY,15/02/2023)
***************************************************************** */
#include "M_File_IOUart.h"//定义结构体
struct IOUart  IOUart_h_Struct={ .IO_UART_RecvStat = COM_STOP_BIT};
/*****************************************
* 函数名: IoUartSendByte
* 功能说明: 模拟串口发送1个字节数据
* 形参:Byte 要发送的字节
* 返回值: 无
******************************************/
void IoUartSendByte(uint8_t Byte)
{uint8_t tmp = 0;static uint8_t count = 0;static uint8_t Old_IO_UART_StartTXFlag  = 0;// 开始位if(IOUart_h_Struct.IO_UART_StartTXFlag1BIT != Old_IO_UART_StartTXFlag) {if(IOUart_h_Struct.IO_UART_StartTXFlag1BIT == 1){IO_UART_TXD(0); //将TXD的引脚的电平置低count = 0;}Old_IO_UART_StartTXFlag = IOUart_h_Struct.IO_UART_StartTXFlag1BIT;}else {if(count < 8)	{				tmp = (Byte >> count) & 0x01;if(tmp==0){IO_UART_TXD(0);}else{IO_UART_TXD(1);}count++;}else{IO_UART_TXD(1);//将TXD的引脚的电平拉高IOUart_h_Struct.IO_UART_StartTXFlag1BIT = 0;   //结束 发送数据Old_IO_UART_StartTXFlag = 0;count =0;}}   
}/*******************************************************************************
*Name :            IO_UART_SendData
*Syntax :          void  IO_UART_SendData(const char *Data,uint32_t size)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口发送字符串
********************************************************************************/
void  IO_UART_SendString(const char *String)
{for(int i=0; i<strlen(String); i++){IoUartSendByte(String[i]);}
}/*******************************************************************************
*Name :            IO_UART_SendData
*Syntax :          void  IO_UART_SendData(const char *Data,uint32_t size)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口发送数据
********************************************************************************/
void IO_UART_SendData(const char *Data,uint32_t size)
{if(IOUart_h_Struct.IO_UART_StartTXFLAGBuff == 0){IOUart_h_Struct.IO_Send_Size = size;memcpy(IOUart_h_Struct.IO_UART_TXBuff,Data,size);		IOUart_h_Struct.IO_UART_StartTXFLAGBuff = 1;}
}/*******************************************************************************
*Name :            IO_UART_ReciveData
*Syntax :          void  IO_UART_ReciveData(void)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口接收字符串
********************************************************************************/
void IO_UART_ReciveData(void)
{if(IOUart_h_Struct.IO_UART_StartRxFlag == 1){IOUart_h_Struct.IO_UART_RecvStat++;if(IOUart_h_Struct.IO_UART_RecvStat == COM_STOP_BIT){									//接收到完整的1个字节数据if(IOUart_h_Struct.IO_UART_RxNum < _IO_RX_LENGHT){IOUart_h_Struct.IO_UART_RxBuff[IOUart_h_Struct.IO_UART_RxNum++] = IOUart_h_Struct.IO_UART_RecvData;	//存入缓冲区}else{IOUart_h_Struct.IO_UART_RxNum = 0;										}IOUart_h_Struct.IO_UART_StartRxFlag = 0;							IOUart_h_Struct.IO_UART_RecvData = 0;return;}if(IO_UART_RXD)//读取接收引脚的状态{IOUart_h_Struct.IO_UART_RecvData |= (1 << (IOUart_h_Struct.IO_UART_RecvStat - 1));}else{IOUart_h_Struct.IO_UART_RecvData &= ~(1 << (IOUart_h_Struct.IO_UART_RecvStat - 1));}}
}/*******************************************************************************
*Name :            IO_UART_Send_Buff
*Syntax :          void IO_UART_Send_Buff(void)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口发送数据处理
********************************************************************************/
void IO_UART_Send_Buff(void)
{static uint8_t OLD_IO_UART_StartTXFLAGBuff = 0;static uint8_t OLD_Send_Count = 0;if(OLD_IO_UART_StartTXFLAGBuff!= IOUart_h_Struct.IO_UART_StartTXFLAGBuff){		 if(IOUart_h_Struct.IO_UART_StartTXFLAGBuff == 1){OLD_Send_Count = 0;			  IOUart_h_Struct.IO_UART_StartTXFlag1BIT = 1; //发送数据}		 OLD_IO_UART_StartTXFLAGBuff = IOUart_h_Struct.IO_UART_StartTXFLAGBuff;		}else{if(IOUart_h_Struct.IO_UART_StartTXFLAGBuff == 1){if(OLD_Send_Count<IOUart_h_Struct.IO_Send_Size){IoUartSendByte(IOUart_h_Struct.IO_UART_TXBuff[OLD_Send_Count]);if(IOUart_h_Struct.IO_UART_StartTXFlag1BIT == 0){						OLD_Send_Count++;if(OLD_Send_Count<IOUart_h_Struct.IO_Send_Size){IOUart_h_Struct.IO_UART_StartTXFlag1BIT = 1; //发送数据}}else{}		 }else{IOUart_h_Struct.IO_UART_StartTXFLAGBuff = 0;IOUart_h_Struct.IO_Send_Size =0 ;	}}}
}/*******************************************************************************
*Name :            IO_UART_Time_CountDown_Deal
*Syntax :          void IO_UART_Time_CountDown_Deal(void)
*Parameters(in) :
*Parameters(out) : 无
*Return value :    无
*Description :     IO串口倒计时处理
********************************************************************************/
void IO_UART_Time_CountDown_Deal(void)
{if(IOUart_h_Struct.IO_UART_RxTimeOut>0){if(IOUart_h_Struct.IO_UART_RxTimeOut == 1){IOUart_h_Struct.IO_UART_RxTimeOut --;IOUart_h_Struct.IO_UART_RxOK = 1;     //一条数据接收完成IOUart_h_Struct.IO_UART_RxNum = 0;//完整接受到一条数据//TsUserM_h_Flag.e_u_UartAppceptFlag[0] = ON;return ;}IOUart_h_Struct.IO_UART_RxTimeOut--;	}else{}
}
/* *****************************************************************
*Copyright (C), 2019-2023, TTT
*Filename:          M_File_IOUart.h
*Author  :          ZY
*Created Date :     15/02/2023
*Abstract   :       处理全局参数
*History :
*V1.0.0   : Initial (by ZY,15/02/2023)
***************************************************************** */
#ifndef __M_FILE_IOUart_H__
#define __M_FILE_IOUart_H__//#include "M_File_Flag.h"
/*自己定义导入的.h 文件 ******************************************************/#define _IO_RX_LENGHT        200       																//接收数据长度
#define _IO_RX_TIMECOUNTDOWN  15																	//接收数据超时倒计时
/*
*@串口接收字节
*/
enum 
{COM_START_BIT = 0,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,
};struct IOUart{uint8_t 	IO_UART_RecvData; 							//接收数据uint8_t 	IO_UART_RecvStat;  							//接收状态		uint16_t    IO_UART_RxNum;								//接收数据长度uint8_t 	IO_UART_RxBuff[_IO_RX_LENGHT];				//模拟串口接收数据缓冲区uint8_t     IO_UART_TXBuff[_IO_RX_LENGHT];				//模拟串口发送数据缓冲区uint32_t 	IO_UART_RxTimeOut;            				//模拟串口接收超时计数uint8_t 	IO_UART_RxOK;               				//数据接收完成标志uint8_t 	IO_UART_StartRxFlag;						//开始接收标志,1开始接收,0不接收			uint8_t   IO_UART_StartTXFlag1BIT;						//开始发送标志,1开始发送,0不发送uint8_t   IO_UART_StartTXFLAGBuff;						//多字节发送uint16_t  IO_Send_Size;	
};/*把下面的引脚重新指定到自己指定的引脚上面 打开宏定义 ******************************************************/
#if 0    
//读取Rx脚
#define IO_UART_RXD  HAL_GPIO_ReadPin(IO_RX_GPIO_Port,IO_RX_Pin)     //模拟串口RX端
//设置Tx脚拉高拉低
#define IO_UART_TXD(n)  if(n) HAL_GPIO_WritePin(IO_TX_GPIO_Port, IO_TX_Pin, GPIO_PIN_SET); \else  HAL_GPIO_WritePin(IO_TX_GPIO_Port, IO_TX_Pin, GPIO_PIN_RESET);
//设置时钟脚													
#define IO_UART_CLK(n)  if(n) HAL_GPIO_WritePin(IO_CLK_GPIO_Port, IO_CLK_Pin, GPIO_PIN_SET); \else  HAL_GPIO_WritePin(IO_CLK_GPIO_Port, IO_CLK_Pin, GPIO_PIN_RESET);
#endifextern struct IOUart  IOUart_h_Struct;extern void IO_UART_Send_Buff(void);
extern void IO_UART_SendData(const char *Data,uint32_t size);
extern void IO_UART_Time_CountDown_Deal(void);
extern void IO_UART_ReciveData(void);#endif
定时器中断回调里面处理部分:
			HAL_GPIO_TogglePin(IO_CLK_GPIO_Port, IO_CLK_Pin);				//翻转输出时钟if(HAL_GPIO_ReadPin(IO_CLK_GPIO_Port, IO_CLK_Pin))      		//判断电平 当前输出为 高电平{IO_UART_Send_Buff();}else    //引脚拉低{IO_UART_Time_CountDown_Deal(); 			IO_UART_ReciveData();										 //处理虚拟串口接收数据	if((IO_UART_RXD == GPIO_PIN_RESET)&&(IOUart_h_Struct.IO_UART_StartRxFlag == 0))  //判定是否有低电平数据进入{if(IOUart_h_Struct.IO_UART_RecvStat == COM_STOP_BIT)			//状态为停止位{IOUart_h_Struct.IO_UART_RecvStat = COM_START_BIT;			//设置状态为起始位							IOUart_h_Struct.IO_UART_StartRxFlag = 1;					//开始接收					IOUart_h_Struct.IO_UART_RecvData = 0;  						//接收数据							IOUart_h_Struct.IO_UART_RxTimeOut = _IO_RX_TIMECOUNTDOWN;//接收到数据把接收超时清零																}		 }}

上面是核心代码,如果接受不定长度代码 可在 IO_UART_Time_CountDown_Deal 函数里面 写接受完成标志。

模拟串口 printf 数据

/*******************************************************************************
Name           	 :FlagM_Uart_Printf
Syntax           :void UartM_485Printf(char *fmt,...)
Parameters(in)   :None              :-
Parameters(out)  :None              :-
Return value     :-                 :-
Description      :模拟串口数据打印
|******************************************************************************/
__align(8) char usart_txBuff[USART_TXBUFF_SIZE];                //字节对齐缓冲区
void FlagM_IOUart_Printf(char *fmt,...)
{uint32_t length;va_list ap;va_start(ap,fmt);vsprintf(usart_txBuff,fmt,ap);va_end(ap);length=strlen((const char*)usart_txBuff);   IO_UART_SendData(usart_txBuff,length);    
}


从机部分(时钟来来自主机):

IO_CLK :外部中断脚(EXIT)上升下降沿触发
IO_RX :接受输入脚(GPIO)
IO_TX :数据输出脚(GPIO)
在这里插入图片描述
在这里插入图片描述
基本配置比较简单

代码部分:

核心部分一样的,差异就是 主机在定时回调里面处理数据,而从机则是在外部中断中回调里处理数据

	if(GPIO_Pin == IO_CLK_Pin){if(IO_UART_CLK == GPIO_PIN_RESET)							//RX 时钟引脚为低电平  {			IO_UART_Time_CountDown_Deal();  						//处理虚拟串口接收数据			 IO_UART_ReciveData();								//处理接受的数据if((IO_UART_RXD == GPIO_PIN_RESET)&&(IOUart_h_Struct.IO_UART_StartRxFlag == 0)){if(IOUart_h_Struct.IO_UART_RecvStat == COM_STOP_BIT)//状态为停止位{IOUart_h_Struct.IO_UART_RecvStat = COM_START_BIT;	//设置状态为起始位IOUart_h_Struct.IO_UART_StartRxFlag = 1;					//开始接收					IOUart_h_Struct.IO_UART_RecvData = 0;  						//接收数据IOUart_h_Struct.IO_UART_RxTimeOut = _IO_RX_TIMECOUNTDOWN;//接收到数据把接收超时清零																}}}else if(IO_UART_CLK == GPIO_PIN_SET)		{//这里是数据发送部分 上升沿发送数据IO_UART_Send_Buff(); }}

如有问题可以加群讨论: 764284134

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2779024.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

中年低端中产程序员从西安出发到海南三亚低成本吃喝万里行:西安-南宁-湛江-雷州-徐闻-博鳌-陵水-三亚-重庆-西安

文章大纲 旅途规划来回行程的确定南宁 - 北海 - 湛江轮渡成为了最终最大的不确定性&#xff01;感谢神州租车气温与游玩地点总体花费 游玩过程出发时间&#xff1a;Day1-1月25日星期四&#xff0c;西安飞南宁路途中&#xff1a;Day2-1月26日星期五&#xff0c;南宁-湛江-住雷州…

python coding with ChatGPT 打卡第19天| 二叉树:合并二叉树

相关推荐 python coding with ChatGPT 打卡第12天| 二叉树&#xff1a;理论基础 python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历 python coding with ChatGPT 打卡第14天| 二叉树的广度优先遍历 python coding with ChatGPT 打卡第15天| 二叉树&#xff1a;翻转…

【C语言——打印乘法口诀表】

乘法表&#xff1a; 我们可以定义一个i控制行的变化&#xff0c;外加看上图的表得知我们需要用到循环结构&#xff0c;i是行需要不停的加加&#xff0c;因此&#xff0c;for循环比较好用&#xff0c;可以用两个嵌套的循环&#xff0c;外层循环即用到的i表示的是每一行的打印&am…

阿里云游戏服务器多少钱一年?

阿里云游戏服务器租用价格表&#xff1a;4核16G服务器26元1个月、146元半年&#xff0c;游戏专业服务器8核32G配置90元一个月、271元3个月&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云游戏专用服务器详细配置和精准报价&#xff1a; 阿里云游戏服务器租用价格表 阿…

《计算思维导论》笔记:10.4 关系模型-关系运算

《大学计算机—计算思维导论》&#xff08;战德臣 哈尔滨工业大学&#xff09; 《10.4 关系模型-关系运算》 一、引言 本章介绍数据库的基本数据模型&#xff1a;关系模型-关系运算。 二、什么是关系运算 在数据库理论中&#xff0c;关系运算&#xff08;Relational Operatio…

PKI - 借助Nginx 实现Https_使用CA签发证书

文章目录 Pre概述操作步骤1. 生成 CA 密钥对2. 生成自签名的 CA 证书3. 生成服务器密钥对和证书签名请求 (CSR)4. 使用 CA 签署服务器证书 Nginx Https 自签证书1. 生成自签名证书和私钥2. 配置 Nginx 使用 CA签发的 HTTPS 证书3. 重启 Nginx 服务4. 直接访问5. 不验证证书直接…

代码随想录算法训练营day15||二叉树part02、102.二叉树的层序遍历、 226.翻转二叉树(优先掌握递归)、101. 对称二叉树 (优先掌握递归)

102.二叉树的层序遍历 题目&#xff1a;给你一个二叉树&#xff0c;请你返回其按 层序遍历 得到的节点值。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 接下来我们再来介绍二叉树的另一种遍历方式&#xff1a;层序遍历。 层序遍历一个二叉树。就是…

Centos7之忘记Root用户密码的处理方式

Centos7之忘记Root用户密码的处理方式 文章目录 Centos7之忘记Root用户密码的处理方式1.场景描述2. 重置密码1. 重启系统进入编辑界面2. 按方向键下键↓&#xff0c;找到设置语言的地方3. 进入bash界面后&#xff0c;可以输入passwd命令重新设置root密码 1.场景描述 长时间未使…

前后端分离nodejs+vue流浪狗宠物领养公益网站

1.发现公益&#xff1a;主要是根据社会上的调研&#xff0c;来收集的社会上有关流浪狗的公益活动&#xff0c;发布在公益网站上能被更多人发现&#xff0c;主要让更多人能参与到公益活动中来&#xff0c;并调动群众的同情心和爱心&#xff0c;借此希望在养宠物的主人能避免自己…

vue3 之 商城项目—项目搭建起步

1.创建项目 1️⃣ npm init vuelatest2️⃣ npm install3️⃣ npm run dev4️⃣目录调整 2.git管理项目 基于creact-vue创建出来的项目默认没有初始化git仓库&#xff0c;需要我们手动初始化 执行命令 git init git add. git commit -m init3.项目起步—配置别名路径联…

前端开发_AJAX基本使用

AJAX概念 AJAX是异步的JavaScript和XML(Asynchronous JavaScript And XML)。 简单点说&#xff0c;就是使用XMLHttpRequest对象与服务器通信。 它可以使用JSON&#xff0c;XML&#xff0c;HTML和text文本等格式发送和接收数据。 AJAX最吸引人的就是它的“异步"特性&am…

顶级思维方式——对优秀人才的定义

目录 1、乔布斯对优秀人才的定义 2、 乔布斯对优秀人才的管理 3、感到压力焦虑的时候怎么办 注&#xff1a; 以下内容均来自乔布斯、贝索斯的采访视频摘录 1、乔布斯对优秀人才的定义 乔布斯&#xff1a; 公司规模变大之后&#xff0c;就会变得循规蹈矩。他们觉得只要遵守流…

MySQL数据库-索引概念及其数据结构、覆盖索引与回表查询关联、超大分页解决思路

索引是帮助mysql高效获取数据的数据结构,主要用来提高检索的效率,降低数据库的IO成本(输入输出成本&#xff08;Input-Output Cost&#xff09;),同时通过索引对数据进行排序也能降低数据排序的成本,降低了CPU的消耗。 Mysql的默认存储引擎InnoDB&#xff0c;InnoDB采用的B树的…

肯尼斯·里科《C和指针》第12章 使用结构和指针(2)双链表

12.3 双链表 单链表的替代方案就是双链表。在一个双链表中&#xff0c;每个节点都包含两个指针——指向前一个节点的指针和指向后一个节点的指针。这可以使我们以任何方向遍历双链表&#xff0c;甚至可以随意在双链表中访问。下面的图展示了一个双链表。 下面是节点类型的声明&…

算法刷题:移动零

移动零 .题目链接详解curdesc算法原理 答案 . 题目链接 移动零 详解 题目要求我们要把数组中所有的零都移动到数组的末尾,且要求其余数字顺序不改变.这道题,我们使用到的是双指针算法: 利用两个指针,将数组分为三个部分, 三个区间分别为 [0,desc][desc1,cur-1][cur,n-1] 在…

算法学习——LeetCode力扣二叉树篇1

算法学习——LeetCode力扣二叉树篇1 144. 二叉树的前序遍历 144. 二叉树的前序遍历 - 力扣&#xff08;LeetCode&#xff09; 描述 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&a…

电子电器架构 —— 区域控制器是未来架构的正解吗?

电子电器架构 —— 区域控制器是未来架构的正解吗? 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶…

C++ //练习 5.5 写一段自己的程序,使用if else语句实现把数字成绩转换成字母成绩的要求。

C Primer&#xff08;第5版&#xff09; 练习 5.5 练习 5.5 写一段自己的程序&#xff0c;使用if else语句实现把数字成绩转换成字母成绩的要求。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /***************************…

视觉开发板—K210自学笔记(四)

在点灯之后&#xff0c;我们就需要饿补一下相关的编程基础知识&#xff0c;了解基本语法&#xff0c;加深底蕴才能编写出更好的程序来。由于 MaixPy 是基于 MicroPython 之上进行开发构建的&#xff0c;提供给用户最终的接口是 Micropython &#xff0c;所以在使用 MaixPy 开发…

C语言-----自定义类型-----结构体枚举联合

结构体和数组一样&#xff0c;都是一群数据的集合&#xff0c;不同的是数组当中的数据是相同的类型&#xff0c;但是结构体中的数据类型可以不相同&#xff0c;结构体里的成员叫做成员变量 结构体类型是C语言里面的一种自定义类型&#xff0c;我们前面已经了解到过int,char,fl…