ARM裸机开发-点亮LED灯

目录

背景介绍

底板原理图

[一.引脚复用功能+引脚电气属性(IOMUXC 32)](#一.引脚复用功能+引脚电气属性(IOMUXC 32))

1.IOMUXC_SW_MUX_CTL_PAD:引脚复用功能

2.IOMUXC_SW_PAD_CTL_PAD:电气属性

3.代码

[二.GPIO模块(GPIO 28)](#二.GPIO模块(GPIO 28))

1.DR

2.GDIR

3.PSR

4.ICR与EDGE

5.IMR

6.ISR

7.总结

8.代码

[三.时钟模块(CCM 18)](#三.时钟模块(CCM 18))

1.简介

2.代码

四.全局代码

五.环境搭建

1.安装FileZilla与SSH服务器

2.Ubuntu

3.编译

4.烧录


背景介绍

本文开始在VScode上使用IMX6ULL以真机进行学习,注意,创建的.S文件在本篇学习时注意大写,在编译时会进行部分预处理操作,小写则.s文件会跳过预处理阶段。

芯片原理图参考如下。

其中:

  • SOC IMX6ULL NXP(核心处理器) :这是整块板子的"大脑"。NXP(恩智浦)的 IMX6ULL 基于 ARM Cortex-A7 架构,主频通常在 528MHz 或 800MHz。

  • DDR3 512MB(运行内存 / RAM) :相当于电脑的内存条 。它是掉电丢失的。当 Linux 系统启动时,内核(kernel)会被加载到这块 DDR3 里运行,它负责临时存放程序运行时产生的大量数据。

  • EMMC nand flash 8GB(存储 / ROM) :相当于电脑的硬盘。它是掉电不丢失的。用来永久存放系统镜像(uboot、kernel、设备树、rootfs文件系统)和用户程序。

  • 蜂鸣器贴纸有"+"号,表示有源蜂鸣器,上电即可工作

  • 左下角BOOT拨码开关设置启动模式,将1,7位拉高则开机。

底板原理图

其中两列引脚通过板子的两列白色插座接入核心板,故核心板自然会将插座的引脚接入SOC内部

该芯片GPIO分为5组:GPIO1、GPIO2、GPIO3、GPIO4、GPIO5.若为图中LED0所在的GPIO 3则表示默认为GPIO1组(注意中间有空格)

其中,DCDC表示直流供电,LED1是物理设备编号,LED0则是引脚工作名称,由原理图可知被接入到GPIO1组的03号引脚,显然右边接入高电平,控制LED0输出低电平则灯亮,故本文后续点灯操作将围绕此展开。

一.引脚复用功能+引脚电气属性(IOMUXC 32)

点灯第一步,先配置引脚复用功能以及电气属性

**1.IOMUXC_SW_MUX_CTL_PAD:**引脚复用功能

SION为控制输入输出功能,而当采用GPIO功能时,输入输出默认打开,而当采用其他七种模式时默认关闭;MUX_MODE为引脚复用的功能,易知,共有八种模式,而该引脚默认为0101,即GPIO1_IO03功能

2.IOMUXC_SW_PAD_CTL_PAD:电气属性

HYS:仅输入功能使用,用于过滤波形,整形。(点灯为输出功能,故暂不使用)

PUS:仅输入可用,控制上拉下拉电阻。

PUE:仅输入可用,控制引脚使用上拉还是保持电压

PKE:打开PUE的开关,为1表示PUE上拉保持功能开

ODE:控制输出模式,默认为0,即推挽输出。

漏级开漏输出,引脚是高阻态,可以输出低电平,但无法输出高电平,除非外部接入上拉电阻;

推挽输出:驱动能力强,既可输出高电平,也可输出低电平

SPEED:控制输出速度,合适即可,高速EMI电磁干扰会增加功耗

DSE:驱动能力,表示驱动多强的外部设备,电阻越小,电流越大,驱动变强,但同时功耗变大,芯片发烫。(给高电平有效,对点灯无用,默认即可)

SRE:从低电平变为高电平需要时间,压摆率越高曲线越陡(适用高速设备,USB,CAN,网口)

对于点灯而言,电气属性设置为默认值即可,由图易知,默认值0X10B0,配置之后该引脚则为GPIO功能。

3.代码

cpp 复制代码
_led_init:
    // 配置引脚复用功能:GPIO1_IO03
    ldr r0, =0x020E0068
    mov r1, #0x05
    str r1, [r0]

    // 配置引脚电气属性:默认属性(0x10B0)
    ldr r0, =0x020E02F4
    ldr r1, =0x10B0
    str r1, [r0]

二.GPIO模块(GPIO 28)

1.DR

GPIOx_DR:控制第几组的GPIO引脚

DR:控制引脚高低电平,例如,本篇要点亮的LED灯为GPIO1组的IO03引脚,则让bit3置0,灯亮

2.GDIR

GDIR(GPIO direction register):GPIO方向寄存器,即控制输入/输出模式,对应LED,GPIO_03显然该寄存器应置1

3.PSR

PSR:只读,读取引脚电平,(1为高,0为低电平),同时DR也可读

4.ICR与EDGE

ICR1:配置引脚中断方式的寄存器,从0-31,一共32位,每两位控制一个引脚的中断触发方式,寄存器编号即引脚编号,显然要配置GPIO03号引脚则应配置6.7位.对应具体设置位依次为低电平触发,高电平触发,上升沿触发,下降沿触发。

ICR1为0-15位引脚,ICP2为16-31位引脚。

ICR2原理同上,此外,EDGE寄存器则可控制为双边沿触发

5.IMR

IMR:控制中断是否屏蔽,显然要将LED灯对应的bit3为置1,表示开中断

6.ISR

ISR:查询引脚是否产生中断,1表示产生中断

7.总结

GPIOx_DR:向GPIOx组某个引脚写0或1(低电平/高电平)
GPIOx_GDIR:设置GPIOx组某个引脚方向(输入0和输出1)
GPIOx_PSR(只读):读取GPIOx组某个引脚的状态
GPIOxICR:设置GPIOx组某个引脚中断触发方式(低电平、高电平、下降沿、上升沿)
GPIOx_IMR:设置GPIOx组某个引脚中断屏蔽/允许
GPIOx_ISR:用来查询GPIOx组某个引脚是否产生中断
GPIOx_EDGE_SEL:设置中断触发方式为双边沿触发

以上则为GPIO模块的相关寄存器解析,对于点灯功能而言无需中断,此处了解即可,后续开发将会详细介绍。点灯仅需设置GPIOx_DR与GPIOx_GDIR寄存器,将引脚置低电平且设置为输出方向。

8.代码

cpp 复制代码
_led_init:
    // 配置引脚方向(GDIR):输出模式
    ldr r0, =0x0209C004
    ldr r1, [r0]
    orr r1, r1, #(1 << 3)
    str r1, [r0]
    bx lr
    
_led_on:
    //给DR bit3位低电平
    ldr r0, =0x0209C000
    ldr r1, [r0]
    bic r1, r1, #(1 << 3)
    str r1, [r0]
    bx lr

三.时钟模块(CCM 18)

1.简介

外设工作需要时钟,不同外设工作频率不同,需要的时钟频率则也不同,故点灯则也需设置对应时钟频率。

右侧为外设,cg(clock gate)为控制外设开关,ARM处理器中,为了保证低功耗,每个外设门都是关闭的,要实现点灯,则首先打开对应的cg开关。

CCGRx即为控制外设cg的寄存器,每两位控制一个外设,置11即为开启。为方便后续开发,将CCM_CCGR0-CCM_CCGR6都置为全1,即0xFFFF FFFF,表示打开所有外设时钟门。

2.代码

cpp 复制代码
_enable_clocks:
    // CCM_CCGR0 clock enable
    ldr r0, =0x020C4068
    ldr r1, =0xFFFFFFFF
    str r1, [r0]

    // CCM_CCGR1 clock enable
    ldr r0, =0x020C406C
    str r1, [r0]

    // CCM_CCGR2 clock enable
    ldr r0, =0x020C4070
    str r1, [r0]

    // CCM_CCGR3 clock enable
    ldr r0, =0x020C4074
    str r1, [r0]   

    // CCM_CCGR4 clock enable
    ldr r0, =0x020C4078
    str r1, [r0]

    // CCM_CCGR5 clock enable
    ldr r0, =0x020C407C
    str r1, [r0]

    // CCM_CCGR6 clock enable
    ldr r0, =0x020C4080
    str r1, [r0]
    bx lr

四.全局代码

系统初次复位时在SVC模式,复位异常(位于0x00地址),此时若执行一条跳转指令到复位异常函数处,则可完成一些初始化工作。此外SVC模式仅在刚复位时使用,此后大多时候在system进行工作,故再不做过多阐述。(可参考上篇启动代码的学习)

异常复位函数

cpp 复制代码
    .global _start

    _start:
    ldr pc, =_reset_handler
	ldr pc, =_undefine_handler
	ldr pc, =_software_handler
	ldr pc, =_prefetch_abort_handler
	ldr pc, =_data_abort_handler
	ldr pc, =_reserved_handler
	ldr pc, =_irq_handler
	ldr pc, =_fiq_handler

_fiq_handler:
	ldr pc, =_fiq_handler

_irq_handler:
	ldr pc, =_irq_handler

_reserved_handler:
	ldr pc, =_reserved_handler

_data_abort_handler:
	ldr pc, =_data_abort_handler

_prefetch_abort_handler:
	ldr pc, =_prefetch_abort_handler
  
_software_handler:
    ldr pc, =_software_handler

_undefine_handler:
	ldr pc, =_undefine_handler

finished:
    b finished

复位函数:

其中 _enable_clocks,_led_init, _led_on函数均以在上述中实现。

cpp 复制代码
_reset_handler:
    // 设置irq模式
    mrs r0, cpsr
    bic r0, r0, #0x1F
    orr r0, r0, #0x12  
    msr cpsr, r0
    // 设置irq模式下的栈(32M)
    ldr sp, =0x86000000

    // 设置system模式,相比user模式多了特权模式
    mrs r0, cpsr
    bic r0, r0, #0x1F
    orr r0, r0, #0x1F
    msr cpsr, r0
    // 设置system模式下的栈
    ldr sp, =0x84000000   
    // 打开所有外设时钟门
    bl _enable_clocks
    // 初始化led
    bl _led_init
    // led亮
    bl _led_on
    b finished

上篇由于是仿真学习,使用的是芯片内部的RAM,而本芯片SOC内部的RAM不允使用,故使用DDR3的内存,而该芯片规定该内存起始地址为0X8000 0000,大小512MB,即到0XA000 0000,在这中间可自行根据七种异常栈分配内存。

五.环境搭建

1.安装FileZilla与SSH服务器

由于在Ubuntu中运行VScode比较卡顿,故选择在Windows上的VScode进行代码编写,再将代码工程传输到Ubuntu中进行编译下载。首先为了方便从Windows和Ubuntu之间的文件传输,我们需要在Windows下安装FileZilla,该软件下载地址如下:https://www.filezilla.cn/download。 FileZilla作为客户端,Ubuntu作为服务端。

该软件底层通过采用应用层的FTP协议实现文件传输。

操作过程如下:


此外还需要在Ubuntu下安装SSH服务器: sudo apt-get install vsftpd ,并配置文件: sudo vi
/etc/vsftpd.conf 。使能本地功能和写入功能:

最后,执行 sudo /etc/init.d/vsftpd restart刷新,使配置文件生效

2.Ubuntu

1.设置NAT模式(分配了虚拟IP,只要Windows能上网,Ubuntu就能上网)

cpp 复制代码
sudo /etc/init.d/networking restart
//执行此代码重启网络
  • 桥接模式 :虚拟机就像你家里的另一台独立电脑。它和你的 Windows 宿主机平起平坐,共用同一个真实路由器(你之前连的手机热点)。

  • NAT模式 :虚拟机就像一个住在你 Windows 宿主机里面的"租客"。它通过宿主机代理上网,外部网络(手机热点)根本看不见它。

对比维度 桥接模式(VMnet0) NAT模式(VMnet8)
IP地址来源 外部路由器 分配(你之前是手机热点给的 10.138.48.x VMware虚拟路由器 分配(现在是 192.168.85.x
网络地位 和宿主机(Windows)是平等的同学 是宿主机的下属
宿主机通信 直接通过局域网通信(但受限于外部路由器) 通过宿主机虚拟网卡 192.168.85.1 通信
外网通信 直接暴露在局域网中,占用一个外部IP 经过宿主机 NAT(网络地址转换)转发,共用宿主机IP
外部设备访问虚拟机 能(如果局域网没做隔离,比如你之前手机热点的问题) 不能直接访问(除非在VMware里设置端口转发
虚拟机访问宿主机 能(但需要依赖外部路由器的配置) 能(直接 ping 192.168.85.1 即可)

2.安装ARM编译器

由于Ubuntu位X86架构,gcc为对应的编译指令,而要在ARM芯片上运行,则需要安装对应ARM架构的编译器 。

将该编译器拷贝至Ubuntu的家目录下,再切换至根目录的/usr/local目录下,创建arm文件夹,再将家目录的编译器安装包复制于当前arm文件夹,sudo mv ~/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz .再解压压缩包 sudo tar -xvf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz。解压后会产生对应文件夹,进入其中的bin目录,其中为arm编译的工具链,pwd打印出当前目录路径,复制保存,再sudo vim ~/.bashrc进入配置文件,配置刚才复制的路径如下,最后sudo reboot重启虚拟机即可,最后arm-linux-gnueabihf-gcc -v查看安装编译器版本

3.编译

家目录下创建imx6ull/led_asm(对应的芯片文件夹保存编译文件),通过FileZilla上传VScode上写的start.S文件至此,编译三步骤:

  • 编译:arm-linux-gnueabihf-gcc -g -c start.S -o start.o,
    • 其中"-g"选项是产生调试信息,GDB 能够使用这些调试信息进行代码调试。"-c"选项是编译源 文件,但是不链接。"-o"选项是指定编译产生的文件名字,这里我们指定 start.s 编译完成以后的文件名字为 start.o。执行上述命令以后就会编译生成一个start.o 文件
  • 链接:arm-linux-gnueabihf-ld -Ttext 0X87800000 start.o -o start.elf
    • 本次课程中,我们所有的裸机例程都是烧写到 SD 卡中,上电以后 I.MX6U 的内部 boot rom 程 序会将可执 行文件拷贝 到链接地址 处,这个链 接地址可以 在 I.MX6U 的内部 128KBRAM 中 (0X900000~0X91FFFF),也可以在外部的 DDR 中。本次课程中所有裸机例程的链接地址都在 DDR中,链 接起始地址为 0X87800000。I.MX6U-ALPHA 开发板的 DDR 容量有两种:512MB 和256MB,起始地址都为 0X80000000,只不过 512MB 的终止地址为0X9FFFFFFF,而 256MB 容量的终止地址为 0X8FFFFFFF。之所以选择 0X87800000 这个地址是因为后面要讲的 Uboot 其链接地址就是 0X87800000, 这样我们统一使用 0X87800000 这个链接地址,不容易记混。
  • 格式转换:arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin
    • 我们需要用它将 start.elf 文件转换为start.bin 文件 。上述命令中,"-O"选项指定以什么格式输出,后面的"binary"表示以二进制格式输出,选项"-S"表示不要复制源文件中的重定位信息和符号信息,"-g"表示不复制源文件中的调试信息。上述命令执行完成以后,就会在工程目录下多一个 start.bin 文件。至此我们终于等到了想要的东西---start.bin 文件
  • 反汇编:arm-linux-gnueabihf-objdump -D led.elf > led.dis(可不用)
    • 大多数情况下我们都是用 C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,当然这个工作目前对于我们不是必须的。一般来说可以将 elf 文件反汇编。上述代码中的"-D"选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 start.dis 文件。我们可以打开这个文件,查看其中的内容可以发 现这里面都是汇编代码。注意通过 start.dis 这个反汇编文件可以明显的看出我们的代码已经链接到了以 0X87800000 为起始地址的区域。

4.烧录

装入SD卡,插入读卡器,连接到虚拟机,

ls /dev/sd*查看已有设备,
例如我这里看到的结果是/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 四个文件,分别代 表Ubuntu下的磁盘和三个分区

然后将imxdownload文件上传至led_asm,执行chmod 0777 imxdownload给予权限

使用命令 ./imxdownload start.bin /dev/sdb烧录,注意不要写为 . /imxdownload led.bin /dev/sda, sda是系统磁盘,会造成Ubuntu损坏.

当出现此界面时下载成功,注意下载速度为KB/S,若为MB/S单位则下载失败,拔卡重启虚拟机再插卡, 注意保证读卡器没有损坏,部分廉价读卡器在插入时易因为静电烧坏。

若是插入新读卡器则可能会出现自动挂载情况,输入lsblk查看,例如,sdb挂载到boot与rootfs文件。

执行下面指令卸载挂载,因为 Linux 系统为了安全, 不允许对已经挂载的分区进行 dd 裸写入 。如果不卸载,你执行烧录会直接报 Device or resource busy 错误。

cpp 复制代码
sudo umount /dev/sdb1
sudo umount /dev/sdb2
//或者一句
sudo umount /dev/sdb*

最后再进行烧录,看到进度条跑完、提示烧录成功后。然后安全弹出读卡器,把 SD 卡插回 i.MX6ULL 开发板,拨到 SD 卡启动模式,上电!

cpp 复制代码
sudo ./imxdownload start.bin /dev/sdb

至此,灯亮了,路也就清晰了,前路漫漫亦灿灿,与所有在嵌入式路上摸黑前行的朋友共勉。

相关推荐
GeW3 小时前
工作忙.基础弱,也能快速拿RHCE?这套方法亲测有效
linux
Wang's Blog3 小时前
Java 接入Redis: 使用Jedis操作Redis
java·服务器·redis
Ivanqhz3 小时前
数据搬运是性能关键
java·服务器·网络·人工智能·深度学习
糖糖单片机设计3 小时前
基于STM32的电子密码锁设计与实现(矩阵键盘+防拆检测+GSM远程报警)
人工智能·stm32·单片机·嵌入式硬件
其实防守也摸鱼3 小时前
常见安全架构中 Shiro 的认证确认机制解析
运维·服务器·数据库·windows·github
cyl12240713 小时前
2026年10月嵌入式培训指南:在“AI+嵌入式“爆发前夜,如何选择你的职业跳板?
人工智能·嵌入式硬件
Julien20043 小时前
自动挂载网络附加存储
linux·服务器·网络
充哥单片机设计4 小时前
【STM32开源项目】智能家电控制系统
stm32·单片机·嵌入式硬件·毕业设计·智能家电控制·家电控制
codigger4 小时前
服务器又卡了?一篇讲透 Linux 性能排查(基础四件套 + perf/strace/火焰图)
linux·后端·性能优化