Как стать автором
Обновить

Комментарии 44

Какой знакомый агрегат ILI9341.

А что касается костыляторства типа ввода задержек через delay(X); — сам еще подумал, когда портировал одну из популярных либ под вышеозначенный дисплей с ардуины на чистый Си под AVR, нафига тут это? Задумался еще. Но выручил товарищ, который на SPI собаку съел без хлеба. А порт писать пришлось из-за отсутствия времени на курение даташита на дисплей… но не про это сейчас.

Я к чему все это? ИМХО, подобные решения, которые описываются в статье, лезут именно из таких вот либ. Поскольку свое время довольно много перекопал инфы по экрану на контроллере ili9341 — везде натыкался на один и тот же код инициализации, а порой и работы. Зачастую даже с родными комментами на каком-то иероглефическом диалекте. При чем это не зависело не от производителя контроллера, ни от его семейства или разрядности (с небольшими правками кода конечно же). Везде одно и тоже!

Так что тут нечему удивляться. Проще скопировать и допилить, чем курить маны и даташиты для понимания как правильно. Да и сам порой грешен, что уж тут =(.
это понятно, у меня у самого примерно такая ситуация была…
проблемы стали возникать когда я стал менять скорость SPI — вся работа с дисплеями стала рушиться! и вот тогда я понял что работаю с дисплеем не правильно…

сейчас у меня все модули работы с дисплеями работают как минимум от 1 мгц до максимума… — это как раз самый лучший показатель что все написано верно…
Это еще что. Я видел какой-то феерический код для ili9341, где DMA запускали для передачи 4(!!!) байт.
Хотя честно признаюсь, писать фреймбуффер на MCU затруднительно, если оперативки на кристалле меньше, чем 150 килобайт(то есть почти всегда). И приходится писать какой-то хитрозакрученный код, который рисует экран по несколько строк. Работает шустро, но писать сложнее(хотя я просто повесил семафор на HAL_SPI_TxCpltCallback)
Держите восклицательный знак, который можно поставить в заголовок — !
What Unicode sorcery is this?!
Только администрация все равно его уберет.
Почему? У меня есть несколько постов с ним, не убрали.
Видимо как часто бывает с модерацией — прокатит/не прокатит.
Немного позанудствую:
1) Вы везде пишете STM32, но упоминание конкретной линейки я не нашёл. Это стоит добавить, так как в разных линейках может встречаться (и встречается) разная реализация ip-ядер одних и тех же интерфейсов.
Всё дальнейшее справедливо для линейки STM32L1**
2) Вы везде упоминаете про быстродействие. Тогда уже стоит упомянуть, что контроллер SPI может может пережёвывать данные по 16 бит за раз. (смотрите бит SPI_CR1_DFF)
К тому же вместо бессмысленного ожидания лучше использовать прерывания или DMA.
PS: недавно как раз подключал дисплей к STM32L151 по SPI. Код писал на прерываниях — всё заработало без проблем с первого раза. По-моему в reference manual всё достаточно хорошо про работу SPI расписано.
Вот так:


посмотрел бегло по даташитам
STM32F1, STM32F2, STM32F4 — имеют одинаковый по функциональности SPI
в STM32F3 — по все видимости вообще организован FIFO 3x32!!!

про 16 бит у меня написано, да и не ставилась цель рассказывать про битность… это узкоцелевая статья — про работу флагов интерфейса SPI

DMA и прерывания вы не сможете удобно и эффективно использовать при передаче «солянки» из данных и команд…

Что за дисплей подключали?
DMA и прерывания вы не сможете удобно и эффективно использовать при передаче «солянки» из данных и команд…

На самом деле — можно. Во всяком случае, в вариантах с заливкой/передачей областей данных. Да, придётся команды передавать минуя DMA, либо посылками по 1-2 байта. Это медленно, согласен. Зато после этого при заполнении области можно заранее подготовить эту область (или указать один нулевой байт) и «выстрелить» DMA.
я об этом и написал :-)
одну две команды все равно вручную посылать нужно…
Опять таки DMA это хорошо, но все таки нужно понимать как происходит работа изнутри…
Как происходит работа модуля — понятно. В документации черным по-английски это описано.
Как по мне — так интереснее как раз научиться работать в связке в DMA контроллером. Например, недавно откопал особенность документации DMA в линейке F1, где сказано:
DMA channel x number of data register (DMA_CNDTRx)

NDT[15:0]: Number of data to transfer
Number of data to be transferred (0 up to 65535). This register can only be written when the channel is disabled. Once the channel is enabled, this register is read-only, indicating the remaining bytes to be transmitted. This register decrements after each DMA transfer.

Так вот, регистр содержит не количество оставшихся БАЙТ, а количество оставшихся транзакций. Я долго пытался понять, почему контроллер случайным образом улетает в HardFault на ровном месте. А затем оказалось, что DMA портил стэк, находящийся за буферами, с которыми производились DMA-операции. Но было весело.
гм… ну да, DMA оперирует числом транзакций, одновременно в настройках указывается сколько байт (1/2/4) передается за раз…
Логично, да только в описании — байты. =]
Вот и были весёлые старты.
Ну вы в ST, надеюсь, отписались? ) Чтоб в следующей версии даташита исправили…
Что за дисплей подключали?

LS013B7DH06 — трансфлективный, цветной, низкопотребляющий. Похож на тот, что в новых часах pebble time стоит, только от sharp-а.
про 16 бит у меня написано, да и не ставилась цель рассказывать про битность

Просто если организовывать обмен на прерываниях, то можно сократить количество прерываний аж в два раза. Если просто в цикле ожидать, то да, смысла нет.
DMA и прерывания вы не сможете удобно и эффективно использовать при передаче «солянки» из данных и команд

Не вижу никаких проблем. Сначала пишем команду с данными в буфер, потом пересылаем буфер с помощью DMA/прерываний. Собственно, я так и сделал.
если ваш дисплей нуждается в указании вида данных (команда/данные) по линии DC — то все равно перед отправкой по DMA данных вам нужно будет ждать флага BSY=1 (после отправки команды), и только потом устанавливать DC=1
Линии DC нет — всё управление по SPI. Сначала всегда передаётся команда, потом, если надо, данные.
ООО!!! тогда у вас классный дисплей!!! работать с ним одно удовольствие! :-)
жаль мелковат…

Скажем «Нет!» двойной буферизации в эпоху тотальной ардуинизации :)

Вообще-то в этом и смысл двойной буферизации — чтобы у МП-системы (в случае SoC это соседи по коммунальному кристаллу) было время на спокойную загрузку следующего байта не за длительность последнего бита, а за целый байт вперед. То есть вменяемому инженеру достаточно было просто прочитать, что в таком-то устройстве ВВ она поддерживается, и у него бы даже рука не поднялась переключать что-то там по ^BUSY — это как на автомобиле по сигналу о нейтральной передаче включить задний ход :)

Тот же факт, что в инете полно объездов на кривой козе воркэраундов с задержками, означает, что обезъяна с гранатой — явление массовое, а ведь STM32 по насыщенности периферией — что машинный зал EC ЭВМ
Обезьна с гранатой, это, к сожалению, не просто массовое явление — это являение повсеместное. Вплоть до того, что сам смотришь на код, написанный тобой же лет так пять назад, когды ты только осваивал какую-нибудь очередную железяку, и тихо сползаешь на пол офигивая от одного вопроса: «как, как это чудо работает? почему? оно не может работать! не должно!»…

Вопрос в том — как остановить расползание подобных сумасшедших поделий… статьи подобные обсуждаемой — это хорошо, но нужно что-то делать более системное… вопрос только «что»…
вот и решил на хабре написать…
все таки ресурс достаточно хорошо индексируется поисковиками, может быть кто то набредет до статьи раньше чем напишет не правильный код…
тем более что неправильно написанный код еще и нервов портит — потому что вроде работает, но что нить изменишь и уже не работает!..
И правильно — хабр, как явление — сам по себе хороший префильтр, если бы еще не форк на geektimes, болезненный для эмбед систем, как разлом печатной платы варварами — то было бы совсем хорошо. А так приходится материал собирать по кускам — тут STM32, там STM32 :) — только среди «желтых страниц» про планы Илона Маска уже (

А если серьезно — организаторам всего хабраинкубатора хорошо бы задуматься о том, что непродуманный раскол — как непродуманная политика государства — думающий народ погорюет-погорюет — и потихоньку потянется на другие ресурсы — и никакой риторикой их обратно уже не заманишь. Интересно, есть ли статистика просмотров/комментариев/голосования сходных по тематике материалов до раскола и после?
меня больше напрягает на хабре невозможность писать комментарии спустя какое то время если изначально не участвовал в обсуждении… вот это я точно понять не могу… :-(
да и невозможность комметирования другими пользователями… получается что туториалы на хабре — это бесполезное занятие, так как спросить смогут далеко не все — сделали бы для них отдельную ветку обсуждения тогда что ли…
А почему не использовать готовые библиотеки (HAL, а именно — HAL_SPI_Transmit) от производителя? Там, полагаю, такие моменты учтены. Или просто заглянуть внутрь реализации и выдрать оттуда нужный кусок с проверками флагов состояний.
интересный у вас способ программировать :-)
а почему бы не разобраться как делать правильно? вы уверены что библиотека написана правильно? — я например, после того как сделал минимальную выборку по интернету — уже не уверен!!!
почитайте хабр — статьи об обнаруженных ошибках в стандартных библиотеках возникают постоянно… разница между мною и вами — в том что я смогу понять как должно быть написано, а вы нет…

и если поступать так как вы предлагаете то будут получаться системы которые работают и глючат одновременно… :-)
вы обо мне ничего не знаете, пожалуйста, придержите ваше мнение обо мне при себе. У меня простой вопрос, а о способе программировать я ни слова не писал.

Библиотеки создаются для того, чтобы многократно переиспользовать их, в том числе и другими программистами. То, что я использую HAL совсем не означает, что ни разу не имплементил передачу вручную. Более того, делал и bit banging и плисы задел немного.

Меня привлекают общедоступные популярные библиотеки от производителя, которые обновляются регулярно. Они экономят время. При этом, я не отбрасываю теорию и low-level работу, когда это необходимо.
значит написанное мною не для вас…
я писал для тех кто хочет разобраться сам, чтобы потом создавать эффективный код, возможно и те библиотеки которые вы потом собираетесь использовать…

фантастикой не увлекаетесь? у Айзека Азимова был помоему рассказ про профориентацию людей в технократическом обществе будущего… там все люди селекционировались по умениям, потом им давали пленки чтобы освоить то или иное оборудование, потом на основе полученного образования их направляли на те или иные планеты где это оборудование использовалось…

но были люди которые не подпадали ни под одну из программ..- потому что они обладали интеллектом чтобы создавать эти самые пленки для обучения других…

Мы с вами просто в разных группах :-) вы любите использовать готовое, я люблю создавать с нуля…

Меня привлекают общедоступные популярные библиотеки от производителя, которые обновляются регулярно. Они экономят время. При этом, я не отбрасываю теорию и low-level работу, когда это необходимо.

по тому что вы написали делается абсолютно противоположный вывод…

за минус спасибо!
минус — исключительно за переход на личности, который присутствует даже в этом вашем комментарии. Так делать не стоит.
И для справки — SPI_I2S_SendData это функция библиотеки StdPeriph, предшественника HAL. Считается legacy и не рекомендуется к использованию в новых проектах.
я знаю… но я вообще маргинал — люблю ассемблер, а в нем — нет правил!
я знаю… но по материалам данной статьи — вы рассказали как правильно проверять состояние флагов используя язык С + CMSIS + StdPeriph. Ровно так, как это сделано в новых версиях той же библиотеки.
причем тут библиотеки ?

если вы не будете использовать готовый драйвер дисплея — то вы наступите на эти грабли даже с библиотекой StdPariph и HAL !!

Я же написал: Весь дьявол в линии DC… вы можете абсолютно правильно послать байт (слово) в SPI_DR, но не проверив перед сменой DC флаг BSY — вы порушите обмен с дисплеем… — вот о чем статья!!!

и ни одна библиотека Вам это не подскажет сделать!!! — потому что это аппаратные особенности конкретных дисплеев…
причем выше уже привели ссылку на дисплей у которого нет линии DC и все команды отправляются в перемешку с данными просто по SPI… — там проблемы не будет, и достаточно будет использовать HAL или StdPeriph чтобы все работало быстро…
но дисплеи на PCD8544, ILI9341, ST7735 — вы не запустите без правильного управления линией DC!!!

p.s. не считайте опять что я перехожу на личности — вы с какими микроконтроллерами работали и какие дисплеи подключали?
HAL инкапсулирует логику проверки готовности периферии к приему/передачи данных. Вам нет необходимости проверять никакие флаги. Просто представьте, что ваш цикл проверки флага находится внутри *_SendData.
работаю с двумя сериями f1 & f4, чаще всего — STM32F103RB и STM32F429ZI. Дисплеи были — st7735, ili9341, ili9806e. Сейчас пробую работу TFT-LCD + FSMC. Это дает бОльшую гибкость по выбору панелей.

Для асинхронной работы в большинстве случаев использую отсылку/прием через прерывания (функции *_IT) или DMA, где это резонно
ОО! а с ILI9806E работали по SPI или параллельным?
я посмотрел по даташиту SPI должен быть 9-ти битным (первый бит DC)…

у меня как то был чернобелый дисплей с 9-ти битным интерфейсом управления — кроме как ногодрыгом с ним работать и не получилось :-(
Вот собственно реализация функции. Столь высокая сложность обсусловлена соблюдением MISRA C 2004 и совместимости с RTOS. Но вы без труда найдете проверку флагов. Функция передачи с использованием прерываний немного проще, но все так же обвешена проверками для соблюдения MISRA.

HAL_SPI_TransmitReceive
/**
  * @brief  Transmit and Receive an amount of data in blocking mode.
  * @param  hspi: pointer to a SPI_HandleTypeDef structure that contains
  *               the configuration information for SPI module.
  * @param  pTxData: pointer to transmission data buffer
  * @param  pRxData: pointer to reception data buffer
  * @param  Size: amount of data to be sent and received
  * @param  Timeout: Timeout duration
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)
{
#ifdef USE_SPI_CRC
  __IO uint16_t tmpreg = 0U;
#endif
  uint32_t tickstart = 0U;
  HAL_StatusTypeDef errorcode = HAL_OK;

  /* Check Direction parameter */
  assert_param(IS_SPI_DIRECTION_2LINES(hspi->Init.Direction));

  /* Process Locked */
  __HAL_LOCK(hspi);

  /* Init tickstart for timeout managment*/
  tickstart = HAL_GetTick();

  if(!((hspi->State == HAL_SPI_STATE_READY) || \
    ((hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES) && (hspi->State == HAL_SPI_STATE_BUSY_RX))))
  {
    errorcode = HAL_BUSY;
    goto error;
  }

  if((pTxData == NULL) || (pRxData == NULL) || (Size == 0U))
  {
    errorcode = HAL_ERROR;
    goto error;
  }

  /* Don't overwrite in case of HAL_SPI_STATE_BUSY_RX */
  if(hspi->State == HAL_SPI_STATE_READY)
  {
    hspi->State = HAL_SPI_STATE_BUSY_TX_RX;
  }

  /* Set the transaction information */
  hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
  hspi->pRxBuffPtr  = (uint8_t *)pRxData;
  hspi->RxXferCount = Size;
  hspi->RxXferSize  = Size;
  hspi->pTxBuffPtr  = (uint8_t *)pTxData;
  hspi->TxXferCount = Size;
  hspi->TxXferSize  = Size;

  /*Init field not used in handle to zero */
  hspi->RxISR       = NULL;
  hspi->TxISR       = NULL;

#ifdef USE_SPI_CRC
  /* Reset CRC Calculation */
  if(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SPI_RESET_CRC(hspi);
  }
#endif

  /* Check if the SPI is already enabled */
  if((hspi->Instance->CR1 &SPI_CR1_SPE) != SPI_CR1_SPE)
  {
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
  }

  /* Transmit and Receive data in 16 Bit mode */
  if(hspi->Init.DataSize == SPI_DATASIZE_16BIT)
  {
    while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U))
    {
      /* Check TXE flag */
      if((hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)))
      {
        hspi->Instance->DR = *((uint16_t *)pTxData);
        pTxData += sizeof(uint16_t);
        hspi->TxXferCount--;

#ifdef USE_SPI_CRC
        /* Enable CRC Transmission */
        if((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE))
        {
          SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
        }
#endif
      }

      /* Check RXNE flag */
      if((hspi->RxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)))
      {
        *((uint16_t *)pRxData) = hspi->Instance->DR;
        pRxData += sizeof(uint16_t);
        hspi->RxXferCount--;
      }
      if((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >=  Timeout))
      {
        errorcode = HAL_TIMEOUT;
        goto error;
      }
    }
  }
  /* Transmit and Receive data in 8 Bit mode */
  else
  {
    while((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U))
    {
      /* check TXE flag */
      if((hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)))
      {
        *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++);
        hspi->TxXferCount--;

#ifdef USE_SPI_CRC
        /* Enable CRC Transmission */
        if((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE))
        {
          SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
        }
#endif
      }

      /* Wait until RXNE flag is reset */
      if((hspi->RxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)))
      {
        (*(uint8_t *)pRxData++) = hspi->Instance->DR;
        hspi->RxXferCount--;
      }
      if((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >=  Timeout))
      {
        errorcode = HAL_TIMEOUT;
        goto error;
      }
    }
  }

#ifdef USE_SPI_CRC
  /* Read CRC from DR to close CRC calculation process */
  if(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    /* Wait until TXE flag */
    if(SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, SET, Timeout, tickstart) != HAL_OK)
    {
      /* Error on the CRC reception */
      SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC);
      errorcode = HAL_TIMEOUT;
      goto error;
    }
    /* Read CRC */
    tmpreg = hspi->Instance->DR;
    /* To avoid GCC warning */
    UNUSED(tmpreg);
  }

  /* Check if CRC error occurred */
  if(__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR))
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC);
    /* Clear CRC Flag */
    __HAL_SPI_CLEAR_CRCERRFLAG(hspi);

    errorcode = HAL_ERROR;
  }
#endif

  /* Check the end of the transaction */
  if(SPI_CheckFlag_BSY(hspi, Timeout, tickstart) != HAL_OK)
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
    errorcode = HAL_ERROR;
  }

error :
  hspi->State = HAL_SPI_STATE_READY;
  __HAL_UNLOCK(hspi);
  return errorcode;
}

Но у вас же тут нет ничего, связанного с внешним сигналом RS(DC), и HAL за вас не сделает с ним ничего, являясь обёрткой для SPI, который, в данном случае, логику работы с дисплеем полностью не реализует — не умеет он по волшебству дёргать внешним пином. Вам, всё равно, в реализации вашего драйвера дисплея надо дождаться где-то конца передачи, и выставить правильно DC, если того требует работа с конкретным дисплеем.
А проверять флаги, или получать или состояние spi через HAL, или вообще без обёртки всё это реализовать на ассемблере — какая разница? Это вопрос вкуса. Смысл статьи в том, что надо правильно ловить окончание передачи данных прежде чем изменять состояние пина DC, не более того. И с HAL, в данном конкретном случае, только чуть сложнее выстрелить себе в ногу. Хотя у него есть свои заморочки — почитайте, например в сообществе на easyelectronics, там баги неприятные находили в HAL.
ИМХО, вы не правы,
если я правильно понял — в конце процедуры есть проверка BSY

/* Check the end of the transaction */
  if(SPI_CheckFlag_BSY(hspi, Timeout, tickstart) != HAL_OK)
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
    errorcode = HAL_ERROR;
  }

Поэтому после вызова этой процедуры можно смело давать команду на смену состояния DC…
Сама процедура тоже хороша тем что готова бросать буфер — то есть ее можно очень даже удобно использовать с дисплеями: CS=0, DC=0, кинул команду, DC=1, кинул данные (причем несколько данных), CS=1
Очень хорошая процедура!!!
Конечно могут быть случаи когда она будет работать медленнее специально оптимизированной — но для общего применения очень даже хорошая процедура…
спасибо. я рад, что смог донести мысль. Именно реализации на HAL позволяют использовать их в многозадачных решениях и позволяют добиться отказоустойчивости за счет таймаутов в блокируемых вызовах.
> Конечно могут быть случаи когда она будет работать медленнее специально оптимизированной

Ну так это не полная реализация для рассматриваемого в статье дисплея, о чём я и сказал. Если она используется только для передачи коротких команд, и обёрнута в clear DC — set DC, а для данных используется не она а передача буфера через DMA, например, то всё в принципе, ок. Я нигде не говорил, что она не правильна.

Но опять же, при чём тут достоинства HAL? Просто сделано правильно и проверяется, в итоге, HALом нужный флаг, а не через StdPeriph или напрямую. Собственно я об этом и писал — «И с HAL, в данном конкретном случае, только чуть сложнее выстрелить себе в ногу». Ничем реализация блокирующей записи с использованием StdPeriph или с прямой работой с регистрами отличаться не будет — надо будет только чуть больше кода написать, и возможно при этом ошибиться. Зато будет чёткое понимание как оно работает, подходит-ли полностью для конкретного случая, можно сделать не блокирующий вызов, и что-то полезное сделать пока данные отсылаются, и.т.п.

Мысль иными словами такая — HAL отнюдь не какая-то «серебряная пуля» или кнопка «сделать хорошо», а лишь один из вариантов абстракции, который можно выбрать, а можно и не выбрать, в зависимости от проекта.
в общем мысль: все равно желательно знать как работает изнутри, тогда все равно что использовать (HAL, StdP, CMSIS) :-)
поставлю плюсик :-)
Вы несколько сумбурно осветили вопрос о порядке проверки готовности передатчика, попробую уточнить. Если у Вас идет передача готовых пакетов и есть какая-либо буферизация, то неважно, когда проверять флаг готовности передачи — в конце (так чаще делают) либо в начале (а вот так делают реже, хотя так правильнее).
Эта правильность проявит себя, когда Вы тратите значительное (сравнимое со всеменем передачи пакета) время на подготовку очередного пакета и у Вас нет буферизации. Вот тогда действительно получается разница — в первом варианте вы положили пакет в передатчик, потом ждете, пока передача завершится, потом готовите следующий пакет, при этом можно было бы уже передавать, но пока нечего и передатчик простаивает.
Во втором варианте вы положили пакет в передатчик, и идете готовить следующий, далее, если надо, ждете, когда передача завершится и повторяете процедуру. При этом Вы выигрываете за счет того, что подготовка очередного пакета и передача текущего осуществляются одовременно.
Но в любом случае называть первый способ неправильный — это неправильно (игра слов, однако), он всего лишь неоптимальный, хотя для меня является критерием профессионализма.

А вот со второй частью дела обстоят намного хуже, причем не только для SPI, а и для других интерфейсов со значительным временем передачи (как правило, последовательных).
Что касается различия битов «готовности передачи» и «завершения передачи», то описаны они в документации (как правило) весьма понятно, применение одного вместо другого просто недопустимо (хотя встречается повсеместно) и может приводить (и часто приводит) к появлению труднообнаружимых (причем непостоянных) ошибок. Такая практика не имеет ни малейшего оправдания и объясняется низкой инженерной культурой определенного количества разработчиков.
спасибо! хорошее дополнение…

кстати, в документации флаги BSY и TXE описаны, но я не нашел места где было бы написано что TXE — это к SPI_DR, а BSY к Shift_reg… как то везде все в общем описано: что мол TXE это флаг буфера, а BSY конца передачи (причем BSY по разному работает в зависимости от режима Master/Slave и это вносит еще большую неразбериху в документацию)

так же как и на диаграммах (например № 255 в Reference manual) указаны моменты общей генерации флагов, но вот передача данных из SPI_DR и Shift_reg — не указаны… в итоге опять не складывается все в одну картинку, и не понятно какой же флаг в какой момент использовать… поэтому я и нарисовал свои диаграммы где отдельной строкой отобразил SPI_CR и отдельной строкой Shift_Reg — тогда сразу становиться понятной логика срабатывания флагов…
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.