Переходим с STM32 на российский микроконтроллер К1986ВЕ92QI. Практическое применение: Опрашиваем клавиши, управляем яркостью светодиода (генерируем напряжение на выводе) с помощью ШИМ (PWM+TIM+PORT)
Вступление.
В двух предыдущих статьях мы генерировали при помощи ШИМ тактовый сигнал нужной нам частоты, получая на светодиоде равный промежутки свечения и его отсутствия. Данная задача имеет место быть на практике (в одной из последующих статей мы с ней точно столкнемся). Но чаще всего ШИМ используют по другому назначению. Одно из самых распространенных — управление яркостью светодиодов или скоростью вращения моторов. Так же при помощи ШИМ можно генерировать звук (о чем будет следующая статья). А в данной статье мне хотелось бы рассказать, как на нашем контроллере можно реализовать управление яркостью светодиода.
Начнем: изменение настроек основного таймера.
За основу мы возьмем проект из этой статьи. На основе функции initTimerPWMled создадим функцию initTimerPWMconstPeriod. Параметром функции будет уже не PWM_speed (частота ШИМ-а), а timeEnabledState — время действия импульса. О теории генерации напряжения на выводе методом ШИМ прекрасно рассказывает эта коротенькая статья. Для начала определимся с параметрами:
- Пусть длительность целого периода функции будет 0xFFFF тиков таймера (можно выбрать любое значение, я выбрал это значение для удобства и чтобы не менять код для следующей статьи о генерации звука ШИМ-ом)
- Тогда параметр функции timeEnabledState будет показывать сколько тактов из 0xFFFF на выводе присутствует »1». Все остальное время на канале »0». Например, при timeEnabledState = 0×4000, сигнал будет иметь следующий вид.
При 0×8000 будут почти равны.
Ну, а при 0xF000 сигнал »1» будет почти всегда.
Чем меньше по времени период, тем лучше. Мы выбрали период длинной в 0xFFFF тактов. Для достижения хороших результатов, нам нужно, чтобы частота прохождения этих периодов была максимальна. Для этого выключим делитель тактового сигнала в функции initTimerPWMconstPeriod.
MDR_TIMER1->PSG = 0; // Делитель тактовой частоты отсутствует.
Далее нужно изменить способ формирования сигнала. Как мы помним из предыдущей статьи, за это отвечает группа бит OCCM регистра CH1_CNTRL. Тогда мы выбирали режим инвертирования сигнала REF при CNT == CCR1. Так как CCR было = 0 по умолчанию, то регулируя ARR основного счетчика — мы получали тот же эффект. Сейчас же мы в ARR записываем количество тактов всего периода (0xFFFF), а в CRR будем писать количество тактов единицы на выводе (время действия импульса). А режим выберем 6 (0b110:1, если DIR= 0 (счет вверх), CNT
MDR_TIMER1->ARR = 0xFFFF; // Период постоянный. И дает возможнсоть выбирать период заполнения от 0 до 0xFFFF.
MDR_TIMER1->CCR1 = timeEnabledState; // Канал будет держать 1 до этого значения и 0 - после.
MDR_TIMER1->CH1_CNTRL = 6<= CCR1;
По сути, на этом этапе мы уже получили функцию, задающую скважность ШИМ сигнала. Но нужно позаботиться о том, чтобы при смене значения CCR1 счетчик не вышел за пределы. Для этого нужно установить бит CRRRLD в регистре CH1_CNTRL2. Он разрешает смену значения CCR1 и CCR2 лишь при CNT == 0.
MDR_TIMER1->CH1_CNTRL2 = TIMER_CH_CNTRL2_CCRRLD; // CCR1 обновляется лишь при CNT = 0. Чтобы не было глюков.
Закончим: изменение параметров прерывания.
С настройкой ШИМ-а разобрались. Осталось только немного изменить прерывание по опросу клавиш. Зададим новую переменную, характеризующую длительность импульса в периоде. Назовем ее PWM_time. По умолчанию пусть будет 0xFFFF (ШИМ-а при таком значении нет, светодиод горит). А далее посмотрим, с каким интервалом делать шаг при нажатии на клавишу. Для этого разложим число тактов периода на простые множители.
// 65535 = 3·5·17·257. 257 раз пусть будет для нас диапазоном от 0 - не горит до 257 - горит на полную. => шаг 3*5*17 = 255.
Из расчета видим, что шагая с интервалом 255 мы можем пройти всю шкалу от 0 до 257. Так и поступим. Получим следующий код.
// 65535 = 3·5·17·257. 257 раз пусть будет для нас диапазоном от 0 - не горит до 257 - горит на полную. => шаг 3*5*17 = 255.
if (UP_FLAG == 0) PWM_time+=255; // Проверияем, нажата ли какая-нибудь клавиша. Если нажата - что-то делаем с частотой.
else if (DOWN_FLAG == 0) PWM_time-=255;
else if (LEFT_FLAG == 0) PWM_time-=255;
else if (RIGHT_FLAG == 0) PWM_time+=255;
if (PWM_time < 0) PWM_time = 0; // Проверяем, чтобы время "единицы" было не меньше нуля и не больше периода.
else if (PWM_time > 0xFFFF) PWM_time = 0xFFFF;
Осталось только присвоить новое значение.
MDR_TIMER1->CCR1 = PWM_time; // Меняем частоту.
Еще раз замечу, что период мы не трогаем. Он постоянен. Меняем только длительность импульса. Целиком прерывание будет иметь следующий вид.
int PWM_time = 0xFFFF; // По началу горит полностью.
void Timer2_IRQHandler (void)
{
MDR_TIMER2->STATUS = 0; // Сбрасываем флаг. Обязательно первой коммандой.
//LED1_FLAG = !LED1_FLAG; // Показываем, что прерывание было обработано.
// 65535 = 3·5·17·257. 257 раз пусть будет для нас диапазоном от 0 - не горит до 257 - горит на полную. => шаг 3*5*17 = 255.
if (UP_FLAG == 0) PWM_time+=255; // Проверияем, нажата ли какая-нибудь клавиша. Если нажата - что-то делаем с частотой.
else if (DOWN_FLAG == 0) PWM_time-=255;
else if (LEFT_FLAG == 0) PWM_time-=255;
else if (RIGHT_FLAG == 0) PWM_time+=255;
if (PWM_time < 0) PWM_time = 0; // Проверяем, чтобы время "единицы" было не меньше нуля и не больше периода.
else if (PWM_time > 0xFFFF) PWM_time = 0xFFFF;
MDR_TIMER1->CCR1 = PWM_time; // Меняем частоту.
}
Я намеренно скрыл строку инверсии светодиода, показывающую, что прерывание происходит. Ибо тока на 2 светодиода не хватает и появляются нежелательные пульсации.
Заключение
В данной статье мы рассмотрели, как можно настроить ШИМ и управлять им. В следующей статье мы попробуем сгенерировать при помощи ШИМ звук.
Файлы проекта к статье.
Список предыдущих статей.