当前位置: 首页> 文旅> 美景 > 企业建设流程_手机钓鱼网站免费制作_百度公司招聘_企业推广网络营销

企业建设流程_手机钓鱼网站免费制作_百度公司招聘_企业推广网络营销

时间:2025/7/10 20:54:30来源:https://blog.csdn.net/solomonzw/article/details/146230056 浏览次数:1次
企业建设流程_手机钓鱼网站免费制作_百度公司招聘_企业推广网络营销

一、 MPU、MCU、SoC、Application Processors的概念

CPU的发展有两个路线:MPU、MCU。

MPU只是一个处理器,需要搭配内存等非常多的其他外设才可以构成一个系统;

MCU内部有处理器、内存、Flash及其他模块,仅仅需要搭配少量外设就可以构成一个系统。

1. CPU(Central Processing Unit):

中央处理器,在PC机它是一个独立的芯片。

在嵌入系统中,它是芯片里的一个单元,跟其他模块比如USB、UART、音频组成一个芯片。

2. MPU(Mircro Processor Unit):

微处理器单元,其作用等同于在PC上使用的CPU,它也只仅仅是一个处理器,需要配合内存、Flash等外设才可以使用。

现在,除了个人电脑上的CPU,基本上找不到MPU了。并且我们一般不把电脑上的CPU当作MPU,毕竟它也是挺大的,并不“微小”。

3. MCU(Micro Controller Unit):

微控制器单元,有时又称为单片机。

MCU内部集成了处理器和各类模块,比如USB控制器、UART控制器、内存、Flash等等。只需要外接少量的器件,就可以搭建一个电子系统。

C51芯片、STM32等芯片,都是MCU。

MCU芯片内部的内存或Flash,容量在几KB、几百KB、几MB的量级,一般不再需要外接内存或Flash。

4. Application Processors:

手机中的主芯片跟MCU类似,也是集成了处理器和各类模块。但是它的性能已经极大提升,可以外接几GB的内存、几GB的Flash。

在手机中,这个主芯片一般用来处理显示、输入,运行用户的程序,所以称它为“Application Processors”。

“Application Processors”的概念可以扩展到其他场景,不再局限于手机。

跟MCU进行比较,Application Processors有以下不同:

a. 集成了更多的模块:

Application Processors内部集成了更多的模块,比如用于数据处理的DSP、用于图形显示的GPU,甚至有多个处理器。

这里又引入一个概念“片上系统”(SoC,System on Chip),SoC的本意是在一个芯片上就可以搭建完整的系统。

但是这个概念在日常使用中比较宽泛:MCU芯片也可以称为SoC,Application Processors也可以称为SoC,即使它们还必须外接内存/Flash等外设才可以运行。

在以前的文档中涉及SoC时,意指比较复杂的系统;这时候MCU不属于SoC,因为MCU比较简单。但是时代在发展,MCU也越来越复杂了,所以把MCU也当作SoC也是可以的。

在手机电路板中,可用空间非常小。Application Processors还需要搭配内存芯片才可以使用,于是发展出了一种名为package-on-package (PoP)的工艺:在电路板上先焊接Application Processors,在Application Processors上面再焊接内存芯片,即2个芯片叠在一起。

b. 运行的操作系统不同:

MCU上一般不运行操作系统,或是运行一些资源耗费较小的小型实时操作系统(RTOS)。

MCU一般用来处理实时性要求高的事情,处理一些比较简单的事情。

Application Processors基本上都会运行比较复杂的操作系统(比如Linux),在操作系统上运行多个APP。

二、 哈弗架构与冯诺伊曼架构

 CPU架构可以分为哈弗架构与冯诺伊曼架构。哈弗架构中指令与数据分开存放,CPU可以同时读入指令、读写数据。冯诺伊曼架构中指令、数据混合存放,CPU依次读取指令、读写数据,不可同时操作指令和数据。
ARM公司的芯片,ARM7及之前的芯片是冯诺伊曼架构,ARM7之后使用“改进的哈弗架构”。“改进的哈弗结构”如下所示:

 在“改进的哈弗架构”里,指令和数据在外部存储器中混合存放;CPU运行时,从指令cache中获得指令,从数据cache中读写数据。

终极记忆技巧

  • 冯·诺依曼 → ​​“一”​​(一体存储、一条总线)→ 通用场景。
  • 哈佛 → ​​“二”​​(二分开存储、二总线)→ 专用场景。

三、 指令集:CISC和RISC

CISC和RISC的区别:
CISC(Complex Instruction Set Computers,复杂指令集计算集)和RISC(Reduced Instruction Set Computers,精减指令集计算集)是两大类主流的CPU指令集类型。
其中CISC以Intel、AMD的X86 CPU为代表,而RISC以ARM、IBM Power为代表。开源的RISC-V也是RISC指令集。
RISC的设计初衷针对CISC CPU复杂的弊端,选择一些可以在单个CPU周期完成的指令,以降低CPU的复杂度,将复杂性交给编译器。

举一个例子,下图是实现这样的乘法运算:a = a * b。它需要4个步骤:读出a的值、读出b的值、相乘、写结果到a。

当然,CISC也是要通过操作内存、寄存器、运算器来完成复杂指令的。它在实现时,是将复杂指令转换成了一个微程序,微程序在制造CPU时就已存储于微服务存储器。一个微程序包含若干条微指令(也称微码),执行复杂指令时,实际上是在执行一个微程序。这也带来两种指令集的一个差别,微程序的执行是不可被打断的,而RISC指令之间可以被打断,所以理论上RISC可更快响应中断。

1. 指令能力:

CISC的指令能力强,单多数指令使用率低却增加了CPU的复杂度,指令是可变长格式;RISC的指令大部分为单周期指令,指令长度固定。RISC对内存只有load/store操作,数据的运算都是在CPU内部实现。

2. 寻址方式:CISC支持多种寻址方式;RISC支持的寻址方式少;

3. 实现方式:CISC通过微程序控制技术实现;RISC增加了通用寄存器,硬布线逻辑控制为主,采用流水线方式执行。

4. 研发周期:CISC的研制周期长;RISC硬件简单,需要优化编译器。

ARM公司的芯片都使用RISC指令集,对内存只有load/store操作,数据的处理是在CPU寄存器上进行。

ARMv8 架构与指令集.学习笔记 - 沉思 - CSDN博客.html

ARMv8 架构与指令集.学习笔记_armv8架构和指令集 学习笔记-CSDN博客

AArch32和AArch64之间的切换只能通过发生异常或者系统Reset来实现.(A32 -> T32之间是通过BX指令切换的)

一篇文章读懂Armv8 AArch64 - 简书.html

一篇文章读懂Armv8 AArch64 - 简书

CISC和RISC的区别

CISC与RISC指令集对比-CSDN博客

嵌入式系统硬件结构与启动

第01节 XIP的概念

XIP: eXecute In Place, 本地执行。可以不用将代码拷贝到内存,而直接在代码的存储空间运行。
XIP devices: Devices which are directly addressable by CPU

📌 XIP(本地执行)就像在图书馆直接阅读

核心概念: 代码无需借书回家(复制到内存),直接在书库(存储介质)里阅读(执行)


📚 传统执行 vs XIP执行

传统方式XIP方式
必须把书借回家(代码拷贝到RAM)直接在图书馆看书(存储介质直接执行)
需要书架空间(占用内存)节省书架空间(减少内存占用)
每次都要搬运(启动延迟)即拿即读(快速启动)

🧩 XIP三大关键要素

1️⃣ 直接寻址的书架(存储介质特性)
  • Nor Flash:像排列整齐的书架,可随机翻阅任意一页
    支持XIP → 常见于嵌入式设备固件存储

  • Nand Flash:像打包的快递箱,必须整箱拆封才能找内容
    不支持XIP → 多用于大容量存储(如SSD)

2️⃣ 图书导航系统(CPU支持)
  • CPU需自带"图书检索能力"(直接访问存储地址)

  • 传统架构:CPU → RAM → 存储设备(三级跳转)
    XIP架构:CPU → 存储设备(一步直达)

3️⃣ 特殊书籍编码(代码优化)
  • 位置无关代码(PIC):书的内容不依赖摆放位置
    如:"第三章内容"不写"参见第50页",而写"参见下一章"

  • 只读设计:图书馆的书不可涂改(XIP通常用于只读场景)


🛠️ XIP实际应用场景

1️⃣ 智能手表启动

// 固件直接烧录在Nor Flash中
void main() {while(1) {display_time();  // 直接在Flash中执行}
}
  • 优势:0毫秒加载时间,省去RAM拷贝过程

2️⃣ 汽车电子控制单元(ECU)
  • 紧急制动程序固化在XIP设备中
    碰撞发生时无需等待代码加载,直接执行

3️⃣ 物联网传感器

# 嵌入式Linux内核XIP配置选项
CONFIG_MTD_XIP=y  # 启用XIP支持
CONFIG_ROMFS_FS=y # 只读文件系统

⚖️ XIP的优缺点对比

优点缺点
✅ 节省30-50%内存空间❌ 执行速度比RAM慢约3-5倍
✅ 实现纳秒级启动❌ 只读限制(无法动态修改代码)
✅ 降低硬件成本(减少RAM芯片)❌ 需要特殊存储介质(如Nor Flash)

🔍 技术细节扩展

  • 硬件加速方案
    部分MCU内置Flash加速器(如STM32的ART Accelerator)

    FLASH_ACR |= FLASH_ACR_PRFTEN; // 启用预取缓冲
  • 混合执行模式
    热代码(高频调用函数)加载到RAM,冷代码保留在XIP
    类似把常看的几本书带回家,其他仍在图书馆阅读


总结:XIP就像图书馆的阅览室革命——
对资源受限的嵌入式设备来说,XIP技术通过:

  1. 🚀 消除启动延迟:即点即读的执行方式

  2. 💾 突破内存限制:在存储芯片上直接运行代码

  3. 🔋 降低功耗:减少数据搬运的能耗
    成为物联网时代的核心技术基石。

问题引出:
 a. 系统支持SPI FLASH启动.
    这意味着可以运行SPI FLASH上的代码
    the system can boot from spi flash,
    so it needs to run code on spi flash
    
 b. 但是SPI FLASH不是XIP设备, 
    cpu无法直接执行里面的代码
    but the spi flash isn't xip device,
    cpu can't run code on spi flash directly
 
    问题来了,
    CPU如何执行SPI FLASH上的代码?
    一上电, CPU执行的第1个程序、第1条指令在哪里?
    
    how can the cpu run the code on spi flash ?
    where is the first code run by cpu, when power up ?

答案:
a. ARM板子支持多种启动方式:XIP设备启动、非XIP设备启动等等。
   比如:Nor Flash、SD卡、SPI Flash, 甚至支持UART、USB、网卡启动。
   这些设备中,很多都不是XIP设备。
   
   问:既然CPU无法直接运行非XIP设备的代码,为何可以从非XIP设备启动?
   答:上电后,CPU运行的第1条指令、第1个程序,位于片内ROM中,它是XIP设备。
       这个程序会执行必要的初始化,
       比如设置时钟、设置内存;
       再从"非XIP设备"中把程序读到内存;
       最后启动这上程序。
       
    猜测: ARM芯片内部有很多部件,这是一个片上系统(System on chip),
          比如有:
          cpu
          rom
          ram
          memory controller --- ddr
          sd/mmc controller --- sd card
          spi controller    --- spi flash
          usb controller    --- usb storage device
          uart controller
          ......
          interrtupt controller
          

b. 跟PC的类比
   CPU      ---- 单独的芯片
   启动设备 ---- BIOS芯片
   DDR      ---- 单独的可拔插式模块
   存储设备 ---- SATA硬盘,可拔插
   usb controller ...


第03节 嵌入式系统启动流程概述

主芯片内部有ROM,ROM程序协助从非XIP设备启动。以SD卡启动为例。而CPU只能运行XIP设备中的程序ROM程序做什么?
显然:ROM需要把SD卡上的程序读到内存里(片内RAM或是片外的DDR)

ROM程序要做的事情:
a. 初始化硬件
   初始化时钟,提高CPU、外设速度
   初始化内存:DDR需要初始化才能使用
   初始化其他硬件,比如看门狗、SD卡等
   
b. 从外设把程序复制到内存
b.1支持那么多的启动方式,SD卡、SPI FLASH、USB DISK,
   怎么选择?
   通过跳线,选择某个设备;
   或
   通过跳线,选择一个设备列表,按列表顺序逐个尝试
   或
   不让客户选择,按固定顺序逐个尝试
   
b.2 内存那么大,把程序从SD卡等设备,复制到内存哪个位置?复制多长?
    烧写在SD卡等设备上的程序,含有一个头部信息,里面指定了内存地址和长度;
    或
    不给客户选择,程序被复制到内存固定的位置,长度也固定。

b.3 程序在SD卡上怎么存?
    原始二进制(raw bin),
    或
    作为一个文件保存在分区里
    
c. 执行新程序
第04节 具体单板的启动流程


硬件知识_LED原理图

我们怎样去点亮一个LED呢?分为三步:

1.看原理图,确定控制LED的引脚;

2.看主芯片的芯片手册,确定如何设置控制这个引脚;

3.写程序;

LED的驱动方式,常见的有四种。

方式1:使用引脚输出3.3V点亮LED,输出0V熄灭LED。

方式2:使用引脚拉低到0V点亮LED,输出3.3V熄灭LED。

有的芯片为了省电等原因,其引脚驱动能力不足,这时可以使用三极管驱动。

方式3:使用引脚输出1.2V点亮LED,输出0V熄灭LED。

方式4:使用引脚输出0V点亮LED,输出1.2V熄灭LED。

由此,主芯片引脚输出高电平/低电平,即可改变LED状态,而无需关注GPIO引脚输出的是3.3V还是1.2V。
所以简称输出1或0:逻辑1-->高电平;逻辑0-->低电平

三极管的正偏反偏其实可以用一个简单的比喻来理解——水闸控制水流


1. 先理解三极管的结构

三极管就像有两个水闸(PN结)的水管系统,分为 发射极(E)基极(B)集电极(C)

  • NPN型:两个闸门朝外开(发射极→基极→集电极)

  • PNP型:两个闸门朝内开(方向相反)


2. 什么是「正偏」和「反偏」?

  • 正偏:给闸门一个「开门」的电压,允许水流通过。
    (相当于给PN结的P区接高电压,N区接低电压)

  • 反偏:给闸门一个「关门」的电压,阻止水流通过。
    (P区接低电压,N区接高电压)


3. 三极管的三种工作模式

三极管的两个闸门(发射结和集电结)可以组合出三种工作状态:

工作模式发射结偏置集电结偏置比喻解释应用场景
放大模式正偏反偏基极轻轻推闸,集电极大水流出信号放大(收音机)
饱和模式正偏正偏两个闸门全开,水流最大开关导通(LED控制)
截止模式反偏反偏两个闸门紧闭,完全断流开关断开

举个实际例子——水龙头控制水桶

  1. 放大模式

    • 轻轻拧水龙头(基极小电流),水管(集电极)就流出大量水。

    • 这就是为什么三极管能放大信号!

  2. 开关模式

    • 水龙头拧到底(基极大电流),水管全开(饱和导通);

    • 关紧水龙头(基极无电流),水管关闭(截止)。

    • 这就是三极管作为电子开关的原理!


常见误区提醒

  1. 正偏 ≠ 一定导通

    • 三极管导通需要两个条件:发射结正偏 + 集电结反偏(放大模式)或正偏(饱和模式)。

  2. NPN和PNP方向相反

    • NPN型用「正电压」驱动,PNP型用「负电压」驱动(就像水闸开门方向不同)。


一句话总结

正偏是「开闸放水」,反偏是「关闸断流」 —— 通过控制两个闸门的开关组合,三极管就能实现「信号放大」和「电子开关」两大核心功能! 🚀

 三极管和mos驱动LED电路_三极管驱动led-CSDN博客

三极管和mos驱动LED电路_三极管驱动led-CSDN博客

 

 S3C2440框架与启动过程

在嵌入式系统中,NOR Flash 和 NAND Flash 启动方式的主要区别在于 CPU 如何读取引导程序(Bootloader)。我用一个通俗易懂的方式来比喻:

  • NOR Flash 启动就像是直接从书本上阅读 📖——可以随机访问,每一页都能直接翻到,随时查看内容。
  • NAND Flash 启动就像是从压缩包里解压再阅读 📦,需要先找到引导程序,把它复制到 RAM 里,再运行。

1. NOR Flash 启动(XIP 方式)

适合用于小型嵌入式设备,常见于 MCU、BIOS 启动等

🔹 特点

  • 代码可以直接执行(即 XIP,eXecute In Place
  • CPU 上电后能直接读取 NOR Flash 的内容,不需要加载到 RAM
  • 适合存放 Bootloader 和固件
  • 无需额外的启动代码,启动时间短
  • 由于并行读取,NOR Flash 启动速度快
  • 缺点存取速度比 NAND 慢、容量小、价格高

🔸 常见应用

  • 单片机(MCU)固件存储(STM32、ESP32 内置 Flash)
  • 计算机 BIOS(主板上的 Flash 存储)
  • 高端嵌入式系统(如高端路由器、医疗设备)
  • 汽车控制单元(ECU)

📌 启动过程

  1. 设备上电,CPU 直接读取 NOR Flash 中的 Bootloader 代码。
  2. Bootloader 运行后,初始化 CPU、RAM、时钟等。
  3. Bootloader 负责加载操作系统或应用程序到 RAM 并执行。

🛑 注意: 由于 NOR Flash 容量有限,写入速度慢,如果你的固件较大,通常会把 Bootloader 存在 NOR Flash,而把操作系统加载到 RAM 运行。


(2) NAND Flash 启动(Boot ROM + Bootloader)

适合存储大数据,常用于 Linux 设备(如树莓派、安卓手机、SSD、U盘)

🔹 NAND Flash 的问题

  • CPU 不能直接从 NAND Flash 读取指令,因为它的读取方式是“块级”,不像 NOR 可以随时读取任意地址
  • 解决方案:使用 ROM——嵌入式 CPU 一般内置一个Boot ROM,会在上电时先运行这里的程序,然后去 NAND 里找 Bootloader 并加载到 RAM 运行。

🔸 启动过程

  1. 上电后,CPU 先运行 Boot ROM
  2. Boot ROM 负责从 NAND 里找 Bootloader,并把它加载到 RAM 运行
  3. Bootloader 再去 NAND 里加载 Linux 内核(或其他操作系统)到 RAM 运行
  4. 内核启动后,再去 NAND 里加载根文件系统(RootFS)

🛑 注意: 因为 NAND Flash 有“坏块”问题,所以 Bootloader 需要有 坏块管理功能(Bad Block Management, BBT),并通过 ECC 纠错算法 来确保数据正确性。

🔸 常见应用

  • U 盘
  • TF 卡
  • 固态硬盘(SSD)
  • 智能手机、智能家电

5. 总结对比

对比点NOR Flash 启动NAND Flash 启动
CPU 是否能直接读取?✅ 可以,按字节读取❌ 不能,需要 Boot ROM 加载
擦除方式整块擦除(慢)需要按页读写,块擦除
读速度
写入速度
容量小(16MB~512MB)大(1GB~几百GB)
功耗
适合存储固件(MCU、BIOS、代码)主要用于存大数据(SSD、U盘)

所以:

  • 用 MCU(STM32、ESP32)做嵌入式系统?→ 选 NOR Flash
  • 做 Linux、Android 设备(嵌入式系统、手机、SSD)?用 NAND Flash

你是想给 STM32 + ESP32 做 OTA 升级,需要用 外部 Flash 存储,具体要看用的是 QSPI Flash 还是 NAND/NOR Flash,然后再选择合适的 Bootloader 和 Flash 驱动。如果你的项目用的是 Zigbee 传感器系统,那么 可能需要选择 NOR Flash 作为存储固件的存储器

你现在用的是哪款 STM32 MCU?具体是外部 SPI Flash 还是 NAND Flash? 如果你能给我发一个开发板型号或者具体使用的 Flash 型号,我可以帮你分析一下最合适的 OTA 方案! 😃

GPIO: General-purpose input/output,通用的输入输出口

  1. GPIO模块一般结构:
  1. 有多组GPIO,每组有多个GPIO
  2. 使能:电源/时钟
  3. 模式(Mode):引脚可用于GPIO或其他功能
  4. 方向:引脚Mode设置为GPIO时,可以继续设置它是输出引脚,还是输入引脚
  5. 数值:对于输出引脚,可以设置寄存器让它输出高、低电平

      对于输入引脚,可以读取寄存器得到引脚的当前电平

  1. GPIO寄存器操作:
  1. 芯片手册一般有相关章节,用来介绍:power/clock

可以设置对应寄存器使能某个GPIO模块(Module)

有些芯片的GPIO是没有使能开头的,即它总是使能的

  1. 一个引脚可以用于GPIO、串口、USB或其他功能,

有对应的寄存器来选择引脚的功能

  1. 对于已经设置为GPIO功能的引脚,有方向寄存器用来设置它的方向:输出、输入
  2. 对于已经设置为GPIO功能的引脚,有数据寄存器用来写、读引脚电平状态

GPIO寄存器的2种操作方法:

原则:不能影响到其他位

  1. 直接读写:读出、修改对应位、写入
  2. 要设置bit n:
     

    val = data_reg;
    val = val | (1<<n);
    data_reg = val;
    要清除bit n:
    val = data_reg;
    val = val & ~(1<<n);
    data_reg = val;
    
    b.	set-and-clear protocol:
    set_reg, clr_reg, data_reg 三个寄存器对应的是同一个物理寄存器,
    要设置bit n:set_reg = (1<<n);
    要清除bit n:clr_reg = (1<<n);
    

&(按位与):像是在找公共好友,你和朋友在社交软件上都关注的人,才会出现在“交集”里。
|(按位或)是朋友圈合并,只要有一个人认识,就加到列表里。

 

第03节_IMX6ULL的GPIO操作方法

1. IMX6ULL的GPIO操作方法

CCM: Clock Controller Module (时钟控制模块)

IOMUXC ​: IOMUX Controller,IO复用控制器

GPIO: General-purpose input/output,通用的输入输出口

1.1  IMX6ULL的GPIO模块结构

参考资料:芯片手册《Chapter 28​: General Purpose Input/Output (GPIO)》

有5组GPIO(GPIO1~GPIO5),每组引脚最多有32个,但是可能实际上并没有那么多。

GPIO1有32个引脚:GPIO1_IO0~GPIO1_IO31;

GPIO2有22个引脚:GPIO2_IO0~GPIO2_IO21;

GPIO3有29个引脚:GPIO3_IO0~GPIO3_IO28;

GPIO4有29个引脚:GPIO4_IO0~GPIO4_IO28;

GPIO5有12个引脚:GPIO5_IO0~GPIO5_IO11;

GPIO的控制涉及4大模块:CCM、IOMUXC、GPIO模块本身,框图如下:

 

1.2  CCM用于设置是否向GPIO模块提供时钟

参考资料:芯片手册《Chapter 18: Clock Controller Module (CCM)》

GPIOx要用CCM_CCGRy寄存器中的2位来决定该组GPIO是否使能。哪组GPIO用哪个CCM_CCGR寄存器来设置,请看上图红框部分。

CCM_CCGR寄存器中某2位的取值含义如下:

① 00:该GPIO模块全程被关闭

② 01:该GPIO模块在CPU run mode情况下是使能的;在WAIT或STOP模式下,关闭

③ 10:保留

④ 11:该GPIO模块全程使能

GPIO2时钟控制:

GPIO1、GPIO5时钟控制:

 

GPIO3时钟控制:

GPIO4时钟控制:

 

1.3  IOMUXC:引脚的模式(Mode、功能)

参考资料:芯片手册《Chapter 32: IOMUX Controller (IOMUXC)》。

 

好的!你可以把 IOMUX(I/O Multiplexer) 理解为一个“信号分流器”,它能让一个引脚(pad)根据需要选择不同的功能。简单来说,就是一条路上有多个岔路口,你可以选择走哪条


1. IOMUX 的作用

在嵌入式系统中,每个 引脚(pin) 都可以有多种用途,比如:

  • GPIO(普通输入/输出)
  • UART(串口通信)
  • I2C(数据总线)
  • SPI(高速通信)
  • PWM(脉宽调制)
  • ADC(模拟输入)
  • 甚至可能是 专用的功能接口

一个物理引脚可以被用作不同的功能,但一个引脚同一时间只能选一个功能。这里就需要 IOMUX(I/O多路复用),它相当于一个开关,你可以选择这个引脚具体执行哪个功能。


2. 什么是 MUX_MODE

每个引脚有多个可用功能,MUX_MODE 就是用来告诉芯片 “当前选择哪种功能” 的设置选项。

举个例子: 假设你有一个引脚 GPIO1_IO03(编号3的引脚),它可能有不同的用途(功能)

  • ALT0 - GPIO3_IO03(普通IO)
  • ALT1 - PWM1_OUT(输出PWM信号)
  • ALT2 - UART2_TX(作为串口发送引脚)
  • ALT3 - SPI1_MISO(作为 SPI 的数据输入)
  • ALT4 - 其他功能...

你可以通过 IOMUXC_SW_MUX_CTL_xxx 这个寄存器,来告诉芯片你想让这个引脚干什么。比如:

IOMUXC_SW_MUX_CTL_PAD_GPIO3_IO03 = 0x03; // 选择 SPI1_MISO 功能

  • 这样,原本是 GPIO3_IO03 的引脚,现在变成了 SPI 总线的 MISO(主输入从输出)。
  • 如果你改成 ALT2,那么它就会变成 UART 发送(TX) 的引脚。
  • 如果你设置为 ALT0,它就变回普通 GPIO

简单来说,MUX_MODE 就是这个引脚的“职业”,你可以决定它是干啥的!


3. 什么是 IOMUXC_SW_MUX_CTL_GRP_<GROUP_NAME>

除了单独控制每个引脚的功能,有时候一个设备需要多个引脚一起工作,比如:

  • UART(串口)一般需要 TX(发送)、RX(接收)
  • I2C 需要 SDA(数据线)和 SCL(时钟)
  • SPI 可能需要 SCK(时钟)、MOSI(主发从收)、MISO(主收从发)、CS(片选)

为了方便,IOMUX 会把多个引脚分成一个组(Group),用 IOMUXC_SW_MUX_CTL_GRP_xxx 来设置整个组的工作模式,而不是一个个去改 IOMUXC_SW_MUX_CTL_PAD_xxx

💡 通俗易懂的比喻 想象你有一个万用插座,可以插不同的家电:

  • ALT0:电饭煲模式
  • ALT1 - 充电模式(比如给手机充电)
  • ALT2 - 电风扇模式
  • ALT3 - 电暖气模式

你可以在 IOMUXC_SW_MUX_CTL_PAD_xxx选择插座的工作模式,但如果有多个插口(就像 UART 需要 TX/RX ),那你可以一口气给所有插口都设定一个模式,靠 IOMUXC_SW_MUX_CTL_GRP_xxx 一起管理它们。


4. 总结

  • IOMUXC_SW_MUX_CTL_PAD_xxx单个引脚多功能选择(相当于给某个员工分配不同工作)。
  • IOMUXC_SW_MUX_CTL_GRP_xxx:一组多个引脚一起设定(相当于给一整个部门安排相同任务)。
  • ALT0ALT1ALT2 等等代表引脚的不同功能模式(比如作为 GPIO、UART、SPI 等)。
  • <<(左移)等于乘以 2,常用于控制引脚功能选择的二进制掩码

🔹 一个例子 假如你要把 GPIO3_IO03 变成 UART TX(发送)模式:

IOMUXC_SW_MUX_CTL_PAD_GPIO3_IO03 = (1 << 2); // 这里 1 << 2 表示选择 ALT2 模式(串口 TX)

这里的 1 << 2 的意思就是把 1 向左移动 2 位,变成 00000100,然后把它赋值给 MUX 寄存器,让它选择 ALT2(UART TX)


4. 总结

符号作用类比
<< (左移)值变大,相当于乘 2把数字变成更大的单位(比如 10 * 2 = 20)
&(按位与)两个位都为 1 才是 1,否则 0选共同点,比如你和朋友都认识某人,才会把这个人加入列表
``(按位或)只要一个是 1 结果就是 1

如果你要控制单个引脚的功能,就用 IOMUXC_SW_MUX_CTL_PAD_xxx自己选要给这个引脚什么功能
如果你要给一组引脚一起设置功能(比如 UART、SPI 这些需要多个引脚一起工作),就用 IOMUXC_SW_MUX_CTL_GRP_xxx 进行批量设置

对于某个/某组引脚,IOMUXC中有2个寄存器用来设置它:

① 选择功能:

IOMUXC_SW_MUX_CTL_PAD_<PADNAME> :Mux pad xxx,选择某个pad的功能

IOMUXC_SW_MUX_CTL_GRP_<GROUP NAME>:Mux grp xxx,选择某组引脚的功能

某个引脚,或是某组预设的引脚,都有8个可选的模式(alternate (ALT) MUX_MODE)。

比如:下图这个loopbck很适合自检

 回环测试就像设备的“自问自答”——通过自发自收信号,无需外部依赖即可验证自身核心功能是否正常。它是工程师排查硬件、网络问题的“第一道检测工具”,能快速区分设备内部故障与外部环境问题

② 设置上下拉电阻等参数:

IOMUXC_SW_PAD_CTL_PAD_<PAD_NAME>:pad pad xxx,设置某个pad的参数

IOMUXC_SW_PAD_CTL_GRP_<GROUP NAME>:pad grp xxx,设置某组引脚的参数

比如:

IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00寄存器描述

地址: 0x20E_0000 基地址 + 0x26E8 偏移量 = 0x20E_26E8h

名称描述
16HYS迟滞特性(Hysteresis)
15-14PUS上拉/下拉电阻选择
13PUE保持(Keeper)或上拉(Pull)选择
12PKE上拉/保持使能位
11-10ODE开漏输出使能
9-8SPD速度(SPEED)
7Reserved保留位
6Reserved保留位
5-3DSE驱动能力
0SRE滞后特性(Slew Rate)

具体字段含义:

  1. HYS(迟滞特性)

    • 0 关闭
    • 1 使能迟滞特性(Hysteresis)
  2. PUS(上拉/下拉电阻选择)

    • 00:100K 欧姆下拉电阻(100K_Ohm_Pull_Down)
    • 01:47K 欧姆上拉
    • 10:100K 欧姆上拉
    • 11:22K 欧姆上拉
  3. PUE(上拉/保持功能)

    • 0保持(Keeper)(维持原状态)
    • 1:上拉(Pull)
  4. PKE(上拉/保持 使能位)

    • PKE_0:关闭上拉/保持功能
    • PKE_1:使能上拉/保持功能
  5. ODE(开漏输出 使能位)

    • ODE_0:禁用开漏(普通推挽模式)
    • ODE_1:使能开漏(Open Drain 模式)
  6. SPEED(速度)

    • 00:低速(low 50MHz)
    • 01:中速(100MHz)
    • 10:中高速(150MHz)
    • 11:高速(200MHz)
  7. DSE(驱动能力)

    • 000:高驱动能力, 0 欧姆
    • 001
    • 010:3_Ω
    • 011:3_Ω_150
    • 100:6_Ω
    • 101:9_Ω
    • 110:12_Ω
    • 111:驱动能力最大(DSE_7)
  8. SRE(边沿速率)

    • SRE_0:慢速
    • SRE_1:快速

 

  • HYS:防止信号抖动(开了更稳定)
    想象一下,如果你在一个坡上走路,快到坡顶时,不是一下子就翻过去,而是走到一定角度才会彻底翻过去。这就是 迟滞 的作用。
    如果没有迟滞,信号在“高”和“低”之间的边界徘徊时,会不断震荡(类似于手抖导致误触)。开启 HYS,能让信号更加稳定,不会误触发。       

    适用场景:
    机械开关(因为开关的弹簧可能导致短暂的接触不稳定,开启 HYS 过滤掉误触)。
    在有电噪声干扰的环境中(防止误触发)。
    例如:按钮按下后,不希望它来回抖动触发多次,开启 HYS 就能防止错误触发。

  • PUE选择是上拉还是 Keeper(保持原来的状态)
  • 保持(Keeper)模式:就像在门上装了一个门吸,轻推门不会自动关上,而是会“维持”上一个状态。如果门是关的,它会尽量保持关着;如果是开的,它会保持打开,只有用力推才会改变状态。
  • 上拉(Pull-up)模式:就像门弹簧,让门自动关上,不让它悬在半空。对于信号来说,就是让它自然地处于高电平
  • 禁用上拉/保持(PUE_0):门完全松开,外界施加什么力,它就会往那边倒。信号会随外部电流自由变化,不会自己保持高电平。

  • PKE:开关,控制是否启用 PUE
  • PKE = 1:允许 PUE 设置生效,可以选择上拉或保持。
  • PKE_0(禁用):完全关闭上拉和保持功能。
  • PKE_1(使能):允许使用 PUE 的上拉或保持功能。
  • PKE 关闭 → 无论 PUE 设置什么,pull-up/pull-down 功能都关闭,等同于悬空。
  • PKE 开启 → PUE 才能生效(才能控制是上拉,还是保持)。
  • 案例: 如果你希望某个引脚默认是高电平(上拉)

  • 你需要先设置 PKE = 1(打开上拉/保持功能)
  • 选择 PUE = 1(开启上拉)
  • 选择 PUS(上拉电阻大小)

  • ODE开漏模式(外部上拉)
  • 适用于 I2C 这类需要多个设备共用数据线的情况
  • 在开漏模式下:
    • 不导通(High-Z):外部上拉电阻决定信号高电平
    • 导通(低电平):可以拉低电平,但不能主动输出高电平
  • DSE控制引脚的驱动能力(决定电流大小)
  • 如果 DSE = 000(低驱动),相当于用细的水管,水流小,供电弱。
  • 如果 DSE = 110(最大驱动),相当于用粗水管,水流量大,动力强。
  • 应用场景

  • 如果你要驱动的是 LED等小功率负载,可以用小的驱动能力(低电流)。
  • 如果你要驱动的是 电机、蜂鸣器等大功率器件,就需要高驱动能力,DSE 设大一点。

  • SRE信号切换的速度(高速切换可能导致干扰)
  • SRE_0(慢速):你慢慢按开关,灯泡逐渐亮起(慢速变化,信号平稳)
  • SRE_1(快速):你突然按开开关,灯泡“啪”地一下亮了(信号切换很快)
  • 什么时候用?
  • 低速模式(SRE_0)

    • 适用于低速信号,如普通 GPIO,I2C
    • 这样可以减少信号干扰(EMI),让信号更稳定。
  • 高速模式(SRE_1)
    • 用于需要高速信号的场合,比如 SPI、SD卡 或者某些高速时钟信号
    • 可能会产生更多电磁干扰(EMI),容易导致信号质量变差,所以不能盲目提高。

 

  • HYS:防止信号抖动(开了更稳定)
  • PUS:选择默认状态(上拉/下拉电阻的大小)
  • PUE选择是上拉还是 Keeper(保持原来的状态)
  • PKE:开关,控制是否启用 PUE
  • ODE开漏模式(外部上拉)
  • DSE控制引脚的驱动能力(决定电流大小)
  • SRE信号切换的速度(高速切换可能导致干扰)
1.4  GPIO模块内部

框图如下:


GPIO_EDGE_SEL(中断触发方式选择)

这个寄存器用来 选择是上升沿触发还是下降沿触发。通常和 GPIO_IMR 配合使用。

设为 1(高低沿触发) → 无论 电平从低到高(上升沿)还是从高到低(下降沿),都会触发中断。
设为 0(由 ICR 寄存器决定) → 只有特定的电平变化才会触发中断。

通俗解释

  • 想象你家门口装了个门铃感应器,你可以选择:
    • 打开门时触发(上升沿触发)
    • 关门时触发(下降沿触发)
    • 两者都触发(双沿触发)
  • 比如:
    • 你希望 按下按钮(低 -> 高)时触发中断,就选择上升沿触发
    • 你希望松开 按钮时触发,就选择下降沿 触发。
    • 你希望按下和松开 按钮都触发事件,就用 双沿触发

GPIO_IMR(中断屏蔽寄存器 - Interrupt Mask Register)

  • 这个和 PUE 的作用有点类似,都是控制是否使能中断
  • 它和 GPIO_ISR 搭配使用。
    • 设为 1(使能) → 当有中断发生,CPU 会接收到中断信号。
    • 设为 0(禁用) → 即使 GPIO_ISR 被置1,CPU 也不会被通知(等于屏蔽掉这个中断)。

PUE 不同的地方是:

  • PUE 只是控制 是否上拉/下拉,主要是设置输入信号的默认状态
  • IMR 控制的是 是否让中断信号传到 CPU,如果不想让某个信号打扰 CPU,就可以在 GPIO_IMR 中屏蔽它。

GPIO_ISR(中断状态寄存器 - Interrupt Status Register)

  • 记录了哪些引脚发生了中断
  • 通过写入 1 清除相应的中断标志位(清除后 CPU 就不会再收到这个中断)。
通俗解释
  • 假设你的门铃突然响了,你可以查看摄像头(GPIO_ISR),看看是不是外卖来了。
  • 如果是快递员在门口站着,GPIO_ISR 对应的引脚位是 1,代表有人按了门铃。
  • 当你开门后,相当于手动写 1GPIO_ISR,清除这个状态,让它恢复到 0,下次有人按门铃时,你才能再次收到通知。

代码中你需要做的事情

  1. 检查 GPIO_ISR,看看哪个引脚触发了中断。
  2. 清除中断状态:通过1GPIO_ISR 的相应位,防止中断一直触发。
  3. 执行中断回调:比如用户按下按钮后,你可以在中断处理程序(ISR)里执行特定任务,比如点亮 LED 或者发送数据。

我们暂时只需要关心3个寄存器:

① GPIOx_GDIR:设置引脚方向,每位对应一个引脚,1-output,0-input

② GPIOx_DR:设置输出引脚的电平,每位对应一个引脚,1-高电平,0-低电平

③ GPIOx_PSR:读取引脚的电平,每位对应一个引脚,1-高电平,0-低电平

怎么编程

1.5  读GPIO

翻译一下:

① 设置CCM_CCGRx寄存器中某位使能对应的GPIO模块 // 默认是使能的,上图省略了

② 设置IOMUX来选择引脚用于GPIO

③ 设置GPIOx_GDIR中某位为0,把该引脚设置为输入功能

④ 读GPIOx_DR或GPIOx_PSR得到某位的值(读GPIOx_DR返回的是GPIOx_PSR的值)

 

1.6  写GPIO

翻译一下:

① 设置CCM_CCGRx寄存器中某位使能对应的GPIO模块 // 默认是使能的,上图省略了

② 设置IOMUX来选择引脚用于GPIO

③ 设置GPIOx_GDIR中某位为1,把该引脚设置为输出功能

④ 写GPIOx_DR某位的值

需要注意的是,你可以设置该引脚的loopback功能,这样就可以从GPIOx_PSR中读到引脚的有实电平;你从GPIOx_DR中读回的只是上次设置的值,它并不能反应引脚的真实电平,比如可能因为硬件故障导致该引脚跟地短路了,你通过设置GPIOx_DR让它输出高电平并不会起效果。

好的!我尽量用通俗的比喻和例子来解释这个问题,保证你听完就懂!


场景比喻:电灯开关和灯泡

假设你有一个电灯开关(GPIO引脚),开关有两个状态:​开(高电平)​关(低电平)​

  1. GPIOx_DR(数据寄存器)​

    • 就像你手动拨动开关的动作
    • 你告诉开关:“我要开灯!”(设置 DR=1),或者“我要关灯!”(设置 DR=0)。
    • 但是,如果灯泡坏了,或者电线断了,你拨动开关后,灯泡可能根本不会亮(实际电平不对)。
    • 此时,你如果只看自己拨动开关的动作(读 DR),你会以为灯是开着的,但实际灯泡根本没亮!
  2. GPIOx_PSR(引脚状态寄存器)​

    • 就像你直接去看灯泡的状态
    • 如果灯泡真的亮了(引脚实际电平=高),你看到的就是“亮”;如果没亮(实际电平=低),看到的就是“灭”。
    • 这就是“真实电平”的检测。

Loopback(回环功能)的作用

  • 没有 Loopback
    你只能看到自己拨动开关的动作(读 DR),但看不到灯泡实际亮不亮(无法读真实电平)。
    问题:如果灯泡坏了(比如引脚短路到地),你设置 DR=1 以为灯亮了,但实际灯没亮,你却不知道!

  • 启用 Loopback
    相当于在开关旁边装了一个摄像头,直接监控灯泡的状态(读 PSR)。
    效果:即使灯泡坏了,你也能通过摄像头看到“灯泡没亮”,从而发现问题!


通俗总结

  1. GPIOx_DR

    • 是“你希望引脚输出的值”(比如你希望灯亮)。
    • 但它只是你的“愿望”,不代表现实!
    • 读它时,只能看到你上次设置的愿望值​(比如你设置过 DR=1,读回来就是 1)。
  2. GPIOx_PSR

    • 是“引脚实际的物理电平”(比如灯泡实际亮不亮)。
    • 如果硬件有问题(比如引脚短路到地),即使你设置 DR=1PSR 也会告诉你实际是 0
  3. Loopback 的意义

    • 开启后,你可以通过 PSR 直接看到引脚的真实状态,避免被 DR 的“虚假愿望”欺骗!

实际例子

假设你控制一个 LED 灯(接在某个 GPIO 引脚):

  1. 你设置 DR=1(想让 LED 亮)。
  2. 如果 LED 正常,PSR 读回来也是 1(实际电平=高)。
  3. 但如果 LED 引脚短路到地
    • 你设置 DR=1,但实际电平被拉低到 0(LED 不亮)。
    • 读 DR 时,你看到的是 1(你以为灯亮了)。
    • 读 PSR 时,你看到的是 0(实际灯没亮,发现问题了!)。

一句话总结

  • ​**DR 是“你想让引脚输出的值”,PSR 是“引脚实际的值”​**。
  • 硬件故障时,DR 会撒谎,PSR 会说实话!​
  • Loopback 功能就是让你能直接听到“真话”(PSR)的钥匙!

第04节_IMX6ULL的LED寄存器操作

 

 

第05节_IMX6ULL的LED编程

 

 

int main(void)
{
volatile unsigned int *pReg;/* 使能GPIO5:默认使能*//*把GPI05_3设置为GPIO功能:IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000+0x14*/
pReg = (volatile unsigned int *) (0x02290000 + 0x14);
*pReg |= (0x5);/*把GPI05_3设置为输出引脚,GPI05_GDIR地址:0x020AC004 */
pReg = (volatile unsigned int *)(0x020AC004);
*pReg |= (1 << 3);pReg=(volatile unsigned int *)(0x020Ac00e);/*GPIO5_DR地址:0x020Aceee*/while (1)
{/* 设置GPI05_3输出1*/*pReg |= (1 << 3);delay(1000000);/* 设置GPI05_3输出e */*pReg &= ~(1 << 3);delay(1000000);
}return 0;
} << end main >>

 

.text
.global _start
_start:ldr sp, =(0x80000000+0x100000)bl main

 

我来用比喻和通俗的方式解释这段汇编代码的作用,保证你轻松理解!


场景比喻:搭建舞台前的准备工作

假设你要在舞台上表演(运行一个程序),但表演开始前需要做两件事:

  1. 腾出一块空地放道具​(设置栈空间)
    ——对应 ldr sp, =(0x80000000+0x100000)
  2. 喊主角上台​(跳转到 main 函数)
    ——对应 bl main

逐行解释

1. .text
  • 作用:告诉编译器:“后面的代码是程序的正片(指令),不是数据或其他东西”。
  • 类比:就像在剧本开头写“第一幕:正片开始”。
2. .global _start
  • 作用:声明 _start 是一个全局符号,表示“这是程序的入口”。
  • 类比:在剧本封面写:“演出从‘序幕’这个场景开始”。
3. _start:
  • 作用:标签(Label),标记程序开始执行的位置。
  • 类比:剧本中的“序幕”章节标题。
4. ldr sp, =(0x80000000+0x100000)
  • 作用:设置栈指针(Stack Pointer, sp),告诉程序“栈的起始位置在哪里”。
  • 详细解释
    • 0x80000000 可能是内存的起始地址(比如某些嵌入式系统的内存布局)。
    • 0x100000 是偏移量(1MB),所以栈顶地址是 0x80000000 + 0x100000 = 0x80100000
    • ldr sp, =... 表示把计算后的地址加载到栈指针寄存器 sp 中。
  • 类比:在舞台上划出一块区域(比如舞台右侧)专门用来临时堆放道具(栈空间)。
5. bl main
  • 作用:跳转到 main 函数,并保存返回地址(方便后续返回)。
  • 详细解释
    • bl 是“Branch with Link”指令,类似于调用函数。
    • main 通常是 C 程序的入口函数。
  • 类比:喊主角(main 函数)上台表演,同时记住现在的位置(返回地址),等主角演完再回来。

为什么需要这段代码?

  1. 栈的作用
    程序运行时需要临时存放数据(比如局部变量、函数调用的返回地址),就像表演时需要临时堆放道具。
    必须提前设置栈的位置,否则程序会崩溃(就像道具没地方放,导致表演混乱)。

  2. 从汇编跳转到 C 代码
    在嵌入式系统或裸机编程中,硬件启动后首先执行的是汇编代码,负责初始化基本环境(如栈),然后才能运行 C 代码。


完整流程比喻

  1. 开机瞬间
    硬件刚启动,就像舞台一片漆黑,没有任何道具和演员。

  2. 执行 _start

    • 设置栈指针:在舞台右侧划出一块区域放道具(ldr sp, ...)。
    • 调用 main:聚光灯亮起,主角 main 上台开始表演(程序正式运行)。
  3. 程序结束
    如果 main 函数返回(一般不会),程序会回到 bl main 的下一条指令,但通常嵌入式程序会无限循环或停机。


补充说明

  • 地址 0x80000000 的含义
    不同硬件平台的内存地址可能不同,这里只是一个示例。例如,某些 ARM 系统会将内存起始地址设为 0x80000000

  • 栈的增长方向
    栈通常是从高地址向低地址“向下生长”(像叠盘子,后放的盘子在下层)。设置 sp 为 0x80100000 后,栈会从这个地址开始向下使用。


一句话总结

这段代码是程序的“启动脚本”,做了两件事:

  1. 腾出一块地方给栈用​(设置 sp)。
  2. 喊 main 函数来干活​(跳转到 C 代码)。

 

 

  • ROM 代码负责加载用户程序(led.bin
  • ROM 代码不会直接运行 led.bin,而是先把它搬运到 DDR(0x80100000)
  • 程序从 0x80100000 处开始运行,栈从 0x80100000 向下增长

再举个例子

假设你刚买了一台电脑(类似 ROM 代码的作用),但里面没有系统。

  1. 电脑启动后,会去 U 盘或者硬盘里找系统(类似 BootROM 查找 SD 卡)。
  2. 找到系统(比如 led.bin),它不会直接运行,而是先复制到内存(类似 0x80100000)。
  3. 复制完成后,电脑跳转到内存里的系统入口地址,开始运行(类似 ldr sp, =0x80100000)。

整体流程

  1. 系统上电后,ROM 代码负责启动

    • ROM 代码(固件)会先去 EMMC 或 TF 卡(SD 卡)中读取存储在 1K 偏移处的启动信息
    • 这个启动信息告诉 ROM 应该去哪里加载用户程序(比如 led.bin)。
  2. ROM 代码把 led.bin 复制到 0x80100000 地址(DDR 里的某个区域)。

    • led.bin 是你的应用程序,比如它控制 LED 亮灭。
    • ROM 代码不会直接运行 led.bin,它只是负责搬运
  3. ROM 代码跳转到 0x80100000 处开始执行 led.bin

    • led.bin第一条指令ldr sp, =0x80100000,它的作用是 初始化栈指针(SP)
    • 之后,led.bin 开始执行,比如点亮 LED。

 

这部分指的是 “镜像的头部(Boot Header)+ 用户程序” 存放在 eMMC 或 TF/SD 卡上 偏移 1KB(即 0x400) 的位置。

i.MX 系列处理器(或类似架构)中,ROM(内部固化的引导代码) 会在上电后,去指定存储设备(如 eMMC、SD 卡)偏移 1KB 的位置读取一段**“头部信息”**。这段头部里包含了:

  1. 镜像长度
  2. 加载地址(Load Address)
  3. 启动参数(DCD 配置等)
  4. 校验信息(可选)

以及你真正的应用程序(这里是 led.bin)。

为什么要放在偏移 1KB?

  • i.MX 处理器的 BootROM 约定从偏移 0x400(也就是 1KB)处开始读取镜像信息。
  • 这 1KB 的区域既包含 Boot Header,也紧接着拼上了你的 led.bin
  • 当 BootROM 读到这个头部后,就知道要把 led.bin 加载到 哪块内存 并如何跳转执行。

简单比喻

  • 头部(Header) 就像一本书的目录页,告诉系统“程序放在哪儿、大小是多少、要怎么读”。
  • led.bin 就是正文,真正要执行的代码。
  • BootROM 启动时先看“目录”,然后把正文搬到内存指定位置,最后跳转执行。

因此,图中标注的 1k led_imx = 头部 + led.binled_img = 1k.bin + 头部 + led.bin 就是指:

  1. 在镜像文件的最前面拼接一段 头部信息(通常大小约 1KB)
  2. 再紧接着放 led.bin
  3. 最终将它们写到存储介质(eMMC 或 TF/SD)偏移 1KB

这样 BootROM 就能正确识别并加载你的 led.bin 程序去运行了。

 此资料是韦东山课程学习总结而来作为自我学习的资源

关键字:企业建设流程_手机钓鱼网站免费制作_百度公司招聘_企业推广网络营销

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: