Создание наносекундной задержки в C на STM32

Я использую контроллер STM32F2 и взаимодействую с ST7036 ЖК-дисплей через 8-битный параллельный интерфейс.

В техническом описании говорится, что между удержанием адреса и временем настройки должна быть задержка в 20 наносекунд.

Как создать задержку в 20 наносекунд в C?


person pranathi    schedule 14.11.2012    source источник
comment
Вы пробовали функцию nanosleep()? Примечание: вам нужно включить <time.h>, чтобы использовать его.   -  person    schedule 14.11.2012
comment
Вам не нужно делать задержки ns. Это минимальные задержки по даташиту, но можно и больше. Кроме того, почему вы не хотите использовать SPI или I2C? Это намного проще, и вы можете отправлять данные одним пакетом. Так вы освободите контроллер для других задач.   -  person Bulkin    schedule 02.02.2018


Ответы (3)


Используйте stopwatch_delay(4) ниже, чтобы выполнить задержку приблизительно 24 нс. Он использует регистр STM32 DWT_CYCCNT, специально предназначенный для подсчета фактических тактовых импульсов, расположенный по адресу 0xE0001004.

Чтобы проверить точность задержки (см. main), вы можете позвонить STOPWATCH_START, запустить stopwatch_delay(ticks), затем позвонить STOPWATCH_STOP и проверить с помощью CalcNanosecondsFromStopwatch(m_nStart, m_nStop). Отрегулируйте ticks по мере необходимости.

uint32_t m_nStart;               //DEBUG Stopwatch start cycle counter value
uint32_t m_nStop;                //DEBUG Stopwatch stop cycle counter value

#define DEMCR_TRCENA    0x01000000

/* Core Debug registers */
#define DEMCR           (*((volatile uint32_t *)0xE000EDFC))
#define DWT_CTRL        (*(volatile uint32_t *)0xe0001000)
#define CYCCNTENA       (1<<0)
#define DWT_CYCCNT      ((volatile uint32_t *)0xE0001004)
#define CPU_CYCLES      *DWT_CYCCNT
#define CLK_SPEED         168000000 // EXAMPLE for CortexM4, EDIT as needed

#define STOPWATCH_START { m_nStart = *((volatile unsigned int *)0xE0001004);}
#define STOPWATCH_STOP  { m_nStop = *((volatile unsigned int *)0xE0001004);}


static inline void stopwatch_reset(void)
{
    /* Enable DWT */
    DEMCR |= DEMCR_TRCENA; 
    *DWT_CYCCNT = 0;             
    /* Enable CPU cycle counter */
    DWT_CTRL |= CYCCNTENA;
}

static inline uint32_t stopwatch_getticks()
{
    return CPU_CYCLES;
}

static inline void stopwatch_delay(uint32_t ticks)
{
    uint32_t end_ticks = ticks + stopwatch_getticks();
    while(1)
    {
            if (stopwatch_getticks() >= end_ticks)
                    break;
    }
}

uint32_t CalcNanosecondsFromStopwatch(uint32_t nStart, uint32_t nStop)
{
    uint32_t nDiffTicks;
    uint32_t nSystemCoreTicksPerMicrosec;

    // Convert (clk speed per sec) to (clk speed per microsec)
    nSystemCoreTicksPerMicrosec = CLK_SPEED / 1000000;

    // Elapsed ticks
    nDiffTicks = nStop - nStart;

    // Elapsed nanosec = 1000 * (ticks-elapsed / clock-ticks in a microsec)
    return 1000 * nDiffTicks / nSystemCoreTicksPerMicrosec;
} 

void main(void)
{
    int timeDiff = 0;
    stopwatch_reset();

    // =============================================
    // Example: use a delay, and measure how long it took
    STOPWATCH_START;
    stopwatch_delay(168000); // 168k ticks is 1ms for 168MHz core
    STOPWATCH_STOP;

    timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
    printf("My delay measured to be %d nanoseconds\n", timeDiff);

    // =============================================
    // Example: measure function duration in nanosec
    STOPWATCH_START;
    // run_my_function() => do something here
    STOPWATCH_STOP;

    timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
    printf("My function took %d nanoseconds\n", timeDiff);
}
person bunkerdive    schedule 01.10.2013
comment
Вы уверены, что это сработает? Цикл инструкций будет составлять около 5 нс. Очевидно, что код использует более 5 инструкций. Таким образом, минимальное время будет 25 нс... Однако аппаратная задержка может быть намного меньше 25 нс. - person richieqianle; 31.07.2014
comment
Ага. Код должен быть изменен по мере необходимости. Конечно, можно было бы просто использовать минимум необходимых фрагментов, или в идеале пользователь этого кода должен запустить __no_operation() тысячу раз в цикле внутри main() (например, где run_my_function()), чтобы получить наносекундный секундомер для 1000 запусков, а затем просто разделить это число на 1000, чтобы увидеть, как долго один конвейерный вызов __no_operation() занимает рассматриваемую систему... и затем использовать по желанию. - person bunkerdive; 31.07.2014
comment
Просто комментарий, 1000 NOP/1000 не могут быть равны 1 NOP. В любом случае, отличное объяснение! - person richieqianle; 31.07.2014
comment
Правда, он лишь приближается к конвейерному NOP. Так что да, чем меньше NOP используется, тем больше измерение будет отклоняться от реальности (в некоторой степени). - person bunkerdive; 31.07.2014
comment
Несколько замечаний: 1) если CPU_CYCLES является автономным таймером, это не сработает, когда он приблизится к 0xFFFFFFFF, потому что end_ticks переполнится, а затем stopwatch_getticks() >= end_ticks немедленно выйдет. 2) 1000 * nDiffTicks переполнится, если вы оставите секундомер включенным более чем на 26ms (если я правильно рассчитал). 3) Непонятно, почему ваши макросы STOPWATCH_START/STOPWATCH_STOP не используют DWT_CYCCNT, поскольку он уже определен. 4) Для общего решения stopwatch_reset - плохая идея, так как это мешает вам использовать секундомер из нескольких прерываний. - person Groo; 02.12.2019

Первая найденная мной спецификация Stm32f2 предполагает тактовую частоту 120 МГц. Это около 8 нс за такт. Вам потребуется около трех инструкций одного цикла между последовательными операциями записи или чтения/записи. В C, вероятно, подойдет a++; (если a находится в стеке).

person Aki Suihkonen    schedule 14.11.2012
comment
Да - точно - все ответы, но этот дает решения, которые потребуют в 100 раз больше времени, чем требуется... 20 нс - это всего лишь несколько циклов, нескольких NOP в сборке будет более чем достаточно... - person Freddie Chopin; 23.11.2014
comment
Не было бы хорошо проверить задержку, используя регистр счетчика циклов, специально разработанный для этой цели, независимо от того, какой метод задержки используется? В противном случае, я думаю, это можно было бы проверить с помощью осциллографа и некоторых цифровых контактов. - person bunkerdive; 21.04.2017
comment
Вышеприведенная функция stopwatch_delay() прекрасно справляется с этой задачей, и ее можно проверить или использовать для разных длин задержки. - person bunkerdive; 21.04.2017

Вы должны изучить периферийное устройство FSMC, доступное в вашем чипе. Хотя конфигурация может быть сложной, особенно если вы не подключаете часть памяти, для которой она была разработана, вы можете обнаружить, что ваше устройство с параллельным интерфейсом довольно хорошо отображает один из режимов интерфейса памяти.

Такие виды внешних контроллеров памяти должны иметь множество настраиваемых параметров синхронизации для поддержки ряда различных микросхем памяти, чтобы вы могли гарантировать тайминги, требуемые вашей таблицей данных.

Приятное преимущество возможности сделать это заключается в том, что ваш ЖК-дисплей будет выглядеть как любое старое периферийное устройство с отображением памяти, абстрагируя детали интерфейса более низкого уровня.

person asund    schedule 29.01.2018