emulating a OLED display on a VGA and/or a LCD monitor

Post here all questions related to LibMaple core if you can't find a relevant section!
feluga
Posts: 64
Joined: Wed Mar 18, 2020 2:50 am

Re: Looking for a suitable SPI slave example/tutorial

Post by feluga »

Y@@J wrote: Thu Dec 17, 2020 6:00 pm I have the BlueVGA demo running, and new problem : it runs (with some glitches) on an old LCD monitor, but not on the 52Pi LCD (https://wiki.52pi.com/index.php/7-Inch- ... U:_EP-0084) : it cannot "read" the signal. With TVOut (composite), the demo worked with NTSC only. And no knowledge about TV/Video ! Maybe have to make a VGA to composite adapter, and see what happens.

Will see for power up chronology when I get a VGA display : can't test at the moment, and have to learn BlueVGA (or other lib)
A few important points about BlueVGA:
1- It relies on using PC13, PC14, PC15 at a high speed. Only original STM32F103 can do it. No clone is capable.
2- BlueVGA generates 640x480@60Hz standard signal. Maybe your 52Pi LCD can't receive a sign in this format.
3- Check on https://github.com/RoCorbera/BlueVGA/issues/4 -- it talks about old VGA monitors and issues with Bluepill clones, as well as a potential work around using PB15, PB14 and PB13 instead of PC15, PC14 and PC13.
Y@@J
Posts: 57
Joined: Mon Sep 28, 2020 8:51 pm

Re: Looking for a suitable SPI slave example/tutorial

Post by Y@@J »

I'm aware of the issue about fake bluepills and the resolution. This is why I used a BluePill with a legit STM32 (with 128K) and an old monitor as reference. The 52Pi LCD does not support VGA 640x480. It is a dead end. Now considering transfering the data over the 2nd SPI to the RasPi as master, and make it display the images through its GPU.
feluga
Posts: 64
Joined: Wed Mar 18, 2020 2:50 am

Re: Looking for a suitable SPI slave example/tutorial

Post by feluga »

Y@@J wrote: Fri Dec 18, 2020 9:27 pm Now considering transfering the data over the 2nd SPI to the RasPi as master, and make it display the images through its GPU.
The Rasp PI can control the 3d Printer and display everything you need on a HDMI port.
Going this way I see no point in using Marlin anymore.

This may help you in using Rasp Pi:
https://hackaday.com/2017/12/26/fast-3d ... you-think/
https://www.klipper3d.org/
https://github.com/KevinOConnor/klipper ... ommands.md
https://github.com/KevinOConnor/klipper
Y@@J
Posts: 57
Joined: Mon Sep 28, 2020 8:51 pm

Re: Looking for a suitable SPI slave example/tutorial

Post by Y@@J »

Yes, but I'm a Marlin fanboy ! Such a fanboy that I never installed Klipper !
This project is a challenge. Still have to learn SPI on RasPi, OctoPrint development, and come back to STM32 for bitmaps over SPI. Months of fun.
Most of all, I'm used to Marlin, and when I use my printer, it has to do what I need immediatly. It's a machine tool, as are my lathes, my milling machine, or my bandsaw. The printer works with Marlin, and I'm happy with Marlin.
I would probably have tested Klipper if I had a fast delta. But it's a cartesian.

The cleaned code :
The decoding function now takes 30µs instead of 480µs.

Code: Select all

/*
 Name:		SpiSlave32.ino
 Created:	15/12/2020 22:44:11
 Author:	Y@@J and many others...

	SPI sniffer for the SSD1306/SSD1309 ; for marlin UI, u8glib
	rebuilds the bitmap, displays the data as ASCII art if __SERIAL_DEBUG

	thread on stm32duino.com :
	https://www.stm32duino.com/viewtopic.php?f=47&t=815&e=1&view=unread#unread
	
	usefull links :
	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
	https://sparklogic.ru/libraries-hardware/hard-time-to-make-spi-work-in-slave-mode.html

	SSD1306-9			SPI			STM32

	SCL					SCLK		PA5			/!\	NOT 5V TOLERANT /!\
	SDA					MOSI		PA7			/!\	NOT 5V TOLERANT /!\
	RES					N/A			not used
	DC					N/A			not used
	SS					SS			PA4			/!\	NOT 5V TOLERANT /!\

	SS : LOW for pages, including commands ; HIGH between pages and between frames
	DC : LOW for commands only

	SPI data :

	1 SPI frame = 8 pages
	1 page = 128 pixels
	1 page : 3 command bytes + 128 bytes (= 8 lines on the display)
	1 command byte = 0x10 0x00 0XBn , n = page # (0...7)
	total : 8 pages ; 8 * 131 = 1048 bytes, CLK @ 1MHz

	SPI : SPI1, DMA1, DMA_CH2, SPI_MODE0, DATA_SIZE_8BIT
*/

#include <SPI.h>

#define __SERIAL_DEBUG
#define BAUD_RATE 250000

// data (SSD1306/SSD1309) : 1 frame = 8 pages, 1 page = 3 command bytes + 128 bytes, 1 page = 8 graphic lineS
#define GFX_PAGE_COUNT			8
#define	GFX_LINES_PER_PAGE		8
#define GFX_CMD_SIZE			3
#define GFX_PAGE_SIZE			128
#define SPI_PAGE_SIZE			(GFX_CMD_SIZE + GFX_PAGE_SIZE)
#define SPI_FRAME_SIZE			(GFX_PAGE_COUNT * SPI_PAGE_SIZE)

// SPI Rx buffer
#define DMA_BUFFER_SIZE (2 * SPI_FRAME_SIZE) // for 2 frames (DMA tube configuration with DMA_CFG_CIRC)
volatile uint8_t rx_buffer[DMA_BUFFER_SIZE] = { 0 };

// bitmap
volatile uint8_t bitmap[GFX_PAGE_COUNT][GFX_PAGE_SIZE] = { 0 };

// ISR
volatile bool dma_transfer_complete = false;

void rxDMAirq(void)
{
	dma_irq_cause cause = dma_get_irq_cause(DMA1, DMA_CH2);

	if (cause == DMA_TRANSFER_COMPLETE) // also resets DMA bits
	{
		dma_transfer_complete = true;
		return;
	}

#ifdef __SERIAL_DEBUG
	Serial.print("rxDMAirq error : ");
	Serial.println(cause);
#endif
}

// decoding function : copy SPI buffer to bitmap, removes the command bytes
// 30µs (vs 480µs with the previous one)
bool rebuildBitmap()
{
	for (int i = 0; i < GFX_PAGE_COUNT; i++)
	{
		uint8_t* pRxBuf = (uint8_t*)rx_buffer + SPI_FRAME_SIZE + i * SPI_PAGE_SIZE;
		uint16_t cmd = *(uint16_t*)pRxBuf; // command bytes
		uint8_t pageNum = *(uint8_t*)(pRxBuf + 2);

		// test 1st 3 bytes (= command bytes) of each frame ; 0x10 0x00 0xBn , n = frame #
		if(cmd != 0x10 || pageNum != i + 0xB0)
			return false; // error, not in sync, etc.

		// copy pages to bitmap, drop command bytes
		memcpy((uint8_t*)&bitmap + i * GFX_PAGE_SIZE, pRxBuf + GFX_CMD_SIZE, GFX_PAGE_SIZE);
	}
	return true;
}

// setup functions
void setup()
{
#ifdef __SERIAL_DEBUG
	Serial.begin(BAUD_RATE);
#endif
	delay(100);
	setupSPI();
	setupDMA();
}

void setupSPI(void)
{
	// MOSI, MISO, SCK pins are set by the library
	pinMode(BOARD_SPI_DEFAULT_SS, INPUT);
	
	// number of the SPI peripheral : ????
	SPI.setModule(1);

	// The clock value is not used, SPI1 is selected by default
	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());
}

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
		DMA_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,						// reserved
		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)
		{
#ifdef __SERIAL_DEBUG
			Serial.print("Rx DMA configuration error: ");
			Serial.println(ret_rx, HEX);
			Serial.println("Reset is needed!");
			delay(100);
#else
			// will trigger watchdog... or not...
#endif
		}
	}

	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 
}

// loop() and display
void loop()
{
	if (dma_transfer_complete)
	{
#ifdef __SERIAL_DEBUG
		if (rebuildBitmap())
			printFrame();
#else
		rebuildBitmap();
#endif
		dma_transfer_complete = false;
	}

	delay(500);
}

#ifdef __SERIAL_DEBUG

// Serial.print() bitmap as ASCII art
void printFrame()
{
	for (int page = 0; page < 8; page++)
	{
		for (int line = 0; line < GFX_LINES_PER_PAGE; line++)
		{
			for (int col = 0; col < GFX_PAGE_SIZE; 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();
}

#endif

// END OF FILE
Y@@J
Posts: 57
Joined: Mon Sep 28, 2020 8:51 pm

Re: Looking for a suitable SPI slave example/tutorial

Post by Y@@J »

Chances are the learning curve for OctoPrint and the GPIO will be very flat...
The project must go on ! As I can't use BlueVGA because of true VGA (640x480) not being supported by the LCD, I started to make a "GPU" from a Nano with TVOut library.
For now, it's a static image, converted from the SPI buffer to the TVOut format, and saved as C code. Good results. The conversion takes 4.5ms, it will be good enough for 30fps (NTSC).
DSC_7844.JPG
DSC_7844.JPG (70.8 KiB) Viewed 4894 times
Next step will be sending the data over a parallel bus, and the data can now be written directly into the vieo memory (1024 bytes : double buffering is not an option !)
Y@@J
Posts: 57
Joined: Mon Sep 28, 2020 8:51 pm

BluePill as I²C slave with Roger's Core

Post by Y@@J »

Hi,

AFAIK, Roger's core offers no support for STM32 as I²C slave. Could you confirm ? (or not...).
(official core or coding low level serial protocols are not options !)
Is parallel transfer the only option (SPI being not usable, and 1-wire being too slow)
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: BluePill as I²C slave with Roger's Core

Post by ag123 »

why don't you try hardware serial i.e. uart, e.g. Serial1, Serial2 etc.
Serial can be left for usb serial.
that should work quite well, Serial support in libmaple is mature. i fixed up the code once and made it drop framing (i.e. bytes with corrupted stop bits) or parity errors. that made it work in situations like programming another device over serial where you may press reset on the (other) target device. that often creates some spurious bytes due to the reset.
And serial is bidirectional, there isn't a need to be 'slave'
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: BluePill as I²C slave with Roger's Core

Post by stevestrong »

Roger's core DOES support I2C slave, please have a look to: https://github.com/rogerclarkmelbourne/ ... e/examples
Y@@J
Posts: 57
Joined: Mon Sep 28, 2020 8:51 pm

Re: BluePill as I²C slave with Roger's Core

Post by Y@@J »

Good news !

Don't know why I was thinking Roger's Core didn't support I2C slave...

Thanks for the answers.
ag123 wrote: Mon Dec 21, 2020 9:39 pm why don't you try hardware serial i.e. uart, e.g. Serial1, Serial2 etc.
Serial can be left for usb serial.
that should work quite well, Serial support in libmaple is mature. i fixed up the code once and made it drop framing (i.e. bytes with corrupted stop bits) or parity errors. that made it work in situations like programming another device over serial where you may press reset on the (other) target device. that often creates some spurious bytes due to the reset.
And serial is bidirectional, there isn't a need to be 'slave'
The problem with serial (UART and probably I²C either) is not the STM32, it is the thing at the other end : a Nano running TVOut. Data can be transfered only during the vertical blanking interval, or I get sync problems (tried softaware serial, just to see).
I measured with the scope : 60fps, interval = 5ms. A guy did that (I²C) : https://nootropicdesign.com/hackvision/ ; he connects a Nunchuk (I²C), and I'm pretty sure he activates/read/deactivates during blanking. Have to read the sources further.
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: BluePill as I²C slave with Roger's Core

Post by stevestrong »

Which throuhput do you need?

The fastest transfer between devices would be parallel, so if you have 4 or better 8 bits free then you can make it real fast.
The next fastest way is SPI, it can go up to 32Mbps, which yields 4MBps. This would be the best solution as it involves mostly HW working in the background not overloading the DMA (on slave side) as in the first case.
Then comes serial, STM32F1x it can go up to 4.5Mbps (~0.58MBps).
I2C can reach 1Mbps in best case and does not support DMA.

So my favorite would be SPI. And I do not understand why you say that is not usable.
Then I see serial (hardware, not software) as real alternative, the Nano can go up to 1Mbps (with 16MHz clock), so it beats the I2C because the transmission/reception can be buffered and then it works in the background while the MCU does other things, on the slave side this is of huge advantage. With I2C you block the slave MCU till you get all the data.
Post Reply

Return to “General discussion”