SPI.setDataMode(SPI_MODE0)
Возможные значения аргументов этой функции от SPI_MODE0 до SPI_MODE3. Определяют полярность и фазу тактового сигнала. Обычно нет необходимости изменять эту настройку, если только документация не требует установить какой-то определенный режим работы для организации обмена с ведомым устройством
Объединенные передача и прием происходят в функции transfer. Эта функция посылает байт данных и возвращает байт данных, принятый в процессе передачи:
byte sendByte = 0x23;
byte receiveByte = SPI.transfer(sendByte);
Поскольку диалог с периферией обычно имеет форму запроса со стороны ведущего и ответа со стороны ведомого, часто последовательно выполняются две передачи данных: одна — запрос, другая (возможно, во время передачи нулей) — ответ периферийного устройства. Вы увидите, как это происходит, в следующем примере.
Пример SPI
Этот пример демонстрирует взаимодействие платы Arduino с интегральной микросхемой восьмиканального АЦП MCP3008, добавляющего еще восемь 10-битных аналоговых входов. Эта микросхема стоит очень недорого и легко подключается к плате. На рис. 9.6 изображена схема подключения микросхемы к плате Arduino с использованием макетной платы и нескольких проводов. Переменное сопротивление служит для изменения уровня напряжения на аналоговом входе 0 между 0 и 5 В.
Рис. 9.6. Схема соединения компонентов для примера SPI
Ниже приводится скетч для этого примера:
// sketch_09_01_SPI_ADC
#include <SPI.h>
const int chipSelectPin = 10;
void setup()
{
Serial.begin(9600);
SPI.begin();
pinMode(chipSelectPin, OUTPUT);
digitalWrite(chipSelectPin, HIGH);
}
void loop()
{
int reading = readADC(0);
Serial.println(reading);
delay(1000);
}
int readADC(byte channel)
{
unsigned int configWord = 0b11000 | channel;
byte configByteA = (configWord >> 1);
byte configByteB = ((configWord & 1) << 7);
digitalWrite(chipSelectPin, LOW);
SPI.transfer(configByteA);
byte readingH = SPI.transfer(configByteB);
byte readingL = SPI.transfer(0);
digitalWrite(chipSelectPin, HIGH);
// printByte(readingH);
// printByte(readingL);
int reading = ((readingH & 0b00011111) << 5)
+ ((readingL & 0b11111000) >> 3);
return reading;
}
void printByte(byte b)
{
for (int i = 7; i >= 0; i--)
{
Serial.print(bitRead(b, i));
}
Serial.print(" ");
}
Функция printByte использовалась на этапе разработки для вывода двоичных данных. Несмотря на то что Serial.print может выводить двоичные значения, она не добавляет ведущие нули, что немного усложняет интерпретацию данных. Функция printByte, напротив, всегда выводит все 8 бит.
Чтобы увидеть данные, поступающие от микросхемы MCP3008, уберите символы // перед двумя вызовами printByte, и данные появятся в окне монитора последовательного порта.
Наиболее интересный для нас код сосредоточен в функции readADC, которая принимает номер канала АЦП (от 0 до 7). Прежде всего нужно выполнить некоторые манипуляции с битами, чтобы создать конфигурационный байт, определяющий вид преобразования аналогового сигнала и номер канала.
Микросхема поддерживает два режима работы АЦП. В одном выполняется сравнение двух аналоговых каналов, а во втором, несимметричном режиме (который используется в этом примере), возвращается значение, прочитанное из указанного канала, как в случае с аналоговыми входами на плате Arduino. В документации к MCP3008 (http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) указывается, что в настроечной команде должны быть установлены четыре бита: первый бит должен быть установлен в 1, чтобы включить несимметричный режим, следующие три бита определяют номер канала (от 0 до 7).
Микросхема MCP3008 не поддерживает режим побайтовой передачи, в котором действует библиотека SPI. Чтобы MCP3008 распознала эти четыре бита, их нужно разбить на два байта. Далее показано, как это делается:
unsigned int configWord = 0b11000 | channel;
byte configByteA = (configWord >> 1);
byte configByteB = ((configWord & 1) << 7);
Первый байт конфигурационного сообщения содержит две единицы, первая из которых может не понадобиться, а вторая — это бит режима (в данном случае несимметричного). Другие два бита в этом байте — старшие два бита номера канала. Оставшийся бит из этого номера передается во втором конфигурационном байте как самый старший бит.
Следующая строка устанавливает уровень LOW в линии выбора ведомого устройства, чтобы активировать его:
digitalWrite(chipSelectPin, LOW);
После этого отправляется первый конфигурационный байт:
SPI.transfer(configByteA);
byte readingH = SPI.transfer(configByteB);
byte readingL = SPI.transfer(0);
digitalWrite(chipSelectPin, HIGH);
Аналоговые данные не будут передаваться обратно, пока не будет отправлен второй байт. 10 битов данных из АЦП разбиты на два байта, поэтому, чтобы подтолкнуть отправку оставшихся данных, выполняется передача нулевого байта.