Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Расчёт скорости GPIO
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
bullit
День добрый всем!

Прошу помочь с расчётом.
Есть мк LPC2194. Необходимо проделать следующее
1) считать значение порта Р0 с 0 по 7 (8 линий)
2) с "0" на "1" переключить одну ногу(не из тех 8 линий)
3) снова считать значение (1)
4) значения по байтно записывать в память оперативную
Условия:
- и на всё это отводится не более 900 нс.
- цикл (1-4) повторяется раз в 2 мкс
- VPBDIV = 0 , т.е. деления частоты для переферии нет = частоте тактирования проца. (хотя не уверен)
- Кварц 10МГц, с ФАПЧ 60 МГц.

Можно ли успеть?
Думаю асм вставку придётся делать... + код в оперативу кидать...

К сожелению генератора под рукой нет... чтоб проверить.

А смысл в следующем: у ацп есть функция смена местами на выводах старшую и младшую байт, тем самым используя 8 линий сосчитать 16 бит. Для этого нужно "менять" уровень на определённой ноге.

Уж не знаю с какой скорость читать будет... но обычный "дрогатель":
Код
IO0SET = 0x40000000;
IO0CLR = 0x40000000;
Дал мне максимум 2,5 МГц при расположение кода в оперативе... Это максимум? (правда сейчас стоит проц 2294 и кварц 14,7456 МГц и ФАПЧ до 56 МГц должно разгонять)
GetSmart
Для IOPIN, IOSET, IOCLR запись в порт 7 тактов, чтение порта 8 тактов. В последних ревизиях (/01) работая через быстрые регистры периферии запись будет 2 такта, чтение 3 такта.

Вне зависимости откуда выполняется прога - из рамы или из флэша.
bullit
МК именно 01. (только сейчас на олимексовской плате тренеруюсь H2294(старый, не 01))
А что за быстрые регистры? Они: IOPIN, IOSET, IOCLR?

А есть воопче смысл в рам кидать? и для каких операций?

Цитата
Вне зависимости откуда выполняется прога - из рамы или из флэша.
А в мануале прочёл:
Код
For the best performances, compile this code in the ARM mode and execute
from the on-chip SRAM memory.
и код:
ldr r0,=0xe01fc1a0 /*register address--enable fast port*/
mov r1,#0x1
str r1,[r0] /*enable fast port0*/
ldr r1,=0xffffffff
ldr r0,=0x3fffc000 /*direction of fast port0*/
str r1,[r0]
ldr r0,=0xe0028018 /*direction of slow port 1*/
str r1,[r0]
ldr r0,=0x3fffc018 /*FIO0SET -- fast port0 register*/
ldr r1,=0x3fffc01c /*FIO0CLR0 -- fast port0 register*/
ldr r2,=0x00001000 /*select fast port 0.12 for toggle*/
ldr r3,=0xE0028014 /*IO1SET -- slow port1 register*/
ldr r4,=0xE002801C /*IO1CLR -- slow port1 register*/
ldr r5,=0x00100000 /*select slow port 1.20 for toggle*/
/*Generate 2 pulses on the fast port*/
str r2,[r0]
str r2,[r1]
str r2,[r0]
str r2,[r1]
/*Generate 2 pulses on the slow port*/
str r5,[r3]
str r5,[r4]
str r5,[r3]
str r5,[r4]
loop: b loop
Получается есть эфект?

Т.е. регистры FIO0MASK FIO0PIN
GetSmart
Цитата(bullit @ Dec 22 2008, 17:23) *
...
Условия:
- и на всё это отводится не более 900 нс.
- цикл (1-4) повторяется раз в 2 мкс
- VPBDIV = 0 , т.е. деления частоты для переферии нет = частоте тактирования проца. (хотя не уверен)
- Кварц 10МГц, с ФАПЧ 60 МГц.

Можно ли успеть?
Думаю асм вставку придётся делать... + код в оперативу кидать...
Успеть можно. На асме не напрягаясь даже на старых чипах. Без использования рамы.

Цитата
Получается есть эфект?
Чуть быстрее предвыборка после перехода. Возможно 1-2 такта. Ещё быстрее выполняются команды типа:
Код
ldr r4,=0xE002801C
А линейный код из рамы и флэша исполняется одинаково быстро.

Кроме того, неясно, кроме чтения/записи в порт будет какая-то основная прога выполняться? Если да, то придётся делать через FIQ, но при этом появится джиттер.
bullit
Цитата
Кроме того, неясно, кроме чтения/записи в порт будет какая-то основная прога выполняться? Если да, то придётся делать через FIQ, но при этом появится джиттер.

Да будет конечно...
Необходимо с 2 АЦП в течении порядка 2 мс, с переодичностью 2 мкс, опрашивать байты (8 линий). Кстати забыл совсем, что необходимо сначала с одного сосчитать 2 бита и потом с другого 2 бита. Между чтениями битов переключать "старший/младший"(byteswap помоему) и на всё это 900 нс. Т.е. на каждую ацп по 450 нс.
По программе: запускаем конвертирование (1 на ногу КОНВ) и ждём когда бизи в ноль упадёт... Как лучше всего "ждать бизи"? по прерыванию или по опросу? В это время не на что не отвлекаюсь... все прерывания стороние не интересуют.

А джитер будет больше 1 - 2 тактов?
aaarrr
Цитата(bullit @ Dec 22 2008, 21:05) *
Как лучше всего "ждать бизи"? по прерыванию или по опросу? В это время не на что не отвлекаюсь... все прерывания стороние не интересуют.

Однозначно по опросу.

Цитата(bullit @ Dec 22 2008, 21:05) *
А джитер будет больше 1 - 2 тактов?

Да, и значительно больше.
GetSmart
Цитата(bullit @ Dec 23 2008, 00:05) *
Да будет конечно...
Необходимо с 2 АЦП в течении порядка 2 мс, с переодичностью 2 мкс, опрашивать байты (8 линий). Кстати забыл совсем, что необходимо сначала с одного сосчитать 2 бита и потом с другого 2 бита. Между чтениями битов переключать "старший/младший"(byteswap помоему) и на всё это 900 нс. Т.е. на каждую ацп по 450 нс.
По программе: запускаем конвертирование (1 на ногу КОНВ) и ждём когда бизи в ноль упадёт... Как лучше всего "ждать бизи"? по прерыванию или по опросу? В это время не на что не отвлекаюсь... все прерывания стороние не интересуют.?
А эти две миллисекунды нельзя полностью "посвятить" работе с АЦП? Было бы неплохо и очень надёжно. Ждать бизи лучше по тактам, если известно сколько оно длится. А если он длится больше 400 нс, то становится долго его ждать по опросу, и можно в лимит не уложиться.

Цитата
А джитер будет больше 1 - 2 тактов?
Намного больше. В среднем вход в FIQ будет задержан на 4 такта. Минимум 0 (или 1 - хз), максимум около 20, но недавно исследуя этот вопрос выше 17 я не смог получить, хотя использовал в коде самые длинные команды LDM и STM.
bullit
Цитата
А эти две миллисекунды нельзя полностью "посвятить" работе с АЦП? Было бы неплохо и очень надёжно.

Именно 2 мс буду отданы на АЦП...
Цитата
Ждать бизи лучше по тактам, если известно сколько оно длится. А если он длится больше 400 нс, то становится долго его ждать по опросу, и можно в лимит не уложиться

Т.к. конвертирование на двух АЦП я запускаю одновременно, то ждать буду на каком АЦП появится раньше, с того и начну опрос. Известно, что от запуска конвертирования до спада бизи в ноль как максимум 1100нс. Получается на опрос с двух АЦП 900нс максимум.
Думаю всё таки зациклить опрос бизи... делать паралельно нечего... жди приход да и всё...
Главное успеть за 900нс опросить оба АЦП.
А вот по поводу запуска конвертирования: лучше на таймер 2 мкс отсчёт сделать? потому как более надёжного способа я не вижу...
GetSmart
Цитата(bullit @ Dec 23 2008, 10:59) *
А вот по поводу запуска конвертирования: лучше на таймер 2 мкс отсчёт сделать? потому как более надёжного способа я не вижу...
Можно использовать функцию MATx.x на некоторых пинах проца. Она может инвертироваться строго по таймеру. Можно на ней сделать меандр 2 мкс. А в основной процедуре крутиться и ждать "бизи". Потом считывать значения. MATx.x не имеет джиттера и, как я понял, будет запускать оба АЦП одновременно.
aaarrr
Цитата(bullit @ Dec 23 2008, 07:59) *
Т.к. конвертирование на двух АЦП я запускаю одновременно, то ждать буду на каком АЦП появится раньше, с того и начну опрос.

Не советую: на этой нехитрой логике Вы потеряете времени больше, чем теоретически можно отыграть на разности времени преобразования. Смотрите оба BUSY вместе.
bullit
Чесно говоря не понял что Вы предлогаете.
Я считывать буду с обоих входов (бизи), а уж кто первый, тот и первый в опрос уйдёт.
Бизи активен нулём.
1) считали два бита
2) умножили на маску. если равен нуля то определяем какой из них, опятьже маской.
или:
1) опросили одну ногу, равна ли 0, то вперёд
2) опросили вторую, равна ли 0, то вперёд.

Ну вроде как 2 вариант быстрее...
GetSmart
Цитата(bullit @ Dec 23 2008, 13:30) *
...
Ну вроде как 2 вариант быстрее...

Быстрее будет первый вариант когда оба бита бизи висят на одном порте. Т.о. считали IOPIN, умножили на маску и... туды или сюды. Если оба в еденице, то декрементируйте счётчик опроса (для защиты от зависона). Если ещё не конец, то снова на чтение IOPIN. Если конец, то выход, АЦП зависло.
aaarrr
Цитата(bullit @ Dec 23 2008, 10:30) *
Ну вроде как 2 вариант быстрее...

Оба варианта медленные. Лучше так: считали 2 бита, если оба равны нулю, то начинаем считывать АЦП. Считываем всегда в одном порядке. Иначе потеряете вагон времени.
bullit
У меня обе АЦПшки висят на одном порту. АЦП имеет 3 состояние, управляемое CS и RD.
Заводить бизи с двух АЦП на одну ногу - значит не иметь информации с какого именно АЦП пришло. Использовать отдельные порты МК для каждого АЦП и накладно (трудно развести плату), да и портов нет. Потому как возможно часть свободных портов может уйти на другие нужды.

Наверое рисунок схемы будет Вам полезен. А то всплывают каждый раз новые подробности... файл бмп в архиве (с 2 Мб до 57 кб smile.gif ).


Цитата
Оба варианта медленные. Лучше так: считали 2 бита, если оба равны нулю, то начинаем считывать АЦП. Считываем всегда в одном порядке. Иначе потеряете вагон времени.
Получается что ждём пока оба не закончут конвертирование. В принципе вариант хорош тем, что шумы при работе МК не пройдут на другой АЦП, который в это время будет опрашиваться... А так только по одной линии - что в принципе снизит уровень "помех наводок". НО тогда надо иметь хороший запас во времени.

И еще хотел сросить про организацию сохранения результатов в памяти. Можно ли силами си/си++ организовать массив или что-то подобное.
И еще: как вы можите видеть: старший бит АЦП на "месте" младшего бита мк. Как можно "перевернуть" данные? Хотя это можно сделать и позднее на ПК. Сделано так с точки зрения разводки платы, нет перекрёсных цепей.
aaarrr
Цитата(bullit @ Dec 23 2008, 12:46) *
НО тогда надо иметь хороший запас во времени.

Вы же их запускаете одновременно, какой запас по времени? За время между сбросами BUSY разных АЦП Вы ровным счетом ничего не успеете сделать.
GetSmart
Цитата(bullit @ Dec 23 2008, 15:46) *
Заводить бизи с двух АЦП на одну ногу - значит не иметь информации с какого именно АЦП пришло. Использовать отдельные порты МК для каждого АЦП и накладно (трудно развести плату), да и портов нет. Потому как возможно часть свободных портов может уйти на другие нужды.

Речь шла о том, чтобы весить бизи на два разных пина одного порта. Порт - это IOPIN0 или IOPIN1, или IOPINx. Порт содержит до 32 пинов, то есть бит. Хотя есть вариации у каждого проца, иногда всего пол порта наружу выходит, а иногда с дырками, но не суть. Два пина есть на любом порте. И их оба можно прочитать за одно чтение порта, сэкономив МНОГО времени.

ЗЫ. P0.14 зря задействовали. Он управляет бутлоадером. Его надо бы подтянуть к плюсу через 10к.
bullit
Я подумал на один пин smile.gif
Думаю поступлю именно как Вы указали в 12 посте. Опрос обоих ног "на ноль". А потом покомбенирую: или ждать пока оба в ноль, или пока один из двух в ноль не уйдет. В принципе зависеть будет от реакции.
Даже если я бутлоадер не юзаю?
GetSmart
Цитата(bullit @ Dec 23 2008, 17:05) *
Даже если я бутлоадер не юзаю?

Если бутлоадер не используется, то P0.14 можно использовать, но только если есть гарантия, что на этом входе во время сброса не будет нуля. Например пин можно использовать для вывода данных из проца. Но подтягивать его к 3.3в обязательно.
bullit
Хотел бы поднять тему, с целью получения совета, на вопрос в посте выше №14:
Цитата
И еще хотел сросить про организацию сохранения результатов в памяти. Можно ли силами си/си++ организовать массив или что-то подобное.

Програмирую а IARe.
Т.е. мне нужно в оперативу скинуть 2 бита с двух ацп, и таких измерений, т.е. 2 бита всего 1000. Но можно и меньше до 900. Памяти ОЗУ у меня в принципе нечем не занято. Я принимаю команду по SPI, даю одну команду на UART и принимаю данные с АЦП, а затем уже их по SPI сливаю. И потом опять всё повтаряю.
Вот как програмно реализовать массив?
aaarrr
Цитата(bullit @ Feb 3 2009, 18:56) *
Вот как програмно реализовать массив?

Если у Вас проблемы с производительностью, то логичнее вообще не паковать, пока память позволяет.
Или работайте с АЦП пакетами, т.е. делаем линейно 4 измерения - укладываем в 1 байт.
bullit
1000 прощений!!!
не 2 бита, а 2 байта!!!
2 байта * 2 канала * 1000 = 4000 байт. Памяти в LPC2194 /01 16 кило SRAM, так что "места много"!
aaarrr
Цитата(bullit @ Feb 3 2009, 21:15) *
не 2 бита, а 2 байта!!!

Мне показалось, что у Вас 2 старших бита надо куда-то складывать. Бывает.

А уж с двумя байтами вообще никаких проблем не вижу. Сделайте массив short'ов, чтобы работало побыстрее.
bullit
К сожелению я с С/С++ пока на Вы. В дельфях еще более менее. Поэтому в виде кода можно увидеть?
И еще не понятно насчет быстрых GPIO, т.е. вместо обычных регистров GPIO использовать FGPIO регистры?
Типа d: array[0..1000;0..1]: short; ? smile.gif
aaarrr
Цитата(bullit @ Feb 3 2009, 21:35) *
Типа d: array[0..1000;0..1]: short; ? smile.gif

Типа:
Код
short d[1000];

Хотя не знаю, чем это может помочь - надо изучать язык.

Цитата(bullit @ Feb 3 2009, 21:35) *
И еще не понятно насчет быстрых GPIO, т.е. вместо обычных регистров GPIO использовать FGPIO регистры?

Да.
koyodza
Вставлю свои 5 копеек:
я так понял, что эти два АЦП 8-разрядные? Тогда выбросьте LPC и возьмите STM32, там это всё на внутренних АЦП реализуемо. Выйдет проще, дешевле, ещё и в потреблении выиграть можно
bullit
Цитата
я так понял, что эти два АЦП 8-разрядные?

Нет! Это 14 битная АЦП, с паралельным выходом, с возможностью передачи по 8 линиям все 14 бит. Т.е. сначала младший потом старший.

А вариант с STM32 я уже смотрел. И Силабсы 06х серии тоже. НО остановился на этом варианте.
bullit
Делал тут я испытания...
и вот что у меня получилось:
CODE
// Настройка выводов для работы Первого АЦП
#define CONV_START1 FIO0CLR_bit.P0_29=1; // Старт Конвертирования АЦП
#define CONV_STOP1 FIO0SET_bit.P0_29=1; // Вернуть состояние входа "CONV" АЦП в нормальное состояние
#define START_READ1 FIO0CLR_bit.P0_30=1; // Разрешение на чтение ответа от АЦП
#define STOP_READ1 FIO0SET_bit.P0_30=1; // Запрет чтения ответа от АЦП
#define READ_LOWBYTE1 FIO1CLR_bit.P1_24=1; // Чтение МЛАДШЕГО байта
#define READ_HIGHBYTE1 FIO1SET_bit.P1_24=1; // Чтение старшего байта
#define BUSY_ADC1 FIO0PIN_bit.P0_16 // Чтение стостояния АЦП

// Настройка выводов для работы Второго АЦП
#define CONV_START2 FIO0CLR_bit.P0_10=1; // Старт Конвертирования АЦП
#define CONV_STOP2 FIO0SET_bit.P0_10=1; // Вернуть состояние входа "CONV" АЦП в нормальное состояние
#define START_READ2 FIO0CLR_bit.P0_11=1; // Разрешение на чтение ответа от АЦП
#define STOP_READ2 FIO0SET_bit.P0_11=1; // Запрет чтения ответа от АЦП
#define READ_LOWBYTE2 FIO0CLR_bit.P0_13=1; // Чтение МЛАДШЕГО байта
#define READ_HIGHBYTE2 FIO0SET_bit.P0_13=1; // Чтение старшего байта
#define BUSY_ADC2 FIO0PIN_bit.P0_12 // Чтение стостояния АЦП

// Настройка выводов для работы 2-ух АЦП
#define CONV_START FIO0CLR=0x20000400; // Старт Конвертирования АЦП
#define CONV_STOP FIO0SET=0x20000400; // Вернуть состояние входа "CONV" АЦП в нормальное состояние


// Усиление
#define GAIN1 FIO0SET = 0x1A000000; // Усиление 1(0) dB
#define GAIN2 FIO0SET = 0x18000000; FIO0CLR = 0x02000000; // Усиление 3 dB
#define GAIN3 FIO0SET = 0x12000000; FIO0CLR = 0x08000000; // Усиление 6 dB
#define GAIN4 FIO0SET = 0x10000000; FIO0CLR = 0x0A000000; // Усиление 9 dB
#define GAIN5 FIO0SET = 0x0A000000; FIO0CLR = 0x10000000; // Усиление 12 dB
#define GAIN6 FIO0SET = 0x08000000; FIO0CLR = 0x12000000; // Усиление 15 dB
#define GAIN7 FIO0SET = 0x02000000; FIO0CLR = 0x18000000; // Усиление 18 dB
#define GAIN8 FIO0CLR = 0x1A000000; // Усиление 21 dB


// Variable

u8 dataMB;
u8 dataSB;
u16 data[1000][2];


int main (void)
{
// System initialization, this will map the exception vectors.
LPC2294SystemInit(APBDIVISOR);
// initialization PLL
LPC2194PLLInit();
// Power Control
LPC2194PowerControl();
// First disable interrupts.
__disable_interrupt();
// Setup interrupt controller.
LPC2294InitVIC();
// Enable interrupts.
// __enable_interrupt();
// Set up peripheral registers.
LPC2294InitPIO();

// Установить усиление 5
GAIN5

CONV_STOP1
STOP_READ1
CONV_STOP2
STOP_READ2

for (;;){
// Loop forever.

for (u16 i = 0; i<1000; i++) {
CONV_START
CONV_STOP

while(BUSY_ADC1 == 1){}; //Ждем Busy

START_READ1
dataMB = FIO0PIN0;
READ_HIGHBYTE1
dataSB = FIO0PIN0;
data[i][0] = dataMB;
data[i][0] = data[i][0] | (dataSB << 8);

STOP_READ1
// Чтение со второго канала
START_READ2
dataMB = FIO0PIN0;
READ_HIGHBYTE2
dataSB = FIO0PIN0;
data[i][1] = dataMB;
data[i][1] = data[i][1] | (dataSB << 8);

// Конец
READ_LOWBYTE2
READ_LOWBYTE1
STOP_READ2

}

}
}// End Main

Так при максимальной оптимизации (в сторону скорости) время между стартами конвертирования 2,5 мкс. а мне надо 2 мкс. Время конвертирования 1,1 мкс. Т.е. на все остальные операции не более 0,9 мкс.
К сожелению с асмом тока знаком, но не программировал. Так бы на асме быстрее получилось бы. Или нет?
Или может из генерированного файла взять посмотреть?
Как можно ускорить? Может буфер сделать не двойным, а четверным? тогда можно будет избавиться от data[i][1] = data[i][1] | (dataSB << 8);
GetSmart
Цитата(bullit @ Feb 20 2009, 15:07) *
Как можно ускорить? Может буфер сделать не двойным, а четверным? тогда можно будет избавиться от data[i][1] = data[i][1] | (dataSB << 8);

Вы походу не практиковались в написании оптимизированных прог даже на си. Замечания по проге:
1. CONV_START и CONV_STOP сделаны правильно, а все остальные обращения к FIO неправильно, т.к. при побитовом обращение к ним происходит ненужное чтение. Регистры FIO0SET и FIO0CLR аппаратно сделаны для ускорения работы с портами. В них лучше всего только писать и не читать (ну кроме регистра SET, там отдельная песня). Так что записав в регистр 0х00010000 будет сброшен (или установлен) 16-ый бит в порте, а другие биты не изменятся.
2. Цикл переделайте на обращение к массиву через указатель, с увеличивающимся адресом, а не через индекс по i. Лучше сделать dataMB и dataSB локальными, т.к. неизвестно с какой скоростью будет работать компилятор со статикой. Локальные переменные должны быть размером 32 бита для ускорения работы. Например так:
Код
u16 *ptr = (u16 *)&data[0];

u32  mb, sb;

for (u16 i = 0; i<1000; i++) {
CONV_START
CONV_STOP

// перенёс сюда для ускорения
READ_LOWBYTE2
READ_LOWBYTE1

while(BUSY_ADC1 == 1){};

START_READ1
mb = FIO0PIN0;
READ_HIGHBYTE1
sb = FIO0PIN0;
*ptr++ = (mb & 0xff) | (sb << 8);
STOP_READ1

START_READ2
mb = FIO0PIN0;
READ_HIGHBYTE2
sb = FIO0PIN0;
*ptr++ = (mb & 0xff) | (sb << 8);
STOP_READ2
}

Если это не поможет, то придётся делать на асме.
bullit
Спасибо большое! Не думал что так много ошибок, + перенос переключение реад старший младший байт - как это я его не догадался перенести...
Да, оптимизацией кода я действительно никогда не занимался. Имею опыт работы только с Мегами от АVR. Оптимизация никогда не требовалась...

Во вторник буду проверять!

А где можно посмотреть в сторону оптимизации кода? т.е. какие операции выполняются с разной скоростью, ну и все подобное.... Потому как прямо такого не встречал!

PS C наступающим всех, с 23 февралем!
GetSmart
Цитата(bullit @ Feb 21 2009, 10:39) *
А где можно посмотреть в сторону оптимизации кода?

В листинге smile.gif
А длительность инструкций процессора ARM7 в файле arm7tdmi.pdf. Должен лежать где-нибудь в сети.

Думаю можно ещё ускорить перенеся CONV_START в конец, сразу после чтения обоих каналов
CODE

CONV_START
CONV_STOP

for (u16 i = 0; i<1000; i++) {
READ_LOWBYTE2
READ_LOWBYTE1

while(BUSY_ADC1 == 1){};

START_READ1
mb = FIO0PIN0;
READ_HIGHBYTE1
sb = FIO0PIN0;
*ptr++ = (mb & 0xff) | (sb << 8);
STOP_READ1

START_READ2
mb = FIO0PIN0;
READ_HIGHBYTE2
sb = FIO0PIN0;
*ptr++ = (mb & 0xff) | (sb << 8);
STOP_READ2
CONV_START
CONV_STOP
}
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.