本帖最后由 JackTang1994 于 2022-1-13 09:43 编辑
#技术资源# #申请原创#
要求:最好有学习过C语言
AVR汇编器使用文档:https://ww1.microchip.com/downloads/en/DeviceDoc/40001917A.pdf
AVRASM2汇编器下载:http://www.vfx.hu/avr/download/avrasm2.zip
AVR指令集文档:https://www.microchip.com/DS40002198
ATmega4809 Curiosity Nano开发板:https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega4809_Curiosity_Nano_Schematics.pdf
软件环境:MPLAB X IDE v5.5,AVRSAM2汇编器
硬件环境:ATmega4809 Curiosity Nano开发板
汇编文件组成
伪指令:操作汇编器的指令,不会被编译到代码中
代码指令:操作MCU的指令,真正执行的代码。
注释:注释符号有";",有些汇编器支持"//"和"/**/"
符号标签:用于标识地址,类似于C语言中的函数名
以下代码为一个完整的汇编示例,包含上述所列的部分
.include <m4809def.inc>
Start_main:
ldi r23, 0x20 ; Toogle PF5
out PORTF_OUTTGL, r23
rjmp Start_main
什么是伪指令?
答:伪指令,其实不是汇编指令编译后不会被编译成代码。它是用来指导汇编器进行汇编操作的,不同的汇编器使用的伪指令不一样。
什么是指令?
答:指令就是实实在在的代码程序,用于操作处理器(MCU)的。
汇编指令类型
数据传输(内存操作):数据读取,外设操作
程序流程操作(跳转指令):函数调用、跳转等
逻辑、算术运算:+、-、*、/和与、或、非、异或等运算
位操作:左移、右移等操作MCU操作指令:BREAK、NOP等
具体的指令说明需要查看AVR指令集文档
汇编文件格式
*.asm、*.s、*.S、*.850、*.v800等
汇编与C语言区别?
答:汇编代码执行速度快和具体芯片内核有关,不同的芯片内核所使用的汇编指令不同。汇编代码运行环境简单,占用空间少,移植性差。
C语言移植性好与具体的芯片内核无关,易于阅读,占用空间比汇编大些,C语言运行需要相关环境(最简单需要栈空间,复杂的需要堆以及库函数等).汇编语言对硬件的操作性是最灵活的可以任意操作硬件。汇编语言一般用于MCU底层的开发如:启动代码、操作系统底层实现等。C语言可移植性好,一般用于外设驱动的开发如:串口、SPI、网口、USB等驱动。
先不讲AVR的指令集,而是通过一个具体的实例来解释。这样应该更容易让人接受,好的接下来就开始写代码。
程序的主流程很简单:每隔0.5秒翻转一次LED引脚的电平。
程序比较完整的流程图:延时0.5秒的代码分成了两层循环,因为一层循环延时时间不够。
C语言实现
#include <iom4809.h>
int main(void)
{
uint16_t i = 1000;
uint8_t j = 100;
PORTF_DIR = 0x20;
while(1)
{
PORTF_OUTTGL = 0x20;
j = 100;
while(j--)
{
i = 1000;
while(i--);
}
}
}
将C语言转换成汇编实现
从上面C语言代码中,我们可以得到"循环"、”条件判断“、”变量名定义“等部分,而汇编与之对应的指令分别如下:
循环:使用跳转指令和标签
条件判断:使用带条件跳转指令、状态寄存器、标签
变量名定义:使用.def汇编伪指令、普通寄存器
具体代码实现如下:
; code by jack
.include <m4809def.inc> ; 包含ATmega4809头文件。头文件路径可以从我上一篇**了解,可以使用文本编辑工具打开它查看里面的内容。
; default system clock : 20/6 = 3.333MHZ ATmega4809芯片默认的时钟为内部20MHZ,默认分频系数为6
; .def汇编器伪指令。此处用于定义寄存器的别名,后续可以再次用.def修改其名称。
.def mask = r16
.def ledR = r17
.def outLoopR = r18 ; 外层循环数值的数值保存寄存器
.def innLoopRL = r24 ; 内层循环数值的数值高8位数保存寄存器
.def innLoopRH = r25 ; 内层循环数值的数值低8位数保存寄存器
; .equ汇编器伪指令。定义常量,后续不可以再修改类似C语言的const。
.equ oVal = 208 ; 外层循环数值
.equ iVal = 1999 ; 内层循环数值
; .CSEG汇编器伪指令。表示将以下代码放置在代码段。
.CSEG
.ORG 0x00 ; code space开始地址。这个地址可以从芯片手册的Memory章节获取。后续会解释下
; 初始化LED使用的IO引脚
; PF5
; output mode
ldi r23, 0x20 ; 将0x20加载到r23寄存器。PF5对应第PortF控制寄存器中的第6位
sts PORTF_DIR, r23 ; 设置为输出模式
Start_main: ; 符号标签,可以自定名称。这个相当于C语言中的函数名称。它可以表示地址
; Toogle PF5。翻转LED相应的IO引脚电平
ldi r23, 0x20 ; 1 cycle。将0x20数据加载到r23寄存器中
sts PORTF_OUTTGL, r23 ; 2 cycle。将r23寄存器中为数值写入到Data space空间
ldi outLoopR, oVal ; 1 cycle。将oVal数据(208)加载到r23寄存器中
Delay_ms: ; 符号标签,可以自定名称。这个相当于C语言中的函数名称。它可以表示地址
; delay 500ms
ldi innLoopRL, LOW(iVal) ; 1 cycle。 LOW为汇编器内置的函数,只取iVal的低8位数据
ldi innLoopRH, HIGH(iVal) ; 1 cycle HIGH为汇编器内置的函数,只取iVal的高8位数据
; delay ms
delay_us: ; 符号标签,可以自定名称。这个相当于C语言中的函数名称。它可以表示地址
; 2 cycle
; sbiw指令为字操作指令,它能操作两个连续的寄存器且会自动操作Carry位。简单讲就是可以操作16bit的数据,只需要给它低的寄存器
; 因为iVal为16位的数据,上面代码已经将它的低8位和高8位分别保存到了r24,r25寄存器中了。所以这里只需要给r24寄存器。sbiw指令会
; 处理完低8位的数据后自动将r25寄存器中的数据进行减1运算。
sbiw innLoopRL, 1 ; sbiw减法运算。这里表示减1
brne delay_us ; 1 or 2 cycle。条件跳转,判断上一条指令sbiw innLoopRL, 1结果是否不等0
dec outLoopR ; 1 cycle。 自减1。相当于C语言中i--操作
brne Delay_ms ; 1 or 2 cycle。条件跳转,判断上一条指令dec outLoopR, 1结果是否不等0
rjmp Start_main ; 2 cycle。跳转到Start_main标签符号处
关于.ORG伪指令后为什么是0x00地址?
从数据手册的Memory章节可以的Memory map图中可以看出,在Code Space中Flash的开始地址为0x0000
个人理解:关于Code Space与Data Space的区别主要是使用指令的不同,有些指令只能操作Flash。而ATmega4809将Flash区域映射到了Data Space所以可以使用Data Space的指令来操作Flash.
关于汇编器内置的函数有哪些?
这个可以从AVR汇编使用手册中第7章节可以查询到。比如:上述代码中使用到的LOW()、HIGH()函数
实际效果:
|