05_第1个程序深度解析
01_编程知识_进制
02_编程知识_字节序_位操作
编程知识_字节序_位操作
1. 字节序
假设int a = 0x12345678;
16进制数中每位数值占据4 bit;在内存中,是以8个bit作为1byte。 因此0x12345678中每两位作为1byte, 其中0x78是低byte,0x12是高byte。 在内存中的存储方式有两种:
0x12345678的低位(0x78)存在低地址,即方式1,叫做小字节序(Little endian); 0x12345678的高位(0x12)存在低地址,即方式2,叫做大字节序(Big endian); 一般的arm芯片都是小字节序,有些处理器,可以置某个寄存器,让整个系统使用大字节序或小字节序。
-
小端模式(低位在左):
- 适合需要频繁处理数据低位的场景。例如x86芯片(如你的电脑CPU)用小端,因为加减法运算时,CPU可以直接从低地址开始处理低位字节,效率更高。
- 类比:计算“1234+5678”时,先算个位(4+8),再算十位(3+7),小端模式让CPU按这个顺序处理更自然。
-
大端模式(高位在左):
- 符合人类书写习惯(如数字“1234”从左到右是高位到低位),早期IBM大型机、网络设备采用大端,方便调试时直接查看内存中的高位数据
2. 位操作
2.1 逻辑移位
在嵌入式开发中,我们只涉及逻辑移位:不关心符号位,都是补0 算术移位,需要分有符号型值和无符号型值:
-
对于无符号型值,算术移位等同于逻辑移位。
-
对于有符号型值 ,算术左移等同于逻辑左移;算术右移补的是符号位,正数补0,负数补1。
2.1.1 逻辑左移
int a = 0x123; int b = a<<2; // b=0x48C
2.1.2 逻辑右移
int a = 0x123; int b = a>>2; // b=0x48
2.2 取反
unsigned int a = 0x123; unsigned int b = ~a; // b的每一位,都是a对应位的取反
2.3 位与
unsigned int a = 0x123; unsigned int b = 0x456; unsigned int c = a & b; // c等于a位与b,即:a,b的每一位进行与操作
两位相与,结果如下:
1 & 1 = 1 1 & 0 = 0 0 & 1 = 0 0 & 0 = 0
所以,上述C语言代码的结果如下图所示:
2.4 位或
unsigned int a = 0x123; unsigned int b = 0x456; unsigned int c = a | b; // c等于a位或b,即:a,b的每一位进行或操作
两位相或,结果如下:
1 | 1 = 1 1 | 0 = 1 0 | 1 = 1 0 | 0 = 0
所以,上述C语言代码的结果如下图所示:
2.5 置位
unsigned int a = 0x123; unsigned int b = a | (1<<7) | (1<<8); // 设置a的bit7, bit8, 赋给b
2.6 清位
unsigned int a = 0x123; unsigned int b = a & ~((1<<7) | (1<<8)); // 清除a的bit7, bit8, 赋给b
2.7 把某几位设置为某值
比如要把bit7设置为1,把bit8清除为0,这可以分两步操作:先设置bit7,再清除bit8。 还有一种情况:bit[8:7]= val, 不知道val的取值是多少,怎么办? 先清除bit8、bit7,再或上val,代码如下:
unsigned int a = 0x123; unsigned int b = a & ~(3<<7); /* 清除bit7, bit8 */ b = b | (val << 7); /* 设置bit7, bit8为val */
03_编程知识_汇编_反汇编_机器码蹲
1. 程序处理的4个步骤
我们的第1个LED程序涉及2个文件:start.S、main.c,它们的处理过程如下:
我们想深入理解ARM架构,想深入理解汇编与C,想深入理解栈的作用,想深入理解C语言的实质, 就必须把最终的可执行程序,反汇编后,阅读得到的汇编代码。
上述4个步骤的细节,在后面课程里再讲。 现在只需要理解“汇编”、“反汇编”的概念:
-
汇编 汇编文件转换为目标文件(里面是机器码)。
-
反汇编 可执行文件(目标文件,里面是机器码),转换为汇编文件。
2. KEIL下怎么反汇编
在KEIL的User选项中,如下图添加这两项:
fromelf --bin --output=led.bin Objects\led_c.axf fromelf --text -a -c --output=led.dis Objects\led_c.axf
然后重新编译,即可得到二进制文件led.bin(以后会分析)、反汇编文件led.dis。 如下图操作:
3. GCC下反汇编
使用GCC工具链编译程序时,在Makefile中有这一句:
$(OBJDUMP) -D -m arm led.elf > led.dis # OBJDUMP = arm-linux-gnueabihf-objdump
它就是把可执行程序led.elf,反汇编,得到led.dis。
4. 机器码与汇编
参考资料:
doc_and_source_for_mcu_mpu\通用资料\ARM: DDI0403E_B_armv7m_arm.pdf P254 // cortex M3/M4 ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf P410 // cortex A7
前面介绍过伪指令,伪指令是实际不存在的ARM命令,编译器在编译时转换成存在的ARM指令。 我们代码中的ldr r1, =0x????????
这条伪指令的真实指令是什么呢? 对于我们使用的3款板子,汇编代码如下(如果你的板子不是这3款之一,请灵活变通,知识是一样的):
LDR SP, =(0x20000000+0x10000) // STM32F103 ldr sp, =(0x80000000+0x100000) // IMX6ULL ldr sp, =0xc0000000 + 0x100000 // STM32MP157 A7
我们可以通过反汇编来查看, 只摘取前面一小段。
4.1 STM32F103反汇编
我们只摘取前面一小段,第一列是地址,第二列是机器码,第三列是汇编:
4.2 STM32MP157反汇编
4.3 IMX6ULL反汇编
4.4 机器码与汇编示例
4.4.1 Thumb/Thumb2指令集
4.4.2 ARM指令集
4.5 解析LDR伪指令
为什么 PC=当前指令+4或8?
-
CORTEX M3/M4 使用Thumb2指令集,一条指令是16位或32位。
-
CORTEX A7
默认使用ARM指令集,一条指令是32位的。
-
流水线
ARM指令采用流水线机制:
-
当前执行地址A的指令,
-
同时已经在对下一条指令进行译码
-
同时已经在读取下下一条指令:PC = A +4 (Thumb/Thumb2指令集)、PC = A + 8 (ARM指令集)
-
4.5 总结
-
C 为了方便人类方便使用,发明的高级语言,要转换为汇编。
-
汇编 为了解放人类的记忆,发明的“助记符”,不用去记各类机器码。
最终要转换为机器码。
-
机器码
给CPU使用
04_编程知识_C与汇编深入分析
1. 汇编怎么调用C函数
1.1 直接调用
bl main
1.2 想传参数怎么办?
在arm中有个ATPCS规则(ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)。 约定r0-r15寄存器的用途:
-
r0-r3
调用者和被调用者之间传参数
-
r4-r11
函数可能被使用,所以在函数的入口保存它们,在函数的出口恢复它们。
代码示例:
int delay(unsigned int d) { while (d--); return 0; }
在汇编里调用delay:
ldr r0, =1000000 /* 给delay函数传参数,保存在r0里 */ bl delay cmp r0, #0 /* 返回值保存在r0中 */
2. C函数的反汇编码阅读
要解决这几个问题:
-
为什么调用C函数前要设置栈?栈的作用是?
-
C函数传参
-
C函数执行过程体验
3. Flash上的内容
3.1 反汇编示例
-
LDR sp, [pc, #4]
- 作用:初始化栈指针(SP),为后续函数调用分配内存空间。
- 类比:炒菜前先准备好锅和铲子(栈是临时存放数据的“工作台”)。
-
BL mymain
- 作用:跳转到主函数(
mymain
),同时保存返回地址到LR寄存器。 - 类比:厨师暂停当前步骤,先去处理另一道菜(主函数),做完再回来继续。
- 作用:跳转到主函数(
-
NOP
- 作用:空操作,用于占位或延时。
- 类比:等待水烧开的1分钟,不做其他动作。
四、程序如何运行?
- 上电启动:CPU从
0x08000000
(RESET地址)开始执行。 - 初始化栈:
LDR sp, [pc, #4]
设置好栈指针。 - 调用主函数:
BL mymain
进入主程序逻辑。 - 函数返回:主函数执行完后,通过
MOV PC, LR
返回初始位置。
3.2 烧写在Flash上的内容
地址 | Flash内容 |
---|---|
0x08000000 | 00000000 |
0x08000004 | 08000009 |
0x08000008 | f8dfd004 |
0x0800000c | f000f80c |
0x08000010 | 20010000 |
0x08000014 | bf00b501 |
0x08000018 | 1e419800 |
…… | …… |
3.3 启动流程
上电后:
-
设置栈:CPU会从0x08000000读取值,用来设置SP(我们的程序里再次设置了SP)
-
跳转:CPU从0x08000004得到地址值,根据它的BIT0切换为ARM状态或Thumb状态,然后跳转
-
对于cortex M3/M4,它只支持Thumb状态,所以0x08000004上的值bit0必定是1
-
0x08000004上的值 = Reset_Handler + 1
-
-
从Reset_Handler继续执行
4.课后作业
-
编写一个纯汇编程序,实现点灯
-
编写一个c程序,实现用按钮控制LED
05_纯汇编点灯
1. 程序流程图
start使能GPIO模块设置引脚为GPIO功能设置引脚为输出设置引脚输出高电平delay设置引脚输出低电平delay
2. delay函数流程图
startR0减1等于0?返回LR所示地址yesno
3. 写程序
-
寄存器操作 对于寄存器的操作,主要涉及读、修改、写。
-
读可以使用LDR指令,代码为
LDR R1, [R0]
-
写使用STR指令,代码为
STR R1, [R0]
-
修改稍微复杂,清除位使用BIC或AND指令,设置位使用ORR指令
LDR R0, =(1<<20) | (1<<21) BIC R1, R1, R0 ; 清除R1的bit20, bit21 LDR R0, =(1<<20) ORR R1, R1, R0 ; 设置R1的bit20
-
-
函数里的条件判断 比如减1操作,代码为
SUB R0, R0, #1
但是顺便使用减1后的结果影响程序状态寄存器,代码为SUBS R0, R0, #1
-
程序的调用与返回
-
传参,代码为
LDR R0, =VAL
-
调用,代码为
BL delay
,它顺便把下一条指令的地址保存在LR寄存器了 -
返回,代码为
MOV PC, LR
-
4. 现场写程序
06_使用按键控制LED
使用按键控制LED
1. 先看原理图
-
100ASK IMX6ULL按键原理图
-
我们使用KEY2来控制LED:按下KEY2则灯亮,松开后灯灭
-
课后作业:使用KEY1来控制LED(我在视频里演示KEY2,它更复杂一点)
-
KEY2用的是GPIO04_IO14引脚
2. 再看芯片手册
2.1 使能GPIO4模块
CCM_CCGR3地址:20C_4000h base + 74h offset = 0x020C4074
-
Clock is off during all modes. Stop enter hardware handshake is disabled.
翻译:所有模式下时钟均关闭。停止进入硬件握手(Hardware Handshake)功能被禁用。
技术解读:- 此状态下,无论系统处于何种模式(如运行、待机等),时钟信号均停止工作。
- 硬件握手通常指通过信号(如RTS/CTS)控制数据流,禁用后可能影响外设通信的同步性。
-
Clock is on in run mode, but off in WAIT and STOP modes.
翻译:时钟在运行(RUN)模式下开启,但在等待(WAIT)和停止(STOP)模式下关闭。
技术解读:- 运行模式下CPU和外设正常工作时钟运行,而低功耗模式(如等待、停止)下关闭以节省能耗。
- 典型场景如STM32的停止模式(Stop Mode),所有时钟暂停,仅保留RTC等必要外设。
-
Not applicable (Reserved).
翻译:不适用(保留)。
技术解读:- 表示此条目当前无实际功能,可能为未来扩展预留。
-
Clock is on during all modes, except STOP mode.
翻译:除停止(STOP)模式外,所有模式下时钟均保持开启。
技术解读:- 仅在停止模式下关闭时钟,其他模式(如运行、睡眠)时钟持续运行。
- 适用于需要实时响应的场景,例如通过外部中断快速唤醒系统。
二、关键模式对比
模式 | 时钟状态 | 典型应用场景 | 相关技术文档 |
---|---|---|---|
所有模式 | 关闭 | 全系统休眠或深度节能场景 | 13 17 19 |
运行模式 | 开启 | 正常执行代码、外设操作 | 17 18 19 |
等待/停止模式 | 关闭 | 低功耗待机,依赖外部事件唤醒 | 13 17 18 |
保留模式 | 不适用 | 未来功能扩展或特殊配置预留 | 13 |
三、技术背景
-
硬件握手(Hardware Handshake)
- 一种通过物理信号(如RTS/CTS)控制数据流的方式,禁用后可能导致通信同步问题。
- 常见于串口通信中,用于避免数据溢出或丢失。
-
低功耗模式设计
- 停止模式(STOP):关闭所有时钟,仅保留关键外设(如RTC),功耗极低。
- 待机模式(Standby):关闭1.8V内核电源,仅通过特定唤醒源(如外部中断)恢复。
-
时钟管理策略
- 通过寄存器(如RCC_APB1ENR)动态启停外设时钟以优化功耗。
- 唤醒后需重新初始化时钟和外设状态。
四、实际应用建议
- 选择模式依据:根据系统对实时性、功耗的需求选择模式(如电池供电设备优先停止模式)。
- 硬件握手禁用风险:可能导致通信错误,需评估是否依赖硬件流控。
- 唤醒源配置:在停止模式下需提前配置有效唤醒源(如RTC闹钟、外部中断)。
2.2 设置引脚工作于GPIO模式
IOMUXC_SW_MUX_CTL_PAD_NAND_CE1_B 地址:20E_0000h base + 1B0h offset = 0x020E01B0
1. 主复用模式(ALT0-ALT5)
复用模式 | 功能描述 | 对应外设实例 |
---|---|---|
ALT0 | 连接至NAND Flash控制器 | rawnand |
ALT1 | 连接至USDHC1数据线6(SD卡控制器) | usdhc1 |
ALT2 | 连接至QSPI闪存数据线2 | qspi |
ALT3 | 连接至ECSPI3主输出从输入(SPI总线) | ecspi3 |
ALT4 | 连接至EIM地址总线18(外部接口模块) | eim |
ALT5 | 配置为通用GPIO引脚 | gpio4 (GPIO4_IO14) |
2. 扩展复用模式(ALT8)
复用模式 | 功能描述 | 对应外设实例 |
---|---|---|
ALT8 | 连接至UART3的CTS流控信号 | uart3 |
2.3 设置引脚为输入引脚
GPIO4_GDIR地址:0x020A8004
一、原文翻译
GPIO方向寄存器(GPIO_GDIR)
- 寄存器功能:通过寄存器中的位(Bit n)控制对应 GPIO[n] 信号的方向。
- 注意:仅当对应的 I/O复用器(I/O MUX) 配置为 GPIO功能 时,此寄存器的设置才会生效。
位值定义:
- 0(输入模式):GPIO引脚配置为 输入(读取外部信号)。
- 1(输出模式):GPIO引脚配置为 输出(驱动外部设备)。
2.4 读取引脚值
GPIO4_DR地址:0x020A8000
数据位(Data bits)
- 寄存器功能:当信号配置为 输出模式(GDIR[n]=1)时,此寄存器定义了GPIO输出的电平值。写入该寄存器的值会被存储,读取操作则根据当前模式返回不同结果:
- 输出模式(GDIR[n]=1):返回寄存器中存储的值(即用户写入的值)。
- 输入模式(GDIR[n]=0):返回引脚的实际输入电平值。
注意:
- 复用器配置要求:必须通过I/O复用器(IOMUX)将引脚配置为 GPIO模式,否则GPIO_DR的值无法与引脚信号连通。
- 输入路径禁用时的行为:若输入路径被禁用(例如未配置为输入模式),读取数据寄存器将始终返回零值。
3. 现场写程序
写好的源码:doc_and_source_for_mcu_mpu\IMX6ULL\source\02_录制视频时现场编写的源码\04_key_led
07_串口UART编程
01_硬件知识_UART硬件介绍
UART的全称是Universal Asynchronous Receiver and Transmitter,即异步发送和接收。 串口在嵌入式中用途非常的广泛,主要的用途有:
-
打印调试信息;
-
外接各种模块:GPS、蓝牙;
串口因为结构简单、稳定可靠,广受欢迎。
通过三根线即可,发送、接收、地线。
TxD线把PC机要发送的信息发送给ARM开发板。 最下面的地线统一参考地。
2. 串口的参数
-
波特率:一般选波特率都会有9600,19200,115200等选项。其实意思就是每秒传输这么多个比特位数(bit)。
-
起始位:先发出一个逻辑”0”的信号,表示传输数据的开始。
-
数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。小端传输。
-
校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。
-
停止位:它是一个字符数据的结束标志。
怎么发送一字节数据,比如‘A‘? ‘A’的ASCII值是0x41,二进制就是01000001,怎样把这8位数据发送给PC机呢?
-
双方约定好波特率(每一位占据的时间);
-
规定传输协议
-
原来是高电平,ARM拉低电平,保持1bit时间;
-
PC在低电平开始处计时;
-
ARM根据数据依次驱动TxD的电平,同时PC依次读取RxD引脚电平,获得数据;
-
前面图中提及到了逻辑电平,也就是说代表信号1的引脚电平是人为规定的。 如图是TTL/CMOS逻辑电平下,传输‘A’时的波形:
在xV至5V之间,就认为是逻辑1,在0V至yV之间就为逻辑0。
如图是RS-232逻辑电平下,传输‘A’时的波形:
在-12V至-3V之间,就认为是逻辑1,在+3V至+12V之间就为逻辑0。
RS-232的电平比TTL/CMOS高,能传输更远的距离,在工业上用得比较多。
市面上大多数ARM芯片都不止一个串口,一般使用串口0来调试,其它串口来外接模块。
3. 串口电平
ARM芯片上得串口都是TTL电平的,通过板子上或者外接的电平转换芯片,转成RS232接口,连接到电脑的RS232串口上,实现两者的数据传输。
现在的电脑越来越少有RS232串口的接口,当USB是几乎都有的。因此使用USB串口芯片将ARM芯片上的TTL电平转换成USB串口协议,即可通过USB与电脑数据传输。
上面的两种方式,对ARM芯片的编程操作都是一样的。
4. 串口内部结构
ARM芯片是如何发送/接收数据? 如图所示串口结构图:
要发送数据时,CPU控制内存要发送的数据通过FIFO传给UART单位,UART里面的移位器,依次将数据发送出去,在发送完成后产生中断提醒CPU传输完成。 接收数据时,获取接收引脚的电平,逐位放进接收移位器,再放入FIFO,写入内存。在接收完成后产生中断提醒CPU传输完成。
02_IMX6ULL_UART操作
1. 串口编程步骤
1.1 看原理图确定引脚
-
有很多串口,使用哪一个?看原理图确定
1.2 配置引脚为UART功能
-
至少用到发送、接收引脚:txd、rxd
-
需要把这些引脚配置为UART功能,并使能UART模块
1.3 设置串口参数
-
有哪些参数?
-
波特率
-
数据位
-
校验位
-
停止位
-
-
示例: 比如
15200,8n1
表示波特率为115200,8个数据为,没有校验位,1个停止位
1.4 根据状态寄存器读写数据
-
肯定有一个数据寄存器,程序把数据写入,即刻通过串口向外发送数据
-
肯定有一个数据寄存器,程序读取这个寄存器,就可以获得先前接收到的数据
-
很多有状态寄存器
-
判断数据是否发送出去?是否发送成功?
-
判断是否接收到了数据?
-
2. IMX6ULL串口框架
参考手册IMX6ULLRM.pdf中《CChapter 55: Universal Asynchronous Receiver/Transmitter (UART)》
各类芯片的UART框图都是类似的,当设置好UART后,程序读写数据寄存器就可以接收、发送数据了。
3. IMX6ULL串口操作
3.1 看原理图确定引脚
-
100ASM IMX6ULL的UART1接到一个USB串口芯片,然后就可以通过USB线连接电脑了
-
原理图如下
-
上图中的USART1_RX、USART1_TX,接到了PA9、PA10
3.2 使能UART
3.2.1 设置UART的总时钟
参考IMX6ULL芯片手册《Chapter 18: Clock Controller Module (CCM)》, 根据IMX6ULL的时钟树,设置CSCDR1寄存器就可以给UART提供总时钟,如下图:
下图是CSCDR1(CCM Serial Clock Divider Register 1)的位说明,对于UART,我们选择时钟源位80M:
3.2.2 给UART模块提供时钟
3.2.3 使能UART模块
虽然给UART提供了时钟,但是UART本身并未使能,需要设置以下寄存器:
3.2.4 配置引脚功能
-
配置UART1_TX引脚 在芯片手册中搜索UART1_TX,可以找到下图所示寄存器:
-
配置UART1_RX引脚 在芯片手册中搜索UART1_RX,可以找到下图所示寄存器:
3.2.5 IMX6ULL特殊的地方
-
Daisy Chain select IMX6ULL还有一个“Daisy Chain select”功能,比如下图中: A、B、C三个引脚都可以连接到Module X,它们都可以驱动Module X。 使用哪一个引脚呢?还需要设置“Daisy Chain select”,用来选择A、B、C之一。
Daisy Chain Select有什么用处呢? 比如UART,它只有TXD、RXD两个引脚:RXD有外部电路输入。 能否让UART的TXD直接给RXD提供数据?可以!这就是回环,方便调试。 怎么做? 可以在外部电路把TXD接到RXD,也可以在芯片内部让RXD可以选择数据来源,如下图所示:
-
UART1_RX的输入选择
3.3 设置串口参数
3.3.1 设置波特率
波特率算公式:
上述公式中,Ref Freq为80MHZ,BUMR和UBIR在寄存器中设置。
-
要先设置UBIR,再设置BUMR
-
UBIR、BUMR中的值,是实际值减1
为什么要拆分成两个寄存器?
-
扩展数值范围
波特率计算需要将主时钟分频到极低频率(如115200Hz),但单个寄存器的位数(如16位)不足以容纳过大的分频系数。- 拆分成分子和分母:通过分数形式(如
分子/分母
)表示分频比,可覆盖更广的频率范围。例如,若主频80MHz,分频系数可能需要达到数千级,拆分为两个寄存器可避免溢出。
- 拆分成分子和分母:通过分数形式(如
-
避免误差累积
直接使用整数分频可能导致波特率误差(如80000000/(16 * 3125)=1600
误差较大)。通过分子和分母的比值(如(3124+1)/(71+1)
),可以更精确逼近目标波特率。 -
硬件同步需求
硬件要求 先更新UBIR再更新UBMR,以确保分频比一次性生效。若只写一个寄存器,硬件会忽略新值,直到两者均被更新。
二、两者的具体分工
-
UBIR(分子寄存器)
- 存储 分频比的分子减1,例如目标分子是72,则UBIR写入71。
- 作用类似“分频器的精细调节”,用于微调波特率精度。
-
UBMR(分母寄存器)
- 存储 分频比的分母减1,例如目标分母是3125,则UBMR写入3124。
- 决定基础分频系数,类似“分频器的大范围调节”。
三、实际应用举例
假设主时钟80MHz,目标波特率115200:
- 计算公式:波特率=16×71+13124+180,000,000=115200
- 操作步骤:
- 设置UBIR = 71(分子72-1)
- 设置UBMR = 3124(分母3125-1)
此时硬件会按比例分频,确保数据位宽度精准。
四、不设置它们的后果
- 通信失败:波特率偏差超过3%时,接收方可能无法正确解析数据位。
- 硬件冻结:若仅配置一个寄存器,硬件会忽略新分频比,维持原有波特率。
3.3.2 设置数据格式
比如数据位设置为8,无校验位,停止位设置为1。
3.3.3 IMX6ULL芯片要求必须设置
对于UART1_UCR3的bit2,必须设置为1,芯片要求的,没什么道理可讲:设置UART1_UCR3的bit2=1就像给电路板上的信号“开绿灯”,确保数据从正确的路径进入UART模块。虽然表面上没有直接逻辑,但这是芯片硬件设计的基础规则,不遵守会导致功能异常。开发时只需按手册操作即可,无需深究其物理原理
3.4 根据状态寄存器读写数据
3.4.1 状态寄存器
3.4.2 接收数据寄存器
读这个寄存器,就可读取串口数据,如下图:
3.4.3 发送数据寄存器
3.5 寄存器地址
-
其他寄存器地址 用到的寄存器,从C代码定义如下:
volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA ; volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA ; volatile unsigned int *IOMUXC_UART1_RX_DATA_SELECT_INPUT ; volatile unsigned int *CCM_CSCDR1; volatile unsigned int *CCM_CCGR5; IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA = (volatile unsigned int *)(0x20E0084); IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA = (volatile unsigned int *)(0x20E0088); IOMUXC_UART1_RX_DATA_SELECT_INPUT = (volatile unsigned int *)(0x20E0624); CCM_CSCDR1 = (volatile unsigned int *)(0x020C4024); CCM_CCGR5 = (volatile unsigned int *)(0x020C407C);
-
UART1寄存器
UART1基地址:0x02020000,里面的寄存器用结构体来表示比较方便:
/*根据IMX6ULL芯片手册<<55.15 UART Memory Map/Register Definition>>的3608页,定义UART的结构体,*/ typedef struct { volatile unsigned int URXD; /**< UART Receiver Register, offset: 0x0 串口接收寄存器,偏移地址0x0 */ unsigned char RESERVED_0[60]; volatile unsigned int UTXD; /**< UART Transmitter Register, offset: 0x40 串口发送寄存器,偏移地址0x40*/ unsigned char RESERVED_1[60]; volatile unsigned int UCR1; /**< UART Control Register 1, offset: 0x80 串口控制寄存器1,偏移地址0x80*/ volatile unsigned int UCR2; /**< UART Control Register 2, offset: 0x84 串口控制寄存器2,偏移地址0x84*/ volatile unsigned int UCR3; /**< UART Control Register 3, offset: 0x88 串口控制寄存器3,偏移地址0x88*/ volatile unsigned int UCR4; /**< UART Control Register 4, offset: 0x8C 串口控制寄存器4,偏移地址0x8C*/ volatile unsigned int UFCR; /**< UART FIFO Control Register, offset: 0x90 串口FIFO控制寄存器,偏移地址0x90*/ volatile unsigned int USR1; /**< UART Status Register 1, offset: 0x94 串口状态寄存器1,偏移地址0x94*/ volatile unsigned int USR2; /**< UART Status Register 2, offset: 0x98 串口状态寄存器2,偏移地址0x98*/ volatile unsigned int UESC; /**< UART Escape Character Register, offset: 0x9C 串口转义字符寄存器,偏移地址0x9C*/ volatile unsigned int UTIM; /**< UART Escape Timer Register, offset: 0xA0 串口转义定时器寄存器 偏移地址0xA0*/ volatile unsigned int UBIR; /**< UART BRM Incremental Register, offset: 0xA4 串口二进制倍率增加寄存器 偏移地址0xA4*/ volatile unsigned int UBMR; /**< UART BRM Modulator Register, offset: 0xA8 串口二进制倍率调节寄存器 偏移地址0xA8*/ volatile unsigned int UBRC; /**< UART Baud Rate Count Register, offset: 0xAC 串口波特率计数寄存器 偏移地址0xAC*/ volatile unsigned int ONEMS; /**< UART One Millisecond Register, offset: 0xB0 串口一毫秒寄存器 偏移地址0xB0*/ volatile unsigned int UTS; /**< UART Test Register, offset: 0xB4 串口测试寄存器 偏移地址0xB4*/ volatile unsigned int UMCR; /**< UART RS-485 Mode Control Register, offset: 0xB8 串口485模式控制寄存器 偏移地址0xB8*/ } UART_Type; UART_Type *uart1 = (UART_Type *)0x02020000;