2015-05-14 13 views
2

Я хочу, чтобы генерировать ШИМ волну в режиме ШИМ Timer0 в ATmega8, как на рисунке ниже:Как сделать импульс PWM с 20% -ным рабочим циклом в AVR?

enter image description here

Он имеет рабочий цикл 20%, но она не может быть реализована с только ШИМ режиме. Я попытался использовать быстрый режим PWM в обратном режиме и попытался проверить TCNT0, пока он не достигнет 64H, поэтому я могу очистить PIN-код OC0, когда он достигнет его.

Мне было интересно, правильно ли этот метод работает, когда я очищаю 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

Вам нужен только один импульс или что-то еще? Похоже, PWM должен уметь отлично справляться с этим. – tangrs

+0

Нет, я вообще не хочу полной волны. Примерно за один импульс я думаю, что это работает. На самом деле я хочу знать, что изменение вручную OC0 будет влиять на внутренние функции PWM или нет? –

+0

Я до сих пор не знаю, зачем вам это нужно. Сигнал PWM будет периодическим, так что неважно, где вы начинаете? – tangrs

ответ

2

ШИМ-циклов в AVR обычно начинаются с переполнением таймера (за исключением фазы правильной и фазы/частоты правильно ШИМ) ... Другими словами, у вас есть контроль на но не с самого начала. В вашем примере - по каким-либо причинам - вы хотите контролировать начало цикла PWM (в 32 часа от переполнения таймера), а также рабочий цикл (около 20% ... 32h от FFh).

Таким образом, вы можете рассмотреть

  • использовать бесплатный запуск таймера генерации прерывания каждые 1/FFh
  • использовать один 8-битный регистр, который вы приращение при каждом вызове прерывания (он будет переполнение после FF ... это OK)
  • , если этот регистр считывает 32h или 64h, инвертировать выходной контакт (или установки/сброса, как показано ниже)
  • инициализировать программу, установив выходной контакт 0 до SEI

Я быстро угнали один из моих PWM LED вещи на моем AT90USBKEY2 - используя AT90USB1287 процессор - и модифицировали его в соотв. вышеупомянутому (извините код немного длинный: -o ... смотри ниже)

Edit:

Строго говоря, все это имеет смысл только если у вас есть точка синхронизации ... если вы смотрите на 20% -ную форму сигнала в изоляции, которую вы не можете определить, если она начинается с временного интервала 0x00 или 0x32 ... осциллограф всегда будет синхронизироваться на восходящем (или падающем) фронте импульса. Таким образом, вам нужно будет предоставить ссылку на начало кадра ШИМ путем вывода импульса на другом выводе при переполнении 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 
0

Возможно, но для этого требуется помощь по ЦП, а не режим PWM. Если вы включите прерывание совпадения выходного компаратора, и процессор может отреагировать в течение 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, или сброс значения таймера, предоставляет точную синхронизацию инструкции процессора на внешние сигналы.Если вы хороши в подсчете циклов, это выполнимо, но много усилий (сравните, например, демонстрационную версию lft's craft). Точка PWM-режима в AVR двоякая; во-первых, он добавляет второй край при переполнении таймера, а во-вторых, он задерживает обновления до следующего периода таймера. Комбинация позволяет вам изменять ширину импульса в любое время, не вызывая сбоев, но не предназначена для фазовых сдвигов, как в вашем примере.

Если вы не хотите использовать прерывания еще, вы могли бы одинаково проверить флаг прерывания в цикле опроса (только не установлен 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).