基于ATMega16定时器驱动成品舵机的实例(汇编)

本例讨论ATMega16中通过定时器产生舵机所需要的PWM波形,从而控制舵机的运作。

一般的商用成品舵机外形如下图所示。

它有三根连接线,其中两根是电源线(一般为红黑或红橙),另一根是PWM的输入线(一般为白色或棕色)。本例要求通过ATMega16输出PWM对舵机进行控制,让舵机从+90度逐渐转动到-90度,然后又从-90度逐渐转动到+90度,如此循环。舵机与ATMeaga16连接时,把舵机的PWM输入线连接至ATMega16的PD4引脚,再给舵机接上电源即可。

下面分析一下舵机的控制原理,舵机所需要的PWM脉冲周期固定为20ms,当脉冲高电平宽度为1.5ms时舵盘处于中点位置,当脉冲高电平变为0.5ms时舵盘向左转了约90度,当脉冲高电平变为2.5ms时舵盘向右转了约90度。通过脉冲宽度从0.5ms到2.5ms之间变化,就可以控制舵面在正负90度之间转动。具体如下图所示。

本例选择定时器T1来实现,由于要实现20ms的固定周期,所以只有使用可变频率的形式。下面分别以相位修正PWM模式、相频修正PWM模式及快速PWM模式来实现同样的功能。

a)采用相位修正PWM模式,以ICR1为TOP,OCR1B进行匹配输出,完整的汇编代码如下。

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.DEF    FLAG = R18
.DEF    ONE = R19
.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, $34
    OUT     TCCR1B, TMP
    OUT     TCCR1A, TMP1         ;OC1B强制输出1
    SBI     DDRD, 4              ;设置PD4为输出方向
    LDI     TMP, $22
    LDI     TMP1, $12
    OUT     TCCR1A, TMP    
    OUT     TCCR1B, TMP1         ;设置定时器T1为8分频,ICR1为TOP的相位修正PWM模式,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            ;清除溢出中断标志
    LDI     TMP1, $02
    LDI     TMP, $EE
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初值,让舵机回中
    LDI     TMP1, $27
    LDI     TMP, $10
    OUT     ICR1H, TMP1
    OUT     ICR1L, TMP           ;确定TOP值,输出周期为20ms
    CLR     FLAG                 ;标志位清零
    LDI     ONE, $01
    SEI                          ;开启总中断
LOOP:
    RJMP    LOOP
;T1溢出中断服务程序
TIME1_OVF:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    SBRS    FLAG, $0            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD
    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                         ;清进/借位
    SUB     TMP1, ONE
    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, $1            ;已到$FA则表示到最底,置标志位为0
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
FORWARD:
    IN      TMP, OCR1BL         ;获取当前比较值
    IN      TMP1, OCR1BH
    ADD     TMP, ONE            ;加1
    BRCS    N1                  ;进/借位为1
    CPI     TMP1, $04
    BREQ    N2
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
N1: CLC                         ;清进/借位
    ADD     TMP1, ONE
    CLR     TMP
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;给OCR1B赋初值
    RJMP    EXIT1
N2: CPI     TMP, $E2
    BREQ    N3
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
N3: SBR     FLAG, $1            ;已到$4E2则表示到最高,置标志位为1
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
EXIT1:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI

b)采用相频修正PWM模式,以OCR1A为TOP,以OCR1B进行匹配输出,完整的汇编代码如下。

.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, $27
    LDI     TMP, $10
    OUT     OCR1AH, TMP1
    OUT     OCR1AL, TMP          ;给OCR1A赋值,确定TOP值,输出周期为20ms
    LDI     TMP1, $02
    LDI     TMP, $EE
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初值,让舵机回中
    CLR     FLAG                 ;标志位清零
    LDI     ONE, $01
    SEI                          ;开启总中断
LOOP:
    RJMP    LOOP
;OCR1A匹配中断服务程序
TIME1_OCR1A:
    IN      TMP, SREG
    PUSH    TMP
    SBRS    FLAG, $0            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD
    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    EXIT
M1: CLC                         ;清进/借位
    SUB     TMP1, ONE
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP
    RJMP    EXIT
M2: CPI     TMP, $FA
    BREQ    M3
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT
M3: CBR     FLAG, $1            ;已到$FA则表示到最底,置标志位为0
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT
FORWARD:
    IN      TMP, OCR1BL         ;获取当前比较值
    IN      TMP1, OCR1BH
    ADD     TMP, ONE            ;加1
    BRCS    N1                  ;进/借位为1
    CPI     TMP1, $04
    BREQ    N2
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT
N1: CLC                         ;清进/借位
    ADD     TMP1, ONE
    CLR     TMP
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初值
    RJMP    EXIT
N2: CPI     TMP, $E2
    BREQ    N3
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT
N3: SBR     FLAG, $1            ;已到$4E2则表示到最高,置标志位为1
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
EXIT:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI

c)采用快速PWM模式,以ICR1为TOP,OCR1B进行匹配输出,完整的汇编代码如下。 

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                ;定义R16寄存器的别名(注意小于16号的寄存器不能进行LDI操作)
.DEF    TMP1 = R17
.DEF    FLAG = R18
.DEF    ONE = R19
.ORG    $0000
    JMP        RESET             ;复位的向量入口,地址为$00
.ORG    $000A
    RJMP       TIME1_ICR1        ;ICR1捕获中断跳转
.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, $22
    LDI     TMP1, $1A
    OUT     TCCR1A, TMP    
    OUT     TCCR1B, TMP1         ;设置定时器T1为8分频,8位快速PWM模式,OC1B匹配置0溢出置1
    IN      TMP, TIMSK           ;获取原TIMSK的值
    SBR     TMP, $20             ;置第5位为1
    OUT     TIMSK, TMP           ;回写TIMSK,允许ICR1捕获中断
    IN      TMP, TIFR
    SBR     TMP, $20
    OUT     TIFR, TMP            ;清除捕获中断标志
    LDI     TMP1, $05
    LDI     TMP, $DC
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP          ;给OCR1B赋初值,让舵机回中
    LDI     TMP1, $4E
    LDI     TMP, $20
    OUT     ICR1H, TMP1
    OUT     ICR1L, TMP           ;给ICR1赋值,确定TOP值,输出周期为20ms
    CLR     FLAG                 ;标志位清零
    LDI     ONE, $01
    SEI                          ;开启总中断
LOOP:
    RJMP    LOOP
;ICR1捕获中断服务程序
TIME1_ICR1:
    IN      TMP, SREG
    PUSH    TMP                 ;SREG的值压栈
    SBRS    FLAG, $0            ;标志位为1则执行减操作(减小占空比),为0则执行加操作(增加占空比)
    RJMP    FORWARD
    IN      TMP, OCR1BL         ;获取当前比较值
    IN      TMP1, OCR1BH
    SUB     TMP, ONE            ;减1
    BRCS    M1                  ;进/借位为1
    CPI     TMP1, $01
    BREQ    M2
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
M1: CLC                         ;清进/借位
    SUB     TMP1, ONE
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP
    RJMP    EXIT1
M2: CPI     TMP, $F4
    BREQ    M3
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
M3: CBR     FLAG, $1            ;已到$FA则表示到最底,置标志位为0
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
FORWARD:
    IN      TMP, OCR1BL         ;获取当前比较值
    IN      TMP1, OCR1BH
    ADD     TMP, ONE            ;加1
    BRCS    N1                  ;进/借位为1
    CPI     TMP1, $09
    BREQ    N2
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
N1: CLC                         ;清进/借位
    ADD     TMP1, ONE
    CLR     TMP
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;给OCR1B赋初值
    RJMP    EXIT1
N2: CPI     TMP, $C4
    BREQ    N3
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
    RJMP    EXIT1
N3: SBR     FLAG, $1            ;已到$9C4则表示到最高,置标志位为1
    OUT     OCR1BH, TMP1
    OUT     OCR1BL, TMP         ;更新比较值
EXIT1:
    POP     TMP
    OUT     SREG, TMP           ;SREG的值出栈
    RETI

下面对程序中的相关部分进行一下说明。
1)由于舵机接受的是周期为20ms的PWM信号,所以只能使用定时器的可变频率的模式,包括相频率模式、相位模式及快速PWM模式。
2)虽然理论上也可选择8位定时器(T0、T2),经过1024分频后把PWM的周期控制到20ms,但由于每个舵机的控制脉宽只有0.5ms~2.5ms的高电平区间,使用8位模式时占空比调整的步长过大,会导致对舵机控制的精确度降低,所以建议使用16位定时器(T1)来实现,以提高控制精度。
3)由于在双斜坡(相频或相位PWM)方式中,更改一次匹配值会让斜坡两边的匹配点都发生变化,而单斜坡(快速PWM)方式则只有一个匹配点发生变化,所以理论上,单斜坡方式的精度要比双斜坡方式高一倍,但双斜坡方式的相位控制要比单斜坡方式的好。工程应用时可根据实际情况进行选择。
4)对于TOP,在T1中可以有OCR1A或ICR1两个选择,选择哪一个都可以,但在更新匹配值时,建议相位修正模式下在溢出中断中进行,而相频修正和快速模式下都在TOP中进行。
5)PWM脉冲输出引脚固定在PD5或PD4两个引脚上,不能使用其他引脚。

本部分使用到了AVR中I/O空间的8个寄存器,即SPH、SPL、DDRD、PORTD、TCNT1、TCCR1A、TCCR1B和TIMSK。其中SPH、SPL、DDRD、PORTD等4个寄存器的介绍可参考“基于ATMega16的流水灯实例”一文,TCNT1寄存器可参考“基于ATMega16的数码管动态扫描实例”一文。TCCR1A、TCCR1B、TIMSK、TIFR等4个寄存器可参考“基于ATMega16定时器T1产生PWM的实例”一文。

汇编程序一共使用到了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的实例”一文。SUB、BRCS、CLC、CBR、LSR、ROR等6条指令可参考“基于ATMega16定时器T1产生PWM的实例”一文。 

posted @ 2024-02-22 15:19  fxzq  阅读(33)  评论(0编辑  收藏  举报