Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: быстрый тайминг GPIO для LPC
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
ГУ-49А
Есть такая задача: реализовать быстрый синхронный 16-битный вывод в GPIO-порт для LPC2103 (через FIO). При этом ещё должна выполняться фоновая задача. Проблема в том, что вывод в порт необходимо делать стабильно, через равные интервалы времени, с как можно большей частотой. Скорость выполнения фоновой задачи при этом не принципиальна, но она не должна задерживать основной вывод в порт.

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

Если же просто перемежать вызовы команд этих двух задач, то не понятно, как при этом обеспечить стабильный тайминг вывода в порт - ведь подсчёт тактов для пайплайна задача непростая... Есть ли какой-то инструментарий для подсчёта тактов выполнения команд на LPC? Чтобы можно было с учётом этого написать на асме код фоновой задачи так, чтобы вперемешку с ним выводить в порт данные строго каждые N тактов?

Какие ещё существуют способы решения данной задачи?
zltigo
Цитата(ГУ-49А @ Jan 6 2008, 22:16) *
Какие ещё существуют способы решения данной задачи?

Конкретных способов решения абстрактной ("делать стабильно, через равные интервалы времени, с как можно большей частотой") задачи (с неназванными временами) не существует smile.gif
zhevak
Цитата(ГУ-49А @ Jan 7 2008, 01:16) *
Какие ещё существуют способы решения данной задачи?


А вообще о каких скоростях вывода в порт идет речь? Т.е. Сколь раз в секунду требуется выкидывать данные.

Как-то давным давно у меня возникла подобная задача: что-то там делать в реальном времени. Я это "дело" навесил на обработчик прерываний от таймера. А что бы понять, сколько времени у меня реально занимает процесс, я не стал заморачиваться подсчетом тактов, а сделал по-простому.

При входе в прерывание устанавливал единичку на свободной лапке микроконтроллера, а перед выходом из прерывания -- нолик. На осциллографе наблюдал прямоугольник. Скоп был хороший (LeCroy) и позволял собирать статистику -- min и max значения. Я задал ему наблюдать длительность импульса. Я запустил эту дело, пошел пить кофею, а когда вернулся уже имел и минимум, и максимум. Далее осталось только прикинуть сколько времени остнется фоновому процессу.

Я понимаю, что все это решалось очень по-крестьянски, но оцените эффективность такого решения. А достоверность результатов? Думаю, имеет смысл Вам проделать такой же эксперимент. А там, получив результаты, сориентируетесь.
Rst7
Цитата
Скоп был хороший (LeCroy) и позволял собирать статистику


Не могу не отметить, что наличие хорошего скопа не есть необходимое условие. Берем китайский мультиметр и меряем напряжение на ножке. CPUload=100*Uножки/Uпитания в процентах.

Пардон за оффтоп.
ГУ-49А
Цитата(zltigo @ Jan 6 2008, 22:45) *
Конкретных способов решения абстрактной ("делать стабильно, через равные интервалы времени, с как можно большей частотой") задачи (с неназванными временами) не существует smile.gif

Некоторые способы я перечислил. Меня устроит, если вывод в порт будет происходить, скажем, каждые 10 тактов. Если получится быстрее - отлично; если можно только медленнее - надо видеть, насколько. За каждый выигранный или проигранный такт я хочу побороться.

Если самый лучший по скорости вывода способ будет напрямую зависеть от конкретных команд задачи (как в случае с вышеупомянутым методом интерлива кода), то, само-собой, кроме меня никто мою задачу решать не будет. Но в таком случае я хотел бы получить ответ на ранее заданный вопрос: Есть ли какой-то инструментарий для подсчёта тактов выполнения команд на LPC? Какова вообще методика расчёта таймингов, ведь кто-то наверняка уже это делал?

Или всё-таки можно сделать по-другому?

Цитата(zhevak @ Jan 6 2008, 22:50) *
А вообще о каких скоростях вывода в порт идет речь? Т.е. Сколь раз в секунду требуется выкидывать данные.

Речь идёт о попытке достижения теоретического максимума на данном железе и задаче. Т.е. порядок цифр - мегагерцы.
Цитата(zhevak @ Jan 6 2008, 22:50) *
Как-то давным давно у меня возникла подобная задача: что-то там делать в реальном времени. Я это "дело" навесил на обработчик прерываний от таймера. А что бы понять, сколько времени у меня реально занимает процесс, я не стал заморачиваться подсчетом тактов, а сделал по-простому.

Повесить на таймер и измерять скорость осциллом - это начальный вариант для такой задачи, спасибо. Каких частот вам удалось достичь, скажем, для простого "дёргания ногой"? Как вы считаете, насколько можно превзойти этот метод?
zltigo
Цитата(ГУ-49А @ Jan 6 2008, 23:04) *
вывод в порт будет происходить, скажем, каждые 10 тактов

Задача для LPC2000 решения не имеет. Порядка 4 тактов только вывод через FastGPIO причем это тупая загрузка содержимого регистра по адресу содержащимуся в другом регистре. При этом нужно еще подтаскивать поток в 12 мегабайт в секунду smile.gif Ну и кто-то там еще про "фоновые задачи" smile.gif .....
Цитата
Или всё-таки можно сделать по-другому?

12 МегаБайт в секунду это скорости совсем других контролеров + аппаратная поддержка для обеспечения синхронного порта.


Цитата(ГУ-49А @ Jan 6 2008, 23:10) *
Каких частот вам удалось достичь, скажем, для простого "дёргания ногой"?

Ознакомьтесь с документацией - узнаете предел.
GetSmart
Думаю можно достичь скорости вывода до Fosc/25 (меандр Fosc/50) или около того. Если в основной проге не будет длинных команд LDM и STM.
ГУ-49А
Цитата(zltigo @ Jan 6 2008, 23:46) *
Задача для LPC2000 решения не имеет.

Значит, будем искать решение задачи для более низких граничных частот.
Цитата(zltigo @ Jan 6 2008, 23:46) *
Ознакомьтесь с документацией - узнаете предел.

Обратите внимание, что вопрос касался выдачи в порт по таймеру, причём на фоне другой задачи.

Цитата(GetSmart @ Jan 7 2008, 11:43) *
Думаю можно достичь скорости вывода до Fosc/25 (меандр Fosc/50) или около того. Если в основной проге не будет длинных команд LDM и STM.

Спасибо. Я готов переписать фоновую прогу наиболее короткими командами в рамках возможного. Но как, тем не менее, обеспечивать стабильность по частоте вывода? Как компенсировать разное время выполнения команд фоновой проги? В связи с этим вынужден повторить свой вопрос: есть ли готовый инструментарий, который, как минимум, для линейного кода на асме проставляет такты (для данного ядра), и, как максимум, может дополнять команды до нужного кол-ва тактов командами-пустышками либо усложнять команды (без изменения алгоритма)?
zhevak
Боюсь нарваться на скандал, но все же скажу, ибо истина дороже синяков.

Уважаемый, если у Вас стали возникать такие вопросы (типа "повставлять NOPы для выравнивания скоростей"), то мне кажется Вы на ложном пути. Вам нужно либо взять проц по-быстрее, либо в корне пересмотреть парадигму построения вашей системы. При Вашем подходе малейшее отклонение в сторону дискредитирует Вашу систему. И Вы будете вынуждены начинать все сначала. На фоне рилтайм-процессов, у системы совсем нет запаса по выч. мощности. Опасная игра, однако!

Если у Вас так жестко с реальным временем, то, будь я на Вашем месте, я бы наверно отказался бы от фоновых процессов совсем. Вынес бы их на другой микроконтроллер. (Не зная точно задачу, можно так-акого насоветовать!... smile.gif )

Хотя, я вот сейчас подумал, что наверное Вы сделали действие с точностью до наоборот: сначала прикупили железо, а сейчас думаете, как из него выжать по максимуму. От сюда и пошли Ваши проблемы.
GetSmart
Цитата(ГУ-49А)
Спасибо. Я готов переписать фоновую прогу наиболее короткими командами в рамках возможного. Но как, тем не менее, обеспечивать стабильность по частоте вывода?
Я имелл ввиду вызов FIQ по таймеру. При этом без всякого гемора будет стабильная частота и небольшой джиттер.

По поводу подсчёта тактов, вроде бы отладчик в ИАРе умеет их считать. Однако не знаю насколько он умный, т.к. в разных режимах ускорения или предвыборки после смены PC тратятся дополнительные "пустые" такты. Вообще, идея интересная. Но, думаю, подобных инструментариев нет и всё придётся делать ручками. Предворительно вызубрив наизусть длительность всех команд и их вариантов. Предворительно перед этим выяснив все тонкости конвейера и прочие нюансы архитектуры ARM7-LPC. Если всё это закончится успехом, то можно достичь теоретически предельной скорости вывода, равной сумме тактов самой длинной используемой команды, плюс 5 тактов (3 на чтение из ОЗУ и 2 на запись в порт). Возможно это будет Fosc/10.
ГУ-49А
Цитата(zhevak @ Jan 7 2008, 14:46) *
Боюсь нарваться на скандал, но все же скажу, ибо истина дороже синяков.
...сейчас думаете, как из него выжать по максимуму. От сюда и пошли Ваши проблемы.

Вы совершенно правы. Речь идёт о том, чтобы решить эту задачу на уже существующем железе, и при том, что, очевидно, существуют более подходящие для этого аппаратные решения. Большинство концептуальных недостатков подобной реализации мне, увы, известны, и я благодарен вам за понимание тех трудностей, с которыми я столкнулся. И задачка эта, мне кажется, по-своему интересна, хоть и в таком извращённом виде. smile.gif

Если готового инструментария для подгонки тактов нет, то посоветуйте, хотя бы, хороший инструментарий для подсчёта тактов для команд написанной программы, без необходимости её компиляции и пошаговой прогонки. Или, может, мне стоит написать такой инструментарий самому?
GetSmart
Цитата
Или, может, мне стоит написать такой инструментарий самому?
О!!! А вот это хорошая идея. Это будет проще всего. Если проект серьёзный, то я бы на вашем месте потратил недельку-другую на некий конвертер, который любой файл, скомпиленный например ИАРом сконвертит и навставляет в код команд вывода. У АРМа совсем немного команд. Нужно только зарезервировать пару регистров чтоб ИАР их не использовал, и избегать условных команд с переменым кол-вом тактов. А в остальном ничего сложного. Правда в полностью автоматический конвертер не получится, остальное придётся опять ручками. Но это реальный вариант.
ГУ-49А
Цитата(GetSmart @ Jan 7 2008, 15:37) *
Я имелл ввиду вызов FIQ по таймеру. При этом без всякого гемора будет стабильная частота и небольшой джиттер.

Согласен, этот запасной вариант у нас уже есть. Теперь давайте попробуем подумать над более сложной альтернативой:
Цитата(GetSmart @ Jan 7 2008, 15:37) *
По поводу подсчёта тактов, вроде бы отладчик в ИАРе умеет их считать. Однако не знаю насколько он умный, т.к. в разных режимах ускорения или предвыборки после смены PC тратятся дополнительные "пустые" такты.

С отладчиком в IAR'е, как и с Протеусовским эмулятором в этом плане довольно сложно работать. Нужно каждый раз "ходить" по программе, считать дельту счётчика тактов, одним словом - неудобно это для такой тонкой оптимизации. Куда удобнее было бы для каждой команды из программы сразу посчитать кол-во тактов, для неё необходимых.
Цитата(GetSmart @ Jan 7 2008, 15:37) *
Вообще, идея интересная. Но, думаю, подобных инструментариев нет и всё придётся делать ручками. Предварительно вызубрив наизусть длительность всех команд и их вариантов. Предварительно перед этим выяснив все тонкости конвейера и прочие нюансы архитектуры ARM7-LPC. Если всё это закончится успехом, то можно достичь теоретически предельной скорости вывода, равной сумме тактов самой длинной используемой команды, плюс 5 тактов (3 на чтение из ОЗУ и 2 на запись в порт). Возможно это будет Fosc/10.

Именно это и представляется мне на данный момент. Вопрос только в том, можно ли как-то облегчить себе труд. Например, можно написать парсер кода, который проэмулирует конвейер ARM7TDMI-S ровно настолько, чтобы посчитать количество тактов и выдать их мне. Просто меня удивляет, что такой утилиты, судя по всему, нет?
GetSmart
Вы бы ещё пояснили подробности такого нестандартного вывода из процессора. Что за данные и в какое устройство перегоняются? Это связано с управлением светодиодами?

Цитата(ГУ-49А)
Например, можно написать парсер кода, который проэмулирует конвейер ARM7TDMI-S ровно настолько, чтобы посчитать количество тактов и выдать их мне.
У LPC и SAM7 одинаковое ядро ARM7TDMI-S, но конвейер разный. Так что всё это будет конкретно привязано к конкретному процессору. Даже к конкретной его ревизии (ревизиям!). Так как никто не обещал, что улучшений в сторону ускорения работы процессора в новых ревизиях не предвидится.
zhevak
К сожалению, ни готового решения, ни совета я Вам дать не могу.

Выше люди уже предлагали использовать FIQ. Почему не проходит этот вариант?

Вы уже почти сутки пытаетесь теоретически определить способность системы решать Вашу задачу. Думаю, за сутки можно было одновременно и на форуме поговорить со спецами и накидать пилот-проектик для FIQ. Посмотреть осциллографом скорости, оценить джиттер. Т.е. опробовать идею. Джиттер (дрожжание) обязательно будет, т.к. в системе присутствуют модуль MAM, система не мгновенно отрабатывет прерывание, ... да, много, много всего может "случится между ложкой и ртом".

Да, поможет Вам практика! Сотня-другая циклов стирания-программирования не сильно износят ресурс микроконтроллера. Зато Вы четко будете ориентироваться в том, что Вы делаете, и в каком направлении Вам "пилить" дальше.

Удачи!
ГУ-49А
Цитата(zhevak @ Jan 7 2008, 16:12) *
К сожалению, ни готового решения, ни совета я Вам дать не могу.
Выше люди уже предлагали использовать FIQ. Почему не проходит этот вариант?
...
Удачи!

Спасибо. Этот вариант "проходит", просто не является оптимальным по максимальной частоте вывода в порт по сравнению с его потенциальной альтернативой (интерлив кода). Моя задача скорее R&D'шная и не требует наискорейшей реализации. Поэтому я могу себе позволить "семь раз отмерить".

Цитата(GetSmart @ Jan 7 2008, 16:06) *
У LPC и SAM7 одинаковое ядро ARM7TDMI-S, но конвейер разный. Так что всё это будет конкретно привязано к конкретному процессору. Даже к конкретной его ревизии (ревизиям!). Так как никто не обещал, что улучшений в сторону ускорения работы процессора в новых ревизиях не предвидится.

Где в документации, в таком случае, искать такты для нужного конвейера нужной ревизии LPC2103? Я знаю только про общий армовский Instruction Set... Существующие счётчики тактов тоже далеко не точны, получается? Т.е. одна из сложностей задачи - нет чёткой информации о таймингах конкретных конвейеров?

Цитата(GetSmart @ Jan 7 2008, 16:06) *
Вы бы ещё пояснили подробности такого нестандартного вывода из процессора. Что за данные и в какое устройство перегоняются? Это связано с управлением светодиодами?

Данные идут в R-2R, т.е. такой себе дешевый тупой аналог DDS.
GetSmart
Цитата(ГУ-49А)
Т.е. одна из сложностей задачи - нет чёткой информации о таймингах конкретных конвейеров?
О-да! В принципе это можно сделать ручками. В этом плане ARM7 лучше чем суперскалярные процы с кэшами. В них вообще предсказывать количество тактов дело неблагодарное. Я например уже исследовал скорости выполнения некоторых команд на LPC2132. Команды и их параметры делятся на классы и потом у каждого сочетания этих двух-трёх классов на реальном железе определяется кол-во тактов. А вот подозрения об ускорении новых ревизий у меня возникли когда давно я измерял скорость вывода в порт на LPC2138 rev- получил 8 тактов, потом ещё мерил на процах LPC2132 revC и получил 7 тактов. Возможно я ошибся, но все-таки никто ничего не гарантировал. Предполагаю только что конвейеры у одинаковых ревизий процов, появившихся более-менее в одно время должны быть одинаковыми.
zltigo
Цитата(ГУ-49А @ Jan 7 2008, 12:25) *
Обратите внимание, что вопрос касался выдачи в порт по таймеру, причём на фоне другой задачи.

Обратите внимание, что количество тактов для входа в обработчик прерывания (в частности в FIC) документировано (контроллер прерываний используется от ARM Company, описание соответственно...). Процедура сброса контроллера прерывания и длительность процедуры возврата тоже не является секретом фирмы. Затраты на махание ножкой - тоже известны. Длительность прерываемых команд - зависит от того, какие лично Вы собираетесь использовать. Вопрос - чего Вам не хватает для ответа на вопрос?
Alex03
Цитата(ГУ-49А @ Jan 7 2008, 19:44) *
Цитата(GetSmart @ Jan 7 2008, 19:06) *
У LPC и SAM7 одинаковое ядро ARM7TDMI-S, но конвейер разный. Так что всё это будет конкретно привязано к конкретному процессору. Даже к конкретной его ревизии (ревизиям!). Так как никто не обещал, что улучшений в сторону ускорения работы процессора в новых ревизиях не предвидится.
Где в документации, в таком случае, искать такты для нужного конвейера нужной ревизии LPC2103? Я знаю только про общий армовский Instruction Set... Существующие счётчики тактов тоже далеко не точны, получается? Т.е. одна из сложностей задачи - нет чёткой информации о таймингах конкретных конвейеров?

ИМХО вы не по делу "трогаете" конвейер(ы) ядра, думаю что во всех чипах на ARM7TDMI-S ядро одинаковое. Длительности исполнения команд ядром расписаны АРМ-ом, НО....
Дальше в дело вмешивается некий контроллер памяти (внешний для ядра), именно он может вставлять вэйтстейты, именно он может иметь возможность отложенной записи (некоторой глубины и/или разной по разным адресам), именно он может разбивать одно обращение к памяти на несколько (в случае невыровненных данных и/или менее разрядной памяти).

И в обшем случае невозможно определить время выполнения конкретной команды на конкретном камне не зная предистории выполнения других команд (тут влияет как состояние самого ядра, так и состояние внешней по отношению к ядру обвязке), не зная "а по какому адресу эта команда полезет в память" (одна и та же команда в внутреннюю RAM пишет за такт, а в порт в/в за N тактов, а в внешннюю RAM за M тактов), и т.д.

в ряде случаев псевдокод
Код
вывод слова в порт ввода вывода
вывод слова в порт ввода вывода

будет выполняться за тоже время что и
Код
вывод слова в порт ввода вывода
одна или несколько команд (типа пересылки регистр/регистр)
вывод слова в порт ввода вывода
GetSmart
Цитата(Alex03)
одна и та же команда в внутреннюю RAM пишет за такт
За 2 такта.
Код
вывод слова в порт ввода вывода
одна или несколько команд (типа пересылки регистр/регистр)
вывод слова в порт ввода вывода
Это было бы в идеально сделанном проце, но в LPC это скорее всего не так. Кажется я это проверял. Однако всю малину могут испортить команды с неопределённым во время компиляции/конвертации количеством циклов. Вообще, подводных камней в конвертере будет много, но идея всё-равно интересная. Если б мне на работе это потребовалось сделать, я бы с удовольствием занялся.

Кстати, есть алгоритм, которым можно достичь супер-пиковой скорости вывода до Fosc/2. Однако в нём джиттер будет пипец как гадить. Однако и его можно полностью подавить. Короче, мисль человеческая безгранична smile.gif

Пока неизвестны характеристики выходного сигнала с R-2R ничего конкретного предложить не могу. Да и алгоритм супер секретный smile.gif
ГУ-49А
Цитата(Alex03 @ Jan 8 2008, 10:20) *
Дальше в дело вмешивается некий контроллер памяти (внешний для ядра), именно он может вставлять вэйтстейты, именно он может иметь возможность отложенной записи (некоторой глубины и/или разной по разным адресам), именно он может разбивать одно обращение к памяти на несколько (в случае невыровненных данных и/или менее разрядной памяти). ...

В конкретном случае с LPC2103 мы можем допустить, что подобным образом может "проказничать" MAM, ведь внешнего RAM'а нет, да и внутренний всего 1 банк в 8 кб? И тогда, отключив MAM, выполняя код из RAM и читая/записывая короткими словами, можно побороть эту проблему?

Цитата(GetSmart @ Jan 8 2008, 11:59) *
Кстати, есть алгоритм, которым можно достичь супер-пиковой скорости вывода до Fosc/2. Однако в нём джиттер будет пипец как гадить. Однако и его можно полностью подавить. Короче, мисль человеческая безгранична smile.gif

Видимо, речь идёт о выводах MATx.y? К сожалению, этот метод не подходит, нужен честный вывод с 12-битным квантованием. Что касается алгоритма вывода, то есть область памяти с данными, их-то и надо слать в порт (16-битный, нужны 12 бит). Всё просто, как видите, за исключением маниакального желания обогнать способ вывода по прерыванию таймера.
GetSmart
Цитата
Видимо, речь идёт о выводах MATx.y? К сожалению, этот метод не подходит, нужен честный вывод с 12-битным квантованием.
Ну вообще-то речь идёт о честном 30-битном квантовании, но нет так нет. Всё-равно никто его не знает, иначе уже сказали бы.
Цитата
В конкретном случае с LPC2103 мы можем допустить, что подобным образом может "проказничать" MAM, ведь внешнего RAM'а нет, да и внутренний всего 1 банк в 8 кб? И тогда, отключив MAM, выполняя код из RAM и читая/записывая короткими словами, можно побороть эту проблему?
Ну да, так чуть-чуть можно ускорить прогу т.к. после смены PC меньше тактов ожидания будет теряться, однако LPC хорош тем, что он даже из внутреннего флэша исполняет большинство команд за такт. Экономия будет только на переходах и на чтении констант из флэш. Наверное это действительно контроллер памяти и внутренних шин AHB и VPB вносят лишние циклы ожидания.
rezident
Цитата(ГУ-49А @ Jan 7 2008, 02:10) *
Или всё-таки можно сделать по-другому?
Может я что-то пропустил в обсуждении, но я бы реализовал такую задачу на стандартном порту SSC или SPI, дополнив ARM десериализатором на любой мелкой ПЛИС. При использовании буферизации потока (DMA) и синхронизации вывода (а SSC и SPI и так синхронные интерфейсы) джиттер будет минимальным. Ну и для фоновой задачи в 16-раз больше времени будет оставаться.
P.S. Извиняюсь, если не совсем в тему.
digital
Цитата
Может я что-то пропустил в обсуждении, но я бы реализовал такую задачу на стандартном порту SSC или SPI, дополнив ARM десериализатором на любой мелкой ПЛИС.

Можно воспользоваться готовой микросхемой parallel<>SPI например MCP23S17 (Microchip) 16бит скорость 10мбит/сек
GetSmart
Господа-товарищи!
Я тоже сначала подумал об SSP, но даже если он был бы в LPC2103, то максимально возможная скорость вывода на нём равна Fosc/32, что меньше чем в простейшей проге с использованием FIQ, хотя и без джиттера. Да ещё и схема снаружи нужна.
rezident
Цитата(GetSmart @ Jan 8 2008, 20:18) *
максимально возможная скорость вывода на нём равна Fosc/32
В любом режиме или только в режиме мастера с тактированием изнутри кристалла?
GetSmart
Скорость вывода бита = 2 такта. Чтобы вывести 16 бит потратится 32 такта. Это в самом-самом идеальном случае. У меня под рукой нет мануала на LPC2103. Короче, если бы в 2103-ем был SSP как в 213х-ом.
GetSmart
Цитата(GetSmart)
Кстати, есть алгоритм, которым можно достичь супер-пиковой скорости вывода до Fosc/2. Однако в нём джиттер будет пипец как гадить. Однако и его можно полностью подавить. Короче, мисль человеческая безгранична smile.gif

Пока неизвестны характеристики выходного сигнала с R-2R ничего конкретного предложить не могу. Да и алгоритм супер секретный smile.gif
Попытался накидать прогу. Оказывается для данного применения мой алгоритм не подходит. Жаль. Он может увеличить амплитудное разрешение, а не временное.

Могу только предложить алгоритм полного избавления от джиттера во время FIQ в ущерб скорости вывода. Возможно скорость упадёт до Fosc/32.
ГУ-49А
Цитата(GetSmart @ Jan 8 2008, 17:47) *
Скорость вывода бита = 2 такта. Чтобы вывести 16 бит потратится 32 такта. Это в самом-самом идеальном случае. У меня под рукой нет мануала на LPC2103. Короче, если бы в 2103-ем был SSP как в 213х-ом.

В 2103-м на SSP тоже нельзя добиться нужной скорости, это точно. К тому же, есть неприятная errata на этот случай, можно нарваться: "Initial data bits/clocks of the SSP transmission are shorter than subsequent pulses at higher frequencies. ... At high SSP frequencies, it is found that the first four pulses are shorter than the subsequent pulses.".

Цитата(GetSmart @ Jan 8 2008, 20:29) *
Могу только предложить алгоритм полного избавления от джиттера во время FIQ в ущерб скорости вывода. Возможно скорость упадёт до Fosc/32.

С интересом выслушаю ваши предложения...
GetSmart
Код
LDR  R9,[R8]  ; R8 = T0CR (инициализируется во время LowLevelInit)
AND  R9,R9,#7 ; с небольшим риском эту команду можно убрать
ADD  R15,R15,R9
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
Я точно не анализировал, но может быть этот алгоритм уменьшит скорость в два раза.

кол-во NOPов тоже нужно подобрать получше. Вобщем я обрисовал только идею. Возможно этот пример нужно немного доработать.
alexander55
Вопрос к автору.
Что Вы хотите сделать ?
Варианты :
- генератор сигналов произвольной формы
- систему регулирования какого-то параметра.
В зависимости от ответа на этот вопрос, решение будет различным.
ГУ-49А
Цитата(alexander55 @ Jan 9 2008, 09:23) *
Вопрос к автору.
Что Вы хотите сделать ?
Варианты :
- генератор сигналов произвольной формы
- систему регулирования какого-то параметра.
В зависимости от ответа на этот вопрос, решение будет различным.

Ответ: Вариант 1-й, т.е. генератор сигналов произвольной формы (на фоне выполнения другой менее приоритетной задачи).
Повторюсь, интересуют программные, а не аппаратные решения.

Цитата(GetSmart @ Jan 8 2008, 21:57) *
кол-во NOPов тоже нужно подобрать получше. Вобщем я обрисовал только идею. Возможно этот пример нужно немного доработать.

Идею уловил, спасибо.
alexander55
Цитата(ГУ-49А @ Jan 9 2008, 21:16) *
Ответ: Вариант 1-й, т.е. генератор сигналов произвольной формы (на фоне выполнения другой менее приоритетной задачи).

Понятно.

Цитата(ГУ-49А @ Jan 9 2008, 21:16) *
Повторюсь, интересуют программные, а не аппаратные решения.

Самым лучшим вариантом Вашей задачи является программно-аппаратное решение (это мое мнение).
GetSmart
Попытался применить код, который я тут запостил и нашёл в нём некоторые недоработки. Во-первых прибавлять к PC (R15) нужно смещение, умноженное на 4. Остальное в коде:
Код
LDR  R9,[R8] ; R8 = T0CR (инициализируется во время LowLevelInit)
AND  R9,R9,#15; с небольшим риском эту команду можно убрать
ADD  R15,R15,R9,LSL #2
NOP
...
NOP

PS. Странно, что никто другой не заметил.
GetSmart
Попробывал применить этот алгоритм в своём генераторе сигнала. Оказалось от джиттера полностью не избавиться. Оказалось что либо команда чтения таймера (или периферии вообще) занимает разное число тактов, либо перенос инфы из таймера происходит в разные моменты исполнения данной команды. Вобщем очень забавные аномалии происходят в данном коде. Хотя с виду он выглядит идеально. В AVR он точно работал бы идеально.

Точнее так: с момента чтения таймера и до вывода чего-то в порт (после NOPов) всё равно проходит разное количество тактов. Очень может быть что и команда вывода в порт исполняется за разное кол-во тактов.

Процессор LPC2132 revA. Процедура прерывания во флэше. Частота 59 МГц, MAM = 2 (full).
Alex03
А если MAM=0?
GetSmart
Частота 5 МГц, PLL off, MAM=0, MAMTIM=1. Прерывание во флэш.
Ощущение такое, что джиттера не стало. Однако как это проверить "на коленке" я пока не знаю. Цифрового осциллографа под руками нет. У меня сейчас программа формирует меандр от ШИМа на одном пине, а в прерывании я программно устанавливаю другой пин как бы синфазно с шимом и на лету могу корректировать сдвиг программного пина с точностью до такта. На древнем осциллографе с полосой 5 МГц (!) еле-еле видна иголка, которую можно уширять, можно убрать или перенести вниз изменяя сдвиг буквально на такт. Вроде всё красиво.
GetSmart
Частота 59 МГц, прерывание в RAMe, MAM = 2, MAMTIM = 3.

Джиттера нет. Скорость изменения уровня на пине не менее двух тактов.

Видмио ограничена мощностью выходного каскада и собственной ёмкостью пина.
ГУ-49А
Позвольте изложить некоторые результаты по открытой мной теме.

1. Удалось реализовать метод интерлива кода вывода в порт (16 бит) и фоновой задачи.
2. При простых командах (см. ниже) фоновой задачи удалось достичь частоты дискретизации 14 МГц (!), без джиттера (LPC2103, 70 MHz), т.е. за 5 тактов.
3. При большинстве сложных команд частота дискретизации = 7 МГц, без джиттера (за 10 тактов).

Теперь подробности.

1) Реализация подобного вывода в порт реальна, если код выполняется из RAM (ramfunc). В этом случае тайминг команд становится предсказуем, т.е.:
- простая арифметика и логика = 1 такт;
- запись в память и в FIO-порт = 2 такта;
- чтение из памяти, команды перехода = 3 такта, и т.д.
Как видим, это соответствует заявленным таймингам для данного семейства ARM7TDMI-S. К сожалению, вызов более сложной функциональности LPC2103 требует существенно большего количества тактов, например:
- запись байта в регистр SSP = 7 тактов;
- чтение слова данных из ADC (независимо от режима) = целых 8 тактов.
(Кстати, скажем, отправку по SSP/SPI в некоторых случаях можно выполнить программно - работает не хуже, и тайминг команд остаётся 3 такта вместо 7-ми.)
2) Использование встроенного таймера позволяет с точностью до такта определять тайминги участков кода и проверять результат.
3) Общее правило подсчёта максимальной частоты дискретизации при таком методе:
Fs = Fclk/(Cycles_max + 2),
где Cycles_max - длительность самой медленной команды, в тактах (но не менее 3 тактов).
4) Инструментарий писать не стал, по 2-м причинам:
4.1) В общем случае ldr/str может обращаться к каким угодно спец.регистрам периферии, и тайминг будет существенно меняться. Отследить это можно только при эмуляции прошивки, что неоправданно усложняет задачу.
4.2) Для простых же команд тайминги вычисляются слишком просто, нопы вставлять для выравнивания (align) тактов тоже просто - польза от такой утилиты в моём случае неочевидна.

Если интересно, как можно за 5 тактов и в порт выводить 16 бит из памяти, и фоновую программу по 3 такта выполнять, с контролем оверрана DDS-буфера - расскажу и покажу.
GetSmart
Конечно интересно! Думаю даже не мне одному smile.gif
alexander55
Цитата(GetSmart @ Feb 19 2008, 07:31) *
Конечно интересно! Думаю даже не мне одному smile.gif

Это интересно.
ГУ-49А
Цитата(GetSmart @ Feb 19 2008, 06:31) *
Конечно интересно! Думаю даже не мне одному smile.gif


Можно сделать, например, так:

Код
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 5-cycles no-jitter 16-bit buffer output to FIO on LPC2103
;; registers used: r7-r12
    
FIOBUF_5_START MACRO
    ldr r10, [r11], #4
    ldr r7, [r11], #4
    strh r10, [r12]
    mov r10, r10, lsr #16  
    mov r8, r7, lsr #16
    nop
    ENDM

FIOBUF_5 MACRO
    strh r10, [r12]
    ldr r9, [r11], #4
    strh r7, [r12]
    ldr r7, [r11], #4
    strh r8, [r12]
    mov r10, r9, lsr #16
    mov r8, r7, lsr #16
    nop
    strh r9, [r12]
    ENDM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Usage example:

    ldr r11, =buf      ; 32-bit aligned!
    ldr r12, =FIOPINL

    FIOBUF_5_START
    FIOBUF_5
   ; any 3-cycle user command
loop:
    FIOBUF_5
   ; any 3-cycle user command
    FIOBUF_5
   ; any 3-cycle user command
    FIOBUF_5
    
   ; !buffer overrun check!
    FIOBUF_CHECK
    
    FIOBUF_5
    b loop


Как видим, между каждыми вызовами FIOBUF_5 мы вызываем команды фоновой задачи, сгруппированные ровно по 3 такта. Например:
Код
    FIOBUF_5
    ldr r0, [r4, r2]   ; [3] cycles
    FIOBUF_5
    add r2, r2, #2     ; [1]
    cmp r2, #20        ; [1]
    nop                ; [1]
    FIOBUF_5
    blo skip_label     ; [1] or [3] (if jumped)
    nop                ; [1]
    nop                ; [1]
    FIOBUF_5
    strh r0, [r6]      ; [2]
    mov r0, #1         ; [1]
skip_label:
    FIOBUF_5
    ldr r0, [r4, r1]   ; [3]
    FIOBUF_5
   ; ...

При этом, условные переходы легко учесть, если сразу в начале метки перехода будет стоять макрос вывода в порт, а после команды - 2 нопа для компенсации тактов. Однотактные команды с условиями (напр. subeq) тоже можно спокойно использовать. А вот многотактные условные команды придётся преобразовать в отдельную проверку и условный переход.

Легко заметить, что при каждом вызове FIOBUF_5 читаются сразу 2 32-битных слова из буфера. Поэтому нужно в код фоновой программы вставлять дополнительные проверки для реализации цикличности буфера. Размер буфера может быть произвольный! В конце буфера нужно сделать "запас" из, скажем, 16 семплов (скопировать начальные семплы и выровнять до 32-бит), поскольку мы не можем делать частые проверки - и потом из текущего указателя вычитаем размер буфера при превышении:
Код
FIOBUF_CHECK MACRO  ;;; \Warning: Destroys r0,r1 and flags!
    ldr r0, =last_buffer_ptr
    FIOBUF_5
    ldr r1, =buffer_length_in_bytes
    FIOBUF_5
    cmp r11, r0
    subhi r11, r11, r1 ; buffer ptr
    nop
    ENDM


Как я уже писал, если у вас есть команды больше 3-х тактов, то подобный метод необходимо соответственно адаптировать. Чем за большее кол-во тактов идёт вывод, тем красивее становится код. wink.gif

Напоминаю, что код надо размещать в секции для копирования в RAM, тогда тайминги команд будут точными. Также не учитываются фоновые прерывания.
Метод хоть и не универсальный, но позволяет сделать полноценный синтез периодических сигналов с точным контролем частоты и формы. Например, табличный синус по этому методу на осциллографе выглядит весьма симпатично.
Спасибо за внимание!
GetSmart
Очень даже неплохо получилось. Можно сказать что это экстремальное программирование! Я такие вещи очень люблю. Жаль только, что весь проект при таком подходе нужно будет писать на асме. И по прерываниям будущий автор будет скучать smile.gif
GetSmart
Цитата(ГУ-49А)
Что ж, давайте ещё раз напомню, в чём заключалась моя задача. Нужно было делать одновременно 2 вещи:
1) Выводить в порт FIO 16 бит информации;
2) Выводить в SPI по 8 бит данных.
Причём, выводить в порт FIO как можно быстрее (это приоритетная задача). Дык вот, если использовать аппаратный SSP, то пока мы кидаем в него данные, проходит 7 тактов, следовательно, мы не можем слать в порт FIO чаще, чем за 7+2 такта (2 идёт на сам вывод в порт), т.е. за 9 тактов. Если же мы используем программный SPI, то шлём эти биты вручную, да, менее эффективно, чем по SSP, но зато у нас появляется возможность выводить в FIO каждые 3+2 такта, т.е. за 5 тактов. Жертвуем при этом скоростью вывода в SPI (примерно 60 тактов за бит, что в моём случае полностью устраивает). Но куда важнее мне было получить максимальную скорость вывода в FIO, что я и сделал. Именно поэтому я и писал, что существуют те редкие случаи, когда целесообразнее использовать программный SPI, и в этом нет ничего страшного.
Можно вопрос? Почему скорость программного SPI была 60 тактов на бит? Вроде бы в Вашей программе можно параллельно с 16 бит основными данными по двум программным линиям выдавать SCK и MOSI со скоростью 10 тактов на бит. То есть "плевать" в GPIO порт сразу 18 бит данных.
ГУ-49А
Цитата(GetSmart @ Feb 24 2008, 17:04) *
Можно вопрос? Почему скорость программного SPI была 60 тактов на бит? Вроде бы в Вашей программе можно параллельно с 16 бит основными данными по двум программным линиям выдавать SCK и MOSI со скоростью 10 тактов на бит. То есть "плевать" в GPIO порт сразу 18 бит данных.

Увы, нельзя совместить основные данные с данными SPI, т.к. у них разный период повторения. Данные SPI выводятся по 8 бит, а затем делается большая пауза (порядка 1 мс, поэтому мне скорость вывода через SPI не так важна), а вывод основных данных продолжается всё время (и период повторения может быть любым, с точностью до сэмпла). Кроме того, при наиболее экстремальном способе основного вывода каждые 5 тактов можно успевать доставать только 16 бит за раз, а ведь MOSI1 лежит в верхнем полуслове. Поэтому и получалось у меня 60 бит:
Код
ldr   ; загружаем данные очередного бита для нашего SPI
FIOBUF_5; 17 тактов
str    ; пишем данные этого бита в MOSI1
nop
FIOBUF_5; 17 тактов
str    ; устанавливаем CLK1 в 1 - наш бит идёт во внешний девайс (позже MOSI1).
nop
FIOBUF_5; 17 тактов  -- здесь CLK1 автоматически сбрасывается в 0

Конечно, если подумать, то можно сделать и поменьше - за счёт битовых сдвигов и т.п. Хм, возможно...

Если же можно было основной вывод делать, например, за 8 тактов, то тут уже другая картина. FIOBUF_8 будет занимать всего 5 тактов вместо 17-ти, и скорость вывода в SPI будет уже 24 такта/бит. Ну а если за 9 тактов, то можно спокойно уже через аппаратный SSP выводить.

P.S. Пардон, что немного сумбурно излагаю...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.