ARM裸机学习6——UART

目录

一、通信的基本概念

[1.1 通信的方式分类](#1.1 通信的方式分类)

二、UART

[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 常用的串口调试软件:

  1. windows下的串口调试软件很多,也各有优缺点。调试51单片机时可以使用STC提供的ISP下载程序,其中就包括了一个串口调试助手;
  2. 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

五、实验现象

  1. 烧录程序到开发板,打开串口调试工具(波特率 115200,8 位数据位,1 停止位,无校验)
  2. 串口会打印提示:请输入两个整数,用空格分隔:
  3. 在串口工具中输入100 6并回车
  4. 串口立即输出:100 + 6 = 106
  5. 若能正常实现上述效果,证明stdio库移植成功。

六、基于 stdio 的进阶应用场景

完成stdio移植后,我们可以基于 UART 实现更丰富的功能:

  1. 串口命令交互系统:通过串口输入命令控制 LED 灯、蜂鸣器、继电器等外设
  2. 调试信息输出 :使用printf()打印程序运行状态、变量值、错误日志
  3. 设备数据交互:与其他串口设备(如传感器、蓝牙模块)进行数据通信
  4. 串口固件升级:通过串口实现 IAP(在应用编程),升级程序固件
  5. 人机交互界面:实现简单的菜单式交互,方便调试和功能测试
相关推荐
Zarek枫煜3 小时前
[特殊字符] C3语言:传承C之高效,突破C之局限
c语言·开发语言·c++·单片机·嵌入式硬件·物联网·算法
Lugas Luo3 小时前
SATA 硬盘识别延时:协议层与内核机制分析
linux·嵌入式硬件
somi73 小时前
ARM-10-I.MX6U ADC
arm开发·嵌入式硬件·adc·自用
nfsf4 小时前
【ZMCT103C电流互感器应用原理图 交流电流】
嵌入式硬件
进击的小头4 小时前
01_第一篇:到底什么是嵌入式芯片?与通用CPU_GPU_DSP的核心区别
单片机·嵌入式硬件
hoiii1875 小时前
STM32 RS232串口通讯实验
stm32·单片机·嵌入式硬件
可乐鸡翅好好吃5 小时前
Keil更改RAM地址
网络·单片机·嵌入式硬件
惶了个恐6 小时前
嵌入式硬件第七弹——ARM(4)
arm开发·stm32·单片机·嵌入式硬件·arm·硬件工程
Lugas Luo7 小时前
SATA 协商流程深度分析 (基于 libata 与 AHCI 控制器)
linux·嵌入式硬件