№ 3772 В разделах: Electronics
Programming
от October 31st, 2011,
В подшивках: Arduino
Прошляпил я как-то параметры купленного микроконтроллера AtTiny461. Думал там 6 шимов, а оказалось 3 + 3 инверсных выхода. Но ZiB подсказал клевый выход – сделать программный ШИМ. Этот МК работает на частоте 20 МГц, поэтому можно сделать все 12 необходимых мне ШИМов всего лишь на 1 чипе.
Принцип оказался проще некуда. Допустим, нам надо светить светодиодом с яркостью 00110010b (50d). В этом байте 8 бит. Проход начинаем с самого левого бита до самого правого. Длительность задержки таймера зависит от текущей позиции обрабатываемого бита. На самом левом задержка должна быть 128 тактов, на самом правом 1, т.е. мы делим задержку предыдущего бита на 2 и полученную задержку применяем к текущему биту. Знаю, сразу не вкурили. Сейчас объясню:
Прежде всего нужно инициализировать таймер:
void init_timer() { TCCR1A = (0 << WGM10) | (0 << WGM11); TCCR1B = (0 << CS12) | (1 << CS11) | (1 << CS10) | (1 << WGM12) | (0 << WGM13); OCR1A = 128; TIMSK |= _BV(OCIE1A); sei(); }
Обработчик таймера должен выглядеть примерно так:
static volatile uint8_t arr_value = 128, led_value = 0, led_value_bit = 0; ISR(TIMER1_COMPA_vect) { // изменение задержки OCR1A = (uint16_t) arr_value; arr_value = arr_value / 2; // проверяем первый бит яркости if (led_value & 0x80) LED_PORT &= ~LED_BIT; // 0 - выкл диод else LED_PORT |= LED_BIT; // 1 - вкл диод led_value <<= 1; // сдвиг значения яркости на бит влево led_value_bit++; // запоминаем текущий бит // если посмотрели все 8 битов, то сбрасываемся и берем значение яркости заново if (led_value_bit > 7) { led_value = led_brightness; led_value_bit = 0; arr_value = 128; } }
А теперь основная программа
#include <avr/io.h> #include <avr/delay.h> #include <avr/interrupt.h> #include <inttypes.h> #define LED_PORT PORTB #define LED_DDR DDRB #define LED_BIT (1<<PB4) #define LED_GREEN (1<<PB3) void main() { LED_DDR |= LED_BIT | LED_GREEN; init_timer(); while (1) { _delay_ms(20); LED_PORT ^= LED_GREEN; // моргаю зеленым. так, для красоты. led_brightness = 1; led_value = led_brightness; // делаем копию для прерывания /* //медленно изменяем яркость туда-сюда if (led_direction > 0) { led_brightness++; } else { led_brightness--; } if (led_brightness == 255) led_direction = 0; if (led_brightness == 0) led_direction = 1; */ } return 0; }
В конце бесконечного цикла есть закомментированный кусок кода. Чтобы светодиод медленно разгорался и гаснул, раскомментируйте его, а строку с led_brightness = 1;
вообще уберите.
Fortune cookie: Today's spam: Euromillion Loteria Espaol
А как же печально известный переход 0x7f 0x80? Когда получается то слишком длинный 0, то слишком длинная 1 и, как следствие, мерцание.
ну мерцает, да. только программно исправлять такие недостатки.
в этом плане мне нормальный шим нравится больше, чем подобная возня, но в его отсутствии можно и так.