Хак по прерываниям SPI и ноге CS в режиме Master

Как известно, один из наиболее глупых косяков МК серии STM32 — отсутствие аппаратного управления шиной CS.

Когда я задавал французу, который вел лекцию по STM32 в Москве об этом вопрос, он даже удивился: кому это нужно? Однако, много мелкой периферии вообще не заводится без сброса линии CS после каждой посылки — ЦАПы, вообще воспринимают этот строб как команду к выставлению. А те, кто работать без этой линии как-то могут, рано или поздно ловят сдвиг передачи (это когда один строб SCK либо добавляется, либо пропадает) и тоже начинает глючить.

Но это пол беды. Казалось — настрой прерывание Transfer Complete, да дрыгай там ножкой. А вот и нет. Нету там прерывания такого.

Так что приходится применять хак: всегда включать режим полного дуплекса и использовать прерывание RX not empty.

Кусок кода, иллюстрирующий данное безобразие
1) инициализация, после включения RCC и настройуи пинов:

  // Настройка SPI
    SPI_InitTypeDef SPI_InitStruct;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
    SPI_InitStruct.SPI_CPHA              = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_CPOL              = SPI_CPOL_Low;
    SPI_InitStruct.SPI_DataSize          = SPI_DataSize_8b;

    SPI_InitStruct.SPI_Direction         = SPI_Direction_2Lines_FullDuplex;
// изначально тут было       SPI_InitStruct.SPI_Direction         = SPI_Direction_1Line_Tx;
// Я даже пин MISO использую совсем для других целей. 
// Но только в режиме полного дуплекса работает прерывание RXNE, которое срабатывает после передачи байта.
    SPI_InitStruct.SPI_FirstBit          = SPI_FirstBit_LSB;
    SPI_InitStruct.SPI_Mode              = SPI_Mode_Master;
    SPI_InitStruct.SPI_NSS               = SPI_NSS_Soft;
  // Инициализируем SPI
    SPI_Init(SPI2, &SPI_InitStruct);

  //Включаем соответствующее прерывание:
    SPI_I2S_ITConfig(SPI2,SPI_IT_RXNE,ENABLE);
  // SPI_Cmd(SPI2, ENABLE);

2) Посылка данных:

    STROB_RESET; // опускаю строб
    SPI_SendData(SPI2,data); //Отправляю данные



3) Само прерывание:

void SPI2_IRQHandler(void)
{

    if (SPI_GetITStatus(SPI2,SPI_IT_RXNE))
    {
        uint16_t tmp = SPI_ReceiveData(SPI2); // Читаю "данные". Так сбрасывается флаг RXNE.
        STROB_SET; //Поднимаю пин строба.
    }
}

  • +1
  • 26 апреля 2012, 12:17
  • dekar

Комментарии (74)

RSS свернуть / развернуть
Ну тут как-то можно понять: SPI — шина, на которой могут быть несколько slave-устройств, у которых ноги SS могут быть заведены на произвольные пины процессора. А городить адресацию slave-ов с произвольными пинами обойдется в гемор и добавление еще альтернативных функций на каждый пин.
0
И вдобавок — обмен по шине spi не всегда однобайтен. Вон у того же выкидыша микрочипа mrf24j40 — там двух и трех-байтные обмены. Аппаратно разрулить SS в такой мешанине невозможно.

Да, можно предложить какую-то схему типа «перед отправкой последнего байта выставлять какой-то флажок, который автоматически поднимет ногу по окончанию передачи», но это потенциальный гемор как в I2C с окончанием передачи, плюс надо задуматься о настройке паузы между окончанием передачи и поднятием SS (а она может нормироваться в даташите устройства).
0
Аппаратное управление выходом NSS включается каким-то битом в настройках.
0
  • avatar
  • evsi
  • 26 апреля 2012, 13:04
Это немного не то. Он не дает стробы после каждого байта.
0
А у кого это интерестно дает?

Если нужен строб на каждый байт, то надо просто ждать окончания передачи байта, хардваря поднимает слейв и после этого загоняем следующий байт.
Причем ждать надо не емпти, а:
21.4.3 SPI status register (SPI_SR)
Bit 7 BSY: Busy flag
0: SPI not busy
1: SPI is busy in communication or Tx buffer is not empty
0
Ждать в пустом цикле? No way!
0
А как тогда вы будите ждать возникновения прерывания или момента для отправки новой порции данных?
0
Вообще-то и не должен. Если нужны стробы после каждого байта, то и отсылать/принимать надо по одному байту. Ну или управлять этим програмно. В любом случае отсутствие возможности нарушать протокол врядли можно отнести к «одному из глупых косяков».
+1
«отсутствие возможности» — это всегда косяк.
«нарушить протокол» — расскажите, как дёрганье строба между передачами нарушает протокол?
0
«отсутствие возможности» — это всегда косяк.
Извините, конечно, за грубость, но это вы чушь написали.
«нарушить протокол» — расскажите, как дёрганье строба между передачами нарушает протокол?
Это не строб между передачами, а строб во время одной передачи.
0
Извините, конечно, за грубость, но это вы чушь написали.
Отличный аргумент.
Если вам интересно (хотя, по опыту нашей переписки ещё в прошлой ветке, есть сомнение в том, что вас может интересовать мнение, отличное от вашего), наличие возможности не заставляет вас ею пользоваться. А отсутствие — заставляет не пользоваться. Что лучше? Применение для указанной мною фичи существует. Отсутствие возможности — тоже. Это плохо, когда есть задача, но нет прямой возможности её реализовать. К примеру — работа с внешним ЦАПом через DMA без прерываний.

Это не строб между передачами, а строб во время одной передачи.
Иногда передача — это один байт. Тогда это строб между передачами.
0
Если вам интересно (хотя, по опыту нашей переписки ещё в прошлой ветке, есть сомнение в том, что вас может интересовать мнение, отличное от вашего),
Вы, кажется, меня с собой перепутали.
наличие возможности не заставляет вас ею пользоваться. А отсутствие — заставляет не пользоваться. Что лучше?
У вас отсутствует возможность умереть от, например, чумы. Вы действительно считаете это косяком?
0
Вы, кажется, меня с собой перепутали.
Это только кажется.
У вас отсутствует возможность умереть от, например, чумы. Вы действительно считаете это косяком?
Ложь. Штамм чумы я могу взять этажом выше.
0
Это только кажется.
Угу. Только не мне.
Ложь. Штамм чумы я могу взять этажом выше.
Ну раз у вас такие широкие возможности, то ничего не мешает вам выбрать вместо чумы любую другую давно исчезнувшую болячку и повторить эксперимент с проверкой на косячность отсутствие возможности ею заболеть.
0
Иногда передача — это один байт. Тогда это строб между передачами.
То, что они могут совпадать вовсе не значит, что это одно и то же. Стробы между передачами делаются, между отдельными байтами в одной передаче — нет.
0
Как это предложение связано с тем, что иногда мне нужны хардварные стробы для передачи каждого байта, а STM мне этого не даёт?
0
Это предложение непосредственно связано с тем, как должен вести себя CS согласно протокола.
0
какого протокола согласн, насяньника?
0
какого протокола согласн
Вы уже успели забыть о чем идет речь?
насяньника
Я, к счастью, вам не начальник.
0
Протокол — это то, что идёт поверх интерфейса. SPI тут не причём.
0
Интерфейс реализует в железе протокол обмена. Впрочем да, я помню, что мнение отличное от вашего, вас не интересует, даже если это мнение является общепринятым.
0
SPI — это интерфейс. Протокол — это, например, modbus.
0
Вот, например, Microchip с вами не согласен.
0
Как хорошо, что ST со мной согласен. И Atmel. А Motorolla — вообще всеми руками за.
0
Ничего не имею против того, что бы вам продолжало казаться, что они с вами согласны, хотя на самом деле это не так.
0
Да, в преведённом файле описание протокола.
Вы правде не улавливаете разницу «протокол» <-> «интерфейс»?
0
Похоже вы не в курсе, что протоколы бывают разного уровня, в том числе такие, в которых описывается порядок дрыгания ногами вместе с задержками и прочей требухой.
0
О! Вы на правельном пути. И бывает такой протокол SPI, в котором надо дёргать CS после каждого байта. И хорошо, когда реализация такого протокола возможна аппаратно.
0
О! Вы на правельном пути.
К счастью не на «правельном», а на «правильном», в отличие от.
И бывает такой протокол SPI, в котором надо дёргать CS после каждого байта.
Полагаю, вас не затруднит привести линк на описание «такого протокола»? Меня особенно интересует как, в таком случае, определяется окончание многобайтовой пересылки. Как это делается в известных мне версиях протокола SPI я в курсе.
0
В таких протоколах, как правело, нету многобайтных пересылок. Что не мешает им существовать.
0
Я вообще не пойму зачем строб после каждого байта. Там ведь есть счетчик который считает биты.

В сдвиговом регистре нужен строб. Но там никуя не SPI.
0
Но там никуя не SPI.
а что?
0
обычно нет названия. В сдвиговых регистрах не SPI протокол. Там используется свой. У разных фирм они могут различаться.
0
Если в них нет многобайтных пересылок, то они ничем не отличаются от знакомых мне SPI и, следовательно, они не требуют побайтового строба. Если же вы пытаетесь работать в многобайтовом режиме при том, что периферия требует однобайтового (иначе не нужен был бы строб), то это проблема не «глупых косяков» железа, а чего-то другого.
+1
Не дай бог мне столкнуться с этим.
0
Про сдвиговик вот я и забыл! Ярчайший пример того, что этот строб нужен. Хотя, может вам с evsi сдвиговые регистры не нужны? Тогда понятно всё.
0
Там не SPI. В MBI5030 тоже протокол похож на SPI, также во многих цифровых энкодерах есть схожий протокол.
0
Там не SPI.
Чем он отличается от SPI? Почему это не он?
0
Потому что строб сигнал это не CS (crystal select). Там это строб.
+1
digteh.ru/digital/image/PoslReg3.gif — датаграмма сдвигового регистра
en.wikipedia.org/wiki/File:SPI_timing_diagram2.svg — возможные виды SPI. Нетрудно найти тот вид SPI, который реализован в сдвиговом регистре. Или совпадение датаграмм — это не достаточно, чтобы интерфейс был одним и тем же?
0
Найди в даташите на сдвиговый регистр упоминание об SPI.

Аппаратная поддержка дрыганья ножки CS есть в ADuM7xxx. Там для этого есть регистр, в который записывается длинна посылки в байтах. Но там это ахерительно не удобно. Т.к. перед использованием нужно пройти инициацию.
0
Вы вероятно забыли что SS/CS (Slave/Chip Select), внезапно, является сигналом выбора чипа, а не строб-сигналом (сигналом защелки).
+1
Вы, будьте добры, не сравнивайте *** с пальцем. Вон народ в дисплеях тоже повадился орать I2C! Хотя его там и в помине нет. А чё, ведь тоже 9-бит, значит i2c, логично же.
0
У вас отсутствует возможность быть бабой — это косяк, а ну бегом бегите исправлять :)
0
ты это серьёзно?
0
Эта настройка работает только на вход в режиме slave.
0
С чего бы вдруг?
0
И кто только лекции не читает:
NSS management by hardware or software for both master and slave: dynamic change
of master/slave operations
реф-ман (aka CD00246267.pdf) 21.3.1 General description в нем Slave select (NSS) pin management
0
Только в slave mode, только как вход. Увы.
0
CD00246267.pdf:
...
21.2.1 SPI features
...
● NSS management by hardware or software for both master and slave: dynamic change 
of master/slave operations
...
0
Теперь читайте дальше, и смотрите, как это работает.
0
Я прочитал. Более того, я этим пользовался.
0
Сорри, был неправ, не пользовался (понадеялся на либу, как оказалось зря, там тоже програмное управление).
0
Это в еррате написано что ли? Что-то я в ней ни нашел ни слова. Зато в пункте на который я дал ссылку четко написано:
● Hardware NSS mode: there are two cases:
– NSS output is enabled: when the STM32F100xx operates as a Master and the
NSS output is enabled through the SSOE bit in the SPI_CR2 register, the NSS pin
is driven low and all the NSS pins of devices connected to the Master NSS pin see
a low level and become slaves when they are configured in NSS hardware mode.
...cut...
Так же, если открыть предлагаемый алгоритм настройки мастера 21.3.3 Configuring the SPI in master mode, то видим:
5. If the NSS pin is required in input mode, in hardware mode, connect the NSS pin to a
high-level signal during the complete byte transmit sequence. In NSS software mode, set the SSM and SSI bits in the SPI_CR1 register. If the NSS pin is required in output
mode, the SSOE bit only should be set.

Я чего-то упустил?
0
Я чего-то упустил?
Например отсутствие записи о том, что NSS поднимается аппаратно после конца передачи.
0
Нашел, из описания битов регистров становится ясно:
Bit 8 SSI: Internal slave select
This bit has an effect only when the SSM bit is set. The value of this bit is forced onto the NSS pin and the IO value of the NSS pin is ignored.
0
Это вообще о настройках SPI.
0
А у какого микроконтроллера линия CS дергается аппаратно??
0
  • avatar
  • a9d
  • 26 апреля 2012, 13:49
Замечу, не просто аппаратно, а на каждый байт.
0
SPORT у blackfinов, у некоторых армов (SSP вроде называется).
не совсем SPI правда (там 6 ног, раздельно на приём и на передачу данные, клоки, синхронизвация), но заставить работать как SPI чтобы синхоимпульс как CS каждый байт дергался можно.
0
Не обязательно байт. Но обязательно — пересылку.
0
На пересылку он вполне работает.
0
что есть пересылка? Как МК понимает, что она закончилась? Где мне указать размер пересылки?
0
Внимательное изучение раздела 21.3.3 вам, возможно, поможет.
0
К сожалению, не помогло. Попытки производи уже несколько раз.
0
Идея использования прерывания RX not empty интересная. Если б Вы добавили в статью кусок кода это реализующий — статья была бы наглядней и более законченной.
0
Код зависит от библиотек. Я использую смесь из CMSIS и прямого управления регистрами, так что мой код скорее убавит наглядности, чем добавит.
0
Зато он покажет суть метода. Или опишите его в статье более подробно, разобрав на примере. Тогда статья будет более соответствовать коллективному блогу. В данный момент практической пользы от данного материала нет, особенно для начинающих.
0
А ынче так модно :(
+1
ок, убедил. Сейчас дорисую
0
Во. Теперь другое дело.
0
«1) инициализация, после включения RCC и настройуи пинов»
0
Для вывода регулярных значений в SPI (скоростной точный ЦАП, например) применяю ШИМ-выход таймера как выход CS. В связке с другим таймером, работает полностью аппаратно, можно выводить несколько мегавыборок/секунду по несколько байт каждая.
0
Хак не в том, чтобы сделать на прерываниях, хак в том, чтобы от них свалить на DMA :-)))) на прерываниях-то каждый может…
+2
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.