ARM架构与编程(三)(05_第1个程序深度解析、(01_编程知识_进制、02_编程知识_字节序_位操作、03_编程知识_汇编_反汇编_机器码蹲、04_编程知识_C与汇编深入分析、纯汇编点灯))

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芯片都是小字节序,有些处理器,可以置某个寄存器,让整个系统使用大字节序或小字节序。

  1. ​小端模式(低位在左)​​:

    • 适合需要频繁处理数据低位的场景。例如x86芯片(如你的电脑CPU)用小端,因为加减法运算时,CPU可以直接从低地址开始处理低位字节,效率更高。
    • 类比:计算“1234+5678”时,先算个位(4+8),再算十位(3+7),小端模式让CPU按这个顺序处理更自然。
  2. ​大端模式(高位在左)​​:

    • 符合人类书写习惯(如数字“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 反汇编示例

  1. LDR sp, [pc, #4]

    • ​作用​​:初始化栈指针(SP),为后续函数调用分配内存空间。
    • ​类比​​:炒菜前先准备好锅和铲子(栈是临时存放数据的“工作台”)。
  2. BL mymain

    • ​作用​​:跳转到主函数(mymain),同时保存返回地址到LR寄存器。
    • ​类比​​:厨师暂停当前步骤,先去处理另一道菜(主函数),做完再回来继续。
  3. NOP

    • ​作用​​:空操作,用于占位或延时。
    • ​类比​​:等待水烧开的1分钟,不做其他动作。

​四、程序如何运行?​

  1. ​上电启动​​:CPU从 0x08000000(RESET地址)开始执行。
  2. ​初始化栈​​:LDR sp, [pc, #4] 设置好栈指针。
  3. ​调用主函数​​:BL mymain 进入主程序逻辑。
  4. ​函数返回​​:主函数执行完后,通过 MOV PC, LR 返回初始位置。

 

3.2 烧写在Flash上的内容

地址Flash内容
0x0800000000000000
0x0800000408000009
0x08000008f8dfd004
0x0800000cf000f80c
0x0800001020010000
0x08000014bf00b501
0x080000181e419800
…………

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

  1. ​Clock is off during all modes. Stop enter hardware handshake is disabled.​
    ​翻译​​:所有模式下时钟均关闭。停止进入硬件握手(Hardware Handshake)功能被禁用。
    ​技术解读​​:

    • 此状态下,无论系统处于何种模式(如运行、待机等),时钟信号均停止工作。
    • 硬件握手通常指通过信号(如RTS/CTS)控制数据流,禁用后可能影响外设通信的同步性。
  2. ​Clock is on in run mode, but off in WAIT and STOP modes.​
    ​翻译​​:时钟在运行(RUN)模式下开启,但在等待(WAIT)和停止(STOP)模式下关闭。
    ​技术解读​​:

    • 运行模式下CPU和外设正常工作时钟运行,而低功耗模式(如等待、停止)下关闭以节省能耗。
    • 典型场景如STM32的停止模式(Stop Mode),所有时钟暂停,仅保留RTC等必要外设。
  3. ​Not applicable (Reserved).​
    ​翻译​​:不适用(保留)。
    ​技术解读​​:

    • 表示此条目当前无实际功能,可能为未来扩展预留。
  4. ​Clock is on during all modes, except STOP mode.​
    ​翻译​​:除停止(STOP)模式外,所有模式下时钟均保持开启。
    ​技术解读​​:

    • 仅在停止模式下关闭时钟,其他模式(如运行、睡眠)时钟持续运行。
    • 适用于需要实时响应的场景,例如通过外部中断快速唤醒系统。

​二、关键模式对比​

​模式​​时钟状态​​典型应用场景​相关技术文档
​所有模式​关闭全系统休眠或深度节能场景

13

17

19

​运行模式​开启正常执行代码、外设操作

17

18

19

​等待/停止模式​关闭低功耗待机,依赖外部事件唤醒

13

17

18

​保留模式​不适用未来功能扩展或特殊配置预留

13


​三、技术背景​

  1. ​硬件握手(Hardware Handshake)​

    • 一种通过物理信号(如RTS/CTS)控制数据流的方式,禁用后可能导致通信同步问题。
    • 常见于串口通信中,用于避免数据溢出或丢失。
  2. ​低功耗模式设计​

    • ​停止模式(STOP)​​:关闭所有时钟,仅保留关键外设(如RTC),功耗极低。
    • ​待机模式(Standby)​​:关闭1.8V内核电源,仅通过特定唤醒源(如外部中断)恢复。
  3. ​时钟管理策略​

    • 通过寄存器(如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闪存数据线2qspi
​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):返回引脚的实际输入电平值。

​注意​​:

  1. ​复用器配置要求​​:必须通过I/O复用器(IOMUX)将引脚配置为 ​​GPIO模式​​,否则GPIO_DR的值无法与引脚信号连通。
  2. ​输入路径禁用时的行为​​:若输入路径被禁用(例如未配置为输入模式),读取数据寄存器将始终返回零值。

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

为什么要拆分成两个寄存器?​

  1. ​扩展数值范围​
    波特率计算需要将主时钟分频到极低频率(如115200Hz),但单个寄存器的位数(如16位)不足以容纳过大的分频系数。

    • ​拆分成分子和分母​​:通过分数形式(如 分子/分母)表示分频比,可覆盖更广的频率范围。例如,若主频80MHz,分频系数可能需要达到数千级,拆分为两个寄存器可避免溢出。
  2. ​避免误差累积​
    直接使用整数分频可能导致波特率误差(如 80000000/(16 * 3125)=1600 误差较大)。通过分子和分母的比值(如 (3124+1)/(71+1)),可以更精确逼近目标波特率。

  3. ​硬件同步需求​
    硬件要求 ​​先更新UBIR再更新UBMR​​,以确保分频比一次性生效。若只写一个寄存器,硬件会忽略新值,直到两者均被更新。


​二、两者的具体分工​

  1. ​UBIR(分子寄存器)​

    • 存储 ​​分频比的分子减1​​,例如目标分子是72,则UBIR写入71。
    • 作用类似“分频器的精细调节”,用于微调波特率精度。
  2. ​UBMR(分母寄存器)​

    • 存储 ​​分频比的分母减1​​,例如目标分母是3125,则UBMR写入3124。
    • 决定基础分频系数,类似“分频器的大范围调节”。

​三、实际应用举例​

假设主时钟80MHz,目标波特率115200:

  • ​计算公式​​:波特率=16×71+13124+1​80,000,000​=115200
  • ​操作步骤​​:
    1. 设置UBIR = 71(分子72-1)
    2. 设置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;

03_IMX6ULL_UART编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值