Как стать автором
Обновить

Комментарии 25

Вообще то фигня какая то в функции.
Смотрел в свое время исходники memcpy для ARM и она использует команды массовой пересылки по 8 слов через два «стека», а тут побайтно?

В CRT не реализация memcpy/memset, а процедур, которые позволяют скопировать секцию .data (memory_copy) из Flash в RAM или обнулить секцию .bss (memory_set). О реализации memcpy в стандартной поставке CrossWorks/SES ничего плохого пока сказать не могу. Для встраиваемых систем в основном встречаются подобные реализации.


Когда я увидел реализацию в CRT, то у меня тоже были вопросы, зачем это всё делать побайтно, если инструкции по времени выполнения и по конечному результату не различимы.

Как-то встречались реализации копирования /заполнения через LDM/STM именно в стартовом коде, причём без проверок на некратные размеры, что и логично — в отличие от «нормальных» memcpy/memset, эти размеры известны уже на этапе компоновки и можно выбрать наиболее эффективные реализации (и вот кстати, например у IAR этот код вынесен в отдельные модули и компоновщик может выбрать из нескольких вариантов, например с RLE-сжатием .data — это ещё поднимет скорость, т.к. чтений памяти будет меньше).
С нехорошим стартовым кодом от авторов IDE тоже сталкивались, у Keil ещё на AT91SAM7 в их «готовой» инициализации PLL был race condition, когда одной записью в регистр параллельно включался осциллятор и тут же на него переключались — у нас всё работало, а уже после массовых продаж у некоторых клиентов вылезли безнадёжно глухие зависания прямо на старте (такие что и JTAG не работал, а куда ему?).

схема включения пьезо-кнопки не предусматривала какую-либо аппаратную защиту от антидребезга


Правильно будет "защиту от дребезга".


Вообще проблему решает обычный RS триггер с установкой от кнопки и сбросом после запуска МК

Поправил, спасибо.


Вообще проблему решает обычный RS триггер с установкой от кнопки и сбросом после запуска МК

Да, конечно, особенно если кнопка используется только, например, для пробуждения. Но если есть необходимость помимо пробуждения отслеживать длительность нажатия, то тут уже не совсем подойдёт данное решение, к сожалению.

Вообще проблему решает обычный RS триггер с установкой от кнопки и сбросом после запуска МК
Кнопка зачётная! (это наверное потому, что цену с ходу не нашел). Прервался даже от чтения статьи посмотреть на неё поближе и после:
We offer an embedded CPU for data output compatible with various communication protocols.
с сайта, ожидал что сейчас будет о перепрошивке кнопки).
В общем как то RS триггер на неё — не очень выглядит:)

Только отдельного MCU в кнопке не хватало для полного счастья. :)
Выглядит она стильно, но нет понимания, нажал ты её или нет. У меня рядом с домом на пешеходном переходе установили похожую кнопку в светофоре. Там, похоже, подсветка запитана от 3.3В и в солнечную погоду пойди разбери, заморгала она или нет.

От кнопки ожидаешь что она — как кнопка )

А вы дальше так от 4 МГц и работаете? Или переключаете тактовую? Не имеет смысла, например, сначала переключить тактирование, а потом уже копировать? Смена тактовой частоты не очень сложное занятие, можно и на асме под конкретный проект переписать в startup, если это даст в итоге уменьшение времени запуска.

Справедливое замечание. :)
Мы переключаем контроллер на работу от 80 МГц. В статье на это не акцентировалось внимание, но сейчас это переключение происходит до копирования/установки оперативной памяти.


Но хотелось показать, в чем корень проблемы на программном уровне, потому что у кого-то не будет возможности это сделать.

А было после копирования? Т.е в 20 раз вы ускорили просто поменяв порядок?

Да. При переходе на другой источник тактовой частоты всё равно есть некоторые задержки (ожидание готовности источника, например), но в целом это существенно помогает ускорить выполнение стартового кода.

Вдогонку: можно и кнопку опросить в startup вообще до всего и заснуть назад, не тратя электроэнергию (оно же наверное от батареи, судя по L серии и сну?) на полноценную инициализацию, если срабатывание было ложным. Насколько понимаю (конкретно с STM32L4 не знаком), раз пин кнопки в состоянии пробуждать, то он и во сне остаётся настроен в нужном направлении и как-то тактируется, т.е. можно сразу смотреть его состояние в регистре ввода.

Нет, порт кнопки все равно нужно инициализировать после пробуждения от прерывания. Но это в любом случае на порядки быстрее полной инициализации всей периферии и памяти :)

Нет. :) Почему?

У меня была схожая задача — обработать внешнее прерывание как можно быстрее. Хотя цель была другой — максимальная экономия энергии батареи (обработка импульсов от датчика дождя в погодной станции).
Все решается гораздо проще — проверка причины пробуждения производится в стартовом коде до всех стартовых инициализаций. Инициализируется только порт этой кнопки, после чего проверяется ее состояние. Убедились, что кнопка нажата — пошли дальше на стартовую инициализацию, иначе заснули обратно :)

У этого проекта тоже была цель максимально сохранить батарейку. :)
Собственно, аналогичные манипуляции по сокращению времени старта системы были также сделаны. Просто в любом случае зачем греть воздух при инициализации памяти?) Врядли качество обнуления зависит от того пишем мы нули побайтово или целыми словами.

Я согласен, оптимизация — это всегда хорошо, но в статье проблема была обозначена именно по времени пробуждения для обработки нажатия, и именно она решается проще, чем отладка и переписывание CRT-библиотек :)

И да, и нет. :)
Изменения в CRT, которые описаны выше, делаются наивным способом — убирается суффикс b, отвечающий за побайтовую операцию записи/чтения. И меняется размер смещения адреса с 1 на 4. Ускорение в 4 раза уже есть, менять порядок работы в startup пока не нужно, если всё устраивает. :)

Изменения в CRT, которые описаны выше, делаются наивным способом — убирается суффикс b,

А разве это не влечет за собой и изменение вызывов этих функций? Раньше им передавалась длина в байтах, теперь надо передавать в словах.
Ускорение в 4 раза уже есть, менять порядок работы в startup пока не нужно, если всё устраивает. :)

А зачем тратить кучу времени на инициализацию всей периферии и памяти только для того, чтобы проверить состояние одного вывода? Хотя если ложное срабатывание, после которого надо опять заснуть, происходит один раз из тысячи, то и так сойдет :)

Для большей ясности о том, как вызываются mem_copy и mem_zero:


Reset_Handler:
/* Copy the data segment initializers from flash to SRAM */
    ldr r0, =_sidata
    ldr r1, =_sdata
    ldr r2, =_edata
    bl mem_copy
/* Zero BSS section */
    movs r0, #0
    ldr r1, =_sbss
    ldr r2, =_ebss
    bl mem_zero

По факту им без разницы, им передаётся адрес начала и конца секции с которой надо провести манипуляцию.
Если бы это было написано на C:


    // было:
    // uint8_t *data_rom_start_p = &_sidata;
    // uint8_t *data_ram_start_p = &_sdata;
    // uint8_t *data_ram_end_p = &_edata;
    // стало:
    uint32_t *data_rom_start_p = &_sidata;
    uint32_t *data_ram_start_p = &_sdata;
    uint32_t *data_ram_end_p = &_edata;
    // --- mem_copy ---
    while(data_ram_start_p < data_ram_end_p)
    {
        *data_ram_start_p++ = *data_rom_start_p++;
    }

    // было:
    // uint8_t *bss_start_p = &_sbss; 
    // uint8_t *bss_end_p = &_ebss;
    // стало:
    uint32_t *bss_start_p = &_sbss; 
    uint32_t *bss_end_p = &_ebss;
    // --- mem_zero ---
    while(bss_start_p < bss_end_p)
    {
        *bss_start_p++ = 0;
    }

Хотя если ложное срабатывание, после которого надо опять заснуть, происходит один раз из тысячи, то и так сойдет :)

Да, это была редкость, которая возникла пару раз, после чего была исправлена. Но началось всё вообще с того, что QA-инженер сказал, что ему тяжело давить на кнопку и что раньше было лучше. :)

Для большей ясности

А, там конечный адрес передается, а не длина. Тогда да :)
О, я тоже с подобным сталкивался. Делал передачу показаний счетчика воды.
Тоже использовал STM32L4 в DeepSleep режиме.
Я проблему быстрого пробуждения обошел по-другому.
я поставил кастомный хэндлер на прерывание сброса и в нем явно инициализировал только то что мне нужно было для проверки причины пробуждения и записи результата в RTC память.
Если выяснялось что пора передавать данные в телеграм бот, то уже делалось полное пробуждение с инициализацией CRT и вызовом main.

я мерял профилировoщком из VisualGDB. Время обработки импульса со счетчика с последующим засыпанием (то есть без полного пробуждения) было порядка 100-200 тактов CPU.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации