|
быстрый тайминг GPIO для LPC |
|
|
|
Jan 9 2008, 18:16
|
Участник

Группа: Участник
Сообщений: 32
Регистрация: 26-11-07
Пользователь №: 32 699

|
Цитата(alexander55 @ Jan 9 2008, 09:23)  Вопрос к автору. Что Вы хотите сделать ? Варианты : - генератор сигналов произвольной формы - систему регулирования какого-то параметра. В зависимости от ответа на этот вопрос, решение будет различным. Ответ: Вариант 1-й, т.е. генератор сигналов произвольной формы (на фоне выполнения другой менее приоритетной задачи). Повторюсь, интересуют программные, а не аппаратные решения. Цитата(GetSmart @ Jan 8 2008, 21:57)  кол-во NOPов тоже нужно подобрать получше. Вобщем я обрисовал только идею. Возможно этот пример нужно немного доработать. Идею уловил, спасибо.
|
|
|
|
|
Jan 10 2008, 06:23
|
Бывалый
    
Группа: Свой
Сообщений: 1 584
Регистрация: 7-08-07
Пользователь №: 29 615

|
Цитата(ГУ-49А @ Jan 9 2008, 21:16)  Ответ: Вариант 1-й, т.е. генератор сигналов произвольной формы (на фоне выполнения другой менее приоритетной задачи). Понятно. Цитата(ГУ-49А @ Jan 9 2008, 21:16)  Повторюсь, интересуют программные, а не аппаратные решения. Самым лучшим вариантом Вашей задачи является программно-аппаратное решение (это мое мнение).
|
|
|
|
|
Jan 13 2008, 11:41
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

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

Группа: Участник
Сообщений: 32
Регистрация: 26-11-07
Пользователь №: 32 699

|
Позвольте изложить некоторые результаты по открытой мной теме.
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-буфера - расскажу и покажу.
|
|
|
|
|
Feb 19 2008, 09:47
|
Участник

Группа: Участник
Сообщений: 32
Регистрация: 26-11-07
Пользователь №: 32 699

|
Цитата(GetSmart @ Feb 19 2008, 06:31)  Конечно интересно! Думаю даже не мне одному  Можно сделать, например, так: Код ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 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-х тактов, то подобный метод необходимо соответственно адаптировать. Чем за большее кол-во тактов идёт вывод, тем красивее становится код. Напоминаю, что код надо размещать в секции для копирования в RAM, тогда тайминги команд будут точными. Также не учитываются фоновые прерывания. Метод хоть и не универсальный, но позволяет сделать полноценный синтез периодических сигналов с точным контролем частоты и формы. Например, табличный синус по этому методу на осциллографе выглядит весьма симпатично. Спасибо за внимание!
|
|
|
|
|
Feb 24 2008, 15:04
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(ГУ-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 бит данных.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Feb 24 2008, 16:02
|
Участник

Группа: Участник
Сообщений: 32
Регистрация: 26-11-07
Пользователь №: 32 699

|
Цитата(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. Пардон, что немного сумбурно излагаю...
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|