【方法】STM32新建一个空白的s文件,并完全用汇编实现一个函数

2 篇文章 0 订阅

【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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值