如何在AVR中制作占空比为20%的PWM脉冲?

时间:2015-05-14 14:13:23

标签: assembly avr pwm

我想在ATMEGA8的timer0的PWM模式下生成一个PWM波,如下图所示:

enter image description here

占空比为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

2 个答案:

答案 0 :(得分:2)

AVR中的PWM周期通常以定时器溢出开始(相位校正和相位/频率校正PWM除外)...换句话说,您可以控制占空比,但不能控制启动时间。在您的示例中 - 无论出于何种原因 - 您希望控制PWM周期的开始(从定时器溢出开始32h)以及占空比(约20%...... FFh的32h)。

所以你可能想要考虑

  • 使用自由运行的定时器,每1 / FFh生成一次中断
  • 使用一个8位寄存器,在每次中断调用期间递增(在FF后它会溢出......没关系)
  • 如果该寄存器读取32h或64h,则反转输出引脚(或设置/复位如下)
  • 通过在SEI
  • 之前将输出引脚设置为0来初始化程序

我使用 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输出)。