Продолжим изучать модуль CCP и остановимся на самом интересном применении ШИМ.
Задача: регулировка яркости светодиода с помощью ШИМ.
Исходный материал: PIC16f628a и простенькая devboard + proteus. Понять принцип работы ШИМа помогает его блок-схема: За период ШИМа отвечает регистр PR2, когда таймер TMR2 дотикает до значения регистра PR2 на выходе устанавливается высокий уровень (одновременно с обнулением таймера).
В то же время содержимое регистра CCPR1L + два бита CCP1X, CCP1Y копируются в регистр CCPR1H, когда таймер дотикает до значения в этих регистрах происходит сброс и выход переходит в 0.
Все логично и просто.
Рассмотрим базовую настройку. Для настройки ШИМа нужно знать два следующих уравнения:
Период ШИМ = (PR2 + 1)⋅4⋅Tosc⋅TMR2_precaller_value
Ширина импульса = (CCPR1L:CCP1CON<5:4>)⋅Tosc ⋅ TMR2_precaller_value
В даташите подробно описан поэтапный способ настройки, приведу его еще и здесь:
1. Установка периода ШИМ посредством записи в регистр PR2.
2. Установка скважности — регистр CCPR1L и CCP1CON<5:4>
3. Определить RB3 как выходной порт
4. Задать коэффициент деления предделителя таймера и включить таймер
Перейдем к конкретным действиям:
Задача: Регулировка яркости светодиода с помощью ШИМ
Параметры нашего эксперимента: Частота ШИМ — 2 КГц Скважность меняется в 8 раз (от 0 до 100%) Скважность регулируется посредством кнопок
Чтобы получить такую частоту, в PR2 должно быть записано число 124 (коэффициент деления предделителя таймера установим равным 4).
Ну и ради примера расчета скважности, рассчитаем значение CCPR1L:CCP1CON<5:4> для 50% (50% от 500 = 250) :
250u = 4*0.25u*x => x = 250 => CCPR1L = 0b00111110, CCP1X = 1, CCP1Y = 0
Вот и все расчеты, ниже привожу мой код: #include #define _XTAL_FREQ 4000000 __CONFIG(WDTDIS & UNPROTECT & MCLREN & LVPDIS & HS); void pwm() { unsigned char x = 0; unsigned char state = 1; while (state) { if (!RB0) { __delay_ms(100); if (!RB0) { if (x > 0) { x--;} else x = 0; } } if (!RB1) { __delay_ms(100); if (!RB1) { if (x < 9) { x++;} else x = 8; } } if (!RB2) { // Выход из прерывания (в данном контексте можно и не делать) __delay_ms(100); if (!RB2) { state = 0; INTF = 0; } } switch (x) { case 0: // 0% CCPR1L = 0; CCP1X = 0; CCP1Y = 0; break; case 1: // 12.5% CCPR1L = 0b00001111; CCP1X = 1; CCP1Y = 0; break; case 2: // 25% CCPR1L = 0b00011111; CCP1X = 0; CCP1Y = 0; break; case 3: // 37.5% CCPR1L = 0b00101110; CCP1X = 1; CCP1Y = 1; break; case 4: // 50% CCPR1L = 0b00111110; CCP1X = 0; CCP1Y = 1; break; case 5: // 62.5% CCPR1L = 0b01001110; CCP1X = 0; CCP1Y = 0; break; case 6: // 75% CCPR1L = 0b01011101; CCP1X = 1; CCP1Y = 0; break; case 7: // 87.5% CCPR1L = 0b01101101; CCP1X = 0; CCP1Y = 1; break; case 8: // 100% CCPR1L = 0b011111100; CCP1X = 1; CCP1Y = 1; break; } } } void main() { PORTB = 0; TRISB = 0b00000111; PR2 = 0b01111100; CCPR1L = 0; // По умолчанию 0% CCP1X = 0; CCP1Y = 0; CCP1M3 = 1; // Включаем ШИМ CCP1M2 = 1; T2CKPS0 = 1; T2CKPS1 = 0; // Предделитель на 4 TMR2ON = 1; // Включаем 2 таймер GIE = 1; INTE = 1; for (;;) { // Пустой бесконечный циклл } } void interrupt isr() { // Прерывание по RB0 if (INTF) { pwm(); } }
Осциллограма с 50% скважностью (1 деление = 1 мс)
Источник: http://diymicro.ru/?p=95 |