Goal was to make a standalone HUB75 display driver using an AVR controller.
While anything with more power like an ESP32 would be way better for the job, i just love AVR so much i really wanted to make it work.
Software
The basic idea is to have two buffers. One screenbuffer which constantly gets rendered (=the stuff on screen) and one working buffer or shadow buffer how i call it which we can slowly fill with stuff that we want on screen without flickering. So the basic working principle is:
1. Clear shadow buffer
2. write pixels we want to shadow buffer
3 command to copy shadow buffer to screen
Color codes are 1 byte / 2 bit per color
Color byte = 0b00bbggrr
so yellow would be 0b00001111
Examples:
Control with an Arduino via TWI commands:
Control with python script via USB-UART dongle:
-> I just used the first pretty GIF i found on Google for testing. Original GIF by MetaruPX on Newgrounds
TWI commands
Commands are just a command byte, followed by 2-4 data bytes (except clear buffer) sent to the address
Clear buffer:
1 byte: 0x01
Draw pixel:
4 byte: 0x02, x_pos, y_pos, color
Draw letter:
5 byte: 0x03, x_pos, y_pos, ASCII, color
Copy buffer:
1 byte: 0x04
So to write a red "A" to position 10,10 the arduino command would be:
Wire.beginTransmission(0x41);
Wire.write(0x03);
Wire.write(10);
Wire.write(10);
Wire.write('A');
Wire.write(0b00000011);
Wire.endTransmission();
Then just send another 0x04 to the address and done. Cant get any easier than that.
The AVR already runs on its very limit and it was not possible to add another bigger buffer. So every command needs to be its own Transmission.
UART control
Uart control is meant for "streaming" so the commands dont exist.
You start a transmission by writing 0xF0 (clear shadow buffer and start counter)
Then you write the color-bytes for every pixel
And end the transmission with 0xF1 (copy shadow buffer to screen)
So an example python script to write one screen would be:
pack = bytearray()
pack.append(0xF0)
for y in range(64):
for x in range(64):
pack.append(Buffer[x][y])
pack.append(0xF1)
ser.write(pack)
Hardware

The design is kept in a handy business-card size. Input and supply in the bottom, output on top.
Controller
For this project i use an AVR128DA32.
The clock speed is a bit higher than the other AVR with 24MHz.
Also the DA family has the most RAM which is needed for the screen buffers.
Power supply
The need for beefy 5V supplies is kinda annoying. So i added an onboard DCDC.
Maximum Vin = 26V.
Iout max ~2A (enough to drive a 64x64 matrix at full tilt)
Connectivity
Two TWI connectors with a Adafruit-STEMMA pinout. Compatible with 3v3 and 5V (configurable via solder jumpers) and optional 4k7 pullup.
The slave address is 0x40 with an configurable offset via solder jumpers (0x40 - 0x4F)
A JST-PH connector for UART control (fixed BAUD rate of 230400)