(couldn't post more than 3 files in a post ?)

- PulseView4.JPG (39.5 KiB) Viewed 8676 times
As you can see, the data are really simple ! In fact I'm not satisfied. I used a SSD1306 OLED because the protocol is simple. But this display is not supported by Marlin in SPI mode. It has to be done by hand (I posted how to do it on RepRap forum). Wish I could emulate the universal I²C 12864 LCD, as it is always supported. But for now, it is too complicated (and communication with this display is bidirectionnal). Also, I²C could allow for displaying Marlin UI in an Octoprint plugin. Can't believe nobody did it !!!
I²C is the way to go on a RasPi as there's no support for SPI slave mode. Master only.
The code, that works (not sure how and why) ; there was a lot of trial and errors, and I don't understand all the details. Was so focused on, that I just realized stevestrong was on this thread !
As you can see I finally cheat, reading the second page of a 2 page buffer.
Code: Select all
/*
Name: SpiSlave32.ino
Created: 15/12/2020 22:44:11
Author: Y@@J and many others...
SPI SSD1306 emulator
https://web.archive.org/web/20190316162926/https://www.stm32duino.com/viewtopic.php?f=9&t=2846&start=10
https://github.com/rogerclarkmelbourne/Arduino_STM32/pull/503
http://stm32duinoforum.com/forum/viewtopic_f_14_t_3527_start_10.html
https://web.archive.org/web/20190316162929/https://www.stm32duino.com/viewtopic.php?f=9&t=2846
https://web.archive.org/web/20190316155730/https://github.com/stevstrong/Audio-sample/blob/master/stm32/STM32_ADC_Host.ino
https://github.com/stevstrong/Audio-sample/blob/master/stm32/STM32_ADC_Host.ino
https://www.stm32duino.com/viewtopic.php?f=9&t=2846&start=10
https://www.stm32duino.com/viewtopic.php?f=2&t=3
Hard time to make SPI work in slave mode :
https://web.archive.org/web/20190316162926/https://www.stm32duino.com/viewtopic.php?f=9&t=2846&start=10
https://sparklogic.ru/libraries-hardware/hard-time-to-make-spi-work-in-slave-mode.html
"Without seing your code, i can only suggest to
- disable DMA,
- read the SPI data register
- setup and enable the next DMA transfer.
The data to be sent must be valid before you do the last step, because the DMA
will deliver the first byte to transmit to SPI when the SPI is enabled (triggered by the TXE flag)."
(stevestrong, last post on the thread)
*/
#include <SPI.h>
/*
// CLK : scope -> 1MHz
/!\ PA4 PA5 PA6 PA7 NOT 5V TOLERANT /!\
pins STM32 log.analyzer
#define PIN_SCK PA5 // SCL BLACK SCK
#define PIN_MOSI PA7 // SDA BROWN MOSI
#define PIN_SS PA4 // AKA CS, A0 YELLOW Chip Select LOW on page, HIGH between pages
#define PIN_MISO PA6 //
*/
#define CLK_DIVIDER SPI_CLOCK_DIV16
// Rx buffer
// 8 pages : 8*128 pixels = 8 * 3 bytes (= command) + 8 * 128 bytes (= pixels) = 144
#define FRAME_SIZE (8 * (3 + 128))
#define BUFFER_SIZE (2 * FRAME_SIZE)
volatile uint8_t rx_buffer[2*FRAME_SIZE];
// bitmap
volatile uint8_t bitmap[8][128] = { 0 };
// interruption routine
volatile bool dma_transfer_complete = false;
volatile bool dma_transfer_error = false;
void rxDMAirq(void)
{
//DMA_TRANSFER_COMPLETE, // Transfer is complete.
//DMA_TRANSFER_HALF_COMPLETE, // Transfer is half complete.
//DMA_TRANSFER_ERROR, // Error occurred during transfer.
//DMA_TRANSFER_DME_ERROR, // Direct mode error occurred during transfer.
//DMA_TRANSFER_FIFO_ERROR, // FIFO error occurred during transfer.
uint32_t dma_isr = dma_get_isr_bits(DMA1, DMA_CH2);
//if (dma_get_irq_cause(DMA1, DMA_CH2) == DMA_TRANSFER_COMPLETE)
//{
// Serial.println("error : DMA_TRANSFER_COMPLETE");
// //dma_transfer_error = true;
//}
dma_transfer_complete = true;
}
// decoding ; quickly adapted from the AVR "on the fly" decoding IRQ code
// to be rewritten with page memcpy()
bool rebuidBitmap()
{
if (rx_buffer[0] != 0x10 || rx_buffer[1] != 00 || rx_buffer[2] != 0xB0)
return false;
for (int i = 0; i < FRAME_SIZE; i++)
removeCommandBytes(*(rx_buffer + FRAME_SIZE + i));
return true;
}
void removeCommandBytes(uint8_t dataByte)
{
static uint8_t* ptr = (uint8_t*)bitmap;
static unsigned nFrameBytes = 0; // compteur bytes total : 0-1023
static unsigned nPageBytes = 0; // compteur bytes page : 0-127
static unsigned nCmdBytes = 0; // compteur bytes cmd début page : 0-2
// drop 3 bytes
if (nCmdBytes < 3)
{
nCmdBytes++;
return;
}
*(ptr++) = dataByte;
nFrameBytes++;
nPageBytes++;
// 128 bytes = 1 page was read : reset counters
if (nPageBytes > 127)
nPageBytes = nCmdBytes = 0;
if (nFrameBytes > 1023)
{
// reset before next call
ptr = (uint8_t*)bitmap;
nFrameBytes = nPageBytes = nCmdBytes = 0;
}
}
// setup functions
void setup()
{
Serial.begin(250000);
delay(100);
setupSPI();
setupDMA();
}
void setupDMA(void)
{
dma_init(DMA1); // SPI is on DMA1
spi_rx_dma_disable(SPI.dev()); // Disable in case already enabled
dma_disable(DMA1, DMA_CH2); // Disable in case it was already enabled
dma_detach_interrupt(DMA1, DMA_CH2);
// DMA tube configuration for SPI1 Rx
dma_tube_config rx_tube_cfg =
{
&SPI1->regs->DR, // Source of data
DMA_SIZE_8BITS, // Source transfer size
&rx_buffer, // Destination of data
DMA_SIZE_8BITS, // Destination transfer size
BUFFER_SIZE, // Number of data to transfer
// Flags :
DMA_CFG_DST_INC | // auto increment destination address
DMA_CFG_CIRC | // circular buffer
DMA_CFG_CMPLT_IE | // set tube full IRQ
DMA_CCR_PL_VERY_HIGH, // very high priority
0, // Un-used
DMA_REQ_SRC_SPI1_RX // Hardware DMA request source
};
// SPI1 Rx is channel 2
int ret_rx = dma_tube_cfg(DMA1, DMA_CH2, &rx_tube_cfg);
if (ret_rx != DMA_TUBE_CFG_SUCCESS)
{
while (1)
{
Serial.print("Rx DMA configuration error: ");
Serial.println(ret_rx, HEX);
Serial.println("Reset is needed!");
delay(100);
}
}
spi_rx_reg(SPI.dev()); // Clear RX register in case we already received SPI data
dma_attach_interrupt(DMA1, DMA_CH2, rxDMAirq); // Attach interrupt to catch end of DMA transfer
dma_enable(DMA1, DMA_CH2); // Rx : Enable DMA configurations
spi_rx_dma_enable(SPI.dev()); // Tx : SPI DMA requests for Rx and Tx
}
void setupSPI(void)
{
// MOSI, MISO, SCK PINs are set by the library
pinMode(BOARD_SPI_DEFAULT_SS, INPUT); // SS
///// ?????
SPI.setModule(1);
// "The clock value is not used, SPI1 is selected by default" ?????????????
//
//SPISettings spiSettings(CLK_DIVIDER, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT);
SPISettings spiSettings(0, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT);
SPI.beginTransactionSlave(spiSettings);
// Clear RX register in case we already received SPI data
spi_rx_reg(SPI.dev()); // ???
}
// loop() and display
void loop()
{
if (dma_transfer_complete)
{
if (rebuidBitmap())
printFrame();
dma_transfer_complete = false;
}
delay(500);
}
void printFrame()
{
for (int page = 0; page < 8; page++)
{
// 128 blocs de 8 lignes ; bits de poids faible = ligne du haut
for (int line = 0; line < 8; line++)
{
for (int col = 0; col < 128; col++)
{
uint8_t val = bitmap[page][col];
if (val & (1 << line))
Serial.print('#');
else
Serial.print(' ');
}
Serial.println();
}
}
for (int i = 0; i < 128; i++)
Serial.print('X');
Serial.println();
}