【stm32】串口通信

2023-11-02

一、串口通信协议原理

串口在嵌入式系统当中是一类重要的数据通信接口,其本质功能是作为 CPU 和串行设备间的编码转换器。当数据从 CPU 经过串行端口发送出去时,字节数据转换为串行的位;在接收数据时,串行的位被转换为字节数据。应用程序要使用串口进行通信,必须在使用之前向操作系统提出资源申请要求(打开串口),通信完成后必须释放资源(关闭串口)。典型地,串口用于 ASCII 码字符的传输。通信使用3根线完成:地线,发送数据线,接收数据线。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通行的端口,这些参数必须匹配:波特率是一个衡量通信速度的参数,它表示每秒钟传送的 bit 的个数;数据位是衡量通信中实际数据位的参数,当计算机发送一个信息包,标准的值是 5,7 和 8 位。如何设置取决于你的需求;停止位用于表示单个包的最后一位,典型的值为 1,1.5和 2 位,停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会;奇偶校验位是串口通信中一种简单的检错方式,有四种检错方式——偶、奇、高和低,也可以没有校验位。
对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。

物理层:物理层上我们这里拿出标准的RS232串口和USB转串口进行分析、讲解。在物理层上我们用到最多的还是USB转串口。
RS232:
在这里插入图片描述
在原理图上我们可以看到右侧RS232串口上两个通讯设备的“DB9 接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232 标准”传输数据信号。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片(MAX3232CSE)”转换成控制器能识别的“TTL 校准”的电平信号才能实现通讯。

我们知道常见的电子电路中常使用 TTL 的电平标准,理想状态下,使用 5V 表示二进制逻辑 1,使用 0V 表示逻辑 0;而为了增加串口通讯的远距离传输及抗干扰能力,它使用-15V 表示逻辑 1,+15V 表示逻辑 0。由于控制器一般使用 TTL 电平标准,所以常常会使用 MA3232 芯片对 TTL 及 RS-232 电平的信号进行互相转换。即:
在这里插入图片描述

USB转串口:
这个通讯方式是在单片机中最经常运用到的通讯方式。USB转串口主要用于设备跟电脑通信。
在这里插入图片描述
这里原理图上用到的电平转换芯片主要是CH340G。一般我们主要运用的电平转换芯片有:CH340、PL2303、CP2102、FT232。

协议层:我们在使用串口的时候主要还是用到“通用异步通讯”——可以参考通讯基本概念基础。这篇讲解了通讯的一些基本原理。所以在协议层上就不多讲解这个概念问题了。

协议层是指串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。如图所示:
在这里插入图片描述

二、usart功能框图

在这里插入图片描述
在功能框图上把其划分为四个部分——GPIO引脚、数据寄存器、控制器、波特率。我们在这里就主要讲解前三大功能,波特率一般可以在stm32上自主设置波特率的值(一般常用9600、15200)。

GPIO引脚:在功能框图上对应的1框图上我们看到主要有Tx、Rx、SW_RX、nRTS、nCTS、SCLK六个功能引脚,在stm32上这些功能引脚当中最常用的是Tx、Rx引脚。

TX:发送数据输出引脚。
RX:接收数据输入引脚。
SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚(很少用到)。
nRTS:请求以发送 (Request To Send),n 表示低电平有效。如果使能 RTS 流控制,当 USART 接收器准备好接收新数据时就会将nRTS引脚变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制。
nCTS:清除以发送 (Clear To Send),n 表示低电平有效。如果使能 CTS 流控制,发送器在发送下一帧数据之前会检测nCTS引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。

注意:在stm32的UART 只是异步传输功能,所以没有SCLK、nCTS 和nRTS 功能引脚。

数据寄存器:数据寄存器控制usart的读取还是发送。在官方的参考手册上对于数据寄存器(USART_DR)是这样描述的——包含接收到数据字符或已发送的数据字符,具体取决于所执行的操作是“读取”操作还是“写入”操作。
因为数据寄存器包含两个寄存器,一个用于发送 (TDR),一个用于接收 (RDR),因此它具有 双重功能(读和写)。

对于写的时候我们数据寄存器选择TDR寄存器,在进行偏移0x04个位,在传输到发送数据传输引脚(Tx)。
在这里插入图片描述
在读取的时候原理同读取一样。
在这里插入图片描述
控制器:主要是由控制寄存器USART_CR1、USART_CR2、USART_CR3共同进行控制;在功能框图上我们可以发现其USART的控制器具有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。

发送器:当 USART_CR1 寄存器的发送使能位 TE 置 1 时,启动数据发送,发送移位寄存器的数据会在 TX引脚输出,如果是同步模式 SCLK 也输出时钟信号。一个字符帧发送需要三个部分:**起始位 + 数据帧 + 停止位。**起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据帧就是我们要发送的 8 位或 9 位数据,数据是从最低位开始传输的;停止位是一定时间周期的高电平。停止位时间长短是可以通过 USART 控制寄存器2(USART_CR2) 的 STOP[1:0] 位控制,可选 0.5个、1 个、1.5 个和 2 个停止位。默认使用 1 个停止位。2 个停止位适用于正常 USART 模式、单
线模式和调制解调器模式。0.5 个和 1.5 个停止位用于智能卡模式。

在这里插入图片描述
当发送使能位 TE 置 1 之后,发送器开始会先发送一个空闲帧 (一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状态寄存器 (USART_SR) 的 TC 位为 1,表示数据传输完成,如果 USART_CR1 寄存器的 TCIE 位置1,将产生中断。
在这里插入图片描述
接收器:如果将 USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置 1,同时如果USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断。
在这里插入图片描述

三、stm32库的配置

主要的函数:

USART_Init();  //初始化串口
USART_ITConfig(); //中断配置函数
NVIC_Init();  //配置中断
USART_SendData();  //数据发送函数
USART_GetFlagStatus(); //接受标志位函数
USART_GetITStatus();  //发送标志位函数
USART_ClearITPendingBit();//清除中断预处理
USART_ReceiveData();  //数据接受函数 

usart_init()

void USART2_Init(unsigned long ulBaud)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  USART_InitTypeDef USART_InitStruct;
  NVIC_InitTypeDef NVIC_InitStruct;
  

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStruct);
  

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStruct); 
  

  USART_InitStruct.USART_BaudRate = ulBaud;  //波特率
  USART_InitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
  USART_InitStruct.USART_StopBits = USART_StopBits_1;  //1位停止位
  USART_InitStruct.USART_Parity = USART_Parity_No;  //无校验位
  USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
  USART_InitStruct.USART_HardwareFlowControl= USART_HardwareFlowControl_None;//硬件流控制,一般选择none
  USART_Init(USART2, &USART_InitStruct);
  
	// 使能串口
  USART_Cmd(USART2, ENABLE);
  
	// 允许串口接受中断
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);	
  
	// 允许串口中断
  NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;//中断通道为串口2
  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//先站优先级
  NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//从占优先级
  NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能
  NVIC_Init(&NVIC_InitStruct);
}

发送一个字符

unsigned char USART_SendChar(USART_TypeDef* USARTx, unsigned char ucChar)
{
  while(!USART_GetFlagStatus(USARTx, USART_FLAG_TXE));//不断检查标志位
  USART_SendData(USARTx, ucChar);//发送字符数据
  return ucChar;//返回发送的字符
}

发送字符串

void USART_SendString(USART_TypeDef* USARTx, unsigned char* pucStr)
{
  while(*pucStr != '\0')
    USART_SendChar(USARTx, *pucStr++);
}

接受中断

void USART2_IRQHandler(void)
{	
		u8 temp;
		if(USART_GetITStatus(USART2,USART_IT_RXNE) == SET)
		{
				USART_ClearITPendingBit(USART2,USART_IT_RXNE);
				temp =  USART_ReceiveData(USART2);
				if(temp == '\n')
				{
						RXCUNT = 0;
						RXOVER = 1;
						USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
				}else
				{
						RXBUF[RXCUNT] = temp;
						RXCUNT++;
				}
		}
}

main函数
在main函数上要用到几个标志位

u8 RXBUF[20];  //储存数组
u8 RXCUNT=0;   //接受计数
u8 RXOVER=0;     //接受标准
int main(void)
{
 USART2_Init(9600);
 while(1)
 {
      if(RXOVER==1)   //接受标志位
		{
			RXOVER=0;   //标志清零
			USART_ITConfig(USART2,USART_IT_RXNE, ENABLE);   //清除中断标志位
		 if(RXBUF[0]=='C')   //当发送“C”则打印1111
			{
				
				USART_SendString(USART2,"  1111  ");	
			}
			if(RXBUF[0]=='S')
			{
				
				USART_SendString(USART2,"  2222  ");	
			
			}
      memset(RXBUF,'\0',sizeof(RXBUF));  //清空数组
			
		}
 }
}

注意:在我们使用串口清空函数(memser)的时候头文件要加上#include"string.h"。不然会报错。
现象:
在这里插入图片描述

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

【stm32】串口通信 的相关文章

  • warning: ISO C++ forbids converting a string constant to 'char*'

    warning ISO C forbids converting a string constant to char Wwrite strings 解决办法 int main int argc char argv char str 先把C
  • sql 时间计算

    兩個時間之差的合計 DECLARE I INT SET I DATEDIFF ms GETDATE RAND 24 GETDATE SELECT convert varchar 10 I 86400000 Days convert varc
  • QT on Android的rtsp播放器demo

    手机 平板
  • 文件导入之Validation校验List对象数组

    背景 我们的接口是一个List对象 对象里面的数据基本都有一些基础数据校验的注解 我们怎么样才能校验这些基础规则呢 我们在导入excel文件进行数据录入的时候 数据录入也有基础的校验规则 这个时候我们又该如何少写代码让Validation框
  • rtthread空闲线程与定时器

    rtthread为什么要用到空闲线程 RT Thread是一个实时操作系统 它的内核采用了基于线程的设计思路 在RT Thread中 空闲线程是一种特殊的线程 它在系统没有其他任务需要执行时会被调度执行 因此 空闲线程的存在可以利用CPU的

随机推荐

  • 动态规划总结

    感谢赵剑波大牛的原稿 转载于http blog sina com cn zhaojianbo0124 第一节 动态规划基本概念一 动态规划三要素 阶段 状态 决策 他们的概念到处都是 我就不多说了 我只说说我对他们的理解 如果把动态规划的求
  • open3d显示RGBD点云(rope3d数据集)

    rope3d中同时有深度图 因此 可以创建rgbd图像点云 借助open3d库 需要确定相机内参和外参 注意统一坐标系 例如都显示在world坐标系 usr bin env python3 coding utf 8 for reading
  • 【六】Python全栈之路--for循环

    文章目录 1 双向循环的练习 2 break pass continue的使用 3 for循环 4 小练习 1 双向循环的练习 1 用两个循环完成十行十列的小星星 j 0 while j lt 10 打印星星 i 0 while i lt
  • [MAE]Masked Autoencoders掩膜自编码器

    目录 非对称的编码 解码架构 方法 流程 编码器 解码器 Partial Fine tuning 图像MAE MixMIM Uniform Masking HiViT 视频MAE VideoMAE 多模态MAE M3AE https git
  • v-model支input父子组件传值

    v model用于表单数据的双向绑定 其实它就是一个语法糖 这个背后就做了两个操作 v bind绑定一个value属性 v on指令给当前元素绑定input事件 自定义组件使用v model 应该有以下操作 接收一个value prop 触
  • 线程获取锁的顺序java_多线程(五) java的线程锁

    在多线程中 每个线程的执行顺序 是无法预测不可控制的 那么在对数据进行读写的时候便存在由于读写顺序多乱而造成数据混乱错误的可能性 那么如何控制 每个线程对于数据的读写顺序呢 这里就涉及到线程锁 什么是线程锁 使用锁的目的是什么 先看一个例子
  • Linux进程替换

    目录 进程替换 1 定义 2 为什么要进行进程替换 3 怎样进行进程程序替换 4 进程替换的原理 原理总结 5 Linux进程替换的函数 5 1参数 5 2函数返回值问题 5 3 execl函数用例 5 3 2升级案例 编辑 5 3 3调用
  • MySQL的DQL查询语句

    DQL 查询语句 1 排序查询 语法 order by 子句 order by 排序字段1 排序字段2 排序方式 ASC 升序 磨人的 DESC 降序 注意 如果有多个排序条件 则当前面的条件一致时 才会判断第二条件 2 聚合函数 将一列数
  • INS/GNSS组合导航(二)-INS常用的坐标系统及转换

    一 常用坐标系 由于INS是一种完全自主的导航系统 不与外界发生联系 但是我们又想知道物体的的位置 尤其是其与GNSS组合时需要知道其与GNSS在同一坐标系下的位置信息 这就牵扯到了INS的几个导航系统之间的转换 笼统的讲 INS输出的是载
  • springcloud整合consul

    搭建consul环境 linux安装consul 搭建生产者 1 建module provider payment8006 2 写pom
  • electron自动更新

    1 主进程background js引入autoUpdater let autoUpdater require electron updater 2 主进程写入更新代码 if process env WEBPACK DEV SERVER U
  • 图形学实验五曲线生成算法

    实验五 曲线生成算法 实验类型 设计型 实验学时 2实验要求 必修 一 实验目的 了解曲线的生成原理 掌握几种常见的曲线生成算法 利用VC OpenGL实现Bezier曲线生成算法 二 实验内容 1 分析空间曲线生成算法的原理 绘制其程序流
  • 2017年全国大学生电子设计竞赛综合测评题目解析——Multisim仿真

    2017年综合测评仿真电路讲解 题目和结果链接 常用电路Multisim仿真 方波 三角波振荡电路 常用电路Multisim仿真 有源低通滤波器设计 常用电路Multisim仿真 数字芯片74LS74构建分频器设计 常用电路Multisim
  • python 主力资金_真正的主力进场加仓指标源码

    import numpy as np def initialize context g up price 向上碎形最高价 g low price 向下碎形最低价 g up fractal exists 判断有效向上碎形 g down fra
  • Mybatis-Plus学习---(五)灵活使用wapper条件构造器

    手都敲麻了 翻译自官方 条件构造器 说明 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中 例如 query like StringUtils isNotBlank name Entity getN
  • Linux 安装 MariaDB

    CentOS 7 自带的 MariaDB 是 5 5 版本的 使用 yum install mariadb server 命令安装的默认版本是 5 5 的 这是因为系统默认源只有 5 5 的版本 安装准备 检查是否安装过 MariaDB 或
  • MapInfo数据格式

    MapInfo采用双数据库存储模式 即其空间数据与属性数据是分开来存储的 属性数据存储在关系数据库的若干属性表中 而空间数据则以MapInfo的自定义格式保存于若干文件中 二者通过一定的索引机制联系起来 为了提高查询和处理效率 MapInf
  • Octave Convolution[论文详解]

    Drop an Octave Reducing Spatial Redundancy in Convolutional Neural Networks with Octave Convolution github 传统的卷积运算 要成为过去
  • 利用 Transformer 网络建立预测模型

    引言 我最近读了一篇非常有趣的论文 Deep Transformer Models for Time Series Forecasting The Influenza Prevalence Case 我认为这可能是一个有趣的项目 从零开始实
  • 【stm32】串口通信

    串口通信 一 串口通信协议原理 二 usart功能框图 三 stm32库的配置 一 串口通信协议原理 串口在嵌入式系统当中是一类重要的数据通信接口 其本质功能是作为 CPU 和串行设备间的编码转换器 当数据从 CPU 经过串行端口发送出去时