基于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的实例”一文。