首页 > 其他分享 >基于ATMega16定时器T1产生PWM的实例

基于ATMega16定时器T1产生PWM的实例

时间:2024-02-19 11:25:30浏览次数:32  
标签:TMP 引脚 LDI T1 ATMega16 PWM TMP1 OUT

本例讨论ATMega16中通过定时器T1产生脉冲波形(含PWM)的具体过程,利用汇编程序实现CTC方式、快速PWM模式、相位修正PWM模式和相频修正PWM模式等实例。定时器T1与定时器T0、T2不一样,它具有16位结构,除了能实现更长时间的定时外,它还具有很多附加功能,比T0、T2要复杂一些。另外,T1还有一个很特别的地方,就是它具有两个独立输出的匹配单元,可同时产生两路不同占空比的PWM波。

1、CTC模式产生脉冲

CTC方式通过对计数值的比较来实现引脚电平的变化。当计数值增加到与比较值相等时,可触发中断,并可在OC1A引脚(PD5)、OC1B引脚(PD4)上实现电平的变低、变高或取反。下面2个实例分别实现了使用系统时钟的固定频率脉冲(无中断)输出和可变频率脉冲(使用中断)输出。

a)通过OC1A引脚(PD5)和OC1B(PD4)输出1kHz、占空比50%的固定方波。汇编代码如下。

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.ORG    $0000
    JMP        RESET             ;复位的向量入口,地址为$00
.ORG    $002A                    ;避开中断向量入口地址,主程序入口地址为$2A
RESET:
    LDI     TMP, $08
    LDI     TMP1, $AC
    OUT     TCCR1B, TMP
    OUT     TCCR1A, TMP1         ;OC1A及OC1B强制输出0
    SBI     DDRD, 4              ;设置PD4为输出方向
    SBI     DDRD, 5              ;设置PD5为输出方向
    LDI     TMP, $50
    LDI     TMP1, $1B
;    LDI     TMP1, $09
    OUT     TCCR1A, TMP          ;设置输出引脚取反    
    OUT     TCCR1B, TMP1         ;设置定时器T1为64分频,CTC模式
    CLR     TMP
    LDI     TMP1, $3E            ;设置定时器T1的初始比较值为$3E,即脉冲初始频率为1kHz
    OUT     ICR1H, TMP                
    OUT     ICR1L, TMP1
;    OUT      OCR1AH, TMP
;    OUT      OCR1AL, TMP1
    CLR     TMP
    OUT     TCNT1H, TMP
    OUT     TCNT1L, TMP           ;计数值清零
LOOP:
    RJMP    LOOP

在PD5和PD4引脚上产生的脉冲波形如下图所示。

b)通过匹配中断不断修改T1的比较值,实现OC1A引脚(PD5)和OC1B(PD4)输出频率不断更新(降低)的方波,脉冲占空比仍然保持为50%。汇编代码如下。

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.DEF    CNT = R18
.CSEG                            ;以下数据放置在Code区域,即Flash中
.ORG    $0000
    JMP     RESET                ;复位的向量入口,地址为$00
;.ORG    $000A
;   RJMP      TIME1_OCR          ;ICR1比较匹配中断跳转
.ORG    $000C
    RJMP      TIME1_OCR          ;OCR1A比较匹配中断跳转
.ORG    $002A                    ;避开中断向量入口地址,主程序入口地址为$2A
RESET:
    LDI     TMP, HIGH(RAMEND)    ;获取RAM空间的最大地址高字节(ATMega16为$04)
    OUT     SPH, TMP             ;高字节送SP高位
    LDI     TMP, LOW(RAMEND)     ;获取RAM空间的最大地址低字节(ATMega16为$5F)
    OUT     SPL, TMP             ;低字节送SP低位
    LDI     TMP, $08
    LDI     TMP1, $AC
    OUT     TCCR1B, TMP
    OUT     TCCR1A, TMP1         ;OC1A及OC1B强制输出0
    SBI     DDRD, 4              ;设置PD4为输出方向
    SBI     DDRD, 5              ;设置PD5为输出方向
    LDI     TMP, $50             ;设置输出引脚取反 
    LDI     TMP1, $09            ;设置定时器T1为64分频,OCR1A比较的CTC模式
;   LDI     TMP, $50             ;设置输出引脚取反 
;   LDI     TMP1, $19            ;设置定时器T1为64分频,ICR1比较的CTC模式
    OUT     TCCR1A, TMP   
    OUT     TCCR1B, TMP1         
    CLR     TMP
    LDI     TMP1, $3E            ;设置定时器T1的初始比较值为$3E,即脉冲初始频率为1kHz
    OUT     OCR1AH, TMP
    OUT     OCR1AL, TMP1
;   OUT     ICR1H, TMP
;   OUT     ICR1L, TMP1
    CLR     TMP
    OUT     TCNT1H, TMP
    OUT     TCNT1L, TMP          ;计数值清零
    IN      TMP, TIMSK           ;获取原TIMSK的值
    SBR     TMP, $10             ;置第4位为1
;   SBR     TMP, $20             ;置第5位为1
    OUT     TIMSK, TMP           ;回写TIMSK,允许OCR1A比较中断
    IN      TMP, TIFR
    SBR     TMP, $10
;   SBR     TMP, $20
    OUT     TIFR, TMP            ;清除比较中断标志
    SEI                          ;打开总中断
    CLR     CNT
LOOP:
    RJMP    LOOP
;OCR1A或ICR1比较中断服务程序
TIME1_OCR:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    INC     CNT                 ;计数值加1
    CPI     CNT, 2              ;计数值与2比较
    BRNE    EXIT                ;计数值未到2时直接返回(保证一个脉冲周期的对称性)
    LDI     CNT, $20            ;设置步进值为$20
    IN      TMP, OCR1AL         ;获取当前OCR1A比较值
;   IN      TMP, ICR1L          ;获取当前ICR1的比较值
    ADD     TMP, CNT            ;当前比较值加$20,增加的步进为$20
    CPI     TMP, $FE            ;当前比较值与$FE比较
    BRNE    N                   ;若比较值未加到$FE,则跳转去更新比较值
    LDI     TMP, $3E            ;若比较值已加到$FE,则比较值恢复初始值
N:  OUT     OCR1AL, TMP         ;更新OCR1A的比较值
;N:  OUT     ICR1L, TMP         ;更新ICR1的比较值
    CLR     CNT                 ;CNT值恢复为0
EXIT:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI                        ;中断返回

在PD5引脚上产生的脉冲波形如下图所示。

下面对程序中的相关部分进行一下说明。
1)定时器T1为16位定时器,所以其TCNT1、OCR1A、ICR1等寄存器均为16位(分为高、低两个8位),在写这些寄存器时,应先写高位再写低位,在读这些寄存器时,应先读低位再读高位。
2)在CTC模式下,定时器T1有两种输出模式,即通过OCR1A寄存器或ICR1寄存器预设比较值,当定时器计数值(TCNT1中的值)达到比较值时,发生匹配事件。这时可实现对引脚PD5、PD4上的电平进行操作,具体的操作方式由寄存器TCCR1A来配置,可以有引脚置低电平、引脚置高电平和引脚电平取反等3种形式。
3)脉冲输出引脚固定在PD5、PD4两个引脚上,不能使用其他引脚。在CTC模式下,两个引脚输出同样的脉冲波形。
4)计数值匹配之后TCNT1会自动清零,反复循环,若不使用比较中断,整个CTC过程无需CPU干预。
5)当不使用比较中断时,由于无法更改比较值,所以只能实现固定频率的脉冲输出。使用比较中断时,可在中断服务程序中更新比较值,从而实现对输出频率的更改。
6)在初始化阶段,通过强制写TCCR1A寄存器的第3位(FOC1A),并配置为匹配时输出低电平的方式,把PD5、PD4强制置低电平,即脉冲的起始为低电平(也可为高电平,看需求设置)。
7)在初始化阶段,应该先配置OC1A的输出电平,再配置PD5、PD4为输出方向。
8)在比较匹配中断服务程序中,对状态寄存器SREG的值采取了栈保护(一般中断程序都应如此)。
9)为了可以看到明显的脉冲宽度变化,在使用系统时钟的程序中,对比较值的变化步长取$20(即32),一共可比较6次,在引脚PD5、PD4上可看到6个不同宽度的脉冲。
10)为了保证输出脉冲在每个完整周期内的占空比均为50%,在中断服务程序中加入了比较判断,以保证每两次比较中断之后才更改一次比较值。所以严格来说,CTC并不属于PWM方式,但这种方式更灵活。
11)上述程序是通过OCR1A的比较输出方式,把程序中注释的语句释放,再注释掉相邻的相关语句,就可转变成通过ICR1的比较输出方式。

2、快速PWM模式

快速PWM模式也是通过对计数值的比较来实现引脚电平的变化,但与CTC方式不同的是,CTC方式在比较匹配后计数值就清零了,而快速PWM模式在比较匹配之后,计数值依然增加,直到溢出后才清零。在匹配时输出引脚置低(或高)电平,在溢出时输出引脚置高(或低)电平。这样每次计数值从初始值到溢出就成为一个完整的脉冲周期,而比较值可在初始值到溢出值之间移动,方便更改脉冲的占空比。特别地,在定时器T1中,可通过设置两个比较值(OCR1A、OCR1B)来同时匹配输出两路脉冲波形。另外,还可以选择固定频率和可变频率两种方式来产生PWM波。

a)下面的实例采用固定频率方式,同时从OC1A引脚(PD5)、OC1B(PD4)输出频率为固定的488Hz,占空比不同且可变的PWM脉冲,汇编代码如下。

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.DEF    CNT = R18
.DEF    CNT1 = R19
.DEF    FLAG = R20
.ORG    $0000
    JMP        RESET             ;复位的向量入口,地址为$00
.ORG    $000C
    RJMP       TIME1_OCR1A       ;定时器T1的OCR1A比较匹配中断跳转
.ORG    $000E
    RJMP       TIME1_OCR1B       ;定时器T1的OCR1B比较匹配中断跳转
.ORG    $002A                    ;避开中断向量入口地址,主程序入口地址为$2A
RESET:
    LDI     TMP, HIGH(RAMEND)    ;获取RAM空间的最大地址高字节(ATMega16为$04)
    LDI     TMP1, LOW(RAMEND)    ;获取RAM空间的最大地址低字节(ATMega16为$5F)
    OUT     SPH, TMP             ;高字节送SP高位  
    OUT     SPL, TMP1            ;低字节送SP低位
    OUT     TCCR1B, TMP
    OUT     TCCR1A, TMP1         ;OC1A及OC1B强制输出0
    SBI     DDRD, 4              ;设置PD4为输出方向
    SBI     DDRD, 5              ;设置PD5为输出方向
    LDI     TMP, $A1
    LDI     TMP1, $0B
    OUT     TCCR1A, TMP    
    OUT     TCCR1B, TMP1         ;设置定时器T1为64分频,8位快速PWM模式,OC1A、OC1B都是匹配置0溢出置1
    IN      TMP, TIMSK           ;获取原TIMSK的值
    SBR     TMP, $18             ;置第3、4位为1
    OUT     TIMSK, TMP           ;回写TIMSK,允许OCR1A、OCR1B比较中断
    IN      TMP, TIFR
    SBR     TMP, $18
    OUT     TIFR, TMP            ;清除比较中断标志
    CLR     TMP1
    LDI     TMP, $10
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP          ;给OCR1A赋初值
    CLR     TMP1
    LDI     TMP, $80
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初值
    CLR     CNT                  ;计数值清零
    CLR     CNT1                 ;计数值清零
    CLR     FLAG                 ;标志位清零
    SEI                          ;开启总中断
LOOP:
    RJMP    LOOP
;OCR1A匹配中断服务程序
TIME1_OCR1A:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    INC     CNT
    CPI     CNT, $6
    BRNE    EXIT                ;计数不到6次则直接返回,用于占空比变化速度控制
    CLR     CNT                 ;到6次先清零
    SBRS    FLAG, $0            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD
    IN      TMP, OCR1AL         ;获取当前比较值
    DEC     TMP                 ;减1
    CPI     TMP, $10            ;是否减到$10
    BRNE    NEXT                ;未到$10则更新比较值后返回
    CBR     FLAG, $1            ;已到$10则表示到最底,置标志位为0
    OUT     OCR1AL, TMP         ;更新比较值一次比较值后返回
    RJMP    EXIT
FORWARD:
    IN      TMP, OCR1AL         ;获取当前比较值
    INC     TMP                 ;加1
    CPI     TMP, $80            ;是否加到$80
    BRNE    NEXT                ;未到$80则更新比较值后返回
    SBR     FLAG, $1            ;已到$80则表示到最高,置标志位为1
NEXT:
    OUT     OCR1AL, TMP         ;更新比较值
EXIT:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI
;OCR1B匹配中断服务程序
TIME1_OCR1B:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    INC     CNT1
    CPI     CNT1, $6
    BRNE    EXIT1               ;计数不到6次则直接返回,用于占空比变化速度控制
    CLR     CNT1                ;到6次先清零
    SBRS    FLAG, $1            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD1
    IN      TMP, OCR1BL         ;获取当前比较值
    DEC     TMP                 ;减1
    CPI     TMP, $80            ;是否减到$80
    BRNE    NEXT1               ;未到$80则更新比较值后返回
    CBR     FLAG, $2            ;已到$80则表示到最底,置标志位为0
    OUT     OCR1BL, TMP         ;更新比较值一次比较值后返回
    RJMP    EXIT1
FORWARD1:
    IN      TMP, OCR1BL         ;获取当前比较值
    INC     TMP                 ;加1
    CPI     TMP, $F0            ;是否加到$F0
    BRNE    NEXT1               ;未到$F0则更新比较值后返回
    SBR     FLAG, $2            ;已到$F0则表示到最高,置标志位为1
NEXT1:
    OUT     OCR1BL, TMP         ;更新比较值
EXIT1:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI

在PD5和PD4引脚上产生的PWM波形如下面两张图片所示,图片为占空比变化截取的其中两张,上半部分为OCR1A的波形,下半部分为OCR1B的波形。可明显看出占空比的不同(频率相同)。

b)下面的实例采用可变频率方式,同时从OC1A引脚(PD5)、OC1B(PD4)输出频率为1kHz,占空比不同且可变的PWM脉冲,汇编代码如下。

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.DEF    CNT = R18
.DEF    CNT1 = R19
.DEF    FLAG = R20
.ORG    $0000
    JMP        RESET             ;复位的向量入口,地址为$00
.ORG    $000C
    RJMP       TIME1_OCR1A       ;定时器T1的OCR1A比较匹配中断跳转
.ORG    $000E
    RJMP       TIME1_OCR1B       ;定时器T1的OCR1B比较匹配中断跳转
.ORG    $002A                    ;避开中断向量入口地址,主程序入口地址为$2A
RESET:
    LDI     TMP, HIGH(RAMEND)    ;获取RAM空间的最大地址高字节(ATMega16为$04)
    LDI     TMP1, LOW(RAMEND)    ;获取RAM空间的最大地址低字节(ATMega16为$5F)
    OUT     SPH, TMP             ;高字节送SP高位  
    OUT     SPL, TMP1            ;低字节送SP低位
    OUT     TCCR1B, TMP
    OUT     TCCR1A, TMP1         ;OC1A及OC1B强制输出0
    SBI     DDRD, 4              ;设置PD4为输出方向
    SBI     DDRD, 5              ;设置PD5为输出方向
    LDI     TMP, $A2
    LDI     TMP1, $1B
    OUT     TCCR1A, TMP    
    OUT     TCCR1B, TMP1         ;设置定时器T1为64分频,8位快速PWM模式,OC1A、OC1B都是匹配置0溢出置1
    IN      TMP, TIMSK           ;获取原TIMSK的值
    SBR     TMP, $18             ;置第3、4位为1
    OUT     TIMSK, TMP           ;回写TIMSK,允许OCR1A、OCR1B比较中断
    IN      TMP, TIFR
    SBR     TMP, $18
    OUT     TIFR, TMP            ;清除比较中断标志
    CLR     TMP1
    LDI     TMP, $10
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP          ;给OCR1A赋初值
    CLR     TMP1
    LDI     TMP, $3E
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初值
    CLR     TMP1
    LDI     TMP, $7D
    OUT     ICR1H, TMP1          
    OUT     ICR1L, TMP           ;给ICR1赋初值
    CLR     CNT                  ;计数值清零
    CLR     CNT1                 ;计数值清零
    CLR     FLAG                 ;标志位清零
    SEI                          ;开启总中断
LOOP:
    RJMP    LOOP
;OCR1A匹配中断服务程序
TIME1_OCR1A:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    INC     CNT
    CPI     CNT, $10
    BRNE    EXIT                ;计数不到16次则直接返回,用于占空比变化速度控制
    CLR     CNT                 ;到16次先清零
    SBRS    FLAG, $0            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD
    IN      TMP, OCR1AL         ;获取当前比较值
    DEC     TMP                 ;减1
    CPI     TMP, $10            ;是否减到$10
    BRNE    NEXT                ;未到$10则更新比较值后返回
    CBR     FLAG, $1            ;已到$10则表示到最底,置标志位为0
    OUT     OCR1AL, TMP         ;更新比较值一次比较值后返回
    RJMP    EXIT
FORWARD:
    IN      TMP, OCR1AL         ;获取当前比较值
    INC     TMP                 ;加1
    CPI     TMP, $3E            ;是否加到$3E
    BRNE    NEXT                ;未到$3E则更新比较值后返回
    SBR     FLAG, $1            ;已到$3E则表示到最高,置标志位为1
NEXT:
    OUT     OCR1AL, TMP         ;更新比较值
EXIT:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI
;OCR1B匹配中断服务程序
TIME1_OCR1B:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    INC     CNT1
    CPI     CNT1, $10
    BRNE    EXIT1               ;计数不到16次则直接返回,用于占空比变化速度控制
    CLR     CNT1                ;到16次先清零
    SBRS    FLAG, $1            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD1
    IN      TMP, OCR1BL         ;获取当前比较值
    DEC     TMP                 ;减1
    CPI     TMP, $40            ;是否减到$40
    BRNE    NEXT1               ;未到$40则更新比较值后返回
    CBR     FLAG, $2            ;已到$40则表示到最底,置标志位为0
    OUT     OCR1BL, TMP         ;更新比较值一次比较值后返回
    RJMP    EXIT1
FORWARD1:
    IN      TMP, OCR1BL         ;获取当前比较值
    INC     TMP                 ;加1
    CPI     TMP, $70            ;是否加到$70
    BRNE    NEXT1               ;未到$70则更新比较值后返回
    SBR     FLAG, $2            ;已到$70则表示到最高,置标志位为1
NEXT1:
    OUT     OCR1BL, TMP         ;更新比较值
EXIT1:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI

在PD5和PD4引脚上产生的PWM波形如下面两张图片所示,图片为占空比变化截取的其中两张,上半部分为OCR1A的波形,下半部分为OCR1B的波形。可明显看出占空比的不同(频率相同)。

下面对程序中的相关部分进行一下说明。
1)在快速PWM模式下,定时器T1通过OCR1A和OCR1B两个寄存器来设置比较值,用于调整脉冲的占空比。当定时器计数值(TCNT1中的值)达到比较值时,发生匹配事件,这时可对引脚PD5和PD4上的电平进行操作。然后计数值继续增加直到溢出,溢出时也可对引脚PD5和PD4上的电平进行操作。引脚电平具体的操作方式由寄存器TCCR1A来配置,可以选择在匹配时引脚置低电平、溢出时引脚置高电平,或相反。
2)脉冲输出引脚固定在PD5、PD4两个引脚上,不能使用其他引脚。在CTC模式下,两个引脚输出同样的脉冲波形。
3)计数值匹配之后TCNT1会继续增加直到溢出,如此反复循环,若不使用比较中断和溢出中断,整个PWM过程无需CPU干预。
4)T1在快速PWM模式下,可以选择固定频率(计数最大值分别为8位、9位或10位三种)方式,也可选择可变频率(计数最大值分别由寄存器OCR1A和ICR1来确定)方式。若选择可变频率方式,则会占用一项T1的其他功能(程序中使用ICR1,占用了T1的输入捕获功能)。
5)如果要更新比较值(OCR1A、OCR1B的值),操作可放在比较中断服务程序中进行,但更新的值不会立即生效,而是要等到此次计数溢出之后,才会生效,这样能保证波形的对称性。
6)为了可以在示波器上看到明显的脉冲占空比的变化,上述程序中,对比较值的更新设置在触发比较中断6次(或16次)之后再进行。这样可有效地减慢占空比的变化过程,在引脚PD5、PD4上可看到PWM的占空比不继增加,又不继减小,如此往复循环。

3、相位修正PWM模式

相位修正PWM模式也是通过对计数值的比较来实现引脚电平的变化,但与快速PWM模式不同的是,相位修正PWM模式不仅使用了计数的增加部分(上升段),同时使用了计数的减小部分(下降段)。即当TCNT1的值增加到最大值(固定或可变)后,计数值并不清零,而是又逐渐减小,直到最小值($0000),然后又增加,如此循环。而在上升段和下降段均可与OCR1A或OCR1B进行比较匹配,匹配时引脚PD5或PD4上的电平都可进行操作。在计数回到最小值($0000)时,可触发溢出中断。在比较匹配时,可触发比较中断。

a)下面的实例采用固定频率方式,同时从OC1A引脚(PD5)、OC1B(PD4)输出频率为固定的244Hz,占空比不同且可变的PWM脉冲,汇编代码如下。

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.DEF    CNT = R18
.DEF    CNT1 = R19
.DEF    FLAG = R20
.ORG    $0000
    JMP        RESET             ;复位的向量入口,地址为$00
.ORG    $0010
    RJMP       TIME1_OVF         ;定时器T1的溢出中断跳转
.ORG    $002A                    ;避开中断向量入口地址,主程序入口地址为$2A
RESET:
    LDI     TMP, HIGH(RAMEND)    ;获取RAM空间的最大地址高字节(ATMega16为$04)
    LDI     TMP1, LOW(RAMEND)    ;获取RAM空间的最大地址低字节(ATMega16为$5F)
    OUT     SPH, TMP             ;高字节送SP高位  
    OUT     SPL, TMP1            ;低字节送SP低位
    LDI     TMP, $00
    LDI     TMP1, $FC
    OUT     TCCR1B, TMP
    OUT     TCCR1A, TMP1         ;OC1A及OC1B强制输出1
    SBI     DDRD, 4              ;设置PD4为输出方向
    SBI     DDRD, 5              ;设置PD5为输出方向
    LDI     TMP, $A1
    LDI     TMP1, $03
    OUT     TCCR1A, TMP    
    OUT     TCCR1B, TMP1         ;设置定时器T1为64分频,8位相位修正PWM模式,OC1A、OC1B都是上升匹配置0,下降匹配置1
    IN      TMP, TIMSK           ;获取原TIMSK的值
    SBR     TMP, $4              ;置第2位为1
    OUT     TIMSK, TMP           ;回写TIMSK,允许T1溢出中断
    IN      TMP, TIFR
    SBR     TMP, $4
    OUT     TIFR, TMP            ;清除溢出中断标志
    CLR     TMP1
    LDI     TMP, $10
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP          ;给OCR1A赋初值
    CLR     TMP1
    LDI     TMP, $80
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初值
    CLR     CNT                  ;计数值清零
    CLR     CNT1                 ;计数值清零
    CLR     FLAG                 ;标志位清零
    SEI                          ;开启总中断
LOOP:
    RJMP    LOOP
;T1溢出中断服务程序
TIME1_OVF:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    INC     CNT
    CPI     CNT, $04
    BRNE    EXIT                ;计数不到4次则直接返回,用于占空比变化速度控制
    CLR     CNT                 ;到4次先清零
    SBRS    FLAG, $0            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD
    IN      TMP, OCR1AL         ;获取当前比较值
    DEC     TMP                 ;减1
    CPI     TMP, $10            ;是否减到$10
    BRNE    NEXT                ;未到$10则更新比较值后返回
    CBR     FLAG, $1            ;已到$10则表示到最底,置标志位为0
    OUT     OCR1AL, TMP         ;更新比较值一次比较值后返回
    RJMP    EXIT
FORWARD:
    IN      TMP, OCR1AL         ;获取当前比较值
    INC     TMP                 ;加1
    CPI     TMP, $80            ;是否加到$80
    BRNE    NEXT                ;未到$80则更新比较值后返回
    SBR     FLAG, $1            ;已到$80则表示到最高,置标志位为1
NEXT:
    OUT     OCR1AL, TMP         ;更新比较值
EXIT:
    INC     CNT1
    CPI     CNT1, $04
    BRNE    EXIT1               ;计数不到4次则直接返回,用于占空比变化速度控制
    CLR     CNT1                ;到4次先清零
    SBRS    FLAG, $1            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD1
    IN      TMP, OCR1BL         ;获取当前比较值
    DEC     TMP                 ;减1
    CPI     TMP, $80            ;是否减到$80
    BRNE    NEXT1               ;未到$80则更新比较值后返回
    CBR     FLAG, $2            ;已到$80则表示到最底,置标志位为0
    OUT     OCR1BL, TMP         ;更新比较值一次比较值后返回
    RJMP    EXIT1
FORWARD1:
    IN      TMP, OCR1BL         ;获取当前比较值
    INC     TMP                 ;加1
    CPI     TMP, $F0            ;是否加到$F0
    BRNE    NEXT1               ;未到$F0则更新比较值后返回
    SBR     FLAG, $2            ;已到$F0则表示到最高,置标志位为1
NEXT1:
    OUT     OCR1BL, TMP         ;更新比较值
EXIT1:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI

在PD5和PD4引脚上产生的PWM波形如下面两张图片所示,图片为占空比变化截取的其中两张,上半部分为OCR1A的波形,下半部分为OCR1B的波形。可明显看出占空比的不同(频率相同)。对比快速PWM模式的图片可以看出,相位修正PWM模式的周期要更长一些,即频率要低一些。

b)下面的实例采用可变频率方式,同时从OC1A引脚(PD5)、OC1B(PD4)输出频率为1kHz,占空比不同且可变的PWM脉冲,汇编代码如下。

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.DEF    CNT = R18
.DEF    CNT1 = R19
.DEF    FLAG = R20
.DEF    ONE = R21
.ORG    $0000
    JMP        RESET             ;复位的向量入口,地址为$00
.ORG    $0010
    RJMP       TIME1_OVF         ;定时器T1的溢出中断跳转
.ORG    $002A                    ;避开中断向量入口地址,主程序入口地址为$2A
RESET:
    LDI     TMP, HIGH(RAMEND)    ;获取RAM空间的最大地址高字节(ATMega16为$04)
    LDI     TMP1, LOW(RAMEND)    ;获取RAM空间的最大地址低字节(ATMega16为$5F)
    OUT     SPH, TMP             ;高字节送SP高位  
    OUT     SPL, TMP1            ;低字节送SP低位
    LDI     TMP, $00
    LDI     TMP1, $FC
    OUT     TCCR1B, TMP
    OUT     TCCR1A, TMP1         ;OC1A及OC1B强制输出1
    SBI     DDRD, 4              ;设置PD4为输出方向
    SBI     DDRD, 5              ;设置PD5为输出方向
    LDI     TMP, $A2
    LDI     TMP1, $12
    OUT     TCCR1A, TMP    
    OUT     TCCR1B, TMP1         ;设置定时器T1为8分频,ICR1为TOP的相位修正PWM模式,OC1A、OC1B都是上升匹配置0,下降匹配置1
    IN      TMP, TIMSK           ;获取原TIMSK的值
    SBR     TMP, $4              ;置第2位为1
    OUT     TIMSK, TMP           ;回写TIMSK,允许T1溢出中断
    IN      TMP, TIFR
    SBR     TMP, $4
    OUT     TIFR, TMP            ;清除比较中断标志
    CLR     TMP1
    LDI     TMP, $20
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP          ;给OCR1A赋初始比较值
    CLR     TMP1
    LDI     TMP, $FA
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初始比较值
    LDI     TMP1, $01
    LDI     TMP, $F4
    OUT     ICR1H, TMP1
    OUT     ICR1L, TMP           ;给ICR1赋值,确定PWM的频率(1kHz)
    CLR     CNT                  ;计数值清零
    CLR     CNT1                 ;计数值清零
    CLR     FLAG                 ;标志位清零
    LDI     ONE, $01             ;赋值1,用于累加(减)
    SEI                          ;开启总中断
LOOP:
    RJMP    LOOP
;T1溢出中断服务程序
TIME1_OVF:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    INC     CNT
    CPI     CNT, $0A
    BRNE    EXIT                ;计数不到10次则直接返回,用于占空比变化速度控制
    CLR     CNT                 ;到10次先清零
    SBRS    FLAG, $0            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD
    IN      TMP, OCR1AL         ;获取当前比较值
    IN      TMP1, OCR1AH
    DEC     TMP                 ;减1
    CPI     TMP, $20            ;是否减到$20
    BRNE    NEXT                ;未到$20则更新比较值后返回
    CBR     FLAG, $1            ;已到$20则表示到最底,置标志位为0
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;更新比较值一次比较值后返回
    RJMP    EXIT
FORWARD:
    IN      TMP1, OCR1AH
    IN      TMP, OCR1AL         ;获取当前比较值
    INC     TMP                 ;加1
    CPI     TMP, $FA            ;是否加到$FA
    BRNE    NEXT                ;未到$FA则更新比较值后返回
    SBR     FLAG, $1            ;已到$FA则表示到最高,置标志位为1
NEXT:
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;更新比较值
EXIT:
    INC     CNT1
    CPI     CNT1, $0A
    BRNE    EXIT1               ;计数不到10次则直接返回,用于占空比变化速度控制
    CLR     CNT1                ;到10次先清零
    SBRS    FLAG, $1            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD1
    IN      TMP, OCR1BL         ;获取当前比较值
    IN      TMP1, OCR1BH
    SUB     TMP, ONE            ;减1
    BRCS    M1                  ;进/借位为1
    CPI     TMP1, $00
    BREQ    M2
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
M1: CLC                         ;清进/借位
    CLR     TMP1
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP
    RJMP    EXIT1
M2: CPI     TMP, $FA
    BREQ    M3
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
M3: CBR     FLAG, $2            ;已到$FA则表示到最底,置标志位为0
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
FORWARD1:
    IN      TMP, OCR1BL         ;获取当前比较值
    IN      TMP1, OCR1BH
    ADD     TMP, ONE            ;加1
    BRCS    N1                  ;进/借位为1
    CPI     TMP1, $01
    BREQ    N2
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
N1: CLC                         ;清进/借位
    LDI     TMP1, $01
    CLR     TMP
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;给OCR1B赋初值
    RJMP    EXIT1
N2: CPI     TMP, $D4
    BREQ    N3
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
N3: SBR     FLAG, $2            ;已到$1D4则表示到最高,置标志位为1
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
EXIT1:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI

在PD5和PD4引脚上产生的PWM波形形式上与前面例a(固定频率)的一致,只是频率设置成了1kHz。

下面对程序中的相关部分进行一下说明。
1)在相位修正PWM模式下,定时器计数值(TCNT1中的值)从0开始逐渐增加到最大值(固定或可变),然后又逐渐减小,直到最小值0。即在相位修正模式下,计数过程形成了“双斜坡”,所以产生的PWM频率要比快速PWM模式的低。
2)在斜坡的上升段,如果计数值(TCNT1中的值)与比较值(OCR1A、OCR1B的值)匹配成功,可对引脚PD5和PD4上的电平进行操作。在斜坡的下降段,如果计数值与比较值匹配成功,也可对引脚PD5和PD4上的电平进行操作。引脚电平具体的操作方式由寄存器TCCR1A来配置,可以选择在上升段匹配时引脚置低电平,在下降段匹配时引脚置高电平,或者相反。所以,在一个双斜坡周期内,会有两次比较,可通过比较值来调整PWM的占空比。
3)脉冲输出引脚固定在PD5、PD4两个引脚上,不能使用其他引脚。
4)在匹配成功时可触发比较中断,在计数值减小到0时可触发溢出中断。若不使用比较中断和溢出中断,则整个PWM过程无需CPU干预。
5)T1在相位修正PWM模式下,可以选择固定频率(计数最大值分别为8位、9位或10位三种)方式,也可选择可变频率(计数最大值分别由寄存器OCR1A和ICR1来确定)方式。若选择可变频率方式,则会占用一项T1的其他功能(程序中使用ICR1,占用了T1的输入捕获功能)。
6)如果要更新比较值(OCR1A、OCR1B的值),操作可放在溢出中断服务程序中进行,但更新的值不会立即生效,而是要等到此次计数值达到最大值(TOP)之后,才会更新,这样能保证PWM波形的对称性。
7)如果要更新TOP的值(即改变频率),虽然OCR1A或ICR1寄存器都可以做到,但若要经常动态的改变频率,还是推荐使用OCR1A寄存器,因为它具有双缓冲结构,而ICR1寄存器不具备。
8)所谓的相位修正,其实就是相位可调,由于OCR1A、OCR1B的值的值可以更改,所以脉冲的相位也就随之更改了。但如果只是单个PWM输出,则相位修正没有太大的实际意义,可认为仅仅比快速模式降低了频率而已。
9)为了可以在示波器上看到明显的脉冲占空比的变化,上述程序中,对比较值的更新设置在触发比较中断4次(或10次)之后再进行。这样可减慢占空比的变化过程,在引脚PD5和PD4上可看到PWM的占空比不继增加,又不继减小,如此往复循环。

4、相频修正PWM模式

相频修正PWM模式即相位频率修正PWM模式,它是在相位修正PWM模式的基础上,通过把匹配值的更新放在计数的最低处,从而保证了脉冲波形在双斜坡两边始终保持对称,这对于要经常需要改变最高点的值时,保持波形的精度很有利。如果最高点保持不变,那它与相位修正模式没有什么区别。相频修正PWM模式是定时器T1所特有的模式。

下面的实例采用OCR1A为TOP,通过OC1B(PD4)输出连续频率变化的PWM脉冲,汇编代码如下。

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.DEF    FLAG = R18
.DEF    ONE = R19
.ORG    $0000
    JMP        RESET             ;复位的向量入口,地址为$00
.ORG    $000C
    RJMP       TIME1_OCR1A       ;OCR1A比较中断跳转
.ORG    $002A                    ;避开中断向量入口地址,主程序入口地址为$2A
RESET:
    LDI     TMP, HIGH(RAMEND)    ;获取RAM空间的最大地址高字节(ATMega16为$04)
    LDI     TMP1, LOW(RAMEND)    ;获取RAM空间的最大地址低字节(ATMega16为$5F)
    OUT     SPH, TMP             ;高字节送SP高位  
    OUT     SPL, TMP1            ;低字节送SP低位
    LDI     TMP, $00
    LDI     TMP1, $34
    OUT     TCCR1B, TMP
    OUT     TCCR1A, TMP1         ;OC1B强制输出1
    SBI     DDRD, 4              ;设置PD4为输出方向
    LDI     TMP, $21
    LDI     TMP1, $12
    OUT     TCCR1A, TMP    
    OUT     TCCR1B, TMP1         ;设置定时器T1为8分频,OC1A为TOP的相频修正PWM模式,OC1B都是上升匹配置0,下降匹配置1
    IN      TMP, TIMSK           ;获取原TIMSK的值
    SBR     TMP, $10             ;置第4位为1
    OUT     TIMSK, TMP           ;回写TIMSK,允许OCR1A比较中断
    IN      TMP, TIFR
    SBR     TMP, $10
    OUT     TIFR, TMP            ;清除比较中断标志
    LDI     TMP1, $09
    LDI     TMP, $FF
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP          ;给OCR1A赋初值
    LDI     TMP1, $04
    LDI     TMP, $FF
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初值
    LDI     FLAG, $01            ;标志位置1
    LDI     ONE, $01
    SEI                          ;开启总中断
LOOP:
    RJMP    LOOP
;OCR1A比较中断服务程序
TIME1_OCR1A:
    IN      TMP, SREG
    PUSH    TMP
    SBRS    FLAG, $0            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD
    IN      TMP, OCR1AL         ;获取当前TOP值
    IN      TMP1, OCR1AH
    SUB     TMP, ONE            ;减1
    BRCS    M1                  ;进/借位为1
    CPI     TMP1, $00
    BREQ    M2
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;更新TOP值
    RCALL   DIV2                ;更新比较值
    RJMP    EXIT
M1: CLC                         ;清进/借位
    SUB     TMP1, ONE
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP
    RCALL   DIV2                ;更新比较值
    RJMP    EXIT
M2: CPI     TMP, $F0
    BREQ    M3
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;更新TOP值
    LSR     TMP
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT
M3: CBR     FLAG, $1            ;已到$FA则表示到最底,置标志位为0
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;更新TOP值
    LSR     TMP
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT
FORWARD:
    IN      TMP, OCR1AL         ;获取当前比较值
    IN      TMP1, OCR1AH
    ADD     TMP, ONE            ;加1
    BRCS    N1                  ;进/借位为1
    CPI     TMP1, $09
    BREQ    N2
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;更新TOP值
    RCALL   DIV2                ;更新比较值
    RJMP    EXIT
N1: CLC                         ;清进/借位
    ADD     TMP1, ONE
    CLR     TMP
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;给OCR1B赋初值
    RCALL   DIV2                ;更新比较值
    RJMP    EXIT
N2: CPI     TMP, $FF
    BREQ    N3
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;更新TOP值
    RCALL   DIV2
    RJMP    EXIT
N3: SBR     FLAG, $1            ;已到$4E2则表示到最高,置标志位为1
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP         ;更新TOP值
    RCALL   DIV2                ;更新比较值
EXIT:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI
;比较值为TOP值的一半
DIV2:
    CLC                         ;进位位清零
    ROR     TMP1                ;高8位带进位位右移
    ROR     TMP                 ;低8位带进位位右移
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RET

在PD4引脚上产生的PWM波形如下面两张图片所示,上张为频率较低的波形,下张为频率较高的波形。可明显看出周期的变化。

下面对程序中的相关部分进行一下说明。
1)相位频率修正PWM模式为定时器T1所特有,其他定时器不具备该功能,使用它可连续的更改PWM的频率。
2)在相位频率修正PWM模式下,定时器计数值(TCNT1中的值)从0开始逐渐增加到最大值(TOP),然后又逐渐减小,直到最小值0。即在相位频率修正模式下,计数过程形成了“双斜坡”,所以产生的PWM频率要比快速PWM模式的低。
3)在斜坡的上升段,如果计数值(TCNT1中的值)与比较值(OCR1A、OCR1B的值)匹配成功,可对引脚PD5和PD4上的电平进行操作。在斜坡的下降段,如果计数值与比较值匹配成功,也可对引脚PD5和PD4上的电平进行操作。引脚电平具体的操作方式由寄存器TCCR1A来配置,可以选择在上升段匹配时引脚置低电平,在下降段匹配时引脚置高电平,或者相反。所以,在一个双斜坡周期内,会有两次比较,可通过比较值来调整PWM的占空比。
4)相位频率修正PWM模式的频率只能是可变的,由寄存器OCR1A和ICR1来决定TOP的值。其中,OCR1A具有双缓冲结构,特别适用于经常更改频率的场合,但会失去了一个比较匹配的功能。使用ICR1时,一般会让其频率固定。
5)脉冲输出引脚固定在PD4或PD5引脚上,不能使用其他引脚。
6)在匹配成功时可触发比较中断(包括使用OCR1A作为TOP时),在计数值减小到0时可触发溢出中断。若要不断地更改TOP值和比较值,建议放在TOP进行(OCR1A或ICR1中断程序中),更新会在BUTTOM(底部)时发生,这样能让输出的波形保持对称。若不使用比较中断和溢出中断,则整个PWM过程无需CPU干预。
7)上述程序中,为了让输出的脉冲始终保持50%的占空比,比较值OCR1B取TOP值的一半。但由于AVR并没有提供除法指令,所以程序采取了整体(16位)右移1位的形式。当高8位为0时,低8位逻辑右移1位即可。当高8位不为0时,先进行高8位右移,并把最低位移入到进位位C中(最高位置0),然后低8位再进行带进位位的右移即可。

下面来讨论一下以上所有程序代码中用到的寄存器。

本部分使用到了AVR中I/O空间的8个寄存器,即SPH、SPL、DDRD、PORTD、TCNT1、TCCR1A、TCCR1B和TIMSK。其中SPH、SPL、DDRD、PORTD等4个寄存器的介绍可参考“基于ATMega16的流水灯实例”一文,TCNT1寄存器可参考“基于ATMega16的数码管动态扫描实例”一文。下面来看TCCR1A和TCCR1B两个寄存器,它们同属于定时器T1的控制寄存器,初始值为全0,具体如下表所示。

在TCCR1A中,高4位分别用来确定两个匹配输出引脚(OC1A、OC1B)的电平,其配置与定时器T0的一样,具体可参考“基于ATMega16定时器T0产生PWM的实例”一文。第3、2两位(FOC1A、FOC1B)是两个匹配输出引脚的强制输出比较位,用来在非PWM模式(CTC模式)下强制形成一次匹配事件,在PWM模式下该位必须写0。第1、0两位和TCCR1B中的第4、3两位一共组成了4位(WGM13~WGM10),用来选择T1的工作模式,一共有15种模式,具体如下表所示。

在TCCR1B中的低3位(CS12~CS10)用来选择时钟的分频值,具体可参考“基于ATMega16的数码管动态扫描实例”一文。

接下来看TIMSK寄存器,它是定时器的中断屏蔽寄存器,初始值为全0,具体如下表所示。

表中的第5~2位分别是定时器T1的输入捕获中断使能位、OCR1A匹配中断使能位、OCR1B匹配中断使能位和溢出中断使能控制位,把相应的位置1就可以使能对应的中断(同时还要使能总中断)。

最后看TIFR寄存器,它是中断标志寄存器,为T0、T1和T2所共有,初始值为全0,具体如下表所示。

其中的第5~2位分别用来标志T1的输入捕获中断、OCR1A匹配中断、OCR1B匹配中断和溢出中断,当定时器有相应的中断发生时,对应的位会被硬件置1 ,中断响应后会自动清零,写1将强制清零该位。

本章节中的汇编程序一共使用到了25种指令,其中的JMP、RJMP、LDI、OUT、DEC、BRNE、PUSH、POP等8条指令可参考“基于ATMega16的流水灯实例”一文,SEI、CLR、INC、RETI、ADD等5条指令可参考“基于ATMega16的数码管动态扫描实例”一文,IN、CPI、SBRS、BREQ等4条指令可参考“基于ATMega16的数码管时钟显示实例”一文。SBI、SBR等2条指令可参考“基于ATMega16定时器T0产生PWM的实例”一文。剩余6条指令解释如下。

1)不带进位位减法
  SUB   Rd, Rr    0 ≤ d ≤ 31,0 ≤ r ≤ 31
说明:将两个寄存器中的数据相减,结果放在目的寄存器Rd中。
操作:Rd ← Rd - Rr    PC ← PC + 1   16位机器码:0001 10rd dddd rrrr
2)进位标志位C为1跳转
  BRCS   k    -64 ≤ k ≤ 63
说明:测试进位标志C,如果C位被置位,则相对PC值跳转k个字。k 为7位带符号数,最多可向前跳63个字,向后跳64个字。这条指令相当于指令BRBS 0,k。
操作:If C = 1 then PC ← PC + k + 1, else PC ← PC + 1   16位机器码:1111 00kk kkkk k000
3)清进位位
  CLC
说明:清零SREG状态寄存器中的进位标志C。
操作:C ← 0 PC ← PC + 1   16位机器码:1001 0100 1000 1000
4)寄存器位清零
  CBR   Rd, K    16 ≤ d ≤ 31,0 ≤ K ≤ 255
说明:清除寄存器Rd中的指定位,利用寄存器Rd的内容与常数表征码K的补码相与,其结果放在寄存器Rd中。
操作:Rd ← Rd · ($FF - K) PC ← PC + 1   16位机器码:0111 KKKK dddd KKKK
5)寄存器逻辑右移
  LSR   Rd    0 ≤ d ≤ 31
说明:寄存器 Rd中所有位右移1 位,第7 位被清0,第0位移到SREG 中的C标志。这个运算实现了无符号数更有效率的除2 操作,C标志用于结果的舍入。
操作:0 → b7--------b0 → C    PC ← PC + 1   16位机器码:1001 010d dddd 0110
6)带进位位的寄存器循环右移
  ROR    Rd    0 ≤ d ≤ 31
说明:寄存器 Rd的所有位右移一位,C标志被移到Rd的第7 位,Rd的第0 位被移到C标志位。
操作:C → b7--------b0 → C    PC ← PC + 1   16位机器码:1001 010d dddd 0111

此外,程序中用到的伪指令.INCLUDE、.DEF、.ORG可参考“基于ATMega16的流水灯实例”一文。 

 

标签:TMP,引脚,LDI,T1,ATMega16,PWM,TMP1,OUT
From: https://www.cnblogs.com/fxzq/p/18009342

相关文章

  • 2024.2.17模拟赛T1题解
    先考虑\(q=(1...n)\)的情况:发现如果设\(divcnt(p)\)表示将\(p\)划分为极小值域连续段的个数,那满足\(divcnt(p)\gem\)的排列都是合法的。那现在要求出有多少个排列符合条件可以先算出长度为\(i\),\(divnct\)为\(1\)的排列个数,这可以用dp解决然后再背包一下,就能求......
  • Go语言指南练习:rot13Reader
    题目:有种常见的模式是一个io.Reader包装另一个io.Reader,然后通过某种方式修改其数据流。例如,gzip.NewReader函数接受一个io.Reader(已压缩的数据流)并返回一个同样实现了io.Reader的*gzip.Reader(解压后的数据流)。编写一个实现了io.Reader并从另一个io.Reader中读取数据的rot13Rea......
  • 转载洛谷:23.08.19 普及模拟1 T1
    Past题目描述所有人,都有一段支离破碎的过去。你有\(n\)段过去的经历,有时顺利,有时不顺,于是你用一个评价值\(a_i\)来描述你的第\(i\)段经历,它们构成了长度为\(n\)的序列\(a\)。你决定对过去进行反思总结,反思深度为\(d\)。如果\(d\ge1\),那你就要算出\(a\)的所有子区间的和之和;如......
  • Nand2tetris Part1 lab04-lab06
    概述在前三个lab中,我们构建了hack计算机所需要的基础的芯片。后三个lab则是教我们如何使用这些芯片去搭建一个hack计算机并且使用汇编语言。Week04&Week05把这两个lab放在一起是因为他们给我的感觉更像是知识的互补。lab05教会你汇编语言的规范是什么,lab06则是......
  • (坚持每天写算法)算法学习与复习part1基础算法1-13——位运算
    最近确实有在写算法,在写dp,之前学的时候不全,被计数,树型等dp折磨了一下。位运算是将重点放在数字的位上,通常作为辅助行动,比如状态dp,有的时候是为了节省时空复杂度而使用的。这是今天的题目: 位运算应用的情况除了上面讲的,还有单纯的位问题,上面的题目就是一个例......
  • PWM
    PWM如何实现数字量转换成模拟量:根据面积等效原理电压不同时间不同的两组输入,如果它们的电压与时间乘积相同,那么它们的输出波形就是相同的上图使用了方波来模拟正弦波如果在Ts内输出连续占空比为75%的PWM波,假设输出为4V,那么它的输出波形可以等效持续Ts的3V电压PWM参数......
  • CMU 15-445(Fall 2023) Project1 BUFFER POOL个人笔记
    PROJECT#1-BUFFERPOOL总结本文章不涉及实验要求实现的方法的具体代码,且只包括基础部分的内容。Project1只做了基础部分,没有做针对排行榜的优化。Project1基础部分不算难,但Bustub中只提供了简单的测试样例,通过了本地的测试后提交到gradescope可能拿不了满分,需要根据gradesco......
  • PyQt5+python AttributeError: 'QMainWindow' object has no attribute 'slot1'解决办
    Step1.原因分析因为以前写过pyqt5,所以还是有一定的基础的,但是最近重新写上位机的时候,突然发现以前的办法不好用了,以前就是直接从Qtdesigner创建完成后,使用指令生成一个py文件,从main文件中调用就可以了,这次发现,直接使用槽函数,会报错AttributeError:'QMainWindow'objec......
  • rtthread系统用 RT1062 移植网卡功能(LAN8720A)
    RT-Thread:v4.0.2(master)SOC:i.MXRT1062Board:自研控制板背景描述1.有个控制板网口一直没人调试。2.NXPRT1xxx系列ENET_MAC调试心得。3.EthernetMAC控制器调试《终极指南》。 开整RT1062移植网卡功能(LAN8720A)1、i.MXRT系列外设驱动添加指南2、LAN8720A芯片的......
  • CEIT算法训练-双指针部分题解(T1-3)
    AnagramSearch题意简述两个字符串\(s\)和\(t\)相似的定义为:\(s\)可以打乱之后重新排列成为\(t\)。题目给出\(a\)和\(b\),问\(a\)中有多少子串(连续的一段)与\(b\)相似。同时,\(a\)中还含有\(?\)字符,他可以等价于任何字符(可以变成任何字符)解题思路实际上,根据题......