PWM своими руками
- Войдите на сайт для отправки комментариев
Изучая второе занятие с Arduino поставил себе задачу сделать PWM без использования analogWrite. Ну, почти получилось, я может быть ещё не все моменты понял, но результат, на мой взгляд, заметно лучше. Во-первых, разрешение в области малых значений лучше, во вторых, можно получить устойчивую светимость светодиода (мой индикатор) при меньшей яркости.
Иллюстрация.
void setup_timer1 () { noInterrupts(); // disable all interrupts TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; OCR1A = 1; // compare match register 16MHz/256/2Hz TCCR1B |= (1 << WGM12); // CTC mode TCCR1B |= (1 << CS10)|(1 << CS11); // 64 prescaler TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt interrupts(); }
Это установка таймера. Я, надо признаться, не совсем понимаю, срабатывает он каждый тик или каждый второй тик. Прошу комментария на эту тему.
int led = 9; void setup() { pinMode(led, OUTPUT); setup_timer1(); }
Светиться будет светодиод на 9-м пине. Сделал так, чтобы было проще сравнить с тем, что делает analogWrite.
byte current_state = 0; /* 1.25 microsecs */ static inline void ledWriteB(byte pin) { pin -= 8; byte pins = PORTB; byte old_pins = pins; if (current_state & 1) { pins |= (HIGH << pin); --current_state; } else if (current_state & 2) { pins &= ~(HIGH << pin); current_state -= 2; } if (old_pins != pins) PORTB = pins; }
Несколько оптимизированный вариант digitalWrite, по моим замерам, довольно быстрый. Совмещено с логикой распределения импульсов 1 и 0. Если в 0м бите 1, то выдать 1, если в 1м бите 1, то надо выдать 0. Биты устанавливаются специальной логикой так, чтобы уровень 0.5 выдавал всегда 1 и 0 подряд, равномерно, если уровень выше, то 1-цы будут идти соответственно чаще, но равномерно.
typedef struct _int_bresenham { int ante, conseq; int ante_err, conseq_err; } int_bresenham; /* 1.2 microsecs */ static inline byte dither_bresenham(int_bresenham *br) { byte ret = 0; if (br->ante_err < br->ante && br->conseq_err < br->conseq) { br->conseq_err += br->ante; br->ante_err += br->conseq; } if (br->ante <= br->ante_err) { ++ret; br->ante_err -= br->ante; } if (br->conseq <= br->conseq_err) { ret |= 2; br->conseq_err -= br->conseq; } return ret; }
Это собственно логика, распределяющая переходы равномерно в заданном соотношении. Авторство моё :) Идея позаимствована у Брезенхема из целочисленного алгоритма рисования линий.
void loop() { short pot_value = analogRead(A0); // analogWrite(led, pot_value / 4); current_br.ante = 1023 - pot_value; current_br.conseq = pot_value; } ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine { if (current_state == 0) { current_state = dither_bresenham(¤t_br); } ledWriteB(led); }
Тут происходит действие -- считываются показания потенциометра, которые используются для расчёта коэффициентов. В прерывании таймера вычисляется соотношение 1 и 0 на следующий шаг и подсвечивается светодиод нужным уровнем яркости.
Интересует мнение опытного народа о потобных извратах, имеет ли это когда либо смысл или я просто чего-то не знаю? Я всего лишь второй день с новой игрушкой, никогда раньше не имел дел с микроконтроллерами.
Вы изобретаете велосипед : ) http://arduino.ru/forum/programmirovanie/analogwrite-na-lyubom-vyvode
Вы изобретаете велосипед : ) http://arduino.ru/forum/programmirovanie/analogwrite-na-lyubom-vyvode
Файл библиотеки лежит на таком хостинге, с которого у меня не получается скачать. Нажимаю "скачать", а мне в ответ "http://download73.files.attachmail.ru/DA49F37530AB4AED8D38E2670A7CA509/865449855ff0d45e8b066fdd4a59a1f2/PWM.rar не найден". Поэтому, мне не удалось посмотреть что же там внутри, было интересно сравнить реализации. Собственно, я ж начинающий, это моя обязанность -- изобрести велосипеды самостоятельно. Главное, чтобы колёса квадратными не оказались -- моя тема об этом.
Там совсем другая история...
Вы используете аппаратный ШИМ вам сюда.
Как реализован программный ШИМ (то что в архиве) написано в сообщении #30.
Там совсем другая история...
Вы используете аппаратный ШИМ вам сюда.
Да неправда, у меня самый настоящий програмный PWM.
Как реализован программный ШИМ (то что в архиве) написано в сообщении #30.
Я, к сожалению, плохо (т.е. никак) знаю ассемблер AVR, можешь объяснить что делает CHECK словами? Мой последний чем-то похожий ассемблер, который я знал, был Z80, на котором я писал года так 22 назад.
Как реализован программный ШИМ (то что в архиве) написано в сообщении #30.
Хочу задать вопрос о выборе прескейл значения. Если я правильно прочитал, то у тебя 256, правильно? Т.е. частота таймера у тебя будет будет 16мгц/256/1 = 62500, правильно? Быстрее просто не успевает обработчик прерывания?
Добавлю иллюстрацию использования метода. Это мой третий урок по Ардуино, подключен RGB LED и переменный резистор (потенциометер). Три разных цвета RGB зажигаются-гаснут по синусоиде, расхождение угла между ними задаётся положением резистора.
Да неправда, у меня самый настоящий програмный PWM.
Да, наверное, не вчитывался... где то промелькнуло про FastPWM вот и подумал...
что делает CHECK словами?
Хочу задать вопрос о выборе прескейл значения. Если я правильно прочитал, то у тебя 256, правильно? Т.е. частота таймера у тебя будет будет 16мгц/256/1 = 62500, правильно? Быстрее просто не успевает обработчик прерывания?
Будет ли успевать обработчик или нет зависит от количества задействованных выводов, измерений не проводил, так что при разных прескалерах и количестве выводов какая реальная частота ШИМ не могу сказать.
Да собственно сравнивает pwms[0] с pwm и устанавливает или сбрасывает бит порта.
Логика действия ускользнула. Можно рассказать на пальцах?
Будет ли успевать обработчик или нет зависит от количества задействованных выводов, измерений не проводил, так что при разных прескалерах и количестве выводов какая реальная частота ШИМ не могу сказать.
Это какой-то другой даташит, не от Arduino Uno. В том, что я читал, были CS12-CS10.
Да собственно сравнивает pwms[0] с pwm и устанавливает или сбрасывает бит порта.
Логика действия ускользнула. Можно рассказать на пальцах?
Если интересно, я могу рассказать логику действий dither_bresenham, если по коду непонятно. В этом исполнении разрядность PWM динамическая и плавность по большей части зависит только от быстродействия.
Это какой-то другой даташит, не от Arduino Uno. В том, что я читал, были CS12-CS10.
Сорри, осознал, что мы про разные таймеры говорим. Я про таймер1, а ты про таймер2.
Последняя версия исправленного и оптимизированного dither_bresenham
Наконец-то зачитал популярную статейку о ШИМ, понял как работает код у maksim. Задумался...
Моя реализация алгоритма Брезенхема тут.
Видео демо тут.
Собирал термостат с ПИД регулированием.
оно на любой ардуинке будет работать? Или зависит от камушка?
оно на любой ардуинке будет работать? Или зависит от камушка?
Алгоритм Брезенхема на любой! Или Вы не об этом?
я про прямое указание портов в скетче.
У одного класса ардуин как правило да. Вообще, скетч вот, просто попробуй на той, что тебя интересует: https://github.com/jabbervorx/arduino-rgb-lesson/blob/master/src/wave-rgb.cpp
я про прямое указание портов в скетче.
для Arduino Uno можно вот моё взять: https://github.com/jabbervorx/arduino-any-pc-pwm/blob/master/include/digitalPortsFast.h
если нужно универсально, то есть другие, очень похожие по смыслу варианты. более полные библиотеки попадались, но мне хватало моей.