Прерывание таймера срабатывает слишком часто
Следующий вопрос касается Arduino Pro Mini (8 МГц, 3,3 В)
Я разрабатываю (еще один) шилд драйвера светодиодной ленты. Для этого я написал простой контур управления, который регулирует яркость канала (рабочий цикл ШИМ) до заданного целевого значения с плавным переходом.
Для тестирования я настроил Timer1 с интервалом 0,5 Гц для случайного назначения новых целевых значений. Это работает почти так, как и предполагалось. При просмотре значений вы можете видеть постоянные периоды значений и совершенно случайные, быстро меняющиеся периоды между ними (изображение создано с помощью встроенного графика Arduino). Изогнутые, довольно плавные линии — это выходы ШИМ, следующие за целевым значением (эта часть, как подтверждено, работает):
Соответствующий код, отвечающий за изменение целевого значения:
ISR(TIMER1_COMPA_vect) //прерывание таймера 1 0,5 Гц
{
for(auto it = 0; it < 4; ++it)
channel_target[it] = random(0, 255); // присваиваем случайное значение в диапазоне 0..255 (максимум для ШИМ)
}
Я предполагаю, что ISR вызывается очень быстро в нечетких интервалах, но поскольку я еще не много работал с Arduino, я не знаю, какие фоновые функции могут этому помешать.
Для полноты картины полный код:
double channel[4];
uint8_t channel_target[4];
const double channel_k_p[4] = { 0.001, 0.001, 0.001, 0.001 };
const double channel_k_i[4] = { 0.01, 0.01, 0.01, 0.01 };
double err;
const uint8_t led_pins[4] = { 3, 5, 6, 9 };
char serial_buffer[100];
void setup()
{
Serial.begin(9600);
for(auto it = 0; it < 4; ++it)
channel[it] = 0;
for(auto it = 0; it < 4; ++it)
{
pinMode(led_pins[it], OUTPUT); // устанавливает вывод как выход
analogWrite(led_pins[it], 0);
}
cli();//остановить прерывания
TCCR1A = 0;// установить весь регистр TCCR1A в 0
TCCR1B = 0;// то же самое для TCCR1B
TCNT1 = 0;//инициализируем значение счетчика до 0
// установить регистр сравнения для приращения 1 Гц
OCR1A = 15624;// = (8*10^6) / (0.5*1024) - 1 (должно быть < 65536)
// включить режим CTC
TCCR1B |= (1 << WGM12);
// Установить биты CS10 и CS12 для предварительного делителя 1024
TCCR1B |= (1 << CS12) | (1 << CS10);
// включить прерывание сравнения таймера
TIMSK1 |= (1 << OCIE1A);
//установить прерывание таймера 2 на 500 Гц
TCCR2A = 0;// установить весь регистр TCCR2A в 0
TCCR2B = 0;// то же самое для TCCR2B
TCNT2 = 0;//инициализируем значение счетчика до 0
// установить регистр сравнения для приращения 8 кГц
OCR2A = 248;// = (8*10^6) / (500*64) - 1 (должно быть < 256)
// включить режим CTC
TCCR2A |= (1 << WGM21);
// Установить бит CS21 для 64-кратного предварительного делителя
TCCR2B |= (1 << CS21) | (1 << CS20);
// включить прерывание сравнения таймера
TIMSK2 |= (1 << OCIE2A);
sei(); // повторно активировать прерывания
randomSeed(analogRead(0));
}
void loop()
{
for(auto it = 0; it < 4; ++it)
{
sprintf(serial_buffer, "%d ", channel_target[it]);
Serial.write(serial_buffer);
}
for(auto it = 0; it < 4; ++it)
{
sprintf(serial_buffer, "%d ", round(channel[it]));
Serial.write(serial_buffer);
}
Serial.write("\n");
delay(100);
}
ISR(TIMER2_COMPA_vect) // прерывание таймера 2 на частоте 1 кГц
{
// *******************************
// Контур управления яркостью канала
for(auto it = 0; it < 4; ++it)
{
err = channel_target[it] - channel[it];
channel[it] += err * channel_k_p[it]; // пропорциональный
channel[it] -= err * 0.002 * channel_k_p[it]; // интеграл
if(channel[it] > 255)
channel[it] = 255;
analogWrite(led_pins[it], floor(channel[it]));
}
}
ISR(TIMER1_COMPA_vect) //прерывание таймера 1 0,5 Гц
{
for(auto it = 0; it < 4; ++it)
channel_target[it] = random(0, 255); // присваиваем случайное значение в диапазоне 0..255 (максимум для ШИМ)
}
@Patrick, 👍3
1 ответ
Лучший ответ:
Функция analogWrite()
генерирует выходной сигнал ШИМ с использованием
аппаратный таймер. Выход на выводе 3 управляется таймером 2, тогда как
выход на контакте 9 управляется таймером 1. Здесь вы пытаетесь
используйте эти таймеры для генерации ШИМ и для собственной синхронизации кода
в то же время. Это не может работать. Либо вы позволяете библиотеке ядра Arduino
используйте таймеры для analogWrite()
, или вы принимаете их для себя, но не
и то, и другое.
Решение вашей проблемы очень простое. В вашем коде на самом деле нет ничего требует таймера для начала. Вы можете управлять как яркостью контур управления и значение изменяется с использованием техники Blink Без задержки Учебник Arduino:
const uint32_t TARGET_CHANGE_PERIOD = 500000;
const uint32_t BRIGHTNESS_UPDATE_PERIOD = 1000;
void loop()
{
uint32_t now = micros();
static uint32_t last_target_change;
if (now - last_target_change >= TARGET_CHANGE_PERIOD) {
last_target_change += TARGET_CHANGE_PERIOD;
// Измените целевые значения яркости.
}
static uint32_t last_brightness_update;
if (now - last_brightness_update >= BRIGHTNESS_UPDATE_PERIOD) {
last_brightness_update += BRIGHTNESS_UPDATE_PERIOD;
// Обновляем яркость каналов.
}
}
- 4-битный счетчик вверх и вниз
- Светодиод Arduino PWM с замиранием в сборке
- генерировать два сдвинутых по фазе ШИМ-импульса, запускаемых внешним сигналом с частотным разделением, с помощью Arduino uno?
- ATmega328P - проблема с использованием таймера 2 для генерации тона
- Мега: присоединение Interrupt на выводе 18/19/20/21 не работает
- Проблема с прерываниями
- Вопрос таймера: фазовая корректировка ШИМ на определенной частоте
- Светодиод питания ШИМ выходит из строя при близком к нулевому пороге