Двустороння связь по SPI

Ilya_Sh

✩✩✩✩✩✩✩
7 Апр 2025
18
0
Доброго времени суток. Я пытаюсь организовать двустороннюю связь по SPI между NodeMCU esp-12e (esp8266) в качестве мастера и atmega48 в качестве слейва. Данные от мастера слейв получает без проблем, однако ответ от слейва на мастере записать не получается: в мониторе порта всегда 255.
Код мастера:
#include <SPI.h>
uint8_t value = 250;
void setup() {
  pinMode(D8,OUTPUT);
  digitalWrite(D8,1);
  Serial.begin(9600);
  delay(1000);
  SPI.begin();
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
}

void loop() {
  digitalWrite(D8,0);
  SPI.transfer(value);
  delayMicroseconds (20);
  Serial.println(SPI.transfer(0));
  digitalWrite(D8,1);
  delay(1000);
}
Код слейва:
#define PIN_SS   PB2
#define PIN_MOSI PB3
#define PIN_MISO PB4
#define PIN_SCK  PB5

volatile uint8_t received_byte = 0;

void setup() {

    pinMode(PIN_SS, INPUT);
    pinMode(PIN_MOSI, INPUT);
    pinMode(PIN_SCK, INPUT);
    pinMode(PD5, OUTPUT);
    digitalWrite(PD5,0);
    pinMode(PIN_MISO, OUTPUT);
    SPCR  |= _BV(SPE);
    SPCR |= (1 << SPIE);
}

ISR(SPI_STC_vect) {
    received_byte = SPDR;
    if(received_byte==250)digitalWrite(PD5,1);
    else digitalWrite(PD5,0);
    SPDR=128;
}

void loop() {}
Подскажите, пожалуйста, в чём у меня может быть проблема. Не исключаю, что проблема может быть в аппаратной части:
Мастер
Слейв
D8 (HCS)PB2 (SS)
D7 (HMOSI)PB3 (MOSI)
D6 (HMISO)PB4 (MISO)
D5 (HSCLK)PB5 (SCK)
1785951613588.png
1785951680622.png
Порты соеденены напрямую, без какаких-либо подтяжек. Логические уровни согласованы, земля общая. Спасибо
 

Bruzzer

★★★★✩✩✩
23 Май 2020
826
257
#define PIN_SS PB2
#define PIN_MOSI PB3
#define PIN_MISO PB4
#define PIN_SCK PB5
Я не знаю, в этом ли причина, но частая причина ошибок в AVR, это когда используют записи вида "PB2" в стандартных функциях pinMode и digitalWrite digitalRead. Попробуйте вместо PB2 - PB5 написать номера в нумерации Arduino т.е. 10 - 13

Добавлено позже. При использовании стандартных пинов в UNO подобных можно в pinMode и digitalWrite digitalRead просто сразу писать SS MOSI MISO SCK
 
Изменено:

bort707

★★★★★★★
21 Сен 2020
3,506
1,004
Мне кажется тут неправильно. SPI это синхронная шина, которая на каждый переданный бит получает бит в ответ. А вы сначала передаете число, а когда передача закончена - пытаетесь что-то принять, когда в шине уже ничего нет.

Кроме того, будет ли передача двухсторонней или нет, зависит от библиотеки SPI на конкретной плате. В некоторых реализациях SPI.transfer(value); ответ не сохраняет
 

Ilya_Sh

✩✩✩✩✩✩✩
7 Апр 2025
18
0
@bort707, разве аппаратная шина SPI слейва на каждый бит мастера не отвечает битом из SPDR? Фиктивная передача нуля — всего лишь попытка от безысходности, если пытаться прочитать значение, возвращаемое SPI.transfer(value) без дополнительных передач, то там будут те же 255. Вы не могли бы привести пример кода двусторонней реализации аппаратного SPI, если я не прав? А что касается реализаций SPI.transfer(), которые не возвращают ответ слейва, то это проблема в каких-то определённых версиях SPI.h?
 
Изменено:

Ilya_Sh

✩✩✩✩✩✩✩
7 Апр 2025
18
0
@EugeneFrol, насколько я понял, SPCR |= (1 << SPIE) как раз таки включает прерывание по SPI, которое у меня, вроде, нормально работает
 
Изменено:

Sergo_ST

★★★★★★✩
15 Мар 2020
1,306
955
@Ilya_Sh, Попробуйте устанавить SPDR=128; сразу после received_byte = SPDR;. Скорее всего digitalWrite() выполняется дольше 20мкс и возникает коллизия регистра данных.
 
  • Лойс +1
Реакции: EugeneFrol

Sergo_ST

★★★★★★✩
15 Мар 2020
1,306
955
@Ilya_Sh, Кстати да, @Bruzzer прав. Под масками PDn/PBn/PCn скрываются номера бит, значения которых от 0 до 7.
Укажите порядковый номер пина для плат Ардуино нано/уно(если хотите использовать pinMode()), или инициализируйте пины через регистры с использованием масок PDn/PBn/PCn.

В данном случае у вас пин MISO так и остаётся в режиме входа после старта мк(а нужно в режиме выхода), поэтому и не происходит передача, но происходит приём тк все остальные пины по умолчанию и так в режиме входа.
 

bort707

★★★★★★★
21 Сен 2020
3,506
1,004
@Bruzzer,
Стм32
Довольно часто мы используем SPI с устройствами, которые только принимают данные и никогда не шлют ничего назад - например ТФТ экраны. Для таких случаев в библиотеке СПИ может быть предусмотрен отдельный трансфер-онли метод, который, конечно же, принимает байты из шины (иначе никак), но никуда их не сохраняет. В частности, в пакете ардуино для Стм32 такое есть
 

Bruzzer

★★★★✩✩✩
23 Май 2020
826
257
В частности, в пакете ардуино для Стм32 такое есть
Я не смог найти.
Если под
>В некоторых реализациях SPI.transfer(value); ответ не сохраняет
Вы имели в виду "ответ не возвращает", то непонятно, зачем делать специально функцию ничего не возвращающую, если можно просто проигнорировать возвращаемое значение. Если вы имели в виду именно "ответ не сохраняет", то это "обычное поведение SPI.h" на отправку одного байта, не сохранять ответ, а возвращать его как результат.
 

SpyHUNTERrzn

✩✩✩✩✩✩✩
24 Июл 2025
4
0
@Sergo_ST, извиняюсь за офтоп, но хотелось бы спросить, а вообще возможно программировать номера пинов в среде ArduinoIDE именно в виде PDn/PBn/PCn? Работаю в основном с голыми камнями, и было бы удобнее в виде PDn/PBn/PCn нежели искать распиновку и сравнивать.
 

bort707

★★★★★★★
21 Сен 2020
3,506
1,004
вообще возможно программировать номера пинов в среде ArduinoIDE именно в виде PDn/PBn/PCn? Работаю в основном с голыми камнями, и было бы удобнее в виде PDn/PBn/PCn
Это зависит от того пакета Ардуино, который вы используете для работы с конкретным камнем. Через что вы работаете с Атмегой48 - через стандартный АВР пакет или через что-то типа МиниКоре?

В МиниКоре вместо PDn/PBn/PCn используйте дефайны вида PIN_PBn / PIN_PCn - это будет работать как вы хотите.
 
Изменено: