Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Как сделать программную задержку на STM32
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2
Tahoe
Цитата(polyname @ Feb 1 2013, 16:11) *
а не как у многих тут, которые суют деления/умножения, и даже плавучку в функцию задержки.

Если речь про мой код, то могу посоветовать только голову включить. Хотел бы я посмотреть на компилер, препроцессор которого, увидев констатное 8000000/1000, пропустит мимо и отправит вычисляться в рантайм.


Вот что действительно имеет смысл поправить, так это вместо
Код
if (!(SCB_DEMCR & 0x01000000))

для единообразия:
Код
if (!(CoreDebug->DEMCR & 0x01000000))
demiurg_spb
Код
void dwt_init(void)
{
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CYCCNT       = 0;                            // reset
    DWT->CTRL        |= 1;                            // enable the counter
}

static __inline uint32_t dwt_dt(uint32_t t0, uint32_t t1)
{
    return (t1 - t0); // всегда верно, даже если t1<t0
}

void dwt_delay(uint32_t us) // microseconds (max = 2^32 / (fcpu*1.0E-6) - 1)
{
    uint32_t t0 = DWT->CYCCNT;
    uint32_t dt = us * (F_CPU/1000000UL); // 1us = 72tics @ 72MHz

    while (dwt_dt(t0, DWT->CYCCNT) < dt)  {;}
}
И нет проблем при попадании момента переполнения счётчика внутрь измеряемого диапазона времени...
Естественно максимальный диапазон не должен превышать одного периода dwt таймера, то бишь типа uint32_t.
megajohn
Цитата(KnightIgor @ Jun 28 2012, 11:42) *
Счетчик надо проинициализировать (включить): - должен подкорректировать, т.к. признаком уже включенного таймера должен служить бит в SCB_DEMCR, а не в DWT_CONTROL - связано с работой под отладчиком.
Код

    if (!(SCB_DEMCR & 0x01000000))
    {
...
    }


извиняюсь что поднял старый пост. Но у меня так не тикало на LPC1778 - то есть бит DEMCR_TRCENA был установлен, а DWT->CTRL сброшен. Может всё-таки так ?

Код
    if( !( CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk ) || !( DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk ) )
    {
        DWT->CYCCNT  = 0;
                            CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
        DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // enable the counter
    }



кстати, кому интересно, расширил разрядность DWT до 64 бит
Код
//----------------------------------------------------------------------------
u64 core_get_cycles_counter( void )
{
    TN_INTSAVE_DATA

    u64 ret = 0;        
    static u32 cch = 0, ccl_prev = 0;
    u32 ccl = DWT->CYCCNT;
    
    tn_disable_interrupt();
    if( ccl_prev > ccl )
        cch++;
    ret = ((u64)cch << 32) + ccl;
    ccl_prev = ccl;
    tn_enable_interrupt();
    
    return ret;
}

//----------------------------------------------------------------------------
u64 get_sys_mks( void )
{
    u64 ret = core_get_cycles_counter() / ( F_CPU / 1000000UL );
    return ret;
}

P.S. в векторе SysTick_Handler системного тика каждую секунду считывает core_get_cycles_counter чтобы DWT->CYCCNT не переполнился более одного раза
Rash
на новых мк приходится штриховать условие (//)
Код
if (!(SCB_DEMCR & 0x01000000))

потом расштриховываешь и всё нормально работает при отладке. Правда это на STM32F4/
С чем связано ещё не разбирался
toweroff
Тут тоже набрел на решение, которое, как мне кажется, зависит только от тактовой частоты - это время доступа с внутреннему ОЗУ
И тогда довольно точно можно подгонять задержки
Код
volatile uint32_t trash_in, trash_out;

for (uint32_t i=0; i<delay; i++) trash_in = trash_out;
megajohn
Цитата(toweroff @ Aug 7 2013, 11:13) *
Тут тоже набрел на решение, которое, как мне кажется, зависит только от тактовой частоты - это время доступа с внутреннему ОЗУ
И тогда довольно точно можно подгонять задержки


кто юзал IAR и переходил с AVR на CM3 всегда недоумевали - "куда делись привычные __delay_cycles( N ) ?"
а ответ простой - в CM3 есть DMA, который перехватывает системную шину и собственно приостанавливает доступ. Так что на ваших for(i<const) ничего толкового не получишь.

P.S. И как мне говорили, есть еще какие-то факторы. Так что если кто подскажет буду признателен
Rimsky
Цитата(ukpyr @ Jun 28 2012, 18:01) *
не понятно зачем пляски с бубном, выше приводились задержки на инлайн-асме - работают отлично

+100

Вот код под IAR
Код
#define __delay_us( US ) __delay_loops( (uint32_t)((double)US * F_CPU / 3000000.0))
#define __delay_ms( MS ) __delay_loops( (uint32_t)((double)MS * F_CPU / 3000.0))
#define __delay_s( S )   __delay_loops( (uint32_t)((double)S  * F_CPU / 3.0))

#pragma inline=forced
void __delay_loops(uint32_t loops){
        asm(    "Metka:    SUBS     %[R1], %[R1], %[imm] \n"
                "       BNE.N    Metka        \n"
                : [R1]"+r" (loops)
        : [imm]"i"(1));
        };

VAI
Цитата(Rimsky @ Aug 12 2013, 06:14) *
Вот код под IAR....

Постом выше megajohn написал, почему Ваши задержки будут кривыми... + к этому, ещё и срабатывание прерываний во время исполнения циклов.
ukpyr
DMA далеко не всегда используется.

можно добавить запрет прерываний:
Код
#define cli() asm volatile ( "CPSID i \n" )
#define sei() asm volatile ( "CPSIE i \n" )

#define __delay_us_cli( US ) cli(); __delay_us( US ); sei();


другой вариант - выделить для задержек таймер.
MrYuran
Цитата(ukpyr @ Aug 12 2013, 12:30) *
можно добавить запрет прерываний:

А можно из буханки ржаного хлеба..
ну вы понимаете, о чем я..
Rimsky
Цитата(VAI @ Aug 12 2013, 18:03) *
Постом выше megajohn написал, почему Ваши задержки будут кривыми... + к этому, ещё и срабатывание прерываний во время исполнения циклов.

Да, проверил, есть такой косяк. Сделал по другому, может уже и не в тему топика, поскольку заюзал таймер:

Код
#define F_CPU    24000000UL

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM6->CR1 |= TIM_CR1_CEN;


// Процедура формирования линейной задержки
#pragma inline=forced
void __delay_loops(uint16_t loops)
{
    TIM6->CNT = 0x0000;
    while(loops > TIM6->CNT);
}

#define __delay_us( US ) __delay_loops( (uint16_t)(US * (uint16_t)(F_CPU / 1000000UL)) )

#pragma inline=forced
void __delay_ms(uint16_t ms){
    do{     
        __delay_us(1000);
    }
    while(ms--);
};
Kot_dnz
Цитата(Rash @ Aug 7 2013, 09:52) *
на новых мк приходится штриховать условие (//)
Код
if (!(SCB_DEMCR & 0x01000000))

потом расштриховываешь и всё нормально работает при отладке. Правда это на STM32F4/
С чем связано ещё не разбирался

О, а можно с этого места по подробнее.
Подключен stm32f103, из IAR прошиваю через ST-LINK, функция расшифровки ногодрыга реализована через DWT - все работает как ожидалось.
Сбрасываю питание - функция перестает работать. Никакие электролиты и пр на питании не помогают. Остальные функции работают как нужно.
CODE
void DWT_Init(void)
{
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}

uint32_t DWT_Get(void)
{
return DWT->CYCCNT;
}

inline uint8_t DWT_Compare(int32_t tp)
{
return (((int32_t)DWT_Get() - tp) < 0);
}

void DWT_Delay(uint32_t us) // microseconds
{
int32_t tp = DWT_Get() + us * (F_CPU/1000000);
while (DWT_Compare(tp));
}

что я делаю не так?

Цитата(Kot_dnz @ Sep 28 2014, 13:15) *
[code]void DWT_Init(void)
{
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}


Сорри, нашел - не хватало строки
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
интересное проявление баго-фичи...
etoja
Можно ещё выдавать байт в неиспользуемый SPI, UART или дёргать ножки вывода. Эти операции тоже идут долго.
AVR
Цитата(Firer @ Feb 10 2012, 16:51) *
Нужно написать библиотеку работы с LCD.
Там нужна задержка около 1мкс.

Я решаю это так (и на STM32 и на MDR32 и на других):
Код
unsigned int delay_ms(unsigned int ms)
{
    volatile unsigned int i, t = 0;
    //for(i = 0; i < (ms*400); i++) t++; // @ 8 MHz
    for(i = 0; i < (ms*5714); i++) t++;
    return t;
}

unsigned int delay_10us(unsigned int us)
{
    volatile unsigned int i, t = 0;
    //for(i = 0; i < (us*4); i++) t++; // @ 8 MHz
    for(i = 0; i < (us*57); i++) t++;
    return t;
}

Сначала мигаю светодиодом на delay_ms(1000) - периоды были несколько секунд - так вычисляю во сколько раз мне нужно меньше чтобы была как можно ближе к одной секунде. Получил число допустим 5714. Ну а 10 мкс - это в 100 раз меньше - 57.

Знаю какие недостатки этого метода, но лично мне нравится и всё работает стабильно sm.gif
kolobok0
Цитата(AVR @ Oct 3 2014, 11:00) *
Код
    //for(i = 0; i < (ms*[b]400[/b]); i++) t++; // @ 8 MHz
    for(i = 0; i < (ms*[b]5714[/b]); i++) t++;

    //for(i = 0; i < (us*[b]4[/b]); i++) t++; // @ 8 MHz
    for(i = 0; i < (us*[b]57[/b]); i++) t++;

...мне нравится и всё работает стабильно sm.gif


почему бы и нет?
только маленьчкое замечание: цифры я бы вычислял бы в зависимости от дефайна тактовой. Тем более в STM вроде как они объявлены. Тогда писать новые строчки под разную частоту не понадобиться.
И по закону мерфи - меньше кода, меньше багов...

ЗЫ
И ещё бы добавил ифдеф для делителей меньше 1 или 2... Типа вонинг! не допустимо маленький делитель(умножитель - кому как sm.gif ) для данных функций.
menzoda
kolobok0, AVR

Для большей точности и повторяемости можно реализовать на ассемблере, получится совсем немного инструкций, можно будет точнее вычислить кол-во тактов на один цикл, не будет плавать из-за смены опций компиляции. Чтобы не мешали прерывания можно их запрещать на время задержки.
Сергей Борщ
Цитата(kolobok0 @ Oct 3 2014, 10:27) *
только маленьчкое замечание:
И настройки оптимизации, а также версию компилятора прибить гвоздями к этому исходнику. Чтобы времянки случайно не уплыли.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.