Непонятки с SPI.transfer() - атмега328

Простенький пример - запускаю таймер и в его прерывании отправляю по SPI буфер 16 байт.
Если отправлять циклом “фор” по одному байту - передаются данные, если отправлять буфером - отправляются нули.
Код

#include <SPI.h>
// данные
uint8_t dd[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
volatile uint8_t* d_ptr = dd;
uint8_t datasize = 16;

ISR(TIMER2_COMPA_vect)
  {
   d_ptr = dd;  
// вариант 1 - отправка побайтно
  /*for (int i = 0;i < datasize;i++) {
    SPI.transfer(*(d_ptr++));
    }*/
 // вариант 2 - отправка буфером     
    SPI.transfer(d_ptr, datasize);
  }

 
void setup() {
  // put your setup code here, to run once:

  SPI.begin(); //Initialize the SPI port.
  SPI.setBitOrder(MSBFIRST); // Set the SPI bit order
  SPI.setDataMode(SPI_MODE0); //Set the  SPI data mode 0
  SPI.setClockDivider(SPI_CLOCK_DIV8); 
  
    uint8_t TC2_prescaler = ((1 << CS22) | (1 << CS21) | (0 << CS20));
    uint8_t TC2_top = 200;
    TCCR2A = (1 << COM2B1) | (0 << COM2B0) | (1 << WGM21) | (1 << WGM20); // OC2B non-invert, Fast PWM mode
    TCCR2B = 0;
    OCR2A = TC2_top;
    OCR2B = (100 * TC2_top) / 255;
    TCCR2B = TC2_prescaler;
    TIMSK2 = (1 << OCIE2A); // enable compA irq
  
}

void loop() {
  // put your main code here, to run repeatedly:

}

Arduino IDE 1.8.3
Arduino Nano atmega328

побайтно

буфер

Я так понимаю, что если бы это был глюк в либе SPI - об этом давно было бы известно. Значит остается вариант, что я где-то накосячил. Вопрос где?

Прошу прощения, чуть переврал код при копировании. Сейчас исправлено.

ты с дуба рухнул?
спай отсылку в прерываниях делать, только флаг поднять, остальные дела в лупе решать, при этом запрети рекурсию.

У меня 1.8.19 выдаёт как раз на эту тему

C:\Users\123\Documents\ARDUINO\MYPROJ\Spi_b707\Spi_b707.ino: In function ‘void __vector_7()’:
C:\Users\123\Documents\ARDUINO\MYPROJ\Spi_b707\Spi_b707.ino:15:33: warning: invalid conversion from ‘volatile void*’ to ‘void*’ [-fpermissive]
SPI.transfer(d_ptr, datasize);
^
In file included from C:\Users\123\Documents\ARDUINO\MYPROJ\Spi_b707\Spi_b707.ino:1:0:
C:\Program Files\Arduino\hardware\arduino\avr\libraries\SPI\src/SPI.h:244:22: note: initializing argument 1 of ‘static void SPIClass::transfer(void*, size_t)’
inline static void transfer(void *buf, size_t count) {
^~~~~~~~

В побайтном варианте - не ругается

не на эту

На эту, на эту. Оно же тебе прямо “в лоб” говорит (о чем выше xDriver писал):

возможно

почему нет? оба метода не используют прерывания:

inline static uint8_t transfer(uint8_t data) {
    SPDR = data;
    asm volatile("nop");
    while (!(SPSR & _BV(SPIF))) ; // wait
    return SPDR;
  } 

inline static void transfer(void *buf, size_t count) {
    if (count == 0) return;
    uint8_t *p = (uint8_t *)buf;
    SPDR = *p;
    while (--count > 0) {
      uint8_t out = *(p + 1);
      while (!(SPSR & _BV(SPIF))) ;
      uint8_t in = SPDR;
      SPDR = out;
      *p++ = in;
    }
    while (!(SPSR & _BV(SPIF))) ;
    *p = SPDR;
  } 

Поэтому совершенно не ясно, почему один работает, а другой нет.

не вижу такого варианта в исходниках SPI

В строке 4 моего кода убери volatile - и предупреждения не будет. Результат работы кода не изменится.

Я вижу что буфер затирается ПРИНЯТЫМИ данными ! Идёт именно обмен - передал получил и сохранил вместо переданного. На первом заходе думаю всё улетает нормально.

1 лайк

Причем и там и там код блокируется ожиданием окончания передачи !

Да, ты абсолютно прав! Так и есть.
Поменял код на такой

#include <SPI.h>
// данные
uint8_t dd[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
uint8_t dd2[16];
volatile uint8_t* d_ptr;
uint8_t datasize = 16;

ISR(TIMER2_COMPA_vect)
  {
  memcpy(dd2, dd, 16);
  d_ptr = dd2;  
  SPI.transfer(d_ptr, datasize);
  }

все работает

ожиданием передачи очередного байта. Это нормально.

Если не ждать - можно делать подготовку следующего байта - можно выиграть до +50% к скорости передачи !!!

SPI.transfer(dd2, datasize);

какую подготовку? это ж один байт в буфере? чего его готовить?

это уже мелочи.
Использовал промежуточный указатель для общности со вторым вариантом, где имя массива не прокатывает.

Если просто ждать:
image

И приблизительно вот так:
image
если не ждать …

А код покажешь?

Хотя у меня все равномерно, таких пропусков как у тебя - нет

С буфером более менее красиво - пара лишних тактов всего. А если байтами или словами … Адафруит цвет передаёт словом - не буфером !!! И первая картинка именно при её отправке …

Тут обсуждалось -
https://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/st7735-160-na-128-podsvetka-barakhlit?page=8#comment-669867

В реальности разница в скорости передачи между буфером и байтами всего порядка 15%. Хотя если посмотреть в исходник метода - становится понятно почему…