Передмова
Звісно, штучних затримок в програмі потрібно уникати. І часові затримки краще організовувати за допомоги переривань по таймерам з подальшої обробкою. Але в невеличких проектах і там де виконання програми не критичне до затримок, дуже зручно використовувати прості функції 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, але я ще не дійшов до цього :-)
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
Немає коментарів:
Дописати коментар