steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Weird artifacts on dot matrix display

Sat Jun 08, 2019 9:28 pm

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.

User avatar
Paeryn
Posts: 2616
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Weird artifacts on dot matrix display

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?
She who travels light — forgot something.

steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Re: Weird artifacts on dot matrix display

Sun Jun 09, 2019 8:46 am

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.

steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Re: Weird artifacts on dot matrix display

Thu Jun 20, 2019 1:17 pm

Bumping, I still need help. Anyone has an idea what might be the issue?

salvato
Posts: 45
Joined: Tue Jan 15, 2013 9:21 pm

Re: Weird artifacts on dot matrix display

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 ?

steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Re: Weird artifacts on dot matrix display

Fri Jun 21, 2019 8:48 pm

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.

dsyleixa123
Posts: 306
Joined: Mon Jun 11, 2018 11:22 am

Re: Weird artifacts on dot matrix display

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...

User avatar
PeterO
Posts: 4882
Joined: Sun Jul 22, 2012 4:14 pm

Re: Weird artifacts on dot matrix display

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
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

salvato
Posts: 45
Joined: Tue Jan 15, 2013 9:21 pm

Re: Weird artifacts on dot matrix display

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();
}

steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Re: Weird artifacts on dot matrix display

Mon Jul 01, 2019 6:32 pm

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.

steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Re: Weird artifacts on dot matrix display

Mon Jul 01, 2019 6:33 pm

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.

steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Re: Weird artifacts on dot matrix display

Mon Jul 01, 2019 6:34 pm

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.

steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Re: Weird artifacts on dot matrix display

Sun Jul 14, 2019 9:02 pm

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.

steven1578
Posts: 10
Joined: Mon May 20, 2019 8:01 am

Re: Weird artifacts on dot matrix display

Mon Jul 29, 2019 3:56 pm

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

User avatar
Paeryn
Posts: 2616
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Weird artifacts on dot matrix display

Mon Jul 29, 2019 8:27 pm

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.
She who travels light — forgot something.

Return to “C/C++”