【main.c】
#include <stdio.h>
#include <stm32f1xx.h>
#include "common.h"
uint16_t test(void);
int main(void)
{
uint16_t value;
HAL_Init();
clock_init();
usart_init(115200);
value = test();
printf("value=0x%04x\n", value);
return 0;
}
新建一个test.s文件,其中的汇编代码如下:
area |.text|, code, readonly
export test
test proc
mov r0, #0xabcd ; 指定函数返回值
bx lr ; 返回
endp
end
注意,除了test proc要顶格写外,其他行前必须要加一个tab,否则编译不通过。
test函数执行后,返回值为0xabcd。
函数传入的参数保存在r0, r1, r2等寄存器中,可以直接在汇编代码里面使用。在下面的汇编代码中,用mla指令计算r0*r1+r2的值,然后存入r0作为函数的返回值。
area |.text|, code, readonly
export test
test proc
mla r0, r0, r1, r2 ; r0=r0*r1+r2
bx lr ; 返回
endp
end
#include <stdio.h>
#include <stm32f1xx.h>
#include "common.h"
uint32_t test(uint32_t a, uint32_t b, uint32_t c);
int main(void)
{
uint32_t value;
HAL_Init();
clock_init();
usart_init(115200);
value = test(8, 6, 10);
printf("value=%u\n", value);
return 0;
}
test函数返回值为8*6+10=58。
我们用汇编语言操作GPIOA->BSRR寄存器,同时翻转PA4和PA5两个IO口,看看STM32F1的GPIO最快翻转到多少MHz的频率。
#include <stdio.h>
#include <stm32f1xx.h>
#include "common.h"
uint32_t test(uint32_t a, uint32_t b, uint32_t c);
void io_test(void);
static void io_init(void)
{
GPIO_InitTypeDef gpio;
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pin = GPIO_PIN_4 | GPIO_PIN_5;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio);
}
int main(void)
{
uint32_t value;
HAL_Init();
clock_init();
usart_init(115200);
value = test(8, 6, 10);
printf("value=%u\n", value);
io_init();
io_test();
return 0;
}
area |.text|, code, readonly
export test
export io_test
test proc
mla r0, r0, r1, r2 ; r0=r0*r1+r2
bx lr ; 返回
endp
io_test proc
ldr r0, = 0x40010810 ; GPIOA->BSRR
mov r1, #0x30 ; GPIO_BSRR_BS4 | GPIO_BSRR_BS5
mov r2, #0x300000 ; GPIO_BSRR_BR4 | GPIO_BSRR_BR5
loop
str r1, [r0] ; PA4和PA5设为高电平
str r2, [r0] ; PA4和PA5设为低电平
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
str r1, [r0]
str r2, [r0]
b loop
endp
align ; 避免added x bytes of padding的警告
end
最后,示波器测出来,翻转频率刚好是18MHz。
io_test函数目前是在Flash里面执行的,我们把它复制到SRAM里面执行试试看:
(这里顺便教大家怎样在STM32单片机里面,把Flash中的函数复制到内部SRAM执行)
#include <stdio.h>
#include <stm32f1xx.h>
#include <string.h>
#include "common.h"
typedef void (*Runnable)(void);
uint32_t test(uint32_t a, uint32_t b, uint32_t c);
void io_test(void);
static void io_init(void)
{
GPIO_InitTypeDef gpio;
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pin = GPIO_PIN_4 | GPIO_PIN_5;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio);
}
int main(void)
{
uint8_t buffer[256] __attribute__((aligned)); // 必须保证buffer的地址能被4整除
uint32_t value;
Runnable run;
HAL_Init();
clock_init();
usart_init(115200);
value = test(8, 6, 10);
printf("value=%u\n", value);
memcpy(buffer, (char *)io_test - 1, sizeof(buffer)); // 将io_test函数的代码复制到buffer数组中
run = (Runnable)(buffer + 1); // 函数指针指向buffer数组
io_init();
run(); // 执行buffer数组里面的代码
return 0;
}
test.s里面的io_test proc前要加上align,保证io_test函数的地址能被4整除:
align ; 保证函数的地址能被4整除
io_test proc
运行程序,示波器里面仍然还是18MHz的波形,速度并没有得到提升,而且时钟频率没有之前稳定了。
上面的汇编代码有一个问题,就是在汇编函数里面操作了r0, r1, r2寄存器,却没有提前保存这三个寄存器的原有内容,导致函数退出后,main函数里面的一些局部变量的值被篡改。
比如run()函数执行一次后,会发现函数指针run的指向改变了,再执行run()就会Hard Fault。
解决办法就是用push指令压栈,函数返回时不用bx lr,用pop出栈指令。
test proc
push {r0-r2, lr} ; 保存寄存器的原有内容,以及函数的返回地址(lr就是r14寄存器)
…
pop {r0-r2, pc} ; 恢复寄存器的原有内容,并返回(pc就是r15寄存器)
endp