Перші кроки з STM32 і компілятором mikroC для архітектури ARM — Частина 2, продовження

Розібравшись з таймером, спробуємо використовувати його для чого, крім генерації тимчасових інтервалів. Найчастіше за допомогою таймера генерується сигнал ШІМ. Що це таке можна почитати на просторах Мережі, наприклад у всеведающий Википедии.
Основна принадність ШІМ в тому, що він дозволяє за допомогою ключів, що працюють в імпульсному режимі (найбільш ефективному щодо втрат енергії) змінювати діюче значення прикладеного напруження у тій чи іншій навантаженні. Для ШІМ сигналу основними параметрами є загальна тривалість імпульсу та тривалість його активного стану (зазвичай високого рівня сигналу).
Діюче значення напруги має залежність від тривалості активного стану імпульсу. Наш МК вміє генерувати ШІМ за допомогою апаратних функцій таймера, не займаючи дорогоцінний час ядра процесора.
image
Використавши МК в якості ШІМ-генератора можна керувати потужним навантаженням (наприклад нагрівачем або електродвигуном). Для згладжування вищих гармонік проникаючих в навантаження від самого ШІМ модулятора необхідно застосовувати LC або RC фільтр низьких частот.
Одна зі схем управління електродвигуном постійного струму за допомогою МК наведена нижче.
image
В даному випадку сам двигун маючи індуктивну складову навантаження виступає фільтром низьких частот.
Кожен таймер має 4 незалежно настроюються каналу, кожен з яких може використовуватися для генерації імпульсів (compare mode), або для їх захоплення (capature mode). Для початку розглянемо які конфігураційні регістри необхідні для того, щоб налаштувати наш таймер в режим генерації ШІМ сигналу:
TIMx_CR1 — Регістр налаштування таймера.
У минулій частині ми вже використали біт TIMx_CR1.CEN (включення/відключення таймера) цього регістра. Ще важливим для нас буде біт TIMx_CR1.DIR — напрям рахунку. За замовчуванням TIMx_CR1.CEN=0, рахунок прямої, показання инкрементируются до значення 65535 або значення, записане в регістр TIMx_ARR. При встановленні біта TIMx_CR1.CEN=1 — зворотний рахунок, при кожному такті вхідного синхросигналу показання зменшуються від 65535 до 0 (або значення регістра (TIMx_ARR).
TIMx_ССR1, TIMx_ССR2, TIMx_ССR3, TIMx_ССR4 — регістри захоплення-порівняння.
Запис значень в них задає шпаруватість імпульсів ШІМ. У режимі захоплення імпульсів у ці клітинки записується значення регістра таймера TIMx_CNT при фіксації на вході відповідного каналу імпульсу.
TIMx_CCMR1, TIMx_CCMR2 — регістри налаштування каналів захввата/генерації.
Настроює режим роботи таймера як джерела імпульсів або ШІМ сигналу, або як вимірювача тривалості імпульсів (режим захоплення). TIMx_CCMR1 відноситься до 1 і 2 каналу таймера, TIMx_CCMR2 — до 3 і 4. Далі ми будемо розглядати регістр TIMx_CCMR1 налаштування TIMx_CCMR2 — аналогічні
image
TIMx_CCMR1.CC1S, використовуються для включення режиму генерації імпульсів або режиму захоплення імпульсів.
  • TIMx_CCMR1.CC1S= 00 – канал працює в режимі генерації імпульсів;
  • TIMx_CCMR1.CC1S= 01 – канал працює в режимі захоплення, сигнал захоплення — TI1;
  • TIMx_CCMR1.CC1S= 10 – канал працює в режимі захоплення, сигнал захоплення — TI2;
TIMx_CCMR1.CC2S, аналогічно, за винятком того, що
  • TIMx_CCMR2.CC1S= 01 – канал працює в режимі захоплення, сигнал захоплення — TI2;
  • TIMx_CCMR2.CC1S= 10 – канал працює в режимі захоплення, сигнал захоплення — TI1.
Дана настройка необхідна для можливості підключення до 2 каналах сигналу з одного входу, що використовується, наприклад, для захоплення ШІМ сигналу.
Решта біти регістра TIMx_CCMR1 залежить від того включений режим генерації або режим захоплення. Поки наша мета — генерація ШІМ (TIMx_CCMR1.CC1S= 00) сигналу, тому опишемо призначення основних параметрів для цього режиму роботи.
TIMx_CCMR1.CC1S,TIMx_CCMR1.CC2S — біти налаштування сигналу на виході відповідного каналу таймера.
  • TIMx_CCMR1.CC1S = 000 – збіг регістру порівняння (CCR1) і рахункового регістра (CNT) не впливає на стан виходу таймера;
  • TIMx_CCMR1.CC1S = 001 – при CCR1=CNT сигнал на виході каналу =1;
  • TIMx_CCMR1.CC1S = 010 – при CCR1=CNT сигнал на виході каналу =0;
  • TIMx_CCMR1.CC1S = 010 – при CCR1=CNT сигнал на виході каналу інвертується;
  • TIMx_CCMR1.CC1S =100 — на виході постійно 0;
  • TIMx_CCMR1.CC1S =101 — на виході постійно 1;
  • TIMx_CCMR1.CC1S = 110 – режим ШИМа №1 (Прямий ШІМ). Якщо рахунковий регістр працює на додавання: при CNT<CCR1 сигнал на виході = 1, інакше 0. Якщо рахунковий регістр працює на віднімання: при CNT>CCR1 сигнал на виході = 0, інакше 1.
  • TIMx_CCMR1.CC1S = 111 – режим ШИМа №2 (Зворотний ШІМ). Якщо рахунковий регістр працює на додавання: при CNT<CCR1 сигнал сигнал на виході = 0, інакше-1. Якщо рахунковий регістр працює на віднімання: при CNT>CCR1 сигнал на виході = 1, інакше 0 .
TIMx_CCER — Регістр включає і відключає відповідний канал генерації/захоплення сигналу таймером.
image
  • TIMx_CCER1.CC1E, TIMx_CCER1.CC2E, TIMx_CCER1.CC3E, TIMx_CCER1.CC4E — включає і вимикає відповідний канал генерації/захоплення.
  • TIMx_CCER1.CC1P, TIMx_CCER1.CC2P, TIMx_CCER1.CC3P, TIMx_CCER1.CC4P — інверсія полярності вихідного сигналу для режиму генерації і вибір переднього або заднього фронту імпульсу для режиму захоплення.
    Для початку необхідно налаштувати порт мікроконтролера для роботи з виходом таймера. Цей режим роботи порту називається: Alternate Function. Для його включення в microC є функція:
GPIO_Alternate_Function_Enable(&module);

тут &module — вказівник на модуль, що реалізує альтернативну функцію порту вводу/виводу. наприклад для 3 каналу TIM3 це &_GPIO_MODULE_TIM3_CH3_PB0, для 2 каналу TIM2: &_GPIO_MODULE_TIM2_CH2_PA1. Загалом, покажчик має вигляд &_GPIO_MODULE_TIMa_CHb_Pxy, де х — порт а в — висновок, до якого згідно даташита на конкретний мікроконтролер підключений b-канал а-таймера (Тут дуже може допомогти Code Assistsnt (ctrl-пробіл), вводимо _GPIO_MODULE і вибираємо те, що нам потрібно з випадаючого списку).
Давайте спробуємо згенерувати ШІМ сигнал зі шпаруватістю 25% на виведенні PB0 нашого мікроконтролера:
void main()
{

GPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM3_CH3_PB0); // Включаємо альтернативну функцію для виведення РВ0

RCC_APB1ENRbits.TIM3EN=1; //Подаємо тактування на TIM3

TIM3_PSC=7199; // Переддільник дорівнює 7200, таймер тактується частотою 10 КГц.

TIM3_ARR=99; // Таймер вважає від 0 до 99, і обнуляється. 10000 / (99+1) = 100 Гц. Це і буде частота сигналу на виході нашого ШИМа

TIM3_CCR3=25; // При значенні 25 сигнал на виході змінює рівень з високого на низький, з 100 тактів 25 рівень на виході буде високим, решта 75 - низьким.

TIM3_CCERbits.CC3E=1; // Включаємо 3 вихід нашого таймера

TIM3_CCMR2_Outputbits.OC3M=0b110; // Конфігуруємо режим виходу - OC3M=110 - прямий ШІМ.

TIM3_CR1bits.CEN=1; //Включаємо таймер
while(1)
{

}
}

На виході PB0 отримуємо таку картину:
image
У microC ШІМ модулятор можна налаштувати за допомогою вбудованих функцій. Для цього спочатку иннициализируем таймер в режим ШІМ модулятора за допомого ифункции
period = PWM_TIMх_Init(freg);

  • freg — частопа роботи ШІМ модулятора в герцах;
  • period — функція повертає період нашого генератора: кількість тактів таймера за один період вихідного сигналу, по суті значення регістра TIMх_ARR
Після цього встановимо шпаруватість генерованих імпульсів потрібного нам каналу за допомогою функції:
PWM_TIMх_Set_Duty(duty, _PWM_INVERTED /_PWM_NON_INVERTED, channel);

  • duty — шпаруватість нашого ШІМ сигналу, по суті значення регістра TIMх_CCRy, змінюється від 0 до значення period;
  • _PWM_INVERTED або _PWM_NON_INVERTED — прямий чи зворотній шім;
  • channel — канал ШІМ модулятора, має значення:
    • _PWM_CHANNEL1 — для 1 каналу;
    • _PWM_CHANNEL2 — для 2 каналу;
    • _PWM_CHANNEL3 — для 3 каналу;
    • _PWM_CHANNEL4 — для 4 канали;
Нарешті запустимо наш генератор ШИМ функцією
PWM_TIM3_Start(channel, &module)

  • channel — канал ШІМ модулятора;
  • &module — вказівник на модуль, що виводить сигнал ШІМ на фізичний висновок мікроконтролера, значення те, що ми вказували при перекладі порту мікроконтролера командою GPIO_Alternate_Function_Enable(&module) в режим Alternate Function для підключення виходу таймера до висновку МК.
Напишемо просту програму збільшує яскравість вбудованого в нашу плату світлодіода при натисканні на кнопку.
unsigned short state; // наша мінлива станів, бит1 використовується для визначення відпускання кнопки.
unsigned int period; //змінна зберігає значення періоду імпульсів 
unsigned short dutyled;//змінна зберігає шпаруватість імпульсів
void main()
{
dutyled=10; //почнемо з 10%
GPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM3_CH4_PB1); //Включаємо альтернативну функцію для піна PB0 GPIO_Digital_Input(&GPIOb_BASE, _GPIO_PINMASK_8); 
period = PWM_TIM3_Init(5000); //Иннициализируем ШІМ з частотою 5000 Гц
PWM_TIM3_Set_Duty((period/100)*dutyled, _PWM_NON_INVERTED, _PWM_CHANNEL4); //встановлюємо шпаруватість для 4 каналу ШІМ Таймера3
PWM_TIM3_Start(_PWM_CHANNEL4, &_GPIO_MODULE_TIM3_CH4_PB1); // Запускаємо 4 канал ШІМ, выводем на пін PB1 

while(1)
{
if (Button(&GPIOb_IDR, 8, 1, 1))
state.b1=1;

if (state.b1 && Button(&GPIOb_IDR, 8, 1, 0))
{
//Натиснули Кнопку і відпустили
state.b1= ~state.b1; 
dutyled = dutyled + 10;
if (dutyled >= 100) //якщо шпаруватість більше 100% то скидаємо 0
dutyled=10;
PWM_TIM3_Set_Duty((period/100)*dutyled, _PWM_NON_INVERTED, _PWM_CHANNEL4); // Змінюємо шпаруватість імпульсів
}
}
}

Тепер при кожному натисканні на кнопку наш світлодіод починає світиться яскравіше. Шпаруватість ШІМ сигналу збільшується після кожного натискання на 10% збільшується і яскравість світлодіода.
Давайте за допомогою вбудованих функцій microC згенеруємо 2 ШІМ сигналу на виходах каналів CH2 (PA7) і CH3 (PB0) TIM3 з скважностями відповідно 15% і 55%
unsigned int period, duty1, duty2; //змінні зберігають значення періоду і шпаруватості імпульсів 
void main()
{
duty1=55; //Заносимо в змінні значення шпаруватості імпульсів
duty2=15;

GPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM3_CH3_PB0); //Включаємо альтернативну функцію для піна PB0
GPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM3_CH2_PA7);//Включаємо альтернативну функцію для піна РА7

period = PWM_TIM3_Init(1000); // Иннициализируем ШІМ з частотою 1000 Гц

PWM_TIM3_Set_Duty((period/100)*duty1, _PWM_NON_INVERTED, _PWM_CHANNEL3); //встановлюємо шпаруватість для 3 каналу ШІМ Таймера3
PWM_TIM3_Set_Duty((period/100)*duty2, _PWM_NON_INVERTED, _PWM_CHANNEL2); //встановлюємо шпаруватість для 2 каналу ШІМ Таймера3

PWM_TIM3_Start(_PWM_CHANNEL3, &_GPIO_MODULE_TIM3_CH3_PB0); //Запускаємо 3 канал ШІМ, выводем на пін PB0
PWM_TIM3_Start(_PWM_CHANNEL2, &_GPIO_MODULE_TIM3_CH2_PA7); // Запускаємо 2 канал ШІМ, выводем на пін PA7

while(1)
{

}

Підключивши осцилограф до висновків PA7 і PB0 можемо спостерігати наші сигнали:
image
Осцилограф вимірює діючі значення RMS, і ми можемо спостерігати що вони залежать від шпаруватості наших імпульсів. Цим можна користуватися для генерації ШИМом довільних сигналів, наприклад синусоїди. Давайте спробуємо це зробити:
Таймер 3 буде генерувати сигнал ШІМ, шпаруватість якого ми будемо змінювати за законом синусоїди. Для експерименту розіб'ємо період на 40 частин (з кроком в 9 градусів) і для них будемо обчислювати значення синуса, і записувати нормовані значення періоду ШИМа в регістр CCR потрібного нам каналу, тим самим змінюючи шпаруватість імпульсів і формуючи на виході піна таймера МК синусоїду. STM32 досить приозводительный МК, тому для невисоких частот, близько 100-н герц, можна користуватися безпосередньо Сишной функцією SIN(). Налаштуємо Таймер 2 на виклик переривань з частотою 4000 Гц. Враховуючи один період синусоїди у нас займає 40 відліків (360 / 9) ми повинні на екрані осцилографа побачити синусоїду з частотою 100 Гц.
unsigned int cpwm; //змінна для поточного значення шпаруватості імпульсів
unsigned int ugol = 0; //змінна для кута в градусах
const float PI=3.1416; //масив ПІ=3,1416

void main()

{
GPIO_Alternate_Function_Enable(&_GPIO_MODULE_TIM3_CH3_PB0);

// Налаштовуємо 3 таймер в режим генерації ШІМ сигналу частотою 72 КГц
RCC_APB1ENRbits.TIM3EN=1;
TIM3_CCERbits.CC3E=1;
TIM3_CCMR2_Outputbits.OC3M=0b110;
TIM3_CCR3 = 1000;
TIM3_ARR = 1999;
TIM3_PSC=0;
TIM3_CR1bits.CEN=1;

// Налаштовуємо 4 таймер для генерації переривань з частотою 4000 Гц
RCC_APB1ENR.TIM4EN = 1;
TIM4_CR1.CEN = 0;
TIM4_PSC = 719;
TIM4_ARR = 24; 
NVIC_IntEnable(IVT_INT_TIM4); // Дозволяємо вектор переривань від таймера TIM4
TIM4_DIER.UIE = 1; // Дозволяємо таймеру викликати переривання
TIM4_CR1.CEN = 1; //Включаємо таймер 4

while(1)
{

}
}
void Timer2_interrupt() iv IVT_INT_TIM4
{
TIM4_SR.UIF = 0;// Скидаємо прапор переривання 
cpwm= 1000 + (1000 * sin((PI*ugol)/180)); //Обчислюємо поточне значення синусоїди
TIM3_CCR3 = cpwm; //Пишемо обчислене значення в регістр таймера ССR
ugol = ugol + 9; //крок у 9 градусів
if (ugol > 359) ugol = 0; //замикаємо аргумент функції sin в її області визначення
}

сигнал з виходу нашого МК потрібно пропустити через фільтр низьких частот, для того щоб відсікти частоту, на якій працює генератор ШИМ. Так як частота нашої синусоїди 100 Гц, частоту зрізу фільтра виберемо 300 Гц.
image
Ось такий сигнал показує нам осцилограф:
image
Ось і підійшла до кінця друга частина нашого циклу. У наступній частині я спробую розповісти про роботу з послідовним інтерфейсом UART, і спробуємо використовувати можливості МК для зв'язку з модулем GSM.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.