• 简单的STM32 汇编程序—闪烁LED


    要移植操作系统,汇编是道不得不跨过去的坎。所以承接上篇的思路,我准备用汇编写一个简单的闪烁LED灯的程式。以此练习汇编,为操作系统做准备。

    第一步,还是和上篇一样,建立一个空的文件夹。

    第二步,因为是要用汇编来写程式,所以不需要启动代码,这里选择否。

    第三步,建立一个.s文件,并把文件添加到工程中。

    第四步,在LED.s文件中添加如下代码。

    LED0 EQU 0x422101a0 
    RCC_APB2ENR EQU 0x40021018
    GPIOA_CRH EQU 0x40010804
    
    
    
    Stack_Size      EQU     0x00000400
    
                    AREA    STACK, NOINIT, READWRITE, ALIGN=3
    Stack_Mem       SPACE   Stack_Size
    __initial_sp
    
    
    
    
    
    
    
                    AREA    RESET, DATA, READONLY
    
    __Vectors       DCD     __initial_sp               ; Top of Stack
                    DCD     Reset_Handler              ; Reset Handler
                        
                        
                    AREA    |.text|, CODE, READONLY
                        
                    THUMB
                    REQUIRE8
                    PRESERVE8
                        
                    ENTRY
    Reset_Handler 
                    BL LED_Init
    MainLoop        BL LED_ON
                    BL Delay
                    BL LED_OFF
                    BL Delay
                    
                    B MainLoop
                 
    LED_Init
                    PUSH {R0,R1, LR}
                    
                    LDR R0,=RCC_APB2ENR
                    ORR R0,R0,#0x04
                    LDR R1,=RCC_APB2ENR
                    STR R0,[R1]
                    
                    LDR R0,=GPIOA_CRH
                    BIC R0,R0,#0x0F
                    LDR R1,=GPIOA_CRH
                    STR R0,[R1]
                    
                    LDR R0,=GPIOA_CRH
                    ORR R0,R0,#0x03
                    LDR R1,=GPIOA_CRH
                    STR R0,[R1]
                    
                    MOV R0,#1 
                    LDR R1,=LED0
                    STR R0,[R1]
                 
                    POP {R0,R1,PC}
    
                 
    LED_ON
                    PUSH {R0,R1, LR}    
                    
                    MOV R0,#0 
                    LDR R1,=LED0
                    STR R0,[R1]
                 
                    POP {R0,R1,PC}
                 
    LED_OFF
                    PUSH {R0,R1, LR}    
                    
                    MOV R0,#1 
                    LDR R1,=LED0
                    STR R0,[R1]
                 
                    POP {R0,R1,PC}             
                 
    Delay
                    PUSH {R0,R1, LR}
                    
                    MOVS R0,#0
                    MOVS R1,#0
                    MOVS R2,#0
                    
    DelayLoop0        
                    ADDS R0,R0,#1
    
                    CMP R0,#330
                    BCC DelayLoop0
                    
                    MOVS R0,#0
                    ADDS R1,R1,#1
                    CMP R1,#330
                    BCC DelayLoop0
    
                    MOVS R0,#0
                    MOVS R1,#0
                    ADDS R2,R2,#1
                    CMP R2,#15
                    BCC DelayLoop0
                    
                    
                    POP {R0,R1,PC}    
                 
        ;         NOP
                 END
                     
                     

    ///////////////////////////////////////////////////////

    代码的简单讲解

    1,预定义

    LED0 EQU 0x422101a0 ;PA8的Bit-Bond地址。

    RCC_APB2ENR EQU 0x40021018

    GPIOA_CRH EQU 0x40010804

    为方便操作,给每个需要用到的寄存器地址定义一个名字,类似于C语言的#define。PA8的Bit-Bond地址的计算方法可按上篇文章中C语言的算法算出。后面的两个地址时固定的,可从STM32的手册查询,或者根据ST官方的库文件查找计算。

    2,分配栈空间

    Stack_Size EQU 0x00000400

    AREA STACK, NOINIT, READWRITE, ALIGN=3

    Stack_Mem SPACE Stack_Size

    __initial_sp

    这一段摘自启动文件。要读懂这段代码,首先要了解两个命令。

    AREA命令:AREA 命令指示汇编器汇编一个新的代码段或数据段。段是独立的、指定的、不可见的代码或数据块,它们由链接器处理。格式如下:

    AREA 段名,段属性1,段属性2,段属性3。。。

    AREA STACK, NOINIT, READWRITE, ALIGN=3

    NOINIT: = NO Init,不初始化。

    READWRITE : 可读,可写。

    ALIGN =3 : 2^3 对齐,即8字节对齐。

    SPACE命令:SPACE 命令保留一个用零填充的存储器块。

    所以整段的意思为:分配一个STACK段,该段不初始化,可读写,按8字节对齐。分配一个大小为Stack_Size的存储空间,并使栈顶的地址为__initial_sp。

    3,分配向量表

    AREA RESET, DATA, READONLY

    __Vectors DCD __initial_sp ; Top of Stack

    DCD Reset_Handler ; Reset Handler

    这里的向量可参考我之前写的《STM32向量表详细分析》。

    4,开始代码段

    AREA |.text|, CODE, READONLY

    通知汇编器,开始代码段。

    THUMB

    REQUIRE8

    PRESERVE8

    这段的意思是,汇编器支持THUMB指令,代码段按8字节对齐。

    ENTRY命令:声明整个程式的入口点,入口点有且仅有一个。不管哪种语言,编译器都得有个入口点,这没什么好说的。

    5,程序正式开始。

    后面的代码,皆用标准的THUMB2汇编指令。首先了解下代码中用到的指令。

    BL:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器。

    B:无条件跳转。

    PUSH和POP:可以看到,所有的子程序都是由PUSH和POP包起来的。借用一张图解释下这两个指令。

    据上可知,PUSH {R0,R1, LR}的意思即把R0,R1,LR中的值放入堆栈中。由于主程式中使用BL跳转指令,所以LR中的值实际上就是当前PC的值。而POP {R0,R1,PC}的意思即是将栈中之前存的R0,R1,LR的值返还给R0,R1,PC。这样就完成了子程序的返回。

    LDR和STR:寄存器的装载和存储指令。

    LDR是把地址装载到寄存器中(比如R0)。

    STR是把值存储到寄存器所指的地址中。

    举个例子:

    MOV R0,#1 ;将立即数1送入R0.

    LDR R1,=LED0;将PA8 bit-bond的地址送入R1.

    STR R0,[R1];将R0的值,也就是1,送给R1中的值所指向的地址中,也就是PA8的bit-bond地址。

    上面三句话的意思即是将PA8置1。

    ORR和BIC:

    ORR 按位或操作。ORR R0,R0,#0x04意思即将R0中的数或上0x04,再将结果送往R0中。实际意思就是将R0的第二位置1,其他位不变。

    BIC 先把立即数取反,再按位与。

    CMP和BCC:CMP是比较两个数,相等或大于则将标志位C置位,否则将C清零。BCC是个组合指令,实际为B+CC,意思是如果C=0则跳转。

    CMP R2,#15; 计算R2-15的值,若是R2<15,则C=0;若是R2>=15,则C=1。

    BCC DelayLoop0;若是C=0,则跳到DelayLoop0,若是c=1,则不跳转。

    以上就是代码段相关指令的介绍,相信了解了这些指令的含义,要理解代码并不困难。

    整个代码的结构和上篇用C语言写的基本是一样的。可参照理解

    ////////////////////////////////////////////////////

    第五步,编译,下载。

    编译后,会有一个警告 No section matches pattern……可不用管。下载后,LED灯正常闪烁。

  • 相关阅读:
    【BZOJ 3709: [PA2014]Bohater】
    清北学堂2019.8.10 & 清北学堂2019.8.11 & 清北学堂2019.8.12
    清北学堂2019.8.9
    清北学堂2019.8.8
    清北学堂2019.8.7
    清北学堂2019.8.6
    【洛谷T89379 【qbxt】复读警告】
    【洛谷T89353 【BIO】RGB三角形】
    【洛谷T89359 扫雷】
    【洛谷P2016战略游戏】
  • 原文地址:https://www.cnblogs.com/WeyneChen/p/4860764.html
Copyright © 2020-2023  润新知