JennaSys / micropython-max7219

MicroPython driver for MAX7219 7-segment modules
MIT License
18 stars 1 forks source link

Extra flicker when cascading MAX7219s #3

Open diyelectromusic opened 1 year ago

diyelectromusic commented 1 year ago

Thanks for this library. I'm having a play with it and a Raspberry Pi Pico. I might be doing something wrong, but I seem to get additional flickering on any digits off the second MAX7219 when cascading two devices.

The flickering seems to go away if additional NOOPs are added before the digit update commands in the flush() method (in addition to afterwards), but I don't know if this is the designed behaviour or just happenstance!

Specifically, I've found that the following seems to work for me:

self._write(([MAX7219_REG_NOOP, 0] * (self.devices-dev)) + [pos + MAX7219_REG_DIGIT0, buffer[pos + (current_dev * self.scan_digits)]] + ([MAX7219_REG_NOOP, 0] * dev))

as a replacement for: https://github.com/JennaSys/micropython-max7219/blob/master/max7219.py#L85

This is having two MAX7219 cascading (with DOUT of the first linked to DIN on the second, etc), each driving 8 digits, so digits=16 and scan_digits=8. I wasn't entirely sure if this was the correct settings, but I think that is correct.

Thoughts?

Kevin

JennaSys commented 1 year ago

Not sure, but I'll check it out and see if I can duplicate that behavior once I get a chance to rig up a test.

I know I've run into similar issues with the MAX7219 on long runs of 8x8 LED matrices (48 of them actually), but fixing it was a combination of running parallel clock lines on shorter runs for better timing, and buffering the data line output. But just two cascaded MAX7219s should not have this issue.

What made you think of putting in more NOOPs in to fix it?

diyelectromusic commented 1 year ago

I added a print(data) line to the _write() method (well actually, first I tried various prints in flush, then building the required bytes in a single buffer for all devices, to write out in a single string, and stuff like that), and saw the output was something like the following: [1, 48] [2, 48] [3, 109] [4, 109] [5, 121] [6, 121] [7, 51] [8, 51] [1, 91, 0, 0] [2, 91, 0, 0] [3, 95, 0, 0] [4, 95, 0, 0] [5, 112, 0, 0] [6, 112, 0, 0] [7, 127, 0, 0] [8, 127, 0, 0]

As as the flickering I was seeing was an echo of the data from the first device in the cascade - i.e. I could see the first device value flick past, prior to showing the correct value on the second device - it looked like the values from the first device were somehow being shifted out through the second device prior to showing the second devices actual values.

Sorry, I'm not explaining it very well! For example: Dev 1, digits 0,1 = 44; Dev 2, digits 0,1 = 11, then I'd see the extra segments from the two 4's from the first device flash up on the second device just prior to it correctly displaying 11.

So it looked like the data from the first device bleeding over into the second as it was updating, albeit briefly.

So I don't know the exact mechanisms at play, but just wondered if the writes always had to account for both devices each time, hence trying the NOOPs. I couldn't make any sense of the datasheet which only had one example, with NOOPS at the end...

As I say, I did wonder if the buffer could be constructed in a single 16-byte string and sent out in one single write with no NOOPs at all, but my python isn't great, and I was getting messed up between bytes, bytearrays, and lists and wanting to do the "C" equivalent of buf[(dev*8+pos)*2] = REG_DIGIT0+pos; buf[(dev*8+pos)*2 + 1] = data

So that might be worth a look, as it might save some SPI writes if that works too...?

Kevin

JennaSys commented 1 year ago

It's been a while since I worked out this code, but yea the bit math can get messy with trying to make sure the right bits end up in the correct positions as they get pushed through. I'll look at combining blocks together as you suggest to so everything flushes out at once.

Thanks for the feedback. I won't likely get to this right away, but it's on my list now.

diyelectromusic commented 1 year ago

Ah, turns out it isn't possible to dump the whole buffer in one go, but it is possible to interleave the writes to the registers for both chips, thus avoiding the NOOPs, so for me, the following seems to work...

 def flush(self):
        buffer = self._buffer.copy()
        if self.reverse:
            buffer.reverse()

        tmpbuf = [0] * self.devices * 2
        for pos in range(self.scan_digits):
            for dev in range(self.devices):
                if self.reverse:
                    current_dev = self.devices - dev - 1
                else:
                    current_dev = dev

                tmpbuf[(self.devices-current_dev-1)*2] = pos + MAX7219_REG_DIGIT0
                tmpbuf[(self.devices-current_dev-1)*2 + 1] = buffer[pos + (current_dev * self.scan_digits)]

            self._write(tmpbuf)