Работа Arduino по шине SPI в режиме Slave

Здравствуйте! Бьюсь над этим уже длительное время, вдруг тут смогут подскащать. Задача у ардуино следующая: нужно эмулировать датчик, которого у меня нет в наличии. Общение происходит по SPI, в качестве ведущего устройства выступает stm32, ардуине же выделена роль ведомого. Она должна дождаться байта инициализации (0x03), после чего запустить шим на частоте 4 кГц. Ведущий ловит этот шим, и по нему запрашивает 14 байт у ведомого на чтение. SPI в прерываниях выдает байты из массива, а после того, как выдаст 14(по счетчику), инкрементирует все элементы массива. Это предназначено для того, чтобы оценивать целостность пакетов. Однако, когда я смотрю на стороне мастера то, что мне приходит, пакеты идут не совсем так, как должны (значение в каждом разное)

Исходя из этого могу сделать три предположения:

  1. Я делаю что-то не так на стороне слейва (код прикладываю ниже)
  2. У меня какие-то проблемы с тактированием (вроде бы я настроил SPI на обоих концах так, чтобы там все было одинаково, но мало ли, вдруг что-то не так сделал)
  3. Проблемы идут со стороны мастера.

Для исключения первого предположения я решил написать на этот форум. Подскажите, пожалуйста, бросаются ли какие-то проблемы, мешающие мне реализовать желаемое, в моем коде в глаза? Большое спасибо всем откликнувшимся!

#include <SPI.h>

#define MOSI_PIN 11
#define MISO_PIN 12
#define SCK_PIN 13
#define SS_PIN 10
#define ISR_PIN 9

uint8_t rcv_buf[14];
uint8_t trm_buf[14];
volatile bool flag=false;

void setup() {

  TCCR1A = 0b00000001;  
  TCCR1B = 0b00000010;  

  pinMode(MISO_PIN, OUTPUT);

  SPCR |= _BV(SPE);                       // Переключение в Slave
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.attachInterrupt();

  Serial.begin(115200);
  
}

ISR (SPI_STC_vect)                        // Прерывание по SPI 
{
  uint8_t initByte = SPDR;
  if (initByte == 0x03){
    analogWrite(ISR_PIN, 128);
  }
  else{
    static uint32_t rcv_counter=0;
    rcv_buf[rcv_counter++] = initByte;
    SPDR = trm_buf[rcv_counter];
    if(rcv_counter == 14){
      rcv_counter = 0;
      flag = true;
    }
  }
    
}

void loop() {
  if (flag) {
    Serial.println("done");
    for(uint8_t i = 0; i<14; i++){
				trm_buf[i] += 1;
			}
    flag = false;  
  }
}

В режиме SLAVE такт.частоту устанавливать не надо, т.к. тактирует Мастер.

Попробуйте, для начала, снизить частоту обмена.
P.S

С stm32 почти не знаком, но знаю, что у него 3.3 вольтовая логика. Возьмите ардуино про мини 3.3в, чтобы совместить лог. уровни.
P.P.S.
Да, ещё не знаю, как там у stm32 организован SPI, возможно нужны резисторы подтяжки

1 лайк

Да, у Вас проблемы с пониманием как и что там тактируется. Тактирует только мастер. Слэйв отдаёт свои биты в ответ на биты мастера, типа принял бит и в ответ положил свой (т.е. мастеру, чтобы что-то получить, надо что-то слать, хоть нули - но получает он строго “бит за бит”). В реализации обмен побайтовый: принял байт, тут же отдал свой.

Вот здесь есть совсем простенький пример взаимодействия мастера и слэйва.

1 лайк

Не влезая в логику, исключительно по синтаксису - в строчках 36-37 возможен выход за границу массива.

Массивы

должны быть описаны как волатильные.

1 лайк