2

我想在 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
4

2 回答 2

2

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

所以你可能要考虑

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

我迅速劫持了我的AT90USBKEY2上的一个 PWM LED 东西——使用AT90USB1287处理器——并根据它进行了修改。到上面(对不起,代码有点长:-o ...见下文)

编辑:

严格来说,这一切只有在你有一个同步点时才有意义......如果你孤立地查看 20% 的波形,你无法确定它是从时隙开始0x00还是0x32......示波器总是在上升(或下降)时同步) 脉冲的边缘。因此,您需要通过在溢出时在不同引脚上输出脉冲来提供对 PWM 帧开始的参考PWM_SUBLEVEL。使用来自另一个引脚的这个脉冲作为示波器的同步源,您可以在开始变化时开始看到相位的移动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
于 2015-05-19T21:04:20.537 回答
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 输出)。

于 2015-05-22T09:05:42.203 回答