Page 1 of 1

Weird artifacts on dot matrix display

Posted: Sat Jun 08, 2019 9:28 pm
by steven1578
Hi guys,

I have been trying to make a scoreboard existing of 28 (7x4) P12 dot matrix displays.
I have had this same setup running with a arduino mega and it was running just fine however I would like to run it on my pi so that I connect it to the internet easily. I tried using the same library (freetronics DMD2 https://github.com/freetronics/DMD2) as used on the arduino mega, simplified it and tried making it compatible with RPI.

The problem however is shown in the video:
https://www.youtube.com/watch?v=8G8Ydv_b1eE

My code is as follows:
DMD.h

Code: Select all

#ifndef DMD2_H
#define DMD2_H

#include <wiringPi.h>
#include <stddef.h>
#include <string.h>
#include <cstdlib>
#include <iostream>

using namespace std;

// Dimensions of a single display
const unsigned int PANEL_WIDTH = 32;
const unsigned int PANEL_HEIGHT = 16;

// Clamp a value between two limits
template<typename T> static inline void clamp(T& value, T lower, T upper) {
	if (value < lower)
		value = lower;
	else if (value > upper)
		value = upper;
}

// Swap A & B "in place" (well, with a temp variable!)
template<typename T> static inline void swap(T& a, T& b)
{
	T tmp(a); a = b; b = tmp;
}

// Check a<=b, and swap them otherwise
template<typename T> static inline void ensureOrder(T& a, T& b)
{
	if (b < a) swap(a, b);
}

extern const unsigned char DMD_Pixel_Lut[];


//Pixel/graphics writing modes
enum DMDGraphicsMode {
	GRAPHICS_OFF, // unconditionally off (pixel turns off)
	GRAPHICS_ON, //unconditionally on (pixel turns on, the usual default for drawing)
	GRAPHICS_INVERSE, // on if was going to set to off
	GRAPHICS_OR, // add to pixels already on
	GRAPHICS_NOR, // subtract from pixels already on, don't turn any new ones on
	GRAPHICS_XOR, // swap on/off state of pixels
	GRAPHICS_NOOP // No-Op, ie don't actually change anything
};

// Return the inverse/"clear" version of the given mode
// ie for normal pixel-on modes, the "clear" is to turn off.
// for inverse mode, it's to turn on.
// for all other modes, this is kind of meaningless so we return a no-op
inline static DMDGraphicsMode inverseMode(DMDGraphicsMode mode) {
	switch (mode) {
	case GRAPHICS_ON:
		return GRAPHICS_OFF;
	case GRAPHICS_INVERSE:
		return GRAPHICS_ON;
	default:
		return GRAPHICS_NOOP;
	}
};


class DMDFrame
{
public:
	DMDFrame(unsigned char pixelsWide, unsigned char pixelsHigh);
	DMDFrame(const DMDFrame& source);
	virtual ~DMDFrame();

	// Set a single LED on or off
	void setPixel(unsigned int x, unsigned int y, DMDGraphicsMode mode = GRAPHICS_ON);

	// Get status of a single LED
	bool getPixel(unsigned int x, unsigned int y);

	//Print display
	void printDisplay();

	// Move a region of pixels from one area to another
	void movePixels(unsigned int from_x, unsigned int from_y,
		unsigned int to_x, unsigned int to_y,
		unsigned int width, unsigned int height);

	// Extract a sub-region of the frame as a new frame
	DMDFrame subFrame(unsigned int left, unsigned int top, unsigned int width, unsigned int height);

	// Copy the contents of another frame back into this one at the given location
	void copyFrame(DMDFrame& from, unsigned int left, unsigned int top);

	// Fill the screen on or off
	void fillScreen(bool on);
	inline void clearScreen() { fillScreen(false); };

	// Drawing primitives
	void drawLine(int x1, int y1, int x2, int y2, DMDGraphicsMode mode = GRAPHICS_ON);
	void drawCircle(unsigned int xCenter, unsigned int yCenter, int radius, DMDGraphicsMode mode = GRAPHICS_ON);
	void drawBox(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, DMDGraphicsMode mode = GRAPHICS_ON);
	void drawFilledBox(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, DMDGraphicsMode mode = GRAPHICS_ON);

	// Scrolling & marquee support
	void scrollY(int scrollBy);
	void scrollX(int scrollBy);
	void marqueeScrollX(int scrollBy);
	void marqueeScrollY(int scrollBy);

	void swapBuffers(DMDFrame& other);

	const unsigned char width; // in pixels
	const unsigned char height; // in pixels
protected:
	volatile unsigned char* bitmap;
	unsigned char row_width_bytes; // width in bitmap, bit-per-pixel rounded up to nearest byte
	unsigned char height_in_panels; // in panels

	unsigned char* font;

	// total bytes in the bitmap
	inline size_t bitmap_bytes() {
		return row_width_bytes * height;
	}
	// controller sees all panels as end-to-end, so bitmap arranges it that way
	inline size_t unified_width_bytes() {
		return row_width_bytes * height_in_panels;
	}

	// Panels seen as stretched out in a row for purposes of finding index
	inline int pixelToBitmapIndex(unsigned int x, unsigned int y) {
		unsigned char panel = (x / PANEL_WIDTH) + ((width / PANEL_WIDTH) * (y / PANEL_HEIGHT));
		x = (x % PANEL_WIDTH) + (panel * PANEL_WIDTH);
		y = y % PANEL_HEIGHT;
		int res = x / 8 + (y * unified_width_bytes());
		return res;
	}
	template<typename T> inline void clamp_xy(T& x, T& y) {
		clamp(x, (T)0, (T)width - 1);
		clamp(y, (T)0, (T)width - 1);
	}
};

class DMD : public DMDFrame
{
public:
	DMD(unsigned char panelsWide, unsigned char panelsHigh, unsigned char pin_noe, unsigned char pin_a, unsigned char pin_b, unsigned char pin_sck,
		unsigned char pin_clk, unsigned char pin_r_data);
	void beginNoTimer();
	void scanDisplay();
	void softSPITransfer(unsigned char data);
	inline void setBrightness(unsigned char b){
		brightness = b;
	}
protected:
	void writeSPIData(volatile unsigned char* rows[4], const int rowsize);
private:
	volatile unsigned char scan_row;
	unsigned char pin_noe;
	unsigned char pin_a;
	unsigned char pin_b;
	unsigned char pin_sck;
	unsigned char pin_clk;
	unsigned char pin_r_data;
	unsigned char brightness;
};
#endif
DMD.cpp

Code: Select all

#include "DMD.h"

DMD::DMD(unsigned char panelsWide, unsigned char panelsHigh, unsigned char pin_noe, unsigned char pin_a, unsigned char pin_b, unsigned char pin_sck, unsigned char pin_clk, unsigned char pin_r_data)
	:
	DMDFrame(panelsWide* PANEL_WIDTH, panelsHigh* PANEL_HEIGHT),
	scan_row(0),
	pin_noe(pin_noe),
	pin_a(pin_a),
	pin_b(pin_b),
	pin_sck(pin_sck),
	pin_clk(pin_clk),
	pin_r_data(pin_r_data),
	brightness(255)
{
}

void DMD::beginNoTimer()
{
	digitalWrite(pin_clk, LOW);
	pinMode(pin_clk, OUTPUT);

	digitalWrite(pin_r_data, LOW);
	pinMode(pin_r_data, OUTPUT);

	digitalWrite(pin_noe, LOW);
	pinMode(pin_noe, OUTPUT);

	digitalWrite(pin_a, LOW);
	pinMode(pin_a, OUTPUT);

	digitalWrite(pin_b, LOW);
	pinMode(pin_b, OUTPUT);

	digitalWrite(pin_sck, LOW);
	pinMode(pin_sck, OUTPUT);

	clearScreen();
	scanDisplay();
}

void DMD::scanDisplay()
{
	// Rows are send out in 4 blocks of 4 (interleaved), across all panels

	int rowsize = unified_width_bytes();

	volatile unsigned char* rows[4] = { 
	  bitmap + (scan_row + 0) * rowsize,
	  bitmap + (scan_row + 4) * rowsize,
	  bitmap + (scan_row + 8) * rowsize,
	  bitmap + (scan_row + 12) * rowsize,
	};

	writeSPIData(rows, rowsize);

	digitalWrite(pin_noe, LOW);
	digitalWrite(pin_sck, HIGH); // Latch DMD shift register output
	digitalWrite(pin_sck, LOW); // (Deliberately left as digitalWrite to ensure decent latching time)

	digitalWrite(pin_a, scan_row & 0x01);
	digitalWrite(pin_b, scan_row & 0x02);
	scan_row = (scan_row + 1) % 4;

	if (brightness == 255)
		digitalWrite(pin_noe, HIGH);
	else
		analogWrite(pin_noe, brightness);
}


void DMD::softSPITransfer(unsigned char data) {
	for (unsigned char i = 8; i > 0; i--) {
		unsigned char bit = (data & (1 << i));
		digitalWrite(pin_r_data, bit);
		digitalWrite(pin_clk, 1);
		digitalWrite(pin_clk, 0);
	}
}

void DMD::writeSPIData(volatile unsigned char* rows[4], const int rowsize)
{
	for (int i = 0; i < rowsize; i++) {
		softSPITransfer(*(rows[3]++));
		softSPITransfer(*(rows[2]++));
		softSPITransfer(*(rows[1]++));
		softSPITransfer(*(rows[0]++));
	}
}


DMDFrame.cpp

Code: Select all

#include "DMD.h"

DMDFrame::DMDFrame(unsigned char pixelsWide, unsigned char pixelsHigh)
	:
	width(pixelsWide),
	height(pixelsHigh)
{
	row_width_bytes = (pixelsWide + 7) / 8;
	height_in_panels = (pixelsHigh + PANEL_HEIGHT - 1) / PANEL_HEIGHT;
	bitmap = (unsigned char*)malloc(bitmap_bytes());
	memset((void*)bitmap, 0xFF, bitmap_bytes());
}

DMDFrame::DMDFrame(const DMDFrame& source) :
	width(source.width),
	height(source.height),
	row_width_bytes(source.row_width_bytes),
	height_in_panels(source.height_in_panels)
{
	bitmap = (unsigned char*)malloc(bitmap_bytes());
	memcpy((void*)bitmap, (void*)source.bitmap, bitmap_bytes());
}

DMDFrame::~DMDFrame()
{
	free((void*)bitmap);
}

void DMDFrame::swapBuffers(DMDFrame& other)
{
	volatile unsigned char* temp = other.bitmap;
	other.bitmap = this->bitmap;
	this->bitmap = temp;
}

// Set a single LED on or off. Remember that the pixel array is inverted (bit set = LED off)
void DMDFrame::setPixel(unsigned int x, unsigned int y, DMDGraphicsMode mode)
{
	if (x >= width || y >= height)
		return;

	int byte_idx = pixelToBitmapIndex(x, y);
	unsigned char bit = DMD_Pixel_Lut[x & 0x07];
	switch (mode) {
	case GRAPHICS_ON:
		bitmap[byte_idx] &= ~bit; // and with the inverse of the bit - so
		break;
	case GRAPHICS_OFF:
		bitmap[byte_idx] |= bit; // set bit (which turns it off)
		break;
	case GRAPHICS_OR:
		bitmap[byte_idx] = ~(~bitmap[byte_idx] | bit);
		break;
	case GRAPHICS_NOR:
		bitmap[byte_idx] = (~bitmap[byte_idx] | bit);
		break;
	case GRAPHICS_XOR:
		bitmap[byte_idx] ^= bit;
		break;
	case GRAPHICS_INVERSE:
	case GRAPHICS_NOOP:
		break;
	}
}

bool DMDFrame::getPixel(unsigned int x, unsigned int y)
{
	if (x >= width || y >= height)
		return false;
	int byte_idx = pixelToBitmapIndex(x, y);
	unsigned char bit = DMD_Pixel_Lut[x & 0x07];
	bool res = !(bitmap[byte_idx] & bit);
	return res;
}

void DMDFrame::printDisplay()
{
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			std::cout << (int)getPixel(j, i) << ", ";
		}
		std::cout << std::endl;
	}
}

void DMDFrame::movePixels(unsigned int from_x, unsigned int from_y,
	unsigned int to_x, unsigned int to_y,
	unsigned int width, unsigned int height)
{
	// NB: This implementation is actually a copy-erase so
	// it uses more RAM than a real move implementation would
	// do (however bypasses issues around overlapping regions.)

	if (from_x >= this->width || from_y >= this->height
		|| to_x >= this->width || to_y >= this->height)
		return;

	DMDFrame to_move = this->subFrame(from_x, from_y, width, height);
	this->drawFilledBox(from_x, from_y, from_x + width - 1, from_y + height - 1, GRAPHICS_OFF);
	this->copyFrame(to_move, to_x, to_y);
}

DMDFrame DMDFrame::subFrame(unsigned int left, unsigned int top, unsigned int width, unsigned int height)
{
	DMDFrame result(width, height);

	if ((left % 8) == 0 && (width % 8) == 0) {
		// Copying from/to byte boundaries, can do simple/efficient copies
		for (unsigned int to_y = 0; to_y < height; to_y++) {
			unsigned int from_y = top + to_y;
			unsigned int from_end = pixelToBitmapIndex(left + width, from_y);
			unsigned int to_byte = result.pixelToBitmapIndex(0, to_y);
			for (unsigned int from_byte = pixelToBitmapIndex(left, from_y); from_byte < from_end; from_byte++) {
				result.bitmap[to_byte++] = this->bitmap[from_byte];
			}
		}
	}
	else {
		// Copying not from a byte boundary. Slow pixel-by-pixel for now.
		for (unsigned int to_y = 0; to_y < height; to_y++) {
			for (unsigned int to_x = 0; to_x < width; to_x++) {
				bool val = this->getPixel(to_x + left, to_y + top);
				result.setPixel(to_x, to_y, val ? GRAPHICS_ON : GRAPHICS_OFF);
			}
		}
	}

	return result;
}

void DMDFrame::copyFrame(DMDFrame& from, unsigned int left, unsigned int top)
{
	if ((left % 8) == 0 && (from.width % 8) == 0) {
		// Copying rows on byte boundaries, can do simple/efficient copies
		unsigned int to_bottom = top + from.height;
		if (to_bottom > this->height)
			to_bottom = this->height;
		unsigned int to_right = left + from.width;
		if (to_right > this->width)
			to_right = this->width;
		unsigned int from_y = 0;
		for (unsigned int to_y = top; to_y < to_bottom; to_y++) {
			unsigned int to_end = pixelToBitmapIndex(to_right, to_y);
			unsigned int from_byte = from.pixelToBitmapIndex(0, from_y);
			for (unsigned int to_byte = pixelToBitmapIndex(left, to_y); to_byte < to_end; to_byte++) {
				this->bitmap[to_byte] = from.bitmap[from_byte++];
			}
			from_y++;
		}
	}
	else {
		// Copying not to a byte boundary. Slow pixel-by-pixel for now.
		for (unsigned int from_y = 0; from_y < from.height; from_y++) {
			for (unsigned int from_x = 0; from_x < from.width; from_x++) {
				bool val = from.getPixel(from_x, from_y);
				this->setPixel(from_x + left, from_y + top, val ? GRAPHICS_ON : GRAPHICS_OFF);
			}
		}
	}
}

void DMDFrame::fillScreen(bool on)
{
	memset((void*)bitmap, on ? 0 : 0xFF, bitmap_bytes());
}

void DMDFrame::drawLine(int x1, int y1, int x2, int y2, DMDGraphicsMode mode)
{
	int dy = y2 - y1;
	int dx = x2 - x1;
	int stepx, stepy;

	if (dy < 0) {
		dy = -dy;
		stepy = -1;
	}
	else {
		stepy = 1;
	}
	if (dx < 0) {
		dx = -dx;
		stepx = -1;
	}
	else {
		stepx = 1;
	}
	dy = dy * 2;
	dx = dx * 2;

	setPixel(x1, y1, mode);
	if (dx > dy) {
		int fraction = dy - (dx / 2);	// same as 2*dy - dx
		while (x1 != x2) {
			if (fraction >= 0) {
				y1 += stepy;
				fraction -= dx;	// same as fraction -= 2*dx
			}
			x1 += stepx;
			fraction += dy;	// same as fraction -= 2*dy
			setPixel(x1, y1, mode);
		}
	}
	else {
		int fraction = dx - (dy / 2);
		while (y1 != y2) {
			if (fraction >= 0) {
				x1 += stepx;
				fraction -= dy;
			}
			y1 += stepy;
			fraction += dx;
			setPixel(x1, y1, mode);
		}
	}
}

void DMDFrame::drawCircle(unsigned int xCenter, unsigned int yCenter, int radius, DMDGraphicsMode mode)
{
	// Bresenham's circle drawing algorithm
	int x = -radius;
	int y = 0;
	int error = 2 - 2 * radius;
	while (x < 0) {
		setPixel(xCenter - x, yCenter + y, mode);
		setPixel(xCenter - y, yCenter - x, mode);
		setPixel(xCenter + x, yCenter - y, mode);
		setPixel(xCenter + y, yCenter + x, mode);
		radius = error;
		if (radius <= y) error += ++y * 2 + 1;
		if (radius > x || error > y) error += ++x * 2 + 1;
	}
}

void DMDFrame::drawBox(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, DMDGraphicsMode mode)
{
	drawLine(x1, y1, x2, y1, mode);
	drawLine(x2, y1, x2, y2, mode);
	drawLine(x2, y2, x1, y2, mode);
	drawLine(x1, y2, x1, y1, mode);
}

void DMDFrame::drawFilledBox(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, DMDGraphicsMode mode)
{
	for (unsigned int b = x1; b <= x2; b++) {
		drawLine(b, y1, b, y2, mode);
	}
}

void DMDFrame::scrollY(int scrollBy) {
	if (abs(scrollBy) >= height) { // scrolling over the whole display
	  // scrolling will erase everything
		drawFilledBox(0, 0, width - 1, height - 1, GRAPHICS_OFF);
	}
	else if (scrollBy < 0) { // Scroll up
		movePixels(0, -scrollBy, 0, 0, width, height + scrollBy);
		drawFilledBox(0, height + scrollBy, width, height, GRAPHICS_OFF);
	}
	else if (scrollBy > 0) { // Scroll down
		movePixels(0, 0, 0, scrollBy, width, height - scrollBy);
		drawFilledBox(0, 0, width, scrollBy, GRAPHICS_OFF);
	}
}

void DMDFrame::scrollX(int scrollBy) {
	if (abs(scrollBy) >= width) { // scrolling over the whole display!
	  // scrolling will erase everything
		drawFilledBox(0, 0, width - 1, height - 1, GRAPHICS_OFF);
	}
	else if (scrollBy < 0) { // Scroll left
		movePixels(-scrollBy, 0, 0, 0, width + scrollBy, height);
		drawFilledBox(width + scrollBy, 0, width, height, GRAPHICS_OFF);
	}
	else { // Scroll right
		movePixels(0, 0, scrollBy, 0, width - scrollBy, height);
		drawFilledBox(0, 0, scrollBy, height, GRAPHICS_OFF);
	}
}

void DMDFrame::marqueeScrollX(int scrollBy) {
	// Scrolling is basically the same as normal scrolling, but we save/restore the overlapping
	// area in between to create the marquee effect
	scrollBy = scrollBy % width;

	if (scrollBy < 0) { // Scroll left
		DMDFrame frame = subFrame(0, 0, -scrollBy, height); // save leftmost
		movePixels(-scrollBy, 0, 0, 0, width + scrollBy, height); // move
		copyFrame(frame, width + scrollBy, 0); // drop back at right edge
	}
	else { // Scroll right
		DMDFrame frame = subFrame(width - scrollBy, 0, scrollBy, height); // save rightmost
		movePixels(0, 0, scrollBy, 0, width - scrollBy, height); // move
		copyFrame(frame, 0, 0); // drop back at left edge
	}
}

void DMDFrame::marqueeScrollY(int scrollBy) {
	scrollBy = scrollBy % height;

	if (scrollBy < 0) { // Scroll up
		DMDFrame frame = subFrame(0, 0, width, -scrollBy); // save topmost
		movePixels(0, -scrollBy, 0, 0, width, height + scrollBy); // move
		copyFrame(frame, 0, height + scrollBy); // drop back at bottom edge
	}
	else { // Scroll down
		DMDFrame frame = subFrame(0, height - scrollBy, width, scrollBy); // save bottommost
		movePixels(0, 0, 0, scrollBy, width, height - scrollBy); // move
		copyFrame(frame, 0, 0); // drop back at top edge
	}
}

const unsigned char DMD_Pixel_Lut[] = {
  0x80,   //0, bit 7
  0x40,   //1, bit 6
  0x20,   //2. bit 5
  0x10,   //3, bit 4
  0x08,   //4, bit 3
  0x04,   //5, bit 2
  0x02,   //6, bit 1
  0x01    //7, bit 0
};
run.cpp

Code: Select all

#include "DMD.h"

int main()
{
	wiringPiSetupPhys();
	DMD dmd(7, 1, 24, 37, 38, 35, 23, 19);
	dmd.setBrightness(255);
	dmd.beginNoTimer();
	dmd.drawBox(0, 0, 223, 15);
	for (;;)
	{
		dmd.scanDisplay();
	}
	return 0;
}

The only notable difference I can come up with is that I use the wiringPi library to send the bits. Anyone has an idea what causes these weird artifacts? I am using a PSU from an old PC which has a 20A 5V rail so that shouldn't be the problem since this problem also occurs when using 1 display.

Re: Weird artifacts on dot matrix display

Posted: Sat Jun 08, 2019 11:21 pm
by Paeryn
steven1578 wrote:
Sat Jun 08, 2019 9:28 pm
The problem however is shown in the video:
https://www.youtube.com/watch?v=8G8Ydv_b1eE
Could you describe what the problem is as, according to youtube, This video is unavailable?

Re: Weird artifacts on dot matrix display

Posted: Sun Jun 09, 2019 8:46 am
by steven1578
Paeryn wrote:
Sat Jun 08, 2019 11:21 pm
steven1578 wrote:
Sat Jun 08, 2019 9:28 pm
The problem however is shown in the video:
https://www.youtube.com/watch?v=8G8Ydv_b1eE
Could you describe what the problem is as, according to youtube, This video is unavailable?
I'm sorry I put it on private instead of hidden. Can you see it now?
The problem is that leds that are supposed to be off are randomly flickering on/off.

Re: Weird artifacts on dot matrix display

Posted: Thu Jun 20, 2019 1:17 pm
by steven1578
Bumping, I still need help. Anyone has an idea what might be the issue?

Re: Weird artifacts on dot matrix display

Posted: Thu Jun 20, 2019 3:06 pm
by salvato
I've not looked into your code but... the Arduino MEGA, if i'm not wrong, works with 5V output levels while the Raspberry works at 3.3V.
Could this originate the problem ?

Re: Weird artifacts on dot matrix display

Posted: Fri Jun 21, 2019 8:48 pm
by steven1578
salvato wrote:
Thu Jun 20, 2019 3:06 pm
I've not looked into your code but... the Arduino MEGA, if i'm not wrong, works with 5V output levels while the Raspberry works at 3.3V.
Could this originate the problem ?
Perhaps, with python the code seemed too slow. Maybe combined with the speed of C++ the DMD does not receive enough voltage indeed. I will add a level shifter and see if that works.

Re: Weird artifacts on dot matrix display

Posted: Sat Jun 22, 2019 10:41 am
by dsyleixa123
do I undestand you correctly: did it work by Python, even slowly?
If so then I actually doubt that it's a voltage level issue, tbh...

Re: Weird artifacts on dot matrix display

Posted: Sat Jun 22, 2019 10:49 am
by PeterO
My experience (in 'C') with 4 x 32x32 modules in series is that you need to put some short delays in to give the data line time to settle before toggling the clock line.

Code: Select all

// Short delays for bit banging signals.

void settleTime(time)
{
    int i;
    for (i = time; i != 0; --i) {
        asm("");   // force GCC not to optimize this away.
    }
}
Code to load a line ..

Code: Select all

            settleTime(5);
            // Clock one rows worth of pixels into the displaya
            for(n=0;n<4;n++)
            {
                pixelData = &PixelsFL[(n*32*16)+(32*row)];
                for(col=0;col<32;col++)
                {
                    ClearBits(pixelClkMask);  // Clk low
                    SetBits(*pixelData++  & pixelMask);
                    settleTime(15);
                    SetBits(1<<CLK);          // Clk high
                }
            }
            SetBits(1<<STB);   // Strobe
            settleTime(5);
            ClearBits(1<<STB);
            settleTime(5);
            ClearBits(1<<OE);  // turn display on       
            nanosleep(&onTimeL, NULL);   // Fixed on time 

HTH
PeterO

Re: Weird artifacts on dot matrix display

Posted: Sat Jun 22, 2019 11:34 am
by salvato
Probaly also...you should first define that a pin should be used as a digital output and then assign a value:

Code: Select all

void DMD::beginNoTimer()
{
	digitalWrite(pin_clk, LOW);
	pinMode(pin_clk, OUTPUT);

	digitalWrite(pin_r_data, LOW);
	pinMode(pin_r_data, OUTPUT);

	digitalWrite(pin_noe, LOW);
	pinMode(pin_noe, OUTPUT);

	digitalWrite(pin_a, LOW);
	pinMode(pin_a, OUTPUT);

	digitalWrite(pin_b, LOW);
	pinMode(pin_b, OUTPUT);

	digitalWrite(pin_sck, LOW);
	pinMode(pin_sck, OUTPUT);

	clearScreen();
	scanDisplay();
}
should change in:

Code: Select all

void DMD::beginNoTimer()
{
	pinMode(pin_clk, OUTPUT);
	digitalWrite(pin_clk, LOW);

	pinMode(pin_r_data, OUTPUT);
	digitalWrite(pin_r_data, LOW);

	pinMode(pin_noe, OUTPUT);
	digitalWrite(pin_noe, LOW);

	pinMode(pin_a, OUTPUT);
	digitalWrite(pin_a, LOW);

	pinMode(pin_b, OUTPUT);
	digitalWrite(pin_b, LOW);

	pinMode(pin_sck, OUTPUT);
	digitalWrite(pin_sck, LOW);

	clearScreen();
	scanDisplay();
}

Re: Weird artifacts on dot matrix display

Posted: Mon Jul 01, 2019 6:32 pm
by steven1578
dsyleixa123 wrote:
Sat Jun 22, 2019 10:41 am
do I undestand you correctly: did it work by Python, even slowly?
If so then I actually doubt that it's a voltage level issue, tbh...
Yes it did, but when I wrote it in Python it was not able (not even close) to render enough frames per second.

Re: Weird artifacts on dot matrix display

Posted: Mon Jul 01, 2019 6:33 pm
by steven1578
PeterO wrote:
Sat Jun 22, 2019 10:49 am
My experience (in 'C') with 4 x 32x32 modules in series is that you need to put some short delays in to give the data line time to settle before toggling the clock line.

Code: Select all

// Short delays for bit banging signals.

void settleTime(time)
{
    int i;
    for (i = time; i != 0; --i) {
        asm("");   // force GCC not to optimize this away.
    }
}
Code to load a line ..

Code: Select all

            settleTime(5);
            // Clock one rows worth of pixels into the displaya
            for(n=0;n<4;n++)
            {
                pixelData = &PixelsFL[(n*32*16)+(32*row)];
                for(col=0;col<32;col++)
                {
                    ClearBits(pixelClkMask);  // Clk low
                    SetBits(*pixelData++  & pixelMask);
                    settleTime(15);
                    SetBits(1<<CLK);          // Clk high
                }
            }
            SetBits(1<<STB);   // Strobe
            settleTime(5);
            ClearBits(1<<STB);
            settleTime(5);
            ClearBits(1<<OE);  // turn display on       
            nanosleep(&onTimeL, NULL);   // Fixed on time 

HTH
PeterO
Will try and keep you posted, thanks.

Re: Weird artifacts on dot matrix display

Posted: Mon Jul 01, 2019 6:34 pm
by steven1578
salvato wrote:
Sat Jun 22, 2019 11:34 am
Probaly also...you should first define that a pin should be used as a digital output and then assign a value:

Code: Select all

void DMD::beginNoTimer()
{
	digitalWrite(pin_clk, LOW);
	pinMode(pin_clk, OUTPUT);

	digitalWrite(pin_r_data, LOW);
	pinMode(pin_r_data, OUTPUT);

	digitalWrite(pin_noe, LOW);
	pinMode(pin_noe, OUTPUT);

	digitalWrite(pin_a, LOW);
	pinMode(pin_a, OUTPUT);

	digitalWrite(pin_b, LOW);
	pinMode(pin_b, OUTPUT);

	digitalWrite(pin_sck, LOW);
	pinMode(pin_sck, OUTPUT);

	clearScreen();
	scanDisplay();
}
should change in:

Code: Select all

void DMD::beginNoTimer()
{
	pinMode(pin_clk, OUTPUT);
	digitalWrite(pin_clk, LOW);

	pinMode(pin_r_data, OUTPUT);
	digitalWrite(pin_r_data, LOW);

	pinMode(pin_noe, OUTPUT);
	digitalWrite(pin_noe, LOW);

	pinMode(pin_a, OUTPUT);
	digitalWrite(pin_a, LOW);

	pinMode(pin_b, OUTPUT);
	digitalWrite(pin_b, LOW);

	pinMode(pin_sck, OUTPUT);
	digitalWrite(pin_sck, LOW);

	clearScreen();
	scanDisplay();
}
You are correct, thanks. I simply just copied the libraries code as close as possible to figure out the differences and why it was not working properly.

Re: Weird artifacts on dot matrix display

Posted: Sun Jul 14, 2019 9:02 pm
by steven1578
PeterO wrote:
Sat Jun 22, 2019 10:49 am
My experience (in 'C') with 4 x 32x32 modules in series is that you need to put some short delays in to give the data line time to settle before toggling the clock line.

Code: Select all

// Short delays for bit banging signals.

void settleTime(time)
{
    int i;
    for (i = time; i != 0; --i) {
        asm("");   // force GCC not to optimize this away.
    }
}
Code to load a line ..

Code: Select all

            settleTime(5);
            // Clock one rows worth of pixels into the displaya
            for(n=0;n<4;n++)
            {
                pixelData = &PixelsFL[(n*32*16)+(32*row)];
                for(col=0;col<32;col++)
                {
                    ClearBits(pixelClkMask);  // Clk low
                    SetBits(*pixelData++  & pixelMask);
                    settleTime(15);
                    SetBits(1<<CLK);          // Clk high
                }
            }
            SetBits(1<<STB);   // Strobe
            settleTime(5);
            ClearBits(1<<STB);
            settleTime(5);
            ClearBits(1<<OE);  // turn display on       
            nanosleep(&onTimeL, NULL);   // Fixed on time 

HTH
PeterO
I tried it but had no effect. I have tried using Cython to speed up my python code and it looks as follows: https://www.youtube.com/watch?v=jotjAkaeveM

The framerate is way better now, but still not as high as desired.

I still wonder why my C++ code messes up so bad. I have tried playing with the refreshrate and noticed that it fails even with very low fps. This is 5fps: https://www.youtube.com/watch?v=MV2SG6eu3Ko

Even with this speed it tends to mess up so I got a feeling that it might be some errors in the code.

Re: Weird artifacts on dot matrix display

Posted: Mon Jul 29, 2019 3:56 pm
by steven1578
I have pinpointed the issue down to code:

Original library code:

Code: Select all

static inline __attribute__((always_inline)) void softSPITransfer(uint8_t data, volatile port_reg_t *data_port, port_reg_t data_mask, volatile port_reg_t *clk_port, port_reg_t clk_mask) {
  // Emulate a single byte SPI transfer using software GPIO. Overall this is actually only marginally slower than normal SPI on AVR.
  //
  // MSB first, data captured on rising edge
  for(uint8_t bit = 0; bit < 8; bit++) {
    if(data & (1<<7))
      *data_port |= data_mask;
    else
      *data_port &= ~data_mask;
    *clk_port |= clk_mask;
    data <<= 1;
    *clk_port &= ~clk_mask;
  }
}

void SoftDMD::writeSPIData(volatile uint8_t *rows[4], const int rowsize)
{
  /* Write out 4 interleaved rows of data using software GPIO rather than SPI. */
  volatile port_reg_t *port_clk = (volatile port_reg_t *)portOutputRegister(digitalPinToPort(pin_clk));
  port_reg_t mask_clk = digitalPinToBitMask(pin_clk);
  volatile port_reg_t *port_r_data = (volatile port_reg_t *) portOutputRegister(digitalPinToPort(pin_r_data));
  port_reg_t mask_r_data = digitalPinToBitMask(pin_r_data);

  for(int i = 0; i < rowsize; i++) {
    softSPITransfer(*(rows[3]++), port_r_data, mask_r_data, port_clk, mask_clk);
    softSPITransfer(*(rows[2]++), port_r_data, mask_r_data, port_clk, mask_clk);
    softSPITransfer(*(rows[1]++), port_r_data, mask_r_data, port_clk, mask_clk);
    softSPITransfer(*(rows[0]++), port_r_data, mask_r_data, port_clk, mask_clk);
  }
}
My RPI ported code:

Code: Select all

void DMD::softSPITransfer(unsigned char data) {
	for (unsigned char i = 8; i > 0; i--) {
		unsigned char bit = (data & (1 << i));
		digitalWrite(pin_r_data, bit);
		digitalWrite(pin_clk, 1);
		digitalWrite(pin_clk, 0);
	}
}

void DMD::writeSPIData(volatile unsigned char* rows[4], const int rowsize)
{
	for (int i = 0; i < rowsize; i++) {
		softSPITransfer(*(rows[3]++));
		softSPITransfer(*(rows[2]++));
		softSPITransfer(*(rows[1]++));
		softSPITransfer(*(rows[0]++));
	}
}
Swapping my piece of code with the code in the original library caused the framerate to drop by a significant amount.
Does any of you have a better way to port this small piece of code to rpi without offering too much performance?

Thnx

Re: Weird artifacts on dot matrix display

Posted: Mon Jul 29, 2019 8:27 pm
by Paeryn
steven1578 wrote:
Mon Jul 29, 2019 3:56 pm
My RPI ported code:

Code: Select all

void DMD::softSPITransfer(unsigned char data) {
	for (unsigned char i = 8; i > 0; i--) {
		unsigned char bit = (data & (1 << i));
		digitalWrite(pin_r_data, bit);
		digitalWrite(pin_clk, 1);
		digitalWrite(pin_clk, 0);
	}
}
I don't know about the rest, but your loop is transferring the wrong data, as you give it you are effectively sending the data shifted right one place, starting with a leading 0 and with the last bit of data not sent. You want to send bits 7 down to 0, not 8 down to 1.

The largest value an unsigned char can have is 255, with your loop starting with i=8 the first bit sent is data & (1<<8). 1<<8 is 256 so no matter what data holds, data & 256 will always be 0. The second bit you send will be the most significant bit of data (1<<7 is 128) and the last bit sent is when i=1, 1<<1 is 2 so the least significant bit of data is never sent.