b707, ну с прерываниями оно, наверное, так и должно быть. Но можно и без прерываний тоже самое сделать. Один таймер "мастер" считает некоторое кол-тактов разрешая в это время работу другому, а потом некоторое кол-во запрещая. Другой просто генерит 1МГц, подчиняясь указанием "мастера" когда можно генерить, когда нельзя. Пример, тут 10 микросекунд разрешено и 5 запрещено. На осцилле аккуратные пачки по 10 импульсов.
Попробовал, все отлично, четкие пачки по 138 импульсов. Отмечу только важные нюансы в верхнем коде:
- для точной синхронизации слейв должен запускаться до Мастера (строчки 15 и 16 нужно поменять местами)
- значение регистра сранения задется точно, а не N-1, в отличии от прескалера или ARR. В данном случае это важно. так как разница в единицу дает рассинхронизацию мастера и слейва, которая накапливается от цикла к циклу, меняя фазу сигнала
Когда это поправил - все работает очень четко, число импульсов строго одинаковое, все задержки стабильные.
Но проблемы еще остались. По ходу программы мне нужно эту связку таймеров включать и выключать. Если просто выключить мастер таймер, а потом запустить заново, обнулив при старте счетчики мастера и слейва - в первой пачке будет 141 импульс. Не знаю почему так, но видимо придется при каждом рестарте таймера конфигурировать все регистры заново.
b707, а можно и на одном таймере и ДМА. Таймер тактирует ДМА, а тот кидает ему в регистр CCR каждый раз новое значение, задающее скважность. Когда сигнал не нужен даём нулевую скважность. В итоге имеем ровные пачки импульсов:
Вопрос - а почему у меня упорно засело в голове, что сконфигурировать ДМА-перенос из памяти в GPIO можно только на таймере1 ? - сиху читаю даташит, не вижу таких ограничений
добавка - запустил на таймере3 - все отлично работает. Еще раз спасибо dimax за пример.
Все-таки не понятно, почему "в интернетах" упорно натыкаюсь на сообщения, что работать с GPIO можно тактируя ДМА исключительно первым таймерм?
нормально работает(мигает лампочкой), пока размер куска отведенной динамической памяти не превышает примерно 61-62к, потом виснет, что вполне согласуется с заявленным обьемом RAM 64к
Пытаюсь запустить готовый проект, работающий на F103 - на MK F401.
1 malloc() не работает. Попытки выделить функцией malloc() динамическую область памяти более чем 50-100 байт - терпят неудачу. Причем сама функция, как водится, ошибок не вызывает - но попытки обращения к выделенной памяти приводят к зависанию программы. При этом статически можно выделить память практически любого размера вплоть до полного размера RAM.
2. функция pinMode() не работает в конструкторе глобального обьекта, при этом в setup() и в основной программе работает. Возможно, причина в том, что в конструкторе еще "слишком рано" и инициализация портов GPIO не закончена, а к сетапу она завершается. Пробовал вставлять свою инициализацию шины и GPIO порта до первого pinMode() - не помогло.
это не считая несовместимости по регистрам BRR и BSRR, которую я легко обошел.
2. функция pinMode() не работает в конструкторе глобального обьекта, при этом в setup() и в основной программе работает. Возможно, причина в том, что в конструкторе еще "слишком рано" и инициализация портов GPIO не закончена, а к сетапу она завершается.
Именно.
Собственно, не "не закончена", а "еще не начиналась". По стандарту сначала происходит инициализация всех глобальных переменных, и только потом - выполняется первый исполняемый оператор программы, в данном случае - блока инициализации железа.
Поэтому инициализацию железа в Ардуино принято производить не в конструкторе, а в специальном методе init() или setup().
PS. Кстати, malloc() часом вызывается не из конструктора?
По стандарту сначала происходит инициализация всех глобальных переменных, и только потом - выполняется первый исполняемый оператор программы, в данном случае - блока инициализации железа.
Нет. Порядок запуска программы полностью под контролем программиста. Ничто не мешает написать что угодно перед "call static constructors"
/* Call the clock system initialization function.*/
bl SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
bx lr
Собственно, не "не закончена", а "еще не начиналась". По стандарту сначала происходит инициализация всех глобальных переменных, и только потом - выполняется первый исполняемый оператор программы, в данном случае - блока инициализации железа.
Поэтому инициализацию железа в Ардуино принято производить не в конструкторе, а в специальном методе init() или setup().
как ни странно, на F103 внутри конструктора pinMode() отрабатывает нормально
Мне, конечно, нетрудно всю инициализацию железа перетащить в class.init() - но хотелось бы понять, почему и на ардуино Нано/Мега. и на обычном блюпилле это все работало и в конструкторе, а в F401 нет
Цитата:
PS. Кстати, malloc() часом вызывается не из конструктора?
Для тех, кто в танке: в Ардуино используется язык программирования С++, и речь в данном контексте шла именно о нем.
в Ардуино используется ядро, написанное программистом, и содержащее такой фрагмент кода. Если бы программист напряг мозги, он бы настроил ядро правильно.
сорри, но видимо я до этого еще не дорос... Единственное, что мне пришло в голову - сравнить этот файл из ветки STM32F4 с аналогичным для STM32F1, чтобы попытаться понять, почему проблема с инициализацией возникает только в STM32F4.
Но в ветке STM32F1 аналогичного файла вообще нет... или этот код где-то еще, или как-то обходятся без указания порядка инициализации...
Для тех, кто в танке: в Ардуино используется язык программирования С++, и речь в данном контексте шла именно о нем.
в Ардуино используется ядро, написанное программистом, и содержащее такой фрагмент кода. Если бы программист напряг мозги, он бы настроил ядро правильно.
В Ардуино не используется ядро, в Ардуино используется концепция, т.к. Ардуино - это не конкретный МК, а система, позволяющая единообразно подходить к программированию разных МК, существенно различающихся по своим свойствам.
Да и вообще, квалификация любого специалиста определяется умением пользоваться имеющимся инструментом, а не умением настроить молоток так, чтобы им можно было заворачивать шурупы.
Я, смотрите, старательно вас развожу, задаю каждому вопрос отдельно :) Ответьте лучше мне про malloc() в конструкторе...
Ну, стартовая настройка МК предполагает помимо инициализации периферии также настройку стека, кучи и пр. Опять же, при инициализации аппаратуры, как правило, возникает необходимость в выделении памяти. А выделение памяти стандартным образом из кучи сопровождается накладными расходами. Почему бы, скажем, не зафиксировать буфер Serial по стандартному адресу в обход менеджера кучи?
В общем, нужно либо разбираться с тонкостями реализации, а они, эти тонкости, могут существенно различаться для разных камней (хотя бы потому, что авторы кода - разные), либо попытаться выработать общие подходы, которые будут работать независимо от того, как реализовал блок инициализации тот или иной автор. Отказ от инициализации и распределения памяти в конструкторе и применение вместо этого явные методы инициализации (init или setup) я рассматриваю в качестве таких подходов. Потому как считаю, что следует выбирать камень под задачу, а задачи у меня бывают ох какими разными. Следовательно, детальное изучение особенностей реализации блока инициализации для каждого конкретного камня, на мой взгляд, непроизводительная трата времени.
Например на stm я написал программу, которая распечатывает мне содержимое регистров включенных устройств - и этой информации мне достаточно, чтобы потом перестраивать режимы работы под себя. А разбираться, когда именно и в какой последовательности эта инициализация была проведена - мне не интересно.
сорри, но видимо я до этого еще не дорос... Единственное, что мне пришло в голову - сравнить этот файл из ветки STM32F4 с аналогичным для STM32F1, чтобы попытаться понять, почему проблема с инициализацией возникает только в STM32F4.
Но в ветке STM32F1 аналогичного файла вообще нет... или этот код где-то еще, или как-то обходятся без указания порядка инициализации...
Надо детально смотреть весь код. Это много где может быть реализовано, на самом деле. Я просто привел пример того, что можно элементарно выполнять код до инициализации глобальных переменных.
Все-таки не понятно, почему "в интернетах" упорно натыкаюсь на сообщения, что работать с GPIO можно тактируя ДМА исключительно первым таймерм?
Патамушта в тырнетах тупо копировали у друг друга... без малейшего понимания... что к чему...
Смотреть надо референс... а не даташит... Там есть таблица эвентов для ДМА...
И кстати... Там хоть и написано... что можно юзать только один эвент на канал... но на самом деле... если эвенты синхронизированы и не одновременны... то можно юзать и несколько... если это нужно и соответствует задаче...
И ещё... из того что ещё помню... В Ф4хх... два модуля ДМА... один из них не работает с ЖПИО по определению...
Серия 32Ж тоже не работает по ДМА с ЖПИО... от рождения своего...
+++++++++
И да... ДМА не тактируется таймером... он получает эвенты от таймера...
Такой вопросик - отладку в каком-нибудь из аддонов есть возможность использовать? Ну там расставить точки останова, посмотреть значения переменных во время выполнения?
Понятно, что не в Ардуино ИДЕ, а например в V-Micro ?
В родном STMовском отлаживает. А мне больше нравиться в IAR. Там вообще всё можно смотреть и менять на любом шаге.
вчера весь вечер протрахался с этим, в V-micro c кларковским аддоном так и не смог запустить... То OpenOCD сервер не запускается, пишет "неверные аргументы", Как ни искал - не нашел, где эти аргументы менять...
Потом вообще Ст-линк перестал определяться, насилу вылечил...
Возможно дело не в аддоне и не в Микро, а в том что у меня винда уже слишком замусорена... Например, у меня два компа, на одном СТ-линк работает, а на другом вообще никак
(обратите внимание, что этот конфиг только для комбинации stm32F103C8 + St-link-v2, под другой МК и другой программатор нужно подбирать конфиг отдельно)
Единственное, что осталось не до конца понятным - после загрузки скетча и запуска отладчика обязательно нужно вручную нажать ресет на плате, иначе отладка не стартует. Кто пользуется отладкой в Вмикро - поделитесь, у вас тоже так? Может есть какой-то способ заставить плату перегружаться автоматом?
И еще - некоторые переменные в Watch посмотреть нельзя - пишет "optimezed out". Правильно ли я понял, что это означает что переменная выкинута оптимизатором как неиспользуемая? - странно, я думал в дебаг-режиме всякая оптимизация отключается...
И еще - некоторые переменные в Watch посмотреть нельзя - пишет "optimezed out". Правильно ли я понял, что это означает что переменная выкинута оптимизатором как неиспользуемая? - странно, я думал в дебаг-режиме всякая оптимизация отключается...
1. Переменная выкинута только из памяти, т.е. на нее не распределяется место в стеке/куче. Вместо этого переменная хранится в регистрах и, соответственно, не имеет адреса.
2. Вообще-то режим отладки подразумевает помещение в код дополнительной информации, а не отключение оптимизации. Возможно, это вещи в какой-то степени связанные, но точно не эквивалентные.
Да, насчет переменных уже разобрался, "optimized out" означает, что переменная относится к другому блоку программы и в этом недоступна. Сейчас столкнулся с другой проблемой - в режиме отладки выполнение программы происходит в сотни, а то и в тысячи раз медленнее, чем в релизе, реально просто не могу дойти до места, которое нужно отлаживать. О том, чтобы запускать отладку на том же коде, что и релиз - нечего и думать, приходится убирать все лишнее, сокращать циклы, выкидывать задержки и миллис.
Понимаю, что так быть не должно... пытаюсь найти причину.
На этот случай в отладчиках есть точка останова. До неё обычно в режиме отладки программа добирается с нормальной скоростью.
почему-то не работает. (вообще я знаю, что такое точки останова, это не первый мой дебаг, если что :)
Я уже все прерывания остановил - все равно . "Продолжить до сдедующей точки останова" - идет со скоростью примерно 10-20 ассемблерных инструкций в секунду. то есть очень медленно. Если идти по шагам - скорость нормальная, если не считать того, что каждую строчку пройти надо вручную.
Нашел описание этого бага в инете в паре мест, решения там нет
Для информации - в настройках аддона в режиме Debug стоит какая-то странная опция компилятора -Og
В мане GCC я такой опции не нашел, написано только, что опции оптимизации -Ox можно комбинировать с опциями дебага -gx. но что означает комбинация -Og - не написано.
В итоге поменял эту непонятную опцию на комбинацию -O0 -g3 -ggdb и все поехало.
Возможно опция -g3 избыточна в моем случае, но пока размер прошивки не превышает флеша МК - это не особо важно.
Optimize debugging experience. -Og should be the optimization level of choice for the standard edit-compile-debug cycle, offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience. It is a better choice than -O0 for producing debuggable code because some compiler passes that collect debug information are disabled at -O0.
Like -O0, -Og completely disables a number of optimization passes so that individual options controlling them have no effect. Otherwise -Og enables all -O1 optimization flags except for those that may interfere with debugging:
продолжаю разбираться с F401 в аддоне Кларка. Хочется понять, почему при попытке выделить даже минимальный блок памяти с помощью malloc() все виснет?
Есть те, кто понимает в ld скриптах? Вот скрипт от F401 из аддона Кларка:
/*
* Linker script for libmaple.
*
* Original author "lanchon" from ST forums, with modifications by LeafLabs.
*/
OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
/*
* Configure other libraries we want in the link.
*
* libgcc, libc, and libm are common across supported toolchains.
* However, some toolchains require additional archives which aren't
* present everywhere (e.g. ARM's gcc-arm-embedded releases).
*
* To hack around this, we let the build system specify additional
* archives by putting the right extra_libs.inc (in a directory under
* toolchains/) in our search path.
*/
GROUP(libgcc.a libc.a libm.a)
INCLUDE extra_libs.inc
/*
* These force the linker to search for vector table symbols.
*
* These symbols vary by STM32 family (and also within families).
* It's up to the build system to configure the link's search path
* properly for the target MCU.
*/
INCLUDE vector_symbols.inc
/* STM32 vector table. */
EXTERN(__stm32_vector_table)
/* C runtime initialization function. */
EXTERN(start_c)
/* main entry point */
EXTERN(main)
/* Initial stack pointer value. Relocated to RAM */
EXTERN(__msp_init)
PROVIDE(__msp_init = ORIGIN(ram) + LENGTH(ram));
/* Reset vector and chip reset entry point */
EXTERN(__start__)
ENTRY(__start__)
PROVIDE(__exc_reset = __start__);
/* Heap boundaries, for libmaple */
EXTERN(_lm_heap_start);
EXTERN(_lm_heap_end);
SECTIONS
{
.text :
{
__text_start__ = .;
/*
* STM32 vector table. Leave this here. Yes, really.
*/
*(.stm32.interrupt_vector)
/*
* Program code and vague linking
*/
*(.text .text.* .gnu.linkonce.t.*)
*(.plt)
*(.gnu.warning)
*(.glue_7t) *(.glue_7) *(.vfp11_veneer)
*(.ARM.extab* .gnu.linkonce.armextab.*)
*(.gcc_except_table)
*(.eh_frame_hdr)
*(.eh_frame)
. = ALIGN(4);
KEEP(*(.init))
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
. = ALIGN(0x4);
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*crtend.o(.ctors))
. = ALIGN(4);
KEEP(*(.fini))
. = ALIGN(4);
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*crtend.o(.dtors))
} > REGION_TEXT
/*
* End of text
*/
.text.align :
{
. = ALIGN(8);
__text_end__ = .;
} > REGION_TEXT
/*
* .ARM.exidx exception unwinding; mandated by ARM's C++ ABI
*/
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > REGION_RODATA
__exidx_end = .;
/*
* Read-only data
*/
.rodata :
{
*(.rodata .rodata.* .gnu.linkonce.r.*)
/* .USER_FLASH: We allow users to allocate into Flash here */
*(.USER_FLASH)
/* ROM image configuration; for C startup */
. = ALIGN(4);
_lm_rom_img_cfgp = .;
LONG(LOADADDR(.data));
/*
* Heap: Linker scripts may choose a custom heap by overriding
* _lm_heap_start and _lm_heap_end. Otherwise, the heap is in
* internal SRAM, beginning after .bss, and growing towards
* the stack.
*
* I'm shoving these here naively; there's probably a cleaner way
* to go about this. [mbolivar]
*/
_lm_heap_start = DEFINED(_lm_heap_start) ? _lm_heap_start : __data_end__;
_lm_heap_end = DEFINED(_lm_heap_end) ? _lm_heap_end : __msp_init;
} > REGION_RODATA
/*
* .data
*/
.data :
{
. = ALIGN(8);
__data_start__ = .;
*(.got.plt) *(.got)
*(.data .data.* .gnu.linkonce.d.*)
. = ALIGN(8);
__data_end__ = .;
} > REGION_DATA AT> REGION_RODATA
/*
* .bss
*/
.bss :
{
. = ALIGN(8);
__bss_start__ = .;
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN (8);
__bss_end__ = .;
_end = __bss_end__;
} > REGION_BSS
/*
* Debugging sections
*/
.stab 0 (NOLOAD) : { *(.stab) }
.stabstr 0 (NOLOAD) : { *(.stabstr) }
/* DWARF debug sections.
* Symbols in the DWARF debugging sections are relative to the beginning
* of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
.note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
.ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) }
}
Обратите внимание на строчки 152 и 153, которые устанавливают границы кучи. Верх кучи установлен на метку __msp_init, которая в строке 43 приравнена верху RAM. Тут вроде все хорошо.
А вот низ кучи указывает на окончание секции .data (на метку __data_end__) - а ведь секция data располается не в RAM. а во флеше, да к тому же весьма далеко от адресов, по которым в едином пространстве адресов СТМ расположена оперативка. - Не ошибка ли это?
Если сравнить этот файл с аналогичным для блюпилл,(там malloc() работает) - у блюпилл куча располается между верхом RAM и секцией bss - как описано во всех учебниках.
Короче, я просто взял и заменил в строке 152 метку __data_end__ на __bss_end__ :
a5021, спасибо за инфо. Судя по выделенному куску, с использованием -Og некоторая оптимизация продолжает применяться. Вероятно, именно это и приводило к конфликту с дебаггером, раз при полном отключении оптимизации опцией -O0 дебаггер начал работать.
Не получится легко это сделать. Библиотека завязана на хардверные прерывания и их надо переопределять с авр на стм. Или искать библиотеку где это уже сделали.
Бред околособачий! CPU в гаджетах это одно, а MCU в новых разработках это другое. Пройдёт ещё 3-5 лет и атмэла в новых разработках вообще не будет, от слова совсем.
Ну, не знаю, в своих проектах я пользуюсь исключительно своими библиотеками, поэтому опыта работы с более или менее стандартными не имею.
Хотя, стандартные я нередко использую, когда надо быстро убедиться в работоспособности только что полученного экрана. А т.к экраны я последнее время покупаю в основном 3-вольтовые, думаю, что поддержка stm32f103 мне, скорее всего, где-то попадалась. На чем-то же я их проверял!
b707, ну с прерываниями оно, наверное, так и должно быть. Но можно и без прерываний тоже самое сделать. Один таймер "мастер" считает некоторое кол-тактов разрешая в это время работу другому, а потом некоторое кол-во запрещая. Другой просто генерит 1МГц, подчиняясь указанием "мастера" когда можно генерить, когда нельзя. Пример, тут 10 микросекунд разрешено и 5 запрещено. На осцилле аккуратные пачки по 10 импульсов.
void setup() { pinMode(PB0, PWM); // описываем пин PB0( Timer3 CH3) как PWM TIMER4_BASE->SMCR=1<<7; TIMER4_BASE->PSC=0; TIMER4_BASE->ARR=720+360 -1;//10 тактов разрешать работу tim3, 5 Тактов нет TIMER4_BASE->CCR1=360-1; TIMER4_BASE->CR2=(1<<6); //MMS:001(Enable) TIMER4_BASE->CCMR1=(1<<6)|(1<<5)|(1<<4);//PWM mode TIMER3_BASE->PSC=0; TIMER3_BASE->SMCR=(1<<5)|(1<<4)|(1<<2)|(1<<0);//TS:011 SMS:101 TIMER3_BASE->ARR=71; TIMER3_BASE->CCMR2=(1<<6)|(1<<5)|(0<<4);//PWM mode TIMER3_BASE->CCER=(1<<9)|(1<<8);//cc3e enable default TIMER3_BASE->CCR3=36; TIMER4_BASE->CR1=(1<<0);// старт TIMER3_BASE->CR1=(1<<0);//start timer3 } void loop() {}dimax, спасибо, о таком варианте не подумал.
Попробовал, все отлично, четкие пачки по 138 импульсов. Отмечу только важные нюансы в верхнем коде:
- для точной синхронизации слейв должен запускаться до Мастера (строчки 15 и 16 нужно поменять местами)
- значение регистра сранения задется точно, а не N-1, в отличии от прескалера или ARR. В данном случае это важно. так как разница в единицу дает рассинхронизацию мастера и слейва, которая накапливается от цикла к циклу, меняя фазу сигнала
Когда это поправил - все работает очень четко, число импульсов строго одинаковое, все задержки стабильные.
Но проблемы еще остались. По ходу программы мне нужно эту связку таймеров включать и выключать. Если просто выключить мастер таймер, а потом запустить заново, обнулив при старте счетчики мастера и слейва - в первой пачке будет 141 импульс. Не знаю почему так, но видимо придется при каждом рестарте таймера конфигурировать все регистры заново.
b707, а можно и на одном таймере и ДМА. Таймер тактирует ДМА, а тот кидает ему в регистр CCR каждый раз новое значение, задающее скважность. Когда сигнал не нужен даём нулевую скважность. В итоге имеем ровные пачки импульсов:
#include <dma_private.h> uint32_t mass_ccr[]{36,36,36,36,36,36,36,36,36,36,0,0,0,0,0}; // void setup() { pinMode(PB0, PWM); // описываем пин PB0( Timer3 CH3) как PWM TIMER3_BASE->DIER=(1<<8);//Update DMA request enable TIMER3_BASE->PSC=0; TIMER3_BASE->ARR=71; TIMER3_BASE->CCMR2=(1<<6)|(1<<5)|(1<<3);//PWM mode, Preload CCR3 TIMER3_BASE->CCER=(1<<8);//cc3e enable default TIMER3_BASE->CCR3=36; TIMER3_BASE->CR1|=(1<<0);//start timer3 dma_init(DMA1); dma_setup_transfer(DMA1,DMA_CH3,(uint32_t*)&TIMER3_BASE->CCR3,DMA_SIZE_32BITS, (uint32_t*)mass_ccr, DMA_SIZE_32BITS, (DMA_MINC_MODE|DMA_CIRC_MODE|DMA_FROM_MEM) ); dma_set_num_transfers(DMA1, DMA_CH3, 15); dma_enable(DMA1, DMA_CH3); } void loop() {}b707, а можно и на одном таймере и ДМА.
отлично, спасибо. Это мне как раз на следующую задачку, у меня еще нужно данные грузить в регистры максимально быстро :) ДМА в самый раз для этого
...максимально быстро ... ДМА ...
Знак "=" поставить никак нельзя к сожалению ...
...максимально быстро ... ДМА ...
Знак "=" поставить никак нельзя к сожалению ...
да, знаю.... ногодрыгом быстрее.
Вопрос - а почему у меня упорно засело в голове, что сконфигурировать ДМА-перенос из памяти в GPIO можно только на таймере1 ? - сиху читаю даташит, не вижу таких ограничений
добавка - запустил на таймере3 - все отлично работает. Еще раз спасибо dimax за пример.
Все-таки не понятно, почему "в интернетах" упорно натыкаюсь на сообщения, что работать с GPIO можно тактируя ДМА исключительно первым таймерм?
b707, касательно мк F401 я и сам про это писал , а вот с F103 уже не помню была ли такая особенность.
b707, касательно мк F401 я и сам про это писал , а вот с F103 уже не помню была ли такая особенность.
На F103 работает. Надо будет на 401 проверить.
В даташите на F401 вроде тоже не вижу запрета на использование DMA1 для загрузки данных в регистры GPIO.
вопрос про F401.
Черная плата BlackPill с Али, контроллер STM32F401CC
Вот такой простенький скетч
#define pin_A PC13 uint8_t *aa; uint16_t allocsize = 60000; void scan_running_dmd_R() { digitalWrite(pin_A, !digitalRead(pin_A)); } void setup() { pinMode(pin_A, OUTPUT); aa = (uint8_t*)malloc(allocsize); memset(aa, 0, allocsize); } void loop() { scan_running_dmd_R(); delay(200); }нормально работает(мигает лампочкой), пока размер куска отведенной динамической памяти не превышает примерно 61-62к, потом виснет, что вполне согласуется с заявленным обьемом RAM 64к
Но если мигать лампой через прерывание таймера:
#define MY_TIM Timer3 #define pin_A PC13 uint8_t *aa; uint16_t allocsize = 600; void scan_running_dmd_R() { digitalWrite(pin_A, !digitalRead(pin_A)); } void setup() { pinMode(pin_A, OUTPUT); aa = (uint8_t*)malloc(allocsize); memset(aa, 0, allocsize); MY_TIM.init(); MY_TIM.pause(); MY_TIM.setPeriod(200000); MY_TIM.attachInterrupt(0, scan_running_dmd_R); MY_TIM.refresh(); MY_TIM.resume(); } void loop() { }все виснет уже при попытке выделить динамическю память всего в 1к ! 800 байт работает - 1000 - уже нет.
WTF?
аддон Кларка.
У кого есть F401 - прверьте в обоих аддонах, плиз
Итак, дополнение по проблемам из сообщения #509.
Пытаюсь запустить готовый проект, работающий на F103 - на MK F401.
1 malloc() не работает. Попытки выделить функцией malloc() динамическую область памяти более чем 50-100 байт - терпят неудачу. Причем сама функция, как водится, ошибок не вызывает - но попытки обращения к выделенной памяти приводят к зависанию программы. При этом статически можно выделить память практически любого размера вплоть до полного размера RAM.
2. функция pinMode() не работает в конструкторе глобального обьекта, при этом в setup() и в основной программе работает. Возможно, причина в том, что в конструкторе еще "слишком рано" и инициализация портов GPIO не закончена, а к сетапу она завершается. Пробовал вставлять свою инициализацию шины и GPIO порта до первого pinMode() - не помогло.
это не считая несовместимости по регистрам BRR и BSRR, которую я легко обошел.
Интересно, кто-нибудь еще с пп 1 и 2 сталкивался?
2. функция pinMode() не работает в конструкторе глобального обьекта, при этом в setup() и в основной программе работает. Возможно, причина в том, что в конструкторе еще "слишком рано" и инициализация портов GPIO не закончена, а к сетапу она завершается.
Именно.
Собственно, не "не закончена", а "еще не начиналась". По стандарту сначала происходит инициализация всех глобальных переменных, и только потом - выполняется первый исполняемый оператор программы, в данном случае - блока инициализации железа.
Поэтому инициализацию железа в Ардуино принято производить не в конструкторе, а в специальном методе init() или setup().
PS. Кстати, malloc() часом вызывается не из конструктора?
По стандарту сначала происходит инициализация всех глобальных переменных, и только потом - выполняется первый исполняемый оператор программы, в данном случае - блока инициализации железа.
Нет. Порядок запуска программы полностью под контролем программиста. Ничто не мешает написать что угодно перед "call static constructors"
/* Call the clock system initialization function.*/ bl SystemInit /* Call static constructors */ bl __libc_init_array /* Call the application's entry point.*/ bl main bx lrСобственно, не "не закончена", а "еще не начиналась". По стандарту сначала происходит инициализация всех глобальных переменных, и только потом - выполняется первый исполняемый оператор программы, в данном случае - блока инициализации железа.
Поэтому инициализацию железа в Ардуино принято производить не в конструкторе, а в специальном методе init() или setup().
как ни странно, на F103 внутри конструктора pinMode() отрабатывает нормально
Мне, конечно, нетрудно всю инициализацию железа перетащить в class.init() - но хотелось бы понять, почему и на ардуино Нано/Мега. и на обычном блюпилле это все работало и в конструкторе, а в F401 нет
именно там.
Нет. Порядок запуска программы полностью под контролем программиста. Ничто не мешает написать что угодно перед "call static constructors"
/* Call the clock system initialization function.*/ bl SystemInit /* Call static constructors */ bl __libc_init_array /* Call the application's entry point.*/ bl main bx lrИ что это было?
Для тех, кто в танке: в Ардуино используется язык программирования С++, и речь в данном контексте шла именно о нем.
И что это было?
Для тех, кто в танке: в Ардуино используется язык программирования С++, и речь в данном контексте шла именно о нем.
в Ардуино используется ядро, написанное программистом, и содержащее такой фрагмент кода. Если бы программист напряг мозги, он бы настроил ядро правильно.
https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/master/STM32F4...
PS. Кстати, malloc() часом вызывается не из конструктора?
все-таки уточните, что вы имели в виду?
https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/master/STM32F4...
сорри, но видимо я до этого еще не дорос... Единственное, что мне пришло в голову - сравнить этот файл из ветки STM32F4 с аналогичным для STM32F1, чтобы попытаться понять, почему проблема с инициализацией возникает только в STM32F4.
Но в ветке STM32F1 аналогичного файла вообще нет... или этот код где-то еще, или как-то обходятся без указания порядка инициализации...
И что это было?
Для тех, кто в танке: в Ардуино используется язык программирования С++, и речь в данном контексте шла именно о нем.
в Ардуино используется ядро, написанное программистом, и содержащее такой фрагмент кода. Если бы программист напряг мозги, он бы настроил ядро правильно.
Да и вообще, квалификация любого специалиста определяется умением пользоваться имеющимся инструментом, а не умением настроить молоток так, чтобы им можно было заворачивать шурупы.
andriano, нафига вам ругаться с РКИТом?
Я, смотрите, старательно вас развожу, задаю каждому вопрос отдельно :) Ответьте лучше мне про malloc() в конструкторе...
andriano, нафига вам ругаться с РКИТом?
Я, смотрите, старательно вас развожу, задаю каждому вопрос отдельно :) Ответьте лучше мне про malloc() в конструкторе...
Ну, стартовая настройка МК предполагает помимо инициализации периферии также настройку стека, кучи и пр. Опять же, при инициализации аппаратуры, как правило, возникает необходимость в выделении памяти. А выделение памяти стандартным образом из кучи сопровождается накладными расходами. Почему бы, скажем, не зафиксировать буфер Serial по стандартному адресу в обход менеджера кучи?
В общем, нужно либо разбираться с тонкостями реализации, а они, эти тонкости, могут существенно различаться для разных камней (хотя бы потому, что авторы кода - разные), либо попытаться выработать общие подходы, которые будут работать независимо от того, как реализовал блок инициализации тот или иной автор. Отказ от инициализации и распределения памяти в конструкторе и применение вместо этого явные методы инициализации (init или setup) я рассматриваю в качестве таких подходов. Потому как считаю, что следует выбирать камень под задачу, а задачи у меня бывают ох какими разными. Следовательно, детальное изучение особенностей реализации блока инициализации для каждого конкретного камня, на мой взгляд, непроизводительная трата времени.
Например на stm я написал программу, которая распечатывает мне содержимое регистров включенных устройств - и этой информации мне достаточно, чтобы потом перестраивать режимы работы под себя. А разбираться, когда именно и в какой последовательности эта инициализация была проведена - мне не интересно.
сорри, но видимо я до этого еще не дорос... Единственное, что мне пришло в голову - сравнить этот файл из ветки STM32F4 с аналогичным для STM32F1, чтобы попытаться понять, почему проблема с инициализацией возникает только в STM32F4.
Но в ветке STM32F1 аналогичного файла вообще нет... или этот код где-то еще, или как-то обходятся без указания порядка инициализации...
Надо детально смотреть весь код. Это много где может быть реализовано, на самом деле. Я просто привел пример того, что можно элементарно выполнять код до инициализации глобальных переменных.
Все-таки не понятно, почему "в интернетах" упорно натыкаюсь на сообщения, что работать с GPIO можно тактируя ДМА исключительно первым таймерм?
Патамушта в тырнетах тупо копировали у друг друга... без малейшего понимания... что к чему...
Смотреть надо референс... а не даташит... Там есть таблица эвентов для ДМА...
И кстати... Там хоть и написано... что можно юзать только один эвент на канал... но на самом деле... если эвенты синхронизированы и не одновременны... то можно юзать и несколько... если это нужно и соответствует задаче...
И ещё... из того что ещё помню... В Ф4хх... два модуля ДМА... один из них не работает с ЖПИО по определению...
Серия 32Ж тоже не работает по ДМА с ЖПИО... от рождения своего...
+++++++++
И да... ДМА не тактируется таймером... он получает эвенты от таймера...
Такой вопросик - отладку в каком-нибудь из аддонов есть возможность использовать? Ну там расставить точки останова, посмотреть значения переменных во время выполнения?
Понятно, что не в Ардуино ИДЕ, а например в V-Micro ?
В родном STMовском отлаживает. А мне больше нравиться в IAR. Там вообще всё можно смотреть и менять на любом шаге.
да по моему во всех есть, кроме Ардуино ИДЕ, я вот родной CubeIDE пользуюсь, не понимаю что все так от нее плюются.
В родном STMовском отлаживает. А мне больше нравиться в IAR. Там вообще всё можно смотреть и менять на любом шаге.
вчера весь вечер протрахался с этим, в V-micro c кларковским аддоном так и не смог запустить... То OpenOCD сервер не запускается, пишет "неверные аргументы", Как ни искал - не нашел, где эти аргументы менять...
Потом вообще Ст-линк перестал определяться, насилу вылечил...
Возможно дело не в аддоне и не в Микро, а в том что у меня винда уже слишком замусорена... Например, у меня два компа, на одном СТ-линк работает, а на другом вообще никак
Запустил-таки отладку в Vmicro версии 19.30 для блюпила с кларковским аддоном.
Необходимые настройки для stm32F103C8 и St-link-v2
- В опциях проекта Vmicro включить Micro Debug - Full
- Поменять опции компиляции с оптимизации на Debug -g
- В файл boards в папке ....Arduino\hardware\Arduino_STM32\STM32F1\ в секцию genericSTM32F103C добавить строчку:
(обратите внимание, что этот конфиг только для комбинации stm32F103C8 + St-link-v2, под другой МК и другой программатор нужно подбирать конфиг отдельно)
Единственное, что осталось не до конца понятным - после загрузки скетча и запуска отладчика обязательно нужно вручную нажать ресет на плате, иначе отладка не стартует. Кто пользуется отладкой в Вмикро - поделитесь, у вас тоже так? Может есть какой-то способ заставить плату перегружаться автоматом?
И еще - некоторые переменные в Watch посмотреть нельзя - пишет "optimezed out". Правильно ли я понял, что это означает что переменная выкинута оптимизатором как неиспользуемая? - странно, я думал в дебаг-режиме всякая оптимизация отключается...
И еще - некоторые переменные в Watch посмотреть нельзя - пишет "optimezed out". Правильно ли я понял, что это означает что переменная выкинута оптимизатором как неиспользуемая? - странно, я думал в дебаг-режиме всякая оптимизация отключается...
1. Переменная выкинута только из памяти, т.е. на нее не распределяется место в стеке/куче. Вместо этого переменная хранится в регистрах и, соответственно, не имеет адреса.
2. Вообще-то режим отладки подразумевает помещение в код дополнительной информации, а не отключение оптимизации. Возможно, это вещи в какой-то степени связанные, но точно не эквивалентные.
Да, насчет переменных уже разобрался, "optimized out" означает, что переменная относится к другому блоку программы и в этом недоступна. Сейчас столкнулся с другой проблемой - в режиме отладки выполнение программы происходит в сотни, а то и в тысячи раз медленнее, чем в релизе, реально просто не могу дойти до места, которое нужно отлаживать. О том, чтобы запускать отладку на том же коде, что и релиз - нечего и думать, приходится убирать все лишнее, сокращать циклы, выкидывать задержки и миллис.
Понимаю, что так быть не должно... пытаюсь найти причину.
На этот случай в отладчиках есть точка останова. До неё обычно в режиме отладки программа добирается с нормальной скоростью.
На этот случай в отладчиках есть точка останова. До неё обычно в режиме отладки программа добирается с нормальной скоростью.
Я уже все прерывания остановил - все равно . "Продолжить до сдедующей точки останова" - идет со скоростью примерно 10-20 ассемблерных инструкций в секунду. то есть очень медленно. Если идти по шагам - скорость нормальная, если не считать того, что каждую строчку пройти надо вручную.
Нашел описание этого бага в инете в паре мест, решения там нет
ссылка https://www.openstm32.org/forumthread1369
добил, все работает.
Для информации - в настройках аддона в режиме Debug стоит какая-то странная опция компилятора -Og
В мане GCC я такой опции не нашел, написано только, что опции оптимизации -Ox можно комбинировать с опциями дебага -gx. но что означает комбинация -Og - не написано.
В итоге поменял эту непонятную опцию на комбинацию -O0 -g3 -ggdb и все поехало.
Возможно опция -g3 избыточна в моем случае, но пока размер прошивки не превышает флеша МК - это не особо важно.
Надеюсь, кому-то эта информация будет полезной.
-OgLike -O0, -Og completely disables a number of optimization passes so that individual options controlling them have no effect. Otherwise -Og enables all -O1 optimization flags except for those that may interfere with debugging:
продолжаю разбираться с F401 в аддоне Кларка. Хочется понять, почему при попытке выделить даже минимальный блок памяти с помощью malloc() все виснет?
Есть те, кто понимает в ld скриптах? Вот скрипт от F401 из аддона Кларка:
/* * Linker script for libmaple. * * Original author "lanchon" from ST forums, with modifications by LeafLabs. */ OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") /* * Configure other libraries we want in the link. * * libgcc, libc, and libm are common across supported toolchains. * However, some toolchains require additional archives which aren't * present everywhere (e.g. ARM's gcc-arm-embedded releases). * * To hack around this, we let the build system specify additional * archives by putting the right extra_libs.inc (in a directory under * toolchains/) in our search path. */ GROUP(libgcc.a libc.a libm.a) INCLUDE extra_libs.inc /* * These force the linker to search for vector table symbols. * * These symbols vary by STM32 family (and also within families). * It's up to the build system to configure the link's search path * properly for the target MCU. */ INCLUDE vector_symbols.inc /* STM32 vector table. */ EXTERN(__stm32_vector_table) /* C runtime initialization function. */ EXTERN(start_c) /* main entry point */ EXTERN(main) /* Initial stack pointer value. Relocated to RAM */ EXTERN(__msp_init) PROVIDE(__msp_init = ORIGIN(ram) + LENGTH(ram)); /* Reset vector and chip reset entry point */ EXTERN(__start__) ENTRY(__start__) PROVIDE(__exc_reset = __start__); /* Heap boundaries, for libmaple */ EXTERN(_lm_heap_start); EXTERN(_lm_heap_end); SECTIONS { .text : { __text_start__ = .; /* * STM32 vector table. Leave this here. Yes, really. */ *(.stm32.interrupt_vector) /* * Program code and vague linking */ *(.text .text.* .gnu.linkonce.t.*) *(.plt) *(.gnu.warning) *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.ARM.extab* .gnu.linkonce.armextab.*) *(.gcc_except_table) *(.eh_frame_hdr) *(.eh_frame) . = ALIGN(4); KEEP(*(.init)) . = ALIGN(4); __preinit_array_start = .; KEEP (*(.preinit_array)) __preinit_array_end = .; . = ALIGN(4); __init_array_start = .; KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array)) __init_array_end = .; . = ALIGN(0x4); KEEP (*crtbegin.o(.ctors)) KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*crtend.o(.ctors)) . = ALIGN(4); KEEP(*(.fini)) . = ALIGN(4); __fini_array_start = .; KEEP (*(.fini_array)) KEEP (*(SORT(.fini_array.*))) __fini_array_end = .; KEEP (*crtbegin.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*crtend.o(.dtors)) } > REGION_TEXT /* * End of text */ .text.align : { . = ALIGN(8); __text_end__ = .; } > REGION_TEXT /* * .ARM.exidx exception unwinding; mandated by ARM's C++ ABI */ __exidx_start = .; .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } > REGION_RODATA __exidx_end = .; /* * Read-only data */ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) /* .USER_FLASH: We allow users to allocate into Flash here */ *(.USER_FLASH) /* ROM image configuration; for C startup */ . = ALIGN(4); _lm_rom_img_cfgp = .; LONG(LOADADDR(.data)); /* * Heap: Linker scripts may choose a custom heap by overriding * _lm_heap_start and _lm_heap_end. Otherwise, the heap is in * internal SRAM, beginning after .bss, and growing towards * the stack. * * I'm shoving these here naively; there's probably a cleaner way * to go about this. [mbolivar] */ _lm_heap_start = DEFINED(_lm_heap_start) ? _lm_heap_start : __data_end__; _lm_heap_end = DEFINED(_lm_heap_end) ? _lm_heap_end : __msp_init; } > REGION_RODATA /* * .data */ .data : { . = ALIGN(8); __data_start__ = .; *(.got.plt) *(.got) *(.data .data.* .gnu.linkonce.d.*) . = ALIGN(8); __data_end__ = .; } > REGION_DATA AT> REGION_RODATA /* * .bss */ .bss : { . = ALIGN(8); __bss_start__ = .; *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) . = ALIGN (8); __bss_end__ = .; _end = __bss_end__; } > REGION_BSS /* * Debugging sections */ .stab 0 (NOLOAD) : { *(.stab) } .stabstr 0 (NOLOAD) : { *(.stabstr) } /* DWARF debug sections. * Symbols in the DWARF debugging sections are relative to the beginning * of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) } /DISCARD/ : { *(.note.GNU-stack) } }Обратите внимание на строчки 152 и 153, которые устанавливают границы кучи. Верх кучи установлен на метку __msp_init, которая в строке 43 приравнена верху RAM. Тут вроде все хорошо.
А вот низ кучи указывает на окончание секции .data (на метку __data_end__) - а ведь секция data располается не в RAM. а во флеше, да к тому же весьма далеко от адресов, по которым в едином пространстве адресов СТМ расположена оперативка. - Не ошибка ли это?
Если сравнить этот файл с аналогичным для блюпилл,(там malloc() работает) - у блюпилл куча располается между верхом RAM и секцией bss - как описано во всех учебниках.
Короче, я просто взял и заменил в строке 152 метку __data_end__ на __bss_end__ :
и все заработало!
Теперь вопрос - мое исправление верное или это какой-то костыль?
-OgOtherwise -Og enables all -O1 optimization flags except for those that may interfere with debugging:a5021, спасибо за инфо. Судя по выделенному куску, с использованием -Og некоторая оптимизация продолжает применяться. Вероятно, именно это и приводило к конфликту с дебаггером, раз при полном отключении оптимизации опцией -O0 дебаггер начал работать.
Напомните плиз, кто-то писал, что аддоны СТМ и Кларка не ставятся одновременно. Есть какие-то хаки, как их заставить не мешать друг другу?
b707, это я писал в начале темы, были глюки, не помню уже какие конкретно. Может за давностью времени уже не актуально..
b707, это я писал в начале темы, были глюки, не помню уже какие конкретно. Может за давностью времени уже не актуально..
Димакс, спасибо за отклик. Попробую сам.
А по линкер скриптам (сообщение 535) не подскажете?
У меня стоят оба рядом. Работает тот что выбрал. Глюки связаны только с разной семантикой выражений. НАL не дружит с Кларком.
nik182. спасибо
#include <iBUStelemetry.h> iBUStelemetry telemetry(4); byte i; void setup(){ telemetry.begin(115200); telemetry.addSensor(2); } void loop(){ telemetry.setSensorMeasurement(1, i++); delay(1000); }Не получится легко это сделать. Библиотека завязана на хардверные прерывания и их надо переопределять с авр на стм. Или искать библиотеку где это уже сделали.
Бред околособачий! CPU в гаджетах это одно, а MCU в новых разработках это другое. Пройдёт ещё 3-5 лет и атмэла в новых разработках вообще не будет, от слова совсем.
...прошло 3-5 лет...
вопрос, кому-нибудь удалось адаптировать библиотеку UTFT под STM32F103?
А что, разве она еще не адаптирована?
А что, разве она еще не адаптирована?
из тех, что нашёл, нет, сам удивляюсь
Ну, не знаю, в своих проектах я пользуюсь исключительно своими библиотеками, поэтому опыта работы с более или менее стандартными не имею.
Хотя, стандартные я нередко использую, когда надо быстро убедиться в работоспособности только что полученного экрана. А т.к экраны я последнее время покупаю в основном 3-вольтовые, думаю, что поддержка stm32f103 мне, скорее всего, где-то попадалась. На чем-то же я их проверял!
На чем-то же я их проверял!
я что за UTFT вцепился, там поддержка большого количества контроллеров, лёгкость перехода на экран иного размера