本帖最后由 JackTang1994 于 2022-1-11 16:24 编辑
#申请原创# #技术资源#
资料获取
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开发板
CCP保护寄存器操作
ATmega4809的一些关键性的寄存器被写保护了,操作它们时不能像普通寄存器一样直接将需要设置的值直接写入到相应寄存器中。而是需要通过特定方式进行操作:解锁再操作在写入解锁密钥到CCP寄存器后,必须在4个指令周期内将相关设置写入到相应的外设寄存器(被保护的,属性中有说明)中。因为操作时间关系,要在4个指令周期内操作。所以必须使用汇编代码,因为每一条汇编指令它执行的时间是确定的,而C代码是无法了解其执行时间的长短的。
时钟源设置
在要操作一个MCU前,我们一定要了解它的时钟。因为时钟是一个MCU的心脏,如果时钟配置有问题MCU是无法正常工作的。
查看手册,了解外设时钟源。按照要求设置好时钟:内部时钟10MHZ
主时钟源分频设置
时钟源分频寄存器的属性为Configuration Change Protection(CCP)被保护了。所以对其操作需要使用解锁的方式来操作
FUSE设置(熔断位)
熔断位是用来设置一些出厂默认设置的参数值的寄存器,它只能通过UPID进行修改(也就是只能通过烧录器或者调试器修改)而CPU只能对其进行读操作。
在手册的第7章节Memorys部分,可以查看其具体操作。
比如:芯片内部时钟为什么是20MHZ而不是16MHZ,这个就可以从FUSE默认配置中查看得到结论。
LED引脚配置
查看ATmega4809 Curiosity Nano开发板,确定好LED所使用的引脚以及应该使用什么电平进行操作。如果不懂如何使用,可以查看我以前的**。
查看手册IO外设寄存器,对相应IO进行设置:输出模式
定时器配置
在使用定时器时,需要先确认外设时钟、基本的操作步骤、外设描述。这样就可以对相关外设有一个整体的了解后面使用的时候就会得以应手。一般在操作步骤中都会有对定时器的使用说明,需要使用的寄存器等信息。
定时器设置:普通模式、向上计数、定时时间为500ms
中断使用
中断向量
在中断章节可以查到Atmega4809芯片的中断向量表地址
中断及中断优先级
从CPUINT章节可以了解到ATmega4809的中断执行及优先级。一般我们选择默认配置:不使用Level1优先级,所有外设中断都为Level0优先级。地址高的中断向量优先级低,地址低的中断向量优先级高。
代码编写及说明
示例代码:代码中使用到的一些名称定义全部来自头文件m4809def.inc
; code by jack.tang
; 免责申明:此代码仅供学习使用,没有经过严格测试。
; 不建议用于商业产品,如有问题,概不负责。
.include <m4809def.inc> ; include ATmega4809
; default system clock : 20/3 = 3.333MHZ
.equ unlockKey = 0xD8
;Interrupt Vector.中断向量地址区
.CSEG
.ORG 0x00
rjmp RESET ;0x00地址为RESET复位中断入口
.ORG 0x0E ; TCA0 address. 定时器0溢出中断入口
rjmp TCA0_OVF
;给中断向量表中所有中断预留也空间
; cdoe area
.CSEG
.ORG 0x50 ; code space start address
RESET:
; Init the STACK Pointer. 初始化栈.用于子函数的调用
ldi r16,LOW(RAMEND) ; initialize
out CPU_SPL,r16 ; stack pointer.
ldi r16,HIGH(RAMEND) ; to RAMEND
out CPU_SPH,r16
;Init System Clock. 初始化时钟为内部时钟10MHZ
; Unlock the CCP and Set MCLKCTRLB to 0x01
; 20/2 = 10MHZ
ldi r22, 0x01
ldi r23, unlockKey
ldi r24, LOW(CLKCTRL_MCLKCTRLB)
ldi r25, HIGH(CLKCTRL_MCLKCTRLB)
rcall unlock_ccp ; 调用子函数,当前PC指针会自动被压入栈中保存
; initilaze the IO. 初始化IO. PF5
; led port : PF5
ldi r16, 0x20 ; output mode
sts PORTF_DIR, r16 ; 设置PF5为输出模式
;初始化定时器 Normal模式。 500ms中断一次
ldi r16, 0x4C ;high-8bit value
sts TCA0_SINGLE_PER+1, r16 ; 高8位PER寄存器
ldi r16, 0x4B ;low-8bit value
sts TCA0_SINGLE_PER, r16 ; 低8位PER寄存器
ldi r16, 0x0D ; 设置分频系数。关开启定时器,向上计数
sts TCA0_SINGLE_CTRLA, r16
; 使能TCA0中断
ldi r16, 0x01
sts TCA0_SINGLE_INTCTRL, r16
;中断操作
sei ;开启全局中断
clr r17 ; 将标志位寄存器设置为1
; 主循环
Main_loop:
cpi r17, 0x01 ; 判断r17是否等于0x01.如果不等于则执行跳转指令
brne Main_loop
; Toogle PF5
ldi r16, 0x20 ; 1 cycle
sts PORTF_OUTTGL, r16 ; 2 cycle
clr r17 ; 清零r17
rjmp Main_loop
; TCA0定时器0中断服务函数
TCA0_OVF: ; Timer0 interrrupt. 定时器0中断服务函数 TCA0_SINGLE_INTFLAGS
cli ; 关闭全局中断
ldi r16, 0x01 ;
sts TCA0_SINGLE_INTFLAGS, r16 ; 清除中断标志
ldi r17, 0x01 ; 将r17清零. 设置判断标志。ldi不会影响SEG寄存器值
sei ; 使能全局中断
reti ; 从中断函数中返回到主程序中
; 解锁CCP寄存器
; r22 : Configuration value for Peripherals
; r23 : Store Unlock Key 0xD8 into CCP register
; r24-r25 : Store MCLKCTRLB Peripherals address
unlock_ccp:
movw r30, r24 ; 将r24-r25中的内容复制到r30-r31(Z寄存器)
out CPU_CCP, r23 ; start unlock sequence. 解锁CCP寄存器
st Z, r22 ; r22->Z,将r22寄存器中的值写入到Z寄存器对应的内存中
ret ;子函数返回指令. 保存在栈中的PC指针会被自动弹出
实际效果
正好是1秒闪烁一次,完美的1HZ方波!
下期预告
下一期,准备写下Atmega4809的启动代码。欢迎探讨!^_^
|