Переходим с STM32 на российский микроконтроллер К1986ВЕ92QI. Практическое применение: Опрашиваем клавиши, управляем яркостью светодиода (генерируем напряжение на выводе) с помощью ШИМ (PWM+TIM+PORT)

Вступление.

В двух предыдущих статьях мы генерировали при помощи ШИМ тактовый сигнал нужной нам частоты, получая на светодиоде равный промежутки свечения и его отсутствия. Данная задача имеет место быть на практике (в одной из последующих статей мы с ней точно столкнемся). Но чаще всего ШИМ используют по другому назначению. Одно из самых распространенных — управление яркостью светодиодов или скоростью вращения моторов. Так же при помощи ШИМ можно генерировать звук (о чем будет следующая статья). А в данной статье мне хотелось бы рассказать, как на нашем контроллере можно реализовать управление яркостью светодиода.

Начнем: изменение настроек основного таймера.

За основу мы возьмем проект из этой статьи. На основе функции initTimerPWMled создадим функцию initTimerPWMconstPeriod. Параметром функции будет уже не PWM_speed (частота ШИМ-а), а timeEnabledState — время действия импульса. О теории генерации напряжения на выводе методом ШИМ прекрасно рассказывает эта коротенькая статья. Для начала определимся с параметрами:

  • Пусть длительность целого периода функции будет 0xFFFF тиков таймера (можно выбрать любое значение, я выбрал это значение для удобства и чтобы не менять код для следующей статьи о генерации звука ШИМ-ом)
  • Тогда параметр функции timeEnabledState будет показывать сколько тактов из 0xFFFF на выводе присутствует »1». Все остальное время на канале »0». Например, при timeEnabledState = 0×4000, сигнал будет иметь следующий вид.
    7420d4edd5714f3ea303652046536d75.PNG
    При 0×8000 будут почти равны.
    3f3e24b8750946599a1c819525e0f1a6.PNG
    Ну, а при 0xF000 сигнал »1» будет почти всегда.
    3dfc14ba33c846108fef8b01aa3dec34.PNG

Чем меньше по времени период, тем лучше. Мы выбрали период длинной в 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 светодиода не хватает и появляются нежелательные пульсации.

Заключение

В данной статье мы рассмотрели, как можно настроить ШИМ и управлять им. В следующей статье мы попробуем сгенерировать при помощи ШИМ звук.
Файлы проекта к статье.

Список предыдущих статей.

© Habrahabr.ru