串口通讯(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)!='\0')
{
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("接收到的字符是:%c\n",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);
} }

结语

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

STM32—串口通讯详解的更多相关文章

  1. Linux串口编程详解(转)

    串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接 ...

  2. stm32串口通讯问题

    stm32串口通讯问题 在串口试验中,串口通讯不正常,则可能会出现以下问题: 1. 配置完成后,串口没有任何消息打印. 原因:1,端口配置有问题,需要重新检查I/O口的配置 2,接线有问题,检查接线是 ...

  3. STM32固件库详解

    STM32固件库详解   emouse原创文章,转载请注明出处http://www.cnblogs.com/emouse/ 应部分网友要求,最新加入固件库以及开发环境使用入门视频教程,同时提供例程模板 ...

  4. STM32 HAL库详解 及 手动移植

    源: STM32 HAL库详解 及 手动移植

  5. python中利用队列asyncio.Queue进行通讯详解

    python中利用队列asyncio.Queue进行通讯详解 本文主要给大家介绍了关于python用队列asyncio.Queue通讯的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细 ...

  6. Linux 进程间通讯详解二

    消息队列 --消息队列提供了本机上从一个进程向另外一个进程发送一块数据的方法 --每个数据块都被认为有一个类型,接收者进程接收的数据块可以有不同的类型值 --消息队列也有管道一样的不足,就是每个消息的 ...

  7. STM32硬件调试详解

    STM32的基本系统主要涉及下面几个部分: 一.电源 1).无论是否使用模拟部分和AD部分,MCU外围出去VCC和GND,VDDA.VSSA.Vref(如果封装有该引脚)都必需要连接,不可悬空: 2) ...

  8. (转)stm32启动文件详解

    在<<STM32不完全手册里面>>,用的是STM32F103RBT6,所有的例程都采用了一个叫STM32F10x.s的启动文件,里面定义了STM32的堆栈大小以及各种中断的名字 ...

  9. RS232串口通信详解

    串口是计算机上一种非常通用的设备通信协议. ---------------------------------串口的引脚定义: 9芯 信号方向来自 缩写 描述 1 调制解调器 CD 载波检测 2 调制 ...

随机推荐

  1. 从新建文件夹开始构建UtopiaEngine(2)

    本篇序言 摸了两个月的鱼,又一次拾起了自己引擎的框架,开始完善引擎系统,如果非要用现实中的什么东西比喻的话,那么我们目前实现的框架连个脚手架都不是.把这项目这样晾着显然不符合本人的风格,而且要作为毕业 ...

  2. 备战-Java 容器

    备战-Java 容器 玉阶生白露,夜久侵罗袜. 简介:备战-Java 容器 一.概述 容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着k ...

  3. 「CF505E」 Mr. Kitayuta vs. Bamboos

    「CF505E」 Mr. Kitayuta vs. Bamboos 传送门 如果没有每轮只能进行 \(k\) 次修改的限制或者没有竹子长度必须大于 \(0\) 的限制那么直接贪心就完事了. 但是很遗憾 ...

  4. 没事就要多做多练,Shell脚本循环例题做一做

    Shell脚本循环例题                 一.示例1                 二.示例2                 三.示例3                 四.示例4 ...

  5. C语言:整数保存 原码 反码 补码

    #include <stdio.h> /* 本题结果为:-4 short类型占据2字节 ;赋值后实际占据了3个字节,所以有溢出警告提示,结果只保留0xfffc 保存二进制:1111 111 ...

  6. python 函数定义自变量的写法及调用

    import pandas as pd #函数定义时指明自变量,指明自变量的类型,指定自变量的默认值 #函数定义时,可以通过"自变量名称=常量"的方式指定自变量的默认值,调用时可以 ...

  7. Day4 包机制 及JavaDoc文档.

    包机制 为了更好地组织类,java提供了包机制,用于区别类名的命名空间. 包的本质是文件夹 它语句的语法格式为: package pkg1[. pkg2 [.pkg3...] ] ; 一般利用公司域名 ...

  8. Hive开发要知道数据仓库的四个层次设计

    数据仓库:数据仓库全面接收源系统数据,ETL进程对数据进行规范化.验证.清洗,并最终装载进入数据集市,通过数据集市支持系统进行数据查询.分析,整个数据仓库包含四大层次. 1.数据仓库的四个操作    ...

  9. ASM入网小助手卸载

    目录 ASM小助手卸载 写在前的 卸载 解除U盘禁用 写在后的 ASM小助手卸载 写在前的 有些公司内网的上网认证是用的ASM小助手,不过有时候用自己电脑接入公司内网可能会主动下载到ASM入网小助手的 ...

  10. (6java)计算机语言发展史

    (6java)计算机语言发展史 机器语言: 程序是0和1的组合,比如:0000.0001.1100110 汇编语言: 程序比机器语言好理解一点点 高级语言: 比较适合老美,苦了英语差的孩子们了,哈哈. ...