USART的使用

通信的概念:

作用:

设备之间进行数据的交换。

分类:

传输方式:

有线通信:电平的变化

  • 串行:传输数据的时候,是1bit 1bit 的传输

    ​ 速度相对较慢,传输距离比较远

​ 串口通信:UART、485、CAN、IIC、SPI....

  • 并行:传输数据的时候 并发,多个位同时发送

    ​ 速度快,抗干扰性差:传输距离近

​ CPU 跟存储区之间通信。 屏幕:(数据量大)、8080

无线通信:

  • 短距离无线通信:

    蓝牙、WIFI、红外、2.4G:无线鼠标,ZIGBEE、Lora:2-5Km

  • 远距离无线通信:

    2G、3G、4G、5G

通信的方式

  • 单工:传输数据时,设备只能发送或者是只能接收。

  • 双工:

半双工:可以实现发送和接收,但是不能同时完成。

​ 例如:对讲机

全双工:可以同时实现发送和接收。

​ 例如:手机

  • 同步:通信设备之间有共同的时钟线。设备按照这个时钟来通信。

    谁提供时钟?两个设备之间都有时钟,谁起主导作用,用谁的。谁是主机谁发送。

  • 异步:通信设备各自有自己的时钟,要求通信速率要一致。

UART通信

  • 通信方式:串行 异步 全双工 的通信方式

    全双工也可以做成单工。

  • 使用范围广:

    串口本身不厉害,转成其他的通信方式,就很厉害了。

    串口转蓝牙(设备本身没有蓝牙功能,可以转到其他的通信方式)

    串口转 WIFI

    串口转232电平、串口转485、

    一些传感器:串口的传感器

image-20201117100316897

硬件结构

全双工:一根线用来发,一根线用来收。

​ 两个设备之间要共地,就是有一个电平相同(GND)。参考电位保持一致。没有共地,数据没有参考价值。

image-20201121104449321

T:发送管脚

R:接收管脚

GND

连接方式:

  • 直接相连:两个设备用相同的逻辑电平TTL,共地(参考电平相同)

​ 芯片与芯片之间用UART时

image-20201117101353737
  • 间接相连:用到电平转换芯片(芯片的电压不同)
image-20201117101509583

硬件接口:

UART、232、485

不同的硬件接口,是可以用相同的协议。

不同的硬件接口电平不同。

协议层:232协议

  • 协议是传输方式

一般来说,总线空闲为高电平

开始位 数据位 校验位 停止位
电平状态 高/低 高/低
数据位数 1位 5-8位 1位 1-2位

奇偶校验:错偶数个 都检测不出来。一般来说 奇偶校验 不需要开。

偶校验:数据位中1的个数 + 校验位1的个数 = 偶数

奇校验:数据位中1的个数 + 校验位1的个数 = 奇数

嵌入式设备串口采用比较常用的协议格式是:

1开始 + 8个数据 + 不开校验 + 1个停止 = 10bit

不开校验的原因:1、不准;2、速度慢

数据比较:

一帧数据 是 10个bit 11个bit

假设:1s能够传输 9600bit

​ 一帧数据10bit 1s 960 字节

​ 一帧数据11bit 1s 9600 / 11 = 872 字节

一般不开校验:而是再指定更上层的协议格式,进行校验

波特率:

数据传输的速率。bit/s

每秒钟能够发送的数据 “位” 数

波特率要保持一致

STM32串口

数据结构

1个开始位 8/9数据位

奇偶校验位 0.5 / 1 / 1.5 / 2个停止位

常用的数据格式:1个开始位 + 8个数据位 + 无奇偶校验 + 1个停止位

时钟源的配置

FCLKx(x=1, 2) 的选择是根据USART外设所在的外设总线决定的。

TE、RE 两个使能位,

如果关闭了发送/接收,就不会再提供波特率,数据也就不完整了,所以要等到数据发送完成后在关闭使能位

image-20201121153010660

波特率的计算方法

image-20201117111231079

波特率的寄存器设置

DIV_Mantissa[11:0]: USARTDIV的整数部分

DIV_Fraction[3:0]: USARTDIV的小数部分

小数部分在寄存器的值如何计算?

DIV_Fraction = 小数部分 * 16, 向上取整(四舍五入), 如果有进位,整数部分要加上进位。

发送数据

配置步骤
1. 通过在USART_CR1寄存器上置位UE位来激活USART
2. 编程USART_CR1的M位来定义字长。
3. 在USART_CR2中编程停止位的位数。
4. 如果采用多缓冲器通信,配置USART_CR3中的DMA使能位(DMAT)。按多缓冲器通信中
的描述配置DMA寄存器。
5. 利用USART_BRR寄存器选择要求的波特率。
6. 设置USART_CR1中的TE位,发送一个空闲帧作为第一次数据发送。
7. 把要发送的数据写进USART_DR寄存器(此动作清除TXE位)。在只有一个缓冲器的情况
下,对每个待发送的数据重复步骤7。
8. 在USART_DR寄存器中写入最后一个数据字后,要等待TC=1,它表示最后一个数据帧的
传输结束。当需要关闭USART或需要进入停机模式之前,需要确认传输结束,避免破坏
最后一次传输。
 

字节发送过程

清零TXE位 是通过 对数据寄存器的写 操作来完成的。 TXE位由硬件来设置,它表明:
● 数据已经从TDR移送到移位寄存器,数据发送已经开始
● TDR寄存器被清空
● 下一个数据可以被写进USART_DR寄存器而不会覆盖先前的数据

如果TXEIE位被设置,此标志将产生一个中断。

当一帧发送完成时(停止位发送后)并且设置了TXE位, TC位被置起,如果USART_CR1寄存器
中的TCIE位被置起时,则会产生中断

发送的状态寄存器

1、TXE:

如果该标志位被置1,就说明 “数据寄存器” 的内容被转移到了 “移位寄存器”;就可以往DR寄存器里面写数据,此时数据不会被覆盖。

image-20201121155421209

2、TC:

如果该标志位被置1,就说明 “一帧数据” 发送完成。

image-20201121155624899

3、TE

image-20201121162440918

一般来说发送数据 是 不需要开中断的 ,想发数据的时候,就把数据发出去就行了。

接收数据

配置步骤
1. 将USART_CR1寄存器的UE置1来激活USART。
2. 编程USART_CR1的M位定义字长
3. 在USART_CR2中编写停止位的个数
4. 如果需多缓冲器通信,选择USART_CR3中的DMA使能位(DMAR)。按多缓冲器通信所
要求的配置DMA寄存器。
5. 利用波特率寄存器USART_BRR选择希望的波特率。
6. 设置USART_CR1的RE位。激活接收器,使它开始寻找起始位。
 

RXNE位被置位。它表明移位寄存器的内容被转移到RDR。换句话说,数据已经被接收并且可以被读出(包括与之有关的错误标志)。

如果RXNEIE位被设置,产生中断。

接收数据的状态位

1、RXNE

接收寄存器非空,我们就可以读取寄存器DR的值。

image-20201121161624632

2、RXNEIE

一般都要打开中断,在中断里面接收数据,这样不会阻塞CPU的运行。

image-20201121161946118

3、RE

image-20201121162503074

DR寄存器的介绍

数据寄存器是9位的。

image-20201121162549484

DR寄存器连接两个寄存器 TDR 和 RDR ;发送时,就是往TDR寄存器写,接收时,就是从RDR寄存器读。但是操作的同一个寄存器。

image-20201117164925185

如何读和写寄存器???

赋值 就是 读 和 写 写:USART1->DR = 'A'; 读:data = USART1->DR;

引脚配置

对于芯片:

image-20201117134649303 image-20201121163309867

串口调试

串口调试助手:推荐使用微软商城。

image-20201117142001524

串口助手的接收和发送

  • 串口助手接收到的是 “字符”(8位数)。

  • 串口发送

因为数据时 8bit 的,所以每次的发送应该是发送 8bit 的数据。

1、字符(8bit),每次发送一个字符,就是8bit 的数据。

2、16进制的数据(4bit),因为每个16进制数,只有4bit,所以说每次发16进制的数,要发两个。(8bit)

STM32串口接收数据(从串口助手发送数据时),数据格式。

1、16进制:一个16进制的数是4位,一个数据是两个16进制数,发一个接收的数据有问题,所以说一次要发送两个。

以下是发送接收到一个1蜂鸣器响,但是发送一个16进制的1是没有任何反应的。

image-20201117192628366

发送一个16进制的01,这时候就可以收到你想要的数据1

2、字符:一个字符是8位

printf 发送

如何在STM32代码中使用 printf ? ? ?

构造一个printf函数

STM32中构造的 printf 函数,只需要重写 pfutc 函数。

1、在设置里面勾选 “微库”

image-20201117145201053

2、重写fputc

//包含头文件,在哪使用printf函数,就在那加入
#include "stdio.h"	
//重写printf函数
int fputc(int ch, FILE * Stream)
{
	while((USART1->SR & (1<<6)) == 0);	//等待发送完成,阻塞型发送
	USART1->DR = ch;
	return ch;
}

完成了 fputc函数的重写,就可以使用printf 函数,打印数据了。

相当于一个字符一个字符的往 “串口文件” 打。 执行效率慢,在中断时尽量不要用。

中断的引入

为什么进入中断??

发送数据和接收数据的时候,需要等到他的寄存器空/寄存器有数据,这个时候,CPU处于被阻塞的状态,如果一直不发数据,CPU只能等;如果不一直等,就会使得重要的数据不能及时接收。

开启中断

对于STM32的串口,有很多中断,但是一般只需要开启 接收中断 ,发送中断不需要开,想发数据的时候,直接发送就行了

开启方法:将对应的中断寄存器的值写1,就使能了对应的中断。

中断服务函数

串口的中断有很多,但是只有一个中断服务函数,所以说,当进入中断服务函数的时候,要先判断是哪个中断源引起的中断。

  • 中断的到来判断:判断状态寄存器是否被置1
  • 中断的清除:看stm32对应的SR寄存器,看是否需要软件清零

串口中断是多个中断,要判断是那个中断

image-20201117150453942

清中断标志位

if(USART1->SR & (1<<5))
{
	data = USART1->DR;	//读取DR寄存器,就会清除接收中断
}

接收多个字符

1、 阻塞型 接收

存在的问题,虽然能够接收多个字符,但是属于阻塞型程序的运行,程序的其他端口会出问题,比如说按键的检测。

image-20201117211017615

2、发送中断 接收

当接收寄存器非空时,就会产生中断,进入中断服务函数,在中断中接收数据。

每一次中断就是一帧数据。

但是要清除中断标志位,清除方法:读取DR寄存器的值。

STM32串口配置代码

//配置串口
void Usart1_Config(uint32_t brr)
{
	RCC->APB2ENR |= (1<<2) ;
	RCC->APB2ENR |=	(1<<14);
	//PA9		复用推挽输出
	GPIOA->CRH &= ~(0xF<<4);
	GPIOA->CRH |=  (0xB<<4);
	//PA10		浮空输入/上拉
	GPIOA->CRH &= ~(0xF<<8);
	GPIOA->CRH |=  (0x8<<8);
	GPIOA->ODR |=  (1<<10);
	
	float USARTDIV = 72000000.0f/16/brr;	//浮点型,否则将不会产生小数
	uint16_t Mantissa = USARTDIV;	//整数部分
	uint16_t Fraction = (USARTDIV-Mantissa)*16;	//小数部分
	USART1->BRR = (Mantissa<<4) | Fraction;	//写入BRR寄存器
	
	//USART1->CR1 = 0;	//CR1直接置1,很多按照默认的都可以不用管。
	USART1->CR1 |= (1<<13);	//使能
	USART1->CR1 &=~(1<<12);
	USART1->CR1 |= (1<<3)|(1<<2)|(1<<5);
	USART1->CR2 &= ~(0x3<<12);
	//NVIC
	NVIC_SetPriority(USART1_IRQn, 3);
	NVIC_EnableIRQ(USART1_IRQn);
}
//阻塞型 , 发送数据
void Usart1_SendByte(u8 data)
{
	while((USART1->SR & (1<<6)) == 0);	//等待寄存器的第6位被置一
	USART1->DR = data;
}

void Usart1_SendString(u8 * str, u16 len)
{
	while(len--)
	{
		Usart1_SendByte(*str--);
	}
}
//阻塞型, 接收数据
u8 Usart1_RecvByte(void)
{
	while((USART1->SR & (1<<5)) == 0);	//等待寄存器的第6位被置一
	return USART1->DR;
}
//中断接收数据
u8 data = 0;
void USART1_IRQHandler(void)
{
	//接收非空
	if(USART1->SR & (1<<5))
	{
		data = USART1->DR;
	}
}
posted @ 2020-11-21 16:59  啊振不坏  阅读(935)  评论(0)    收藏  举报
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css