1.VS code安装
Linux 版本安装
把资料盘里的安装包.deb拷贝到Ubuntu中, 使用如下命令安装:![]()
软件图标都在目录/usr/share/applications 中,如图路径
![]()
复制到桌面中
Visual Studio Code 插件的安装
我们需要按照的插件有下面几个:1) 、 C/C++ ,这个肯定是必须的。2) 、 C/C++ Snippets ,即 C/C++ 重用代码块。3) 、 C/C++ Advanced Lint, 即 C/C++ 静态检测 。4) 、 Code Runner ,即代码运行。5) 、 Include AutoComplete ,即自动头文件包含。6)、 Rainbow Brackets ,彩虹花括号,有助于阅读代码。7) 、 One Dark Pro , VSCode 的主题。8) 、 GBKtoUTF8 ,将 GBK 转换为 UTF8 。9)、 ARM ,即支持 ARM 汇编语法高亮显示。10) 、 Chinese(Simplified) ,即中文环境。11) 、 vscode-icons , VSCode 图标插件,主要是资源管理器下各个文件夹的图标。12) 、 compareit ,比较插件,可以用于比较两个文件的差异。13) 、 DeviceTree ,设备树语法插件。14) 、 TabNine ,一款 AI 自动补全插件,强烈推荐,谁用谁知道!![]()
![]()
Visual Studio Code 新建工程
打开 VSCode ,然后在 VSCode 上点击 文件 -> 打开文件夹 … ,选刚刚创建的“1_test”文件夹,打开以后如图
可以看出此时的文件夹“ 1_TEST ”是空的,点击 文件 -> 将工作区另存为 … ,打开工作区命名对话框,输入要保存的工作区路径和工作区名字,如图
的“新建文件”按钮创建 main.c 和 main.h 这两个文件,创建成功以后 VSCode 如图
此时“实验 1 TEST ”中有 .vscode 文件夹、 main.c 和 main.h ,这三个文件和文件夹同样会出现在“实验 1 test”文件夹中,如图main.h 中输入如下所示内容:
#include <stdio.h> int add(int a, int b);
main.c 中输入如下所示内容#include <main.h> int add(int a, int b) { return (a + b); } int main(void) { int value = 0; value = add(5, 6); printf("5 + 6 = %d", value); return 0; }
代码编辑完成以后 VSCode 界面如图
保存,打开终端打开窗口
2.I.MX6U-ALPHA/Mini 开发平台介绍
资源图
正点原子 I.MX6U-Mini 开发板底板板载资源如下:◆ 1 个核心板接口,支持 I.MX6ULL 核心板。◆ 1 个电源指示灯(蓝色)。◆ 1 个状态指示灯(红色)。◆ 1 路 CAN 接口,采用 TJA1050 芯片。◆ 1 路 485 接口,采用 SP3485 芯片。◆ 1 个 ATK 模块接口,支持正点原子蓝牙 /GPS/MPU6050/ 手势识别等模块。◆ 1 个摄像头模块接口。◆ 1 个 USB 串口,可用于代码调试。◆ 1 个 USB HOST 接口,用于 USB 主机通信。◆ 1 个有源蜂鸣器 。◆ 1 个 RS232/RS485 选择接口。◆ 1 个 TF 卡接口。◆ 1 个 10M/100M 以太网接口( RJ45 )◆ 1 组 5V 电源供应 / 接入口◆ 1 组 3.3V 电源供应 / 接入口◆ 1 个直流电源输入接口(输入电压范围: DC6~18V )◆ 1 个启动模式选择配置接口◆ 1 个 RTC 后备电池座,并带电池◆ 1 个复位按钮,可用于复位 MPU 和 LCD◆ 1 个 ON/OFF 按钮。◆ 1 个功能按钮◆ 1 个电源开关,控制整个板的电源◆ 1 个 SDIO WIFI 接口I.MX6U-Mini 硬件资源说明1. SDIO WIFI 接口这是开发板上的一个 SDIO WIFI(P2) 接口,可以通过此接口连接正点原子出品的 SDIO WIFI模块。 SDIO WIFI 接口和 TF 卡共用一个 USDHC 接口,因此不能同时和 TF 卡使用。2. USB 转串口USB 串口,为 MiniUSB 接口( USB_TTL ), USB 连接 CH340C 芯片, CH340C 与 I.MX6IULL 的串口 1 相连接,从而实现 USB 转串口。设计成 USB 串口,是出于现在电脑上串口正在消失, 尤其是笔记本,几乎清一色的没有串口,所以板载 USB 串口可以方便大家调试。3. I.MX6ULL 核心板接口这是开发板底板上面的核心板接口,由 2 个 2*30 的贴片板对板接线端子( 3710F 公座)组成,可以用来插正点原子的 I.MX6UL/ULL 核心板等,从而学习 I.MX6UL/6ULL 等芯片,达到一个开发板,学习多款 SOC 的目的,减少重复投资。4. RGBLCD 接口这是转接板自带的 RGB LCD 接口( LCD ),可以连接各种正点原子的 RGB LCD 屏模块,并且支持触摸屏(电阻 / 电容屏都可以)。采用的是 RGB888 格式,可显示 1677 万色,色彩显示 丰富。5. USB HOST 接口这是开发板板载的一个卧式 USB-A 座( USB_HOST ),我们可以通过这个 USB-A 座,连接U 盘 /USB 鼠标 /USB 键盘等其他 USB 从设备,从而实现 USB 主机功能。6. 后备电池接口这是 I.MX6ULL 后备区域的供电接口,可以用来给 I.MX6ULL 的后备区域提供能量,在外部电源断电的时候,维持 SNVS 区域数据的存储,以及 RTC 的运行。7. 启动 (BOOT) 拨码开关I.MX6U 支持多种启动方式,比如 SD 卡、 EMMC 、 NAND 、 QSPI FALSH 和 USB 等,要想从某一种设备启动就必须先设置好启动拨码开关。 I.MX6U-Mini 开发板用了一个 8P 的拨码开关来选择启动方式,支持从 SD 卡、 EMCM 、 NAND 和 USB 这四种启动方式,这四种启动方式 对应的拨码开关拨动方式已经写在了开发板上。大家在使用的时候根据自己的实际需求设置拨 码开关即可。8. 摄像头模块接口这是开发板板载的一个摄像头模块接口( P1 ),摄像头模块(需自备),对准插入到此插槽中。9. ATK 模块接口这是开发板板载的一个正点原子通用模块接口( P7 ),目前可以支持正点原子开发的 GPS模块、蓝牙模块、 MPU6050 模块、激光测距模块和手势识别模块等,直接插上对应的模块,就 可以进行开发。后续我们将开发更多兼容该接口的其他模块,实现更强大的扩展性能。10. 蜂鸣器这是一个有源蜂鸣器,通过高低电平控制蜂鸣器的开关。11. ON/OFF 按键这是一个 ON/OFF 按键,连接到 I.MX6ULL 的 OF/OFF 引脚上。12. 复位按键这是开发板板载的复位按键( RESET ),用于复位 I.MX6U ,还具有复位液晶的功能,因为液晶模块的复位引脚和 I.MX6U 的复位引脚是连接在一起的,当按下该键的时候, I.MX6U 和液晶一并被复位。13. 用户按键 KEY这是开发板板载的 1 个机械式输入按键( KEY0 ),可以做为普通按键输入使用。14. 蓝色电源指示 LED 灯这是开发板电源指示 LED 灯,为蓝色,当板子供电正常的时候此灯就会常亮。如果此灯不亮的话就说明开发板供电有问题 ( 排除 LED 灯本身损坏的情况 ) 。15. 红色用户 LED 灯这是开发板板载的 1 个 LED 灯,为红色,用户可以使用此 LED 灯。在调试代码的时候,使用 LED 来指示程序状态,这是非常不错的一个辅助调试方法。16. 引出的 IO 口这是开发板 IO 引出端口 P4 ,采用 2*24 排针,总共引出 41 个 IO 口。17. 3.3V 电源输入 / 输出这是开发板板载的一组 3.3V 电源输入输出排针( 2*3 )( JP1 ),用于给外部提供 3.3V 的电源,也可以用于从外部接 3.3V 的电源给板子供电。大家在实验的时候可能经常会为没有 3.3V 电源而苦恼不已,有了 I.MX6U-Mini 开发板,你就可以很方便的拥有一个简单的 3.3V 电源(最大电流不能超过 3000mA )。18. 5V 电源输入 / 输出这是开发板板载的一组 5V 电源输入输出排针( 2*3 )( JP2 ),该排针用于给外部提供 5V 的电源,也可以用于从外部接 5V 的电源给板子供电。同样大家在实验的时候可能经常会为没有 5V 电源而苦恼不已,正点原子充分考虑到了大家需求,有了这组 5V 排针,你就可以很方便的拥有一个简单的 5V 电源( USB 供电的时候,最大电流不能超过 500mA ,外部供电的时候,最大可达 3000mA )。19. 电源开关这是开发板板载的电源开关。该开关用于控制整个开发板的供电。这是一个自锁式开关,按下以后打开开发板电源,整个板子开始供电,电源指示灯 (PWR) 点亮。再次按下开关弹起,关闭开发板电源,整个开发板都将断电,电源指示灯( PWR )会随之熄灭。20. DC6~16V 电源输入这是开发板板载的一个外部电源输入口( DC_IN ),采用标准的直流电源插座。开发板板载了 DC-DC 芯片( JW5060T ),用于给开发板提供高效、稳定的 5V 电源。由于采用了 DC-DC 芯片,所以开发板的供电范围十分宽,大家可以很方便的找到合适的电源(只要输出范围在DC6~16V 的基本都可以)来给开发板供电。在耗电比较大的情况下,比如用到 4.3 屏 /7 寸屏 / 网口的时候,建议使用外部电源供电,可以提供足够的电流给开发板使用。21. 以太网接口 (RJ45)这是开发板板载的以太网接口,连接到 I.MX6U 的 ENET2 网络接口。22. CAN 接口这是开发板板载的 CAN 总线接口( CAN ),通过 2 个端口和外部 CAN 总线连接,即 CANH和 CANL 。这里提醒大家: CAN 通信的时候,必须 CANH 接 CANH , CANL 接 CANL ,否则可能通信不正常!23. RS485/ 串口 3 接口这是 RS485 与 I.MX6ULL 串口 3 的跳线帽设置接口,如果将 485_R 与 U3_T 、 485_T 与U3_R 连起来,那么 RS485 接口就会使用 I.MX6ULL 的串口 3 。做 RS485 实验的时候一定要将此接口连起来。24. RS485 接口这是开发板板载的 RS485 总线接口( RS485 ),通过 2 个端口和外部 485 设备连接。这里提醒大家, RS485 通信的时候,必须 A 接 A , B 接 B 。否则可能通信不正常25. JTAG 接口10P 的 JTAG 接口,可用于连接 JLINK 等调试器。26. USB OTG 接口这是开发板板载的一个 MiniUSB 头( USB_OTG ),用于 USB OTG 实验。27. TF 卡接口这是开发板板载的一个标准 TF 卡接口( TF_CARD ),该接口在开发板的背面,采用小型的TF 卡接口, USDHC 方式驱动,有了这个 TF 卡接口,就可以满足海量数据存储的需求。
3:Cortex-A7 MPCore 架构
Cortex-A7 MPCore
简介
Cortex-A7 MPcore 处理器支持 1~4 核,通常是和 Cortex-A15 组成 big.LITTLE 架构的,Cortex-A15 作为大核负责高性能运算,比如玩游戏啥的, Cortex-A7 负责普通应用,因为 CortexA7 省电。 Cortex-A7 本身性能也不弱,不要看它叫做 Cortex-A7 但是它可是比 Cortex-A8 性能 要强大,而且更省电。“在 28nm 工艺下, Cortex-A7 可以运行在 1.2~1.6GHz ,并且单核面积不大于 0.45mm 2 ( 含有浮点单元、 NEON 和 32KB 的 L1 缓存 ) ,在典型场景下功耗小于 100mW , 这使得它非常适 合对功耗要求严格的移动设备,这意味着 Cortex-A7 在获得与 Cortex-A9 相似性能的情况下, 其功耗更低”。
Cortex-A7 MPCore 的 L1 可选择 8KB 、 16KB 、 32KB 、 64KB , L2 Cache 可以不配,也可以选择 128KB 、 256KB 、 512KB 、 1024KB 。 I.MX6UL 配置了 32KB 的 L1 指令 Cache 和 32KB 的L1 数据 Cache ,以及 128KB 的 L2 Cache 。 Cortex-A7MPCore 使用 ARMv7-A 架构,主要特性如下:①、 SIMDv2 扩展整形和浮点向量操作。②、提供了与 ARM VFPv4 体系结构兼容的高性能的单双精度浮点指令,支持全功能的IEEE754 。③、支持大物理扩展 (LPAE) ,最高可以访问 40 位存储地址,也就是最高可以支持 1TB 的内存。④、支持硬件虚拟化。⑥、支持 Generic Interrupt Controller(GIC)V2.0 。⑦、支持 NEON ,可以加速多媒体和信号处理算法。
Cortex-A 寄存器组
ARM
架构提供了
16
个
32
位的通用寄存器
(R0~R15)
供软件使用,前
15
个
(R0~R14)
可以用
作通用的数据存储,
R15
是程序计数器
PC
,用来保存将要执行的指令。
ARM
还提供了一个当
前程序状态寄存器
CPSR
和一个备份程序状态寄存器
SPSR
,
SPSR
寄存器就是
CPSR
寄存器的
备份。这 18 个寄存器如图
![](https://i-blog.csdnimg.cn/blog_migrate/f13fabfd9a8239d21c2861e5090560c2.png)
通用寄存器
R0~R15 就是通用寄存器,通用寄存器可以分为以下三类:①、未备份寄存器,即 R0~R7 。②、备份寄存器,即 R8~R14 。③、程序计数器 PC ,即 R15 。分别来看一下这三类寄存器:1 、未备份寄存器未备份寄存器指的是 R0~R7 这 8 个寄存器,因为在所有的处理器模式下这 8 个寄存器都是同一个物理寄存器,在不同的模式下,这 8 个寄存器中的数据就会被破坏。所以这 8 个寄存器 并没有被用作特殊用途。2 、备份寄存器备份寄存器中的 R8~R12 这 5 个寄存器有两种物理寄存器,在快速中断模式下 (FIQ) 它们对应着 Rx_irq(x=8~12) 物理寄存器,其他模式下对应着 Rx(8~12) 物理寄存器。 FIQ 是快速中断模 式,看名字就是知道这个中断模式要求快速执行! FIQ 模式下中断处理程序可以使用 R8~R12 寄存器,因为 FIQ 模式下的 R8~R12 是独立的,因此中断处理程序可以不用执行保存和恢复中断现场的指令,从而加速中断的执行过程。
3
、程序计数器
R15
程序计数器
R15
也叫做
PC
,
R15
保存着当前执行的指令地址值加
8
个字节,这是因为
ARM
的流水线机制导致的。
ARM
处理器
3
级流水线:取指
->
译码
->
执行,这三级流水线循环执行,
比如当前正在执行第一条指令的同时也对第二条指令进行译码,第三条指令也同时被取出存放
在
R15(PC)
中。我们喜欢以当前正在执行的指令作为参考点,也就是以第一条指令为参考点,
那么
R15(PC)
中存放的就是第三条指令,换句话说就是
R15(PC)
总是指向当前正在执行的指令
地址再加上
2
条指令的地址。对于
32
位的
ARM
处理器,每条指令是
4
个字节,所以
:
R15 (PC)
值
=
当前执行的程序位置
+ 8
个字节。
程序状态寄存器
所有的处理器模式都共用一个
CPSR
物理寄存器,因此
CPSR
可以在任何模式下被访问。
CPSR
是当前程序状态寄存器,该寄存器包含了条件标志位、中断禁止位、当前处理器模式标志
等一些状态位以及一些控制位。所有的处理器模式都共用一个
CPSR
必然会导致冲突,为此,
除了
User
和
Sys
这两个模式以外,其他
7
个模式每个都配备了一个专用的物理状态寄存器,叫
做
SPSR(
备份程序状态寄存器
)
,当特定的异常中断发生时,
SPSR
寄存器用来保存当前程序状
态寄存器
(CPSR)
的值,当异常退出以后可以用
SPSR
中保存的值来恢复
CPSR
。
4: 汇编LED灯
I.MX6U IO 命名
可以看出, I.MX6ULL 的 IO 分为两类: SNVS 域的和通用的,这两类 IO 本质上都是一样的的形如“ IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 ”的就是 GPIO 命名,命名形式就是“ IOMUXC_SW_MUC_CTL_PAD_ XX_XX ”,后面的“ XX_XX ”就是 GPIO 命名,比如: GPIO1_IO01 、 UART1_TX_DATA 、 JTAG_MOD 等等。 I.MX6ULL 的 GPIO 并不像 STM32 一样以 PA0~15 这样命名,他是根据某个 IO 所拥有的功能来命名的。比如我们一看到 GPIO1_IO01 就知道这个肯定能做 GPIO ,看到 UART1_TX_DATA 肯定就知道这个 IO 肯定能做 为 UART1 的发送引脚。
I.MX6U IO
复用
以“ IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 ”这个 IO 为例
可以看到有个名为: 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 。 GPIO1_IO00 还可以作 为 I2C2_SCL 、 GPT1_CAPTURE1 、 ANATOP_OTG1_ID 等。这个就是 I.MX6U 的 IO 复用,我 们学习 STM32 的时候 STM32 的 GPIO 也是可以复用的。
由此可见,
I.MX6U
的
GPIO
不止
GPIO1_IO00~GPIO1_IO09
这
10
个,其它的
IO
都可以复
用为
GPIO
来使用。
I.MX6U
的
GPIO
一共有
5
组:
GPIO1
、
GPIO2
、
GPIO3
、
GPIO4
和
GPIO5
,
其中
GPIO1
有
32
个
IO
,
GPIO2
有
22
个
IO
,
GPIO3
有
29
个
IO
、
GPIO4
有
29
个
IO
,
GPIO5
最少,只有
12
个
IO
,这样一共有
124
个
GPIO
。
MX6U GPIO
配置
IOMUXC_SW_MUX_CTL_PAD_XX_XX 和 IOMUXC_SW_PAD_CTL_PAD_XX_XX 这两种寄存器都是配置 IO 的,注意是 IO !不是 GPIO , GPIO 是一个 IO 众多复用功能中的一种。比 如 GPIO1_IO00 这个 IO 可以复用为: I2C2_SCL 、 GPT1_CAPTURE1 、 ANATOP_OTG1_ID 、 ENET1_REF_CLK 、 MQS_RIGHT 、 GPIO1_IO00 、 ENET1_1588_EVENT0_IN 、 SRC_SYSTEM_RESET 和 WDOG3_WDOG_B 这 9 个功能, GPIO1_IO00 是其中的一种,我们想要把 GPIO1_IO00 用作哪个外设就复用为哪个外设功能即可。如果我们要用 GPIO1_IO00 来 点个灯、作为按键输入啥的就是使用其 GPIO( 通用输入输出 ) 的功能。将其复用为 GPIO 以后还 需要对其 GPIO 的功能进行配置,
硬件原理分析
可以看出,
LED0
接到了
GPIO_3
上,
GPIO_3
就是
GPIO1_IO03
,当
GPIO1_IO03
输出低电平
(0)
的时候发光二极管
LED0
就会导通点亮,当
GPIO1_IO03
输出高电平
(1)
的时候发
光二极管
LED0
不会导通,因此
LED0
也就不会点亮。所以
LED0
的亮灭取决于
GPIO1_IO03
的输出电平,输出
0
就亮,输出
1
就灭。
实验程序编写
1
、使能
GPIO1
时钟
GPIO1
的时钟由
CCM_CCGR1
的
bit27
和
bit26
这两个位控制,将这两个位都设置位
11
即
可。本教程所有例程已经将
I.MX6U
的所有外设时钟都已经打开了,因此这一步可以不用做。
2
、设置
GPIO1_IO03
的复用功能
找到
GPIO1_IO03
的复用寄存器“
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
”的地址为
0X020E0068
,然后设置此寄存器,将
GPIO1_IO03
这个
IO
复用为
GPIO
功能,也就是
ALT5
。
3
、配置
GPIO1_IO03
找到
GPIO1_IO03
的配置寄存器“
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03
”的地址为
0X020E02F4
,根据实际使用情况,配置此寄存
4
、设置
GPIO
我们已经将
GPIO1_IO03
复用为了
GPIO
功能,所以我们需要配置
GPIO
。找到
GPIO3
对
应的 GPIO 组寄存器地址
![](https://i-blog.csdnimg.cn/blog_migrate/595548e5398f02f248daedb28537a879.png)
5
、控制
GPIO
的输出电平
经过前面几步,
GPIO1_IO03
已经配置好了,只需要向
GPIO1_DR
寄存器的
bit3
写入
0
即
可控制
GPIO1_IO03
输出低电平,打开
LED
,向
bit3
写入
1
可控制
GPIO1_IO03
输出高电平,
关闭
LED
。
所有的裸机实验我们都在
Ubuntu
下完成,使用
VSCode
编辑器!
新建一个名为“
1_leds
”的文件夹,然后在“
1_leds
”
这个目录下新建一个名为“
led.s
”的汇编文件和一个名为“
.vscode
”的目录,创建好以后“
1_leds
”
文件夹如图
![](https://i-blog.csdnimg.cn/blog_migrate/f82737fb076a12bdc35ba5967d1d3010.png)
vscode
文件夹里面存放
VSCode
的工程文件,
led.s
就是我们新建的汇编文件,
我们稍后会在
led.s
这个文件中编写汇编程序。使用
VSCode
打开
1_leds
这个文件夹,打开以后
如图
在
led.s
中输入如下代码:
![](https://i-blog.csdnimg.cn/blog_migrate/78ce952e58ba3a7483f00d30e73afdce.png)
.global _start /* 全局标号 */
/*
* 描述: _start 函数,程序从此函数开始执行此函数完成时钟使能、
* GPIO 初始化、最终控制 GPIO 输出低电平来点亮 LED 灯。
*/
_start:
/* 例程代码 */
/* 1、使能所有时钟 */
ldr r0, =0X020C4068 /* 寄存器 CCGR0 */
ldr r1, =0XFFFFFFFF
str r1, [r0]
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 关闭xx*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 行定义了一个全局标号 _start ,代码就是从 _start 这个标号开始顺序往下执行的。第 11 行使用 ldr 指令向寄存器 r0 写入 0X020C4068 ,也就是 r0=0X020C4068 ,这个是CCM_CCGR0 寄存器的地址。第 12 行使用 ldr 指令向寄存器 r1 写入 0XFFFFFFFF ,也就是 r1=0XFFFFFFFF 。因为我们要开启所有的外设时钟,因此 CCM_CCGR0~CCM_CCGR6 所有寄存器的 32 位都要置 1 ,也就是写入 0XFFFFFFFF 。第 13 行使用 str 将 r1 中的值写入到 r0 所保存的地址中去,也就是给 0X020C4068 这个地址写入 0XFFFFFFFF ,相当于 CCM_CCGR0=0XFFFFFFFF ,就是打开 CCM_CCGR0 寄存器所控制的所有外设时钟。第 15~31 行都是向 CCM_CCGRX(X=1~6) 寄存器写入 0XFFFFFFFF 。这样我就通过汇编代码使能了 I.MX6U 的所有外设时钟。第 35~37 行是设置 GPIO1_IO03 的复用功能, GPIO1_IO03 的复用寄存器地址为 0X020E0068,寄 存 器 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的 MUX_MODE 设置为 5 就 是 将GPIO1_IO03 设置为 GPIO 。第 49~51 行 是 设 置 GPIO1_IO03 的 配 置 寄 存 器 , 也 就 是 寄 存 器IOMUX_SW_PAD_CTL_PAD_GPIO1_IO03 的值,此寄存器地址为 0X020E02F4 ,代码里面已经给出了这个寄存器详细的位设置。第 54~63 行是设置 GPIO 功能,经过上面几步操作, GPIO1_IO03 这个 IO 已经被配置为了GPIO 功能,所以还需要设置跟 GPIO 有关的寄存器。第 54~56 行是设置 GPIO1->GDIR 寄存器,将 GPIO1_IO03 设置为输出模式,也就是寄存器的 GPIO1_GDIR 的 bit3 置 1 。第 61~63 行设置 GPIO1->DR 寄存器,也就是设置 GPIO1_IO03 的输出,我们要点亮开发板上的 LED0 ,那么 GPIO1_IO03 就必须输出低电平,所以这里设置 GPIO1_DR 寄存器为 0 。第 68~69 行是死循环,通过 b 指令, CPU 重复不断的跳到 loop 函数执行,进入一个死循环。
编译代码
1将.c.s文件变为,o
2将所有的.o文件连接为elf格式的可执行文件
3将elf文件转为bin文件
4将elf文件转为汇编,反汇编
链接:
连接就是将所有.o文件链接在一起,并且链接到指定的地方。本实验链接的时候要指定链接起始地址。链接起始地址就是代码运行的起始地址,或者说保存代码的起始地址。
1、arm-linux-gnueabihf-gcc 编译文件
妈的前面注意千万不要敲错了。不然一堆牛马错误
我们是要编译出在 ARM 开发板上运行的可执行文件,所以要使用我们在前面 安装的交叉编译器 arm-linux-gnueabihf-gcc 来编译。因为本试验就一个 led.s 源文件,所以编译比较简单。先将 led.s 编译为对应的.o 文件,在终端中输入如下命令:
上述命令就是将 led.s 编译为 led.o ,其中“ -g ”选项是产生调试信息, GDB 能够使用这些调试信息进行代码调试。“ -c ”选项是编译源文件,但是不链接。“ -o ”选项是指定编译产生的文件名字,这里我们指定 led.s 编译完成以后的文件名字为 led.o。执行上述命令以后就会编译生 成一个 led.o 文件
但是led.o 文件并不是我们可以下载到开发板中运行的文件,一个工程中所有的 C文件和汇编文件都会编译生成一个对应的 .o 文件,我们需要将这 .o 文件链接起来组合成可执行文件
2、arm-linux-gnueabihf-ld 链接文件
arm-linux-gnueabihf-ld 用来将众多的 .o 文件链接到一个指定的链接位置。我们在学习SMT32 的时候基本就没有听过“链接”这个词,我们一般用 MDK 编写好代码,然后点击“编译”, MDK 或者 IAR 就会自动帮我们编译好整个工程,最后再点击“下载”就可以将代码下载到开发板中。这是因为链接这个操作 MDK 或者 IAR 已经帮你做好了,后面我就以 MDK 为例 给大家讲解。大家可以打开一个 STM32 的工程,然后编译一下,肯定能找到很多.o 文件,如图![]()
左侧的 IROM1 我们都知道是设置 STM32 芯片的 ROM 起始地址和大小的,右边的 IRAM1 是设置 STM32 芯片的 RAM 起始地址和大小的。其中 0X08000000 就是 STM32 内 部 ROM 的起始地址,编译出来的指令肯定是要从 0X08000000 这个地址开始存放的。对于 STM32 来说 0X08000000 就是它的链接地址,这些.o 文件就是 这个链接地址开始 依次存放,最终生成一个可以下载的 hex 或者 bin 文件,我们可以打开.map 文件查看一下这些文件的链接地址,在 MDK 下打开一个工程的.map 文件方法
接地址。这里我们要区分“存储地址”和“运行地址”这两个概念,“存储地址”就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。“运行地址”就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。确定了链接地址以后就可以使用 arm-linux-gnueabihf-ld 来将前面编译出来的 led.o 文件链接到 0X87800000 这个地址,使用如下命令:
上述命令中 -Ttext 就是指定链接地址,“ -o ”选项指定链接生成的 elf 文件名,这里我们命名为 led.elf 。上述命令执行完以后就会在工程目录下多一个 led.elf 文件,如图
led.elf 文件也不是我们最终烧写到 SD 卡中的可执行文件,我们要烧写的 .bin 文件,因此还需要将 led.elf 文件转换为 .bin 文件,这里我们就需要用到 arm-linux-gnueabihf-objcopy 这个工具
3、arm-linux-gnueabihf-objcopy 格式转换
arm-linux-gnueabihf-objcopy 更像一个格式转换工具,我们需要用它将 led.elf 文件转换为led.bin 文件,命令如下:
上述命令中,“ -O ”选项指定以什么格式输出,后面的“ binary ”表示以二进制格式输出,选项“ -S ”表示不要复制源文件中的重定位信息和符号信息,“ -g ”表示不复制源文件中的调试信息。上述命令执行完成以后,工程目录如图![]()
4、arm-linux-gnueabihf-objdump 反汇编
大多数情况下我们都是用 C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将 elf 文件反汇编,比如如下命令:
上述代码中的“ -D ”选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 led.dis 文件,如图![]()
总结一下我们为了编译 ARM 开发板上运行的 led.o 这个文件使用了如下命令:
如果我们修改了
led.s
文件,那么就需要在重复一次上面的这些命令,太麻烦了,这个时候
我们就可以使用Makefile 文件了。
5.创建 Makefile 文件
使用“ touch ”命令在工程根目录下创建一个名为“Makefile”的文件![]()
![]()
创建好 Makefile 以后我们就只需要执行一次“make”命令即可完成编译,过程如图
如果我们要清理工程的话执行“make clean”即可
后面。/imxdownload led.bin /dev/sdf
5.代码烧写
我们在调试裸机和
Uboot
的时候是将代码下载到
SD
中,因为方便嘛,当调试完成
以后量产的时候要将裸机或者
Uboot
烧写到
SPI NOR Flash
、
EMMC
、
NAND
等这些存储介质
中的。那么,如何将我们前面编译出来的
led.bin
烧写到
SD
卡中呢?肯定有人会认为直接复制
led.bin
到
SD
卡中不就行了,错!编译出来的可执行文件是怎么存放到
SD
中的,存放的位置是
什么?这个
NXP
是有详细规定的!我们必须按照
NXP
的规定来将代码烧写到
SD
卡中,否则
代码是绝对运行不起来的。
“ imxdownload”路径为: 开发板光盘 ->5 、开发工具 ->2、Ubuntu 下裸机烧写软件 ->imxdownload , imxdownlaod 只能在 Ubuntu 下使用,使用步骤如下:1 、将 imxdownload 拷贝到工程根目录下我们要将 imxdownload 拷贝到工程根目录下,也就是和 led.bin处于同一个文件夹下,要不然烧写会失败的,拷贝完成以后如图2 、给予 imxdownload 可执行权限
我们直接将软件 imxdownload 从 Windows 下复制到 Ubuntu 中以后, imxdownload默认是没有可执行权限的。我们需要给予 imxdownload 可执行权限,使用命令“chmod”,命令如下:当给予 imxdownload可执行权限以后其名字变成了绿色的,如果没有可执行权限的话其名字颜色是白色的。所以在 Ubuntu中我们可以初步的从文件名字的颜色判断其是否具有可执行权限。
3 、确定要烧写的 SD 卡。准备一张新的 SD(TF) 卡,确保 SD卡里面没有数据,因为我们在烧写代码的时候可能会格式化 SD 卡!!!Ubuntu 下所有的设备文件都在目录“ /dev ”里面,所以插上 SD 卡以后也会出现在“ /dev”里面,其中存储设备都是以“ /dev/sd ”开头的。我们要先看一下不插 SD卡的时候电脑都有哪些存储设备,以防插入SD卡以后分不清谁是谁。输入如下所示命令:![]()
![]()
![]()
4 、向 SD 卡烧写 bin 文件使用 imxdownload 向 SD 卡烧写led.bin文件,命令格式如下:
其中 .bin 就是要烧写的 .bin 文件, SD Card 就是你要烧写的 SD 卡,比如我的电脑使用如下命令烧写 led.bin 到/dev/sdd中:![]()
烧写的最后一行会显示烧写大小、用时和速度,比如 led.bin 烧写到 SD 卡中的大小是 3.2KB ,用时 0.0160821s ,烧写速度是 201KB/s。注意这个烧写速度,如果这个烧写速度在几百KB/s以下那么就是正常烧写。![]()
![]()