STM32学习笔记Day4

本文详细介绍了通信基础知识,包括串行与并行通信、数据传输方向、同步与异步通信,然后重点讲解了STM32F1的USART串口通信,包括波特率设定、数据帧格式、起始位和停止位,以及串口初始化过程和中断处理。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

今天学习了STM32的串口通信,USART串口通信的目的是将一个设备的数据传送到另一个设备,扩展硬件系统。在学习串口通信之前我们还需要了解一点通信原理的知识。

一、通信基础知识

1、数据通信方式

按数据通信方式分类,可分为串行通信跟并行通信两种。串行和并行的对比如下图所示:
在这里插入图片描述
串行通信的基本特征是数据逐位顺序依次传输,优点是传输线少、布线成本低、灵活度高等优点,一般用于近距离人机交互,特殊处理后也可以用于远距离,缺点就是传输速率低。
而并行通信是数据各位可以通过多条线同时传输,优点是传输速率高,缺点就是布线成本高,抗干扰能力差因而适用于短距离、高速率的通信。

2、数据传输方向

根据数据传输方向,通信又可以分为全双工、半双工和单工通信。全双工、半双工、单工通信的比较如下图所示:

在这里插入图片描述
单工是指数据传输仅能沿一个方向,不能实现反方向传输,如校园广播。
半双工是指数据传输可以沿着两个方向,但是需要分时进行,如对讲机。
全双工是指数据可以同时进行双向传输,日常的打电话属于这种情形。
这里注意全双工和半双工通信的区别:半双工通信是共用一条线路实现双向通信,而全双
工是利用两条线路,一条用于发送数据,另一条用于接收数据。

3、数据同步方式

根据数据同步方式,通信又可分为同步通信和异步通信。同步通信和异步通信比较如下图所示:
在这里插入图片描述
同步通信要求通信双方共用同一时钟信号,在总线上保持统一的时序和周期完成信息传输。
优点:可以实现高速率、大容量的数据传输,以及点对多点传输。
缺点:要求发送时钟和接收时钟保持严格同步,收发双方时钟允许的误差较小,同时硬件复杂。

异步通信不需要时钟信号,而是在数据信号中加入开始位和停止位等一些同步信号,以便使接收端能够正确地将每一个字符接收下来,某些通信中还需要双方约定传输速率。
优点:没有时钟信号硬件简单,双方时钟可允许一定误差。
缺点:通信速率较低,只适用点对点传输。

4、通信速率

在数字通信系统中,通信速率(传输速率)指数据在信道中传输的速度,它分为两种:传信率和传码率。
传信率:每秒钟传输的信息量,即每秒钟传输的二进制位数,单位为 bit/s(即比特每秒),因而又称为比特率。
传码率:每秒钟传输的码元个数,单位为 Baud(即波特每秒),因而又称为波特率。

二、串口通信协议简介

串口通信是一种设备间常用的串行通信方式,串口按位(bit)发送和接收字节。尽管比特字节(byte)的串行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。串口通信协议是指规定了数据包的内容,内容包含了起始位、主体数据、校验位及停止位,双方需要约定一致的数据包格式才能正常收发数据的有关规范。在串口通信中,常用的协议包括RS-232、RS-422 和 RS-485 等
串口通信的数据包由发送设备的 TXD 接口传输到接收设备的 RXD 接口。在串口通信的协议层中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据,其组成如图所示。低电平代表启动位,高电平代表停止位。
在这里插入图片描述

1、波特率

是串口异步通信,异步通信是不需要时钟信号的,但是这里需要我们约定好两个设备的波特率。波特率表示每秒钟传送的码元符号的个数,所以它决定了数据帧里面每一个位的时间长度。两个要通信的设备的波特率一定要设置相同。

2、数据帧格式

数据帧格式需要我们提前约定好,串口通信的数据帧包括起始位、停止位、有效数据位以及校验位。

3、起始位和停止位

串口通信的一个数据帧是从起始位开始,直到停止位。数据帧中的起始位是由一个逻辑 0 的数据位表示,而数据帧的停止位可以是 0.5、1、1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。

4、有效数据位

数据帧的起始位之后,就接着是数据位,也称有效数据位,这就是我们真正需要的数据,有效数据位通常会被约定为 5、6、7 或者 8 个位长。有效数据位是低位(LSB)在前,高位(MSB)在后。

三、STM32F1的串口简介

1、串口介绍

STM32F1 的串口分为两种:USART(即通用同步异步收发器)和 UART(即通用异步收发器)。UART 是在 USART 基础上裁剪掉了同步通信功能,只剩下异步通信功能。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用串口通信基本都是异步通信。STM32F1 有 3 个 USART 和 2 个 UART,其中 USART1 的时钟源来于 APB2 时钟,其最大频率为 72MHz,其他 4 个串口的时钟源可以来于 APB1 时钟,其最大频率为 36MHz。

2、USART框图

在这里插入图片描述

3、USART信号引脚

TX:发送数据输出引脚
RX:接收数据输入引脚
SCLK:发送器时钟输出,适用于同步传输
SW_RX:数据接收引脚,属于内部引脚,用于智能卡模式
IrDA_RDI:IrDA 模式下的数据输入
IrDA_TDO:IrDA 模式下的数据输出
nRTS:发送请求,若是低电平,表示 USART 准备好接收数据
nCTS:清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送。

4、数据寄存器

USART_DR 包含了已发送或接收到的数据。由于它本身就是两个寄存器组成的,一个专门给发送用的(TDR),一个专门给接收用的(RDR),该寄存器具备读和写的功能。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。

5、串口初始化

第⼀步,开启时钟,把需要⽤的 USART 和 GPIO 的时钟打开
第⼆步, GPIO 初始化,把 TX 配置成复⽤输出, RX 配置成输⼊
第三步,配置 USART ,直接⽤⼀个结构体,就可以配置好所有参数
第四步,如果只需要发送的功能就直接开启 USART ,如果需要接收的功能,还需要再配置中断,在开启 USART 之前,加上 ITConfig 和 NVIC 的代码。

void USART_DeInit(USART_TypeDef* USARTx);//回复缺省值函数
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);//开启串口函数
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState);//开启串口中断函数
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq,FunctionalState NewState);//开启USART到DMA的触发通道函数
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);//发送数据函数
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);//接收数据
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_tUSART_FLAG);//在中断函数外获取标志位函数
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);//在中断函数外清除标志位函数
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);//在中断函数内获取标志位函数
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_tUSART_IT);//在中断函数内清除标志位函数
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
void Serial_Init(void)
{
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 
 GPIO_InitTypeDef GPIO_InitStructure;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 USART_InitTypeDef USART_InitStructure;
 USART_InitStructure.USART_BaudRate = 9600;//波特率
 USART_InitStructure.USART_HardwareFlowControl =
 USART_HardwareFlowControl_None;//⽆流控
 USART_InitStructure.USART_Mode = USART_Mode_Tx;//发送模式
 USART_InitStructure.USART_Parity = USART_Parity_No;//⽆校验位
 USART_InitStructure.USART_StopBits = USART_StopBits_1;//停⽌位⼀位
 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位1字USART_Init(USART1, &USART_InitStructure);
 
 USART_Cmd(USART1, ENABLE);//开启串⼝
}
 
void Serial_SendByte(uint8_t Byte)//发送字符
{
 USART_SendData(USART1, Byte);
 while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
 
void Serial_SendArray(uint8_t *Array, uint16_t Length)//发送数组
{
 uint16_t i;
 for (i = 0; i < Length; i ++)
 {
 Serial_SendByte(Array[i]);
 }
}
 
void Serial_SendString(char *String)//发送字符串
{
 uint8_t i;
 for (i = 0; String[i] != '\0'; i ++)
 {
 Serial_SendByte(String[i]);
 }
}
 
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
 uint32_t Result = 1;
 while (Y --)
 {
 Result *= X;
 }
 return Result;
}
 
void Serial_SendNumber(uint32_t Number, uint8_t Length)//发送数字
{
 uint8_t i;
 for (i = 0; i < Length; i ++)
 {
 Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 +'0');
 }
}
 
int fputc(int ch, FILE *f)//重定向printf
{
 Serial_SendByte(ch);
 return ch;
}
 
void Serial_Printf(char *format, ...)//重定向printf多串⼝使⽤
{
 char String[100];
 va_list arg;
 va_start(arg, format);
 vsprintf(String, format, arg);
 va_end(arg);
 Serial_SendString(String);
}

6、串口收发数据包

数据包的作⽤是:把⼀个个单独的数据给打包起来,⽅便进⾏多字节的数据通信.因为接收⽅可能从任意的位置接收,所以可能出现数据错位的现象,我们需要⼀种⽅式把数据进⾏分割,数据分割开来,分割成⼀批批数据包,接收的时候就知道了对应的数据. 数据包的任务,就是把同⼀批的数据进⾏打包和分割。

串口数据包,通常使用的是额外添加包头包尾的这种方式。防⽌数据包包头包尾和数据重复的⽅法,第⼀种,限制载荷数据的范围,在发送的时候对数据进⾏限幅,第⼆种,尽量使⽤固定长度的数据包,第三种,增加包头包尾的数量,并且让它尽量呈现出载荷数据出现不了的状态。

在这里插入图片描述
文本数据包
在这里插入图片描述
数据包的收发流程:
在这里插入图片描述

void Serial_SendPacket(void)
{
	Serial_SendByte(0xFF);
	Serial_SendArray(Serial_TxPacket, 4);
	Serial_SendByte(0xFE);
}
void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;//状态机变量
	static uint8_t pRxPacket = 0;//指示接收个数
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		uint8_t RxData = USART_ReceiveData(USART1);
		
		if (RxState == 0)
		{
			if (RxData == 0xFF)
			{
				RxState = 1;
				pRxPacket = 0;
			}
		}
		else if (RxState == 1)
		{
			Serial_RxPacket[pRxPacket] = RxData;
			pRxPacket ++;
			if (pRxPacket >= 4)
			{
				RxState = 2;
			}
		}
		else if (RxState == 2)
		{
			if (RxData == 0xFE)
			{
				RxState = 0;
				Serial_RxFlag = 1;
			}
		}
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值