I.MX6U 裸机开发3. GPIO操作控制LED灯
I.MX6U 裸机开发3. GPIO操作控制LED灯
- 一、创建项目目录及源文件
- 1. 新建目录
- 2. 远程开发环境
- 3. 创建源文件
- 二、代码编写
- 1. 打开时钟
- 2. 配置端口复用功能为GPIO
- 3. 配置端口电气属性
- 4. 设置GPIO方向(GDIR寄存器)
- 5. 输出
- 6. 死循环等待
- 三、编译程序
- 1. 整体过程
- 2. 编译,使用命令 arm-linux-gnueabihf-gcc
- 3. 链接,使用命令 arm-linux-gnueabihf-ld
- 3.3.1 起始地址
- 3.3.2 DDR初始化
- 3.3.3 链接命令:
- 4. 格式转换 arm-linux-gnueabihf-objcopy
- 5. 反汇编
- 四、烧写测试
- 1. 准备工具
- 2. 将SD卡通过读卡器连接到虚拟机
- 3. 烧写
- 五、执行
- 1. SD卡插到开发板
- 2. 引导方式拨码开关调到SD卡模式
- 六、创建 makefile
一、创建项目目录及源文件
1. 新建目录
~/linux/IMX6ULL/Board_Drivers/1_leds
2. 远程开发环境
为了方便远程开发,可在本地的vscode安装 Remote Development 插件:
设置方法:
点View-Command Palette,输入Remote,选择 Remote-SSH:Add New SSH Host…
按提示格式输入:
ssh root:123456@192.168.222.129
账号密码按自己真实情况填写,
点击
点击自己Ubuntu设备后的连接:
接下来的提示窗口按提示选择即可:
接下来点击 Open Folder,选择ubuntu上的开发目录 :
3. 创建源文件
leds.s
二、代码编写
1. 打开时钟
I.MX6U的时钟寄存器,首先到IMX6ULL参考手册找到 CCGR0~CCGR6地址,通过设置为0xFFFFFFFF把时钟全部使能。
初始化代码:
.global _start @全局标号_start:/* 使能CCGR0的全部时钟 */LDR R0, =0x020C4068 @ 把 CCGR0 的内存地址赋值给R0LDR R1, =0xFFFFFFFF @ R1 里赋值STR R1, [R0] @ 把 CCGR0 全部设置为高/* 使能CCGR1的全部时钟 */LDR R0, =0x020C406CSTR R1, [R0]/* 使能CCGR2的全部时钟 */LDR R0, =0x020C4070STR R1, [R0]/* 使能CCGR3的全部时钟 */LDR R0, =0x020C4074STR R1, [R0]/* 使能CCGR4的全部时钟 */LDR R0, =0x020C4078STR R1, [R0]/* 使能CCGR5的全部时钟 */LDR R0, =0x020C407CSTR R1, [R0]/* 使能CCGR6的全部时钟 */LDR R0, =0x020C4080STR R1, [R0]
2. 配置端口复用功能为GPIO
在手册找到 1571 页,
代码:
@ 配置 GPIO1_IO03的复用功能@ IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 5LDR R0, =0x020E0068LDR R1, =0x00000005STR R1, [R0]
3. 配置端口电气属性
在手册第1793页:
设置:
- bit0: SRE压摆率,设置为0低速率
- bit5~3:DSE驱动能力,设置为110 (R0/6)
- bit7~6:设置为10(100Mhz)
- bit11:设置为0,关闭开漏输出
- bit12:PKE设置为1,使能pull/keeper
- bit13:PUE设置为0,保持
- bit15~14:PUS设置为00, 等效100欧下拉
- bit16:HYS设置为0关闭
代码:
LDR R0, =0x020E02F4LDR R1, =0x10b0STR R1, [R0]
4. 设置GPIO方向(GDIR寄存器)
手册第 1357 页:
LDR R0, =0x0209C004LDR R1, =0x8STR R1, [R0]
5. 输出
/* 设置GPIO1_IO03输出低电平 */LDR R0, =0x0209C000LDR R1, =0x0STR R1, [R0]
6. 死循环等待
B .
三、编译程序
1. 整体过程
- 编译:将.c .s文件编译为 .o 目标文件
- 链接:将所有的.o 文件链接为 elf 格式可执行文件
- 将 elf 转成.bin 文件。
2. 编译,使用命令 arm-linux-gnueabihf-gcc
命令:
arm-linux-gnueabihf-gcc -g -c leds.s -o led.o
3. 链接,使用命令 arm-linux-gnueabihf-ld
3.3.1 起始地址
为了确定程序在内存中的加载位置,链接器通过链接脚本或命令行参数要指定起始地址。指定起始地址有以下作用:
- 内存布局控制:指定起始地址可以确保程序的各个部分(如代码段、数据段、堆栈等)在内存中有序排列,避免冲突。
- 硬件要求:某些嵌入式系统或硬件平台的特定要求。
- 调试和诊断:指定起始地址有助于调试和诊断,因为开发人员可以明确知道程序在内存中的位置。
使用 I.MX6ULL时,链接起始地址应指向RAM地址。而RAM分为内部RAM和外部RAM,根据文档可查询到,其RAM范围:
- 内部RAM的范围是 0x900000~0x91FFFF,
- 外部RAM的范围,需要视开发板而定。
我这里使用的正点原子I.MX6U-MINI开发板,512M字节的DDR范围是 :
0x80000000~0x9FFFFFFF
对于256M字节的DDR的范围是:
0x80000000~0x8FFFFFFF
本系列博文,链接地址使用了0x87800000,这是以后要学到的Uboot的链接地址就是0x87800000,这里沿用其设置。
3.3.2 DDR初始化
DDR内存模块,在上电后处于未定义状态,要使用需要先初始化。
这里我们要添加一个头部,在这个头部实现 从指定存储中读取头部、初始化DDR,并将bin拷贝到指定地方。
Bin的运行地址要和链接地址保持一致。
3.3.3 链接命令:
arm-linux-gnueabihf-ld -Ttext 0x87800000 led.o -o led.elf
4. 格式转换 arm-linux-gnueabihf-objcopy
本步骤有以下作用:
- 格式转换:将目标文件从一种格式转换为另一种格式。例如,从 ELF 格式转换为二进制格式。
- 去除符号表:可以去除目标文件中的符号表和调试信息,以减小文件大小。
- 提取部分内容:可以从目标文件中提取特定的部分,如只提取代码段或数据段。
- 合并文件:可以将多个目标文件合并为一个文件。
- 修改段属性:可以修改目标文件中段的属性,如段的起始地址、大小等。
命令:
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
5. 反汇编
这一步不是为了烧写,而是为了方便以后调试代码。
命令:
arm-linux-gnueabihf-objdump -D led.elf > led.dis
反汇编生成的文件示例:
四、烧写测试
I.MX6U 支持 SD卡、EMMC、NAND、NOR、SPI Flash,本例程使用烧写到SD卡。
烧写工具使用: imxdownload。
1. 准备工具
将 imxdownload 其拷到ubuntu的tools目录下:
使用 chmod +x imxdownload 给文件可执行权限。
imxdownload 工具会向led.bin添加一个头部。
2. 将SD卡通过读卡器连接到虚拟机
先在ubuntu上执行命令:
ls /dev/sd* -l
输出如下内容:
将U盘连接到Ubuntu虚拟机,再次执行:
ls /dev/sd* -l
输出如下:
其中 /dev/sdb有一个/dev/sdb1备用设备,这个/dev/sdb 就是目标SD卡。
3. 烧写
命令:
../tools/imxdownload led.bin /dev/sdb
其中的load.imx就是最终烧写到SD卡的文件。
五、执行
1. SD卡插到开发板
2. 引导方式拨码开关调到SD卡模式
默认是 EMMC启动方式,按丝印所示调整为SD卡模式:
通过 USB_TTL 上电,
执行效果,启动后等1-2秒,LED1亮。
六、创建 makefile
led.bin : leds.sarm-linux-gnueabihf-gcc -g -c leds.s -o led.oarm-linux-gnueabihf-ld -Ttext 0x87800000 led.o -o led_elfarm-linux-gnueabihf-objcopy -O binary -S -g led_elf led.binarm-linux-gnueabihf-objdump -D led_elf > led.dis
clean:rm -f *.bin *.o *.dis *.elf.PHONY: clean
以后可以下面命令来代码前面的编译、链接、格式转换等工作:
make clean
make
如果提示make没有安装,可以使用:
sudo apt install make -y
安装 make 。