通信
通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统(扩展STM32系统,与别的模块互联,实现外挂模块和读取外挂模块数据)
通信协议:制定通信的规则,通信双方按照协议规则进行数据收发
相关概念
全双工:通信双方能够同时进行双向通信,发送线路与接收线路互不影响(两根通信线)(打电话)
半双工:通信双方在同一时间只能进行单向通信(发电报)
单工:数据只能从一个设备到另一个设备,而不能反着来(收音机)
同步:有单独的时钟线,接收方可以在时钟信号的指引下进行采样
异步:没有始时钟线,需要双方约定一个采样频率,加入帧头帧尾进行采样位置的对齐
单端:引脚的高低电平都是对GND的电压差,所以必须要接GND
差分:靠两个差分引脚的电压差来传输信号的(使用差分信号可以极大地提高看干扰特性,所以一般差分信号的传输速度和距离都会非常高,性能也不错)
串行通信(Serial communication)
是指在计算机总线或其他数据通道上,每次传输一个位元数据,并连续进行以上单次过程的通信方式。与之对应的是并行通信,它在串行端口上通过一次同时传输若干位元数据的方式进行通信。串行通信被用于长距离通信以及大多数计算机网络,在普通应用场合,电缆和同步化使并行通信面临实际应用问题。
(1)异步串行通信 异步串行通信所传输的数据格式(也称为串行帧)由1个起始位、7~9个数据位、1~2个停止位(含1.5个停止位)和1个校验位组成。起始位约定为0,空闲位约定为1。在异步通信方式中,接收器和发送器有各自的时钟,它们的工作是非同步的。
不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,
或者把主体数据进行打包,以数据帧的格式传输数据。
(2)同步串行通信 同步串行通信中,发送器和接收器由同一个时钟源控制。
使用时钟信号进行数据同步,在同步通讯中,数据信号所传输的内容绝大部分就是有效数据,而异步通讯中会包含有帧的各种标识符,所以同步通讯的效率更高,但是同步通讯双方的时钟允许误差较小,而异步通讯双方的时钟允许误差较大。
3)波特率及时钟频率** 波特率BR是单位时间传输的数据位数,即单位:bps(bit per second)1bps = 1bit/s。采用异步串行,互相通信甲乙双方必须具有相同的波特率,否则无法成功地完成数据通信。同步通信中通过主从设备的同步时钟完成数据同步,因此很少有波特率说法,当然也可以看作数据传输的波特率即为同步时钟频率。
4)比特率(Bit Rate)
如果每个符号代表 8 个比特,则每个符号可以传输 8 个比特的数据。
因此,波特率为 9600 波特,每个符号传输 8 个比特,所以比特率为 9600 * 8 = 76800 比特每秒(bps)。 这意味着在每秒钟,设备 A 和设备 B 之间会传输 76800 个比特的数据。
常见串行通信方式
(1) UART
UART串行通信方式称作为异步串行通信,主要通过两个引脚发送(TXD)和接收(RXD)实现数据的发送和接收,由于没有时钟线,所以是一种全双工的异步串行通信接口,通信数据的同步利用设置好的波特率来实现,波特率的最大速率传统上是115200bps但现在对于一些微控制器如STM32芯片其波特率可以达到2Mbps。
UART帧结构
当两个设备需要通过UART协议进行通讯时,它们需要同时约定好以下内容:
-
每一位信号的时间长度T(波特率 = 1/T)
-
帧结构中每一项的具体位数
-
是否有校验位,以及校验位的机制(奇/偶/..)
ODD奇校验EVEN偶校验(以数据位中有奇数/偶数个1来校验)
(2)USART
Universal Synchronous/Asynchronous Receiver/Transmitter
(通用同步/异步收发传输器)
USART 串行通信是在UART的基础上增加了同步通信方式,称为同步/异步串行通信。当设置为异步串行通信时,和前面所述的UART的连接和特性是一模一样的。当设置为同步串行通信时,此时在通信接口增加了一个时钟线,用于同步时钟用从而不再需要设置波特率。
TX(Transmit)引脚:发送数据
RX(Receive)引脚:接收数据
看原理图,确定USART1的发送端TX、接收端RX的引脚,分别是PA9、PA10
我们要用跳线帽,短接左右两边引脚这样才能实现通信
与GPIO使用输入输出模式不同,我们需要使用GPIO的复用模式。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用模式
上述代码只是将GPIO口设置成了复用模式,我们还需要对每个引脚的复用模式进行设置。
这里如果要一次复用多个引脚,不能使用或,要调用两次复用函数。
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
代码
//usart代码
#include "usart.h"
#include "stdio.h"
/*
串口通信
//用串口软件发送hello ,并且在软件上回显
代码思路:
1.查看原理图确定串口引脚 USART1 USART1_TX PA9 USART1_RX PA10
2.GPIO配置
...
选择复用模式
3.USART1配置
a.结构体申明 USART_InitTypeDef
b.时钟使能 使能USART1的时钟
c.将GPIO引脚复用为串口 GPIO_PinAFConfig
d.配置结构体
波特率、模式(收/发)、终止位、校验位、数据位、流控
c.初始化 EXTI_Init
串口中断(可选)
和前面学习的中断一样,需要使能接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
重写串口中断服务函数
USART1_IRQHandler
4.使能串口
USART_Cmd
5.使用串口
调用USART_SendData / USART_ReceiveData
6.串口重定向
重写stdio
*/
void usart1_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//通电还是断电
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //串口1时钟使能
//开始配置F端口下的9号跟10号引脚。
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF; //复用模式
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉模式,暂时没讲
GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed; //引脚响应速度
GPIO_Init(GPIOA, &GPIO_InitStructure);
//将PA9、PA10复用为串口1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8位数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //收和发都使能,必须进行或操作
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
USART_Init(USART1, &USART_InitStructure);
/*
中断
*/
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //外部中断4 EXTI4
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//使能接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
char *p = "hello";
//发单个字符
void usart_sendbyte(USART_TypeDef* usrUSARTx, uint16_t ch)
{
USART_SendData(usrUSARTx,ch);
//等待数据发送寄存器为空
//USART_FLAG_TXE 表示寄存器可以写入下一个数据了,当为SET的时候可以写入
while(USART_GetFlagStatus(usrUSARTx, USART_FLAG_TXE) == RESET);
}
//发字符串
void usart_string(USART_TypeDef* usrUSARTx, char *str)
{
int i= 0;
do
{
usart_sendbyte(usrUSARTx, *(str+i));
i++;
}
while(*(str+i) != '\0' );
}
int fputc(int ch, FILE *stream)
{
USART_SendData(USART1 ,(uint16_t)ch);
//等待数据发送寄存器为空
//USART_FLAG_TXE 表示寄存器可以写入下一个数据了,当为SET的时候可以写入
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
注意:
-
在.c文件里写的函数,一点要在.h文件里面一对一声明
-
重写中断服务函数时要一一对照库函数
-
在主函数里面调用各函数初始化各部分才能正常工作
-
在用printf函数时要重写int fputc(int ch, FILE *stream)函数,加头文件#include "stdio.h",并且要给微库打钩,这样printf才能在串口正常输出。
//重写串口中断服务函数
void USART1_IRQHandler(void)
{
uint16_t rev;
//判断串口接收中断标志是否被置位
if(USART_GetITStatus(USART1 ,USART_IT_RXNE) == SET)
{
rev = USART_ReceiveData(USART1);
//判断亮灯
//清楚中断标志
USART_ClearITPendingBit(USART1 ,USART_FLAG_RXNE);
}
}
//主函数
printf("hello world");
usart_sendbyte(USART1, 'B');
rev = USART_ReceiveData(USART1);
if(rev == 'a')
{
usart_sendbyte(USART1, rev);
//点亮led
GPIO_ToggleBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10);
yanshi();
}
附:
I2C(Inter-Integrated Circuit):I2C是一种双向、串行、同步的通信协议,由Philips公司开发。 它使用两根线进行通信:SCL(时钟线)和SDA(数据线)。 I2C支持多主设备和多从设备,每个设备具有唯一地址,通过地址识别实现数据的发送和接收。 主设备控制时钟线,负责同步数据传输的时序。
SPI(Serial Peripheral Interface):SPI是Motorola公司推出的一种同步串行接口技术。 它以主从方式工作,需要至少四条线:SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。 SPI通信是高速、全双工、同步的,适用于高速数据传输的场景。 当有多个从设备时,可以通过片选信号选择与主设备进行通信的从设备。
相关知识:
串行通信:是一种数据一位一位按顺序传送的通信方式,相比并行通信,它传输速度慢,但传输距离较远且成本较低。
总线:是一种传输数据的通道,它可以在多个设备之间共享,实现设备间的数据通信。
主从设备:在串行通信中,通常有一个主设备负责控制通信的时序和数据流,而从设备则按照主设备的指令进行数据的发送或接收。 在选择USART、UART、I2C或SPI协议时,需要根据应用需求、硬件支持以及通信性能等因素进行综合考虑。例如,如果需要灵活支持同步和异步通信,可以选择USART;如果只需要简单的异步通信,UART可能是更好的选择;如果需要连接多个设备并进行高速数据传输,SPI可能更适合;而如果需要实现设备间的双向通信并控制数据传输的时序,I2C则是一个不错的选择。
原文链接:串行通信大比拼:USART、UART、I2C、SPI有何不同?_spi i2c usart uart cec-CSDN博客