MD_MAX72xx.cpp
/*H*******************************************************
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
********************************************************/
#if defined(__MBED__) && !defined(ARDUINO)
#include "mbed.h"
#else
#include
#include
#endif
#include "MD_MAX72xx.h"
#include "MD_MAX72xx_lib.h"
/**
* \file
* \brief Implements class definition and general methods
*/
MD_MAX72XX:: MD_MAX72XX( moduleType_t mod, uint8_t dataPin, uint8_t clkPin, uint8_t csPin, uint8_t numDevices):
_dataPin( dataPin ), _clkPin( clkPin ), _csPin( csPin ), _hardwareSPI( false )
, _spiRef(SPI), _maxDevices(numDevices), _updateEnabled(true)
#if defined(__MBED__) && !defined(ARDUINO)
, _spi((PinName)dataPin, NC, (PinName)clkPin), _cs((PinName)csPin)
#endif
{
setModuleParameters( mod );
}
MD_MAX72XX::MD_MAX72XX(moduleType_t mod, uint8_t csPin, uint8_t numDevices):
_dataPin(0), _clkPin(0), _csPin(csPin),
_hardwareSPI(true), _spiRef(SPI), _maxDevices(numDevices), _updateEnabled(true)
#if defined(__MBED__) && !defined(ARDUINO)
, _spi(SPI_MOSI, NC, SPI_SCK), _cs((PinName)csPin)
#endif
{
setModuleParameters(mod);
}
MD_MAX72XX::MD_MAX72XX(moduleType_t mod, SPIClass& spi, uint8_t csPin, uint8_t numDevices):
_dataPin(0), _clkPin(0), _csPin(csPin),
_hardwareSPI(true), _spiRef(spi), _maxDevices(numDevices), _updateEnabled(true)
#if defined(__MBED__) && !defined(ARDUINO)
, _spi(SPI_MOSI, NC, SPI_SCK), _cs((PinName)csPin)
#endif
{
setModuleParameters(mod);
}
void MD_MAX72XX::setModuleParameters(moduleType_t mod)
// Combinations not listed as tested have *probably* not
// been tested and may not operate correctly.
{
_mod = mod;
switch (_mod)
{
case DR0CR0RR0_HW: _hwDigRows = false; _hwRevCols = false; _hwRevRows = false; break;
case DR0CR0RR1_HW: _hwDigRows = false; _hwRevCols = false; _hwRevRows = true; break;
case DR0CR1RR0_HW: // same as GENERIC_HW, tested MC 9 March 2014
case GENERIC_HW: _hwDigRows = false; _hwRevCols = true; _hwRevRows = false; break;
case DR0CR1RR1_HW: _hwDigRows = false; _hwRevCols = true; _hwRevRows = true; break;
case DR1CR0RR0_HW: // same as FC16_HW, tested MC 23 Feb 2015
case FC16_HW: _hwDigRows = true; _hwRevCols = false; _hwRevRows = false; break;
case DR1CR0RR1_HW: _hwDigRows = true; _hwRevCols = false; _hwRevRows = true; break;
case DR1CR1RR0_HW: // same as PAROLA_HW, tested MC 8 March 2014
case PAROLA_HW: _hwDigRows = true; _hwRevCols = true; _hwRevRows = false; break;
case DR1CR1RR1_HW: // same as ICSTATION_HW, tested MC 9 March 2014
case ICSTATION_HW: _hwDigRows = true; _hwRevCols = true; _hwRevRows = true; break;
}
}
void MD_MAX72XX::begin(void)
{
// initialize the SPI interface
#ifdef ARDUINO
if (_hardwareSPI)
{
PRINTS("\nHardware SPI");
_spiRef.begin();
}
else
{
PRINTS("\nBitBang SPI")
pinMode(_dataPin, OUTPUT);
pinMode(_clkPin, OUTPUT);
}
// initialize our preferred CS pin (could be same as SS)
pinMode(_csPin, OUTPUT);
digitalWrite(_csPin, HIGH);
#else
_cs = 1;
#endif
// object memory and internals
setShiftDataInCallback(nullptr);
setShiftDataOutCallback(nullptr);
_matrix = (deviceInfo_t *)malloc(sizeof(deviceInfo_t) * _maxDevices);
_spiData = (uint8_t *)malloc(SPI_DATA_SIZE);
#if USE_LOCAL_FONT
setFont(_sysfont);
#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); // ensure no decoding (warm boot potential issue)
clear();
control(SHUTDOWN, OFF); // take the modules out of shutdown mode
}
MD_MAX72XX::~MD_MAX72XX(void)
{
#ifdef ARDUINO
if (_hardwareSPI) _spiRef.end(); // reset SPI mode
#endif
free(_matrix);
free(_spiData);
}
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;
default:
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 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;
}
inline void MD_MAX72XX::spiClearBuffer(void)
// Clear out the spi data array
{
memset(_spiData, OP_NOOP, SPI_DATA_SIZE);
}
void MD_MAX72XX::spiSend(void)
{
#ifdef ARDUINO
// initialize the SPI transaction
if (_hardwareSPI)
_spiRef.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
digitalWrite(_csPin, LOW);
// shift out the data
if (_hardwareSPI)
{
for (uint16_t i = 0; i < SPI_DATA_SIZE; i++)
_spiRef.transfer(_spiData[i]);
}
else // not hardware SPI - bit bash it out
{
for (uint16_t i = 0; i < SPI_DATA_SIZE; i++)
shiftOut(_dataPin, _clkPin, MSBFIRST, _spiData[i]);
}
// end the SPI transaction
digitalWrite(_csPin, HIGH);
if (_hardwareSPI)
_spiRef.endTransaction();
#else
_cs = 0;
_spi.write((const char*)_spiData, SPI_DATA_SIZE, nullptr, 0);
_cs = 1;
#endif
}