前段时间我想学习汇编程序,在阅读了相关文献之后,该进行练习了。 实际上,将进一步讨论。 最初,我在Arduino Uno(Atmega328p)上进行练习,现在我决定继续学习STM32。 STM32F103C8实际上落在我手上,它将进行进一步的实验。
工具
我使用了以下工具:
- 记事本++-用于编写代码
- GNU汇编器编译器
- STM32 ST-LINK实用程序+ ST-LINK V2-用于在微控制器上刷新代码并进行调试
开始
对我来说,汇编语言的主要目的是学习。 由于您永远不知道在哪里遇到另一个有趣的问题,因此决定从头开始编写所有内容。 主要任务是了解中断向量的工作原理。 与STM32中的Atmega不同,中断向量不包含跳转指令:
jmp main
在其中写入特定的地址,并且在中断期间,处理器本身将替换PC寄存器中向量中指定的地址。 这是我的中断向量的示例:
.org 0x00000000 SP: .word STACKINIT RESET: .word main NMI_HANDLER: .word nmi_fault HARD_FAULT: .word hard_fault MEMORY_FAULT: .word memory_fault BUS_FAULT: .word bus_fault USAGE_FAULT: .word usage_fault .org 0x000000B0 TIMER2_INTERRUPT: .word timer2_interupt + 1
我想引起读者的注意的是,第一行不是复位向量,而是初始化堆栈的值。 紧随其后的是一个复位向量,其后是5个强制中断向量(NMI_HANDLER-USAGE_FAULT)。
发展历程
我遇到的第一件事是ARM汇编器语法。 即使在研究中断向量时,我也发现ARM是2种类型的指令,而不是Thumb。 而且该Cortex-M3(STM32F103C8即Cortex-M3)仅支持一组Thumb指令。 我严格按照文档编写了说明,但是出于某种原因,汇编程序对它们进行了诅咒。
需要未移位的寄存器
原来,在程序开始时
.syntax统一
这告诉汇编程序您可以同时使用Thumb和non-Thumb指令。
我遇到的下一件事是禁用的默认GPOI端口。 为了使它们起作用,除其他外,您需要在RCC(复位和时钟控制)寄存器中设置适当的值。 我使用了端口C,可以通过设置RCC_APB2ENR(外围时钟使能寄存器2)中的位4(位的编号从头开始)来打开它。
LED进一步闪烁。 首先,与Arduino一样,您需要设置一个用于记录的引脚。 这可通过GPIOx_CRL(控制寄存器为低电平)或GPIOx_CRH(控制寄存器为高电平)完成。 在这里,有必要取消这些寄存器之一(32位寄存器)中每个引脚有4位负责。 2位(MODEy)确定最大数据速率,2位(CNF)引脚配置。 我使用PORT C引脚14,为此,我在GPIOx_CRH寄存器中设置了位[25:24] = 10和位[27:26] = 00。
为了使二极管燃烧,需要将GPIOx_ODR(输出数据寄存器)中的相应位置1。 在我的例子中,位14。这可以通过创建延迟函数并将其全部放入循环中来结束这个简单的示例,但是我无法做到这一点。 我决定设置计时器中断...事实证明这是徒劳的,主要是因为计时器对于此类任务而言太快了。
我不会详细描述定时器设置,他们对
Github上的代码感兴趣。 这个想法很简单,在一个周期内将处理器发送到Idle,通过计时器退出Idle以打开/关闭LED,然后再次返回Idle。 但是计时器的工作速度比我完成上述所有操作的速度快得多,因此我不得不输入一个额外的计数器。
计数器是一个32位变量,应已包含在SRAM中。 然后另一只耙子在等我。 当我在Atmega中编程以在SRAM中放置变量时,通过.org设置了实际放置数据块的内存起始地址。 现在,在阅读了一些有关内存初始化的内容之后,我不确定这是否正确,但是它确实有效。 我决定用STM32进行同样的处理。 STM32F103C8中的存储器起始地址为0x20000000。 当我在该地址执行.org时,我得到了512mb的二进制文件。 这让我花了两个晚上抽烟。 我仍然不了解它是如何工作的100%,但是据我了解,.data节将必须将变量初始化的值放入可执行文件中,但是在运行时,程序员必须在内存中初始化变量值。 如果我错了,请纠正我。 我最终创建了一个像这样的变量:
.section .bss .offset 0x20000000 flash_counter: .word
在主要功能开始时对其进行初始化,并且LED闪烁。 我希望本文能对某人有所帮助。 如果您有任何疑问,我将很乐意回答。