初始化提交

This commit is contained in:
王立帮
2024-07-20 22:09:06 +08:00
commit c247dd07a6
6876 changed files with 2743096 additions and 0 deletions

View File

@@ -0,0 +1,297 @@
/*
MD_MAX72xx - Library for using a MAX7219/7221 LED matrix controller
See header file for comments
This file contains class and hardware related methods.
Copyright (C) 2012-14 Marco Colli. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include <SPI.h>
#include "MD_MAX72xx.h"
#include "MD_MAX72xx_lib.h"
/**
* \file
* \brief Implements class definition and general methods
*/
MD_MAX72XX::MD_MAX72XX(uint8_t dataPin, uint8_t clkPin, uint8_t csPin, uint8_t numDevices):
_dataPin(dataPin), _clkPin(clkPin), _csPin(csPin), _maxDevices(numDevices),
_updateEnabled(true), _hardwareSPI(false)
{
}
MD_MAX72XX::MD_MAX72XX(uint8_t csPin, uint8_t numDevices):
_dataPin(0), _clkPin(0), _csPin(csPin), _maxDevices(numDevices),
_updateEnabled(true), _hardwareSPI(true)
{
}
void MD_MAX72XX::begin(void)
{
// initialize the AVR hardware
if (_hardwareSPI)
{
PRINTS("\nHardware SPI");
SPI.begin();
// Old mode of operations!
//SPI.setDataMode(SPI_MODE0);
//SPI.setBitOrder(MSBFIRST);
//SPI.setClockDivider(SPI_CLOCK_DIV2);
}
else
{
PRINTS("\nBitBang SPI")
pinMode(_dataPin, OUTPUT);
pinMode(_clkPin, OUTPUT);
}
// initialise our preferred CS pin (could be same as SS)
digitalWrite(_csPin, HIGH);
pinMode(_csPin, OUTPUT);
// object memory and internals
setShiftDataInCallback(NULL);
setShiftDataOutCallback(NULL);
_matrix = (deviceInfo_t *)malloc(sizeof(deviceInfo_t) * _maxDevices);
_spiData = (uint8_t *)malloc(SPI_DATA_SIZE);
#if USE_LOCAL_FONT
#if USE_INDEX_FONT
_fontIndex = (uint16_t *)malloc(sizeof(uint16_t) * ASCII_INDEX_SIZE);
#else
_fontIndex = NULL;
#endif
setFont(NULL);
#endif // INCLUDE_LOCAL_FONT
// Initialize the display devices. On initial power-up
// - all control registers are reset,
// - scan limit is set to one digit (row/col or LED),
// - Decoding mode is off,
// - intensity is set to the minimum,
// - the display is blanked, and
// - the MAX7219/MAX7221 is shut down.
// The devices need to be set to our library defaults prior using the
// display modules.
control(TEST, OFF); // no test
control(SCANLIMIT, ROW_SIZE-1); // scan limit is set to max on startup
control(INTENSITY, MAX_INTENSITY/2); // set intensity to a reasonable value
control(DECODE, OFF); // make sure that no decoding happens (warm boot potential issue)
clear();
control(SHUTDOWN, OFF); // take the modules out of shutdown mode
}
MD_MAX72XX::~MD_MAX72XX(void)
{
if (_hardwareSPI) SPI.end(); // reset SPI mode
free(_matrix);
free(_spiData);
#if USE_LOCAL_FONT && USE_FONT_INDEX
if (_fontIndex != NULL) free(_fontIndex);
#endif
}
void MD_MAX72XX::controlHardware(uint8_t dev, controlRequest_t mode, int value)
// control command is for the devices, translate internal request to device bytes
// into the transmission buffer
{
uint8_t opcode = OP_NOOP;
uint8_t param = 0;
// work out data to write
switch (mode)
{
case SHUTDOWN:
opcode = OP_SHUTDOWN;
param = (value == OFF ? 1 : 0);
break;
case SCANLIMIT:
opcode = OP_SCANLIMIT;
param = (value > MAX_SCANLIMIT ? MAX_SCANLIMIT : value);
break;
case INTENSITY:
opcode = OP_INTENSITY;
param = (value > MAX_INTENSITY ? MAX_INTENSITY : value);
break;
case DECODE:
opcode = OP_DECODEMODE;
param = (value == OFF ? 0 : 0xff);
break;
case TEST:
opcode = OP_DISPLAYTEST;
param = (value == OFF ? 0 : 1);
break;
default:
return;
}
// put our device data into the buffer
_spiData[SPI_OFFSET(dev, 0)] = opcode;
_spiData[SPI_OFFSET(dev, 1)] = param;
}
void MD_MAX72XX::controlLibrary(controlRequest_t mode, int value)
// control command was internal, set required parameters
{
switch (mode)
{
case UPDATE:
_updateEnabled = (value == ON);
if (_updateEnabled) flushBufferAll();
break;
case WRAPAROUND:
_wrapAround = (value == ON);
break;
}
}
bool MD_MAX72XX::control(uint8_t startDev, uint8_t endDev, controlRequest_t mode, int value)
{
if (endDev < startDev) return(false);
if (mode < UPDATE) // device based control
{
spiClearBuffer();
for (uint8_t i = startDev; i <= endDev; i++)
controlHardware(i, mode, value);
spiSend();
}
else // internal control function, doesn't relate to specific device
{
controlLibrary(mode, value);
}
return(true);
}
bool MD_MAX72XX::control(uint8_t buf, controlRequest_t mode, int value)
// dev is zero based and needs adjustment if used
{
if (buf > LAST_BUFFER) return(false);
if (mode < UPDATE) // device based control
{
spiClearBuffer();
controlHardware(buf, mode, value);
spiSend();
}
else // internal control function, doesn't relate to specific device
{
controlLibrary(mode, value);
}
return(true);
}
void MD_MAX72XX::flushBufferAll()
// Only one data byte is sent to a device, so if there are many changes, it is more
// efficient to send a data byte all devices at the same time, substantially cutting
// the number of communication messages required.
{
for (uint8_t i=0; i<ROW_SIZE; i++) // all data rows
{
bool bChange = false; // set to true if we detected a change
spiClearBuffer();
for (uint8_t dev = FIRST_BUFFER; dev <= LAST_BUFFER; dev++) // all devices
{
if (bitRead(_matrix[dev].changed, i))
{
// put our device data into the buffer
_spiData[SPI_OFFSET(dev, 0)] = OP_DIGIT0+i;
_spiData[SPI_OFFSET(dev, 1)] = _matrix[dev].dig[i];
bChange = true;
}
}
if (bChange) spiSend();
}
// mark everything as cleared
for (uint8_t dev = FIRST_BUFFER; dev <= LAST_BUFFER; dev++)
_matrix[dev].changed = ALL_CLEAR;
}
void MD_MAX72XX::flushBuffer(uint8_t buf)
// Use this function when the changes are limited to one device only.
// Address passed is a buffer address
{
PRINT("\nflushBuf: ", buf);
PRINTS(" r");
if (buf > LAST_BUFFER)
return;
for (uint8_t i = 0; i < ROW_SIZE; i++)
{
if (bitRead(_matrix[buf].changed, i))
{
PRINT("", i);
spiClearBuffer();
// put our device data into the buffer
_spiData[SPI_OFFSET(buf, 0)] = OP_DIGIT0+i;
_spiData[SPI_OFFSET(buf, 1)] = _matrix[buf].dig[i];
spiSend();
}
}
_matrix[buf].changed = ALL_CLEAR;
}
void MD_MAX72XX::spiClearBuffer(void)
// Clear out the spi data array
{
memset(_spiData, OP_NOOP, SPI_DATA_SIZE);
}
void MD_MAX72XX::spiSend()
{
// initialise the SPI transaction
if (_hardwareSPI)
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
digitalWrite(_csPin, LOW);
// shift out the data
if (_hardwareSPI)
{
for (int i = 0; i < SPI_DATA_SIZE; i++)
SPI.transfer(_spiData[i]);
}
else
{
for (int i = 0; i < SPI_DATA_SIZE; i++)
shiftOut(_dataPin, _clkPin, MSBFIRST, _spiData[i]);
}
// end the SPI transaction
digitalWrite(_csPin, HIGH);
if (_hardwareSPI)
SPI.endTransaction();
}

View File

@@ -0,0 +1,955 @@
/**
\mainpage Arduino LED Matrix Library
The Maxim 72xx LED Controller IC
--------------------------------
The MAX7219/MAX7221 are compact, serial input/output display drivers that
interface microprocessors to 7-segment numeric LED displays of up to 8 digits,
bar-graph displays, or 64 individual LEDs. Included on-chip are a BCD code-B
decoder, multiplex scan circuitry, segment and digit drivers, and an 8x8 static
RAM that stores each digit.
A 4-wire serial interface (SPI) allows the devices to be cascaded, with
communications passed through the first device in the chain to all others. Individual
elements may be addressed and updated without rewriting the entire display.
This library implements functions that allow the MAX72xx to be used
for LED matrices (64 individual LEDs), allowing the programmer to use the LED
matrix as a pixel device, displaying graphics elements much like any other
pixel addressable display.
Topics
------
- \subpage pageHardware
- \subpage pageSoftware
- \subpage pageConnect
- \subpage pageFontUtility
- \subpage pageRevisionHistory
Copyright
---------
Copyright (C) 2012-16 Marco Colli. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\page pageRevisionHistory Revision History
Revision History
----------------
Nov 2017 version 2.9.1
- Changed SPI buffer handling and isolation of AVR specific features (eg PROGMEM)
- Added MD_MAX72xx_Message_ESP8266 example
- Minor source file cleanup
- Added Extended ASCII font, vertical rotated font and RobotEyes font in fontbuilder
- Modifed fontbuilder output code for consistency with new code
- Added getFont(), getMaxFontWidth() methods
- Changed example - replaced MD_KeySwitch with new MD_UISwitch library
Nov 2016 version 2.9.0
- Added WordClock example
- Deprecated USE_LIBRARY_SPI as no problems reported with new implementation
- Changed MD_ branding to new MajicDesigns diamond
- Small adjustments to initialisation code
Mar 2016 version 2.8
- Added example _Message_SD and renamed _Message to _Message_Serial
- Added Pacman example
- Added PushWheel example
- Added USE_LIBRARY_SPI to enable library SPI object
- Modifed all examples to conditionally include <SPI.h>
- FontBuilder modified to handle definitions for double height fonts
- New txt2font utility for easier font creattion from a text file
- Revised and ro-organised documentation; expanded section on fonts
April 2015 version 2.7
- Changed to Daft Punk example to run without switch
- Now supporting IDE Library Manager
February 2015 version 2.6
- Improvements to HW_Mapper utility
- Added HW_USE_FC16 for FC-16 display modules
- Added USE_HW_OTHER for user defined hardware configuration
- Fixed incorrect spelling for HW_REV_COLS in transformbuffer() & corresponding bug
- Added PrintText_ML example
February 2015 version 2.5
- Documented process for adding new hardware module type
- Fixed PROGMEM definitions for IDE version 1.5.7 compile error
- Added Daft Punk example code
- Updated HW_Mapper example/utility with built-in instructions
- Minor problems with Parola font setting interaction fixed
April 2014 version 2.4
- Improved reliability of initialization code to remove artifacts
+ Changed order of hardware initialization for SS, _csPin
+ Changed initialisation sequence at begin()
+ Fixed memset bug identified by bperrybap
- Reworked command SPI transmissions for efficiency
- Cleanup up compiler warnings on inline wrapper code functions
- Cleaned up examples begin() - better defined library default values
- Reviewed and tidied up some documentation
March 2014 - version 2.3
- Extensive rework of the font system
+ New font Microsoft Excel VBA based builder tool available
+ Removed USE_FONT_ADJUST and related code - replace by builder tool
+ Fixed width font has been removed from the library. Definition still available in font builder
+ fontype_t definition changed to suit new requirements
- Transform zoning implemented (contiguous subset of services)
+ Transformation functions, control, clear, setRow methods overloaded with range specifier
+ User callback for L/R rotation function syntax added a device parameter
+ New Zones example
- USE_*_HW hardware types now separated out for future flexibility
+ Rework of the library to use new schema for defining hardware characteristics
+ New utility code to map out digits and segments for unknown hardware types
- Rechecked and reworked examples for new library
November 2013 - version 2.2
- Replaced reference to SPI library with inline code to allow for different select lines
- Obsoleted INCLUDE_HARDWARE_SPI conditional compile switch
- Fixed legacy code function name error when USE_FONT_ADJUST switch turned on
- Implemented USE_PAROLA_HW to allow cheaply available matrix modules to be used in ganged mode
- Fixed reversal of bit field for set/get Row/Column functions -> flipped charset data
- Added Eyes example program
- Upgraded and reorganized documentation
June 2013 - version 2.1
- Include the selection of hardware SPI interface (10x speed improvement)
- Tidied up comments
April 2013 - version 2.0
- Major update and rewrite of library code:
- Improved speed and efficiency of code
- Increased level of abstraction in the library for pixel methods
- Increased level of abstraction for character and font methods
- Increased number of functions and added variable sized font
- Changed defines to enumerated types within the scope of the class
- Updated functionality to simplify controlling multiple devices
- Changed text and comments to be aligned to doxygen documentation generation
June 2012 - version 1.0
- Incorporated elements of Arduino LedControl (Eberhard Fahle) and MAX7219 libraries
- Easier functionality for pixel graphics treatment of 8x8 matrices
\page pageSoftware Software Library
The Library
-----------
The library implements functions that allow the MAX72xx to be used
for LED matrices (64 individual LEDs), allowing the programmer to use the LED
matrix as a pixel device, displaying graphics elements much like any other
pixel addressable display.
In this scenario, it is convenient to abstract out the concept of the hardware device
and create a uniform and consistent pixel address space, with the libraries determining
device and device-element address. Similarly, control of the devices should be uniform
and abstracted to a system level.
The library still retains flexibility for device level control, should the developer
require, through the use of overloaded class methods.
___
Conditional Compilation Switches
--------------------------------
The library allows the run time code to be tailored through the use of compilation
switches. The compile options start with USE_ and are documented in the section
related to the main header file MD_MAX72xx.h.
_NOTE_: Compile switches must be edited in the library header file. Arduino header file
'mashing' during compilation makes the setting of these switches from user code
completely unreliable.
\page pageConnect System Connections
Connections to the Arduino Board (SPI interface)
------------------------------------------------
The modules are connected through a 4-wire serial interface (SPI), and devices are cascaded,
with communications passed through the first device in the chain to all others. The Arduino
should be connected to the IN side of the first module in the chain.
The Arduino interface is implemented with either
+ The hardware SPI interface, or
+ 3 arbitrary digital outputs that are passed through to the class constructor.
The AVR hardware SPI interface is fast but fixed to predetermined output pins. The more general
software interface uses the Arduino shiftOut() library function, making it slower but allows the
use of arbitrary digital pins to send the data to the device. Which mode is enabled depends
on the class constructor used.
The Arduino interface is implemented with 3 digital outputs that are passed through to
the class constructor. The digital outputs define the SPI interface as follows:
- DIN (MOSI) - the Data IN signal shifts data into the display module. Data is loaded into
the device's internal 16-bit shift register on CLK's rising edge.
- CLK (SCK) - the CLocK signal that is used to time the data for the device.
- LD (SS) - the interface is active when LoaD signal is LOW. Serial data are loaded into the
device shift register while LOAD is LOW and latched in on the rising edge.
The LD signal is used to select the entire device chain. This allows separate LD
outputs to control multiple displays sharing the same DIN and CLK signals. The
software needs to instantiate a separate object for each display.
The remaining interface pins are for +5V and GND. The power supply must be able to supply
enough current for the number of connected modules.
*/
#ifndef MD_MAX72xx_h
#define MD_MAX72xx_h
#include <Arduino.h>
/**
* \file
* \brief Main header file for the MD_MAX72xx library
*/
/**
\def USE_PAROLA_HW
Set to 1 (default) to use the Parola hardware modules. The
software was originally designed to operate with this hardware type.
*/
#define USE_PAROLA_HW 0
/**
\def USE_GENERIC_HW
Set to 1 to use 'generic' hardware modules commonly available, with
connectors at the top and bottom of the PCB, available from many sources.
*/
#define USE_GENERIC_HW 0
/**
\def USE_ICSTATION_HW
Set to 1 to use ICStation DIY hardware module kits available from
http://www.icstation.com/product_info.php?products_id=2609#.UxqVJyxWGHs
This hardware must be set up with the input on the RHS.
*/
#define USE_ICSTATION_HW 0
/**
\def USE_FC16_HW
Set to 1 to use FC16 hardware module kits.
FC16 modules are similar in format to the ICStation modules but are wired differently.
Modules are identified by a FC-16 designation on the PCB
*/
#define USE_FC16_HW 1
/**
\def USE_OTHER_HW
Set to 1 to use other hardware not defined above.
Module 0 (Data In) must be set up on the RHS and the CUSTOM hardware defines
must be set up in the MD_MAD72xx_lib.h file under for this section, using the HW_Mapper
utility to work out what the correct values to use are.
*/
#define USE_OTHER_HW 0
/**
\def USE_LOCAL_FONT
Set to 1 (default) to enable local font in this library and enable
loadChar() and related methods. If the library is just used for
graphics some FLASH RAM can be saved by not including the code to process
font data. The font file is stored in PROGMEM.
*/
#define USE_LOCAL_FONT 1
/**
\def USE_INDEX_FONT
Set to 1 to enable font indexing to speed up font lookups - usually disabled.
This will trade off increased stack RAM usage for lookup speed if enabled.
When disabled lookups will then become linear searches through PROGMEM.
Uses ASCII_INDEX_SIZE elements of uint16_t (512 bytes) if enabled. For most
purposes the increase in speed is not needed.
USE_LOCAL FONT must be enabled for this option to take effect.
*/
#define USE_INDEX_FONT 0
// Display parameter constants
// Defined values that are used throughout the library to define physical limits
#define ROW_SIZE 8 ///< The size in pixels of a row in the device LED matrix array
#define COL_SIZE 8 ///< The size in pixels of a column in the device LED matrix array
#define MAX_INTENSITY 0xf ///< The maximum intensity value that can be set for a LED array
#define MAX_SCANLIMIT 7 ///< The maximum scan limit value that can be set for the devices
/**
* Core object for the MD_MAX72XX library
*/
class MD_MAX72XX
{
public:
#if USE_LOCAL_FONT
/**
* Font definition type.
*
* This type is used in the setFont() method to set the font to be used
*/
typedef const uint8_t fontType_t;
#endif
/**
* Control Request enumerated type.
*
* This enumerated type is used with the control() method to identify
* the control action request.
*/
enum controlRequest_t
{
SHUTDOWN = 0, ///< Shut down the MAX72XX. Requires ON/OFF value. Library default is OFF.
SCANLIMIT = 1, ///< Set the scan limit for the MAX72XX. Requires numeric value [0..MAX_SCANLIMIT]. Library default is all on.
INTENSITY = 2, ///< Set the LED intensity for the MAX72XX. Requires numeric value [0..MAX_INTENSITY]. LIbrary default is MAX_INTENSITY/2.
TEST = 3, ///< Set the MAX72XX in test mode. Requires ON/OFF value. Library default is OFF.
DECODE = 4, ///< Set the MAX72XX 7 segment decode mode. Requires ON/OFF value. Library default is OFF.
UPDATE = 10, ///< Enable or disable auto updates of the devices from the library. Requires ON/OFF value. Library default is ON.
WRAPAROUND = 11 ///< Enable or disable wraparound when shifting (circular buffer). Requires ON/OFF value. Library default is OFF.
};
/**
* Control Value enumerated type.
*
* This enumerated type is used with the control() method as the
* ON/OFF value for a control request. Other values may be used
* if numeric data is required.
*/
enum controlValue_t
{
OFF = 0, ///< General OFF status request
ON = 1 ///< General ON status request
};
/**
* Transformation Types enumerated type.
*
* This enumerated type is used in the transform() methods to identify a
* specific transformation of the display data in the device buffers.
*/
enum transformType_t
{
TSL, ///< Transform Shift Left one pixel element
TSR, ///< Transform Shift Right one pixel element
TSU, ///< Transform Shift Up one pixel element
TSD, ///< Transform Shift Down one pixel element
TFLR, ///< Transform Flip Left to Right
TFUD, ///< Transform Flip Up to Down
TRC, ///< Transform Rotate Clockwise 90 degrees
TINV ///< Transform INVert (pixels inverted)
};
/**
* Class Constructor - arbitrary digital interface.
*
* Instantiate a new instance of the class. The parameters passed are used to
* connect the software to the hardware. Multiple instances may co-exist
* but they should not share the same hardware CS pin (SPI interface).
*
* \param dataPin output on the Arduino where data gets shifted out.
* \param clkPin output for the clock signal.
* \param csPin output for selecting the device.
* \param numDevices number of devices connected. Default is 1 if not supplied.
* Memory for device buffers is dynamically allocated based
* on this parameter.
*/
MD_MAX72XX(uint8_t dataPin, uint8_t clkPin, uint8_t csPin, uint8_t numDevices=1);
/**
* Class Constructor - SPI hardware interface.
*
* Instantiate a new instance of the class. The parameters passed are used to
* connect the software to the hardware. Multiple instances may co-exist
* but they should not share the same hardware CS pin (SPI interface).
* The dataPin and the clockPin are defined by the Arduino hardware definition
* (SPI MOSI and SCK signals).
*
* \param csPin output for selecting the device.
* \param numDevices number of devices connected. Default is 1 if not supplied.
* Memory for device buffers is dynamically allocated based
* on this parameter.
*/
MD_MAX72XX(uint8_t csPin, uint8_t numDevices=1);
/**
* Initialize the object.
*
* Initialise the object data. This needs to be called during setup() to initialise new
* data for the class that cannot be done during the object creation.
*
* The LED hardware is initialized to the middle intensity value, all rows showing,
* and all LEDs cleared (off). Test, shutdown and decode modes are off. Display updates
* are on and wraparound is off.
*/
void begin(void);
/**
* Class Destructor.
*
* Released allocated memory and does the necessary to clean up once the object is
* no longer required.
*/
~MD_MAX72XX();
//--------------------------------------------------------------
/** \name Methods for object and hardware control.
* @{
*/
/**
* Set the control status of the specified parameter for the specified device.
*
* The device has a number of control parameters that can be set through this method.
* The type of control action required is passed through the mode parameter and
* should be one of the control actions defined by controlRequest_t. The value that
* needs to be supplied on the control action required is one of the defined
* actions in controlValue_t or a numeric parameter suitable for the control action.
*
* \param dev address of the device to control [0..getDeviceCount()-1].
* \param mode one of the defined control requests.
* \param value parameter value or one of the control status defined.
* \return false if parameter errors, true otherwise.
*/
bool control(uint8_t dev, controlRequest_t mode, int value);
/**
* Set the control status of the specified parameter for all devices.
*
* Invokes the control function for each device in turn. as this is a wrapper for the
* control(startDev, endDev, ...) methods, see the documentation for that method.
*
* \param mode one of the defined control requests.
* \param value parameter value or one of the control status defined.
* \return No return value.
*/
inline void control(controlRequest_t mode, int value) { control(0, getDeviceCount()-1, mode, value); };
/**
* Set the control status of the specified parameter for contiguous subset of devices.
*
* Invokes the control function for each device in turn for the devices in the subset.
* See documentation for the control() method.
*
* \param startDev the first device for the transformation [0..getDeviceCount()-1]
* \param endDev the last device for the transformation [0..getDeviceCount()-1]
* \param mode one of the defined control requests.
* \param value parameter value or one of the control status defined.
* \return false if parameter errors, true otherwise.
*/
bool control(uint8_t startDev, uint8_t endDev, controlRequest_t mode, int value);
/**
* Gets the number of devices attached to this class instance.
*
* \return uint8_t representing the number of devices attached to this object.
*/
uint8_t getDeviceCount(void) { return(_maxDevices); };
/**
* Gets the maximum number of columns for devices attached to this class instance.
*
* \return uint16_t representing the number of columns.
*/
uint16_t getColumnCount(void) { return(_maxDevices*COL_SIZE); };
/**
* Set the Shift Data In callback function.
*
* The callback function is called from the library when a transform shift left
* or shift right operation is executed and the library needs to obtain data for
* the end element of the shift (ie, conceptually this is the new data that is
* shifted 'into' the display). The callback function is invoked when
* - WRAPAROUND is not active, as the data would automatically supplied within the library.
* - the call to transform() is global (ie, not for an individual buffer).
*
* The callback function takes 2 parameters:
* - the device number requesting the data [0..getDeviceCount()-1]
* - one of the transformation types in transformType_t) that tells the callback function
* what shift is being performed
* The return value is the data for the column to be shifted into the display.
*
* \param cb the address of the function to be called from the library.
* \return No return data
*/
void setShiftDataInCallback(uint8_t (*cb)(uint8_t dev, transformType_t t)) { _cbShiftDataIn = cb; };
/**
* Set the Shift Data Out callback function.
*
* The callback function is called from the library when a transform shift left
* or shift right operation is executed and the library is about to discard the data for
* the first element of the shift (ie, conceptually this is the data that 'falls' off
* the front end of the scrolling display). The callback function is invoked when
* - WRAPAROUND is not active, as the data would automatically supplied to the tail end.
* - the call to transform() is global (ie, not for an individual buffer).
*
* The callback function is with supplied 3 parameters, with no return value required:
* - the device number that is the source of the data [0..getDeviceCount()-1]
* - one of the transformation types transformType_t that tells the callback
* function the type of shifting being executed
* - the data for the column being shifted out
*
* \param cb the address of the function to be called from the library.
* \return No return data
*/
void setShiftDataOutCallback(void (*cb)(uint8_t dev, transformType_t t, uint8_t colData)) { _cbShiftDataOut = cb; };
/** @} */
//--------------------------------------------------------------
/** \name Methods for graphics and bitmap related abstraction.
* @{
*/
/**
* Clear all the display data on all the display devices.
*
* \return No return value.
*/
inline void clear(void) { clear(0, getDeviceCount()-1); };
/**
* Clear all the display data on a subset of devices.
*
* endDev must be greater than or equal to startDev.
*
* \param startDev the first device to clear [0..getDeviceCount()-1]
* \param endDev the last device to clear [0..getDeviceCount()-1]
* \return No return value.
*/
void clear(uint8_t startDev, uint8_t endDev);
/**
* Draw a line between two points on the display
*
* Draw a line between the specified points. The LED will be turned on or
* off depending on the value supplied. The column number will be dereferenced
* into the device and column within the device, allowing the LEDs to be treated
* as a continuous pixel field.
*
* \param r1 starting row coordinate for the point [0..ROW_SIZE-1].
* \param c1 starting column coordinate for the point [0..getColumnCount()-1].
* \param r2 ending row coordinate for the point [0..ROW_SIZE-1].
* \param c2 ending column coordinate for the point [0..getColumnCount())-1].
* \param state true - switch on; false - switch off.
* \return false if parameter errors, true otherwise.
*/
bool drawLine(uint8_t r1, uint16_t c1, uint8_t r2, uint16_t c2, bool state);
/**
* Load a bitmap from the display buffers to a user buffer.
*
* Allows the calling program to read bitmaps (characters or graphic)
* elements from the library display buffers. The data buffer
* pointer should be a block of uint8_t data of size elements that will
* contain the returned data.
*
* \param col address of the display column [0..getColumnCount()-1].
* \param size number of columns of data to return.
* \param *pd Pointer to a data buffer [0..size-1].
* \return false if parameter errors, true otherwise. If true, data will be in the buffer at *pd.
*/
bool getBuffer(uint16_t col, uint8_t size, uint8_t *pd);
/**
* Get the LEDS status for the specified column.
*
* This method operates on a specific buffer
*
* This method operates on one column, getting the bit field value of
* the LEDs in the column. The column is referenced with the absolute column
* number (ie, the device number is inferred from the column).
*
* \param c column which is to be set [0..getColumnCount()-1].
* \return uint8_t value with each bit set to 1 if the corresponding LED is lit. 0 is returned for parameter error.
*/
uint8_t getColumn(uint8_t c) { return getColumn((c / COL_SIZE), c % COL_SIZE); };
/**
* Get the status of a single LED, addressed as a pixel.
*
* The method will get the status of a specific LED element based on its
* coordinate position. The column number is dereferenced into the device
* and column within the device, allowing the LEDs to be treated as a
* continuous pixel field.
*
* \param r row coordinate for the point [0..ROW_SIZE-1].
* \param c column coordinate for the point [0..getColumnCount()-1].
* \return true if LED is on, false if off or parameter errors.
*/
bool getPoint(uint8_t r, uint16_t c);
/**
* Load a bitfield from the user buffer to a display buffer.
*
* Allows the calling program to define bitmaps (characters or graphic)
* elements and pass them to the library for display. The data buffer
* pointer should be a block of uint8_t data of size elements that define
* the bitmap.
*
* \param col address of the display column [0..getColumnCount()-1].
* \param size number of columns of data following.
* \param *pd Pointer to a data buffer [0..size-1].
* \return false if parameter errors, true otherwise.
*/
bool setBuffer(uint16_t col, uint8_t size, uint8_t *pd);
/**
* Set all LEDs in a specific column to a new state.
*
* This method operates on one column, setting the value of the LEDs in
* the column to the specified value bitfield. The column is
* referenced with the absolute column number (ie, the device number is
* inferred from the column). The method is useful for drawing vertical
* lines and patterns when the display is being treated as a pixel field.
* The least significant bit of the value is the lowest row number.
*
* \param c column which is to be set [0..getColumnCount()-1].
* \param value each bit set to 1 will light up the corresponding LED.
* \return false if parameter errors, true otherwise.
*/
bool setColumn(uint8_t c, uint8_t value) { return setColumn((c / COL_SIZE), c % COL_SIZE, value); };
/**
* Set the status of a single LED, addressed as a pixel.
*
* The method will set the value of a specific LED element based on its
* coordinate position. The LED will be turned on or off depending on the
* value supplied. The column number is dereferenced into the device and
* column within the device, allowing the LEDs to be treated as a
* continuous pixel field.
*
* \param r row coordinate for the point [0..ROW_SIZE-1].
* \param c column coordinate for the point [0..getColumnCount()-1].
* \param state true - switch on; false - switch off.
* \return false if parameter errors, true otherwise.
*/
bool setPoint(uint8_t r, uint16_t c, bool state);
/**
* Set all LEDs in a row to a new state on all devices.
*
* This method operates on all devices, setting the value of the LEDs in
* the row to the specified value bit field. The method is useful for
* drawing patterns and lines horizontally across on the entire display.
* The least significant bit of the value is the lowest column number.
*
* \param r row which is to be set [0..ROW_SIZE-1].
* \param value each bit set to 1 will light up the corresponding LED on each device.
* \return false if parameter errors, true otherwise.
*/
inline bool setRow(uint8_t r, uint8_t value) { return setRow(0, getDeviceCount()-1, r, value); };
/**
* Set all LEDs in a row to a new state on contiguous subset of devices.
*
* This method operates on a contiguous subset of devices, setting the value
* of the LEDs in the row to the specified value bit field. The method is useful for
* drawing patterns and lines horizontally across specific devices only.
* endDev must be greater than or equal to startDev.
* The least significant bit of the value is the lowest column number.
*
* \param startDev the first device for the transformation [0..getDeviceCount()-1]
* \param endDev the last device for the transformation [0..getDeviceCount()-1]
* \param r row which is to be set [0..ROW_SIZE-1].
* \param value each bit set to 1 will light up the corresponding LED on each device.
* \return false if parameter errors, true otherwise.
*/
bool setRow(uint8_t startDev, uint8_t endDev, uint8_t r, uint8_t value);
/**
* Apply a transformation to the data in all the devices.
*
* The buffers for all devices can be transformed using one of the enumerated
* transformations in transformType_t. The transformation is carried across
* device boundaries (ie, there is overflow to an adjacent devices if appropriate).
*
* \param ttype one of the transformation types in transformType_t.
* \return false if parameter errors, true otherwise.
*/
inline bool transform(transformType_t ttype) { return transform(0, getDeviceCount()-1, ttype); };
/**
* Apply a transformation to the data in contiguous subset of devices.
*
* The buffers for all devices in the subset can be transformed using one of the enumerated
* transformations in transformType_t. The transformation is carried across
* device boundaries (ie, there is overflow to an adjacent devices if appropriate).
* endDev must be greater than or equal to startDev.
*
* \param startDev the first device for the transformation [0..getDeviceCount()-1]
* \param endDev the last device for the transformation [0..getDeviceCount()-1]
* \param ttype one of the transformation types in transformType_t.
* \return false if parameter errors, true otherwise.
*/
bool transform(uint8_t startDev, uint8_t endDev, transformType_t ttype);
/**
* Turn auto display updates on or off.
*
* Turn auto updates on and off, as required. When auto updates are turned OFF the
* display will not update after each operation. Display updates can be forced at any
* time using using a call to update() with no parameters.
*
* This function is a convenience wrapper for the more general control() function call.
*
* \param mode one of the types in controlValue_t (ON/OFF).
* \return No return value.
*/
void update(controlValue_t mode) { control(UPDATE, mode); };
/**
* Force an update of all devices
*
* Used when auto updates have been turned off through the control
* method. This will force all buffered changes to be written to
* all the connected devices.
*
* \return no return value.
*/
void update(void) { flushBufferAll(); };
/**
* Turn display wraparound on or off.
*
* When shifting left or right, up or down, the outermost edge is normally lost and a blank
* row or column inserted on the opposite side. If this options is enabled, the edge is wrapped
* around to the opposite side.
*
* This function is a convenience wrapper for the more general control() function call.
*
* \param mode one of the types in controlValue_t (ON/OFF).
* \return No return value.
*/
void wraparound(controlValue_t mode) { control(WRAPAROUND, mode); };
/** @} */
//--------------------------------------------------------------
/** \name Methods for managing specific devices or display buffers.
* @{
*/
/**
* Clear all display data in the specified buffer.
*
* \param buf address of the buffer to clear [0..getDeviceCount()-1].
* \return false if parameter errors, true otherwise.
*/
bool clear(uint8_t buf);
/**
* Get the state of the LEDs in a specific column.
*
* This method operates on the specific buffer, returning the bit field value of
* the LEDs in the column.
*
* \param buf address of the display [0..getDeviceCount()-1].
* \param c column which is to be set [0..COL_SIZE-1].
* \return uint8_t value with each bit set to 1 if the corresponding LED is lit. 0 is returned for parameter error.
*/
uint8_t getColumn(uint8_t buf, uint8_t c);
/**
* Get the state of the LEDs in a specified row.
*
* This method operates on the specific buffer, returning the bit field value of
* the LEDs in the row.
*
* \param buf address of the display [0..getDeviceCount()-1].
* \param r row which is to be set [0..ROW_SIZE-1].
* \return uint8_t value with each bit set to 1 if the corresponding LED is lit. 0 is returned for parameter error.
*/
uint8_t getRow(uint8_t buf, uint8_t r);
/**
* Set all LEDs in a column to a new state.
*
* This method operates on a specific buffer, setting the value of the LEDs in
* the column to the specified value bit field. The method is useful for
* drawing patterns and lines vertically on the display device.
* The least significant bit of the value is the lowest column number.
*
* \param buf address of the display [0..getDeviceCount()-1].
* \param c column which is to be set [0..COL_SIZE-1].
* \param value each bit set to 1 will light up the corresponding LED.
* \return false if parameter errors, true otherwise.
*/
bool setColumn(uint8_t buf, uint8_t c, uint8_t value);
/**
* Set all LEDs in a row to a new state.
*
* This method operates on a specific device, setting the value of the LEDs in
* the row to the specified value bit field. The method is useful for
* drawing patterns and lines horizontally across the display device.
* The least significant bit of the value is the lowest row number.
*
* \param buf address of the display [0..getDeviceCount()-1].
* \param r row which is to be set [0..ROW_SIZE-1].
* \param value each bit set to 1 within this byte will light up the corresponding LED.
* \return false if parameter errors, true otherwise.
*/
bool setRow(uint8_t buf, uint8_t r, uint8_t value);
/**
* Apply a transformation to the data in the specified device.
*
* The buffer for one device can be transformed using one of the enumerated
* transformations in transformType_t. The transformation is limited to the
* nominated device buffer only (ie, there is no overflow to an adjacent device).
*
* \param buf address of the display [0..getBufferCount()-1].
* \param ttype one of the transformation types in transformType_t.
* \return false if parameter errors, true otherwise.
*/
bool transform(uint8_t buf, transformType_t ttype);
/**
* Force an update of one buffer.
*
* Used when auto updates have been turned off through the control()
* method. This will force all buffered display changes to be written to
* the specified device at the same time.
* Note that control() messages are not buffered but cause immediate action.
*
* \param buf address of the display [0..getBufferCount()-1].
* \return No return value.
*/
void update(uint8_t buf) { flushBuffer(buf); };
/** @} */
#if USE_LOCAL_FONT
//--------------------------------------------------------------
/** \name Methods for font and characters.
* @{
*/
/**
* Load a character from the font data into a user buffer.
*
* Copy the bitmap for a library font character (current font set by setFont()) and
* return it in the data area passed by the user. If the user buffer is not large
* enough, only the first size elements are copied to the buffer.
*
* NOTE: This function is only available if the library defined value
* USE_LOCAL_FONT is set to 1.
*
* \param c the character to retrieve.
* \param size the size of the user buffer in unit8_t units.
* \param buf address of the user buffer supplied.
* \return width (in columns) of the character, 0 if parameter errors.
*/
uint8_t getChar(uint8_t c, uint8_t size, uint8_t *buf);
/**
* Load a character from the font data starting at a specific column.
*
* Load a character from the font table directly into the display at the column
* specified. The currently selected font table is used as the source.
*
* NOTE: This function is only available if the library defined value
* USE_LOCAL_FONT is set to 1.
*
* \param col column of the display in the range accepted [0..getColumnCount()-1].
* \param c the character to display.
* \return width (in columns) of the character, 0 if parameter errors.
*/
uint8_t setChar(uint16_t col, uint8_t c);
/**
* Set the current font table.
*
* Font data is stored in PROGMEM, in the format described elsewhere in the
* documentation. All characters retrieved or used after this call will use
* the nominated font (default or user defined). To specify a user defined
* character set, pass the PROGMEM address of the font table. Passing a nullptr
* resets the font table to the library default table.
*
* This function also causes the font index table to be recreated if the
* library defined value USE_INDEX_TABLE is set to 1.
*
* NOTE: This function is only available if the library defined value
* USE_LOCAL_FONT is set to 1.
*
* \param f fontType_t pointer to the table of font data in PROGMEM or nullptr.
* \return false if parameter errors, true otherwise.
*/
bool setFont(fontType_t *f);
/**
* Get the maximum width character for the font.
*
* Returns the number of columns for the widest character in the currently
* selected font table. Useful to allocated buffers of the right size before
* loading characters from the font table.
*
* NOTE: This function is only available if the library defined value
* USE_LOCAL_FONT is set to 1.
*
* \return number of columns (width) for the widest character.
*/
uint8_t getMaxFontWidth(void);
/**
* Get the pointer to current font table.
*
* Returns the pointer to the current font table. Useful if user code needs
* to replace the current font temporarily and then restore previous font.
*
* NOTE: This function is only available if the library defined value
* USE_LOCAL_FONT is set to 1.
*
* \return pointer to the start of the font table in PROGMEM.
*/
fontType_t *getFont(void) { return(_fontData); };
#endif // USE_LOCAL_FONT
/** @} */
private:
typedef struct
{
uint8_t dig[ROW_SIZE]; // data for each digit of the MAX72xx (DIG0-DIG7)
uint8_t changed; // one bit for each digit changed ('dirty bit')
} deviceInfo_t;
// SPI interface data
uint8_t _dataPin; // DATA is shifted out of this pin ...
uint8_t _clkPin; // ... signaled by a CLOCK on this pin ...
uint8_t _csPin; // ... and LOADed when the chip select pin is driven HIGH to LOW
bool _hardwareSPI; // true if SPI interface is the hardware interface
// Device buffer data
uint8_t _maxDevices; // maximum number of devices in use
deviceInfo_t* _matrix;// the current status of the LED matrix (buffers)
uint8_t* _spiData; // data buffer for writing to SPI interface
// User callback function for shifting operations
uint8_t (*_cbShiftDataIn)(uint8_t dev, transformType_t t);
void (*_cbShiftDataOut)(uint8_t dev, transformType_t t, uint8_t colData);
// Control data for the library
bool _updateEnabled; // update the display when this is true, suspend otherwise
bool _wrapAround; // when shifting, wrap left to right and vice versa (circular buffer)
#if USE_LOCAL_FONT
// Font related data
fontType_t *_fontData; // pointer to the current font data being used
uint16_t *_fontIndex; // font index for faster access to font table offsets
uint16_t getFontCharOffset(uint8_t c); // find the character in the font data
void buildFontIndex(void); // build a font index
#endif
// Private functions
void spiSend(void); // do the actual physical communications task
void spiClearBuffer(void); // clear the SPI send buffer
void controlHardware(uint8_t dev, controlRequest_t mode, int value); // set hardware control commands
void controlLibrary(controlRequest_t mode, int value); // set internal control commands
void flushBuffer(uint8_t buf);// determine what needs to be sent for one device and transmit
void flushBufferAll(); // determine what needs to be sent for all devices and transmit
uint8_t bitReverse(uint8_t b); // reverse the order of bits in the byte
bool transformBuffer(uint8_t buf, transformType_t ttype); // internal transform function
bool copyRow(uint8_t buf, uint8_t rSrc, uint8_t rDest); // copy a row from Src to Dest
bool copyColumn(uint8_t buf, uint8_t cSrc, uint8_t cDest);// copy a row from Src to Dest
};
#endif

View File

@@ -0,0 +1,373 @@
/*
MD_MAX72xx - Library for using a MAX7219/7221 LED matrix controller
See header file for comments
This file contains methods that act on display buffers.
Copyright (C) 2012-13 Marco Colli. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include "MD_MAX72xx.h"
#include "MD_MAX72xx_lib.h"
/**
* \file
* \brief Implements buffer related methods
*/
bool MD_MAX72XX::clear(uint8_t buf)
{
if (buf > LAST_BUFFER)
return(false);
memset(_matrix[buf].dig, 0, sizeof(_matrix[buf].dig));
_matrix[buf].changed = ALL_CHANGED;
if (_updateEnabled) flushBuffer(buf);
return(true);
}
uint8_t MD_MAX72XX::bitReverse(uint8_t b)
// Reverse the order of bits within a byte.
// Returns the reversed byte value.
{
b = ((b & 0xf0) >> 4) | ((b & 0x0f) << 4);
b = ((b & 0xcc) >> 2) | ((b & 0x33) << 2);
b = ((b & 0xaa) >> 1) | ((b & 0x55) << 1);
return(b);
}
#if HW_DIG_ROWS
bool MD_MAX72XX::copyColumn(uint8_t buf, uint8_t cSrc, uint8_t cDest)
#else
bool MD_MAX72XX::copyRow(uint8_t buf, uint8_t cSrc, uint8_t cDest)
#endif
// Src and Dest are in pixel coordinates.
// if we are just copying rows there is no need to repackage any data
{
uint8_t maskSrc = 1 << HW_COL(cSrc); // which column of bits is the column data
#if HW_DIG_ROWS
PRINT("\ncopyCol: (", buf);
#else
PRINT("\ncopyRow: (", buf);
#endif
PRINT(", ", cSrc);
PRINT(", ", cDest);
PRINTS(") ");
if ((buf > LAST_BUFFER) || (cSrc >= COL_SIZE) || (cDest >= COL_SIZE))
return(false);
for (uint8_t i=0; i<ROW_SIZE; i++)
{
if (_matrix[buf].dig[i] & maskSrc)
bitSet(_matrix[buf].dig[i], HW_COL(cDest));
else
bitClear(_matrix[buf].dig[i], HW_COL(cDest));
}
_matrix[buf].changed = ALL_CHANGED;
if (_updateEnabled) flushBuffer(buf);
return(true);
}
#if HW_DIG_ROWS
uint8_t MD_MAX72XX::getColumn(uint8_t buf, uint8_t c)
#else
uint8_t MD_MAX72XX::getRow(uint8_t buf, uint8_t c)
#endif
// c is in pixel coordinates and the return value must be in pixel coordinate order
{
uint8_t mask = 1 << HW_COL(c); // which column of bits is the column data
uint8_t value = 0; // assembles data to be returned to caller
#if HW_DIG_ROWS
PRINT("\ngetCol: (", buf);
#else
PRINT("\ngetRow: (", buf);
#endif
PRINT(", ", c);
PRINTS(") ");
if ((buf > LAST_BUFFER) || (c >= COL_SIZE))
return(0);
PRINTX("mask 0x", mask);
// for each digit data, pull out the column/row bit and place
// it in value. The loop creates the data in pixel coordinate order as it goes.
for (uint8_t i=0; i<ROW_SIZE; i++)
{
if (_matrix[buf].dig[HW_ROW(i)] & mask)
bitSet(value, i);
}
PRINTX(" value 0x", value);
return(value);
}
#if HW_DIG_ROWS
bool MD_MAX72XX::setColumn(uint8_t buf, uint8_t c, uint8_t value)
#else
bool MD_MAX72XX::setRow(uint8_t buf, uint8_t c, uint8_t value)
#endif
// c and value are in pixel coordinate order
{
#if HW_DIG_ROWS
PRINT("\nsetCol: (", buf);
#else
PRINT("\nsetRow: (", buf);
#endif
PRINT(", ", c);
PRINTX(") 0x", value);
if ((buf > LAST_BUFFER) || (c >= COL_SIZE))
return(false);
for (uint8_t i=0; i<ROW_SIZE; i++)
{
if (value & (1 << i)) // mask off next column value passed in and set it in the dig buffer
bitSet(_matrix[buf].dig[HW_ROW(i)], HW_COL(c));
else
bitClear(_matrix[buf].dig[HW_ROW(i)], HW_COL(c));
}
_matrix[buf].changed = ALL_CHANGED;
if (_updateEnabled) flushBuffer(buf);
return(true);
}
#if HW_DIG_ROWS
bool MD_MAX72XX::copyRow(uint8_t buf, uint8_t rSrc, uint8_t rDest)
#else
bool MD_MAX72XX::copyColumn(uint8_t buf, uint8_t rSrc, uint8_t rDest)
#endif
// Src and Dest are in pixel coordinates.
// if we are just copying digits there is no need to repackage any data
{
#if HW_DIG_ROWS
PRINT("\ncopyRow: (", buf);
#else
PRINT("\ncopyColumn: (", buf);
#endif
PRINT(", ", rSrc);
PRINT(", ", rDest);
PRINTS(") ");
if ((buf > LAST_BUFFER) || (rSrc >= ROW_SIZE) || (rDest >= ROW_SIZE))
return(false);
_matrix[buf].dig[HW_ROW(rDest)] = _matrix[buf].dig[HW_ROW(rSrc)];
bitSet(_matrix[buf].changed, HW_ROW(rDest));
if (_updateEnabled) flushBuffer(buf);
return(true);
}
#if HW_DIG_ROWS
uint8_t MD_MAX72XX::getRow(uint8_t buf, uint8_t r)
#else
uint8_t MD_MAX72XX::getColumn(uint8_t buf, uint8_t r)
#endif
// r is in pixel coordinates for this buffer
// returned value is in pixel coordinates
{
#if HW_DIG_ROWS
PRINT("\ngetRow: (", buf);
#else
PRINT("\ngetCol: (", buf);
#endif
PRINT(", ", r);
PRINTS(") ");
if ((buf > LAST_BUFFER) || (r >= ROW_SIZE))
return(0);
uint8_t value = HW_REV_COLS ? bitReverse(_matrix[buf].dig[HW_ROW(r)]) : _matrix[buf].dig[HW_ROW(r)];
PRINTX("0x", value);
return(value);
}
#if HW_DIG_ROWS
bool MD_MAX72XX::setRow(uint8_t buf, uint8_t r, uint8_t value)
#else
bool MD_MAX72XX::setColumn(uint8_t buf, uint8_t r, uint8_t value)
#endif
// r and value are in pixel coordinates
{
#if HW_DIG_ROWS
PRINT("\nsetRow: (", buf);
#else
PRINT("\nsetCol: (", buf);
#endif
PRINT(", ", r);
PRINTX(") 0x", value);
if ((buf > LAST_BUFFER) || (r >= ROW_SIZE))
return(false);
_matrix[buf].dig[HW_ROW(r)] = HW_REV_COLS ? bitReverse(value) : value;
bitSet(_matrix[buf].changed, HW_ROW(r));
if (_updateEnabled) flushBuffer(buf);
return(true);
}
bool MD_MAX72XX::transform(uint8_t buf, transformType_t ttype)
{
if (buf > LAST_BUFFER)
return(false);
if (!transformBuffer(buf, ttype))
return(false);
if (_updateEnabled) flushBuffer(buf);
return(true);
}
bool MD_MAX72XX::transformBuffer(uint8_t buf, transformType_t ttype)
{
uint8_t t[ROW_SIZE];
switch (ttype)
{
//--------------
case TSL: // Transform Shift Left one pixel element
#if HW_DIG_ROWS
for (uint8_t i=0; i<ROW_SIZE; i++)
#if HW_REV_COLS
_matrix[buf].dig[i] >>= 1;
#else
_matrix[buf].dig[i] <<= 1;
#endif
#else
#warning HW_DIG_ROWS=0
for (uint8_t i=ROW_SIZE; i>0; --i)
_matrix[buf].dig[i] = _matrix[buf].dig[i-1];
#endif
break;
//--------------
case TSR: // Transform Shift Right one pixel element
#if HW_DIG_ROWS
for (uint8_t i=0; i<ROW_SIZE; i++)
#if HW_REV_COLS
_matrix[buf].dig[i] <<= 1;
#else
_matrix[buf].dig[i] >>= 1;
#endif
#else
for (uint8_t i=0; i<ROW_SIZE-1; i++)
_matrix[buf].dig[i] = _matrix[buf].dig[i+1];
#endif
break;
//--------------
case TSU: // Transform Shift Up one pixel element
if (_wrapAround) // save the first row or a zero row
t[0] = getRow(buf, 0);
else
t[0] = 0;
#if HW_DIG_ROWS
for (uint8_t i=0; i<ROW_SIZE-1; i++)
copyRow(buf, i+1, i);
#else
for (int8_t i=ROW_SIZE-1; i>=0; i--)
_matrix[buf].dig[i] <<= 1;
#endif
setRow(buf, ROW_SIZE-1, t[0]);
break;
//--------------
case TSD: // Transform Shift Down one pixel element
if (_wrapAround) // save the last row or a zero row
t[0] = getRow(buf, ROW_SIZE-1);
else
t[0] = 0;
#if HW_DIG_ROWS
for (uint8_t i=ROW_SIZE; i>0; --i)
copyRow(buf, i-1, i);
#else
for (uint8_t i=0; i<ROW_SIZE; i++)
_matrix[buf].dig[i] >>= 1;
#endif
setRow(buf, 0, t[0]);
break;
//--------------
#if HW_DIG_ROWS
case TFLR: // Transform Flip Left to Right
#else
case TFUD: // Transform Flip Up to Down
#endif
for (uint8_t i=0; i<ROW_SIZE; i++)
_matrix[buf].dig[i] = bitReverse(_matrix[buf].dig[i]);
break;
//--------------
#if HW_DIG_ROWS
case TFUD: // Transform Flip Up to Down
#else
case TFLR: // Transform Flip Left to Right
#endif
for (uint8_t i=0; i<ROW_SIZE/2; i++)
{
uint8_t t = _matrix[buf].dig[i];
_matrix[buf].dig[i] = _matrix[buf].dig[ROW_SIZE-i-1];
_matrix[buf].dig[ROW_SIZE-i-1] = t;
}
break;
//--------------
case TRC: // Transform Rotate Clockwise
for (uint8_t i=0; i<ROW_SIZE; i++)
t[i] = getColumn(buf, COL_SIZE-1-i);
for (uint8_t i=0; i<ROW_SIZE; i++)
setRow(buf, i, t[i]);
break;
//--------------
case TINV: // Transform INVert
for (uint8_t i=0; i<ROW_SIZE; i++)
_matrix[buf].dig[i] = ~_matrix[buf].dig[i];
break;
default:
return(false);
}
_matrix[buf].changed = ALL_CHANGED;
return(true);
}

View File

@@ -0,0 +1,427 @@
/*
MD_MAX72xx - Library for using a MAX7219/7221 LED matrix controller
See header file for comments
This file contains methods that work with the fonts and characters defined in the library
Copyright (C) 2012-14 Marco Colli. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include "MD_MAX72xx.h"
#include "MD_MAX72xx_lib.h"
/**
* \file
* \brief Implements font definition and methods
*/
#if USE_LOCAL_FONT
// Local font handling functions if the option is enabled
void MD_MAX72XX::buildFontIndex(void)
{
uint16_t offset = 0;
if (_fontIndex == nullptr)
return;
PRINTS("\nBuilding font index");
for (uint16_t i=0; i<ASCII_INDEX_SIZE; i++)
{
_fontIndex[i] = offset;
PRINT("\nASCII '", i);
PRINT("' offset ", _fontIndex[i]);
offset += pgm_read_byte(_fontData+offset);
offset++;
}
}
uint8_t MD_MAX72XX::getMaxFontWidth(void)
{
uint8_t max = 0;
uint8_t charWidth;
uint16_t offset = 0;
PRINTS("\nFinding max font width");
if (_fontData != nullptr)
{
for (uint16_t i = 0; i < ASCII_INDEX_SIZE; i++)
{
charWidth = pgm_read_byte(_fontData + offset);
/*
PRINT("\nASCII '", i);
PRINT("' offset ", offset);
PRINT("' width ", charWidth);
*/
if (charWidth > max)
{
max = charWidth;
PRINT(":", max);
}
offset += charWidth; // skip character data
offset++; // skip size byte
}
}
PRINT(" max ", max);
return(max);
}
uint16_t MD_MAX72XX::getFontCharOffset(uint8_t c)
{
PRINT("\nfontOffset ASCII ", c);
if (_fontIndex != nullptr)
{
PRINTS(" from Table");
return(_fontIndex[c]);
}
else
{
PRINTS(" by Search ");
uint16_t offset = 0;
for (uint8_t i=0; i<c; i++)
{
PRINTS(".");
offset += pgm_read_byte(_fontData+offset);
offset++; // skip size byte we used above
}
PRINT(" searched offset ", offset);
return(offset);
}
}
bool MD_MAX72XX::setFont(fontType_t *f)
{
_fontData = (f == nullptr ? _sysfont_var : f);
buildFontIndex();
return(true);
}
uint8_t MD_MAX72XX::getChar(uint8_t c, uint8_t size, uint8_t *buf)
{
PRINT("\ngetChar: '", (char)c);
PRINT("' ASC ", c);
PRINT(" - bufsize ", size);
if (buf == nullptr)
return(0);
uint16_t offset = getFontCharOffset(c);
size = min(size, pgm_read_byte(_fontData+offset));
offset++; // skip the size byte
for (uint8_t i=0; i<size; i++)
*buf++ = pgm_read_byte(_fontData+offset+i);
return(size);
}
uint8_t MD_MAX72XX::setChar(uint16_t col, uint8_t c)
{
PRINT("\nsetChar: '", c);
PRINT("' column ", col);
boolean b = _updateEnabled;
uint16_t offset = getFontCharOffset(c);
uint8_t size = pgm_read_byte(_fontData+offset);
offset++; // skip the size byte
_updateEnabled = false;
for (int8_t i=0; i<size; i++)
{
uint8_t colData = pgm_read_byte(_fontData+offset+i);
setColumn(col--, colData);
}
_updateEnabled = b;
if (_updateEnabled) flushBufferAll();
return(size);
}
// Standard font - variable spacing
MD_MAX72XX::fontType_t PROGMEM _sysfont_var[] =
{
0, // 0 - 'Empty Cell'
5, 0x3e, 0x5b, 0x4f, 0x5b, 0x3e, // 1 - 'Sad Smiley'
5, 0x3e, 0x6b, 0x4f, 0x6b, 0x3e, // 2 - 'Happy Smiley'
5, 0x1c, 0x3e, 0x7c, 0x3e, 0x1c, // 3 - 'Heart'
5, 0x18, 0x3c, 0x7e, 0x3c, 0x18, // 4 - 'Diamond'
5, 0x1c, 0x57, 0x7d, 0x57, 0x1c, // 5 - 'Clubs'
5, 0x1c, 0x5e, 0x7f, 0x5e, 0x1c, // 6 - 'Spades'
4, 0x00, 0x18, 0x3c, 0x18, // 7 - 'Bullet Point'
5, 0xff, 0xe7, 0xc3, 0xe7, 0xff, // 8 - 'Rev Bullet Point'
4, 0x00, 0x18, 0x24, 0x18, // 9 - 'Hollow Bullet Point'
5, 0xff, 0xe7, 0xdb, 0xe7, 0xff, // 10 - 'Rev Hollow BP'
5, 0x30, 0x48, 0x3a, 0x06, 0x0e, // 11 - 'Male'
5, 0x26, 0x29, 0x79, 0x29, 0x26, // 12 - 'Female'
5, 0x40, 0x7f, 0x05, 0x05, 0x07, // 13 - 'Music Note 1'
5, 0x40, 0x7f, 0x05, 0x25, 0x3f, // 14 - 'Music Note 2'
5, 0x5a, 0x3c, 0xe7, 0x3c, 0x5a, // 15 - 'Snowflake'
5, 0x7f, 0x3e, 0x1c, 0x1c, 0x08, // 16 - 'Right Pointer'
5, 0x08, 0x1c, 0x1c, 0x3e, 0x7f, // 17 - 'Left Pointer'
5, 0x14, 0x22, 0x7f, 0x22, 0x14, // 18 - 'UpDown Arrows'
5, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, // 19 - 'Double Exclamation'
5, 0x06, 0x09, 0x7f, 0x01, 0x7f, // 20 - 'Paragraph Mark'
4, 0x66, 0x89, 0x95, 0x6a, // 21 - 'Section Mark'
5, 0x60, 0x60, 0x60, 0x60, 0x60, // 22 - 'Double Underline'
5, 0x94, 0xa2, 0xff, 0xa2, 0x94, // 23 - 'UpDown Underlined'
5, 0x08, 0x04, 0x7e, 0x04, 0x08, // 24 - 'Up Arrow'
5, 0x10, 0x20, 0x7e, 0x20, 0x10, // 25 - 'Down Arrow'
5, 0x08, 0x08, 0x2a, 0x1c, 0x08, // 26 - 'Right Arrow'
5, 0x08, 0x1c, 0x2a, 0x08, 0x08, // 27 - 'Left Arrow'
5, 0x1e, 0x10, 0x10, 0x10, 0x10, // 28 - 'Angled'
5, 0x0c, 0x1e, 0x0c, 0x1e, 0x0c, // 29 - 'Squashed #'
5, 0x30, 0x38, 0x3e, 0x38, 0x30, // 30 - 'Up Pointer'
5, 0x06, 0x0e, 0x3e, 0x0e, 0x06, // 31 - 'Down Pointer'
1, 0x00, // 32 - 'Space'
1, 0x5f, // 33 - '!'
3, 0x07, 0x00, 0x07, // 34 - '"'
5, 0x14, 0x7f, 0x14, 0x7f, 0x14, // 35 - '#'
5, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // 36 - '$'
5, 0x23, 0x13, 0x08, 0x64, 0x62, // 37 - '%'
5, 0x36, 0x49, 0x56, 0x20, 0x50, // 38 - '&'
3, 0x08, 0x07, 0x03, // 39 - '''
3, 0x1c, 0x22, 0x41, // 40 - '('
3, 0x41, 0x22, 0x1c, // 41 - ')'
5, 0x2a, 0x1c, 0x7f, 0x1c, 0x2a, // 42 - '*'
5, 0x08, 0x08, 0x3e, 0x08, 0x08, // 43 - '+'
3, 0x80, 0x70, 0x30, // 44 - ','
5, 0x08, 0x08, 0x08, 0x08, 0x08, // 45 - '-'
2, 0x60, 0x60, // 46 - '.'
5, 0x20, 0x10, 0x08, 0x04, 0x02, // 47 - '/'
5, 0x3e, 0x51, 0x49, 0x45, 0x3e, // 48 - '0'
3, 0x42, 0x7f, 0x40, // 49 - '1'
5, 0x72, 0x49, 0x49, 0x49, 0x46, // 50 - '2'
5, 0x21, 0x41, 0x49, 0x4d, 0x33, // 51 - '3'
5, 0x18, 0x14, 0x12, 0x7f, 0x10, // 52 - '4'
5, 0x27, 0x45, 0x45, 0x45, 0x39, // 53 - '5'
5, 0x3c, 0x4a, 0x49, 0x49, 0x31, // 54 - '6'
5, 0x41, 0x21, 0x11, 0x09, 0x07, // 55 - '7'
5, 0x36, 0x49, 0x49, 0x49, 0x36, // 56 - '8'
5, 0x46, 0x49, 0x49, 0x29, 0x1e, // 57 - '9'
1, 0x14, // 58 - ':'
2, 0x80, 0x68, // 59 - ';'
4, 0x08, 0x14, 0x22, 0x41, // 60 - '<'
5, 0x14, 0x14, 0x14, 0x14, 0x14, // 61 - '='
4, 0x41, 0x22, 0x14, 0x08, // 62 - '>'
5, 0x02, 0x01, 0x59, 0x09, 0x06, // 63 - '?'
5, 0x3e, 0x41, 0x5d, 0x59, 0x4e, // 64 - '@'
5, 0x7c, 0x12, 0x11, 0x12, 0x7c, // 65 - 'A'
5, 0x7f, 0x49, 0x49, 0x49, 0x36, // 66 - 'B'
5, 0x3e, 0x41, 0x41, 0x41, 0x22, // 67 - 'C'
5, 0x7f, 0x41, 0x41, 0x41, 0x3e, // 68 - 'D'
5, 0x7f, 0x49, 0x49, 0x49, 0x41, // 69 - 'E'
5, 0x7f, 0x09, 0x09, 0x09, 0x01, // 70 - 'F'
5, 0x3e, 0x41, 0x41, 0x51, 0x73, // 71 - 'G'
5, 0x7f, 0x08, 0x08, 0x08, 0x7f, // 72 - 'H'
3, 0x41, 0x7f, 0x41, // 73 - 'I'
5, 0x20, 0x40, 0x41, 0x3f, 0x01, // 74 - 'J'
5, 0x7f, 0x08, 0x14, 0x22, 0x41, // 75 - 'K'
5, 0x7f, 0x40, 0x40, 0x40, 0x40, // 76 - 'L'
5, 0x7f, 0x02, 0x1c, 0x02, 0x7f, // 77 - 'M'
5, 0x7f, 0x04, 0x08, 0x10, 0x7f, // 78 - 'N'
5, 0x3e, 0x41, 0x41, 0x41, 0x3e, // 79 - 'O'
5, 0x7f, 0x09, 0x09, 0x09, 0x06, // 80 - 'P'
5, 0x3e, 0x41, 0x51, 0x21, 0x5e, // 81 - 'Q'
5, 0x7f, 0x09, 0x19, 0x29, 0x46, // 82 - 'R'
5, 0x26, 0x49, 0x49, 0x49, 0x32, // 83 - 'S'
5, 0x03, 0x01, 0x7f, 0x01, 0x03, // 84 - 'T'
5, 0x3f, 0x40, 0x40, 0x40, 0x3f, // 85 - 'U'
5, 0x1f, 0x20, 0x40, 0x20, 0x1f, // 86 - 'V'
5, 0x3f, 0x40, 0x38, 0x40, 0x3f, // 87 - 'W'
5, 0x63, 0x14, 0x08, 0x14, 0x63, // 88 - 'X'
5, 0x03, 0x04, 0x78, 0x04, 0x03, // 89 - 'Y'
5, 0x61, 0x59, 0x49, 0x4d, 0x43, // 90 - 'Z'
3, 0x7f, 0x41, 0x41, // 91 - '['
5, 0x02, 0x04, 0x08, 0x10, 0x20, // 92 - '\'
3, 0x41, 0x41, 0x7f, // 93 - ']'
5, 0x04, 0x02, 0x01, 0x02, 0x04, // 94 - '^'
5, 0x40, 0x40, 0x40, 0x40, 0x40, // 95 - '_'
3, 0x03, 0x07, 0x08, // 96 - '`'
5, 0x20, 0x54, 0x54, 0x78, 0x40, // 97 - 'a'
5, 0x7f, 0x28, 0x44, 0x44, 0x38, // 98 - 'b'
5, 0x38, 0x44, 0x44, 0x44, 0x28, // 99 - 'c'
5, 0x38, 0x44, 0x44, 0x28, 0x7f, // 100 - 'd'
5, 0x38, 0x54, 0x54, 0x54, 0x18, // 101 - 'e'
4, 0x08, 0x7e, 0x09, 0x02, // 102 - 'f'
5, 0x18, 0xa4, 0xa4, 0x9c, 0x78, // 103 - 'g'
5, 0x7f, 0x08, 0x04, 0x04, 0x78, // 104 - 'h'
3, 0x44, 0x7d, 0x40, // 105 - 'i'
4, 0x40, 0x80, 0x80, 0x7a, // 106 - 'j'
4, 0x7f, 0x10, 0x28, 0x44, // 107 - 'k'
3, 0x41, 0x7f, 0x40, // 108 - 'l'
5, 0x7c, 0x04, 0x78, 0x04, 0x78, // 109 - 'm'
5, 0x7c, 0x08, 0x04, 0x04, 0x78, // 110 - 'n'
5, 0x38, 0x44, 0x44, 0x44, 0x38, // 111 - 'o'
5, 0xfc, 0x18, 0x24, 0x24, 0x18, // 112 - 'p'
5, 0x18, 0x24, 0x24, 0x18, 0xfc, // 113 - 'q'
5, 0x7c, 0x08, 0x04, 0x04, 0x08, // 114 - 'r'
5, 0x48, 0x54, 0x54, 0x54, 0x24, // 115 - 's'
4, 0x04, 0x3f, 0x44, 0x24, // 116 - 't'
5, 0x3c, 0x40, 0x40, 0x20, 0x7c, // 117 - 'u'
5, 0x1c, 0x20, 0x40, 0x20, 0x1c, // 118 - 'v'
5, 0x3c, 0x40, 0x30, 0x40, 0x3c, // 119 - 'w'
5, 0x44, 0x28, 0x10, 0x28, 0x44, // 120 - 'x'
5, 0x4c, 0x90, 0x90, 0x90, 0x7c, // 121 - 'y'
5, 0x44, 0x64, 0x54, 0x4c, 0x44, // 122 - 'z'
3, 0x08, 0x36, 0x41, // 123 - '{'
1, 0x77, // 124 - '|'
3, 0x41, 0x36, 0x08, // 125 - '}'
5, 0x02, 0x01, 0x02, 0x04, 0x02, // 126 - '~'
5, 0x3c, 0x26, 0x23, 0x26, 0x3c, // 127 - 'Hollow Up Arrow'
5, 0x1e, 0xa1, 0xa1, 0x61, 0x12, // 128 - 'C sedilla'
5, 0x38, 0x42, 0x40, 0x22, 0x78, // 129 - 'u umlaut'
5, 0x38, 0x54, 0x54, 0x55, 0x59, // 130 - 'e acute'
5, 0x21, 0x55, 0x55, 0x79, 0x41, // 131 - 'a accent'
5, 0x21, 0x54, 0x54, 0x78, 0x41, // 132 - 'a umlaut'
5, 0x21, 0x55, 0x54, 0x78, 0x40, // 133 - 'a grave'
5, 0x20, 0x54, 0x55, 0x79, 0x40, // 134 - 'a acute'
5, 0x18, 0x3c, 0xa4, 0xe4, 0x24, // 135 - 'c sedilla'
5, 0x39, 0x55, 0x55, 0x55, 0x59, // 136 - 'e accent'
5, 0x38, 0x55, 0x54, 0x55, 0x58, // 137 - 'e umlaut'
5, 0x39, 0x55, 0x54, 0x54, 0x58, // 138 - 'e grave'
3, 0x45, 0x7c, 0x41, // 139 - 'i umlaut'
4, 0x02, 0x45, 0x7d, 0x42, // 140 - 'i hat'
4, 0x01, 0x45, 0x7c, 0x40, // 141 - 'i grave'
5, 0xf0, 0x29, 0x24, 0x29, 0xf0, // 142 - 'A umlaut'
5, 0xf0, 0x28, 0x25, 0x28, 0xf0, // 143 - 'A dot'
4, 0x7c, 0x54, 0x55, 0x45, // 144 - 'E grave'
7, 0x20, 0x54, 0x54, 0x7c, 0x54, 0x54, 0x08, // 145 - 'ae'
6, 0x7c, 0x0a, 0x09, 0x7f, 0x49, 0x49, // 146 - 'AE'
5, 0x32, 0x49, 0x49, 0x49, 0x32, // 147 - 'o hat'
5, 0x30, 0x4a, 0x48, 0x4a, 0x30, // 148 - 'o umlaut'
5, 0x32, 0x4a, 0x48, 0x48, 0x30, // 149 - 'o grave'
5, 0x3a, 0x41, 0x41, 0x21, 0x7a, // 150 - 'u hat'
5, 0x3a, 0x42, 0x40, 0x20, 0x78, // 151 - 'u grave'
4, 0x9d, 0xa0, 0xa0, 0x7d, // 152 - 'y umlaut'
5, 0x38, 0x45, 0x44, 0x45, 0x38, // 153 - 'O umlaut'
5, 0x3c, 0x41, 0x40, 0x41, 0x3c, // 154 - 'U umlaut'
5, 0x3c, 0x24, 0xff, 0x24, 0x24, // 155 - 'Cents'
5, 0x48, 0x7e, 0x49, 0x43, 0x66, // 156 - 'Pounds'
5, 0x2b, 0x2f, 0xfc, 0x2f, 0x2b, // 157 - 'Yen'
5, 0xff, 0x09, 0x29, 0xf6, 0x20, // 158 - 'R +'
5, 0xc0, 0x88, 0x7e, 0x09, 0x03, // 159 - 'f notation'
5, 0x20, 0x54, 0x54, 0x79, 0x41, // 160 - 'a acute'
3, 0x44, 0x7d, 0x41, // 161 - 'i acute'
5, 0x30, 0x48, 0x48, 0x4a, 0x32, // 162 - 'o acute'
5, 0x38, 0x40, 0x40, 0x22, 0x7a, // 163 - 'u acute'
4, 0x7a, 0x0a, 0x0a, 0x72, // 164 - 'n accent'
5, 0x7d, 0x0d, 0x19, 0x31, 0x7d, // 165 - 'N accent'
5, 0x26, 0x29, 0x29, 0x2f, 0x28, // 166
5, 0x26, 0x29, 0x29, 0x29, 0x26, // 167
5, 0x30, 0x48, 0x4d, 0x40, 0x20, // 168 - 'Inverted ?'
5, 0x38, 0x08, 0x08, 0x08, 0x08, // 169 - 'LH top corner'
5, 0x08, 0x08, 0x08, 0x08, 0x38, // 170 - 'RH top corner'
5, 0x2f, 0x10, 0xc8, 0xac, 0xba, // 171 - '1/2'
5, 0x2f, 0x10, 0x28, 0x34, 0xfa, // 172 - '1/4'
1, 0x7b, // 173 - '| split'
5, 0x08, 0x14, 0x2a, 0x14, 0x22, // 174 - '<<'
5, 0x22, 0x14, 0x2a, 0x14, 0x08, // 175 - '>>'
5, 0xaa, 0x00, 0x55, 0x00, 0xaa, // 176 - '30% shading'
5, 0xaa, 0x55, 0xaa, 0x55, 0xaa, // 177 - '50% shading'
5, 0x00, 0x00, 0x00, 0x00, 0xff, // 178 - 'Right side'
5, 0x10, 0x10, 0x10, 0x10, 0xff, // 179 - 'Right T'
5, 0x14, 0x14, 0x14, 0x14, 0xff, // 180 - 'Right T double H'
5, 0x10, 0x10, 0xff, 0x00, 0xff, // 181 - 'Right T double V'
5, 0x10, 0x10, 0xf0, 0x10, 0xf0, // 182 - 'Top Right double V'
5, 0x14, 0x14, 0x14, 0x14, 0xfc, // 183 - 'Top Right double H'
5, 0x14, 0x14, 0xf7, 0x00, 0xff, // 184 - 'Right T double all'
5, 0x00, 0x00, 0xff, 0x00, 0xff, // 185 - 'Right side double'
5, 0x14, 0x14, 0xf4, 0x04, 0xfc, // 186 - 'Top Right double'
5, 0x14, 0x14, 0x17, 0x10, 0x1f, // 187 - 'Bot Right double'
5, 0x10, 0x10, 0x1f, 0x10, 0x1f, // 188 - 'Bot Right double V'
5, 0x14, 0x14, 0x14, 0x14, 0x1f, // 189 - 'Bot Right double H'
5, 0x10, 0x10, 0x10, 0x10, 0xf0, // 190 - 'Top Right'
5, 0x00, 0x00, 0x00, 0x1f, 0x10, // 191 - 'Bot Left'
5, 0x10, 0x10, 0x10, 0x1f, 0x10, // 192 - 'Bot T'
5, 0x10, 0x10, 0x10, 0xf0, 0x10, // 193 - 'Top T'
5, 0x00, 0x00, 0x00, 0xff, 0x10, // 194 - 'Left T'
5, 0x10, 0x10, 0x10, 0x10, 0x10, // 195 - 'Top side'
5, 0x10, 0x10, 0x10, 0xff, 0x10, // 196 - 'Center +'
5, 0x00, 0x00, 0x00, 0xff, 0x14, // 197 - 'Left side double H'
5, 0x00, 0x00, 0xff, 0x00, 0xff, // 198 - 'Left side double'
5, 0x00, 0x00, 0x1f, 0x10, 0x17, // 199 - 'Bot Left double V'
5, 0x00, 0x00, 0xfc, 0x04, 0xf4, // 200 - 'Top Left double V'
5, 0x14, 0x14, 0x17, 0x10, 0x17, // 201 - 'Bot T double'
5, 0x14, 0x14, 0xf4, 0x04, 0xf4, // 202 - 'Top T double'
5, 0x00, 0x00, 0xff, 0x00, 0xf7, // 203 - 'Left Side double spl'
5, 0x14, 0x14, 0x14, 0x14, 0x14, // 204 - 'Center double'
5, 0x14, 0x14, 0xf7, 0x00, 0xf7, // 205 - 'Center + double'
5, 0x14, 0x14, 0x14, 0x17, 0x14, // 206 - 'Bot T double H'
5, 0x10, 0x10, 0x1f, 0x10, 0x1f, // 207 - 'Bot Right double V'
5, 0x14, 0x14, 0x14, 0xf4, 0x14, // 208 - 'Top T double H'
5, 0x10, 0x10, 0xf0, 0x10, 0xf0, // 209 - 'Top Right double V'
5, 0x00, 0x00, 0x1f, 0x10, 0x1f, // 210 - 'Bot Left double V'
5, 0x00, 0x00, 0x00, 0x1f, 0x14, // 211 - 'Bot Right double H'
5, 0x00, 0x00, 0x00, 0xfc, 0x14, // 212 - 'Top Right double H'
5, 0x00, 0x00, 0xf0, 0x10, 0xf0, // 213 - 'Top Right double V'
5, 0x10, 0x10, 0xff, 0x10, 0xff, // 214 - 'Center + double V'
5, 0x14, 0x14, 0x14, 0xff, 0x14, // 215 - 'Center + double H'
5, 0x10, 0x10, 0x10, 0x10, 0x1f, // 216 - 'Bot Right'
5, 0x00, 0x00, 0x00, 0xf0, 0x10, // 217 - 'Top Left'
5, 0xff, 0xff, 0xff, 0xff, 0xff, // 218 - 'Full Block'
5, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 219 - 'Half Block Bottom'
3, 0xff, 0xff, 0xff, // 220 - 'Half Block LHS'
5, 0x00, 0x00, 0x00, 0xff, 0xff, // 221 - 'Half Block RHS'
5, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, // 222 - 'Half Block Top'
5, 0x38, 0x44, 0x44, 0x38, 0x44, // 223 - 'Alpha'
5, 0x7c, 0x2a, 0x2a, 0x3e, 0x14, // 224 - 'Beta'
5, 0x7e, 0x02, 0x02, 0x06, 0x06, // 225 - 'Gamma'
5, 0x02, 0x7e, 0x02, 0x7e, 0x02, // 226 - 'Pi'
5, 0x63, 0x55, 0x49, 0x41, 0x63, // 227 - 'Sigma'
5, 0x38, 0x44, 0x44, 0x3c, 0x04, // 228 - 'Theta'
5, 0x40, 0x7e, 0x20, 0x1e, 0x20, // 229 - 'mu'
5, 0x06, 0x02, 0x7e, 0x02, 0x02, // 230 - 'Tau'
5, 0x99, 0xa5, 0xe7, 0xa5, 0x99, // 231
5, 0x1c, 0x2a, 0x49, 0x2a, 0x1c, // 232
5, 0x4c, 0x72, 0x01, 0x72, 0x4c, // 233
5, 0x30, 0x4a, 0x4d, 0x4d, 0x30, // 234
5, 0x30, 0x48, 0x78, 0x48, 0x30, // 235
5, 0xbc, 0x62, 0x5a, 0x46, 0x3d, // 236 - 'Zero Slashed'
4, 0x3e, 0x49, 0x49, 0x49, // 237
5, 0x7e, 0x01, 0x01, 0x01, 0x7e, // 238
5, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, // 239 - '3 Bar Equals'
5, 0x44, 0x44, 0x5f, 0x44, 0x44, // 240 - '+/-'
5, 0x40, 0x51, 0x4a, 0x44, 0x40, // 241 - '>='
5, 0x40, 0x44, 0x4a, 0x51, 0x40, // 242 - '<='
5, 0x00, 0x00, 0xff, 0x01, 0x03, // 243 - 'Top of Integral'
3, 0xe0, 0x80, 0xff, // 244 - 'Bot of Integral'
5, 0x08, 0x08, 0x6b, 0x6b, 0x08, // 245 - 'Divide'
5, 0x36, 0x12, 0x36, 0x24, 0x36, // 246 - 'Wavy ='
5, 0x06, 0x0f, 0x09, 0x0f, 0x06, // 247 - 'Degree'
4, 0x00, 0x00, 0x18, 0x18, // 248 - 'Math Product'
4, 0x00, 0x00, 0x10, 0x10, // 249 - 'Short Dash'
5, 0x30, 0x40, 0xff, 0x01, 0x01, // 250 - 'Square Root'
5, 0x00, 0x1f, 0x01, 0x01, 0x1e, // 251 - 'Superscript n'
5, 0x00, 0x19, 0x1d, 0x17, 0x12, // 252 - 'Superscript 2'
5, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, // 253 - 'Centered Square'
5, 0xff, 0x81, 0x81, 0x81, 0xff, // 254 - 'Full Frame'
5, 0xff, 0xff, 0xff, 0xff, 0xff, // 255 - 'Full Block'
};
#endif //INCLUDE_LOCAL_FONT

View File

@@ -0,0 +1,529 @@
/*
MD_MAX72xx - Library for using a MAX7219/7221 LED matrix controller
See header file for comments
This file contains library related definitions and is not visible
to user code.
Copyright (C) 2012-14 Marco Colli. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MDMAX72xxLIB_H
#define MDMAX72xxLIB_H
/**
* \file
* \brief Includes library definitions
*/
#define MAX_DEBUG 0 ///< Enable or disable (default) debugging output from the MD_MAX72xx library
#if MAX_DEBUG
#define PRINT(s, v) { Serial.print(F(s)); Serial.print(v); } ///< Print a string followed by a value (decimal)
#define PRINTX(s, v) { Serial.print(F(s)); Serial.print(v, HEX); } ///< Print a string followed by a value (hex)
#define PRINTB(s, v) { Serial.print(F(s)); Serial.print(v, BIN); } ///< Print a string followed by a value (binary)
#define PRINTS(s) { Serial.print(F(s)); } ///< Print a string
#else
#define PRINT(s, v) ///< Print a string followed by a value (decimal)
#define PRINTX(s, v) ///< Print a string followed by a value (hex)
#define PRINTB(s, v) ///< Print a string followed by a value (binary)
#define PRINTS(s) ///< Print a string
#endif
// Opcodes for the MAX7221 and MAX7219
// All OP_DIGITn are offsets from OP_DIGIT0
#define OP_NOOP 0 ///< MAX72xx opcode for NO OP
#define OP_DIGIT0 1 ///< MAX72xx opcode for DIGIT0
#define OP_DIGIT1 2 ///< MAX72xx opcode for DIGIT1
#define OP_DIGIT2 3 ///< MAX72xx opcode for DIGIT2
#define OP_DIGIT3 4 ///< MAX72xx opcode for DIGIT3
#define OP_DIGIT4 5 ///< MAX72xx opcode for DIGIT4
#define OP_DIGIT5 6 ///< MAX72xx opcode for DIGIT5
#define OP_DIGIT6 7 ///< MAX72xx opcode for DIGIT6
#define OP_DIGIT7 8 ///< MAX72xx opcode for DIGIT7
#define OP_DECODEMODE 9 ///< MAX72xx opcode for DECODE MODE
#define OP_INTENSITY 10 ///< MAX72xx opcode for SET INTENSITY
#define OP_SCANLIMIT 11 ///< MAX72xx opcode for SCAN LIMIT
#define OP_SHUTDOWN 12 ///< MAX72xx opcode for SHUT DOWN
#define OP_DISPLAYTEST 15 ///< MAX72xx opcode for DISPLAY TEST
#define ALL_CHANGED 0xff ///< Mask for all rows changed in a buffer structure
#define ALL_CLEAR 0x00 ///< Mask for all rows clear in a buffer structure
#define ASCII_INDEX_SIZE 256 ///< Number of characters in a font table (ASCII maximum)
// Shortcuts
#define SPI_DATA_SIZE (sizeof(uint8_t)*_maxDevices*2) ///< Size of the SPI data buffers
#define SPI_OFFSET(i,x) (((LAST_BUFFER-(i))*2)+(x)) ///< SPI data offset for buffer i, digit x
#define FIRST_BUFFER 0 ///< First buffer number
#define LAST_BUFFER (_maxDevices-1) ///< Last buffer number
// variables shared in the library
extern const uint8_t PROGMEM _sysfont_var[]; ///< System variable pitch font table
/**
\page pageHardware Hardware
Supported Hardware
------------------
This library supports the Parola hardware and the more commonly available LED modules available
from many other sources. The circuits for these modules are essentially identical except
in the way that the LED matrix is wired to the MAX7219 IC. This difference is accounted for in
software when the type of module is selected using the appropriate USE_*_HW compile time switch.
Hardware supported
------------------
- \subpage pageParola
- \subpage pageGeneric
- \subpage pageICStation
- \subpage pageFC16
- \subpage pageNewHardware
Connecting Multiple Modules
---------------------------
Separate modules are connected by the plugging them together edge to edge, with the
OUT side of one module plugged to the IN side of the next. More details can be found
at the end of each module's hardware section.
___
\page pageParola Parola Custom Module
The Parola Module
-----------------
These custome modules allow a 'lego-like' approach to LED matrix display, using standard 8x8 on
LED matrices. The software supports this flexibility through a scalable approach that
only requires the definition of the number of modules to adapt existing software to
a new configuration.
![Completed Parola module with and without the LED matrix] (PCB_Actual.jpg "Parola Custom Modules")
Circuit Schematic
-----------------
The schematic is the basic application circuit that is found on the MAX7219 datasheet,
adapted to the LED matrix. Each Module consists of an 8x8 LED matrix controlled by a
MAX7219 LED controller and a few passive components. These controllers can be daisy
chained, making them ideal for the purpose.
![Parola Circuit Schematic] (Circuit_Schematic.jpg "Parola Schematic")
The PCB design was executed using the autorouting facility in Eagle CAD, and the PCB was
manufactured by SeeedStudio. The Eagle CAD files for the layout and the Gerber files
suitable for SeeedStudio are found on the [Parola website] (https://github.com/MajicDesigns/MD_Parola).
The final design includes edge connections that allow many modules to be connected
together into an extended display, one LED module high.
![PCB layout ready for manufacture] (PCB_Layout.jpg "PCB Design")
Wiring your own Parola standard matrix
--------------------------------------
How the LED matrix is wired is important for the library. The matrix used for library
development was labelled 1088B and is sometime referred to as a **common anode** matrix.
Connections should be made as described in the table below to be consistent with the
assumptions in the software library.
- Columns are addressed through the segment selection lines
- Rows are addressed through the digit selection lines
MAX Signal|MAX7219 Pin|MAX Signal|MAX7219 Pin|
:--------:|----------:|:--------:|----------:|
Dig0 (D0) |2 |SegDP |22 |
Dig1 (D1) |11 |SegA |14 |
Dig2 (D2) |6 |SegB |16 |
Dig3 (D3) |7 |SegC |20 |
Dig4 (D4) |3 |SegD |23 |
Dig5 (D5) |10 |SegE |21 |
Dig6 (D6) |5 |SegF |15 |
Dig7 (D7) |8 |SegG |17 |
Segment data is packed on a per-digit basis, with segment G as the least significant bit (bit 0)
through to A as bit 6 and DP as bit 7.
____
Module Orientation
------------------
G F E D C B A DP
+------------------------+
| 7 6 5 4 3 2 1 0 | DIG0
| 1 | DIG1
| 2 | DIG2
| 3 | DIG3
| O 4 | DIG4
| O O 5 | DIG5
| O O O 6 | DIG6
| O O O O 7 | DIG7
+------------------------+
Vcc ---- ---- Vcc
DOUT <--- ---< DIN
GND ---- ---- GND
CS/LD <--- ---< CS/LD
CLK <--- ---< CLK
____
Module Interconnections
-----------------------
Parola modules are connected by plugging them together.
![Connecting Parola modules] (Modules_conn.jpg "Parola Modules connected")
____
\page pageGeneric Generic Module
Generic MAX7219 Module
------------------------
These modules are commonly available from many suppliers (eg, eBay) at reasonable cost.
They are characterized by IN and OUT connectors at the short ends of the rectangular PCB.
![Generic Module] (Generic_Module.png "Generic Module")
____
Module Orientation
------------------
C C D G V
L S I N c
K N D c
| | | | |
V V V | |
D7 D6 D5 D4 D3 D2 D1 D0
+------------------------+
| 7 6 5 4 3 2 1 0 |- DP
| 1 |- A
| 2 |- B
| 3 |- C
| O 4 |- D
| O O 5 |- E
| O O O 6 |- F
| O O O O 7 |- G
+-----+--+--+--+--+------+
| | | | |
V V V | |
C C D G V
L S O N c
K U D c
T
____
Module Interconnections
-----------------------
Generic modules need to be oriented with the MAX7219 IC at the top and connected using
short patch cables in a spiral pattern. The display is oriented with the IC at the top.
![Connecting Generic modules] (Generic_conn.jpg "Generic Modules connected")
____
\page pageICStation ICStation Module
ICStation DIY Kit Module
------------------------
These modules are available as kits from ICStation (http://www.icstation.com/product_info.php?products_id=2609#.UxqVJyxWGHs).
![ICStation Module] (ICStation_Module.jpg "ICStation Module")
____
Module Orientation
------------------
G F E D C B A DP
+------------------------+
| 7 6 5 4 3 2 1 0 | D7
CLK <---| 1 | D6 <--- CLK
CS <---| 2 | D5 <--- CS
DOUT <---| 3 | D4 <--- DIN
GND ----| O 4 | D3 ---- GND
VCC ----| O O 5 | D2 ---- VCC
| O O O 6 | D1
| O O O O 7 | D0
+------------------------+
____
Module Interconnections
-----------------------
ICStation Modules are connected using the links supplied with the hardware. The display is
oriented with the DIN side on the right.
![Connecting ICStation modules] (ICStation_conn.jpg "ICStation Modules connected")
____
\page pageFC16 FC-16 Module
FC-16 DIY Kit Module
----------------------
These modules are available as kits from some internet suppliers such as G&C Supermarket on eBay
(http://stores.ebay.com.au/gcsupermarkethkcoltd/). They are identifiable by the FC-16 designation
silkscreened on the PCB. Most of the available sets of 4 modules connected as one unit are
FC-16 type.
![FC-16 Module] (FC-16_Module.jpg "FC-16 Module")
____
Module Orientation
------------------
DP A B C D E F G
+------------------------+
| 7 6 5 4 3 2 1 0 | D0
CLK <---| 1 | D1 <--- CLK
CS <---| 2 | D2 <--- CS
DOUT <---| 3 | D3 <--- DIN
GND ----| O 4 | D4 ---- GND
VCC ----| O O 5 | D5 ---- VCC
| O O O 6 | D6
| O O O O 7 | D7
+------------------------+
____
Module Interconnections
-----------------------
FC-16 Modules are connected using the links supplied with the hardware. The display is
oriented with the DIN side on the right. PCB text may appear upside down.
![Connecting FC-16 modules] (FC-16_conn.jpg "FC-16 Modules connected")
____
\page pageNewHardware New Hardware Types
A word on coordinate systems
----------------------------
Two Cartesian coordinate systems are used in the library
- one defines the pixels seen (_display coordinates_), and
- an underlying _hardware coordinate_ system based on digits and segments
mapping to the MAX72xx hardware control registers.
Display coordinates always have their origin in the top right corner of a display.
- Column numbers increase to the left (as do module numbers)
- Row numbers increase down (0..7)
All user functions are consistent and use display coordinates.
Display memory buffers are stored in hardware coordinates that depend on
the hardware configuration (i.e. the module type). It is the job of the low level
library functions to map display to hardware coordinates. Digit 0 is the lowest
row/column number and Segment G is the lowest column/row number.
All the code to do this is in the is in the buffer and pixel modules.
All other library modules are use the primitives made available in these modules.
What needs to change?
---------------------
As there is no standard way of wiring a LED matrix to the MAX72xx IC, each hardware type
definition activates a series of coordinate mapping transformations. Possible changes
are limited to combinations (8 in total) of
- swapping rows and column coordinates (digits and segments in MAX72xx),
- a reversal of row indices, and
- a reversal of column indices.
The hardware types defined in MD_MAX72xx.h activate different library code by defining
appropriate values for the defines listed below, in the MD_MAX72xx_lib.h file.
- HW_DIG_ROWS - MAX72xx digits are mapped to rows in on the matrix. If digits are
not rows then they are columns!
- HW_REV_COLS - Normal column coordinates orientation is 0 col on the right side
of the display. Set to 1 to reverse this (0 on the left).
- HW_REV_ROWS - Normal row coordinates orientation is 0 row at top of the display.
Set to 1 to reverse this (0 at the bottom).
Determining the type of mapping
-------------------------------
The library example code includes a utility called MD_MAX72xx_HW_Mapper.
This is test software to map display hardware rows and columns. It uses a
generic SPI interface and only one MAX72xx/8x8 LED module required. It is
independent of the libraries as the code is used to directly map the display
orientation by setting pixels on the display and printing to the serial monitor
which MAX72xx hardware component (segment and digit) is being exercised.
By observing the LED display and the serial monitor you can build a map like the
one below. It is worth noting the direction in which the rows and columns are
scanned by the utility, as this is the easiest way to work out the row/column
reversal values.
The result of these observations is a grid definition that looks somewhat like:
DIG0 D1 D2 D3 D4 D5 D6 D7
Seg G
Seg F
Seg E
Seg D
Seg C
Seg B
Seg A
Seg DP
From this mapping it is clear
- MAX72xx digits map to the columns, HW_DIG_ROWS is 0.
- DIG0 is on the left (columns were also scanned left to right), so HW_REV_COLS should be set
to 1 to reverse it to the standard 0 on the right.
- Seg G is at the top (rows were also top to bottom), so HW_REV_ROWS should be set to 0,
as it is already standard with 0 on top.
Note that in some situations using the module 'upside down' will result in a better configuration
than would otherwise be the case. An example of this is the generic module mapping. Also remember
that the modules are daisy chained from right to left.
Having determined the values for the defines, the new mapping can be configured, or matched to
an existing hardware type.
___
\page pageFontUtility Create and Modify Fonts
Font Storage Format
-------------------
One default font is defined as part of the library in PROGMEM memory. Alternative fonts
can be specified to the library. The font builder utilities provide a convenient way to
modify existing or develop alternative fonts.
Fonts are stored as a series of contiguous bytes in the following format:
- byte 1 - the number of bytes that form this character (could be zero)
- byte 2..n - each byte is a column of the character to be formed, starting with the
leftmost column of the character. The least significant bit of the byte is the bottom
pixel position of the character matrix (row 7).
To find a character in the font table, the library looks at the first byte (size),
skips 'size'+1 bytes to the next character size byte and repeat until the last or
target character is reached.
The compile-time switch USE_INDEX_FONT enables indexing of the font table for faster access, at
the expense of increased RAM usage. If indexing is enabled, a single lookup is required to
access the character data, rather than the sequential search described above.
The support for fonts (methods and data) may be completely disabled if not required through
the compile-time switch USE_LOCAL_FONT. This will also disable user defined fonts.
____
The txt2font Utility
--------------------
The txt2font utility is a command line application that converts a text definition of the font
into a data file in the right format for MD_MAX72xx to use.
This utility is as an Win32 executable. Users with other Operating Systems will need to compile
a version to work with their OS, using the source code supplied.
The application is invoked from the command line and only the root name of the file is given as a command
line parameter (eg "txt2font fred"). The application will look for and input file with a '.txt' extension
(fred.txt) and produce an output file with a '.h' extension (fred.h).
The txt2font file format is line based. Lines starting with a '.' are directives for the application, all
other lines are data for the current character defintion. An example of the beginning of a font
definition file is shown below.
.NAME sys_var_single
.HEIGHT 1
.WIDTH 0
.CHAR 0
.NOTE 'Empty Cell'
.CHAR 1
.NOTE 'Sad Smiley'
@@@
@@@@@
@ @ @
@@@@@
@@ @@
@ @
@@@
.CHAR 2
.NOTE 'Happy Smiley'
***
The directives have the following meaning:
- .NAME defines the name for the font and is used in naming the font table variable.
The name can appear anywhere in the file. If omitted, a default name is used.
- .HEIGHT defines the height for the font. Single height fonts are '1' and double height fonts are '2'.
If double height fonts are specified then the range of ASCII character values is restricted to 0..127 as
the top and botton halves of the font are stored offset by 128 positions. If omitted, the application
assumes single height font.
- .WIDTH specifies the width of the font for all the characters defined between this WIDTH and the
next WIDTH defintion. 0 means variable width; any other number defines the fixed width. WIDTH may be changed
within the file - for example to define a fixed size space (no pixels!) character in a variable width font.
- .CHAR ends the defintion of the current character and starts the defintion for the specified ASCII value.
Valid parameters are [0..255] for single height, and [0..127] for double height. If a character code is
omitted in the font definition file it is assumed to be empty.
- .NOTE is an option note that will be added as a comment for the entry in the font data table.
Any lines not starting with a '.' are data lines for the current character. The font characters are drawn
using a non-space character (eg, '*' or '@') wherever a LED needs to be 'on'. The application scans from
the top down, so any lines missing at the bottom of the character definition are assumed to be blank.
However, blank lines at the top need to be shown. Any extra rows are ignored will cause program errors.
A number of font definition files are supplied as examples.
___
The FontBuilder Excel/VBA application
-------------------------------------
FontBuilder is a Microsoft Excel spreadsheet with VBA macros to manage a GUI interface for defining
and managing font characters. FontBuilder supports both single and double height fonts. The first tab
in the FontBuilder spreadsheet has instructions for use.
As FontBuilder requires using Microsoft Office products, it does not work environments where
these are not available.
*/
// *******************************************************************************************
// ** Combinations not listed here have probably not been tested and may not work correctly **
// *******************************************************************************************
#if USE_PAROLA_HW // tested MC 8 March 2014
//#pragma message "PAROLA HW selected"
#define HW_DIG_ROWS 1 ///< MAX72xx digits are mapped to rows in on the matrix
#define HW_REV_COLS 1 ///< Normal orientation is col 0 on the right. Set to 1 if reversed
#define HW_REV_ROWS 0 ///< Normal orientation is row 0 at the top. Set to 1 if reversed
#endif
#if USE_GENERIC_HW // tested MC 9 March 2014
//#pragma message "GENERIC HW selected"
#define HW_DIG_ROWS 0 ///< MAX72xx digits are mapped to rows in on the matrix
#define HW_REV_COLS 1 ///< Normal orientation is col 0 on the right. Set to 1 if reversed
#define HW_REV_ROWS 0 ///< Normal orientation is row 0 at the top. Set to 1 if reversed
#endif
#if USE_ICSTATION_HW // tested MC 9 March 2014
//#pragma message "ICSTATION HW selected"
#define HW_DIG_ROWS 1 ///< MAX72xx digits are mapped to rows in on the matrix
#define HW_REV_COLS 1 ///< Normal orientation is col 0 on the right. Set to 1 if reversed
#define HW_REV_ROWS 1 ///< Normal orientation is row 0 at the top. Set to 1 if reversed
#endif
#if USE_FC16_HW // tested MC 23 Feb 2015
//#pragma message "FC16 HW selected"
#define HW_DIG_ROWS 1 ///< MAX72xx digits are mapped to rows in on the matrix
#define HW_REV_COLS 0 ///< Normal orientation is col 0 on the right. Set to 1 if reversed
#define HW_REV_ROWS 0 ///< Normal orientation is row 0 at the top. Set to 1 if reversed
#endif
#if USE_OTHER_HW // user defined custom hardware configuration
//#pragma message "OTHER HW selected"
#define HW_DIG_ROWS 0 ///< MAX72xx digits are mapped to rows in on the matrix
#define HW_REV_COLS 0 ///< Normal orientation is col 0 on the right. Set to 1 if reversed
#define HW_REV_ROWS 0 ///< Normal orientation is row 0 at the top. Set to 1 if reversed
#endif
#ifndef HW_DIG_ROWS
#error "INVALID or missing hardware selected"
#endif
// Macros to map ROW and COLUMN coordinates
#if HW_REV_ROWS
#define HW_ROW(r) (7-r) ///< Pixel to hardware coordinate row mapping
#else
#define HW_ROW(r) (r) ///< Pixel to hardware coordinate row mapping
#endif
#if HW_REV_COLS
#define HW_COL(c) (7-c) ///< Pixel to hardware coordinate column mapping
#else
#define HW_COL(c) (c) ///< Pixel to hardware coordinate column mapping
#endif
#endif

View File

@@ -0,0 +1,284 @@
/*
MD_MAX72xx - Library for using a MAX7219/7221 LED matrix controller
See header file for comments
This file contains methods that act on the matrix as a pixel field,
generally only acting on the visible device range of the buffered
device field (ie, the physical pixel matrix).
Copyright (C) 2012-14 Marco Colli. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include "MD_MAX72xx.h"
#include "MD_MAX72xx_lib.h"
/**
* \file
* \brief Implements pixel related methods
*/
void MD_MAX72XX::clear(uint8_t startDev, uint8_t endDev)
{
if (endDev < startDev) return;
for (uint8_t buf = startDev; buf <= endDev; buf++)
{
memset(_matrix[buf].dig, 0, sizeof(_matrix[buf].dig));
_matrix[buf].changed = ALL_CHANGED;
}
if (_updateEnabled) flushBufferAll();
}
bool MD_MAX72XX::getBuffer(uint16_t col, uint8_t size, uint8_t *pd)
{
if ((col >= getColumnCount()) || (pd == NULL))
return(false);
for (uint8_t i=0; i<size; i++)
*pd++ = getColumn(col--);
return(true);
}
bool MD_MAX72XX::setBuffer(uint16_t col, uint8_t size, uint8_t *pd)
{
bool b = _updateEnabled;
if ((col >= getColumnCount()) || (pd == NULL))
return(false);
_updateEnabled = false;
for (uint8_t i=0; i<size; i++)
setColumn(col--, *pd++);
_updateEnabled = b;
if (_updateEnabled) flushBufferAll();
return(true);
}
bool MD_MAX72XX::drawLine(uint8_t r1, uint16_t c1, uint8_t r2, uint16_t c2, bool state)
// draw a line between two points using Bresentham's line algorithm
{
if (r1 >= ROW_SIZE || r2 >= ROW_SIZE || c1 >= (COL_SIZE*_maxDevices) || c2 >= (COL_SIZE*_maxDevices))
return(false);
if (c1 > c2)
{
uint16_t t;
t = c1; c1 = c2; c2 = t; // swap c1/c2
t = r1; r1 = r2; r2 = t; // swap r1/r2
}
// Bresentham's line algorithm
int16_t dc = abs(c2-c1);
int16_t sc = c1<c2 ? 1 : -1;
int16_t dr = abs(r2-r1);
int16_t sr = r1<r2 ? 1 : -1;
int16_t err = (dc>dr ? dc : -dr)/2;
int16_t e2;
for(;;)
{
setPoint(r1, c1, state);
if (c1 == c2 && r1 == r2) break;
e2 = err;
if (e2 >-dc) { err -= dr; c1 += sc; }
if (e2 < dr) { err += dc; r1 += sr; }
}
if (_updateEnabled) flushBufferAll();
return(true);
}
// used in getPoint and setPoint!
#if HW_DIG_ROWS
#define R r
#define C c
#else
#define R c
#define C r
#endif
bool MD_MAX72XX::getPoint(uint8_t r, uint16_t c)
{
uint8_t buf = c/COL_SIZE;
c %= COL_SIZE;
PRINT("\ngetPoint: (", buf);
PRINT(", ", r);
PRINT(", ", c);
PRINTS(")");
if ((buf > LAST_BUFFER) || (r >= ROW_SIZE) || (c >= COL_SIZE))
return(false);
return(bitRead(_matrix[buf].dig[HW_ROW(R)], HW_COL(C)) == 1);
}
bool MD_MAX72XX::setPoint(uint8_t r, uint16_t c, bool state)
{
uint8_t buf = c/COL_SIZE;
c %= COL_SIZE;
PRINT("\nsetPoint: (", buf);
PRINT(", ", r);
PRINT(", ", c);
PRINT(") = ", state?1:0);
if ((buf > LAST_BUFFER) || (r >= ROW_SIZE) || (c >= COL_SIZE))
return(false);
if (state)
bitSet(_matrix[buf].dig[HW_ROW(R)], HW_COL(C));
else
bitClear(_matrix[buf].dig[HW_ROW(R)], HW_COL(C));
bitSet(_matrix[buf].changed, HW_ROW(R));
if (_updateEnabled) flushBuffer(buf);
return(true);
}
#undef R
#undef C
bool MD_MAX72XX::setRow(uint8_t startDev, uint8_t endDev, uint8_t r, uint8_t value)
{
bool b = _updateEnabled;
PRINT("\nsetRow: ", r);
if ((r >= ROW_SIZE) || (endDev < startDev))
return(false);
_updateEnabled = false;
for (uint8_t i = startDev; i <= endDev; i++)
setRow(i, r, value);
_updateEnabled = b;
if (_updateEnabled) flushBufferAll();
return(true);
}
bool MD_MAX72XX::transform(uint8_t startDev, uint8_t endDev, transformType_t ttype)
{
// uint8_t t[ROW_SIZE];
uint8_t colData;
bool b = _updateEnabled;
if (endDev < startDev) return(false);
_updateEnabled = false;
switch (ttype)
{
case TSL: // Transform Shift Left one pixel element (with overflow)
colData = 0;
// if we can call the user function later then we don't need to do anything here
// however, warparound mode means we know the data so no need to request from the
// callback at all - just save it for later
if (_wrapAround)
colData = getColumn(((endDev+1)*COL_SIZE)-1);
else if (_cbShiftDataOut != NULL)
(*_cbShiftDataOut)(endDev, ttype, getColumn(((endDev+1)*COL_SIZE)-1));
// shift all the buffers along
for (int8_t buf = endDev; buf >= startDev; --buf)
{
transformBuffer(buf, ttype);
// handle the boundary condition
setColumn(buf, 0, getColumn(buf-1, COL_SIZE-1));
}
// if we have a callback function, now is the time to get the data if we are
// not in wraparound mode
if (_cbShiftDataIn != NULL && !_wrapAround)
colData = (*_cbShiftDataIn)(startDev, ttype);
setColumn((startDev*COL_SIZE), colData);
break;
case TSR: // Transform Shift Right one pixel element (with overflow)
// if we can call the user function later then we don't need to do anything here
// however, warparound mode means we know the data so no need to request from the
// callback at all - just save it for later.
colData = 0;
if (_wrapAround)
colData = getColumn(startDev*COL_SIZE);
else if (_cbShiftDataOut != NULL)
(*_cbShiftDataOut)(startDev, ttype, getColumn((startDev*COL_SIZE)));
// shift all the buffers along
for (uint8_t buf=startDev; buf<=endDev; buf++)
{
transformBuffer(buf, ttype);
// handle the boundary condition
setColumn(buf, COL_SIZE-1, getColumn(buf+1, 0));
}
// if we have a callback function, now is the time to get the data if we are
// not in wraparound mode
if (_cbShiftDataIn != NULL && !_wrapAround)
colData = (*_cbShiftDataIn)(endDev, ttype);
setColumn(((endDev+1)*COL_SIZE)-1, colData);
break;
case TFLR: // Transform Flip Left to Right (use the whole field)
// first reverse the device buffers end for end
for (uint8_t buf = 0; buf < (endDev - startDev)/2; buf++)
{
deviceInfo_t t;
t = _matrix[startDev + buf];
_matrix[startDev + buf] = _matrix[endDev - buf];
_matrix[endDev - buf] = t;
}
// now reverse the columns in each device
for (uint8_t buf = startDev; buf <= endDev; buf++)
transformBuffer(buf, ttype);
break;
// These next transformation work the same just by doing the individual devices
case TSU: // Transform Shift Up one pixel element
case TSD: // Transform Shift Down one pixel element
case TFUD: // Transform Flip Up to Down
case TRC: // Transform Rotate Clockwise
case TINV: // Transform INVert
for (uint8_t buf = startDev; buf <= endDev; buf++)
transformBuffer(buf, ttype);
break;
default:
return(false);
}
_updateEnabled = b;
if (_updateEnabled) flushBufferAll();
return(true);
}