本帖最后由 229382777@qq.com 于 2018-11-29 08:08 编辑
很久没回来发贴了,最近抽空写了个简单的汇编程序,由于我后面开发大多在linux平台下进行,所以这次写这个程序的时候也考虑了下使用gcc来完成整个编译链接的过程,当然实际开发不会这么做,毕竟代码一大了,写起Makefile来还是比较麻烦的,而且也没有IDE这个强大的调试追踪功能,所以使用gcc和汇编来完成点灯纯粹仅供学习,这个程序没有使用官方提供的启动文件,也没有那些官方的配置文件,做到了一个文件就能点灯,下面是点灯的汇编代码,可以直接使用原子提供的FlyMcu这个isp下载工具把这段代码进行编译链接后下载到战舰上测试。
[mw_shl_code=asm,true];首先使用 EQU 指示字来定义常数, 主要是定义寄存器地址和一些操作数
;RCC 寄存器地址定义
.equ RCC_BASE, 0x40021000
.equ RCC_CR, (RCC_BASE + 0x00)
.equ RCC_CFGR, (RCC_BASE + 0x04)
.equ RCC_APB2ENR, (RCC_BASE + 0x18)
;GPIOB 寄存器地址定义
.equ GPIOB_BASE, 0x40010C00
.equ GPIOB_CRL, (GPIOB_BASE + 0x00)
.equ GPIOB_ODR, (GPIOB_BASE + 0x0C)
;FLASH缓冲区及访问速度配置
.equ FLASH_ACR, 0x40022000
;用于操作寄存器的一些常数
.equ Bit0, 0x00000001
.equ Bit1, 0x00000002
.equ Bit2, 0x00000004
.equ Bit3, 0x00000008
.equ Bit4, 0x00000010
.equ Bit5, 0x00000020
.equ Bit6, 0x00000040
.equ Bit7, 0x00000080
.equ Bit8, 0x00000100
.equ Bit9, 0x00000200
.equ Bit10, 0x00000400
.equ Bit11, 0x00000800
.equ Bit12, 0x00001000
.equ Bit13, 0x00002000
.equ Bit14, 0x00004000
.equ Bit15, 0x00008000
.equ Bit16, 0x00010000
.equ Bit17, 0x00020000
.equ Bit18, 0x00040000
.equ Bit19, 0x00080000
.equ Bit20, 0x00100000
.equ Bit21, 0x00200000
.equ Bit22, 0x00400000
.equ Bit23, 0x00800000
.equ Bit24, 0x01000000
.equ Bit25, 0x02000000
.equ Bit26, 0x04000000
.equ Bit27, 0x08000000
.equ Bit28, 0x10000000
.equ Bit29, 0x20000000
.equ Bit30, 0x40000000
.equ Bit31, 0x80000000
.equ STACK_TOP, 0x20000800
.text
.global _start
.code 16
.syntax unified
_start:
.word STACK_TOP, start
.type start, function
start:
;外部高速时钟时能 RCC->CR |= Bit16
LDR R0, =RCC_CR
LDR R1, [R0]
ORR R1, #Bit16
STR R1, [R0]
;检验外部时钟是否就绪
RCC_CLK_NOTOK:
LDR R1, [R0]
ANDS R1, #Bit17
BEQ RCC_CLK_NOTOK
LDR R1, [R0]
ORR R1, #Bit17
STR R1, [R0]
;FLASH缓冲区及访问速度配置
LDR R0, =FLASH_ACR
MOV R1, #0X00000032
STR R1, [R0]
;设置系统时钟倍频
LDR R0, =RCC_CFGR
LDR R1, [R0]
;HCLK 2分频
ORR R1, #Bit10
;9倍频[21:18]-->0111
ORR R1, #Bit18
ORR R1, #Bit19
ORR R1, #Bit20
;设置HSE为PLL输入时钟源
ORR R1, #Bit16
STR R1, [R0]
;开启倍频
LDR R0, =RCC_CR
LDR R1, [R0]
ORR R1, #Bit24
STR R1, [R0]
;等待倍频生效
RCC_PLL_NOTOK:
LDR R1, [R0]
ANDS R1, #Bit25
BEQ RCC_PLL_NOTOK
;设置PLL为当前系统时钟
LDR R0, =RCC_CFGR
LDR R1, [R0]
ORR R1, #Bit1
STR R1, [R0]
;验证系统时钟状态是否切换为PLL
MOV R2, #0X02
RCC_PLL_NOTRDY:
LDR R1, [R0]
LSR R1, R1, #2
AND R1, #0X03
CMP R1, R2
BNE RCC_PLL_NOTRDY
;开启APB2口时钟
LDR R0, =RCC_APB2ENR
LDR R1, [R0]
ORR R1, #Bit3
STR R1, [R0]
;配置PB口功能,PB5设置为通用推挽输出
LDR R0, =GPIOB_CRL
LDR R1, [R0]
LDR R2, =0XFF0FFFFF
AND R1, R2
MOV R2, #0X03
ORR R1, R1, R2, LSL #20
STR R1, [R0]
;初始化led灯的状态,默认打开
LDR R0, =GPIOB_ODR
LDR R1, [R0]
ORR R1, #0
STR R1, [R0]
;死循环
deadloop:
b deadloop
.end
[/mw_shl_code]
上面代码的寄存器的配置参考了原子哥代码和网友代码的配置,这部分配置跟原来没什么差别,如果嫌看手册麻烦,也可以反汇编原子哥提供的例程代码,可以很容易得出汇编配置的代码,copy过来用也行。这份汇编代码的反汇编文件我会作为附件上传上来,总的来说就是先配置时钟再配置GPIO,也没有把中断向量表加进来,纯粹的为了点个灯,具体汇编指令的含义如下:1 . word :指示字定义 MSP 起始值为 0x2000_0800,并且把”start”作为复位向量。
2 . text :也是一个预定义的指示字,表示从这以后是一个代码区,需要予以汇编。
3 . global :使_start 标号可以由其它目标文件使用。
4 . code 16 :指示程序代码使用 thumb 写成。
5 . syntax unified :指示使用了统一汇编语言语法。
6 . _start :是一个标号,指示出程序区的入口点
7 . start :是另一个标号,它指示复位向量。
8 . type start, function :宣告了 start 是一个函数。对于所有处于向量表中的异常向量,这种
宣告都是必要的,否则汇编器会把向量的 LSB 清零——这在 thumb 中是不允许的。
9 . end :指示程序文件的结束
其它汇编指令含义如下:EQU:指示字来定义常数
LDR : 从存储器中加载字到一个寄存器中
ORR : 按位或
STR : 把一个寄存器按字存储到存储器中
ANDS/AND : 按位与
BEQ : 数据跳转指令, 标志寄存器中Z标志位等于零时, 跳转到BEQ后标签处
BNE : 数据跳转指令,标志寄存器中Z标志位不等于零时, 跳转到BNE后标签处
MOV : 寄存器加载数据,既能用于寄存器间的传输,也能用于加载立即数
LSR : 逻辑右移
LSL:逻辑左移
b : 跳转
上面有关于 FLASH_ACR 的配置寄存器信息如下:
上面的代码写完后就可以使用 gcc 来编译啦,当前提是你已经搭建好了linux的开发环境,我使用的ubuntu18,安装相应的编译器指令如下:
sudo apt-get install gcc-arm-none-eabi
下载好编译器后,使用下面的指令进行编译链接:
编译:arm-none-eabi-as -mcpu=cortex-m3 -mthumb led.s -o led.o
链接:arm-none-eabi-ld -Ttext 0x8000000 -o led.out led.o
生成反汇编:arm-none-eabi-objdump -S led.out > led.list
生成hex文件:arm-none-eabi-objcopy -Oihex led.out led.hex
需要说明的是上面 -Ttext 0x8000000 用于指示代码段的存放地址,编译后就可以把 hex 文件通过串口下载到战舰开发板上啦,串口烧写过程原子哥的教程有说明,使用FlyMcu这个软件就可以了。
最后就是激动人心的点灯效果了:
一周热门 更多>