脱离IDE,使用汇编在战舰上点个灯

2019-07-21 02:14发布

本帖最后由 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 的配置寄存器信息如下:
1.png

上面的代码写完后就可以使用 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

3.png

需要说明的是上面 -Ttext 0x8000000 用于指示代码段的存放地址,编译后就可以把 hex 文件通过串口下载到战舰开发板上啦,串口烧写过程原子哥的教程有说明,使用FlyMcu这个软件就可以了。
最后就是激动人心的点灯效果了:

2.jpg



友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
19条回答
it_do_just
1楼-- · 2019-07-22 09:46
NULLFF 发表于 2018-11-30 18:51
这样就是精华了?感觉没什么难度啊,代码也不算精简。
之前我用ASM实现了单双精度浮点四则运算,也没见有 ...

代码上确实是没什么难度的,可能只是因为比较少人用gcc自己编译链接吧,点个灯只是抛砖引玉罢了,呵呵
NULLFF
2楼-- · 2019-07-22 11:48
 精彩回答 2  元偷偷看……
opk666
3楼-- · 2019-07-22 14:36
炫酷666
it_do_just
4楼-- · 2019-07-22 15:07
NULLFF 发表于 2018-12-3 11:54
gcc好处是什么,在Windows下的集成开发环境己经做好了一切,很方便。gcc要写除了源码外的不少脚本呢。

MCU上gcc用在开发中没任何好处,只能说有利于学习吧,此贴主要让人了解最底层的操作形式,自己来实现编译链接和代码段的定位这些,如何不依赖ST提供的启动文件,如果真的要这么做写通用的Makefile也是挺麻烦的
tanay
5楼-- · 2019-07-22 16:30
 精彩回答 2  元偷偷看……
joey骚尼
6楼-- · 2019-07-22 18:32
太牛逼了

一周热门 更多>