Счётчик DWT

    Маленькая заметка об очень полезной вещице входящей в состав модуля DWT (Data Watchpoint and Trace unit) имеющегося у stm32.

    Сам по себе модуль DWT достаточно сложная штуковина (почитать можно тут стр. 75), а отвечает он за отладку. Однако речь не о модуле в целом, а об одной из его составляющих — счётчика тактов (далее счётчик DWT).

    По сути, счётчик DWT это просто 32-х битный регистр, значение в котором увеличивается на единичку с каждым последующим тактом. Мы можем писать в этот регистр и читать из него, а значит можем с его помощью измерять время выполнения каких-то кусков программы в тактах, и организовывать микросекундные задержки. Этот счётчик полностью независимый.

    Чтоб измерить время выполнения какого-либо участка программы или функции, нужно сделать так…

      #define    DWT_CYCCNT    *(volatile uint32_t*)0xE0001004
      #define    DWT_CONTROL   *(volatile uint32_t*)0xE0001000
      #define    SCB_DEMCR     *(volatile uint32_t*)0xE000EDFC
    
      char str[16] = {0,};
      uint32_t count_tic = 0;
    
      SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;// разрешаем использовать DWT
      DWT_CONTROL|= DWT_CTRL_CYCCNTENA_Msk; // включаем счётчик
      DWT_CYCCNT = 0;// обнуляем счётчик
    
      // здесь кусок измеряемого участка программы
    
      count_tic = DWT_CYCCNT; // кол-во тактов
      snprintf(str, 16, "Takt %lu\n", count_tic);
      HAL_UART_Transmit(&huart1, (uint8_t*)str, strlen(str), 1000);
    

    image
    Измерил HAL_Delay(1000) на частоте 72МГц.

    Для организации микросекундных пауз нужно создать файл delay_micros.h

    #ifndef __DELAY_US_H__
    #define __DELAY_US_H__
    
    #ifdef __cplusplus
     extern "C" {
    #endif
    
    /******************************************************************************/
    /* Подключение заголовочных файлов используемых модулей */
    #include "main.h"
    
    //#define DWT_CONTROL *(volatile unsigned long *)0xE0001000
    //#define SCB_DEMCR   *(volatile unsigned long *)0xE000EDFC
    
    /******************************************************************************/
    /* inline func */
    __STATIC_INLINE void DWT_Init(void)
    {
    	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // разрешаем использовать счётчик
    	DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;   // запускаем счётчик
    }
    
    __STATIC_INLINE void delay_us(uint32_t us)
    {
    	uint32_t us_count_tic =  us * (SystemCoreClock / 1000000U);
    	DWT->CYCCNT = 0U;
    	while(DWT->CYCCNT < us_count_tic);
    }
    
    #ifdef __cplusplus
    }
    #endif
    #endif //__DELAY_US_H__
    

    … и добавить его в проект.

    В main.c создать инклюд…

    #include "delay_micros.h"
    

    Перед бесконечным циклом инициализировать счётчик…

    DWT_Init();
    

    А задержку делаем так…

    delay_us(100); // 100 микросекунд
    

    Готовый файл можно взять тут.

    Это всё.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 21

      0
      Функция задержки объявлена как delay_us(), а вызывается delay_micros() :) Не заработает однако :)
      А я стараюсь избегать блокирующих задержек. Использую прерывание таймера и набор функций для организации в том числе и задержек:
      // создаем новый таймер обратного отсчета с заданием ему начального значения 100 мсек
      Int8U caltimer = TIMER_NewCountDown(100);
      // проверяем, что таймер еще не истек
      while (TIMER_GetCountDown(caltimer) > 0)
      {
      	// тут можем делать что-то пока ждем истечения таймера
      }
      // тут делаем что-то после истечения таймера
      ...
      // заводим этот же таймер заново на 500 мсек
      TIMER_SetCountDown(caltimer, 500);
      ...
      ...
      // уничтожаем таймер когда необходимость в нем отпала
      TIMER_DeleteCountDown(caltimer);
      


      Вот кусок моего заголовочного файла таймеров:
      void TIMER_Config();
      void TIMER_Enable();
      void TIMER_Disable();
      
      Int32U TIMER_GetTicks();
      Int32U TIMER_GetMSeconds();
      Int32U TIMER_GetSeconds();
      
      // Start new countdown timer with starting value -msecs- and return timer number
      Int8U TIMER_NewCountDown(Int32U msecs);
      
      // Stop and delete countdown timer by its number -timer-
      void TIMER_DeleteCountDown(Int8U timer);
      
      // Return remain value countdown timer by its number -timer-
      Int32U TIMER_GetCountDown(Int8U timer);
      
      // Set new value in countdown timer by its number -timer-
      void TIMER_SetCountDown(Int8U timer, Int32U msecs);
      


      Правда, с микросекундными задержками так особо не поработаешь :)
        0
        «Не заработает однако :)» — исправил. Спасибо.
          0
          Попробуйте многозадачную ОС, например freeRTOS, она позволит вернуться обратно к непрерывному коду из дебрей лапши калбэков. Калбэки, конечно, один из самых эффективных и наименее затратных методов организации кода, но уж очень сложны в написании и отладке. Этим они напоминают ассемблер. И, как и в случае ассемблера, их лучше применять только в критичных секциях, а в остальной части проекта избегать.
            0

            Так тут нет кэлбэков, я вообще очень редко прибегаю к ним :)

              –3
              Калбэки, конечно, один из самых эффективных и наименее затратных методов организации кода, но уж очень сложны в написании и отладке.


              Тег сарказм открыт.
              Правильно! И ни в коем случае не учитесь ничему новому и не развивайтесь. Пусть безопасностью занимается компилятор — хакеры уже написали Rust для себя значит и всем пойдет. Ну пишите ничего сложного. Уже сейчас сложно, а через десяток лет, скатившиеся до уровня обезьян вообще ничего не поймут.
              Тег сарказм закрыт.

              Грустно. Реально грустно все это. И пример который тупо съедает ресурс и жрет батарейку… А счетчик тактов штука классная. Если ее правильно использовать.
                0
                Грустно отлаживать какой-либо сложный протокол с состояниями и отвалом по таймауту на калбэках. Когда так писали код и задачи были попроще.
                  0
                  Ну почему каждый раз одно и то же? Да не задачи были проще. Они сложнее были. Памяти меньше, флеша (если вообще флеша) меньше, скорость меньше, тактов на инструкцию больше.

                  Другое дело, что не был контроллер таким монстром как сейчас. И протокол коммуникационный обрабатывай, и кнопки с дисплеем для интерфейса, и мотором еще покрути… В итоге получается что загрузка-то как была предельной, так и остается. И если она действительно предельная, то с FreeRTOS или любым ее аналогом ловить нечего. Да даже если и не предельная. Давайте возьмем простой и популярный протокол ModBus (пусть будет RTU). Хотите сказать что под FreeRTOS с ее миллисекундным квантом планировщика получится отследить ее таймауты в 1,5 (межсимвольный) и 3,5 (межпакетный) символы? Там время меньше миллисекунды, а точность таймера не точнее той самой миллисекунды. Я, к слову, ни одной реализации не видел. Такой чтобы спецификации соответствовала. Везде такая гадость написана — не дай бог. А уж отброса пакета при превышении межсимвольного, но не достижении межпакетного вообще не видел. В спецификации есть, а сделать не могут.

                  Мир вокруг асинхронен. А если код написан так, что в единой бетономешалке все такты съедаются, то возможность вместо одной три-пять таких мешалок поиметь — временное решение. По сути не решение, а снятие симптомов.

                  «Щёлкни кобылу в нос — она махнет хвостом». Вся наша жизнь по определению асинхронна. Триггер — событие. Будильник — подъем, начало рабочего дня — работа, полдень — обед, конец рабочего дня — домой. Изредка другие триггеры, типа зарплата — откладываем заначку. Или хватает заначки — новый телефончик. В промежутке, если одно событие обработано а другое еще не наступило — отдых и сон. Так и код писать надо. А не уподобляться деклассированным элементам к барыгам за веществами постоянно приходящими с фразой «есть чо?»

                  FreeRTOS хороший продукт. Уж к 9-ой то версии он стал очень хорошим. Но если микроскоп хорош, то это не повод забивать им гвозди (а что, он тоже тяжелый и основание массивное и цельнометаллическое).

                  Пора, наверное, писать статью. Надоело одно и то же из раза в раз объяснять. Собраться бы только…
                    0
                    Конечно напишите статью.
                    Чтоб контроллер не был монстром, а именно: количество прерываний должно быть сильно меньше числа возможных событий, никаких уровней прерываний. И хардварных таймеров нельзя налепить на каждый чих, потому что их сильно меньше, чем хотелось бы.
                    Чтоб нужно было чётко управлять кучей ключей/моторов и не провтыкать тайминг коммуникационного интерфейса или датчиков.
                    Чтоб полная асинхронщина без проверок флагов в карусели, если я правильно вас понял.
                    Хотите сказать что под FreeRTOS с ее миллисекундным квантом планировщика получится отследить ее таймауты в 1,5 (межсимвольный) и 3,5 (межпакетный) символы?
                    Не работал с ней, но там точно нельзя сделать квант времени меньше (да, производительность несколько упадёт) или вообще прикрутить сбоку ловлю интервалов модбаса?
                      0
                      полная асинхронщина без проверок флагов в карусели


                      Именно. И это вполне выполнимо. Перенос обработки событий с одного контекста на другой (с обработчика прерываний периферии на обработчик таймера, допустим) совсем не сложная задача. Больше того, в стародавние времена она решалась практически повсеместно и не воспринималась как нечто из ряда вон.

                      контроллер не был монстром, а именно...


                      И об этом я уже писал. Правда в комментариях к другим статьям. Сложность решения должна соответствовать сложности системы. Для сложных систем совсем не зазорно использовать приоритеты в контроллере прерываний. Больше того, это будет даже приветствоваться. Количество прерываний vs количество событий (внешних, как я понимаю)? Ну, их и так будет меньше. Просто потому, что любая шина как правило содержит одно (редко два) прерывания, а событий генерит… Тот же RS-232 — тут и TXEMPTY/RXEMPTY и TXCOMPLETE/RXCOMPLETE и UNDERRUN/OVERRUN. И это один из самых простых интерфейсов. Что до таймеров, то мне их обычно хватает. Как правило тех, которые временные интервалы нарезают нужно всего два (uSec и mSec) — все остальные интервалы и связанные с ними события строятся на их основе. Остальное — это ШИМ'ы или что-то похожее.

                      … и не провтыкать тайминг ...

                      Так, собственно, именно ради этого и исключаем ОС c ее непрогнозируемыми, кратными 1mSec задержками. Впрочем, да. Тут есть некоторая опасность того, что флуд по одному из входов застопорит работу. Но если той же оси не разрешить приоритет задач (раз уж мы запрещаем приоритет прерываний) то она ровно так же встанет. В противном случае ситуация вырождается в известный анекдот про суровых сибирских мужиков и японскую бензопилу.

                      При всем при этом не могу не согласиться — тема устойчивости системы к флуду обширна. И заслуживает отдельного обсуждения.

                      там точно нельзя сделать квант времени меньше… ?

                      Скорее больше. Так, собственно говоря, обычно так и делают. Даже известный linux'ово-win'довый libmodbus делает (возможно делал — давно не смотрел исходники) именно так. Плюет на межсимвольный интервал и увеличивает до кратных миллисекундам значения межпакетного. Другое дело, что это подход «если нельзя, но очень хочется — значит можно». Мне кажется, уважающий себя и свое творение разработчик не должен так наступать себе на горло. Тем более, что реализуется это крайне просто. Хотя, не буду кривит душой — в подавляющем большинстве случаев требует отдельного аппаратного таймера. Правда, это на контроллере. И даже с той самой FreeRTOS никто не запрещает использовать для этих нужд любой свободный аппаратный таймер. А вот под Linux или Windows… Честно говоря я не очень себе представляю как именно это можно реализовать. Особенно с userspace'а.

                      В целом я ничего не имею против FreeRTOS как таковой. Все мои придирки — это «вкусовщина». Я с этим и не спорю. Больше того, оба подхода вполне могут жить параллельно. То, что терпит уходит на уровень FreeRTOS (таймер которой имеет минимальный приоритет), а то что надо быстро работает на асинхронных веревочках. Просто надо понимать ограничения одного и другого подхода. И не использовать инструменты не по назначению.

                      Ну, и просто в качестве оффтопика:
                      Про «сложно» и «тяжело отлаживается». А посмотрите на исходники. DukeNukem, Prince of Persia, DOOM. Сегодня они кажутся сложными. А когда-то были вполне обыденностью. Не серостью — она сгинула. Но и не прорывом. Сегодня мы, для которых Prince of Persia, написанный когда-то на ассемблере кажется сложным, отказываемся писать асинхронный код, ссылаясь на мифических последователей, которым через 10 лет его придется поддерживать. Я не понимаю такого подхода. Это что — программисты деградируют до офисного планктона? Или что? Или мы сильно льстим себе и наш код умрет как гора той серости, которой не получилось остаться в памяти как той самой Prince of Persia? А может она осталась именно потому что была все же чуточку сложнее, чем окружающие? Не знаю. И даже полемику не хочу разводить. Скорее так — повод задуматься.
                      0
                      Вы вот это сейчас на полном серьёзе? Вы не видите, насколько современные протоколы, обмазанный collision resolve, шифрованием, низкой latency и топологией mesh сопостовимы по сложности с modbus?
                        0
                        Вполне серьезно. Только давайте уточнять о каких протоколах мы говорим.

                        В чем сложность всяких «старых» протоколов. Пусть будет тот же ModBus, или чуть более раннего импульсного набора номера в цифровых АТС? Правильно — это или точные и короткие таймеры, или просто сумасшедшие их количество и дикий разбег по параметрам у конечных устройств. Вот это реально сложно. Попробуйте на досуге просто прикинуть реализацию приема импульсного набора номера на 80186 с 3-4Мгц. Помня о том, что каждая линия это бит в регистре, а абонентов у вас сотня. И помимо приема номера надо еще коммутацию абонентов делать и тоновые сигналы генерировать. Мои учителя такой код мало что писали (один на ассемблере, другой на C) так еще и неоднократно. И даже сертифицировали. Но вот это — сложно.

                        А теперь про то, что было обозначено вами. Что будем брать? 80215.4? Производную от нее ZigBee? Bluetooth? Давайте по честному — тут сложен не слой MAC (те самые mesh, шифрования и все такое прочее) а слой PHY (обнаружение несущей, модуляции/демодуляции сигнала из/в эфир, прием «сырого» пакета). Вот тут не то что микросекундные — тут пикосекундные времена. Но именно поэтому и не работают на этом уровне контроллеры. Жесткая логика, в крайнем случае ПЛИС. А то что контроллерам остается… Да не смешите мои подковы. ModBus (если его полностью по спецификации реализовать) еще сложнее окажется. То же, к слову, и 802.11 касается. Любого. Хоть a/b/g/n/AC/что-там-нынче-можно, хоть s. Тут, правда, вариантов до дури. И если кто и берется сделать на контроллере этот слой, то как правило с очень ограниченной функциональностью. Поймите, все эти слой накрываются теми же настольными осями — Linux, Windows. Ладно, в виндах все закрыто — но посмотрите на код mac80211 в Linux. Там кода много, но он совсем не сложный. И код WPA Supplicant или hostapd вполне открыт. Нет там микросекундных таймеров. Не нужны они. Там серьезные требования по RAM — это да. Хранить все эти таблицы авторизации, маршрутизации и прочего… Разработчики этих протоколов учли старые косяки усложнявшие жизнь программистов. Ну и аппаратные блоки ускорения шифрования. Без них сложно. А про collision resolve — нет обычно там ничего умнее CSMA/CD. А извините, но назвать это сложным… Что ж тогда простое?

                        Вы уточняйте о чем конкретно говорите. Я не могу знать все протоколы. Может быть действительно есть что-то сложное, а я не знаю или не о том рассказываю.
                          0
                          То, что простой протокол всё равно сложно реализовать а старом железе вообще не изменяет того факта, что код WPA Supplicant на голову сложнее того, что писали ваши преподаватели. Со всем уважением к ним. Да, инструмент теперь тоже гораздо более могущественный, и если мне сейчас нужно будет реализовать импульсный набор, мне будет ну просто несравнимо проще, и дело не в оперативной памяти, количестве регистров и более продуманной архитектуре МК(что тоже верно) но, главное, в наличии языков высокого уровня и возможности реюзить чужой код.
                          Однако, вернусь к своему утверждению, что прошивка микросхемы для той же Zigbee или Wifi сложнее «приема импульсного набора номера на 80186 с 3-4Мгц». Особенно если вспомнить, что авторам прошивок всё ещё надо экономить такты, только теперь это связано не с низкими частотами ядра, а с всё возрастающими требованиями к энергоэффективности и, как производное, времени автономной работы. Скажите, вы когда-либо пробовали писать код, который экономит батарейку?
                            0
                            Все мое оборудование работает от батареи. И Linux, и мои драйвера под него, и мои прошивки для контроллеров, которые с этими драйверами общаются. Только прикладной код вне моей компетенции. Так что не только писал, но и пишу. И тем более настаиваю — если важно экономить каждый милливать нельзя использовать энергонеэффективные решения. Не уверен, что это стоит обсуждать в рамках этой темы, но если хотите — поговорим. Не вопрос.

                            Теперь так — мне корпоративная этика не позволяет делить задачи на сложные и не очень. Чужая задача она всегда простая, а своя всегда сложная. Потому позвольте я не буду сравнивать CFG80211+MAC80211+WPASupplicant/Hostapd и реализацию микропрограммы телефонной станции. Особенно когда критерии сравнения не очевидны. В те времена микропрограмма писалась одним человеком примерно за год-два. Сколько человеко-часов потрачено на эту связку не знает никто. Я просто привел пример. Пример того, как можно масимально эффективно использовать процессорное время. И изначально возмутился именно нежеланию людей развиваться в этом направлении. А эффективное использование процессорного времени это или реализация большего количества задач или та самая экономия батареи (больше и лучше спим).

                            Что до языков высокого уровня и возможности реюзать чужой код… Простите, или трусы, или крестик. Или высокий уровень и чужой код, или низкой уровень и энергоэффективность, быстродействие, размер. Желаете попробовать переубедить меня в этим? Ну попробуйте… Пока ни у кого не получилось.

                            Что до Zigbee… Не обижайтесь, но я не разговариваю о вещах на которые нет четкой и понятной спецификации. Я не верю рекламным проспектам, а верю четкой спецификации. Вот когда у меня будет возможность написать с нуля устройство, работающее с этим самым Zigbee (а равно производными типа Z-Remote или Z-Wave) на любом чипе, поддерживающем 802.15.4 — тогда поговорим о сложности. Я не очениваю ни сложность, ни качество закрытого кода. Даже если это библиотека. И никогда не буду использовать библиотеку без исходников в своем проекте. Это позиция заказчика, и я против нее абсолютно ничего не имею. Только за. Пока открыта только спецификация IEEE 802.15.4. Она не сложна в реализации. Настолько, что даже тот же Atmel вполне спокойно сделал ее на callback'ах. Правда довольно паскудно, но факт — сделал. И этим можно пользоваться (если брезгливостью особой не страдать). А можно и переписать. Маленький том 802.15.4 (едва пять сантиметров высотой распечатка на A4) не идет ни в какое сравнение с 802.11 (почти метр толщиной такой же распечатки — и это без ссылочных документов).
                              0
                              В общем коментарии я не увидел того, что задачи не стали проще, хотя увидел, что время на их реализацию одинаковое. Что для простых задач со слабыми инструментами 40 лет назад, что для сложных задач с удобными инструментами современности цикл разработки хорошего устройства до первых стабильных версий около двух лет. Если сложность вы считали не по количеству элементов и взаимодействий, а по затраченным человекочасам, то да, сложность не изменилась. Но я как-то привык мерить сложность исходя из количества старниц в спеках, суммируя их с количеством документации на библиотеки, мануалам по МК и строчкам в ТЗ. А мерить работу усталостью считаю недостойным.
                              Что до языков высокого уровня и возможности реюзать чужой код… Простите, или трусы, или крестик. Или высокий уровень и чужой код, или низкой уровень и энергоэффективность, быстродействие, размер. Желаете попробовать переубедить меня в этим? Ну попробуйте… Пока ни у кого не получилось.

                              Ну лично я с шифрованием, обильно завязанным на плохозадокументированные функции МК не связывался, но видел, как использование проверенных чужих блобов повышало производительность велосипедов в проектах. Также в мире ПИД регулирования код, написанный специалистом-математиком выигрывает у реализации «в лоб», на которую способен программист широкого профиля, или талантливый системщик. И, наоборот, когда математики пишут ядро ОС код также получается хуже, чем код, который для них чужой, но написан системщиками с опытом. Есть, конечно, шанс, что вы гений от программирования и одинаково хорошо умеете делать и UI, и ядро ОС, и алгоритмы ПИД/шифрования, и нейросетки и прочее, но тогда я приклоняюсь, и искренне завидую вам. Но мне часто нужно использовать чужой код именно для того, чтобы повысить энергоэффективность, размер кода и стабильность.
                                0
                                А мерить работу усталостью считаю недостойным.


                                Ваше право. Однако вы слабо себе представляете объем документации раньше. Как, собственно, и сейчас. Сколько реально страниц из 1000+ страничной документации на условный STM32F4 вы РЕАЛЬНО читали и изучали для использования в своем проекте. И те же вопросы, допустим по Qt или Boost. Уверенность в том, что Вам сейчас сложнее не пропала? Другое дело, что человеко-часы тоже довольно менеджерский критерий. Тут я не спорю. Но настоятельно рекомендую хорошо поразмыслить на тему справедливо ли Ваше утверждение: «Что для простых задач со слабыми инструментами 40 лет назад, что для сложных (современных) задач с удобными инструментами современности цикл разработки хорошего устройства до первых стабильных версий около двух лет». Уровень сложности задач мало поменялся. Впрочем, если считать что то, на что Ньютон положил всю жизнь мы сегодня проходим в школе (край в институте) за едва десяток лет, значит Ньютон в сравнении с нами полнейшая тупица, то Вы безусловно правы.

                                Есть, конечно, шанс, что вы гений от программирования


                                Блин, ну почему именно эта фраза, я про «гений от программирования», преследует меня со времен учебы. Нет. Совсем не гений. Просто я не являюсь универсальным специалистом. И никогда не претендовал на эту роль. И никого за такое не агитирую. Наоборот — занимайся тем, что знаешь хорошо. Заметьте — даже здесь хорошо, а не отлично. Отлично налагает слишком много ответственности и практически лишает права на ошибку. А это не правильно. Право на ошибку у разработчика впору включать в декларацию прав человека. Но никогда не останавливайтесь. Всегда учите смежные отрасли. Опять же не диаметрально противоположные, а именно смежные. Если контроллерщик выучит веб-программирование, то свою квалификацию в контроллерах он не повысит, и веб программист из него выйдет посредственный. Ну и если уж расширился, то изучай грамотную реализацию. Мой путь был снабженец, настройщик, схемотехник, контроллерщик, драйверщик, системщик. Все это лет за 20. Ну и школьные увлечения программированием и схемотехникой. Их я не считаю. Очень хочу еще ПЛИСы добавить. Во всяком случае чем дальше, тем больше чувствуется такая необходимость. Правда практических задач пока нет. Потому притормаживаю.

                                Что до «использование проверенных чужих блобов повышало производительность велосипедов в проектах» — сложно все это. Скажем так — я не приветствую такой подход. По мне необходимо доработать велосипед. Иначе можно нарваться на ту самую особенность в поведении, благодаря которой блоб стал таким производительным. Я не могу сказать однозначно что это плохо и это табу, но я точно не могу приветствовать такой подход. Уж простите.
              0
              При вызове функции задержки учитываются такты на сохранение стека, переход в саму функцию, восстановление стека?
                0
                Из кода видно, что не учитывается.
                  0
                  Это я к тому, что через вызов функции задержку менее 6-7 uСек не сделать. В принципе.
                    0
                    А, не углядел. Функция инлайн, она вовсе не вызывается, этот кусок встраивается по месту.
                      0
                      Фу ты… Аналогично. Проглядел…
                0
                А что отладку в SWV не выводите, почему в уарт?

                Only users with full accounts can post comments. Log in, please.