Функция задержки STM32.
Первое на что обратил внимание, после перехода с AVR на STM32 — это отсутствие привычных функций, для реализации задержки: delay_us() и delay_ms(), ну отсутствуют так отсутствуют, подумал тогда и если надо было реализовать задержку, то делал это так.
А переменную time_delay подбирал экспериментально. Нет, конечно же, первым делом, до того как реализовать задержку на цикле for(), искал в интернете как это делают люди. В общем, нашёл реализацию на systick, но сходу разбираться с работой таймера не было желания и всё-таки хотелось найти более простой способ.
Шло время и в одном проекте всё-таки понадобилось чуть точнее отсчитывать временные интервалы, чем это позволял цикл for(), ситуация сложилось так, что после каждого чтения микросхемы надо было выждать паузу, пока она обновит значение регистров, а данных надо было считывать много и после этого их надо было ещё выводить на TFT дисплей, в итоге дисплей перерисовывался очень редко и выглядело это уныло.
Решение было найдено случайно, в одной из прошлых статей описывал как работает DWT, вот на нём и реализовал.
Уже лучше, но данные функции сами являются источником ошибки, во-первых при вызове функции регистры пакуются в стек, во-вторых вычисляется количество тактов, обнуляется и запускается счётчик, а в итоге это всё надо просуммировать и при задержке в несколько микросекунд ошибка будет значительной. И так, что мы можем улучшить: один раз, при инициализации разрешить использовать счётчик, записать в счётный регистр ноль, запустить счётчик и больше не выключать.
Готово, теперь осталось реализовать функцию задержки.
Данная реализация работает даже, если после присваивания t0 произошло переполнение счётчика. Давайте рассмотрим два варианта развития событий, но для простоты, вместо uint32_t будем оперировать типом uint8_t.
t0 = 1, us_count_tic = 100, а DWT->CYCCNT = 10, тогда получаем (uint8_t)(10 - 1) < 100, значение счётчика будет расти и когда оно дойдет до 101 выполнение цикла прекратится.
Если t0 = 255 получаем (uint8_t)(10 - 255) < 100, так как delta возвращает значение типа uint32_t(в данном случае заменил его на uint8_t), то (uint8_t)(10 - 255) = 11 и ситуация развивается аналогично предыдущему примеру.
На этом всё, надеюсь кому-то статья окажется полезной.
void delay(uint32_t time_delay)
{
uint32_t i;
for(i = 0; i < time_delay; i++);
}
А переменную time_delay подбирал экспериментально. Нет, конечно же, первым делом, до того как реализовать задержку на цикле for(), искал в интернете как это делают люди. В общем, нашёл реализацию на systick, но сходу разбираться с работой таймера не было желания и всё-таки хотелось найти более простой способ.
Шло время и в одном проекте всё-таки понадобилось чуть точнее отсчитывать временные интервалы, чем это позволял цикл for(), ситуация сложилось так, что после каждого чтения микросхемы надо было выждать паузу, пока она обновит значение регистров, а данных надо было считывать много и после этого их надо было ещё выводить на TFT дисплей, в итоге дисплей перерисовывался очень редко и выглядело это уныло.
Решение было найдено случайно, в одной из прошлых статей описывал как работает DWT, вот на нём и реализовал.
#define DWT_CYCCNT *(volatile unsigned long *)0xE0001004
#define DWT_CONTROL *(volatile unsigned long *)0xE0001000
#define SCB_DEMCR *(volatile unsigned long *)0xE000EDFC
void delay_us(uint32_t us)
{
int32_t us_count_tick = us * (SystemCoreClock/1000000);
//разрешаем использовать счётчик
SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
//обнуляем значение счётного регистра
DWT_CYCCNT = 0;
//запускаем счётчик
DWT_CONTROL |= DWT_CTRL_CYCCNTENA_Msk;
while(DWT_CYCCNT < us_count_tick);
//останавливаем счётчик
DWT_CONTROL &= ~DWT_CTRL_CYCCNTENA_Msk;
}
//////////////////////////////
void delay_ms(uint32_t ms)
{
int32_t ms_count_tick = ms * (SystemCoreClock/1000);
//разрешаем использовать счётчик
SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
//обнуляем значение счётного регистра
DWT_CYCCNT = 0;
//запускаем счётчик
DWT_CONTROL|= DWT_CTRL_CYCCNTENA_Msk;
while(DWT_CYCCNT < ms_count_tick);
//останавливаем счётчик
DWT_CONTROL &= ~DWT_CTRL_CYCCNTENA_Msk;
}
Уже лучше, но данные функции сами являются источником ошибки, во-первых при вызове функции регистры пакуются в стек, во-вторых вычисляется количество тактов, обнуляется и запускается счётчик, а в итоге это всё надо просуммировать и при задержке в несколько микросекунд ошибка будет значительной. И так, что мы можем улучшить: один раз, при инициализации разрешить использовать счётчик, записать в счётный регистр ноль, запустить счётчик и больше не выключать.
#define DWT_CYCCNT *(volatile unsigned long *)0xE0001004
#define DWT_CONTROL *(volatile unsigned long *)0xE0001000
#define SCB_DEMCR *(volatile unsigned long *)0xE000EDFC
void DWT_Init(void)
{
//разрешаем использовать счётчик
SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
//обнуляем значение счётного регистра
DWT_CYCCNT = 0;
//запускаем счётчик
DWT_CONTROL |= DWT_CTRL_CYCCNTENA_Msk;
}
Готово, теперь осталось реализовать функцию задержки.
static __inline uint32_t delta(uint32_t t0, uint32_t t1)
{
return (t1 - t0);
}
void delay_us(uint32_t us)
{
uint32_t t0 = DWT->CYCCNT;
uint32_t us_count_tic = us * (SystemCoreClock/1000000);
while (delta(t0, DWT->CYCCNT) < us_count_tic) ;
}
Данная реализация работает даже, если после присваивания t0 произошло переполнение счётчика. Давайте рассмотрим два варианта развития событий, но для простоты, вместо uint32_t будем оперировать типом uint8_t.
t0 = 1, us_count_tic = 100, а DWT->CYCCNT = 10, тогда получаем (uint8_t)(10 - 1) < 100, значение счётчика будет расти и когда оно дойдет до 101 выполнение цикла прекратится.
Если t0 = 255 получаем (uint8_t)(10 - 255) < 100, так как delta возвращает значение типа uint32_t(в данном случае заменил его на uint8_t), то (uint8_t)(10 - 255) = 11 и ситуация развивается аналогично предыдущему примеру.
На этом всё, надеюсь кому-то статья окажется полезной.
Похожие статьи