KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记4——Directives

一、若干重要的DIRECTIVE介绍

在前面的工程中,我感觉下面的这些Directives是非常有用的。

名称功能
GET/INCLUDE包含其他文件
IF…ELSE…ENDIF类似与条件编译
WHILE…ENDW重复生成代码、汇编器变量计算
DEF类似于C中的#ifdef
GBLA,GBLL,GBLS定义汇编器变量
SETA,SETL,SETS汇编器变量赋值
EQU符号赋值
RLIST寄存器列表定义
RN寄存器别名
MACRO…MEND宏定义
ROL、ROR、MOD等运算符实现汇编器运算
EXPORT、IMPORT、EXTERN输出/引入符号

这里所有的Directives都在《ARM Developer Suite Assembler Guide》可以查到。

这些Directives是在汇编器的第一遍pass的时候被执行的。有点类似C语言的宏。

二、一些概念

三、Directives详细介绍

1,GET/INCLUDE 文件包含

这个就跟C语言下面的include是几乎一样的。可以将其他的可汇编文件内容拷贝到当前的文件里。

GET filename

这里注意GET的文件要可汇编的。所以如果弄个.h文件是不能用的。一般来说,后面的文件都是.s的。比如。

		get registers.s
		get irqn_vector.s
		get bus_clocks.s

2, IF…ELSE…ENDIF 条件汇编

具体的语法是

IF logical-expression … {ELSE …} ENDIF

注意IF、ELSE和ENDIF都不能顶格写。会被误判成符号。

可以实现条件编辑。比如我这里有一段这样写。就可以实现在设置不同的USART时钟选择的时候,给USART_BSS_VAL这个符号赋不同的值。

if USART_CLOCK_BUSEN = RCC_APB2ENR
USART_BSS_VAL	equ	 (FREQ_APB2 / BAUDRATE / 16 :rol: 4) + ( FREQ_APB2 / BAUDRATE :mod: 16 )
	else
USART_BSS_VAL	equ	 (FREQ_APB1 / BAUDRATE / 16 :rol: 4) + (FREQ_APB1 / BAUDRATE :mod: 16 )		
	endif

3, WHILE…ENDW 循环汇编

可以重复开始一段指令序列或directives。

WHILE logical-expression
code
WEND
where: logical-expression is an expression that can evaluate to either {TRUE} or {FALSE}

注意WHILE和ENDW都不能顶个,会被误判成符号。

我用这个指令可以实现一些数值的计算。

gbla	nCal_PPRE2_Div
	gbla	nCal_PPRE2_Reg
nCal_PPRE2_Div							seta	FDIV_APB2 :ror: 1		
nCal_PPRE2_Reg							seta	0
	while					nCal_PPRE2_Div != 0x01
nCal_PPRE2_Reg							seta	nCal_PPRE2_Reg + 1
nCal_PPRE2_Div							seta	nCal_PPRE2_Div :ror: 1
	wend
nCal_PPRE2_Reg							seta	nCal_PPRE2_Reg + 0x04

4, DEF 符号/变量定义检查

可以检查某个符号/变量是否在系统中被定义。

DEF :DEF:A {TRUE} if A is defined, otherwise {FALSE}.

用的时候要注意那两个冒号不能漏了。

这个运算符可以配合条件汇编、符号/变量定义实现C头文件中的那种Guard。例如下面这样的编码。先判断是否定义了_REGISTER_S_这个符号/变量。如果没有定义,就用gbll定义一个这样的汇编器变量。

	if :def: _REGISTER_S_
	else	
		gbll	_REGISTER_S_
; General Purpose Registers
callee_regs							rlist	{r4-r12,lr}

;SCB
SCB_BaseAddr						equ		0xE000ED00
SCB_CPUID							equ		0xE000ED00
SCB_VTOR							equ		0xE000ED08

	endif
	end

5,GBLA,GBLL,GBLS变量定义

用于定义变量。必须不能顶格写。

Directive功能
GBLA定义一个文内数值变量,初始值为0
GBLL定义一个文内逻辑变量, 初始值为FALSE
GBLS定义一个文内字符串, 初始值为null

语法为

gblx variable
where:
gblx is one of GBLA, GBLL, or GBLS.
variable is the name of the variable. variable must be unique amongst symbols within a source file.

用法在前面有出现

6,SETA,SETL,SETS变量赋值

用于给GBLx定义的变量进行赋值。必须顶格写。

Directive功能
SETA给一个文内数值变量赋值
SETL给一个文内逻辑变量赋值
SETS给一个文内字符串赋值

用法在前面已经出现过。

7,EQU符号常量赋值

给常量赋值。语法比较简单。必须顶格写。 一般来说就是:

name EQU expr{, type}

一般后面花括号里面的我不怎么用。详细的查手册吧。

这里说明一点。由于这些Directives都是运行在第一pass的,而指令都是在第二pass的时候被扫描的。所以对于指令里用的立即数,不论是汇编常量还是汇编变量,用法都是一样的。

8,RLIST寄存器列表定义

给一个通用寄存器的集合命名。必须顶格写。 语法是

name RLIST {list-of-registers}

目前我只是用来给函数需要保护的寄存器做个集合,这样在进入函数和离开函数的时候能有个统一一点的写法。

; General Purpose Registers
callee_regs							rlist	{r4-r12,lr}

在函数里就比较方便。例如下面的这个函数。

			area USB_UART_CODE_SECTION, code
			
			align 4
init		proc
			push	callee_regs
			
			...
			pop		callee_regs
			bx 		lr
			endp

9,RN寄存器别名

给寄存器起别名。必须顶格写。 语法是

name RN expr

	; Define the registers commonly used in this file.
rRCC			rn		r12
rGPIO			rn		r11
rUART			rn		r10	
rNVIC			rn		r9
rpBuf			rn		r8
rBuf_Size		rn		r7

如果寄存器够用的话,给寄存器起别名可以大大增加汇编代码的可读性。如果不够用的话,就得好好规划一下。但是也不是说就没有机会。

具体应用如下面这段代码。

ldr 	r0, [rGPIO, #GPIO_MODER]
			orr		r0,	#TX_PIN_MODER_VAL :or: RX_PIN_MODER_VAL
			str 	r0, [rGPIO, #GPIO_MODER]
			ldr		r0, [rGPIO, #GPIO_PORT_AFIO]
			orr		r0, #RX_PIN_AFIO_VAL:OR:TX_PIN_AFIO_VAL
			str		r0, [rGPIO, #GPIO_PORT_AFIO]

在每个汇编文件中,一个别名只能给一个寄存器,但是反过来不一定。就是说一个寄存器可以有多个别名。

10,MACRO…MEND

可以定义一个类似于C的内联函数或宏函数那样的存在。我叫它是汇编宏。语法是这里打不出来,直接去7-27看就好。

应用如下面这段代码。我定义了一个汇编宏叫wait_for_sr_tc_macro_via_r1 。之后只要在这个文件中需要轮询查sr_tc,就可以用这个宏。

			macro
$label		wait_for_sr_tc_macro_via_r1	
$label.test_sr_tc_m
			ldr		r1, [rUART, #USART_SR]
			tst		r1, #USART_SR_TC
			beq		$label.test_sr_tc_m			
			mend
				
			align 	4
send_ch		proc
			push	callee_regs
			
			load_rUART
			ldr		r2, [rUART, #USART_CR1]
			orr		r2, #USART_CR1_TE
			str 	r2,	[rUART, #USART_CR1]
before_send		wait_for_sr_tc_macro_via_r1	; 	It is important to wait for the tc to be set.
											; 	By checking this bit via DEBUG window is not possible
											;	to find if this bit is set or cleared.
			str		r0, [rUART, #USART_DR]
after_sent		wait_for_sr_tc_macro_via_r1	; 	This is the same as the previous waiting.
			bic		r2, #USART_CR1_TE
			str 	r2,	[rUART, #USART_CR1]				
			pop		callee_regs
			bx 		lr
			endp

如果宏内需要用到跳转,可以用$label指代调用这个宏的那句的标号。

11,ROL、ROR、MOD等运算符

常用的汇编器可以直接使用的运算指令。具体语法参考手册。这里就是强调2点

  1. IMPORTEXTERN进来的符号只能用+和-进行处理,其它的运算符不支持。
  2. 汇编器的这些运算符的运算顺序和C语言是不同的。所以尽可能使用小括号约束。
  3. 不要忘了运算符单词两边的冒号。

A1466W: Operator precedence means that expression would evaluate differently in C

以上的这个警告就是提示,如果运算太长并过于复杂,建议用小括号进行限制一下。
在这里插入图片描述

12,EXPORT、IMPORT、EXTERN

将本文中的符号引出或引入。这个用的很常见。只是要注意,在外部weak定义的符号,如果你想覆盖它,必须把你在文件中定义的符号用export引出去才能真的覆盖。否则这个符号只是在本文中有效。

四、一些体会

灵活使用Directives,可以实现软件的可配置性,并且可以将很多原来由MCU完成的动作提前在上位机上做好。
本文中做的Keil工程可以从这个链接下载:STDIO-App的Keil工程

五、参考文件

  1. 《ARM Developer Suite Assembler Guide》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值