mbzyab 0 27 сентября, 2012 Опубликовано 27 сентября, 2012 · Жалоба Завязка: К отладочной плате olymex была варварски припаяна (так как свободно-торчащих пинов на плате нет) самодельная светодиодная матрица 2х2 (схему матрицы могу приложить, если нужно). Какое-то время мой код для работы с этой матрицей работал без нареканий, а потом в один прекрасный момент я решил поднять частоту процессора (заюзав PLL) до 100 МГц. Светодиоды гореть перестали. Это явление было не очень стабильным, на разных частотах код вел себя по-разному и не всегда одинаково. На четвертый день разборок бажный код редуцировался до вот такого фрагмента, который должен выключать все светодиоды. В таком виде код работает.: LPC_GPIO1->FIOPIN |= (1<<14 | 1<<15); LPC_GPIO1->FIOPIN &= ~(1<<16 | 1<<17); А вот в таком - нет. PLL в обоих случаях отключен, проц тактируется от внутренней цепочки на 4Мгц. uint32_t tempPin; uint32_t mask; uint32_t temp; tempPin = LPC_GPIO1->FIOPIN; mask = (1<<14 | 1<<15); tempPin |= mask; LPC_GPIO1->FIOPIN = tempPin; tempPin = LPC_GPIO1->FIOPIN; mask = (1<<16 | 1<<17); tempPin &= ~mask; При пошаговой отладке работают оба варианта. Если вот этот момент LPC_GPIO1->FIOPIN = tempPin; tempPin = LPC_GPIO1->FIOPIN; не делать (или вставить задержку в пару тактов между этими строчками), то все работает. Т.е., насколько я понимаю, значение в регистре толи меняется, толи считывается недостаточно быстро. Вопрос: прав ли я или проблема может быть в чем-то другом? Если я прав, где в даташите это описано или как это вообще гуглить, что делать? <_> Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RabidRabbit 0 27 сентября, 2012 Опубликовано 27 сентября, 2012 · Жалоба А почему бы не заюзать FIOSET/FIOCLR, удобней же (на мой взгляд)? И 3 последние строчки tempPin = LPC_GPIO1->FIOPIN; mask = (1<<16 | 1<<17); tempPin &= ~mask; вроде вообще не нужны? что они делают? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mbzyab 0 27 сентября, 2012 Опубликовано 27 сентября, 2012 · Жалоба А почему бы не заюзать FIOSET/FIOCLR, удобней же (на мой взгляд)? И 3 последние строчки tempPin = LPC_GPIO1->FIOPIN; mask = (1<<16 | 1<<17); tempPin &= ~mask; вроде вообще не нужны? что они делают? Таки да, FIOSET/FIOCLR заюзать можно и работают они корректно. Просто я к портам по указателям обращаюсь и так пришлось бы хранить два указателя, вместо одного. К тому же FIOPIN должен работать точно так же Три последние строчки - это запись нулей в 16 и 17 бит, то же самое, что FIOPIN &= ~(1<<16 | 1<<17); чтобы выключить столбцы матрицы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mbr 0 28 сентября, 2012 Опубликовано 28 сентября, 2012 · Жалоба FIOPIN &= ~(1<<16 | 1<<17); Это три инструкции: считать, наложить маску, записать. Set/Clr - одна. У кортекса можно вообще обращаться через битовую память. А причина такого поведения может быть, если между read/set другой процесс также ставит свои биты в порте. Попробуйте прерывания запретить. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alx2 0 28 сентября, 2012 Опубликовано 28 сентября, 2012 (изменено) · Жалоба Три последние строчки - это запись нулей в 16 и 17 бит, то же самое, что FIOPIN &= ~(1<<16 | 1<<17); чтобы выключить столбцы матрицы. В первом варианте Вы записываете нули в 16-й и 17-й биты LPC_GPIO1->FIOPIN, а во втором - в 16-й и 17-й биты переменной tempPin. Записи переменной tempPin в LPC_GPIO1->FIOPIN в приведенном Вами фрагменте кода нет (точнее, оно производится до обнуления 16-го и 17-го битов). Таким образом, приведенные Вами фрагменты кода не эквивалентны. По поводу LPC_GPIO1->FIOPIN = tempPin; tempPin = LPC_GPIO1->FIOPIN; Я не знаком именно с LPC, но в некоторых контроллерах такой код, действительно, может прочитать старое (еще не измененное) состояние ног. Если для LPC это справедливо - то в данном фрагменте у Вас race condition, чем и объясняется зависимость результата от частоты. Изменено 28 сентября, 2012 пользователем alx2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RabidRabbit 0 28 сентября, 2012 Опубликовано 28 сентября, 2012 · Жалоба Просто я к портам по указателям обращаюсь и так пришлось бы хранить два указателя, вместо одного. Да зачем два? Адрес FIOnCLR = Адрес FIOnSET + 4 :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mbzyab 0 28 сентября, 2012 Опубликовано 28 сентября, 2012 · Жалоба В первом варианте Вы записываете нули в 16-й и 17-й биты LPC_GPIO1->FIOPIN, а во втором - в 16-й и 17-й биты переменной tempPin. Записи переменной tempPin в LPC_GPIO1->FIOPIN в приведенном Вами фрагменте кода нет (точнее, оно производится до обнуления 16-го и 17-го битов). Таким образом, приведенные Вами фрагменты кода не эквивалентны. Да, пропустил последнюю строчку, где LPC_GPIO1->FIOPIN = tempPin еще раз. Невнимательный стал. По поводу LPC_GPIO1->FIOPIN = tempPin; tempPin = LPC_GPIO1->FIOPIN; Я не знаком именно с LPC, но в некоторых контроллерах такой код, действительно, может прочитать старое (еще не измененное) состояние ног. Если для LPC это справедливо - то в данном фрагменте у Вас race condition, чем и объясняется зависимость результата от частоты. Ну я подозреваю, что так и происходит, только в даташите или юзер-мануале ничего подобного не увидел. Может быть такое поведение и нормально, только вот как я о нем догадаться был должен? А причина такого поведения может быть, если между read/set другой процесс также ставит свои биты в порте. Попробуйте прерывания запретить. Прерывания, разумеется, запрещены. Да зачем два? Адрес FIOnCLR = Адрес FIOnSET + 4 А так получается платформозависимо :) Но, вообще, сейчас мне пришло в голову, что идеально было бы юзать bit-banding. Тогда пришлось бы хранить только один указатель, а сейчас - указатель и маску. Ладно, это потом как-нибудь. -_- Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mbzyab 0 1 октября, 2012 Опубликовано 1 октября, 2012 · Жалоба В целом, вопрос следующий - это вообще нормальное поведение для регистров порта? Или это косяк LPC? Или или или что это вообще. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ReAl 0 1 октября, 2012 Опубликовано 1 октября, 2012 · Жалоба Такое поведение (когда запись на выход может в следующем такте не прочитаться со входа) — так это запросто. Вот из документации на AVR ATmega8 Reading the Pin Value Independent of the setting of Data Direction bit DDxn, the port pin can be read through the PINxn Register Bit. As shown in Figure 22, the PINxn Register bit and the preceding latch constitute a synchronizer. This is needed to avoid metastability if the physical pin changes value near the edge of the internal clock, but it also introduces a delay. ... When reading back a software assigned pin value, a nop instruction must be inserted as indicated in Figure 24. У AVR эта задержка гарантирована и фиксирована, а у LPC могло на низких частотах успевать «аналоговые» задержки (в буферах) до следующего такта выбрать, а на высоких — нет и появилась дополнительная задержка. И NOP не поможет, он растворится между двумя командами обращения к шине периферии. По хорошему, если нужно узнать, что мы вівели в порт (а не что на него пришло снаружи), то читать надо FIOSET (текущее значение регистра выхода), это не будет зависеть ни от задержки выходного буфера от регистра до ножки, ни от задержки входного буфера и подсинхронизации после него, что всё вместе влияет на прохождение сигнала до FIOPIN. А передавать надо один указатель на порт (структуру порта), а там уже port->FIOPIN = port->FIOSET & ~mask; если по какой-то причине не хочется писать port->FIOCLR = mask; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
mbzyab 0 1 октября, 2012 Опубликовано 1 октября, 2012 · Жалоба Такое поведение (когда запись на выход может в следующем такте не прочитаться со входа) — так это запросто. Вот из документации на AVR ATmega8 У AVR эта задержка гарантирована и фиксирована, а у LPC могло на низких частотах успевать «аналоговые» задержки (в буферах) до следующего такта выбрать, а на высоких — нет и появилась дополнительная задержка. И NOP не поможет, он растворится между двумя командами обращения к шине периферии. По хорошему, если нужно узнать, что мы вівели в порт (а не что на него пришло снаружи), то читать надо FIOSET (текущее значение регистра выхода), это не будет зависеть ни от задержки выходного буфера от регистра до ножки, ни от задержки входного буфера и подсинхронизации после него, что всё вместе влияет на прохождение сигнала до FIOPIN. А передавать надо один указатель на порт (структуру порта), а там уже port->FIOPIN = port->FIOSET & ~mask; если по какой-то причине не хочется писать port->FIOCLR = mask; Звучит правдоподобно, но вот отсутствие (или моя неспособность найти) этой информации в даташите меня угнетает. Если уж вставлять задержки, то надо знать, сколько. Шаманить-то не хочется. Структура порта да, как вариант. Проще всего, разумеется, ничего не читать вообще, а только писать в FIOSET и в FIOCLR, не трогая FIOPIN. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Petka 0 2 октября, 2012 Опубликовано 2 октября, 2012 · Жалоба Звучит правдоподобно, но вот отсутствие (или моя неспособность найти) этой информации в даташите меня угнетает. Если уж вставлять задержки, то надо знать, сколько. Шаманить-то не хочется. ..... FIOPIN Fast Port Pin value register using FIOMASK. The current state of digital port pins can be read from this register, regardless of pin direction or alternate function selection (as long as pins are not configured as an input to ADC). The value read is masked by ANDing with inverted FIOMASK. Writing to this register places corresponding values in all bits enabled by zeros in FIOMASK. Important: if an FIOPIN register is read, its bit(s) masked with 1 in the FIOMASK register will be read as 0 regardless of the physical pin state. Последние три слова подтверждают то, что при чтении этого регистра подразумевается именно считывание физического состояния цифрового порта. В независимости от того, настроен порт на вход или на выход. Читаем дальше. Writing to FIOSET/FIOCLR vs. FIOPIN Writing to the FIOSET/FIOCLR registers allow a program to easily change a port’s output pin(s) to both high and low levels at the same time. When FIOSET or FIOCLR are used, only pin/bit(s) written with 1 will be changed, while those written as 0 will remain unaffected. Writing to the FIOPIN register enables instantaneous output of a desired value on the parallel GPIO. Data written to the FIOPIN register will affect all pins configured as outputs on that port: zeroes in the value will produce low level pin outputs and ones in the value will produce high level pin outputs. Запись в этот порт имеет смысл только для пинов настроенных на выход. А теперь самое интересное: Reading the FIOxSET register returns the value of this register, as determined by previous writes to FIOxSET and FIOxCLR (or FIOxPIN as noted above). This value does not reflect the effect of any outside world influence on the I/O pins. Вот этот регистр уже отражает именно внутреннее состояние порта (состояние выходного драйвера) на вывод. Таким образом ваш код должен был иметь вид: FIOPIN = FIOSET ^ MY_MASK; Таким образом не будет никакой "аналоговой" обратной связи по физическому уровню на пине порта. P.S. В начале работы с LPC сам столкнулся с такой фичей. Это побудило просто внимательно перечитать документацию. После прочтения становится всё очевидно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
esaulenka 5 2 октября, 2012 Опубликовано 2 октября, 2012 · Жалоба Проще всего, разумеется, ничего не читать вообще Универсальный совет :-D Самая простая и тупая доработка: // было LPC_GPIO1->FIOPIN |= (1<<14 | 1<<15); LPC_GPIO1->FIOPIN &= ~(1<<16 | 1<<17); // стало int tmp; tmp = LPC_GPIO1->FIOPIN; tmp |= (1<<14 | 1<<15); tmp &= ~(1<<16 | 1<<17); LPC_GPIO1->FIOPIN = tmp; При желании (в ущерб удобочитаемости) можно переписать в одну строку. Ну и поддержу более правильные советы - использовать регистры SET / CLR. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
theBMV 0 3 октября, 2012 Опубликовано 3 октября, 2012 · Жалоба Предлагаю Вам для начала посмотреть, на какой частоте у Вас работает GPIO. Может следует её поднять? Не знаю пределов, но у меня при F ядра 100 МГц и F GPIO 25 МГц всё везде успевает, даже в скоростных шинных обменах, когда PINы SETы и CLRы следуют один за другим в разных вариациях. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
theBMV 0 3 октября, 2012 Опубликовано 3 октября, 2012 (изменено) · Жалоба Предлагаю Вам для начала посмотреть, правильно ли происходит процедура инициализации частот ядра и т.п. Выложите сюда. Если не поможет, предлагаю попробовать отключить акселератор FLASH памяти (FLASHCFG_bit.FLASHTIM) Если и это не поможет, посмотрите настройки компилятора. Какой компилятор? Какие настройки оптимизации? Попробуйте полностью отключить оптимизацию. У меня при включенной оптимизации компилятор выдавал те ещё конструкции! Вот после такого у меня ничего не работало! Изменено 3 октября, 2012 пользователем theBMV Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RabidRabbit 0 3 октября, 2012 Опубликовано 3 октября, 2012 · Жалоба Если не поможет, предлагаю попробовать отключить акселератор FLASH памяти (FLASHCFG_bit.FLASHTIM) Если и это не поможет, посмотрите настройки компилятора. Какой компилятор? Какие настройки оптимизации? Попробуйте полностью отключить оптимизацию. У меня при включенной оптимизации компилятор выдавал те ещё конструкции! Вот после такого у меня ничего не работало! Это из серии вредных советов? ;) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться