汇编LED实验
-
在使用之前要初始化一些外设
-
准备C语言运行环境
-
之后进入C语言
-
使用汇编初始化DDR I.MX6U不需要
-
设置
sp
指针指向DDR, 设置好C语言运行环境
硬件原理分析
stm32初始化流程, 首先使能GPIO时钟, 之后设置GPIO功能, 设置IO复用, 配置GPIO电气属性, 最后使用GPIO输出高低电平
I.MX6U:
时钟
时钟管理18章, 也可以控制内每一个外设的时钟
Address: 20C_4000h base + 68h offset = 20C_4068h
- CCGR0-CCGR6七个寄存器控制所有时钟的使能
每一个有两个位控制时钟, 有三种模式
- 00: 关闭
- 01 :在run模式开启
- 11 : 所有模式开启
为了简单设置CCGR0-6寄存器全部使能0xffffffff, 相当于使能所有的外设时钟
复用
I/O复用寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
Address: 20E_0000h base + 68h offset = 20E_0068h
位0-3四个位设置他的模式
0000ALT0 — Select mux mode: ALT0 mux port: I2C1_SDA of instance: i2c1
0001ALT1 — Select mux mode: ALT1 mux port: GPT1_COMPARE3 of instance: gpt1
0010ALT2 — Select mux mode: ALT2 mux port: USB_OTG2_OC of instance: usb
0100ALT4 — Select mux mode: ALT4 mux port: USDHC1_CD_B of instance: usdhc1
0101ALT5 — Select mux mode: ALT5 mux port: GPIO1_IO03 of instance: gpio1
0110ALT6 — Select mux mode: ALT6 mux port: CCM_DI0_EXT_CLK of instance: ccm
0111ALT7 — Select mux mode: ALT7 mux port: SRC_TESTER_ACK of instance: src
NOTE:ALT7 mode will be automatically active when system reset. The PAD setting will be 100 Kpull down and input enable during reset period. Once system reset is completed, the stateof GPIO1_IO03 will be output keeper and input enable.
1000ALT8 — Select mux mode: ALT8 mux port: UART1_RX of instance: uart1
0101的时候设置为GPIO的功能
配置电气属性
寄存器IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03设置电气属性
Address: 20E_0000h base + 2F4h offset = 20E_02F4h
bit0: 设置压摆率, 就是电平跳变使用的时间
2-1 : 保留
5-3:
000 输出驱动关闭
001: 3.3V时候R0为260Ω, 1.8V时候240Ω, DDR的时候240Ω
010: R0/2
011: R0/3
…
数字越大驱动能力越强
6-7: 设置速度
00SPEED_0low_50MHz — low(50MHz)
01SPEED_1medium_100MHz — medium(100MHz)
10SPEED_2medium_100MHz — medium(100MHz)
11SPEED_3max_200MHz — max(200MHz)
11 : 开漏输出1打开
12 : 上拉保持1开启, 断电以后保持I/O状态, 使能或者禁止上下拉/状态保持器功能,为0时禁止上下拉/状态保持器,为1时使能上下拉和状态保持器
13: 当IO作为输入的时候,这个位用来设置IO使用上下拉还是状态保持器。当为0的时候使用状态保持器,当为1的时候使用上下拉。状态保持器在IO作为输入的时候才有用,顾名思义,就是当外部电路断电以后此IO口可以保持住以前的状态
14, 15 :上拉下拉
00PUS_0_100K_Ohm_Pull_Down — 100K Ohm Pull Down
01PUS_1_47K_Ohm_Pull_Up — 47K Ohm Pull Up
10PUS_2_100K_Ohm_Pull_Up — 100K Ohm Pull Up
11PUS_3_22K_Ohm_Pull_Up — 22K Ohm Pull Up
16: 迟滞比较器,当IO作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。如果需要对输入波形进行整形的话可以使能此位。此位为0的时候禁止迟滞比较器,为1的时候使能迟滞比较器
主要设置, 压摆率, 速度, 驱动能力, 开漏输出, 上下拉
具体的设置为
bit0:0低速率
bit5-3: 110设置为R0/6驱动能力
bit7-6: 10设置为100MHz
bit11: 0开漏输出关闭
bit12: 1使能pullkepper
bit13: 0
bit15-14:00默认下拉
bit16: 0关闭
0x10b0
配置GPIO功能
设置输入输出
第二十八章
GPIOx_DR寄存器控制高低
209_C000
GPIOx_GDIR: 输入输出, 0输入
209_C004
GPIOx_PSR: 只读寄存器, 状态寄存器
GPIOx_ICR1: 中断相关, 用两个位设置中断电平以及上升沿下降沿
00LOW_LEVEL — Interrupt n is low-level sensitive.
01HIGH_LEVEL — Interrupt n is high-level sensitive.
10RISING_EDGE — Interrupt n is rising-edge sensitive.
11FALLING_EDGE — Interrupt n is falling-edge sensitive.
GPIOx_ICR2: 控制位数较高的引脚
GPIOx_IMR: 控制中断的打开关闭1打开
GPIOx_ISR: 中断状态寄存器
只是用最上面两个的寄存器, GPIO1_GDIR的bit3为1输出模式
GPIO1_DR的bit3寄存器为0输出低电平
汇编
编写的是ARM汇编, 使用GCC交叉编译期, 要符合GNU语法
label: instruction @ comment
label: 标签
instruction: 指令
@ 注释
汇编语言的默认入口是_start
常用的指令
数据传输
- 寄存器之间的传递
- 特殊寄存器之间的传递
- 把数字传递到寄存器
MOV R0,R1把R1传递到R0
MRS R0, CPSR 读取特殊寄存器
MSR CPSR,RO 写入特殊寄存器
操作内存存储器访问指令
LDR Rd, [Rn,#offset] 把Rn+offset读取
STR Rd, [Rn,#offset] 放入
LDR R0, =0x3008000 @把数字放入寄存器 LDR R1, [R0] @读取这个地址的数字
LDR R0, =0x30008000 LDR R1, =0x01001010 STR R1, [R0] @ 把数据放入这个地址
出栈压栈
POP {}
PUSH
跳转B、BL、BX
B <lable>
: 跳转到标签
BL <lable>
: 跳转以后把地址保存在Rm中
BX <Rm>
: 跳转到存放在Rm的地址
BLX <Rm>
: 跳转到Rm地址, 并保存返回地址
处理器运行模型
ARM处理器有7种模式, 六种是特权模式, User是非特权模式, 最新的架构加入了其他的模式
最常用的
- Supervisor(SVC): 超级管理员模式, 供操作系统使用
- IRQ: 一般中断模式
- User(USR): 用户模式
寄存器组
内核寄存器, 16个32位, 前十五个可以当做通用使用, 最后一个是PC寄存器
CPSR当前状态寄存器
SPSR: 备份状态寄存器(有的的模式没有)
R13堆栈指针(SP)
R14链接寄存器, 上一级地址(LR)
R15程序计数器(RC)
不同模式的寄存器有可能不一样
状态寄存器保存着计算的结果, 使用的指令集, 大小端, 中断控制, 处理器模式等
编译
将.c .s文件编译为.o文件 arm-linux-gnueabihf-gcc -g -c leds.s -o leds.o
, -g : 调试信息 -c: 源文件编译不连接
链接
将所有的.o文件链接为elf格式的可执行文件, 带有调试信息的bin文件
arm-linux-gnueabihf-ld -Ttext 0x87800000 led.o -o led.elf
“存储地址”就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。“运行地址”就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。
在链接的时候要指定起始的地址, 链接的起始地址就是代码运行的起始地址
对于stm32已经设置好了
对于6ULL指向RAM地址, 没有内部flash, ROM无法使用
一般有内部的RAM和外部的RAM也就是DDR
6ULL内部有128K的RAM(0X900000~0X91FFFF)
DDR中,链接起始地址为0X87800000。对于每一个开发板都不同, 终止地址为0X9FFFFFFF
0X87800000这个地址是因为后面要讲的Uboot
其链接地址就是0X87800000,这样我们统一使用0X87800000这个链接地址
想要使用DDR需要初始化DDR, bin文件不能直接在SD卡等外部存储设备中运行, 需要添加一个头部信息, 包含DDR的初始化参数LXM系列的SOC内部的boot rom里面的程序会从SD卡,EMMC等读取头部信息, 然后初始化DDR, 并把斌文件拷贝到指定的地址
bin的运行地址, 一定要和链接起始地址一致, 位置无关代码除外
格式转换
把elf文件转换为bin文件
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
-O binary : 输出的文件为二进制
-S : 不要复制源文件中的重定位信息以及符号信息
-g : 不复制调试信息
反汇编
也可以吧c的elf文件转换为汇编
arm-linux-gnueabihf-objdump -D led.elf > led.dis
-D: 反汇编所有的段
烧写代码
STM32烧写到内部的flash
6ULL支持SD卡启动, EMMC启动, nor等方式启动
裸机例程烧写到SD卡然后从SD卡启动
在ubuntu下进行烧写, 不是直接拷贝, 而是烧写到绝对地址上, 而且对于L.MX而言不可以直接烧写, 要添加头部, 完成这个工作要使用专用软件
jiao@jiao-virtual-machine:~/linux/IMX6ULL/Bord_Drivers/1_leds$ ls
imxdownload led.bin led.dis led.elf leds.code-workspace leds.o leds.s
imxdownload
软件
格式./imxdownload <.bin file> <SD Card>
可以使用sudo fdisk -l
查看, 确定要烧写的SD卡文件我的是/dev/sdb
jiao@jiao-virtual-machine:~/linux/IMX6ULL/Bord_Drivers/1_leds$ ./imxdownload led.bin /dev/sdb
I.MX6ULL bin download software
Edit by:zuozhongkai
Date:2019/6/10
Version:V1.1
log:V1.0 initial version,just support 512MB DDR3
V1.1 and support 256MB DDR3
file led.bin size = 160Bytes
Board DDR SIZE: 512MB
Delete Old load.imx
Create New load.imx
Download load.imx to /dev/sdb ......
记录了6+1 的读入
记录了6+1 的写出
3232 bytes (3.2 kB, 3.2 KiB) copied, 0.145773 s, 22.2 kB/s
添加头部生成新的文件load.imx然后烧写
创建Makefile
led.bin: leds.s
arm-linux-gnueabihf-gcc -g -c leds.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
clear:
rm -rf *.o led.bin led.elf led.dis
不使用jlnk
引脚冲突, 得不偿失
没有烧写的算法, 不能下载使用, 只能烧写到内部的RAM中去
链接脚本
描述了要链接的文件以及顺序还有首地址
通过“-Ttext”来指定链接地址是0X87800000的,这样的话所有的文件都会链接到以0X87800000为起始地址的区域。但是有时候我们很多文件需要链接到指定的区域,或者叫做段里面
SECTIONS{
. = 0x10000000
.text = (*(.text))
. = 0x30000000
.data = ALLGN(4) : ( *(.data) )
.bss = ALLGN(4) : ( *(.bss) )
}
. : 叫做定位计数器, 默认为0, 代码链接的地址
在放置数据段之前要重新设置定位计数器
SECTIONS{
. = 0X87800000;
.text :
{
start.o
main.o
*(.text)
}
.rodata ALIGN(4) : {*(.rodata*)}
.data ALIGN(4) : { *(.data) }
__bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .;
}
ALIGN(4)表示4字节对齐。也就是说段“.data”的起始地址要能被4整除
start.o要被链接到最开始的地方,因为start.o里面包含这第一个要执行的命令
“bss_start”和“__bss_end”
用来保存.bss
段的起始地址和结束地址