历史上的今天
返回首页

历史上的今天

今天是:2024年09月02日(星期一)

正在发生

2021年09月02日 | STM32—串口通讯详解

2021-09-02 来源:eefocus

串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式,其通讯协议可分层为协议层和物理层。物理层规定通信协议中具有机械、电子功能的特性,从而确保原始数据在物理媒体的传播;协议层主要规定通讯逻辑,统一双方的数据打包、解包标准。通俗的讲物理层规定我们用嘴巴还是肢体交流,协议层规定我们用中文还是英文交流。下面分析一下串口通讯协议的物理层和协议层。


物理层

1.通讯结构

串口通讯的物理层的主要标准是RS-232标准,其规定了信号的用途、通讯接口及信号的电平标准,其通讯结构如下:

在这里插入图片描述

在设备内部信号是以TTL电平标准传输的,设备之间是通过RS-232电平标准传输的,而且TTL电平需要经过电平转换芯片才能转化为RS-232电平,RS-232电平转TTL电平也是如此。


2.电平标准

根据使用的电平标准不同,串口通讯可分为 RS-232标准 及TTL标准,具体标准如下:

在这里插入图片描述

在电子电路中常使用TTL的电平标准,但其抗干扰能力较弱,为了增加串口的通讯距离及抗干扰能力,使用RS-232电平标准在设备之间传输信息,经常使用MA3232芯片对TTL电平及RS-232电平进行相互转换。


协议层

1.数据包

串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备得RXD接口,在协议层中规定了数据包的内容,具体包括起始位、主体数据(8位或9位)、校验位以及停止位,通讯的双方必须将数据包的格式约定一致才能正常收发数据。

在这里插入图片描述

2.波特率

由于异步通信中没有时钟信号,所以接收双方要约定好波特率,即每秒传输的码元个数,以便对信号进行解码,常见的波特率有4800、9600、115200等。STM32中波特率的设置通过串口初始化结构体来实现。

3.起始和停止信号

数据包的首尾分别是起始位和停止位,数据包的起始信号由一个逻辑0的数据位表示,停止位信号可由0.5、1、1.5、2个逻辑1的数据位表示,双方需约定一致。STM32中起始和停止信号的设置也是通过串口初始化结构体来实现。

4.有效数据

有效数据规定了主题数据的长度,一般为8或9位,其在STM32中也是通过串口初始化结构体来实现的。

5.数据校验

在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、 0 校验(space)、 1 校验(mark)以及无(noparity)。这些也都可以在串口初始化结构体中实现的。


USART简介

USART(通用同步异步收发器)是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。有别于 USART 还有一个UART,它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。USART 在 STM32 应用最多莫过于“打印”程序信息,一般在硬件设计时都会预留一USART 通信接口连接电脑,用于在调试程序是可以把一些调试信息“打印”在电脑端的串口调试助手工具上,从而了解程序运行是否正确、如果出错哪具体哪里出错等等。

STM32中一共有5个USART,如示:

在这里插入图片描述

USART的USB转串口原理图如下:

在这里插入图片描述

USART1的发送和接收端口是事先连接好的,如果要使用其他USART只需要将相应的发送接收端口按图连接好即可。


USART有多个中断请求事件:

在这里插入图片描述

开发板与上位机的连接

开发板与上位机之间通过USB线连接,所以在上位机上要配置一个USB转串口 的驱动,以便把USB传输过来的电平转换为TTL电平,TTL电平才能与串口调试助手建立联系。一般使用CH341驱动作为win10下的USB转串口,驱动安装成功的情况下接入USB会在计算机的设备管理器的端口中发现串口:

在这里插入图片描述

(win7系统一般选择CH340作为USB转串口驱动。)


代码讲解:

固件库编程的一大好处就是我们可以根据固件库函数来学习外设的相关知识,而且固件库函数的编写都是建立在对底层寄存器操作上的,所以通过讲解代码可以更好理解串口通讯相关知识。


一.初始化结构体

typedef struct {

 uint32_t USART_BaudRate; // 波特率

 uint16_t USART_WordLength; // 字长

 uint16_t USART_StopBits; // 停止位

 uint16_t USART_Parity; // 校验位

 uint16_t USART_Mode; // USART 模式

 uint16_t USART_HardwareFlowControl; // 硬件流控制

 } USART_InitTypeDef;


USART初始化结构体中的相应变量都对应着数据包中的相对内容。


二.NVIC配置中断优先级

我们在串口接收信息时采用了触发中断事件,所以要配置一下串口中断的优先级:


NVIC_Configuration(void)

{

  NVIC_InitTypeDef NVIC_InitStructure;

  

  /* 嵌套向量中断控制器组选择 */

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  

  /* 配置USART为中断源 */

  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;

  /* 抢断优先级*/

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

  /* 子优先级 */

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

  /* 使能中断 */

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  /* 初始化配置NVIC */

  NVIC_Init(&NVIC_InitStructure);

}


中断相关的知识之前详细讲过,此处就不再累赘讲述。

中断知识链接


三.USART配置函数讲解

USART配置函数的主要作用是打开串口与相应的GPIO引脚,配置好相应串口信息与GPIO引脚的工作模式,以便信息的传输与接收。


void DEBUG_USART_Config(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

USART_InitTypeDef USART_InitStructure;

/* 第一步:初始化GPIO */

// 打开串口GPIO的时钟

DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);

// 将USART Tx的GPIO配置为推挽复用模式

GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);


  // 将USART Rx的GPIO配置为浮空输入模式

GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

/* 第二步:配置串口的初始化结构体 */

// 打开串口外设的时钟

DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

// 配置串口的工作参数

// 配置波特率

USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;

// 配置 针数据字长

USART_InitStructure.USART_WordLength = USART_WordLength_8b;

// 配置停止位

USART_InitStructure.USART_StopBits = USART_StopBits_1;

// 配置校验位

USART_InitStructure.USART_Parity = USART_Parity_No ;

// 配置硬件流控制

USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

// 配置工作模式,收发一起

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

// 完成串口的初始化配置

USART_Init(DEBUG_USARTx, &USART_InitStructure);


/*--------------------------------------------------------*/

// 串口中断优先级配置

NVIC_Configuration();

// 使能串口接收中断

USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);

/*--------------------------------------------------------*/

/* 第三步:使能串口 */

// 使能串口

USART_Cmd(DEBUG_USARTx, ENABLE);

}


第一步:打开了GPIO的时钟,设置发送和接收引脚的信息,将Tx(发送引脚)配置为推挽复用模式用来发送数据,Rx(接收引脚)配置为浮空输入模式用来接收数据。


第二步:首先打开USART1 的时钟,根据USART初始化结构体成员配置相关的信息,之后利用初始化函数将初始化结构体中的信息写入相应寄存器中,然后的话就是引用NVIC_Configuration()函数配置串口中断优先级,打开相应的串口接收中断,中断接收函数的参数如下:

在这里插入图片描述

第三步 :最后相当于打开总电源——使能串口


USART配置函数完成后代表,USART1 的接收和发送准备工作已经准备就绪,接下来就是,串口与上位机之间的信息传递了,信息的发送和接收都有相对于的函数。


四.传输数据的函数:

开发板与上位机之间的数据传输可以有多种方法,下面一一介绍:


1.发送一个字节

以USART_SendData(pUSARTx,ch); 函数为基础建立的函数可以向上位机发送一个字节的数据,利用FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG) 读取发送数据寄存器的状态来 等待发送寄存器将数据成功发送。


void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)

{

/* 发送一个字节数据到USART */

USART_SendData(pUSARTx,ch);

/* 等待发送数据寄存器为空 */

while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

}


2.发送字符串

本质是利用上面的字节发送函数逐位发送字符串中的内容


void USART_SendString(USART_TypeDef * pUSARTx, char *str)

{

unsigned int   k=0;

while(*(str+k)!='')

{

USART_SendData(pUSARTx, *(str+k));

/* 等待发送数据寄存器为空 */

while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

k++;

}

while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);   /* TC:传输完成标志 */

}


3.重定向printf函数发送字符串

关于重定向的知识之前总结过,链接:重定向知识。重定向后的printf()函数功能强大,具有向串口调试助手打印数据的功能,使用方法和c语言时一样,比如printf("欢迎来到小全全的串口实验n");就可以将“欢迎来到小全全的串口实验”这句话发送到上位机中,而且换行符“n”还具有换行作用。


/* 重定向printf函数 */

int fputc(int ch, FILE *f)

{

USART_SendData( DEBUG_USARTx,  (uint8_t) ch);

/* 等待发送完毕 */

while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); 

return ch;

}


4.重定向getchar函数接收字符

具体操作与重定向后的printf函数类似,比如可以通过如下代码向上位机发送已经接收到的数据:


x=getchar();

printf("接收到的字符是:%cn",x);


重定义如下:


///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数

int fgetc(FILE *f)

{

/* 等待串口输入数据 */

while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);


return (int)USART_ReceiveData(DEBUG_USARTx);

}


在使用此函数作为接收数据时记得关闭串口得接收中断!!!


5.通过中断接收

在stm32f10x_it.c中编写USART1中断源相对应得中断函数,利用了固件库函数中的

USART_ReceiveData(DEBUG_USARTx);接收函数

USART_SendData(DEBUG_USARTx, x);发送函数

USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE);判断标志位函数


/* #define  DEBUG_USART_IRQn         USART1_IRQn 

   #define  DEBUG_USART_IRQHandler   USART1_IRQHandler */

void DEBUG_USART_IRQHandler(void)

{

uint16_t  x;

/* 判断是否收到中断信号 */

if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE) == SET)

{

x = USART_ReceiveData(DEBUG_USARTx);

USART_SendData(DEBUG_USARTx, x);

}


}


结语

以固件库函数编程的思路讲解,未能顾及到众多寄存器的讲解,我认为进行固件库编程本身就是学习操作寄存器的过程,很多时候我们不需要知道如何操作寄存器,只要了解如何操作固件库函数即可。(吹爆固件库编程)

推荐阅读

史海拾趣

FOTEK公司的发展小趣事

福禄克并未止步于传统电子测试工具领域,而是积极探索新的增长点。2005年,公司推出了一系列室内空气质量(IAQ)测试工具,旨在为HVAC、建筑物维修和IAQ专业人员提供综合的精密仪器产品组合。这些产品凭借其精确性、可靠性和易用性,迅速获得了市场的认可。此后,福禄克进一步拓展至生物医学领域,推出了电气安全性测试仪、病患模拟器等先进设备,在生物医学测试和模拟产品领域取得了领先地位。

苏州锋驰(Feng)公司的发展小趣事
在科研和实验过程中,为各种实验装置提供稳定的电流输出。
HTSEMI( Jin Yu Semiconductor )公司的发展小趣事

随着技术实力的不断提升,金宇半导体开始积极拓展国内外市场。公司制定了详细的市场拓展计划,通过参加国际电子展、举办技术研讨会等方式,加强与客户的沟通和交流。同时,金宇半导体还加大了品牌建设的力度,通过广告宣传、媒体合作等多种方式提升品牌知名度和美誉度。这些努力使得金宇半导体的市场份额逐年攀升,品牌影响力也日益增强。

台湾兆欣(EMTEK)公司的发展小趣事

兆欣科技股份有限公司成立于2003年,由一群怀揣梦想的电子工程师创立。在创立之初,公司就明确了自己的目标:开发和量产全自动、小型化的SMD绕线电感。这一决定在当时的市场上并不常见,但兆欣团队坚信这是未来的趋势。他们投入大量时间和精力进行研发,成功推出了共模滤波器、高频芯片电感以及薄型功率电感等主力产品。

EOZ S.A.S公司的发展小趣事

为了进一步扩大市场份额,EOZ S.A.S公司积极实施国际化战略。公司通过参加国际电子展、建立海外研发中心和生产基地等方式,加强与国际市场的联系和合作。同时,EOZ S.A.S还针对不同国家和地区的消费者需求进行产品定制和本地化营销,成功将产品打入多个国际市场并取得良好业绩。

微芯(CMOSIC)公司的发展小趣事

随着全球化的加速发展,微芯生物也积极开展国际合作与交流。他们与国际知名医药企业、科研机构以及高校建立了紧密的合作关系,共同开展新药研发和技术创新。通过国际合作与交流,微芯生物不仅吸收了国际先进的研发理念和技术经验,还为公司的发展带来了更广阔的市场空间和合作机会。

问答坊 | AI 解惑

碁于uclinux的网络部分启动分析

碁于uclinux的网络部分启动分析…

查看全部问答>

工业自动化仪表与控制系统未来发展的关注 指针式仪表控制系统

1、自动化仪表与企业的信息化   自动化仪表技术包括信息采集、处理和应用。“企业信息化”实际上是企业信息的集成和整合。为此,必须用自动化和系统的信息模型“简化”、“规则”和“抽象”信息,以便最有效地利用信息。这是自动化仪表领域的一 ...…

查看全部问答>

vxworks会自动生成默认路由吗

vxworks启动之后,routeshow路由表项,发现已经有了一个路由表项,该表项的网关是机器自己BIOS的IP,我想问一下,这是vxworks自动生成的呢,还是因为我系统中装了什么软件添加了这个路由表项,请高人指点,谢谢…

查看全部问答>

专为手机开发建立的交流群

QQ群:74481311 专门为从事手机软件开发的专业人员和业余爱好者建立的高级群,欢迎大家在线交流.…

查看全部问答>

大家进我的编程与硬件交流群呀72651142

大家进我的编程与硬件交流群呀72651142…

查看全部问答>

s3c2440 camera 疑惑(基于7113的视频采集)

各位大侠,小弟现在正在搞2440 的视频开发,初次涉及,想用7113来做视频采集,烦请各位给提供个简单的思路,或者是大致的方向,不胜感激…

查看全部问答>

换书的说

不知道多少芯币能从大虾手里换一本夏宇闻老师的Verilog的书呢?…

查看全部问答>

PSMN1R8-40YLC N-channel 40 V 1.8 mΩ

这款芯片是sot669封装,但是我不清楚是几脚的,关于mb ,到底是画成第五个引脚还是,就只是四个引脚,四个引脚的话,那个Drain是第几引脚?为啥1,2,3引脚都是source引脚?此芯片是用的AltiumDesigner 画的封装,跪求指导。。。…

查看全部问答>

光耦导通后,4脚没有输出

上个贴看不见图片,重发一遍。。。 用单片机引脚控制光耦导通,但是光耦导通后,4引脚始终无输出,百度说是后级电路有问题,但是后级电路是照着光耦规格说明书上画的啊,实在不知道是哪里出了问题。 …

查看全部问答>