参考《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0》
STM32F系列是中低端的32位ARM微控制器,由意法半导体(ST)公司出品,其内核是Cortex-M3
I.MX6U-ALPHA 开发板是一款以 NXP 的 I.MX6UL/ULL 为核心的 Cortex-A7开发平台
arm系列从arm11开始,以后的就命名为cortex,并且性能上大幅度提升。从cortex开始,分为三个系列,a系列,r系列,m系列
m系列与arm7相似,不能跑操作系统(只能跑ucos2),偏向于控制方面,说白了就是一个高级的单片机
arm7是最早的arm产品。m3是cortex m系列的过渡品,其低端市场被cortex m0的高端替代, 其高端市场又被cortex m4的低端取代
arm9 和cortex a8 是一个类型的,都是跑操作系统的,现在的高端手机,三星,htc等智能手机,就是用的cortex a8,cortex a9 内核的芯片作为cpu
(1) ARM7,ARM9属于v4T或v5E架构
(2) ARM11属于v6架构
(3) Contex属于v7架构
ARM7,ARM9的区别在于是否有MMU(存储器管理单元)或MPU(存储器保护单元)
ARM 采用的是32位架构,约定:
交叉编译器: ubuntu自带的gcc编译器是针对X86架构的,如果需要编译ARM架构的代码,就需要安装一个在X86架构的PC上可以运行且编译ARM架构代码的编译器,这个编译器就叫交叉编译器(在一个架构上编译另一个架构的代码)
1、是GCC编译器
2、在X86架构上运行
3、编译出来的可执行文件是在ARM芯片上运行
启动时,CortexA需要用汇编初始化一些SOC外设,设置好C语言运行环境(SP指针指向DDR)
下载链接:Linaro GCC 编译器
下载 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
比如放在目录:/usr/local/arm,然后解压
sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
修改环境变量,在最后面加入内容,然后重启
sudo vi /etc/profile
export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
安装相关库
sudo apt-get install lsb-core lib32stdc++6
查看是否安装成功,有显示版本号即可
arm-linux-gnueabihf-gcc –v
使用交叉编译器的命令:
arm-linux-gnueabihf-gcc
// 1、arm 表示这是编译 arm 架构代码的编译器
// 2、linux 表示运行在 linux 环境下
// 3、gnueabihf 表示嵌入式二进制接口
// 4、gcc 表示是 gcc 工具
Cortex-A 芯片一上电 SP 指针还没初始化,C 环境还没准备好,所以肯定不能运行 C 代码,必须先用汇编语言设置好 C 环境,比如初始化 DDR、设置 SP指针等等,当汇编把 C 环境设置好了以后才可以运行 C 代码
编写的是 ARM汇编,编译使用的 GCC 交叉编译器,汇编代码要符合 GNU 语法,GNU 汇编语法适用于所有的架构,并不是 ARM 独享的。每行一条语句,每条语句有三个可选部分,如下:
label:instruction @ comment
label 即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意 label 后面的冒号“:”,任何以冒号“:”结尾的标识符都会被认识是一个标号。
instruction 即指令,也就是汇编指令或伪指令。
@符号,表示后面的是注释,其实在 GNU 汇编文件中也可以使用“ /* ” 和 “ */ ”来注释,comment 就是注释内容
ARM 中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用
使用.section 伪操作来定义一个段,汇编系统预定义了一些段名:
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。
可以自定义段:.section .testsection @定义一个 testsetcion 段
汇编程序的默认入口标号是_start,也可以在链接脚本中使用 ENTRY 来指明其它的入口点
.global _start @.global 是伪操作,表示_start 是一个全局标号
_start:
ldr r0, =0x12 @r0=0x12
常见的伪操作有:
.byte 定义单字节数据,比如.byte 0x12
.short 定义双字节数据,比如.byte 0x1234
.long 定义一个 4 字节数据,比如.long 0x12345678
.equ 赋值语句,格式为:.equ 变量名,表达式,比如.equ num, 0x12,表示 num=0x12
.align 数据字节对齐,比如:.align 4 表示 4 字节对齐
.end 表示源文件结束
.global 定义一个全局符号,格式为:.global symbol
GNU 汇编同样也支持函数
函数名:
函数体
返回语句 @返回语句不是必须的
/* SVC 中断 */
SVC_Handler:
ldr r0, =SVC_Handler
bx r0
MOV 指令用于将数据从一个寄存器拷贝到另外一个寄存器,或者将一个立即数传递到寄存器里面
MOV R0,R1 @将寄存器 R1 中的数据传递给 R0,即 R0=R1
MOV R0, #0X12 @将立即数 0X12 传递给 R0 寄存器,即 R0=0X12
MRS 指令用于将特殊寄存器(如 CPSR 和 SPSR)中的数据传递给通用寄存器,要读取特殊寄存器的数据只能使用 MRS 指令!
MRS R0, CPSR @将特殊寄存器 CPSR 里面的数据传递给 R0,即 R0=CPSR
MSR 指令和 MRS 刚好相反,MSR 指令用来将普通寄存器的数据传递给特殊寄存器,也就是写特殊寄存器,写特殊寄存器只能使用MSR
MSR CPSR, R0 @将 R0 中的数据复制到 CPSR 中,CPSR=R0
每次只能读写存储器中的一个数据
ARM 不能直接访问存储器,比如 RAM 中的数据,一般先将要配置的值写入到 Rx(x=0~12)寄存器中,然后借助存储器访问指令将 Rx 中的数据写入到寄存器,读取的过程也一样
LDR 指令主要用于从存储加载数据到寄存器 Rx 中,LDR 也可以将一个立即数加载到寄存器 Rx中,LDR 加载立即数的时候要使用“=”,而不是“#”
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, [R0] @读取地址 0X0209C004 中的数据到 R1 寄存器中
STR 指令则是将数据写入到存储器中
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, =0X20000002 @R1 保存要写入到寄存器的值,即 R1=0X20000002
STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中
LDR 和 STR都是按照字进行读取和写入的,也就是操作的 32 位数据,如果要按照字节、半字进行操作的话可以在指令“LDR”后面加上 B 或 H,比如按字节操作的指令就是 LDRB 和STRB,按半字操作的指令就是 LDRH 和 STRH
保存 R0~ R15 寄存器的操作就叫做现场保护,恢复R0~R15 寄存器的操作就叫做恢复现场。在进行现场保护的时候需要进行压栈(入栈)操作,恢复现场就要进行出栈操作
压栈的指令为 PUSH,出栈的指令为 POP,PUSH 和 POP 是一种多存储和多加载指令,即可以一次操作多个寄存器数据,他们利用当前的栈指针 SP 来生成地址
PUSH <reg list> @将寄存器列表存入栈中。
POP <reg list> @从栈中恢复寄存器列表。
假设当前的 SP 指针指向 0X80000000
对于压栈
:
PUSH {R0~R3, R12} @ 将 R0 ~ R3 和 R12 压栈
PUSH {LR} @将 LR 进行压栈
对于出栈:
从栈顶SP 当前执行的位置开始,地址依次减小来提取堆栈中的数据到要恢复的寄存器列表中
POP {LR} @先恢复 LR
POP {R0~R3,R12} @ 在恢复 R0 ~ R3,R12
PUSH 和 POP 的另外一种写法:
STMFD SP!,{R0~R3, R12} @R0~R3,R12 入栈
STMFD SP!,{LR} @LR 入栈
LDMFD SP!, {LR} @先恢复 LR
LDMFD SP!, {R0~R3, R12} @再恢复 R0~R3, R12
STMFD 可以分为两部分:STM 和 FD,同理,LDMFD 也可以分为 LDM 和 FD,STM 和 LDM 是多加载和多存储,可以连续的读写存储器中的多个连续数据;FD(Full Descending)表示满递减,ARM 使用的 FD 类型的堆栈,SP 指向最后最后一个入栈的数值
异常的发生会导致程序正常运行的被打断, 并将控制流转移到相应的异常处理(异常响应),有些异常(fiq、irq)事件处理后,系统还希望能回 到当初异常发生时被打断的源程序断点处继续完成源程序的执行(异常返回),类似的还有子程序的调用和返回
ARM处理器中使用 R14实现对断点和调用点的记录,也称回连接寄存器(LR), 用于记录源程序的断点位置,以便正确的异常返回。
ARM处理器相应异常时,会自动完成将当前的PC保存到LR寄存器。ARM处理器执行子程序调用指令(BL )时,会自动完成将当前的PC的值减去4的结果数据保存到LR寄存器,即将调用指令的下紧邻指令的地址保存到LR
①、直接使用跳转指令 B、BL、BX 等。
②、直接向 PC 寄存器里面写入数据。
B 指令: 会将 PC 寄存器的值设置为跳转目标地址, 一旦执行 B 指令,ARM 处理器就会立即跳转到指定的目标地址(不记录当前状态)
在汇编中初始化 C 运行环境,然后跳转到 C 文件 main 函数中运行
_start:
ldr sp,=0X80200000 @设置栈指针,初始化SP 指针
b main @跳转到 main 函数
BL 指令: 跳转之前在寄存器 LR(R14)中保存当前 PC 寄存器值,跳转结束后再将 LR 寄存器中的值重新加载到 PC 中来就可以继续从跳转之前的代码处运行,是子程序调用一个基本但常用的手段
在Cortex-A 处理器的 irq 中断服务函数中,先使用汇编来实现现场的保护和恢复、获取中断号等,然后执行具体的中断处理过程时( C 函数),需要在汇编中调用 C 函数,执行完成后再返回到irq 汇编中断服务函数,执行恢复现场等操作
push {r0, r1} @保存 r0,r1
cps #0x13 @进入 SVC 模式,允许其他中断再次进去
bl system_irqhandler @加载 C 语言中断处理函数到 r2 寄存器中
cps #0x12 @进入 IRQ 模式
pop {r0, r1}
str r0, [r1, #0X10] @中断执行完成,写 EOIR
ARM 架构提供了 16 个 32 位的通用寄存器(R0 ~ R15)供软件使用,前 15 个(R0~R14)可以用作通用的数据存储,R15 是程序计数器 PC,用来保存将要执行的指令。ARM 还提供了一个当前程序状态寄存器 CPSR 和一个备份程序状态寄存器 SPSR,SPSR 寄存器就是 CPSR 寄存器的备份
R0~R15 就是通用寄存器:
①、未备份寄存器,即 R0~R7。
②、备份寄存器,即 R8~R14。
③、程序计数器 PC,即 R15。
ARM 处理器 3 级流水线:取指->译码->执行,如当前正在执行第一条指令的同时也对第二条指令进行译码,第三条指令也同时被取出存放在 R15(PC)中,R15(PC)中存放的就是第三条指令,对于 32 位的 ARM 处理器,每条指令是 4 个字节,因此R15 保存着当前执行的指令地址值加 8 个字节
CPSR 是当前程序状态寄存器,该寄存器包含了条件标志位、中断禁止位、当前处理器模式标志等一些状态位以及一些控制位。
当特定的异常中断发生时,SPSR 寄存器用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后可以用 SPSR 中保存的值来恢复 CPSR
格式:“IOMUXC_SW_MUC_CTL_PAD_XX_XX”
后面的“XX_XX”就是 GPIO 命名,比如有:GPIO1_IO01、UART1_TX_DATA、JTAG_MOD、SNVS_TAMPER1 等等
对于寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00,寄存器地址为 0X020E005C, 32 位,但是只用到了最低 5 位,其中bit0 ~ bit3(MUX_MODE)就是设置 GPIO1_IO00 的复用功能;
GPIO1_IO00 一共可以复用为 9 种功能 IO,分别对应 ALT0~ALT8,其中 ALT5 就是作为 GPIO1_IO00
IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA寄存器,UART1_TX_DATA 可以复用为 8 种不同功能的 IO,分为
ALT0~ALT5 和 ALT8、ATL9,其中 ALT5 表示 UART1_TX_DATA 可以复用为 GPIO1_IO16
GPIO组
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00用来配置 GPIO1_IO00 的,包括速度设置、驱动能力设置、压摆率设置等等
寄存器地址为 0X020E02E8。这也是个 32 位寄存器,但是只用到了其中的低 17 位
1、HYS(bit16):对应上图的 HYS,用来使能迟滞比较器,当 IO 作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。如果需要对输入波形进行整形的话可以使能此位。此位为 0 的时候禁止迟滞比较器,为 1 的时候使能迟滞比较器
2、PUS(bit15:14):对应上图的 PUS,用来设置上下拉电阻的
3、PUE(bit13):当 IO 作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器。当为 0 的时候使用状态保持器,当为 1 的时候使用上下拉。状态保持器在IO 作为输入的时候才有用,顾名思义,就是当外部电路断电以后此 IO 口可以保持住以前的状态
4、PKE(bit12):对应GPIO电路图的 PKE,此为用来使能或者禁止上下拉/状态保持器功能,为0 时禁止上下拉/状态保持器,为 1 时使能上下拉和状态保持器
5、ODE(bit11):对应GPIO电路图的 ODE,当 IO 作为输出的时候,此位用来禁止或者使能开路输出,此位为 0 的时候禁止开路输出,当此位为 1 的时候就使能开路输出功能
6、SPEED(bit7:6):对应GPIO电路图的 SPEED,当 IO 用作输出的时候,此位用来设置 IO 速度
7、DSE(bit5:3):对应GPIO电路图的 DSE,当 IO 用作输出的时候用来设置 IO 的驱动能力
8、SRE(bit0):对应GPIO电路图的 SRE,设置压摆率,当此位为 0 的时候是低压摆率,当为 1的时候是高压摆率
这里的压摆率就是 IO 电平跳变所需要的时间,比如从 0 到 1 需要多少时间,时间越小波形就越陡,说明压摆率越高;反之,时间越多波形就越缓,压摆率就越低
上图的左下角的 IOMUXC 框图里面就有SW_MUX_CTL_PAD_* 和 SW_PAD_CTL_PAD_* 两种寄存器,分别用来设置 IO 的复用功能和 IO 属性配置
上图的左上角部分为,当 IO 用作 GPIO 的时候需要设置的寄存器:DR、GDIR、PSR、ICR1、ICR2、EDGE_SEL、IMR 和 ISR(每组 GPIO 都有这 8 个寄存器)
① DR寄存器,32位,可读写
② GDIR 寄存器(方向寄存器),用来设置某个 GPIO 的工作方
向的,即输入/输出(0/1)
③ PSR 寄存器(状态寄存器),可获取GPIO 的高低电平值,功能和输入状态下的 DR 寄存器一样
④ ICR1和ICR2寄存器(中断控制寄存器),ICR1用于配置低16个GPIO,ICR2 用于配置高 16 个 GPIO,一个 GPIO 用两个位来配置中断的触发方式
⑤ IMR 寄存器(中断屏蔽寄存器),用来控制 GPIO 的中断禁止和使能
⑥ ISR寄存器(中断状态寄存器),只要某个 GPIO 的中断发生,那么ISR 中相应的位就会被置 1,处理完中断以后,必须清除中断标志位,清除方法就是向 ISR 中相应的位写 1,也就是写 1 代表清零
⑦ EDGE_SEL 寄存器(边沿选择寄存器),器用来设置边沿中断,这个寄存器会覆盖 ICR1 和 ICR2 的设置,如果相应的位被置 1,那么就相当于设置了对应的 GPIO 是双边沿(上升沿和下降沿)触发
每个外设的时钟都可以独立的使能或禁止,这样可以关闭掉不使用的外设时钟,起到省电的目的。如果要使用某个外设的话必须要先使能其时钟
简单了解一下 CCM 里面的外设时钟使能寄存器包含CCM_CCGR0~CCM_CCGR6 这 7 个寄存器,控制着 I.MX6U 的所有外设时钟开关
对于CCM_CCGR0:
CCM_CCGR0为32为位寄存器,其中每 2 位控制一个外设的时钟,比如 bit31:30 控制着GPIO2 的外设时钟
如果要打开 GPIO2 的外设时钟,只需要设置CCM_CCGR0 的 bit31 和 bit30 都为 1 即可,也就是 CCM_CCGR0=3 << 30。反之,如果要关闭GPIO2 的外设时钟,那就设置CCM_CCGR0 的 bit31 和 bit30 都为0即可
通过开发板原理图知道,LED0 (接在 GPIO_3 上,GPIO_3 就是 GPIO1_IO03)
步骤:
① GPIO1 时钟由 CCM_CCGR1 的 bit27 和 bit26 这两个位控制
② IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03复用为 ALT5
③ IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03配置
④ 将GPIO3_GDIR 的 bit3 设置为 1,表示输出
⑤ 向 GPIO3_DR 寄存器的 bit3 写入 0 即可控制 GPIO1_IO03 输出低电平,打开 LED,向 bit3 写入 1 可控制 GPIO1_IO03 输出高电平,关闭 LED
1、工程目录新建一个名为“led.s”的汇编文件
.global _start /* 全局标号 */
/*
* 描述: _start 函数,程序从此函数开始执行此函数完成时钟使能、
* GPIO 初始化、最终控制 GPIO 输出低电平来点亮 LED 灯。
*/
_start: //代码从这里开始执行
/* 例程代码 */
/* 1、使能所有时钟 */
ldr r0, =0X020C4068 /* 寄存器 CCGR0 */ //r0=0X020C4068
ldr r1, =0XFFFFFFFF //r1=0XFFFFFFFF
str r1, [r0] //将 r1 中的值写入到 r0 所保存的地址中去,相当于 CCM_CCGR0=0XFFFFFFFF,打开 CCM_CCGR0 寄存器所控制的所有外设时钟
//下面和上面一样,打开所有的时钟(方便后面的学习实验)
ldr r0, =0X020C406C /* 寄存器 CCGR1 */
str r1, [r0]
ldr r0, =0X020C4070 /* 寄存器 CCGR2 */
str r1, [r0]
ldr r0, =0X020C4074 /* 寄存器 CCGR3 */
str r1, [r0]
ldr r0, =0X020C4078 /* 寄存器 CCGR4 */
str r1, [r0]
ldr r0, =0X020C407C /* 寄存器 CCGR5 */
str r1, [r0]
ldr r0, =0X020C4080 /* 寄存器 CCGR6 */
str r1, [r0]
/* 2、设置 GPIO1_IO03 复用为 GPIO1_IO03 */
ldr r0, =0X020E0068 /* 将寄存器 SW_MUX_GPIO1_IO03_BASE 加载到 r0 中 */
ldr r1, =0X5 /* 设置寄存器 SW_MUX_GPIO1_IO03_BASE 的 MUX_MODE 为 5 */
str r1,[r0]
/* 3、配置 GPIO1_IO03 的 IO 属性
*bit 16:0 HYS 关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper 功能
*bit [12]: 1 pull/keeper 使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度 100Mhz
*bit [5:3]: 110 R0/6 驱动能力
*bit [0]: 0 低转换率
*/
ldr r0, =0X020E02F4 /*寄存器 SW_PAD_GPIO1_IO03_BASE */
ldr r1, =0X10B0
str r1,[r0]
/* 4、设置 GPIO1_IO03 为输出 */
ldr r0, =0X0209C004 /*寄存器 GPIO1_GDIR */
ldr r1, =0X0000008
str r1,[r0]
/* 5、打开 LED0
* 设置 GPIO1_IO03 输出低电平
*/
ldr r0, =0X0209C000 /*寄存器 GPIO1_DR */
ldr r1, =0
str r1,[r0]
/*
* 描述: loop 死循环
*/
loop:
b loop
2、使用交叉编译器 arm-linux-gnueabihf-gcc 来编译,将 led.s 编译为 led.o
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
其中“-g”选项是产生调试信息,GDB 能够使用这些调试信息进行代码调试。“-c”选项是编译源文件,但是不链接。“-o”选项是指定编译产生的文件名字,这里我们指定 led.s 编译完成以后的文件名字为 led.o。执行上述命令以后就会编译生成一个 led.o 文件
3、使用arm-linux-gnueabihf-ld 链接文件
先确定可执行文件其运行起始地址,也就是链接地址
“存储地址”就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。“运行地址”就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错
比如烧写到 SD 卡中,上电以后 I.MX6U 的内部 boot rom 程序会将可执行文件拷贝到链接地址处,比如使用的链接地址都在 DDR中,链接起始地址为 0X87800000
3、将 led.o 文件链接到 0X87800000 这个地址
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
上述命令中-Ttext 就是指定链接地址,“-o”选项指定链接生成的 elf 文件名,这里我们命名为 led.elf。上述命令执行完以后就会在工程目录下多一个 led.elf 文件
4、使用arm-linux-gnueabihf-objcopy 格式转换将 led.elf 文件转换为.bin 文件
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
上述命令中,“-O”选项指定以什么格式输出,后面的“binary”表示以二进制格式输出,选项“-S”表示不要复制源文件中的重定位信息和符号信息,“-g”表示不复制源文件中的调试信息
5、使用arm-linux-gnueabihf-objdump 反汇编将 elf 文件反汇编
arm-linux-gnueabihf-objdump -D led.elf > led.dis
上述代码中的“-D”选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 led.dis 文件
总结:
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
arm-linux-gnueabihf-objdump -D led.elf > led.dis
6、创建 Makefile 文件
① 在工程根目录下创建一个名为“Makefile”的文件
② 输入以下内容:
led.bin:led.s
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
arm-linux-gnueabihf-objdump -D led.elf > led.dis
clean:
rm -rf *.o led.bin led.elf led.dis
③ 编译:输入make即可完成从.s文件到.bin文件
④ 清理工程:make clean
7、代码烧写
虽然I.MX6U 内部有 96K 的 ROM,但是这 96K 的 ROM 是 NXP自己用的,不向用户开放。相当于说 I.MX6U 是没有内部 flash 的,为此,I.MX6U 支持从外置的 NOR Flash、NAND Flash、SD/EMMC、SPI NOR Flash和 QSPI Flash 这些存储介质中启动,所以我们可以将代码烧写到这些存储介质中中
必须按照 NXP 的规定来将代码烧写到 SD 卡中,才能正确运行代码
可以使用正点原子专门编写的“imxdownload”工具将编译出来的.bin 文件烧写到 SD 卡中,下载链接:链接: https://pan.baidu.com/s/17322iKZU_z7rsBoyxecu_Q 提取码: 2333
① 将 imxdownload 拷贝到工程根目录下(和 led.bin 处于同一个文件夹下)
② 给予 imxdownload 可执行权限
chmod 777 imxdownload
③ 确定要烧写的 SD 卡,确保 SD 卡里面没有数据,因为过程可能会格式化 SD 卡
④ 输入以下指令查找SD卡:
ls /dev/sd*
显示结果中:/dev/sdd 和/dev/sdd1 是插入的 SD 卡,/dev/sdd1 是 SD 卡的第一个分区。如果你的 SD 卡有多个分区的话可能会出现/dev/sdd2、/dev/sdd3 等等
⑤ 向 SD 卡烧写 bin 文件
./imxdownload <.bin file> < SD Card>
比如烧写led.bin 到/dev/sdd
./imxdownload led.bin /dev/sdd
烧写速度在几百 KB/s 以下才表示正常烧写
如有错误,还望指正