当前位置: 首页 > news >正文

嵌入式开发代码实践——串口通信(UART)研发

嵌入式开发代码实践——串口通信(UART)研发

串口通信(UART)开发详解

一、UART通信基础概念

1.1 什么是UART?

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)是一种异步串行通信接口。它是嵌入式系统中最常用的通信方式之一。

1.2 UART通信特点

  • 异步通信:无需时钟信号,靠起始位、停止位同步

  • 全双工:可同时发送和接收数据

  • 点对点通信:通常用于两个设备之间的通信

  • 传输距离:TTL电平约1米,RS-232可达15米

1.3 UART数据帧格式

┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│空闲│起始│数据位0│数据位1│...│数据位7│校验位│停止位│空闲│
├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
│高  │低  │数据内容                      │可选  │高   │高  │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
  • 起始位:1位低电平

  • 数据位:通常8位(也可5、6、7位)

  • 校验位:可选(奇校验、偶校验)

  • 停止位:1或2位高电平

二、硬件原理分析

2.1 i.MX6ULL UART硬件框图

┌─────────────────────┐      ┌─────────────────────┐
│      i.MX6ULL       │      │       PC主机        │
│                     │      │                     │
│   UART1控制器       │◄────►│      USB转串口      │
│   - 发送寄存器      │      │       (CH340)       │
│   - 接收寄存器      │      │                     │
│   - 控制寄存器      │      └─────────────────────┘
└─────────┬───────────┘│▼
┌─────────────────────┐
│    硬件引脚          │
│  TXD (发送)          │───┐
│  RXD (接收)          │◄──┘
└─────────────────────┘

2.2 引脚定义(参考原理图)

  • UART1_TX:发送数据引脚,数据从芯片发送出去

  • UART1_RX:接收数据引脚,数据从外部接收进来

  • 波特率:115200 bps(每秒传输115200位)

三、代码详细解析

3.1 头文件定义(uart.h)

#ifndef _UART_H_
#define _UART_H_// 声明UART相关函数
extern void uart1_init(void);          // UART1初始化
extern void putc(unsigned char d);     // 发送一个字符
extern void puts(const char *s);       // 发送字符串
extern unsigned char getc(void);       // 接收一个字符#endif // !_UART_H_

3.2 UART初始化函数(uart.c)

3.2.1 引脚配置
void uart1_init(void)
{// 1. 复用功能配置 - 将引脚配置为UART功能IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0);  // RX引脚复用为UARTIOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0);  // TX引脚复用为UART// 2. 电气特性配置 - 设置引脚的电气参数IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10B0);  // RX引脚电气特性IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10B0);  // TX引脚电气特性

详细解释:

  • IOMUXC_SetPinMux():选择引脚功能

    • 第一个参数:引脚名称(如IOMUXC_UART1_RX_DATA_UART1_RX

    • 第二个参数:复用选择器(0表示选择主要功能)

  • IOMUXC_SetPinConfig():配置引脚电气特性

    • 参数0x10B0含义:

      • 0x:十六进制前缀

      • 10B0:配置驱动强度、上下拉、速度等参数

3.2.2 复位UART控制器
// 3. 软件复位UART控制器UART1->UCR2 &= ~(1 << 0);  // 清除UART使能位,相当于复位

寄存器解释:

  • UART1->UCR2:UART控制寄存器2

  • 位0(UARTEN):UART使能位

    • 0:禁用UART(复位状态)

    • 1:使能UART

  • 先清除此位,再重新配置,确保从干净状态开始

3.2.3 配置UART参数
// 4. 配置UART控制寄存器2unsigned int t;t = UART1->UCR2;                   // 读取当前值t |= (1 << 14);                    // 设置IRTS位,忽略RTS流控t &= ~(1 << 8);                    // 清除PREN位,禁用奇偶校验t &= ~(1 << 6);                    // 清除STPB位,1个停止位t |= (1 << 5);                     // 设置WS位,8位数据位t |= (1 << 2);                     // 设置TXEN位,使能发送器t |= (1 << 1);                     // 设置RXEN位,使能接收器UART1->UCR2 = t;                   // 写回寄存器

UCR2寄存器位详解:

位   名称       功能
──────────────────────────
0    UARTEN     UART使能位
1    RXEN       接收器使能
2    TXEN       发送器使能
5    WS         字长选择(0=7位,1=8位)
6    STPB       停止位数量(0=1位,1=2位)
8    PREN       奇偶校验使能
14   IRTS       忽略RTS流控(必须设置为1)
3.2.4 配置其他UART寄存器
// 5. 配置UART控制寄存器3UART1->UCR3 |= (1 << 2);           // 设置RXDMUXSEL位(必须为1)// 6. 配置FIFO控制寄存器UART1->UFCR &= ~(7 << 7);          // 清除参考时钟分频位UART1->UFCR |= (5 << 7);           // 设置1分频(参考时钟分频器)

UCR3寄存器:

  • 位2(RXDMUXSEL):接收数据多路复用器选择

    • 必须设置为1,芯片工作在MUXED模式

UFCR寄存器:

  • 位7-9(RFDIV):参考时钟分频器

    • 101(十进制5)= 1分频

    • 公式:分频值 = 6 - RFDIV

3.2.5 配置波特率
// 7. 波特率配置(115200 bps)UART1->UBIR = 999;                  // 增量寄存器UART1->UBMR = 43401;                // 模数寄存器// 8. 使能UARTUART1->UCR1 |= (1 << 0);            // 设置UARTEN位,使能UART
}

波特率计算公式:

BaudRate = Ref_Freq / (16 × ((UBMR + 1) / (UBIR + 1)))

其中:

  • Ref_Freq = 80MHz(系统参考时钟)

  • UBIR = 999(增量寄存器值)

  • UBMR = 43401(模数寄存器值)

计算验证:

波特率 = 80,000,000 / (16 × ((43401 + 1) / (999 + 1)))= 80,000,000 / (16 × (43402 / 1000))= 80,000,000 / (16 × 43.402)= 80,000,000 / 694.432≈ 115200 bps

3.3 字符发送函数

void putc(unsigned char d)
{// 等待发送完成(检查TXDC标志位)while ((UART1->USR2 & (1 << 3)) == 0);// 写入要发送的数据到发送寄存器UART1->UTXD = d;
}

工作原理:

  1. UART1->USR2 & (1 << 3):检查状态寄存器2的TXDC位(位3)

    • 0:发送器忙,正在发送数据

    • 1:发送完成,可以发送新数据

  2. while循环:一直等待,直到发送完成标志为1

  3. UART1->UTXD = d:将数据写入发送寄存器,自动开始发送

状态寄存器2(USR2)位:

  • 位3(TXDC):发送完成标志

    • 0:发送器忙或禁用

    • 1:发送缓冲区和移位寄存器都为空

3.4 字符串发送函数

void puts(const char *s)
{// 遍历字符串,逐个发送字符while (*s){putc(*s++);      // 发送当前字符,指针后移}putc('\n');          // 发送换行符,便于阅读
}

工作原理:

  1. while (*s):检查当前字符是否为字符串结束符'\0'

  2. putc(*s++):发送当前字符,然后指针s指向下一个字符

  3. 循环直到遇到字符串结束符

  4. putc('\n'):额外发送换行符

3.5 字符接收函数

unsigned char getc(void)
{// 等待接收数据就绪(检查RDR标志位)while ((UART1->USR2 & (1 << 0)) == 0);// 读取接收寄存器中的数据return (unsigned char)UART1->URXD;
}

工作原理:

  1. UART1->USR2 & (1 << 0):检查状态寄存器2的RDR位(位0)

    • 0:没有接收到数据

    • 1:有数据可以读取

  2. while循环:一直等待,直到有数据可读

  3. (unsigned char)UART1->URXD:读取接收寄存器并转换为无符号字符

状态寄存器2(USR2)位:

  • 位0(RDR):接收数据就绪标志

    • 0:没有接收到新数据

    • 1:接收到新数据,可以读取

接收寄存器(URXD)注意:

  • URXD寄存器是只读的

  • 读取后,RDR标志会自动清除

四、主程序中的UART使用

4.1 主程序调用(main.c)

int main(void)
{// 系统初始化system_interrupt_init();  // 中断系统初始化clock_init();              // 时钟初始化led_init();                // LED初始化beep_init();               // 蜂鸣器初始化key_init();                // 按键初始化gpt1_init();               // 定时器初始化// UART初始化(重点)uart1_init();              // 初始化UART1,配置为115200波特率unsigned char ch;          // 用于存储接收到的字符while(1){// 延时1秒delay_us(1000 * 1000);// 控制LED和蜂鸣器翻转led_nor();             // LED状态翻转beep_nor();            // 蜂鸣器状态翻转// UART通信部分ch = getc();           // 从串口接收一个字符(会等待直到有数据)putc(ch);              // 将接收到的字符发送回去(回显功能)puts("\nhello world!"); // 发送字符串}return 0;
}

4.2 程序流程图

开始↓
初始化系统(时钟、中断等)↓
初始化UART(配置引脚、波特率115200)↓
┌─────────────────────────────┐
│          主循环             │
│  ↓                          │
│  延时1秒                    │
│  ↓                          │
│  LED和蜂鸣器翻转            │
│  ↓                          │
│  等待接收串口数据           │←──┐
│  ↓                          │   │
│  将接收的数据发送回去       │   │
│  ↓                          │   │
│  发送"hello world!"字符串   │   │
│  ↓                          │   │
│  (循环继续)                 │   │
└──────────────┬──────────────┘   ││                  │└──────────────────┘

五、常见问题与调试

5.1 常见问题

  1. 没有输出/全是乱码

    • 检查波特率是否匹配(PC端和开发板都设为115200)

    • 检查TX、RX线是否接反

    • 检查电源是否正常

  2. 只能发送不能接收

    • 检查RX引脚配置是否正确

    • 检查PC端串口工具是否已打开串口

    • 检查线路连接是否良好

  3. 发送数据不完整

    • 检查发送函数是否正确等待发送完成标志

    • 检查是否有其他中断影响

http://www.jsqmd.com/news/394901/

相关文章:

  • 【一文吃透】AI视频全流程实操+工具指南,拆解抽卡/一致性难题
  • 19-2-2026
  • C++游戏开发之旅 14
  • 一文全懂!AI 应用架构师与 AI 安全漏洞检测系统知识全解
  • 大数据架构性能基准测试:TPCx-HS与HiBench实践
  • iptables入门
  • Iptables
  • 零基础也能玩转AI音乐!Lyria 3超详细入门指南
  • 高校教学AI辅助平台数据标注成本高?AI应用架构师的弱监督学习方案
  • 【花雕动手做】6.5寸轮毂电机驱动方案之DC36V600W有霍尔大功率矢量控制器
  • 虚拟同步机(VSG)参数自适应控制,基于T型三电平逆变器的参数自适应控制,采用电压电流双闭环控...
  • 风电、光伏与抽水蓄能电站互补调度运行研究附Matlab代码
  • 多机器人智能体编队:Matlab代码汇总
  • 风电、光伏与储能(含电池和废弃矿井小型抽水蓄能)互补调度运行研究附Matlab代码
  • 分布式传感器算法评估LEACH聚类能量耗尽研究附Matlab代码
  • 风储VSG-基于虚拟同步发电机的风储并网系统附Simulink仿真
  • 风电最大化消纳的热电联产机组联合优化控制附Matlab代码
  • OJ 运营模拟器
  • 锂电池Matlab建模仿真:基于二阶RC等效电路模型与HPPC、CC工况的仿真
  • 2026年假
  • 【渗透测试】HTB靶场之WingData 全过程wp
  • 2026.2.19
  • 第十四日笔记
  • JAVA WEB学习1
  • 突破性进展:基于大模型的上下文理解技术详解
  • 大数据ETL架构:Airflow与DataX集成方案
  • 格雷厄姆的价值线概念及其应用
  • 数据中台建设成熟度评估模型与方法论
  • 基于Spring Boot的投资理财系统设计与实现(任务书)
  • JDK 动态代理和 CGLIB 动态代理有什么区别?