STM32学习笔记<5.UART>

1. 概念

UART( Universal Asynchronous Receiver/Transmitter),通用异步接收器/发送器

2. 背景

两个设备间通信

3. 特点

串行、全双工、异步(这里只讨论异步,同步则需要时钟)

4. 协议层

1. 数据包

起始位(1位低电平)+主体数据(8/9位 低位在前)+校验位(1位。偶校验:主体和校验位的1加起来是偶数,奇校验相反)+停止位(1/1.5/2位高电平),双方只有数据包一致才能正常收发数据

2. 波特率

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

3. 起始和停止信号

数据包的首位就是起始和停止信号,起始位由一个0表示,停止位由0.5、1、1.5、2个1表示

4. 有效数据

规定了数据的长度,一般为8、9位

5. 数据校验

在有效数据后有一个可选的校验位,用于受到干扰时通信出错判断,有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)和无校验(noparity)

5.串口初始化步骤

1.初始化GPIO的时钟

2.初始化GPIO_TX和GPIO_RX引脚的模式,TX要配置成推挽复用,RX要配置成浮空输入

3.初始化UARTx的时钟

4.配置串口的工作参数:波特率、有效字长、数据校验、停止位

5.串口中断优先级配置

6.使能串口中断,一般是接收中断

7.使能串口

8.编写逻辑代码(中断函数)

6.常用寄存器位详解

1. USART_SR(状态寄存器):

1.1 TXE 发送数据寄存器空(判断的是发送数据寄存器)

TXE=1:当发送数据寄存器(TDR)的数据转移到移位寄存器,并且TDR未进新的数据

TXE=0:TDR中有数据

1.2 TC 发送完成(判断的是移位寄存器)

TC=1:当移位寄存器为空(从TDR过来的数据已经全部移给了TX引脚),并且TXE=1(TDR未进新的数据)

TC=0移位寄存器有数据(从TDR过来的数据未能全部移给了TX引脚),或者TXE=0(TDR进了新的数据)

1.3 RXNE 读数据寄存器非空(判断的是接收数据寄存器)

RXNE=1:RDR中有数据

RXNE=0:RDR为空

1.4 三者区别

标志位清除:

TXE--写寄存器DR清零

RXNE--读寄存器DR清零,也可软件手动清零

TC--  读/写寄存器DR清零,也可软件手动清零

备注:(网上看到的,很形象的比喻~)

TXE是指“弹仓”空;
TC是“枪膛”空。
也就是说,你写数据到串口时,是装入弹仓,硬件会将数据移到枪膛,这时,TXE为1,TC为0,STM32硬件的TX脚正在发送数据,但你还可以装入数据到弹仓,装入后,TXE为0,TC为0.
TX发送完一个数据后,立即将数据从弹仓移入枪膛,这时,TXE为1,TC为0.
最后TX发送完数据,你又没有装入新数据,这时。TXE为1,TC为1.

TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断

TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

7. 三种方式显示数据的接收和发送

1.轮询(poll)

1.HAL_UART_Receive

HAL_StatusTypeDef HAL_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

huart:初始化串口的句柄

pData:接收的buff,是一个地址

Size:要接受的大小

Timeout:超时的时间

在规定超时时间内,未接收完Size大小的数据就会返回超时状态。若能发送完,返回ok,退出函数

Timeout=0的时候,只能发送一个字节

2.HAL_UART_Transmit

在规定超时时间内,未发送完Size大小的数据就会返回超时状态。实测115200bps下,1ms能传输11帧(1+8+1=10位)

Timeout不能=0,因为要判断TC,0ms无法完成1个字节的完全发送

备注:这两个函数一般用于重定向发送接收函数

#include<stdio.h>

/* 重定向c库函数printf */
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
    return ch;
}

/* 重定向c库函数getchar,scanf */
int fgetc(FILE *f)
{
    uint8_t ch = 0;
    HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
    return ch;
}

在keil中勾选use Micro LIB,注意如果想要打印中文,要把编码改为GB,因为stm32cubemx默认是utf8的。

2.中断(常用)

实现的功能,接收到以0x0D结尾的字符串,再通过中断发送出去。

1. 使用stm32cubemx生成初始化代码,记得勾选串口全局中断

可以在HAL_UART_MspInit发现除了引脚配置还有内核的中断使能,注意这里只是使能内核的串口中断,需要自己使能外设uart的中断

2.在MX_USART1_UART_Init函数中开启接收中断,增加这么一行

HAL_UART1_Receive_IT(&huart1, (uint8_t *)UART1_temp, 1);

huart1是初始化串口的句柄,UART1_TEMP是元素为1个的数组, 接收长度为1。
长度设置为1的原因:因为这个函数功能是接收到指定长度的数据后会进入一次中断,然后关闭接收中断, 然后调用接收完成函数HAL_UART_RxCpltCallback。想要继续接收数据的话,必须重新调用这个函数,这里我是在HAL_UART_RxCpltCallback处理完接收到的数据后调用的。

 ​如下图:当接收到完整的一帧数据后,会开启发送中断发送数据,然后清掉接收内存

 效果:

3.DMA

和中断实现的功能一样,接收到以0x0D结尾的字符串,然后再发送到串口上

1. 使用stm32cubemx生成初始化代码,Add DMA接收和发送通道

 2.在MX_USART1_UART_Init函数中开启接收中断,增加这么一行

RBuff是长度为1的数组,这里因为需求是连续接收,所以模式要选成循环模式,不然接受完指定长度的数据就会关闭接收中断,当然也可以使用普通模式,然后在发送完成函数中再次调用这句话去使能接收。我这里是使用循环接收模式。

这里是接收到1个字节的长度数据后就会进入接收完成函数,然后接收到指定的数据后就发送出去,如下代码:

 备注:

1.cubemx生成的dma代码的时钟初始化位置不对,需要放到初始化dma之前,不然会导致dma初始化失败

2.dma发送时候,不能立马清楚Rbuff1的内容,因为还没完全发送完成

 效果:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值