打印
[PIC®/AVR®/dsPIC®产品]

AVR 8位单片机汇编——定时器中断使用

[复制链接]
2555|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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的启动代码。欢迎探讨!^_^





使用特权

评论回复
沙发
tpgf| | 2022-2-4 17:48 | 只看该作者
涉及的还是很全面的

使用特权

评论回复
板凳
木木guainv| | 2022-2-4 17:53 | 只看该作者
我就闹不明白  汇编的时候指定的地址如何保证没有错误呢

使用特权

评论回复
地板
磨砂| | 2022-2-4 18:00 | 只看该作者
汇编跟单片机的位数是不是联系的很紧密呢

使用特权

评论回复
5
晓伍| | 2022-2-4 18:06 | 只看该作者
如何入手学习汇编语言呢

使用特权

评论回复
6
八层楼| | 2022-2-4 18:13 | 只看该作者
是否需要弄明白存储地址啥的啊

使用特权

评论回复
7
观海| | 2022-2-4 18:29 | 只看该作者
资源获取的途径还是很多的

使用特权

评论回复
8
JackTang1994|  楼主 | 2022-2-8 10:35 | 只看该作者
木木guainv 发表于 2022-2-4 17:53
我就闹不明白  汇编的时候指定的地址如何保证没有错误呢

编译和链接是两回事。程序代码放置在相应的地址处才能正常执行

使用特权

评论回复
9
JackTang1994|  楼主 | 2022-2-8 10:36 | 只看该作者
八层楼 发表于 2022-2-4 18:13
是否需要弄明白存储地址啥的啊

是的。中断入口地址必须要非常清楚,否则芯片无法正常运行中断无法响应

使用特权

评论回复
10
JackTang1994|  楼主 | 2022-2-9 10:19 | 只看该作者
JackTang1994 发表于 2022-2-8 10:35
编译和链接是两回事。程序代码放置在相应的地址处才能正常执行

使用链接脚本或者链接选项,配合汇编器伪指令使用

使用特权

评论回复
11
tabmone| | 2022-2-13 09:32 | 只看该作者
有汇编语言的教程吗?

使用特权

评论回复
12
qiufengsd| | 2022-2-13 10:31 | 只看该作者
汇编语言真的很复杂了。

使用特权

评论回复
13
pzsh| | 2022-2-13 10:45 | 只看该作者
高手通过汇编了解单片机内部资源和详细运行情况,对于运行速度和资源优化具有很大的意义。

使用特权

评论回复
14
JackTang1994|  楼主 | 2022-2-13 10:55 | 只看该作者
tabmone 发表于 2022-2-13 09:32
有汇编语言的教程吗?

网上搜索下。可以看汇编器使用手册、MCU汇编指令集(一般都有指令的使用例程)

使用特权

评论回复
15
JackTang1994|  楼主 | 2022-2-13 10:56 | 只看该作者
磨砂 发表于 2022-2-4 18:00
汇编跟单片机的位数是不是联系的很紧密呢

汇编和单片机内核有关和单片机位数没有关系。不同内核所使用的汇编指令不同

使用特权

评论回复
16
JackTang1994|  楼主 | 2022-2-13 10:58 | 只看该作者
八层楼 发表于 2022-2-4 18:13
是否需要弄明白存储地址啥的啊

是的。大部分都需要写链接脚本或者设置链接选项,但也有一部分芯片不需要,AVR汇编器就是直接用的伪指令来指定代码存储的地址的

使用特权

评论回复
17
nomomy| | 2022-2-13 14:02 | 只看该作者
如何学习汇编语言  

使用特权

评论回复
18
sanxingnote7| | 2022-2-13 14:10 | 只看该作者
AVR 8位单片机和16位的通用吗

使用特权

评论回复
19
cashrwood| | 2022-2-13 14:20 | 只看该作者
汇编的地址是怎么访问呢?

使用特权

评论回复
20
mattlincoln| | 2022-2-13 14:29 | 只看该作者
一直都是C语言开发,没有接触过汇编语言

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

62

帖子

0

粉丝