середу, 9 листопада 2016 р.

STM32: Бібліотека Delay (мілісекунди, мікросекунди) на таймерах


Передмова

Звісно, штучних затримок в програмі потрібно уникати. І часові затримки краще організовувати за допомоги переривань по таймерам з подальшої обробкою. Але в невеличких проектах і там де виконання програми не критичне до затримок, дуже зручно використовувати прості функції delay. Нещодавно почав використовувати драйвер HAL де є вже функція затримки в мілісекундах. Функції затримки в мікросекундах на жаль там немає. Та і прив'язуватись до якогось високорівневого драйверу в такій простій задачі, як затримки, теж немає сенсу. Вирішив створити бібліотеку delay для своїх потреб з затримками в мілісекундах і мікросекундах. Щоб відлік часу був точним, обов'язково організувати відлік часу на таймерах. Щоб була відв'язана від високорівневих драйверів HAL чи SPL і мала мінімум налаштувань. Щоб достатньо було вказати який використовувати для затримок таймер.

Бібліотека delay

Бібліотека "delay" складається з двох файлів: delay.h і delay.c

Має такі функції:
  • void delayInit(void) - функція вмикання потрібного таймеру для відліку пауз;
  • void delayDeInit(void) - функція вимикання таймеру, як відлік пауз вже не потрібен;
  • void delayMs(volatile uint32_t delay) - функція відліку затримки в мілісекундах, на вхід ціле число мілісекунд
  • void delayUs(volatile uint32_t delay) - функція відліку затримки в мікросекундах, на вхід ціле число мікросекунд
В файлі delay.h обрати який таймер буде вести відлік часу. Можна обрати з TIM1, TIM2, TIM3 та TIM4. Як потрібен інший таймер, дописати в файлі delay.c необхідний таймер по аналогії.
Частота для правильного відліку часу береться зі змінної SystemCoreClock, це якщо частота шини таймеру співпадає з SystemCoreClock (дивись малюнок). Червоним обведено ключові місця:
Однакові частоти
Бувають випадки що частоти не співпадають, як, наприклад, на іншому малюнку:
Частоти не однакові
Як видно з малюнку, частота SYSCLK - 32MHz, а шина таймера на шині APB1 периферії вже тактується 8MHz. І шина таймерів на шині периферії APB2 тактується 16MHz. Це все завдяки дільникам і множникам шин APB. Тому будьте уважні і задавайте правильні частоти в герцах для таймерів в файлі delay.h. 
Звичайно можна визначити частоту автоматично за допомоги функцій драйверу HAL, наприклад такими як:
HAL_RCC_GetSysClockFreq(), HAL_RCC_GetHCLKFreq(), HAL_RCC_GetPCLK1Freq(), HAL_RCC_GetPCLK2Freq(), тощо.
Але ж хотілось відійти від високорівневих драйверів. Напевно є спосіб автоматизувати визначення частоти таймеру і без HAL чи SPL, але я ще не дійшов до цього :-)

delay.h


/*
 * my_delay.h
 *
 *  Created on: 20 груд. 2017 р.
 *      Author: Andriy
 */

#ifndef DELAY_H_
#define DELAY_H_

#include <stdint.h>

//Розкоментувати потрібний таймер для функцій затримки
#define TIMER1
// #define TIMER2
// #define TIMER3
// #define TIMER4

#ifdef TIMER1
#define CURRENT_TIMER               ((TIM_TypeDef *) TIM1)
#endif

#ifdef TIMER2
#define CURRENT_TIMER               ((TIM_TypeDef *) TIM2)
#endif

#ifdef TIMER3
#define CURRENT_TIMER               ((TIM_TypeDef *) TIM3)
#endif

#ifdef TIMER4
#define CURRENT_TIMER               ((TIM_TypeDef *) TIM4)
#endif

#define CURRENT_FREQ    SystemCoreClock

void delayInit(void);
void delayDeInit(void);
void delayMs(volatile uint32_t delay);
void delayUs(volatile uint32_t delay);

#endif /* DELAY_H_ */

delay.c


/*
 * delay.c
 *
 *  Created on: 20 груд. 2017 р.
 *      Author: Andriy
 */

#include <delay.h>
#include "stm32f1xx.h"

// Функція вмикання таймеру для потреб delay
void delayInit(void)
{
#ifdef TIMER1
 RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
#endif

#ifdef TIMER2
 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
#endif

#ifdef TIMER3
 RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
#endif

#ifdef TIMER4
 RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
#endif
}

// Функція вимикання таймеру як немає потреби в delay
void delayDeInit(void)
{
#ifdef TIMER1
 RCC->APB2ENR &= ~RCC_APB2ENR_TIM1EN;
#endif

#ifdef TIMER2
 RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN;
#endif

#ifdef TIMER3
 RCC->APB1ENR &= ~RCC_APB1ENR_TIM3EN;
#endif

#ifdef TIMER4
 RCC->APB1ENR &= ~RCC_APB1ENR_TIM4EN;
#endif
}

//Функція формування затримки в мілісекундах
void delayMs(volatile uint32_t delay)
{
 CURRENT_TIMER->PSC = CURRENT_FREQ/1000-1; //Встановлюємо подрібнювач
 CURRENT_TIMER->ARR = delay; //встановлюємо значення переповнювання таймеру, а також і значення при якому генеруеться подія оновлення
 CURRENT_TIMER->EGR |= TIM_EGR_UG; //Генерируемо Подію оновлення для запису даних в регістри PSC і ARR
 CURRENT_TIMER->CR1 |= TIM_CR1_CEN|TIM_CR1_OPM; //Запускаемо таймер записом биту CEN і встановлюємо режим Одного проходу встановленням біту OPM
 while ((CURRENT_TIMER->CR1) & (TIM_CR1_CEN!=0)); //Виконуємо цикл поки рахує таймер до нуля
}

//Функція формування затримки в мікросекундах
void delayUs(volatile uint32_t delay)
{
    CURRENT_TIMER->PSC = CURRENT_FREQ/1000000-1; ///Встановлюємо подрібнювач
 CURRENT_TIMER->ARR = delay; //встановлюємо значення переповнювання таймеру, а також і значення при якому генеруеться подія оновлення
 CURRENT_TIMER->EGR |= TIM_EGR_UG; //Генерируемо Подію оновлення для запису даних в регістри PSC і ARR
 CURRENT_TIMER->CR1 |= TIM_CR1_CEN|TIM_CR1_OPM; //Запускаемо таймер записом биту CEN і встановлюємо режим Одного проходу встановленням біту OPM
 while ((CURRENT_TIMER->CR1) & (TIM_CR1_CEN!=0)); //Виконуємо цикл поки рахує таймер до нуля
}

Як користуватись

Скопіюйте теку, або файли бібліотеки до теки свого проекту. Приєднайте до свого проекту теку, або файли бібліотеки. До головного файлу main.c додайте #include "delay.h". В файлі "delay.h" зазначте який таймер для відліку затримок будете використовувати. І при потребі, частоту шини таймерів в герцах. Коли потрібна затримка просто викличте одну із функцій delayMs(xxx) чи delayUs(xxx) - де xxx часова затримка в мілісекундах чи мікросекундах. Наприклад, delayMs(500) - означає почекаємо пів секунди. Та не забувайте перед викликом цих функцій увімкнути таймер викликом функції delayInit().

Архів з бібліотекою

Завантажити архів з бібліотекою - download

Немає коментарів:

Дописати коментар