Я пытаюсь сделать пульт дистанционного управления RGB LED подсветкой с помощью ATtiny13A.
Я знаю, что ATtiny85 лучше подходит для этой цели, и я знаю, что в конечном итоге я не смогу уместить весь код, но сейчас моя главная задача - сгенерировать программный ШИМ с использованием прерываний в режиме CTC.
Я не могу работать в любом другом режиме (для быстрой ШИМ с исключением , OCR0A
как , TOP
так как код ИК - приемник Я использую потребности частоту 38 кГц , который он генерирует , используя КТК и который является в основном то же самое) OCR0A=122
.
Так что я пытаюсь (и я видел , как люди упоминают об этом в Интернете) использовать Output Compare A
и Output Compare B
прерывание для создания программного обеспечения PWM.
OCR0A
, который также используется ИК-кодом, определяет частоту, которая меня не волнует. И OCR0B
определяет рабочий цикл ШИМ, который я буду использовать для изменения цвета светодиодов.
Я ожидаю, что смогу получить ШИМ с рабочим циклом 0-100%, изменив OCR0B
значение с 0
на OCR0A
. Это мое понимание того, что должно произойти:
Но на самом деле происходит следующее (это из симуляции Proteus ISIS):
Как вы можете видеть ниже, я могу получить около 25% -75% рабочего цикла, но для ~ 0-25% и ~ 75-100% форма волны просто застревает и не меняется.
ЖЕЛТАЯ линия: Аппаратный ШИМ
RED line: программный ШИМ с фиксированным рабочим циклом
ЗЕЛЕНАЯ линия: Программное обеспечение ШИМ с переменным рабочим циклом
И вот мой код:
#ifndef F_CPU
#define F_CPU (9600000UL) // 9.6 MHz
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
int main(void)
{
cli();
TCCR0A = 0x00; // Init to zero
TCCR0B = 0x00;
TCCR0A |= (1<<WGM01); // CTC mode
TCCR0A |= (1<<COM0A0); // Toggle OC0A on compare match (50% PWM on PINB0)
// => YELLOW line on oscilloscope
TIMSK0 |= (1<<OCIE0A) | (1<<OCIE0B); // Compare match A and compare match B interrupt enabled
TCCR0B |= (1<<CS00); // Prescalar 1
sei();
DDRB = 0xFF; // All ports output
while (1)
{
OCR0A = 122; // This is the value I'll be using in my main program
for(int i=0; i<OCR0A; i++)
{
OCR0B = i; // Should change the duty cycle
_delay_ms(2);
}
}
}
ISR(TIM0_COMPA_vect){
PORTB ^= (1<<PINB3); // Toggle PINB3 on compare match (50% <SOFTWARE> PWM on PINB3)
// =>RED line on oscilloscope
PORTB &= ~(1<<PINB4); // PINB4 LOW
// =>GREEN line on oscilloscope
}
ISR(TIM0_COMPB_vect){
PORTB |= (1<<PINB4); // PINB4 HIGH
}
источник
OCR0A
используется ИК-код, поэтому у меня есть толькоOCR0B
. Я пытаюсь использовать его для генерации программного ШИМ на 3 выводах без ШИМ.Ответы:
Минимальный программный ШИМ может выглядеть так:
Ваша программа устанавливает
dutyCycle
желаемое значение, и ISR выводит соответствующий сигнал ШИМ.dutyCycle
этоuint16_t
значение для значений от 0 до 256 включительно; 256 больше любого возможного значенияcurrentPwmCount
и, таким образом, обеспечивает полный 100% рабочий цикл.Если вам не нужны 0% (или 100%), вы можете сбрить некоторые циклы, используя
uint8_t
так, что либо0
приводит к рабочему циклу 1/256 и255
составляет 100%, либо к0
0%, а255
рабочий цикл равен 255 /. 256.У вас все еще мало времени на 38 кГц ISR; используя небольшой встроенный ассемблер, вы можете сократить количество циклов ISR на 1/3 - 1/2. Альтернатива: запускайте свой код ШИМ только при каждом переполнении таймера, вдвое уменьшая частоту ШИМ.
Если у вас несколько каналов PWM и все контакты, которые вы используете в PMW, одинаковы,
PORT
вы также можете собрать все состояния контактов в переменной и, наконец, вывести их на порт за один шаг, который затем требует только чтения из порт и-с-маской или-с-новым состоянием, запись в порт один раз, а не один раз на вывод / канал .Пример:
Этот код отображает рабочий цикл на логический
1
вывод на выводах; если ваши светодиоды имеют «отрицательную логику» (светодиод включается при низком выводе ), вы можете инвертировать полярность сигнала ШИМ, просто переключившисьif (cnt < dutyCycle...)
наif (cnt >= dutyCycle...)
.источник
if
в подпрограмму прерывания только выполнение кода ШИМ через раз. Делая это, если мой ШИМ-код занимает слишком много времени и пропускается следующее прерывание переполнения, тогда с моей программой все будет в порядке, потому что следующее прерывание ничего не будет делать. Это то, что вы имели в виду?Как заметил @JimmyB, частота ШИМ слишком высока.
Кажется, что прерывания имеют общую задержку в одну четверть цикла ШИМ.
При перекрытии рабочий цикл фиксируется с учетом общей задержки, поскольку второе прерывание ставится в очередь и выполняется после выхода из первого.
Минимальный рабочий цикл ШИМ определяется как процент задержки общего прерывания в период ШИМ. Та же логика применима к максимальному рабочему циклу ШИМ.
Глядя на графики, минимальный рабочий цикл составляет около 25%, а затем общая задержка должна составлять ~ 1 / (38000 * 4) = 6,7 мкс.
Как следствие, минимальный период ШИМ составляет 256 * 6,7 мкс = 1715 мкс и максимальная частота 583 Гц.
Еще несколько объяснений о возможных патчах на высокой частоте:
Прерывание имеет два скрытых окна, когда ничего нельзя сделать, вводя конец, выходя из прерывания, когда контекст сохраняется и восстанавливается. Поскольку ваш код довольно прост, я подозреваю, что это занимает значительную часть времени ожидания.
Решение пропустить низкие значения будет по-прежнему иметь задержку, по крайней мере, при выходе из прерывания и при вводе следующего прерывания, поэтому минимальный рабочий цикл будет не таким, как ожидалось.
Пока это не меньше шага ШИМ, рабочий цикл ШИМ начнется с более высокого значения. Просто небольшое улучшение по сравнению с тем, что есть сейчас.
Я вижу, что вы уже используете 25% процессорного времени в прерываниях, так почему бы вам не использовать 50% или более его, оставьте второе прерывание и просто используйте пул для флага сравнения. Если вы используете значения только до 128, у вас будет только до 50% рабочего цикла, но с задержкой двух команд, которые могут быть оптимизированы в ассемблере.
источник