HHIMERA 0 15 января, 2013 Опубликовано 15 января, 2013 (изменено) · Жалоба С другой стороны, имеющийся у меня негативный опыт неопровержимо доказывает, что с таким подходом могут быть проблемы. Несомненно... Приведённый код избыточен и работоспособен для низких скоростей... На полной скорости (половинной) трансфер станет "дырявым" при 8 bit, где поведение BSY уже отличается... придётся всё пересматривать и т.д. ... Изменено 15 января, 2013 пользователем HHIMERA Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 15 января, 2013 Опубликовано 15 января, 2013 · Жалоба С другой стороны, имеющийся у меня негативный опыт неопровержимо доказывает, что с таким подходом могут быть проблемы. Надо, все-таки, добавить ремарку. Может ваш негативный опыт обусловлен кривизной даташита? Помните про количество тактов 15 или 16? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 15 января, 2013 Опубликовано 15 января, 2013 · Жалоба Может ваш негативный опыт обусловлен кривизной даташита? Помните про количество тактов 15 или 16?Вряд ли. Во-первых, я всё время передавал 16 бит. И во-вторых, как только я стал удерживать чипселект до окончания клоков, проблема ушла. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 12 марта, 2013 Опубликовано 12 марта, 2013 · Жалоба Докладываю. Посылаю в длинный последовательный регистр (или в ЦАП) последовательность из нескольких байтов. Читать ничего не читаю (не нужно, нечего). Так вот в таком случае определять завершение передачи способом while (!(SPI3->SR & SPI_SR_RXNE)); // ждать конец передачи - так нельзя! не годится! Потому что флаг RXNE установится после первой же пересылки байта. И не сбросится, пока не прочитаешь регистр данных DR. Так как мне не нужно было читать из SPI, то конец передачи обнаруживался "досрочно", при первой же проверке SR & SPI_SR_RXNE. В этом случае правильное решение - проверять BSY. while (!(SPI3->SR & SPI_SR_TXE)); // ждать освобождение буфера передатчика while (SPI3->SR & SPI_SR_BSY); // ждать освобождение приемопередатчика Поскольку пересылка байтов может быть прервана в середине, то BSY тоже может иметь "разрыв". Спасает ожидание освобождения буфера передатчика перед проверкой BSY, что гарантирует, что началась пересылка последнего байта, и приемопередатчик занят. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 13 марта, 2013 Опубликовано 13 марта, 2013 · Жалоба И еще. Для переключения полярности синхроимпульсов при работе то на ЦАП, то на регистр, нужно запрещать SPI. Мелочь, об этом и в datasheet написано, но сразу я так не сделал. SPI3->CR1 &= ~SPI_CR1_SPE; // запретить SPI SPI3->CR1 |= SPI_CR1_CPOL; // данные стабильные на срезе \_ SPI3->CR1 |= SPI_CR1_SPE; // разрешить SPI Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ASDFG123 0 13 марта, 2016 Опубликовано 13 марта, 2016 · Жалоба Как правильно написать функцию отправки 16 бит по SPI ? Нужно в общем так: линию Fsync в ноль, затем отправка 16 бит, и после поднять линию Fsync вверх. void SPI1_WriteWorld(int Data) { // FSYNC в 0 GPIO_WriteBit(GPIOA,GPIO_Pin_4,Bit_RESET); // Отправка данных (16 бит) SPI_I2S_SendData(SPI1, Data); // Жду пока буфер TX будет пустой while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) // FSYNC в 1 GPIO_WriteBit(GPIOA,GPIO_Pin_4,Bit_SET); } так или { // FSYNC в 0 GPIO_WriteBit(GPIOA,GPIO_Pin_4,Bit_RESET); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, Data); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); GPIO_WriteBit(GPIOA,GPIO_Pin_4,Bit_SET); } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alechek 0 22 июня, 2018 Опубликовано 22 июня, 2018 · Жалоба Подниму тут тему. Странно, что обсуждают только STM32 SPI MASTER. Хотя, судя по моим мучениям, проблемы с SPI SLAVE тоже имеется. Работаю с STM32F0. Частота SPI максимальная (PCLK/2, PCLK = HCLK) Нарвался на 1 косяк описанный в ERRATA (BUSY=1 навсегда) и еще штуки 4 не описанных никак: 1. При CPOL=1 CPHA=1 SPI просто не рабочий - SO на последнем бите меняет свое состояние в момент, когда происходит фиксакция состояния 2. При CPOL=1 CPHA=0 все нормально, но вот MASTER непонятно когда фиксирует бит, очень похоже, что все равно на последнем фронте (как при CPHA=1)? так как последний бит может прийти неверным! 3. При работе в BIDIMODE=1 при переходе с приема на передачу (у MASTER) должно пройти некоторое время, большее чем длительность 2-х байтов!. Иначе при переключении в BIDIOE = 0 передача первого содержимого в FIFO теряется. Т.е. передача начинается, но первая запись (2 байта, если писали в 16 разрядный DR) на выходе неверная! Ну, или как минимум первый бит static char txbuf[] = "0123456789ABCDEFG"; DEBUG_PIN_HIGH(); SPILINK->CR1 = SPI_MASTER_START_TX_CR1; SPILINK->DR = 0xAAAA; 4. Вообще, первые биты при переходе с приема на передачу могут закосячится ВСЕГДА! Даже если подождать ООчень долго. Тут, как видно, закосячились аж ДВА бита: 5. SPE для SLAVE похоже по боку. При наличии SS даже при сброшенном SPE он все равно начинает прием данных! :wacko: 6. Что за "художества" возникают e MASTER на ноге CLK сразу после SPE=1? При CPOL=1 CPHA=0 При CPOL=0 CPHA=0 последний бит крупным планом Обсчем, не очень понимаю, как с таким работать.... По мне так он в STM нерабочий наравне с I2C PS. Задержка на вход в прерывание у F0 очень своеобразная... Такое ощущение, что где-то стоит делитель на 4: реакция на изменение значение COMPARE у таймера только каждые 4 значение (тактирование от PCLK = HCLK). На примере: Могу попасть либо сюда , либо сюда но никак не попасть между ними! Длительность входа я не измерял. но от прерывание таймера явно раза в 3-4 превышает заявленные 16 циклов Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Vladimir_T 1 12 октября, 2018 Опубликовано 12 октября, 2018 · Жалоба Уважаемые коллеги, прочел материалы по данной теме SPI в STM32F4xx, но прежде все "неточности" работы SPI обнаружил у себя на MCU STM32F410. Мастером является STM32F405 , а ведомым STM32F410. На обоих процессорам обмен производится через прерывания. У мастера обмен: прием/передача без ошибок. А ведомый ведет себя так: принимает без ошибок, а вот передает - с ошибками! Но главное, что пока идет прием одного байта, он умудряется несколько раз вызвать прерывание по опустошению буфера TX. Код обработчика простой, инициализация также. Может будут какие-либо мысли - поделитесь со мной, пожалуйста. Идея обмена проста. В фоновом режиме получаем последовательность и в также заполняем буфер передатчика: опустел буфер, взвелся флаг TXE в SPI->SR, вырабатывается прерывание и в буфер передатчика заносим очередное значение. Но когда начинается обмен, то флаг TXE появляется чаще RXNE, хотя они должны следовать один после другого. Потому тестовые счетчики Cnt1 и Cnt2. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //Config SPI Protocol SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; // SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // High SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 16 SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); SPI_CalculateCRC(SPI1, DISABLE); SPI_SSOutputCmd(SPI1, DISABLE); Cnt_Tx = Cnt_Rx =0; SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE); SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, ENABLE); /***************************************************************************** SPI1_IRQHandler (); ******************************************************************************/ TControl *pCtrl = &Control; u32 Cnt1 =0, Cnt2=0, Cnt3=0; __irq void SPI1_IRQHandler(void) { if (SPI_I2S_GetITStatus(SPI1, SPI_IT_TXE)) { SPI1->DR = *pOut_Buff++; if (++Cnt_Tx == Cnt_Pack) { Cnt_Tx = 0; Cnt1 ++; // For Test pOut_Buff = (u8*)&SPI_USonic_Out; // Init pointer } } if (SPI_I2S_GetITStatus(SPI1, SPI_IT_RXNE)) { *pIn_Buff++ = SPI1->DR; if (++Cnt_Rx == Cnt_Pack) { Cnt_Rx = 0; Cnt2++; // For Test pCtrl-> Link_SPI = True; // Link is Ready pIn_Buff = (u8*)&SPI_USonic_In; // Init pointer } } } // SPI1_IRQHandler (); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlanDrakes 1 14 октября, 2018 Опубликовано 14 октября, 2018 · Жалоба В 12.10.2018 в 11:23, Vladimir_T сказал: Но главное, что пока идет прием одного байта, он умудряется несколько раз вызвать прерывание по опустошению буфера TX Такое поведение я бы понял при наличии буферов FIFO на кристалле (в частности, для STM32F745 / F3xx / F030 такое поведение нормально. Данные загружаются в регистр данных, но попадают в FIFO буфер, в очередь передачи (размер - 32 бита), и фактически буфер передачи в этот момент становится пуст. Но в указаном Вами кристалле, FIFO не описан, значит его и быть не должно. Если есть логический анализатор - можете попробовать выводить отладку на пины. Сверьтесь с графиком 248 (параграф 25.3.9) по передачи данных со стороны ведомого контроллера. Естественно, внутренние сигналы придётся выводить во вне во время прерываний. И помните, что последние будут отставать от фактического события. Но можно сильно увеличить делитель частоты для SPI, чтобы минимизировать отставание. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 16 мая, 2020 Опубликовано 16 мая, 2020 · Жалоба Простите за поднятие старой темы, но я запутался в логие данных SPIx->SR. Прерывание может возникуть только (рассматриваем именно обмен данными, с ошибками и CRC понятно) по TXE и RXNE. Но заполнять TXFIFO нужно, ориентируясь на FTLVL ? Пока он не равен 0x03 (FIFO full)? А читать, ориентируясь по FRLVL, пока он не 0x00 (FIFO empty)? Я, вроде, не новичёк в МК, но давно с STM32 не работал. Уже пять лет как с LPC вожусь. А тут смена камешка. Я почему вопрос задаю? С чисто передачей проблем нет. Но приём был сделан так: смотрим, если нужно принимать данные (т.е. программе они нужны), то вычитываем SPIx-DR пока установлен флаг RXNE. Часть данных читалась, часть исчезала или сдвигалась на одну позицию. После того, как стал ориентироваться на FTLVL/FRLVL вроде ошибки исчезли. Мне кажется логика работы этой периферии у стмок немного замудрённая. В т.ч. флаг BUSY. Если только он гарантирует, что передача окончилась, то почему бы по нему не сделать прерывание? Ведь не всегда CS дёргается аппаратно, если слэйвов несколько, то удобно ждать прерывание... Обработчик выглядит так // Это счётчики для отладки static auto irqCounter = 0; static auto irqRxCounter = 0; static auto irqTxCounter = 0; static auto irqFinishCounter = 0; void SspBus::irqHandler( Bus bus ) { irqCounter++; BaseType_t xRecTaskWoken = pdFALSE; const auto idx = static_cast<int>(bus); configASSERT(idx < ARRAY_SIZE(m_ctrlData)); auto &ctrl = m_ctrlData[idx]; const auto status = ctrl.pRegs->SR; if (status & ( SPI_SR_OVR | SPI_SR_CRCERR | SPI_SR_MODF )) { // Для мастера нужно только обработать OVR и CRCERR configASSERT(0); } while (!( ctrl.pRegs->SR & SPI_SR_FTLVL ) && ctrl.pMsg->length) { irqTxCounter++; ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) ) = *ctrl.pMsg->txbuffer++; if (--ctrl.pMsg->length == 0) { ctrl.pRegs->CR2 &= ~SPI_CR2_TXEIE; } } while (ctrl.pMsg->rxbuffer && ctrl.pRegs->SR & SPI_SR_FRLVL && ctrl.rxcounter) { irqRxCounter++; *ctrl.pMsg->rxbuffer++ = ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) ); if (--ctrl.rxcounter == 0) { ctrl.pRegs->CR2 &= SPI_CR2_RXNEIE; } } if (ctrl.rxcounter == 0 || ( ctrl.pMsg->length == 0 && ctrl.pMsg->rxbuffer == nullptr )) { irqFinishCounter++; vTaskNotifyGiveFromISR(ctrl.tskHndl, &xRecTaskWoken); portYIELD_FROM_ISR(xRecTaskWoken); } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 16 мая, 2020 Опубликовано 16 мая, 2020 · Жалоба Функция для обмена данными так TRetVal SspBus::txrx( AbstractSSPDriver::MsgType &spiCfg ) { auto &ctrl = m_ctrlData[getBusIdx()]; ctrl.tskHndl = xTaskGetCurrentTaskHandle(); configASSERT(xTaskNotifyStateClear(NULL) == pdFALSE); if (spiCfg.use_dma) { RET_VAL(setupDma()); } else { ctrl.pMsg = &spiCfg; ctrl.rxcounter = spiCfg.length; RET_VAL(disableDma()); NVIC_ClearPendingIRQ(ctrl.irqn); ctrl.pRegs->CR2 &= ~SPI_CR2_RXNEIE; ctrl.pRegs->CR2 |= SPI_CR2_TXEIE | ( ctrl.pMsg->rxbuffer ? SPI_CR2_RXNEIE : 0 ); } uint32_t notify = ulTaskNotifyTake(pdTRUE, MSEC(spiCfg.timeout_ms)); ctrl.pRegs->CR2 &= ~( SPI_CR2_RXNEIE | SPI_CR2_TXEIE ); // Поллинг сознательно! Он очень короткий. В противном случае для определения того, что // байт полностью ушёл по MISO нужно городить логику обработки статусных флагов. while (ctrl.pRegs->SR & SPI_SR_BSY); if (spiCfg.rxbuffer == nullptr) while (ctrl.pRegs->SR & SPI_SR_FRLVL) volatile auto dummy = ( * (__IO uint8_t *)( (uint32_t)&( ctrl.pRegs->DR ) ) ); if (!notify) return rvTIME_OUT; return rvOK; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 235 16 мая, 2020 Опубликовано 16 мая, 2020 · Жалоба 28 минут назад, haker_fox сказал: Но заполнять TXFIFO нужно, ориентируясь на FTLVL ? Пока он не равен 0x03 (FIFO full)? А читать, ориентируясь по FRLVL, пока он не 0x00 (FIFO empty)? Дефолт-сити - все знаем, но какой нынче дефолт-МК? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 176 16 мая, 2020 Опубликовано 16 мая, 2020 · Жалоба Тоже сначала не понял, откуда в STM32F407 FIFO на SPI Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 16 мая, 2020 Опубликовано 16 мая, 2020 · Жалоба 3 minutes ago, jcxz said: но какой нынче дефолт-МК? Сорри, запарился. STM32F091RB. 1 minute ago, Arlleex said: откуда в STM32F407 FIFO на SPI Блин. Ещё и не совсем по теме. Ну ладно) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 176 16 мая, 2020 Опубликовано 16 мая, 2020 · Жалоба @haker_fox, в прерывании, в цикле вычитывания из RXFIFO, полагаю, есть синтаксический промах ctrl.pRegs->CR2 &= SPI_CR2_RXNEIE; Думаю, там должно быть ctrl.pRegs->CR2 &= ~SPI_CR2_RXNEIE; А в целом логика да, такая. TXE взведется, когда TXFIFO будет заполнен наполовину или меньше. RXNE взведется, когда RXFIFO будет заполнен не менее, чем на размер, определяемый битом FRXTH (0 - 16 бит; 1 - 8 бит). А уже в прерывании нужно либо долить в ведро дополнить TXFIFO, либо выключить это прерывание, если данных больше нет (это для передачи). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться