目录
[1.1 通信的方式分类](#1.1 通信的方式分类)
[2.1 串口通信的数据格式:](#2.1 串口通信的数据格式:)
[2.2 电器标准](#2.2 电器标准)
[3.1 I.MX6U 的 UART资源](#3.1 I.MX6U 的 UART资源)
[3.2 硬件原理图](#3.2 硬件原理图)
[4.1 常用的串口调试软件:](#4.1 常用的串口调试软件:)
[4.2 参考手册](#4.2 参考手册)
[4.3 模块函数编写](#4.3 模块函数编写)
[4.4 标准 I/O 库(stdio)移植全流程](#4.4 标准 I/O 库(stdio)移植全流程)
[步骤 1:添加必要的空实现函数](#步骤 1:添加必要的空实现函数)
[步骤 2:修改汇编文件扩展名](#步骤 2:修改汇编文件扩展名)
[步骤 3:更新 Makefile 配置](#步骤 3:更新 Makefile 配置)
[六、基于 stdio 的进阶应用场景](#六、基于 stdio 的进阶应用场景)
实验目的:是实现115200,n,8,1与电脑之间通信。
一、通信的基本概念
通信:嵌入式系统中的通信是指两个或两个以上的主机之间的数据互交,这里的主机可以是计算机也可以是嵌入式主机,甚至可以是芯片。
1.1 通信的方式分类
- 按照通信的数据同步方式分
异步通信:
- 概念:通信双方事先约定速率,按照各自的系统计时的通信方式称为异步。
- 特点:以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时
间间隔)是任意的,但每个字符中的各位是以固定的时间传送的
同步通信:
- 概念:通信双方通过时钟线SCL(serial clock)确定通信速率(不用向异步那样约定速率),发送方负责控制时钟线的变化,这种通信方式就称为同步通信。
- 应用: IIC、SPI等。
- 按照数据传送方式分:
串行:节约引脚,通信速率慢
- 概念:串行通信是指将数据拆分成一个个比特,按照先后次序在一根总线上进行发送,是主机间通信的常用方式。
- 优点:系统占用资源少(传输线,IO口),结构简单;传输距离远,抗干扰性好,长距离传送时硬件成本低,且可以利用电话网等现成的设备
- 缺点:传输速率较低,数据的传送控制比并行通信复杂。
- 应用:RS232、RS485、RS422都是串行通信。
并行通信:
- 概念:并行通信是指多个比特同时通过并行线进行传输
- 优点:传输速率高,控制简单
- 缺点:引脚资源占用多(并行线、IO口,还需额外一根时钟线);由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难,抗干扰能力差;硬件成本高
- 应用:AHB、APB总线、imx6ull 24并行数据线连接屏幕
- 按照数据的传输方向分:
| 通信方式 | 数据传输方向 | 数据线数量 | 核心特点 | 适用场景 |
|---|---|---|---|---|
| 单工(Simplex Communication) | 单向固定 | 1 根 | 发送方和接收方固定,无法双向传输 | 红外遥控、串口打印(仅发送) |
| 半双工(Half-Duplex Communication) | 双向但不同时 | 1 根 | 双方可收发数据,但同一时刻仅能单向传输 | 对讲机、I2C 总线(软件半双工) |
| 全双工(Full-Duplex Communication) | 双向同时 | 2 根(TXD+RXD) | 收发独立,可同时进行数据传输 | 单片机与电脑、蓝牙模块通信、打电话 |
- 单工概念:主机间通信时++一方固定为发送端另外一方固定为接收端++,通过一根总线实现数据通信。
- 半双工概念:数据传输可以在两个方向之间交替进行,但不能同时进行。
- 全双工概念:允许通信双方同时发送和接收数据,实现双向通信。
二、UART
- 概念:UART(Universal Asynchronous Receiver Transmitter)即通用异步收发器,是硬件电路中的通信接口,拥有独立的通信规则(协议 ),核心属性为异步、全双工、串行通信。
- 应用:在 WIFI、蓝牙、GPS、GSM/GPRS 等应用的控制。开发板上集成了 1 个串口通信电路,是 USB 转串口模块,它既可下载程序也可实现串口通信功能。
- 比特率:是每秒钟传输二进制代码的位数,单位是:位/秒( bps)。如每秒钟传送 240 个字符,而每个字符格式包含 10 位(1 个起始位、1 个停止位、8 个数据位),这时的比特率为:
10 位×240 个/秒 = 2400 bps
2.1 串口通信的数据格式:

常用数据格式:
- 115200 N 8 1: 波特率-115200 N-无校验 8-8位数据位 1-1位停止位
- 4800 E 8 1.5 (工业上常用) : 波特率-4800 E-偶校验 8-8位数据位 1.5-1.5位停止位
2.2 电器标准
1.主机间通信时的电器物理问题
主机间通信无论采用并行还是串行方式,都无法避免一个物理现象:导线内阻 不为零造成的电压衰减。以之前讨论的TTL电平为例,主机之间的距离会造成高电平在接收端出现衰减现象 和串扰(指不同信号之间相互干扰导致信号失真)影响。
2.电平标准
- TTL电平
**概念:**TTL(Transistor-Transistor Logic)通常指的就是芯片引脚产生的电压,这个电压值跟选择的芯片有关
- 在51单片机系统下是5v,5vTTL通信距离通常被限制在10~20米之间;
- 在2440下是3.3v等等。
特点:传输距离短。
补充:
{ TTL与 RS-232C(老式电脑用) 电平转换,通常使用的电平转换芯片是 MAX232。}
{ TTL与USB电平转换,通常使用的电平转换芯片是 CH340}
RS232:
诞生:为解决传输距离短的问题,IEEE(Institute of Electrical and Electronics Engineers)颁布了RS232标准
具体规定:
- 逻辑高电平(逻辑1):在-3V到-15V之间
- 逻辑低电平(逻辑0):在+3V到+15V之间
- 收发主机间有三根线,分别是收、发和地,因此RS232是全双工的。
- 理论上RS232能够传输20~30米。
特点:传输距离比TTL远。
RS485
诞生:为解决传输距离短的问题
具体规定:
- 正电压表示高电平,负电压表示低电平。
- 逻辑低电平(逻辑0):在-7V到-12V之间
- 逻辑高电平(逻辑1):在+7V到+12V之间
特点
- 差分信号传输(压差)
- 抗干扰能力强,适合工业环境。
- 传输距离比RS232远,可达1200米,适用于大范围的数据传输需求。
- 半双工:因为采用的是压差,RS485在传输数据的某一时刻,两根线都要用到。
补充:
- 更远距离的传输,可以使用485中继器
三、硬件
3.1 I.MX6U 的 UART资源
I.MX6U 一共有8 个 UART,其主要特性如下:
① 兼容 TIA/EIA-232F 标准,++速度最高可到 5Mbit/S++;
② 支持串行 IR 接口,兼容 IrDA(Infrared Data Association红外数据组织),最高可到 115.2Kbit/s;
③ 支持 9 位或者多节点模式++(RS-485)++;
④ 1 或 2 位停止位;
⑤ 可编程的奇偶校验(奇校验和偶校验);
⑥ 自动波特率检测(最高支持 115.2Kbit/S)。
3.2 硬件原理图



四、软件
4.1 常用的串口调试软件:
- windows下的串口调试软件很多,也各有优缺点。调试51单片机时可以使用STC提供的ISP下载程序,其中就包括了一个串口调试助手;
- UartAssist,绿色软件、只有一个执行文件、无需安装;支持ASCII/HEX码数据发送,发送和接收的数据可以在十六进制码和ASCII码之间任意转换,支持发送和显示汉字;可以自动发送校验位,支持多种校验格式,如校验和、LRC、BCC、CRC8、CRC16、CRC32、MD5等,其中CRC校验码可任意定制CRC参数(CRC多项式、初始值、输入反转、输出反转、输出异或值)。
4.2 参考手册
复用引脚配置:

引脚电器属性配置

UART时钟配置




UART属性配置
- 接收缓冲区


- 发送缓冲区

- UART使能



- 停止位




4.3 模块函数编写
- 波特率计算公式

- 初始化函数
cpp
#include "uart.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
void reset_uart1(void)
{
UART1->UCR2 &= ~(1 << 0);
while((UART1->UCR2 & (1 << 0)) == 0);
}
void uart1_init(void)
{
//复用功能
IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0);
IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0);
//电气特性
IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10B0);
IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10B0);
//UART
reset_uart1();
unsigned int t = 0;
t = UART1->UCR2;
t |= (1 << 14);
t &= ~(1 << 8);
t &= ~(1 << 6);
t |= (1 << 5);
t |= (1 << 2);
t |= (1 << 1);
UART1->UCR2 = t;
//MUXC
UART1->UCR3 |= (1 << 2);
//模块时钟分频值
UART1->UFCR &= ~(7 << 7);
UART1->UFCR |= (5 << 7);
//波特率配置
UART1->UBIR = 999;
UART1->UBMR = 43402;
UART1->UCR1 |= (1 << 0);
}
- 功能函数
cpp
// 串口1发送一个字节数据
// d:要发送的字节数据
void putc(unsigned char d)
{
// 循环等待串口发送缓冲区为空(USR2的第3位为1表示发送完成/缓冲区空)
while((UART1->USR2 & (1 << 3)) == 0);
// 向串口数据发送寄存器写入要发送的数据(与0xFF确保是8位数据)
UART1->UTXD = 0xFF & d;
}
// 串口1接收一个字节数据
// 返回值:接收到的字节数据
unsigned char getc(void)
{
// 循环等待串口接收缓冲区有数据(USR2的第0位为1表示接收到数据)
while((UART1->USR2 & (1 << 0)) == 0);
// 从串口数据接收寄存器读取数据,并返回
return (unsigned char)UART1->URXD;
}
// 串口1发送一个字符串
// s:要发送的字符串指针
void puts(const char *s)
{
// 循环遍历字符串,直到遇到结束符'\0'
while(*s)
{
putc(*s++); // 发送当前字符,指针后移
}
putc('\n'); // 字符串发送完毕后,发送换行符
}
// 串口1接收一行字符串(以回车/换行结束)
// pstr:存储接收字符串的缓冲区指针
// 返回值:指向字符串起始地址的指针
char* gets(char* pstr)
{
char* start = pstr; // 保存字符串起始地址,用于返回
// 循环接收字符
while(1)
{
char c = getc(); // 从串口读取一个字符
// 如果收到回车(\r)或换行(\n),表示一行输入结束
if(c == '\r' || c == '\n')
break;
*pstr++ = c; // 将接收到的字符存入缓冲区,指针后移
}
*pstr = '\0'; // 在字符串末尾添加结束符,构成标准C字符串
return start; // 返回字符串起始地址
}
4.4 标准 I/O 库(stdio)移植全流程
在嵌入式 UART 开发中,最基础的收发函数putc()/getc()仅支持单个字节 的读写操作,无法直接使用 C 语言中最常用的printf()(格式化输出)、scanf()(格式化输入)等高级接口。通过移植标准 C 库stdio.h,可以直接复用这些成熟的格式化输入输出函数,大幅提升开发效率,让串口调试、人机交互变得更加便捷。
步骤 1:添加必要的空实现函数
stdio库依赖raise()函数(用于异常处理),如果不实现会导致编译报错。我们只需在uart.c中添加一个空实现即可满足链接需求:
cpp
/**
* @brief raise函数空实现
* @note stdio库依赖此函数,仅需空实现满足链接要求,无实际业务逻辑
* @param n: 异常信号编号
* @retval 无
*/
void raise(int n)
{
// 空实现,仅满足链接需求
}
//补充说明:raise()是 C 标准库中用于向进程发送信号的函数,在裸机嵌入式环境中无实际作用,因此直接空实现即可。
步骤 2:修改汇编文件扩展名
如果你的启动汇编文件名为start.s(小写s),必须将其重命名为start.S(大写 S),原因如下:
- 大写
.S:会触发 GCC 的预处理流程 ,支持#include、#define等宏定义- 小写
.s:直接编译,不执行预处理stdio库依赖预处理流程,因此必须使用大写.S后缀
步骤 3:更新 Makefile 配置
需要在 Makefile 中添加stdio库路径、头文件目录和源码目录,确保编译器能正确找到相关依赖文件。以下是完整可复用的 Makefile 模板:
bash
target = uart
cross_compiler = arm-linux-gnueabihf-
cc = $(cross_compiler)gcc
ld = $(cross_compiler)ld
objcopy = $(cross_compiler)objcopy
objdump = $(cross_compiler)objdump
incdirs = bsp imx6ull stdio/include
srcdirs = bsp project stdio/lib
include = $(patsubst %, -I%, $(incdirs))
pathlib = -lgcc -L/home/linux/tools/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4
cfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.c))
sfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.S))
cfilenodir = $(notdir $(cfiles))
sfilenodir = $(notdir $(sfiles))
cobjs = $(patsubst %, obj/%, $(cfilenodir:.c=.o))
sobjs = $(patsubst %, obj/%, $(sfilenodir:.S=.o))
objs = $(cobjs) $(sobjs)
VPATH = $(srcdirs)
$(target).bin : $(objs)
$(ld) -Timx6ull.lds -o$(target).elf $^ $(pathlib)
$(objcopy) -O binary -S -g $(target).elf $@
$(objdump) -D $(target).elf > $(target).dis
$(sobjs) : obj/%.o : %.S
@mkdir -p obj
$(cc) -Wall -nostdlib -Wa,-mimplicit-it=thumb -nostdlib -fno-builtin -c $(include) -o $@ $<
$(cobjs) : obj/%.o : %.c
@mkdir -p obj
$(cc) -Wall -nostdlib -Wa,-mimplicit-it=thumb -nostdlib -fno-builtin -c $(include) -o $@ $<
.PHONY : clean
clean:
rm -rf $(objs) $(target).elf $(target).bin $(target).dis
load:
./imxdownload $(target).bin /dev/sdb
五、实验现象
- 烧录程序到开发板,打开串口调试工具(波特率 115200,8 位数据位,1 停止位,无校验)
- 串口会打印提示:
请输入两个整数,用空格分隔: - 在串口工具中输入
100 6并回车 - 串口立即输出:
100 + 6 = 106 - 若能正常实现上述效果,证明
stdio库移植成功。

六、基于 stdio 的进阶应用场景
完成stdio移植后,我们可以基于 UART 实现更丰富的功能:
- 串口命令交互系统:通过串口输入命令控制 LED 灯、蜂鸣器、继电器等外设
- 调试信息输出 :使用
printf()打印程序运行状态、变量值、错误日志 - 设备数据交互:与其他串口设备(如传感器、蓝牙模块)进行数据通信
- 串口固件升级:通过串口实现 IAP(在应用编程),升级程序固件
- 人机交互界面:实现简单的菜单式交互,方便调试和功能测试



