我想在ATMEGA8的timer0的PWM模式下生成一个PWM波,如下图所示:
占空比为20%,但单独使用PWM模式无法实现。 我尝试在反向模式下使用快速PWM模式并尝试检查TCNT0,直到达到64H,这样我就可以在到达时清除OC0 PIN。
当我手动清除OC0时,我想知道这种方法是否正常工作?
这是我的代码:
.DEF A = R16 ;GENERAL PURPOSE ACCUMULATOR
.ORG $0000
ON_RESET:
SBI DDRB,3 ;SET PORTB3(OC0) FOR OUTPUT
LDI A,0b01011011 ;SET TO FAST PWM MODE
OUT TCCR0,A ;SET PRESCALER/DIVIDER TO /32
LDI A,32 ;DIFFERENT VALUE
OUT OCR0,A ;FOR COMPARE
MAIN_LOOP:
PLOOP: IN A,TCNT0 ;COMPARE TCNT0
ANDI A,0x64H ;COMPARE TCNT0 TO 64 TO MAKE IT ZERO
BRNEQ PLOOP
CBI PINB,3
RJMP MAIN_LOOP;A CHECK FOR TIMER LOOP
答案 0 :(得分:2)
AVR中的PWM周期通常以定时器溢出开始(相位校正和相位/频率校正PWM除外)...换句话说,您可以控制占空比,但不能控制启动时间。在您的示例中 - 无论出于何种原因 - 您希望控制PWM周期的开始(从定时器溢出开始32h)以及占空比(约20%...... FFh的32h)。
所以你可能想要考虑
我使用 AT90USB1287 处理器迅速劫持了我的 AT90USBKEY2 上的一个PWM LED事件 - 并对其进行了修改。到上面(对不起,代码有点冗长:-o ...见下文)
修改强>
严格地说,如果您有一个同步点,这一切才有意义......如果您单独查看20%波形,则无法确定它是在时间段0x00
还是0x32
开始。 ..示波器将始终在脉冲的上升沿(或下降沿)同步。因此,您需要通过在PWM_SUBLEVEL
溢出时在不同引脚上输出脉冲来提供PWM帧起始的参考。使用来自另一个引脚的脉冲作为示波器的同步源,您可以在开始更改PWM_ON
时开始看到相位的移动。
/*
* AsmFile1.asm
*
* Created: 19.05.2015 22:01:49
* Author: MikeD
*/
.nolist
.include <usb1287def.inc>
.list
.def TMP1 = R16
.def TMP2 = R17
.def PWM_SUBLEVEL = R18
.def PWM_ON = R19
.def PWM_OFF = R20
.cseg
.org 0x0000
jmp V_RESET ; 1 $0000 RESET External pin, Power-on reset, Brown-out reset, Watchdog reset, and JTAG AVR reset
jmp V_NOINT ; 2 $0002 INT0 External Interrupt Request 0
jmp V_NOINT ; 3 $0004 INT1 External Interrupt Request 1
jmp V_NOINT ; 4 $0006 INT2 External Interrupt Request 2
jmp V_NOINT ; 5 $0008 INT3 External Interrupt Request 3
jmp V_NOINT ; 6 $000A INT4 External Interrupt Request 4
jmp V_NOINT ; 7 $000C INT5 External Interrupt Request 5
jmp V_NOINT ; 8 $000E INT6 External Interrupt Request 6
jmp V_NOINT ; 9 $0010 INT7 External Interrupt Request 7
jmp V_NOINT ; 10 $0012 PCINT0 Pin Change Interrupt Request 0
jmp V_NOINT ; 11 $0014 USB General USB General Interrupt request
jmp V_NOINT ; 12 $0016 USB Endpoint/Pipe USB ENdpoint/Pipe Interrupt request
jmp V_NOINT ; 13 $0018 WDT Watchdog Time-out Interrupt
jmp V_NOINT ; 14 $001A TIMER2 COMPA Timer/Counter2 Compare Match A
jmp V_NOINT ; 15 $001C TIMER2 COMPB Timer/Counter2 Compare Match B
jmp V_T2OVF ; 16 $001E TIMER2 OVF Timer/Counter2 Overflow
jmp V_NOINT ; 17 $0020 TIMER1 CAPT Timer/Counter1 Capture Event
jmp V_NOINT ; 18 $0022 TIMER1 COMPA Timer/Counter1 Compare Match A
jmp V_NOINT ; 19 $0024 TIMER1 COMPB Timer/Counter1 Compare Match B
jmp V_NOINT ; 20 $0026 TIMER1 COMPC Timer/Counter1 Compare Match C
jmp V_NOINT ; 21 $0028 TIMER1 OVF Timer/Counter1 Overflow
jmp V_NOINT ; 22 $002A TIMER0 COMPA Timer/Counter0 Compare Match A
jmp V_NOINT ; 23 $002C TIMER0 COMPB Timer/Counter0 Compare match B
jmp V_NOINT ; 24 $002E TIMER0 OVF Timer/Counter0 Overflow
jmp V_NOINT ; 25 $0030 SPI, STC SPI Serial Transfer Complete
jmp V_NOINT ; 26 $0032 USART1 RX USART1 Rx Complete
jmp V_NOINT ; 27 $0034 USART1 UDRE USART1 Data Register Empty
jmp V_NOINT ; 28 $0036 USART1TX USART1 Tx Complete
jmp V_NOINT ; 29 $0038 ANALOG COMP Analog Comparator
jmp V_NOINT ; 30 $003A ADC ADC Conversion Complete
jmp V_NOINT ; 31 $003C EE READY EEPROM Ready
jmp V_NOINT ; 32 $003E TIMER3 CAPT Timer/Counter3 Capture Event
jmp V_NOINT ; 33 $0040 TIMER3 COMPA Timer/Counter3 Compare Match A
jmp V_NOINT ; 34 $0042 TIMER3 COMPB Timer/Counter3 Compare Match B
jmp V_NOINT ; 35 $0044 TIMER3 COMPC Timer/Counter3 Compare Match C
jmp V_NOINT ; 36 $0046 TIMER3 OVF Timer/Counter3 Overflow
jmp V_NOINT ; 37 $0048 TWI 2-wire Serial Interface
jmp V_NOINT ; 38 $004A SPM READY Store Program Memory Ready
V_RESET:
; prepare stack ... special write procedure
ldi TMP1, low(ramend)
ldi TMP2, high(ramend)
out spl, TMP1
out sph, TMP2
; increase CLKIO from 1 to 8MHz ... special write procedure
ldi TMP1, 0b1000_0000
ldi TMP2, 0b0000_0000
sts CLKPR, TMP1
sts CLKPR, TMP2
; initialize variables
clr PWM_SUBLEVEL
ldi PWM_ON, 0x32
ldi PWM_OFF, 0x64
; prepare LED ports
; D2-RD on PORTD4
; D2-GN on PORTD5
; D5-RD on PORTD7
; D5-GN on PORTD6
ldi TMP1, 0b1111_0000
out DDRD, TMP1
; Timer2 (8bit) without prescaler in normal mode
; generates interrupt every 256 CPU clock cycles (32 us) for PWM sublevel
; we use this for PWM as Timer2 has the highest priority amongst timers
clr TMP1
sts TCCR2A, TMP1 ; normal mode, port pins disabled
ldi TMP1, (1 << CS20)
sts TCCR2B, TMP1 ; internal clock, no prescaler
ldi TMP1, (1 << TOIE2)
sts TIMSK2, TMP1 ; overflow interrupt enable
sei ; set general interupt enable flag
MAIN:
rjmp MAIN
V_T2OVF:
; fires every 32 us
inc PWM_SUBLEVEL ; overflows every 8.192 ms, f=122.07... Hz
cp PWM_SUBLEVEL, PWM_ON
breq GO_HI
cp PWM_SUBLEVEL, PWM_OFF
breq GO_LO
reti
GO_HI:
sbi PORTD, PORTD4
reti
GO_LO:
cbi PORTD, PORTD4
reti
V_NOINT:
; fire error LED ... if we get here something is wrong
sbi PIND, PIND7 ; invert output by writing 1 to input bit in output mode
reti
答案 1 :(得分:0)
可以,但需要CPU辅助,而不是PWM模式。如果启用输出比较器匹配中断,并且CPU可以在脉冲持续的50个周期内作出反应,则可以使输出比较模式切换为仅在两个匹配值之间交替。这可以通过单个xor操作轻松完成。
// Setup
TCCR0A = 1<<COM0A0; // toggle output, immediate update (not PWM mode)
OCR0A = 0x32; // initial toggle time
TIMSK0 |= 1<<OCIE0A; // enable interrupt on output compare
TCCR0B = 1<<CS01; // start counter
// In interrupt handler for TIMER0_COMPA
OCR0A ^= 0x32 ^ 0x64; // toggle compare value
直接从CPU更改输出值或重置定时器值等技巧会暴露CPU指令对外部信号的精确定时。如果你擅长循环计数,这是可行的,但需要付出很多努力(例如,比较lft的工艺演示)。 AVR中PWM模式的要点是双重的;首先,它在定时器溢出时增加了第二个边沿,其次它延迟了对下一个定时器周期的更新。该组合允许您随时改变脉冲宽度而不会引起毛刺,但它并不打算像您的示例中那样进行相位偏移。
如果你还不想使用中断,你可以同样检查轮询循环中的中断标志(只是不要设置OCIE0A):
if (TIFR0 & (1<<OCF0A)) {
TIFR0 = 1<<OCF0A; // clear interrupt flag
OCR0A ^= 0x32 ^ 0x64; // toggle compare value
}
顺便说一句,你的边缘值不会给出20%; 256简单地不能被5整除。在19.5%它可能足够接近(虽然接下来的两个范围是19.9%和20.3%),但是如果你想要正好20%,那么就应该看看CTC模式(它使用的是0A比较器用于编程最高值,因此您将留下0B比较器用于PWM输出)。