Почему, если нам нужно читать / записывать с помощью SPI, мы должны делать так?

#embedded #stm32 #spi

Вопрос:

У меня есть вопрос по поводу шины SPI. Я часто вижу в некоторых библиотеках то, что нашел, но не могу понять, как это работает.

Краткие примеры из одной библиотеки, которую я нашел. Написание с помощью SPI:

 static void nRF24_WriteRegister(uint8_t reg, uint8_t val)
{
    uint8_t tmp[2];

    tmp[0] = NRF24_CMD_W_REGISTER | reg;
    tmp[1] = val;

    NRF24_CSN_LOW;

    nRF24_SendSpi(tmp, 2);

    NRF24_CSN_HIGH;
}
 

Как это работает, если мы помещаем в один и тот же фреймовый регистр (в который мы записываем) и данные
в этот регистр?

Но еще больше меня смущает чтение из SPI:

 static uint8_t nRF24_ReadRegister(uint8_t reg)
{
    uint8_t resu<

    reg = NRF24_CMD_R_REGISTER | reg;

    NRF24_CSN_LOW;
    nRF24_SendSpi(amp;reg, 1);
    nRF24_ReadSpi(amp;result, 1);
    NRF24_CSN_HIGH;

    return resu<
}
 

Почему мы должны сначала отправлять какую-то информацию, а потом читать?

Ответ №1:

Устройство SPI содержит набор регистров. У каждого регистра есть адрес и значение. Значения регистров могут быть прочитаны и / или записаны мастером. Устройство SPI должно знать, какой адрес регистра считывает / записывает мастер. Таким образом, протокол заключается в том, что мастер сначала записывает адрес регистра, а затем считывает / записывает значение регистра. Подробные сведения о доступных регистрах и протоколе чтения/записи см. в техническом описании устройства.

В вашем первом примере nRF24_SendSpi(tmp, 2); записывается два байта, сначала адрес регистра, а затем значение регистра.

В вашем втором примере nRF24_SendSpi(amp;reg, 1); записывается один байт, адрес регистра. Затем nRF24_ReadSpi(amp;result, 1); считывается один байт, значение регистра.

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

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

1. Боже мой, как теперь все ясно. Большое вам спасибо!

Ответ №2:

Подчиненный SPI не генерирует тактовый сигнал, только ведущий. Таким образом, ведомому устройству для передачи чего-либо необходим тактовый сигнал от Ведущего устройства. Именно по этой причине ведущему устройству приходится отправлять фиктивные данные — для генерации тактового сигнала для ведомого устройства.

Ответ №3:

В некоторых отношениях ваш вопрос касается поведения конкретного устройства, а не SPI в целом. Однако к первому ответу:

Почему мы должны сначала отправлять некоторую информацию, а затем читать?

«Информация», которую вы отправляете в этом случае, — это команда, указывающая, какой регистр читать.

SPI — это коммуникационный интерфейс типа «Ведущий/ведомый». Подчиненное устройство не может само инициировать связь. Устройства SPI состоят из сдвигового регистра. На фронте синхронизации один бит сдвигается в регистр из MOSI (Master-Out / Slave-In), и один бит сдвигается из регистра в MISO (Master-In / Slave-Out). Мастер управляет часами, поэтому для чтения с устройства мастер должен синхронизировать данные с устройством, чтобы данные могли быть выведены с устройства. По сути, это «дуплексная» операция (т. Е. Данные вводятся и выводятся одновременно. Даже если вы хотите только читать, вы должны писать, и часто это делается путем отправки «фиктивных» данных на MOSI.

Итак, в вашем втором фрагменте: NRF24_CMD_R_REGISTER | reg это команда для чтения индекса регистра reg . Выходные данные будут такими, какие были в регистре сдвига, когда была отправлена команда, поэтому они не будут результатом фактического чтения, потому что эта команда еще не была получена. Когда устройство получит команду complete, оно загрузит регистр сдвига значением в регистре reg , а затем данные должны быть считаны второй операцией шины, которая будет синхронизировать фиктивные данные с MOSI, чтобы получить результат инструкции чтения из MOSI.

Что касается:

Как это работает, если мы помещаем в один и тот же регистр кадров (в который мы записываем) и данные в этот регистр?

В ассоциированном фрагменте первый байт, содержащий: NRF24_CMD_W_REGISTER | reg , — это команда, которая гласит: запишите следующее байтовое значение для регистрации reg . Второй байт , содержащий val данные , в которые они должны быть записаны reg . При nRF24_SendSpi(tmp, 2); вызове оба байта отправляются в MOSI путем синхронизации строки SCLK 16 раз.

введите описание изображения здесь
Атрибуция CBurnett От https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#/media/File:SPI_8-bit_circular_transfer.svg

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

1. Ваше объяснение тоже очень, очень хорошее. Спасибо, сэр!

2. Ваша картинка выглядит забавно в темной теме. Будьте осторожны при использовании альфа-канала 🙂

3. @0andriy — да — для меня тоже выглядит забавно — не мое изображение (как указано в атрибуции). Я не собирался тратить на это слишком много времени. Я полагаю, что если вы решите использовать Темный режим, все это произойдет. На самом деле это, возможно, вопрос для electronics.stackexchange.com в любом случае. Здесь есть подходящая схема, но права на использование не указаны.