// MAX7219 Dot matrix array controlling.
// Present limitations:
// - Maximum number of chained MAX7219 Dot Matrices is 32 (Untested).
// - Sprite image columns width is 8Bits, rows height n (specified when the sprite is declared).
// To-Dos:
// - Modify the sprite struct (And all functions that use it) to handle images of n
// rows high and m columns wide.
// - (Completed) Implement yOffset in the displayImage() function.
// - (Bug fix) Trailing pixels above the sprite when it descends.
#include <SPI.h>
#include "defines.h"
#define SS_PIN 53
// How long one row is in pixels.
#define displayLength 32
uint32_t displayBlock2[8] = {
0b11111111111111111111111111111111,
0b11111111111111111111111111111111,
0b00011111111111111111111111111111,
0b11111111111111111111111111111111,
0b11111111111111111111111111111111,
0b11111111111111111111111111111111,
0b11111111111111111111111111111111,
0b11111111111111111111111111111111,
};
uint8_t img1[8] = {
0b00011000,
0b00111100,
0b01111110,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00011000,
};
Sprite img2 = {
8,
8,
{
0b00011000,
0b00111100,
0b01111110,
0b11100111,
0b11100111,
0b01111110,
0b00111100,
0b00011000,
}
};
void maxTransferCMD(uint8_t address, uint8_t value) {
digitalWrite(SS_PIN, LOW);
SPI.transfer(address); // Send address.
SPI.transfer(value); // Send the value.
SPI.transfer(address); // Send address.
SPI.transfer(value); // Send the value.
digitalWrite(SS_PIN, HIGH); // Finish transfer.
}
void maxTransferDATA(uint8_t row, uint8_t sequence) {
digitalWrite(SS_PIN, LOW);
SPI.transfer(row); // Send address.
SPI.transfer(sequence); // Send the value.
digitalWrite(SS_PIN, HIGH); // Finish transfer.
}
void maxTransferDisplayBlock() {
for(uint8_t row = 0; row < 8; row++){
// Starts the transfer.
digitalWrite(SS_PIN, LOW);
// Sends the updated row for the entire length of the display.
for(uint8_t displayIndex = 0; displayIndex < 32; displayIndex +=8){
// The MAX7219 rows are 1 indexed instead of 0 indexed, so here we apply the offset.
SPI.transfer(row+1);
// Send the row byte for the displayIndex matrix (Furthest matrix from the MCU first).
SPI.transfer((uint8_t) (displayBlock2[row] >> displayIndex));
}
// Finishes the transfer.
digitalWrite(SS_PIN, HIGH);
}
}
void displayBlockWithOffset(int8_t offset) {
for(uint8_t row = 0; row < 8; row++){
// Starts the transfer (Halts any visual changes to the row from this point, just changes
// the input registers of the MAX7219).
digitalWrite(SS_PIN, LOW);
// Sends the updated row for the entire length of the display.
// Furthest matrix from the MCU first, just because thats how the MAX7219 daisychaining works.
for(uint8_t displayIndex = 0; displayIndex < 31; displayIndex +=8){
// The MAX7219 rows are 1 indexed instead of 0 indexed, so here we apply the offset.
SPI.transfer(row+1);
// Send the row byte for the displayIndex matrix.
// The bytes will be left/right shifted (If the offest has a negative or positive value,
// respectively) then clipped from a uint32_t (Unsigned long) to the size of a uint8_t
// before sending.
if(offset < 0){
SPI.transfer((uint8_t) ((displayBlock2[row] << (0-offset)) >> displayIndex));
}
else if(offset > 0){
SPI.transfer((uint8_t) ((displayBlock2[row] >> offset) >> displayIndex));
}
else{
SPI.transfer((uint8_t) (displayBlock2[row] >> displayIndex));
}
}
// Finishes the transfer (Makes visible all changes we've just made to this entire-display-lengthed row by
// propagating the MAX7219 input register contents we just changed into display memory).
digitalWrite(SS_PIN, HIGH);
}
}
void displayImageWithOffset(uint8_t image[], uint8_t imageRows, int8_t xOffset, int8_t yOffset) {
//Serial.println(xOffset);
// The MAX7219 rows are 1 indexed instead of 0 indexed, so here we apply the offset.
for(uint8_t row = 0; row < imageRows; row++){
// Starts the transfer (Halts any visual changes to the row from this point, just changes
// the input registers of the MAX7219).
digitalWrite(SS_PIN, LOW);
// Sends the updated row for the entire length of the display.
// Furthest matrix from the MCU first, just because thats how the MAX7219 daisychaining works.
for(uint8_t displayIndex = 0; displayIndex < (displayLength-1); displayIndex +=8){
SPI.transfer(row+1);
// Send the row byte for the displayIndex matrix.
// The bytes will be left/right shifted (If the offest has a negative or positive value,
// respectively) then clipped from a uint32_t (Unsigned long) to the size of a uint8_t
// before sending.
if(xOffset < 0){
SPI.transfer(((uint32_t)image[row] << 0-xOffset) >> displayIndex);
}
else if(xOffset > 0){
SPI.transfer(((uint32_t)image[row] >> xOffset) >> displayIndex);
}
else{
SPI.transfer((uint32_t)image[row] >> displayIndex);
}
}
// Finishes the transfer (Makes visible all changes we've just made to this entire-display-lengthed row by
// propagating the MAX7219 input register contents we just changed into display memory).
digitalWrite(SS_PIN, HIGH);
}
}
void displayImage(Sprite *img, int8_t xOffset, int8_t yOffset) {
//Serial.println(xOffset);
// The MAX7219 rows are 1 indexed instead of 0 indexed, so here we apply the offset.
for(uint8_t row = 0; row < img->rows; row++){
// Starts the transfer (Halts any visual changes to the row from this point, just changes
// the input registers of the MAX7219).
digitalWrite(SS_PIN, LOW);
// Sends the updated row for the entire length of the display.
// Furthest matrix from the MCU first, just because thats how the MAX7219 daisychaining works.
for(uint8_t displayIndex = 0; displayIndex < (displayLength-1); displayIndex +=8){
SPI.transfer(row+1);
// Send the row byte for the displayIndex matrix.
// The bytes will be left/right shifted (If the offest has a negative or positive value,
// respectively) then clipped from a uint32_t (Unsigned long) to the size of a uint8_t
// before sending.
if(xOffset < 0){
SPI.transfer(((uint32_t)img->pixels[row] << 0-xOffset) >> displayIndex);
}
else if(xOffset > 0){
SPI.transfer(((uint32_t)img->pixels[row] >> xOffset) >> displayIndex);
}
else{
SPI.transfer((uint32_t)img->pixels[row] >> displayIndex);
}
}
// Finishes the transfer (Makes visible all changes we've just made to this entire-display-lengthed row by
// propagating the MAX7219 input register contents we just changed into display memory).
digitalWrite(SS_PIN, HIGH);
}
}
void displayImageY(Sprite *img, int8_t xOffset, int8_t yOffset) {
uint8_t displayRow = 1;
//Serial.println("----");
if(yOffset>-1){
// Start rendering the rows of the image, starting with the first row that is visible on screen.
for(int8_t imgRow = yOffset; imgRow < img->rows; imgRow++){
//Serial.println(imgRow);
// Starts the transfer (Halts any visual changes to the row from this point, just changes
// the input registers of the MAX7219).
digitalWrite(SS_PIN, LOW);
// Sends the updated row for the entire length of the display.
// Furthest matrix from the MCU first, just because thats how the MAX7219 daisychaining works.
for(uint8_t displayIndex = 0; displayIndex < (displayLength-1); displayIndex +=8){
SPI.transfer(displayRow);
// Send the row byte for the displayIndex matrix.
// The bytes will be left/right shifted (If the offest has a negative or positive value,
// respectively) then clipped from a uint32_t (Unsigned long) to the size of a uint8_t
// before sending.
if(xOffset < 0){
SPI.transfer(((uint32_t)img->pixels[imgRow] << 0-xOffset) >> displayIndex);
}
else if(xOffset > 0){
SPI.transfer(((uint32_t)img->pixels[imgRow] >> xOffset) >> displayIndex);
}
else{
SPI.transfer((uint32_t)img->pixels[imgRow] >> displayIndex);
}
}
// Finishes the transfer (Makes visible all changes we've just made to this entire-display-lengthed row by
// propagating the MAX7219 input register contents we just changed into display memory).
digitalWrite(SS_PIN, HIGH);
displayRow++;
}
}
// If 0 or minus value
else{
// Start rendering the rows of the image, starting with the first row that is visible on screen.
for(int8_t imgRow = 0; imgRow < img->rows; imgRow++){
//Serial.println(imgRow);
// Starts the transfer (Halts any visual changes to the row from this point, just changes
// the input registers of the MAX7219).
digitalWrite(SS_PIN, LOW);
// Sends the updated row for the entire length of the display.
// Furthest matrix from the MCU first, just because thats how the MAX7219 daisychaining works.
for(uint8_t displayIndex = 0; displayIndex < (displayLength-1); displayIndex +=8){
SPI.transfer(displayRow-yOffset);
// Send the row byte for the displayIndex matrix.
// The bytes will be left/right shifted (If the offest has a negative or positive value,
// respectively) then clipped from a uint32_t (Unsigned long) to the size of a uint8_t
// before sending.
if(xOffset < 0){
SPI.transfer(((uint32_t)img->pixels[imgRow] << 0-xOffset) >> displayIndex);
}
else if(xOffset > 0){
SPI.transfer(((uint32_t)img->pixels[imgRow] >> xOffset) >> displayIndex);
}
else{
SPI.transfer((uint32_t)img->pixels[imgRow] >> displayIndex);
}
}
// Finishes the transfer (Makes visible all changes we've just made to this entire-display-lengthed row by
// propagating the MAX7219 input register contents we just changed into display memory).
digitalWrite(SS_PIN, HIGH);
displayRow++;
}
}
}
void setup() {
Serial.begin(9600);
pinMode(SS_PIN, OUTPUT);
// Optionally reverse the SPI output bit order (Horizontally mirrors the displayed image).
SPI.setBitOrder(MSBFIRST);
SPI.begin();
// Run test - All LED segments lit.
//maxTransferCMD(MAX7219_TEST, 0x01); delay(1000);
//maxTransferCMD(MAX7219_TEST, 0x00); // Finish test mode.
maxTransferCMD(MAX7219_DECODE_MODE, 0x00); // Disable BCD mode.
maxTransferCMD(MAX7219_BRIGHTNESS, 0x0F); // Use highest intensity.
maxTransferCMD(MAX7219_SCAN_LIMIT, 0x0f); // Scan all digits.
maxTransferCMD(MAX7219_SHUTDOWN, 0x01); // Turn on chip.
/////////////////////////////////////////////
// Offset values and their effects:
// Right shift (32) = Display block is off the right of the screen.
// No Shift (0) = Display block fills the display.
// Left shift (-32) = Display block is off the left side of the screen.
// // Move from offscreen (Right) to off screen (Left).
// for(int8_t i = -32; i <= img2.columns; i++){
// displayImage(&img2,i,0);
// delay(100);
// }
// // Move from offscreen (Left) to off screen (Right)).
// for(int8_t i = img2.columns; i >= -displayLength; i--){
// displayImage(&img2,i,0);
// delay(100);
// }
// Move from offscreen (Below) to off screen (Above).
for(int8_t i = 16; i >= -16; i--){
displayImageY(&img2,-12,i);
delay(100);
}
//////////////////////////////////////////////
}
void loop() {
}