Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Инициализация периферии до входа в main() - возможно ли?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
sonycman
Run-time библиотека Си++ вызывает конструкторы глобальных объектов ещё до начала выполнения функции main().
Хотелось бы, чтобы в этот момент необходимая периферия контроллера уже была проинициализирована, дабы код конструкторов не был ничем ограничен.
Но как это сделать в RealView? Никаких __low_level_init() я в руководстве компилятора не увидел... unsure.gif

ЗЫ: приятно обновился интерфейс форума a14.gif
zltigo
Цитата(sonycman @ Jan 8 2009, 20:39) *
Но как это сделать в RealView? Никаких __low_level_init() я в руководстве компилятора не увидел... unsure.gif

Вставьте вызов иеициализирующей функции в startup после инициализации стека. Все.
sonycman
Цитата(zltigo @ Jan 8 2009, 21:47) *
Вставьте вызов иеициализирующей функции в startup после инициализации стека. Все.

Ага, спасибо.
Я уже подумал про асмовый стартап. Можно, наверное, поставить BL прямо перед вызовом __main. Даже стёк инитить не надо - он грузится автоматически с нулевого адреса таблицы векторов.

А есть ли какие-то ограничения у такого способа? В смысле не лишкануть бы чего в подпрограмме пре-инициализации rolleyes.gif
sergeeff
На сайте atmel'a лежат свежие версии soft'a для sam процессоров (http://www.atmel.com/dyn/general/tech_doc.asp?doc_id=11318&family_id=605). Там, например, есть проект basic-emac-uip-telnetd-project-at91sam9263-ek-keil, а в нем файл - board_cstartup_keil.s. Из него вызывается функция LowLevelInit() как раз до вызова main().
sonycman
Цитата(sergeeff @ Jan 8 2009, 22:35) *
На сайте atmel'a лежат свежие версии soft'a для sam процессоров (http://www.atmel.com/dyn/general/tech_doc.asp?doc_id=11318&family_id=605). Там, например, есть проект basic-emac-uip-telnetd-project-at91sam9263-ek-keil, а в нем файл - board_cstartup_keil.s. Из него вызывается функция LowLevelInit() как раз до вызова main().

А можно привести здесь эти строки? А то качать 40 метров слишком накладно...
zltigo
Цитата(sonycman @ Jan 8 2009, 21:33) *
Даже стёк инитить не надо - он грузится автоматически с нулевого адреса таблицы векторов.

Типа Cortex-M3 smile.gif?
Цитата
А есть ли какие-то ограничения у такого способа?

Ничего необычного нет.
defunct
А чем не устраивает main рассматривать как ту самую функцию настройки периферии, из нее уже запускать что-то еще.

Цитата
А можно привести здесь эти строки? А то качать 40 метров слишком накладно...

Вам оно не пригодится. Между SAM'ом и Cortex'ом пропасть как раз в стартапе.

Цитата
Можно, наверное, поставить BL прямо перед вызовом __main. Даже стёк инитить не надо - он грузится автоматически с нулевого адреса таблицы векторов.

Конечно, можно. ;>

Для кортекса:
Код
Reset_Handler
        IMPORT  LowLevelInit
        IMPORT  __main
        BL      LowLevelInit
        LDR     R0, =__main
        BX      R0


Сам же LowLevelInit объявите как void LowLevelInit(void) в любом .c файле.
sonycman
Цитата(zltigo @ Jan 8 2009, 23:14) *
Типа Cortex-M3 smile.gif?

Ага disco.gif

Цитата(defunct @ Jan 8 2009, 23:35) *
А чем не устраивает main рассматривать как ту самую функцию настройки периферии, из нее уже запускать что-то еще.

Немного не устраивает то, что приходится вводить в классы доп. функцию для инициализации, так как часть работы не может быть выполнена в конструкторе, когда периферия не сконфигурирована... sad.gif
Мелочь, в принципе...
Цитата
Для кортекса:
Код
Reset_Handler
        IMPORT  LowLevelInit
        IMPORT  __main
        BL      LowLevelInit
        LDR     R0, =__main
        BX      R0


Сам же LowLevelInit объявите как void LowLevelInit(void) в любом .c файле.

Спасибо, так и поступлю.
Ещё вот интересно, для чего при переходе на __main юзать R0? А если просто B __main?
zltigo
Цитата(sonycman @ Jan 8 2009, 23:15) *
А если просто B __main?

Если уверены, что сможете дотянуться до main 256*2 байтовым смещением, то можете.
aaarrr
Цитата(defunct @ Jan 8 2009, 22:35) *
А чем не устраивает main рассматривать как ту самую функцию настройки периферии, из нее уже запускать что-то еще.

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

Цитата(sonycman @ Jan 8 2009, 23:15) *
Ещё вот интересно, для чего при переходе на __main юзать R0? А если просто B __main?

А если __main далеко?
_Pasha
Цитата(defunct @ Jan 8 2009, 22:35) *
А чем не устраивает main рассматривать как ту самую функцию настройки периферии, из нее уже запускать что-то еще.

Лично я делаю так:

Вначале надо прописать ввод-вывод. Сколько гемора бывает из-за "неспешной" инициализации порта(ов),


потом - стек, типа low_level_init(), потом - секции, ремап и main()
defunct
Цитата(aaarrr @ Jan 8 2009, 22:30) *
До main хотя бы память должна ожить. Кроме того, тоскливо получается копировать/инициализировать эту память на медленных клоках, например.

Согласен, актуально для МК с кешем, внешним RAM, либо толстым Init RW сегментом.

В контексте кортекса, внешней шины часто вообще нет (оживлять нечего), а внутреннего RAM'a - с гулькин нос, поэтому и PLL настраивать до main'a ради инициализации пары KB переменных смысла особого нет.
aaarrr
Цитата(defunct @ Jan 9 2009, 02:24) *
Согласен, актуально для процессоров с MMU и кешем и внешним SDRAM'ом.

Ну, еще для атмеловских SAM'ов, которые с 32kHz запускаются, тоже весьма актуально.

Цитата(defunct @ Jan 9 2009, 02:24) *
внутреннего RAM'a - с гулькин нос, поэтому и PLL настраивать до main'a ради инициализации пары KB переменных смысла особого нет.

Если это пара десятков килобайт и пара десятков килогерц, то совсем даже не мало получается.
defunct
Цитата(aaarrr @ Jan 9 2009, 01:32) *
Ну, еще для атмеловских SAM'ов, которые с 32kHz запускаются, тоже весьма актуально.

Угу, что правда, то правда.
Цитата
Если это пара десятков килобайт и пара десятков килогерц, то совсем даже не мало получается.

Везде свой подход. Мне что-то подсказывает (соседние ветки), что проц, который использует автор топика стартует на нормальной частоте (явно больше 32kHz) smile.gif
Иначе бы и не заикался на счет инициализации в main smile.gif
sonycman
Цитата(defunct @ Jan 9 2009, 03:35) *
Везде свой подход. Мне что-то подсказывает, что проц, который использует автор топика стартует на нормальной частоте (явно больше 32kHz) smile.gif
Иначе бы и не заикался на счет инициализации в main smile.gif

Да у меня STM32. Стартует на внутреннем RC 8 МГц - вполне хватает, чтобы не слишком торопиться с PLL.
До этого был LM3S601, который я угробил, переназначив вывод житага на GPIO... есть ещё оказывается камни, которые убиваются софтовой ошибкой 01.gif
Кстати, какая разница между внутренними RC генераторами этих контроллеров - с одной стороны 8 МГц +- 1%, с которого чип запускается после ресета.
С другой - 15 МГц +- 50%, запуск с которого невозможен, только после софтового переключения... на кой нужен такой генератор? laughing.gif
Видать, нет у Luminary индивидуальной калибровки... экономят-с...
Dima_G
Не полагайся на порядок вызовов конструкторов, инициализаторов периферии и тд - легко словить грабли в дальнейшем.

У меня все сервисы имеют три стадии инициализации:

1) Собственно сами конструкторы классов. В них я предполагаю, что класс автономен, соответственно и не обращаюсь к другим классам и ресурсам. Здесь же инициализируется периферия (естественно, два класса не могут юзать одну и ту же периферию)

2) Стадия Init - здесь уже налаживаются связи между классами (к этому моменту все необходимые объекты уже созданы)

3) Стадия Run - запускается таски, разрешаются прерывания от периферии и тд - в общем, нормальная работа приложения

объекты глобальные, соответственно создаются до main. А в main я уже вызываю init и run.

Суть в чем - не полагайся, что кто-то что-то сделал заранее smile.gif
sonycman
Цитата(Dima_G @ Jan 9 2009, 09:32) *
Не полагайся на порядок вызовов конструкторов, инициализаторов периферии и тд - легко словить грабли в дальнейшем.

У меня все сервисы имеют три стадии инициализации:

1) Собственно сами конструкторы классов. В них я предполагаю, что класс автономен, соответственно и не обращаюсь к другим классам и ресурсам. Здесь же инициализируется периферия (естественно, два класса не могут юзать одну и ту же периферию)

2) Стадия Init - здесь уже налаживаются связи между классами (к этому моменту все необходимые объекты уже созданы)

3) Стадия Run - запускается таски, разрешаются прерывания от периферии и тд - в общем, нормальная работа приложения

объекты глобальные, соответственно создаются до main. А в main я уже вызываю init и run.

Суть в чем - не полагайся, что кто-то что-то сделал заранее smile.gif

Понятно, спасибо!
Хотя не всегда объект может иметь свою собственную периферию - в случае её совместного использования невозможно ввести полную инициализацию внутрь одного класса.
Dima_G
Цитата(sonycman @ Jan 9 2009, 14:27) *
Понятно, спасибо!
Хотя не всегда объект может иметь свою собственную периферию - в случае её совместного использования невозможно ввести полную инициализацию внутрь одного класса.

Это уже следующий шаг smile.gif. Тут уже соблюдаю правило - на каждую периферию - свой объект (можно назвать драйвером).
Те работа всех объектов с УАРТом идет не напрямую, а через объект - драйвер УАРТ.
Или работа с езернетом - получен кадр - сервис-драйвер езернета отправляет его всем желающим (фактически в моем Enviromnet это выглядит как broadcast пакет. Чем-то идеология на CANbus похожа smile.gif )

Сам понимаешь, при таком подходе легче обходятся грабли с синхронизацией доступа к ресурсу..
sonycman
Цитата(Dima_G @ Jan 10 2009, 21:30) *
Сам понимаешь, при таком подходе легче обходятся грабли с синхронизацией доступа к ресурсу..

Да, синхронизация - дело хитрое.
Вот, к примеру думал тут недавно над одной задачкой...

В общем, есть два канала SPI. И три объекта, "висящие" на них.
Первый - цветной ЖКИ, скорость 12 Мбит, огромные объёмы данных, способен занять канал на длительное время. Самый низкий приоритет.
Второй - диск на флешке. Скорость высокая, но пакеты небольшие - сектора по несколько сот/тысяч байт. Средний приоритет.
И третий - к примеру - декодер МП3 (или ЦАП) или другой девайс с относительно маленьким каналом, но требующий быстрой реакции на запрос.

Как их уместить на одном/двух каналах SPI, чтобы не умереть от геморроя с разделением ресурсов?
defunct
Цитата(sonycman @ Jan 10 2009, 22:51) *
Как их уместить на одном/двух каналах SPI, чтобы не умереть от геморроя с разделением ресурсов?

Два варианта:

A) ЖКИ транзакцию прервать никак нельзя:
SPI1 - ЦАП и FS. (в худшем случае ЦАПу придется дождаться завершения передачи текущего сектора FS, но скорость FS выская поэтому ждать не долго).
SPI2 - ЖКИ.
Приоритет ЦАПа сделать более высоким чем приоритет FS.

Б) ЖКИ транзакцию прервать без потерь можно.
SPI1 - FS
SPI2 - ЦАП и ЖКИ
обмен с ЖКИ моментально прерывается процессом обслуживающим ЦАП, и возобновляется после.



FS и ЖКИ я бы старался не цеплять на один интерфейс, во-первых чтобы скорость SPI не переключать (SPI флеш может и на 25Mhz и выше свистеть), ну и чтобы снизить нагрузку на интерфейс, т.к. потоки данных (по суммарному объему) сравнимые.
Dima_G
Цитата(sonycman @ Jan 11 2009, 00:51) *
Да, синхронизация - дело хитрое.
Вот, к примеру думал тут недавно над одной задачкой...

В общем, есть два канала SPI. И три объекта, "висящие" на них.
Первый - цветной ЖКИ, скорость 12 Мбит, огромные объёмы данных, способен занять канал на длительное время. Самый низкий приоритет.
Второй - диск на флешке. Скорость высокая, но пакеты небольшие - сектора по несколько сот/тысяч байт. Средний приоритет.
И третий - к примеру - декодер МП3 (или ЦАП) или другой девайс с относительно маленьким каналом, но требующий быстрой реакции на запрос.

Как их уместить на одном/двух каналах SPI, чтобы не умереть от геморроя с разделением ресурсов?


Я бы сделал так: два объекта драйвера - ClSPI_Driver<SPI0> и ClSPI_Driver<SPI1>. Следующий шаг - выяснить, необходима ли атомарность передачи больших объемов к ЖКИ (те можно ли бить данные на мелкие пакеты). Если можно передавать только здоровыми кусками (соответственно, остальные девайсы на SPI будут курить бабмбук в это время, что недопустимо), то под ЖКИ придется выделять отдельный канал SPI. А остальные девайсы повесил бы на второй SPI.

Разделение приоритетов передачи данных на одном порту:
Создаешь в драйвере очереди запросов с различными приоритетами. И драйвер, закончив очередную транзакцию начинает проверять очереди и высылать данные в порядке порядке приоритетов. Те пока не высланы данные для декодера (real-time приоритет), не трогать данные для флешки - low-prioritet для этого канала.


Те в общем, механизм был бы такой - ЖКИ единолично работает со своим SPI + канал DMA (драйвер<1>). На втором канале драйвер<2> выгребает данные из low-priority очереди (для флешки). А отдельный таск (или обработчик таймера) периодически подкидывает драйверу<2> блоки данных для декодера, который он отправляет за время не худшее, чем максимальный_объем_пакета_для_флешки / скорость_SPI + время реакции драйвера.

В общем, как-то так smile.gif
sonycman
Цитата(defunct @ Jan 11 2009, 07:12) *
FS и ЖКИ я бы старался не цеплять на один интерфейс, во-первых чтобы скорость SPI не переключать (SPI флеш может и на 25Mhz и выше свистеть), ну и чтобы снизить нагрузку на интерфейс, т.к. потоки данных (по суммарному объему) сравнимые.

Цитата(Dima_G @ Jan 11 2009, 07:26) *
В общем, как-то так smile.gif

Спасибо большое! Буду руководствоваться вашими советами, когда, наконец, руки до этого проекта дойдут.
Вы мне очень помогли cheers.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.