PWM cycles in the AVR usually start with a timer overflow (except phase correct and phase/frequency correct PWM) ... in other words you have control on the duty cycle but not on the start. In your example - for whatever reasons - you want to control the start of the PWM cycle (at 32h from timer overflow) as well as the duty cycle (ca. 20% ... 32h of FFh).
So you may want to consider
- to use a free running timer generating an interrupt every 1/FFh
- use a single 8 bit register which you increment during each call to the interrupt (it will overflow after FF ... that's OK)
- if this register reads 32h or 64h, invert the output pin (or set / reset as below)
- initialize your program by setting the output pin to 0 before SEI
I quickly hijacked one of my PWM LED things on my AT90USBKEY2 - using an AT90USB1287 processor - and modified it acc. to the above (sorry the code is a bit lengthy :-o ... see below)
Edit:
Strictly speaking all this only makes sense if you have a point of synchronisation ... if you look at the 20% waveform in isolation you cannot determine if it starts at timeslot 0x00
or 0x32
... an oscilloscope would always sync at the rising (or falling) edge of the pulse. So you would need to provide a reference to the start of the PWM frame by output of a pulse on a different pin at the overflow of PWM_SUBLEVEL
. Using this pulse from the other pin as a sync source for an oscilloscope you start to see the move of the phase as you start changing 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