Tasmota/lib/lib_display/arduino-tm1637/SevenSegmentTM1637.cpp

435 lines
11 KiB
C++

#include "SevenSegmentTM1637.h"
SevenSegmentTM1637::SevenSegmentTM1637(uint8_t pinClk, uint8_t pinDIO) :
_pinClk(pinClk),
_pinDIO(pinDIO)
{
// setup pins
pinAsOutput(_pinClk);
pinAsOutput(_pinDIO);
digitalHigh(_pinClk);
digitalHigh(_pinDIO);
// setup defaults
setCursor(0, TM1637_DEFAULT_CURSOR_POS);
setPrintDelay(TM1637_DEFAULT_PRINT_DELAY);
setColonOn(TM1637_DEFAULT_COLON_ON);
setBacklight(TM1637_DEFAULT_BACKLIGHT);
// write command SET_DATA (Command1) Defaults
command(
TM1637_COM_SET_DATA |
TM1637_SET_DATA_WRITE |
TM1637_SET_DATA_A_ADDR |
TM1637_SET_DATA_M_NORM
);
};
// Print API ///////////////////////////////////////////////////////////////////
// single byte
size_t SevenSegmentTM1637::write(uint8_t byte) {
TM1637_DEBUG_PRINT(F("write byte:\t")); TM1637_DEBUG_PRINTLN((char)byte);
size_t n = 0;
if ( _cursorPos == _numCols ) {
shiftLeft(_rawBuffer, _numCols);
_rawBuffer[_cursorPos] = encode( (char)byte );
// buffer, length, position
printRaw( _rawBuffer, _numCols, 0);
++n;
};
if (_cursorPos < _numCols) {
_rawBuffer[_cursorPos] = encode( (char)byte );
// buffer, length, position
printRaw( _rawBuffer, _cursorPos+1, 0);
setCursor(1, _cursorPos + 1);
++n;
}
return n;
}
// null terminated char array
size_t SevenSegmentTM1637::write(const char* str) {
TM1637_DEBUG_PRINT(F("write char*:\t")); TM1637_DEBUG_PRINTLN(str);
uint8_t encodedBytes[4];
encode(encodedBytes, str, 4);
uint8_t i =4;
while( str[i] != '\0' ) {
printRaw(encodedBytes);
shiftLeft(encodedBytes, 4);
encodedBytes[3] = encode( str[i] );
i++;
if ( i == TM1637_MAX_CHARS) {
break;
}
}
return i;
};
// byte array with length
size_t SevenSegmentTM1637::write(const uint8_t* buffer, size_t size) {
TM1637_DEBUG_PRINT(F("write uint8_t*:\t("));
for(size_t i=0; i < size; i++) {
TM1637_DEBUG_PRINT((char)buffer[i]);
TM1637_DEBUG_PRINT(i == size -1?F(""):F(", "));
}
TM1637_DEBUG_PRINT(F(") "));
TM1637_DEBUG_PRINT(size);
uint8_t encodedBytes[TM1637_MAX_CHARS];
if ( size > TM1637_MAX_CHARS) {
size = TM1637_MAX_CHARS;
}
size_t length = encode(encodedBytes, buffer, size);
TM1637_DEBUG_PRINT(F(" (")); TM1637_DEBUG_PRINT(length); TM1637_DEBUG_PRINT(F(", "));
TM1637_DEBUG_PRINT(_cursorPos); TM1637_DEBUG_PRINTLN(F(")"));
printRaw(encodedBytes, length, _cursorPos);
return length;
};
// Liquid cristal API
void SevenSegmentTM1637::begin(uint8_t cols, uint8_t rows) {
_numCols = cols;
_numRows = rows;
clear();
};
void SevenSegmentTM1637::init(uint8_t cols, uint8_t rows) {
begin(cols, rows);
}
void SevenSegmentTM1637::clear(void) {
uint8_t rawBytes[4] = {0,0,0,0};
printRaw(rawBytes);
home();
};
void SevenSegmentTM1637::home(void) {
setCursor(0, 0);
};
void SevenSegmentTM1637::setCursor(uint8_t row, uint8_t col) {
col = (col > TM1637_MAX_COLOM -1)?TM1637_MAX_COLOM-1:col;
_cursorPos = col;
};
void SevenSegmentTM1637::setBacklight(uint8_t value) {
value = (value > 100 )?100:value; // 0..100 brightness
// Store the backlight value
_backLightValue = value;
// scale backlight value to 0..8
value /= 10; // 0..10
value = (value > 8 )? 8:value; // only 8 levels and off
uint8_t cmd = TM1637_COM_SET_DISPLAY;;
switch ( value ) {
case 0:
cmd |= TM1637_SET_DISPLAY_OFF;
break;
case 1:
cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_1;
break;
case 2:
cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_2;
break;
case 3:
cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_4;
break;
case 4:
cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_10;
break;
case 5:
cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_11;
break;
case 6:
cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_12;
break;
case 7:
cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_13;
break;
case 8:
cmd |= TM1637_SET_DISPLAY_ON | TM1637_SET_DISPLAY_14;
break;
};
#if TM1637_DEBUG
bool ack = command(cmd);
TM1637_DEBUG_PRINT(F("SET_DISPLAY:\t")); TM1637_DEBUG_PRINTLN((
cmd
), BIN);
TM1637_DEBUG_PRINT(F("Acknowledged:\t")); TM1637_DEBUG_PRINTLN(ack);
#else
command(cmd);
#endif
};
void SevenSegmentTM1637::setContrast(uint8_t value) {
setBacklight(value);
}
void SevenSegmentTM1637::on(void) {
setBacklight(TM1637_DEFAULT_BACKLIGHT);
};
void SevenSegmentTM1637::off(void) {
setBacklight(0);
clear();
};
// SevenSegmentTM1637 public methods
void SevenSegmentTM1637::blink(uint8_t blinkDelay, uint8_t repeats, uint8_t maxBacklight, uint8_t minBacklight) {
for (uint8_t i=0; i < repeats; i++) {
setBacklight(minBacklight); // turn backlight off
delay(blinkDelay);
setBacklight(maxBacklight); // turn backlight on
delay(blinkDelay);
}
// restore backlight
setBacklight(_backLightValue);
}
void SevenSegmentTM1637::setPrintDelay(uint16_t printDelay) {
_printDelay = printDelay;
};
bool SevenSegmentTM1637::getColonOn(void) {
return (_colonOn);
};
void SevenSegmentTM1637::setColonOn(bool setToOn) {
_colonOn = setToOn;
}
void SevenSegmentTM1637::printRaw(uint8_t rawByte, uint8_t position) {
uint8_t cmd[2];
cmd[0] = TM1637_COM_SET_ADR | position;
cmd[1] = rawByte;
if (position == 1) { cmd[1]|=(_colonOn)?TM1637_COLON_BIT:0; };
command(cmd, 2);
};
void SevenSegmentTM1637::printRaw(const uint8_t* rawBytes, size_t length, uint8_t position) {
// if fits on display
if ( (length + position) <= _numCols) {
uint8_t cmd[5] = {0, };
cmd[0] = TM1637_COM_SET_ADR | (position & B111); // sets address
memcpy(&cmd[1], rawBytes, length); // copy bytes
// do we have to print a colon?
if ( position < 2 ) { // printing after position 2 has never a colon
if ( position == 0 && length >= 2) {
// second index is the colon
cmd[2] |= (_colonOn)?TM1637_COLON_BIT:0;
} else {
// first index is the colon
cmd[1] |= (_colonOn)?TM1637_COLON_BIT:0;
}
}
// TM1637_DEBUG_PRINT(F("ADDR :\t")); TM1637_DEBUG_PRINTLN(cmd[0],BIN);
// TM1637_DEBUG_PRINT(F("DATA0:\t")); TM1637_DEBUG_PRINTLN(cmd[1],BIN);
command(cmd, length+1); // send to display
}
// does not fit on display, need to print with delay
else {
// First print 1-4 characters
uint8_t numtoPrint = _numCols - position;
printRaw(rawBytes, numtoPrint, position);
delay(_printDelay);
// keep printing 4 characters till done
uint8_t remaining = length - numtoPrint + 3;
uint8_t i = 1;
while( remaining >= _numCols) {
printRaw(&rawBytes[i], _numCols, 0);
delay(_printDelay);
remaining--;
i++;
};
}
};
// Helpers
uint8_t SevenSegmentTM1637::encode(char c) {
if ( c < ' ') { // 32 (ASCII)
return 0;
}
return pgm_read_byte_near(AsciiMap::map + c - ' ');
};
uint8_t SevenSegmentTM1637::encode(int16_t d) {
// can only encode single digit
if ( d > 9 || d < 0) {
return 0;
};
return pgm_read_byte_near(AsciiMap::map + d + '0' - ' ');
};
size_t SevenSegmentTM1637::encode(uint8_t* buffer, const char* str, size_t bufferSize) {
size_t i;
for (i=0; i < bufferSize; i++) {
if (str[i] == '\0' ) {
return i;
}
buffer[i] = encode( str[i] );
};
return i;
}
size_t SevenSegmentTM1637::encode(uint8_t* buffer, const uint8_t* byteArr, size_t bufferSize) {
size_t i;
for (i=0; i < bufferSize; i++) {
buffer[i] = encode( (char)byteArr[i] );
};
return i;
}
void SevenSegmentTM1637::shiftLeft(uint8_t* buffer, size_t length) {
for (uint8_t i=0; i < length ; i++) {
buffer[i] = buffer[i+1];
}
}
// SevenSegmentTM1637 LOW LEVEL
bool SevenSegmentTM1637::command(uint8_t cmd) const{
return command(_pinClk, _pinDIO, cmd);
};
bool SevenSegmentTM1637::command(uint8_t pinClk, uint8_t pinDIO, uint8_t cmd) {
comStart(pinClk, pinDIO);
comWriteByte(pinClk, pinDIO,cmd);
bool acknowledged = comAck(pinClk, pinDIO);
comStop(pinClk, pinDIO);
return acknowledged;
}
bool SevenSegmentTM1637::command(const uint8_t* commands, uint8_t length) const {
return command(_pinClk, _pinDIO, commands, length);
};
bool SevenSegmentTM1637::command(uint8_t pinClk, uint8_t pinDIO, const uint8_t* commands, uint8_t length) {
bool acknowledged = true;
comStart(pinClk, pinDIO);
for (uint8_t i=0; i < length;i++) {
comWriteByte(pinClk, pinDIO, commands[i]);
acknowledged &= comAck(pinClk, pinDIO);
};
comStop(pinClk, pinDIO);
return acknowledged;
}
uint8_t SevenSegmentTM1637::comReadByte(void) const {
uint8_t readKey = 0;
comStart();
comWriteByte(TM1637_COM_SET_DATA | TM1637_SET_DATA_READ);
comAck();
pinAsInput(_pinDIO);
digitalHigh(_pinDIO);
delayMicroseconds(5);
for ( uint8_t i=0; i < 8; i++) {
readKey >>= 1;
digitalLow(_pinClk);
delayMicroseconds(30);
digitalHigh(_pinClk);
if ( isHigh(_pinDIO) ) {
readKey = readKey | B10000000;
};
delayMicroseconds(30);
};
pinAsOutput(_pinDIO);
comAck();
comStop();
return readKey;
};
void SevenSegmentTM1637::comWriteByte(uint8_t command) const{
comWriteByte(_pinClk, _pinDIO, command);
};
void SevenSegmentTM1637::comWriteByte(uint8_t pinClk, uint8_t pinDIO, uint8_t command) {
// CLK in bits
for ( uint8_t i=0; i < 8; i++) {
digitalLow(pinClk); // CLK LOW
if ( command & B1) {
digitalHigh(pinDIO);// DIO HIGH
} else {
digitalLow(pinDIO); // DIO LOW
}
delayMicroseconds(TM1637_CLK_DELAY_US);
command >>= 1;
digitalHigh(pinClk); // CLK HIGH
delayMicroseconds(TM1637_CLK_DELAY_US);
};
}
void SevenSegmentTM1637::comStart(void) const {
comStart(_pinClk, _pinDIO);
};
void SevenSegmentTM1637::comStart(uint8_t pinClk, uint8_t pinDIO) {
digitalHigh(pinDIO); // DIO HIGH
digitalHigh(pinClk); // CLK HIGH
delayMicroseconds(TM1637_CLK_DELAY_US);
digitalLow(pinDIO); // DIO LOW
}
void SevenSegmentTM1637::comStop(void) const {
comStop(_pinClk, _pinDIO);
};
void SevenSegmentTM1637::comStop(uint8_t pinClk, uint8_t pinDIO) {
digitalLow(pinClk); // CLK LOW
delayMicroseconds(TM1637_CLK_DELAY_US);
digitalLow(pinDIO); // DIO LOW
delayMicroseconds(TM1637_CLK_DELAY_US);
digitalHigh(pinClk); // CLK HIGH
delayMicroseconds(TM1637_CLK_DELAY_US);
digitalHigh(pinDIO); // DIO HIGH
}
bool SevenSegmentTM1637::comAck(void) const {
return comAck(_pinClk, _pinDIO);
};
bool SevenSegmentTM1637::comAck(uint8_t pinClk, uint8_t pinDIO) {
bool acknowledged = false;
digitalLow(pinClk); // CLK LOW
pinAsInputPullUp(pinDIO); // DIO INPUT PULLUP (state==HIGH)
delayMicroseconds(TM1637_CLK_DELAY_US);
acknowledged = isLow(pinDIO);// Ack should pull the pin low again
digitalHigh(pinClk); // CLK HIGH
delayMicroseconds(TM1637_CLK_DELAY_US);
digitalLow(pinClk); // CLK LOW
pinAsOutput(pinDIO);
return acknowledged;
}