Неожиданные проблемы при стыковке STM32F103 и Atmega 88 по SPI

Не очень давно я выкладывал в личный блог заметку:о распиновке кнопок в ручке джойстика Saitek X52
После, я подключил эту ручку к своей электронике, и в процессе этого выявились некоторые траблы, связанные с общением по SPI в режиме Master — Slave контроллеров разных производителей и архитектур.
Мастер: STM32F103C8T6, тактовая 72 МГц, выполняет роль контроллера джойстика и подключен к USB интерфейсу.
Ведомый: Atmega 88, тактовая 12МГц, выполняет роль контроллера кнопок в ручке джоя.
Тактовая частота SPI — порядка 280 КГц. Транзакция — 4 байта данных.
Мастер шлет запросы и отправляет результат в USB
Ведомый — берет значения кнопок и отправляет мастеру в ответ на запрос.
Первое, что неприятно удивило со стороны Атмеги — некорректная работа в режиме слэйва SPI. Заключается оно в следующем: АРМ периодически шлет данные через ДМА непрерывными посылками, длиной 4 байта. Линия CS инициируется на каждую посылку отдельно, т.е. с синхронизацией данных вопросов нет. Атмега успешно принимает первый байт, выдает прерывание, но при попытке записать следующий байт в регистр данных SPI выдает ошибку (флаг занятости интерфейса) и отправляет вместо нужных данных последний принятый байт.
Пробовал самые разные настройки интерфейса на обоих контроллерах, порядок бит, фронты захвата и т.д. Никакого положительного результата не получил.
Решил все тем, что стал слать не посылки из 4х байт, а по 1 байту с задержкой между ними — и все заработало… Но осадочек остался…
Второе, что пока так и не победил, непонятный шум и сдвиг младшего бита данных при приеме на STM32. То есть, допустим, от ведомого к мастеру идет посылка 0xFE,0xFF,0x7F,0xFF. Мастер же принимает ее как 0xFE, 0xFE,0x7F,0xFF. При этом младший бит первого байта убегает в младший бит второго байта (при нажатии кнопки 0 джойстика изменяется принимаемый бит второго байта, хотя лог. анализатор, подключенный к линии показывает корректные значения байт идущих по интерфейсу. Данные искажаются даже в процессе ручной работы с SPI в режиме отладки.
Может конечно где-то непропай, т.к. некоторое время (после отключения лог. анализатора) все работало как надо, но потом опять упало. Победить не смог. Пока в командировке, проверить еще раз и перепаять не могу…
UPD:
В процессе обсуждения в комментах выяснилась интересная деталь по поводу Меги. через неделю буду дома, проверю и отпишу здесь о результатах…
UPD2:
Времени на эксперименты было немного, побыстрому поменял обработчик прерывания, поставив сначала запись в SPDR а потом уже чтение. При непрерывной посылке в 4 байта все равно к мастеру передается корректно только первыц байт. остальную часть передачи занимают принятые байты…
UPD3:
Наконец дошли руки до переделки слэйва, как я и задумывал, на STM32.
В качестве камня поставил STM32F103T4U. Можно конечно было сунуть чего-нибудь помельче, в смысле мощности, но я решил не плодить у себя номенклатуру.
Повесил слэйв на DMA. Завелось все с полпинка. В теле программы кроме инициализации переферии и буфера передачи ничего больше небыло, а связь сразу зработала стабильно.
- 0
- 23 января 2014, 15:17
- Ultrin
1. DMA шлет данные непрерывно, а принимающему контроллеру (без DMA) нужно больше времен (между отправкой байт) на их прем и обработку. Так что решение с задержкам логичное.
2. В отладке «ручной» такие вещи могут работать некорректно, лучше использовать для отладки в этом случает терминал.
2. В отладке «ручной» такие вещи могут работать некорректно, лучше использовать для отладки в этом случает терминал.
1. По таймингам все вполне укладывается в рамки. т.е. обработка прерывания и запись в регистр данных происходит в соответствующей фазе SCK, когда и должен меняться следующий бит у приемника-передатчика.
2. эти данные 100% бьют с тем, что я получаю как в «прогоне» между брекпоинтами так и с тем, что мне по факту приходит в USB при работе контроллера в штатном режиме, а не в режиме отладки.
2. эти данные 100% бьют с тем, что я получаю как в «прогоне» между брекпоинтами так и с тем, что мне по факту приходит в USB при работе контроллера в штатном режиме, а не в режиме отладки.
Ну запись в регистр я не сомневаюсь что идет без проблем, там аппаратная обработка, а вот дальше успевает ли обработать данные МК (считать байт до прихода следующего)…
считать он все успевает. я для контроля даже светодиодом мигал и смотрел все это на риголе. он не записывает новые данные в регистр, считая, что интерфейс занят. Стоит разбить непрерывную посылку на отдельные байты -и все работает. В этом и нестыковка, потому как нигде в спецификации spi не сказано, что между байтами надо делать паузы или дергать ногой cs.
Между 4-байтовыми посылками CS переходит в «1»? Если так, то у STM SPI неправильный — CS это сигнал сброса шины.
CS — вообще-то сигнал выбора слэйва и сброс шины тут не при делах. Да он переходит в 1 между посылками, но т.к. stm в режиме мастера -управление этой линии ручное, и к физическому интерфейсу самого камня имеет опосредованное отношение.
CS — вообще-то сигнал выбора слэйва и сброс шины тут не при делах.Это понятно. Но spi в slave-е сбрасывается-то…
не сбрасывается, а переводит вход в z-состояние. к тому же между посылками как раз все нормально. проблема слэйва в приеме непрерывной серии. а у мастера глюк с младшим битом. частота для самого интерфейса мизерная.
во первых, не сбрасывается она у меги, во вторых, какое это имеет отношение к приему непреиывной серии? между посылками все отрабатывается нормально.
и я о том же. что может быть проще, чем запретить прерывание, считать байт в буфер, закинуть новый байт в регистр данных и разрешив прерывание выйти из обработчика? только не работает зараза. ставит флаг W и все.
текст не привожу, потому как он уже изменен, да и в данный момент я вообще с планшета пишу…
текст не привожу, потому как он уже изменен, да и в данный момент я вообще с планшета пишу…
что может быть проще, чем запретить прерывание, считать байт в буфер, закинуть новый байт в регистр данных и разрешив прерывание выйти из обработчикаЭто вы про программу для AVR? Тогда как-то неправильно.
Зачем запрещать прерывание? При вызове обработчика они и так запретятся. Первое прерывание от SPI будет по переходу SS в «0» — читать там ещё нечего. А байт на передачу можно положить и тогда когда SS=1.
сначала надо читать, потом писать…Никогда не задумывался об этом моменте…
Посмотрел datasheet:
When configured as a Slave, the SPI interface will remain sleeping with MISO tri-stated as long as the SS pin is driven high. In this state, software may update the contents of the SPI Data Register, SPDR, but the data will not be shifted out by incoming clock pulses on the SCK pin until the SS pin is driven low. As one byte has been completely shifted, the end of transmission flag, SPIF is set. If the SPI interrupt enable bit, SPIE, in the SPCR Register is set, an interrupt is requested. The Slave may continue to place new data to be sent into SPDR before reading the incoming data.Получается всё наоборот — сначала пишем, потом читаем.
P.S. Пост никак нельзя исправить на этом сайте что ли? Зря…
Обработчик такой примерно должен быть(если чего не перепутал на ночь глядя):
enum SpiSlaveState_t {
SpiWaitFallingEdge=0,
SpiRxTx=1
};
volatile SpiSlaveState_t SpiSlaveState=SpiWaitFallingEdge;
volatile uint8_t SpiTxBuffer[4], SpiRxBuffer[4], SpiByteCounter;
volatile bool SpiPacketComplete;
OS_INTERRUPT void SPI_STC_vect()
{
switch (SpiSlaveState) {
case SpiWaitFallingEdge:
SPDR=SpiTxBuffer[0];
SpiByteCounter=0;
SpiSlaveState=SpiRxTx;
break;
case SpiRxTx:
{
uint8_t spi_byte_counter=SpiByteCounter;
SPDR=SpiTxBuffer[spi_byte_counter+1];
SpiRxBuffer[spi_byte_counter++]=SPDR;
if (spi_byte_counter==4) {
spi_byte_counter=0;
// 4 байта приняли, 5 - записали на передачу
// но 5-й дропнется когда SS в 1 перейдёт
SpiSlaveState=SpiWaitFallingEdge;
SpiPacketComplete=true;
}
SpiByteCounter=spi_byte_counter;
}
break;
}
}
Хотя switch не нужен — if-а достаточно…
Жаль нет прерывания по переходу SS в «1»… Тоже бы но помешало…
Жаль нет прерывания по переходу SS в «1»… Тоже бы но помешало…
переход cs можно контролировать вне прерывания хоть вручную, хоть через внешнее прерывание по фронту. в обработчике это не актуально, потому что пауза между посылками достаточно большая (в 10ки раз больше длины посылки, в моем случае.
начальный кейс в моем случае тоже не нужен, потому как я разрешаю spi только когда cs=0 и левых прерываний у меня нет. остается только увеличить буфер на 1, чтоб глюков небыло и поменять местами 2 команды работы с SPDR.
кстати, а что такое OS_INTERRUPT? мне такое раньше не поподалось…
начальный кейс в моем случае тоже не нужен, потому как я разрешаю spi только когда cs=0 и левых прерываний у меня нет. остается только увеличить буфер на 1, чтоб глюков небыло и поменять местами 2 команды работы с SPDR.
кстати, а что такое OS_INTERRUPT? мне такое раньше не поподалось…
в обработчике это не актуально, потому что пауза между посылками достаточно большая (в 10ки раз больше длины посылки, в моем случае.В обработчике это не получится сделать — он на фронт не реагирует, только на спад. Может его можно на прерывание PCINT2_vect повесить — не знаю не проверял… Не запараллеливать же ещё один пин на отключение.включение spi…
начальный кейс в моем случае тоже не нужен, потому как я разрешаю spi только когда cs=0В принципе case 0 не обязателен — если первый передаваемый байт заранее известен, то его можно до обмена в SPDR записать или если пауза между спадом SS и началом передачи байта достаточная чтобы байт записать на передачу.
остается только увеличить буфер на 1, чтоб глюков небылоМожно не увеличивать (если вы про буфер передачи) — данные же не переписываются, а только читаются. А вот буфер приёма контролировать надо — мало ли сколько байт мастер решит передать — друг килобайт пропихнёт в шину вместо 4-х байт — тоды будет «ой». А читать можно сколько угодно (хотя тоже возможны варианты — ограничители лучше иметь-таки).
кстати, а что такое OS_INTERRUPT? мне такое раньше не поподалось…В данном случае этот макрос к делу отношения не имеет — просто открыл первый попавшийся проект в Eclipse. Проект из примеров scmrtos. А макрос такой:
#ifdef __INTR_ATTRS /* default ISR attributes from avr/interrupt.h */
# define OS_INTERRUPT extern "C" __attribute__((__signal__,__INTR_ATTRS))
#else
# define OS_INTERRUPT extern "C" __attribute__((__signal__))
#endif
Так и не получилось наладить обмен?
Какой код тестировался последний раз?
Лучше приведите код master-а и slave…
Какой код тестировался последний раз?
Лучше приведите код master-а и slave…
Слэйв.
В мастере ничего особенного, указать буфер для ДМА и дернуть триггер начала передачи.
Если посылка состоит из непрерывной серии в 4 байта — то нормального обмена нет.
Если посылка состоит из раздельных 4хбайт — то обмен работает нормально.
К сожалению не было времени ковырять все детально, и появится оно еще не скоро. Уже решил заменить мегу на 36-ногий STM, как придет посылка. Тогда и ДМА должен работать как надо и мастеру не придется тупить и ожидать реакции от слэйва…
SIGNAL(SIG_SPI)
{
cli();
SPDR = buf_out[num];
num++;
buf_in[num] = SPDR;
num &= 0x03;
sei();
return;
};
В мастере ничего особенного, указать буфер для ДМА и дернуть триггер начала передачи.
Если посылка состоит из непрерывной серии в 4 байта — то нормального обмена нет.
Если посылка состоит из раздельных 4хбайт — то обмен работает нормально.
К сожалению не было времени ковырять все детально, и появится оно еще не скоро. Уже решил заменить мегу на 36-ногий STM, как придет посылка. Тогда и ДМА должен работать как надо и мастеру не придется тупить и ожидать реакции от слэйва…
Уже решил заменить мегу на 36-ногий STM, как придет посылка. Тогда и ДМА должен работать как надоДумаете STM — панацея? Может быть и заработает сразу как надо… Не забудьте рассказать…
Если посылка состоит из непрерывной серии в 4О как…
байта — то нормального обмена нет.
Если посылка состоит из раздельных 4хбайт — то обмен работает нормально.
Так может дело вовсе не slave, а в master-е?
У вас SPI-мастер на какой размер фрейма настроен? И ДМА как данные персылает? Байтами? Или словами?
Тут вопросов может быть больше чем к mega… :)
с ДМА и с SPI мастера как раз вопросов нет. потому как на логическом анализаторе шина читается отлично. Вопрос именно в слэйве. Данный алгоритм передачи непрерывных посылок прекрасно работает, например, с W5100, у которой обмен идет как раз по 4-ре байта на команду, причем на гораздо большей скорости.
Комментарии (57)
RSS свернуть / развернуть