UART(通用异步收发传输器)功能实现与配置
UART(Universal Asynchronous Receiver/Transmitter)是嵌入式系统中常用的串行通信外设,用于实现设备间的异步数据传输(如开发板与电脑的串口调试)。本文详细讲解了 UART1 的硬件配置、寄存器初始化、收发功能实现及使用方法。
一、UART 概述与硬件基础
I.MX6ULL UART 核心特性
兼容 TIA/EIA-232F 标准,最高传输速率可达 5Mbit/s;
支持 8/9 位数据位、1/2 位停止位、奇 / 偶 / 无校验;
支持自动波特率检测(本次实验禁用,使用固定 115200 波特率);
时钟源默认采用pll3_80m(80MHz),可通过分频适配不同波特率;
本次实验使用UART1,通过 USB-TTL 模块与电脑串口调试助手通信。
硬件引脚配置
I.MX6ULL 的 UART1 需通过引脚复用实现功能,开发板上 UART1 的 TX/RX 引脚与 GPIO 复用,具体配置如下:
功能 | 引脚名称 | 复用配置 | 电气属性配置值 |
---|---|---|---|
UART1_TX | UART1_TX_DATA | 复用为 UART1 发送引脚 | 0x10B0 |
UART1_RX | UART1_RX_DATA | 复用为 UART1 接收引脚 | 0x10B0 |
电气属性 0x10B0 含义(参考IOMUXC引脚配置规则):
- bit16(HYS):0(关闭迟滞比较器);
- bit15:14(PUS):01(47K 上拉电阻,防止引脚悬空);
- bit12(PKE):1(使能上下拉功能);
- bit7:6(SPEED):10(中速 100MHz);
- bit5:3(DSE):110(驱动能力 R0/6);
- bit0(SRE):0(低压摆率,降低 EMC 干扰)。
二、UART 初始化实现(寄存器配置)
UART 的初始化核心是引脚复用配置与寄存器参数设置,需按步骤完成时钟、数据格式、波特率等配置。以下基于uart.c的init_uart1函数展开讲解。
引脚复用与电气属性配置
通过 NXP SDK 提供的IOMUXC_SetPinMux和IOMUXC_SetPinConfig函数,将指定引脚复用为 UART1 功能,并配置电气特性:
c
#include "uart.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
void init_uart1(void)
{
// 1. 引脚复用配置:将引脚复用为UART1_TX/RX
IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0); // TX引脚复用
IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0); // RX引脚复用
// 2. 引脚电气属性配置:上拉、速度、驱动能力等
IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10B0);
IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10B0);
UART 核心寄存器初始化
I.MX6ULL 的 UART 通过多个控制寄存器配置工作模式,需按顺序初始化(关键寄存器及作用如下)。
关闭 UART 并清零配置
先清零UCR1(控制寄存器 1)和UCR2(控制寄存器 2),确保初始状态无残留配置:
c
UART1->UCR1 = 0; // 关闭UART,清零所有控制位
UART1->UCR2 = 0; // 清零接收/发送控制配置
配置 UCR2(数据格式与收发使能)
UCR2是 UART 配置的核心,用于设置数据位、停止位、收发使能等关键参数:
c
// UCR2配置:IRTS(忽略RTS) + WS(8位数据) + TXEN(发送使能) + RXEN(接收使能)
UART1->UCR2 |= (1 << 14) | (1 << 5) | (1 << 2) | (1 << 1);
// 说明:
// bit14(IRTS):1=忽略RTS引脚(无需硬件流控)
// bit5(WS):1=8位数据位(默认无校验)
// bit2(TXEN):1=使能发送功能
// bit1(RXEN):1=使能接收功能
配置 UCR3(接收路径选择)
UCR3的RXDMUXSEL位(bit2)是 I.MX6ULL 手册强制要求配置的位,必须设为 1 以选择正确的接收路径:
c
UART1->UCR3 |= (1 << 2); // RXDMUXSEL=1,选择正确的接收数据路径
配置 UFCR(时钟分频)
UFCR(波特率预分频寄存器)用于设置 UART 时钟的预分频系数,确保波特率计算基于正确的时钟频率:
c
UART1->UFCR |= (5 << 7); // 预分频系数=5(对应1分频,时钟源为80MHz)
// 说明:I.MX6ULL UART时钟源默认是pll3_80m(80MHz),此处配置为1分频,实际时钟=80MHz
配置 UBIR 与 UBMR(波特率设置)
波特率由UBIR(波特率整数寄存器)和UBMR(波特率分子寄存器)共同决定,计算公式如下(手册 P3592):
BaudRate= 16× (UBIR+1)/(UBMR+1) × Ref Freq
参数说明:Ref Freq=80MHz(UART 时钟),目标波特率 = 115200;
计算结果:代入公式得 (UBIR+1)/(UBMR+1) ≈43.402,取UBIR=999、UBMR=43402(验证:80MHz/(16∗(43402+1)/(999+1))≈115200)。
代码配置如下:
c
UART1->UBIR = 999; // 波特率整数部分
UART1->UBMR = 43402; // 波特率分子部分
使能 UART
最后通过UCR1的UARTEN位(bit0)使能 UART1:
c
UART1->UCR1 |= (1 << 0); // 使能UART1
}
三、UART 收发功能实现
基于 UART 状态寄存器(USR2)的状态位判断,实现单字符发送、字符串发送、单字符接收功能,确保数据传输的完整性。
单字符发送(putc)
发送单个字符时,需等待发送缓冲(TxFIFO)和移位寄存器为空(USR2->TXDC位为 1),避免数据覆盖:
c
void putc(unsigned char ch)
{
// 等待发送完成(TXDC=1:发送缓冲和移位寄存器为空)
while((UART1->USR2 & (1 << 3)) == 0);
UART1->UTXD = ch; // 将字符写入发送数据寄存器(UTXD)
}
字符串发送(puts)
通过循环调用putc实现字符串发送,直到遇到字符串结束符'\0':
c
void puts(const char *p)
{
while(*p) // 遍历字符串,直到'\0'
{
putc(*p++); // 逐个发送字符
}
// 可选:添加换行符(适配串口助手换行显示)
// putc('\r');
// putc('\n');
}
单字符接收(getc)
接收单个字符时,需等待接收缓冲有数据(USR2->RDR位为 1),避免读取无效数据:
c
unsigned char getc(void)
{
// 等待接收完成(RDR=1:接收缓冲至少有1个字节数据)
while((UART1->USR2 & (1 << 0)) == 0);
return UART1->URXD & 0xFF; // 读取接收数据寄存器(URXD),保留低8位
}
适配标准库的raise函数
移植printf等标准库函数时需提供raise函数(空实现即可,用于异常处理):
c
void raise(void)
{
// 空实现,适配标准库编译
}
完整的UART.C函数
cs
#include "uart.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
void init_uart1(void)
{
IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0);
IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0);
IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10B0);
IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10B0);
UART1->UCR1 = 0;
UART1->UCR2 = 0;
UART1->UCR2 |= (1 << 14) | (1 << 5) | (1 << 2) | (1 << 1);
UART1->UCR3 |= (1 << 2);
UART1->UFCR |= (5 << 7);
UART1->UBIR = 999; // 必须在前面
UART1->UBMR = 43402;
UART1->UCR1 |= (1 << 0);
}
void putc(unsigned char ch)
{
while((UART1->USR2 & (1 << 3)) == 0);
UART1->UTXD = ch;
}
void puts(const char *p)
{
while(*p)
{
putc(*p++);
}
}
unsigned char getc(void)
{
while((UART1->USR2 & (1 << 0)) == 0);
return UART1->URXD & 0xFF; // 把后八位保留,前面的清除
}
void raise(void)
{
}
四、UART 使用示例
在主函数中初始化 UART1 后,即可调用收发函数实现串口通信(如与电脑串口调试助手交互)。
示例代码(main.c 片段)
c
#include "uart.h"
#include "delay.h"
int main(void)
{
unsigned char recv_data; // 接收缓存
// 1. 初始化系统时钟(UART时钟依赖系统时钟)
init_clock();
// 2. 初始化UART1(115200, 8N1:8位数据、无校验、1位停止位)
init_uart1();
// 3. 发送欢迎信息
puts("UART1 Test: 115200 8N1");
puts("Please enter a character: ");
while(1)
{
// 4. 接收电脑发送的字符
recv_data = getc();
// 5. 回显接收的字符(将接收的字符再发送回电脑)
puts("You entered: ");
putc(recv_data);
putc('\r'); // 换行
putc('\n');
}
return 0;
}
串口调试助手配置
- 波特率:115200;
- 数据位:8;
- 停止位:1;
- 校验位:无;
- 流控:无。
五、关键注意事项
- 引脚复用正确性:需确保 UART1 的 TX/RX 引脚复用配置正确(IOMUXC_UART1_TX_DATA_UART1_TX和IOMUXC_UART1_RX_DATA_UART1_RX),错误复用会导致通信失败;
- 波特率匹配:开发板 UART 波特率(115200)需与电脑串口调试助手配置完全一致,否则会出现乱码;
- 状态位检查:收发数据前必须检查USR2的TXDC(发送完成)和RDR(接收就绪)位,避免数据丢失或覆盖;
- 时钟依赖:UART 时钟源(pll3_80m)需通过init_clock正确初始化,否则波特率计算会偏差,导致通信异常。
UART 功能扩展(可选)
- 中断收发:当前为查询模式,可通过 UART 中断(如接收中断、发送中断)实现无阻塞通信,减少 CPU 占用;
- 多字节接收:扩展gets函数,实现字符串接收(需处理换行符或终止符);
- 波特率切换:通过动态修改UBIR和UBMR,支持不同波特率的切换(需重新计算寄存器值)。
通过以上配置,I.MX6ULL 的 UART1 可稳定实现与电脑的串口通信,为嵌入式程序调试(如打印变量、日志)提供核心支持。