|
|
|
2790 1 1 0 |
|
Опции темы | Поиск в этой теме |
26.12.2013, 04:56 | 1 |
Ветеран Фонарёвки
Регистрация: 28.10.2010 Последняя активность: 10.05.2023 13:50 Адрес: Германия
Сообщений: 1192
Записей в дневнике: 1 Сказал(а) спасибо: 27
Поблагодарили: 120 раз(а) в 53 сообщениях
|
аналоговый выход
Приветствую. Сегодня первый день как начал играться с Arduino UNO, опыта в программировании контроллеров у меня раньше не было. Начал с первого урока -- моргающий светодиод. Усложнил задачу, чтобы диод стал медленно гаснуть и загораться без использования analogWrite. Не понравилась идея 490 Гц, да и вообще, в учёбе не должно быть лёгких путей. Помучившись изрядно, добился результата, теперь хотелось бы услышать комментарии и ответы на возникшие вопросы. Код немного длинноват, поскольку писался в Eclipse, а не в стандартной среде.
Основные вопросы у меня по скорости работы разных участков. Например, как я понимаю, при OCR1A = 1 и TCCR1B |= (1 << CS10) -- прерывание таймера должно работать с максимальной скоростью, но я, признаться, так и не понял с какой именно. Увеличивая счётчики прескейлинга или OCR1A, скрость падает, но как-то не так линейно как я ожидал. В программе не использовал плавающую точку нигде, все функции на первый взгляд кажутся почти оптимильными. Никак не мог понять что происходит, пока не перенёс запись в pin13 внутрь обработчика прерывания, в связи с этим второй вопрос -- не съел ли мой таймерный обаботчик всё быстродействие, что на loop() уже почти ничего не осталось? Проблема в том, что если в loop не использовать delay, а сделать эту логику через millis, то переменная current_grad перестаёт увеличиваться совсем -- это совсем не удаётся понять. В итоге сделал увеличение current_grad безусловно и визуально скорость смены яркости вполне комфортная. Логика аналогового выхода в том, что уровни HIGH и LOW распределяются равномерно и при этом пропорционально нужному коэффициенту. Код:
#include <Arduino.h> #include <avr/io.h> #include "HardwareSerial.h" typedef struct _int_ratio { int ante, conseq; } int_ratio; int tbl_sinus_scale = 255; byte tbl_sinus[] = { 0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 49, 53, 57, 62, 66, 70, 75, 79, 83, 87, 91, 96, 100, 104, 108, 112, 116, 120, 124, 127, 131, 135, 139, 143, 146, 150, 153, 157, 160, 164, 167, 171, 174, 177, 180, 183, 186, 190, 192, 195, 198, 201, 204, 206, 209, 211, 214, 216, 219, 221, 223, 225, 227, 229, 231, 233, 235, 236, 238, 240, 241, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, 255 }; int_ratio irsinus(int grad) { int_ratio ret; int ag; grad %= 360; if (grad < 0) grad += 360; ag = grad % 180; if (ag > 90) ag = 180 - ag; ret.ante = tbl_sinus[ag]; ret.conseq = tbl_sinus_scale - ret.ante; if (grad > 180) ret.ante = -ret.ante; return ret; } typedef struct _int_bresenham { int ante, conseq; int ante_err, conseq_err; } int_bresenham; int_ratio dither_bresenham(int_bresenham *br) { int_ratio ret = { 0, 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_err >= br->ante) { ++ret.ante; br->ante_err -= br->ante; } if (br->conseq_err >= br->conseq) { ++ret.conseq; br->conseq_err -= br->conseq; } return ret; } int led = 13; // the setup routine runs once when you press reset: void setup() { // initialize the digital pin as an output. pinMode(led, OUTPUT); // initialize 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 prescaler TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt interrupts(); // enable all interrupts } int current_grad = 1; unsigned long next_turn = 0; int_ratio current_state = { 0, 0 }; int_bresenham current_br = { 0, 0, 0, 0 }; void loop() { unsigned long time = millis(); // if (time > next_turn) { ++current_grad; if (current_grad >= 360) current_grad -= 360; int_ratio cs = irsinus(current_grad); if (cs.ante < 0) cs.ante = -current_state.ante; current_br.ante = cs.ante; current_br.conseq = cs.conseq; current_br.ante_err = current_br.conseq_err = 0; next_turn = time + 10; // } } ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine { int led_level = HIGH; if (current_state.ante == 0 && current_state.conseq == 0) { current_state = dither_bresenham(¤t_br); } if (current_state.ante > 0) { led_level = HIGH; --current_state.ante; } else if (current_state.conseq > 0) { led_level = LOW; --current_state.conseq; } digitalWrite(led, led_level); // включаем LED } int main(void) { init(); setup(); //endless loop for (;;) { loop(); } return 0; } |
26.12.2013, 14:01 | 2 |
Ветеран Фонарёвки
Регистрация: 28.10.2010 Последняя активность: 10.05.2023 13:50 Адрес: Германия
Сообщений: 1192
Записей в дневнике: 1 Сказал(а) спасибо: 27
Поблагодарили: 120 раз(а) в 53 сообщениях
|
Re: аналоговый выход
Последняя оптимизированная версия тут https://github.com/jabbervo...
|