Перейти к содержанию
    

stm32f407 SPI обнаружил косяк

С другой стороны, имеющийся у меня негативный опыт неопровержимо доказывает, что с таким подходом могут быть проблемы.

Несомненно...

Приведённый код избыточен и работоспособен для низких скоростей...

На полной скорости (половинной) трансфер станет "дырявым" при 8 bit, где поведение BSY уже отличается... придётся всё пересматривать и т.д. ...

Изменено пользователем HHIMERA

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

С другой стороны, имеющийся у меня негативный опыт неопровержимо доказывает, что с таким подходом могут быть проблемы.

Надо, все-таки, добавить ремарку. Может ваш негативный опыт обусловлен кривизной даташита? Помните про количество тактов 15 или 16?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может ваш негативный опыт обусловлен кривизной даташита? Помните про количество тактов 15 или 16?
Вряд ли. Во-первых, я всё время передавал 16 бит. И во-вторых, как только я стал удерживать чипселект до окончания клоков, проблема ушла.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Докладываю.

Посылаю в длинный последовательный регистр (или в ЦАП) последовательность из нескольких байтов. Читать ничего не читаю (не нужно, нечего). Так вот в таком случае определять завершение передачи способом

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, что гарантирует, что началась пересылка последнего байта, и приемопередатчик занят.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И еще.

Для переключения полярности синхроимпульсов при работе то на ЦАП, то на регистр, нужно запрещать SPI. Мелочь, об этом и в datasheet написано, но сразу я так не сделал.

  SPI3->CR1 &= ~SPI_CR1_SPE;        // запретить SPI
  SPI3->CR1 |= SPI_CR1_CPOL;        // данные стабильные на срезе \_
  SPI3->CR1 |= SPI_CR1_SPE;        // разрешить SPI

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Как правильно написать функцию отправки 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);
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подниму тут тему.

Странно, что обсуждают только STM32 SPI MASTER.

Хотя, судя по моим мучениям, проблемы с SPI SLAVE тоже имеется.

 

Работаю с STM32F0. Частота SPI максимальная (PCLK/2, PCLK = HCLK)

Нарвался на 1 косяк описанный в ERRATA (BUSY=1 навсегда) и еще штуки 4 не описанных никак:

 

1. При CPOL=1 CPHA=1 SPI просто не рабочий - SO на последнем бите меняет свое состояние в момент, когда происходит фиксакция состояния

post-10882-1529651955_thumb.png

 

2. При CPOL=1 CPHA=0 все нормально, но вот MASTER непонятно когда фиксирует бит, очень похоже, что все равно на последнем фронте (как при CPHA=1)? так как последний бит может прийти неверным!

post-10882-1529652403_thumb.png

 

3. При работе в BIDIMODE=1 при переходе с приема на передачу (у MASTER) должно пройти некоторое время, большее чем длительность 2-х байтов!.

Иначе при переключении в BIDIOE = 0 передача первого содержимого в FIFO теряется. Т.е. передача начинается, но первая запись (2 байта, если писали в 16 разрядный DR) на выходе неверная! Ну, или как минимум первый бит

  static char txbuf[] = "0123456789ABCDEFG";

post-10882-1529653661_thumb.png

      DEBUG_PIN_HIGH();
      SPILINK->CR1 = SPI_MASTER_START_TX_CR1;
      SPILINK->DR = 0xAAAA;

post-10882-1529654766_thumb.png

 

4. Вообще, первые биты при переходе с приема на передачу могут закосячится ВСЕГДА! Даже если подождать ООчень долго.

Тут, как видно, закосячились аж ДВА бита:

post-10882-1529654766_thumb.png post-10882-1529656573_thumb.png

 

5. SPE для SLAVE похоже по боку. При наличии SS даже при сброшенном SPE он все равно начинает прием данных! :wacko:

 

6. Что за "художества" возникают e MASTER на ноге CLK сразу после SPE=1?

При CPOL=1 CPHA=0 post-10882-1529658891_thumb.png

При CPOL=0 CPHA=0 post-10882-1529659014_thumb.png

последний бит крупным планом post-10882-1529659126_thumb.png

 

Обсчем, не очень понимаю, как с таким работать.... По мне так он в STM нерабочий наравне с I2C

 

PS. Задержка на вход в прерывание у F0 очень своеобразная... Такое ощущение, что где-то стоит делитель на 4: реакция на изменение значение COMPARE у таймера только каждые 4 значение (тактирование от PCLK = HCLK).

На примере:

Могу попасть либо сюда post-10882-1529664293_thumb.png, либо сюда post-10882-1529664584_thumb.png

но никак не попасть между ними!

Длительность входа я не измерял. но от прерывание таймера явно раза в 3-4 превышает заявленные 16 циклов

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Уважаемые коллеги, прочел материалы по данной теме 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 ();

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 12.10.2018 в 11:23, Vladimir_T сказал:

Но главное, что пока идет прием одного байта, он умудряется несколько раз вызвать прерывание по опустошению буфера TX

Такое поведение я бы понял при наличии буферов FIFO на кристалле (в частности, для STM32F745 / F3xx / F030 такое поведение нормально. Данные загружаются в регистр данных, но попадают в FIFO буфер, в очередь передачи (размер - 32 бита), и фактически буфер передачи в этот момент становится пуст.

Но в указаном Вами кристалле, FIFO не описан, значит его и быть не должно.

Если есть логический анализатор - можете попробовать выводить отладку на пины. Сверьтесь с графиком 248 (параграф 25.3.9) по передачи данных со стороны ведомого контроллера. Естественно, внутренние сигналы придётся выводить во вне во время прерываний. И помните, что последние будут отставать от фактического события.

Но можно сильно увеличить делитель частоты для SPI, чтобы минимизировать отставание.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Простите за поднятие старой темы, но я запутался в логие данных 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);
    }
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Функция для обмена данными так

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;
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

28 минут назад, haker_fox сказал:

Но заполнять TXFIFO нужно, ориентируясь на FTLVL ? Пока он не равен 0x03 (FIFO full)? А читать, ориентируясь по FRLVL, пока он не 0x00 (FIFO empty)?

Дефолт-сити - все знаем, но какой нынче дефолт-МК?  :unknw:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 minutes ago, jcxz said:

но какой нынче дефолт-МК? 

Сорри, запарился. STM32F091RB.

1 minute ago, Arlleex said:

откуда в STM32F407 FIFO на SPI

Блин. Ещё и не совсем по теме. Ну ладно)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

@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, либо выключить это прерывание, если данных больше нет (это для передачи).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...