diff --git a/IO_TM1638.h b/IO_TM1638.h new file mode 100644 index 0000000..e8df926 --- /dev/null +++ b/IO_TM1638.h @@ -0,0 +1,88 @@ + /* + * © 2024, Henk Kruisbrink & Chris Harlow. All rights reserved. + * © 2023, Neil McKechnie. All rights reserved. + * + * This file is part of DCC++EX API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#ifndef IO_TM1638_h +#define IO_TM1638_h +#include +#include "IODevice.h" +#include "I2CManager.h" +#include "DIAG.h" +#include "TM1638x.h" +class TM1638 : public IODevice { +private: + + TM1638x * tm; + uint8_t _buttons; + uint8_t _leds; + unsigned long _lastLoop; + static const int LoopHz=20; + +private: + // Constructor + TM1638(VPIN firstVpin, byte clk_pin,byte dio_pin,byte stb_pin){ + _firstVpin = firstVpin; + _nPins = 8; + tm=new TM1638x(clk_pin,dio_pin,stb_pin); + _buttons=0; + _leds=0; + _lastLoop=micros(); + addDevice(this); + } + +public: + static void create(VPIN firstVpin, byte clk_pin,byte dio_pin,byte stb_pin) { + if (checkNoOverlap(firstVpin,8)) + new TM1638(firstVpin, clk_pin,dio_pin,stb_pin); + } + + void _begin() override { + tm->reset(); + tm->test(); + _display(); + } + + + void _loop(unsigned long currentMicros) override { + if (currentMicros - _lastLoop > (1000000UL/LoopHz)) { + _buttons=tm->getButtons();// Read the buttons + _lastLoop=currentMicros; + } + // DIAG(F("TM1638 buttons %x"),_buttons); + } + + void _display() override { + DIAG(F("TM1638 Configured on Vpins:%u-%u"), _firstVpin, _firstVpin+_nPins-1); + } + +// digital read gets button state +int _read(VPIN vpin) override { + byte pin=vpin - _firstVpin; + bool result=bitRead(_buttons,pin); + // DIAG(F("TM1638 read (%d) buttons %x = %d"),pin,_buttons,result); + return result; +} + +// digital write sets led state +void _write(VPIN vpin, int value) override { + tm->writeLed(vpin - _firstVpin + 1,value!=0); + } + +}; +#endif // IO_TM1638_h diff --git a/IO_TM1638.hzz b/IO_TM1638.hzz new file mode 100644 index 0000000..3f4ce74 --- /dev/null +++ b/IO_TM1638.hzz @@ -0,0 +1,186 @@ + /* + * © 2024, Henk Kruisbrink & Chris Harlow. All rights reserved. + * © 2023, Neil McKechnie. All rights reserved. + * + * This file is part of DCC++EX API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +/* + * + * Dec 2023, Added NXP SC16IS752 I2C Dual UART + * The SC16IS752 has 64 bytes TX & RX FIFO buffer + * First version without interrupts from I2C UART and only RX/TX are used, interrupts may not be + * needed as the RX Fifo holds the reply + * + * Jan 2024, Issue with using both UARTs simultaniously, the secod uart seems to work but the first transmit + * corrupt data. This need more analysis and experimenatation. + * Will push this driver to the dev branch with the uart fixed to 0 + * Both SC16IS750 (single uart) and SC16IS752 (dual uart, but only uart 0 is enable) + * + * myHall.cpp configuration syntax: + * + * I2CRailcom::create(1st vPin, vPins, I2C address); + * + * myAutomation configuration + * HAL(I2CRailcom, 1st vPin, vPins, I2C address) + * Parameters: + * 1st vPin : First virtual pin that EX-Rail can control to play a sound, use PLAYSOUND command (alias of ANOUT) + * vPins : Total number of virtual pins allocated (2 vPins are supported, one for each UART) + * 1st vPin for UART 0, 2nd for UART 1 + * I2C Address : I2C address of the serial controller, in 0x format + */ + +#ifndef IO_TM1638_h +#define IO_TM1638_h +#include +#include "IODevice.h" +#include "I2CManager.h" +#include "DIAG.h" + +class TM1638 : public IODevice { +private: + + static const uint8_t + INSTRUCTION_WRITE_DATA=0x40, + INSTRUCTION_READ_KEY=0x42, + INSTRUCTION_ADDRESS_AUTO=0x40, + INSTRUCTION_ADDRESS_FIXED=0x44, + INSTRUCTION_NORMAL_MODE=0x40, + INSTRUCTION_TEST_MODE=0x48, + FIRST_DISPLAY_ADDRESS=0xC0, + DISPLAY_TURN_OFF=0x80, + DISPLAY_TURN_ON=0x88; + + static constexpr uint8_t _digits[16]={ // 7-segment hex + 0b00111111,0b00000110,0b01011011,0b01001111, + 0b01100110,0b01101101,0b01111101,0b00000111, + 0b01111111,0b01101111,0b01110111,0b01111100, + 0b00111001,0b01011110,0b01111001,0b01110001 + }; + + uint8_t _clk_pin; + uint8_t _stb_pin; + uint8_t _dio_pin; + uint8_t _buttons; + uint8_t _leds; + uint8_t _pulse; + + +private: + // Constructor + TM1638(VPIN firstVpin, byte clk_pin,byte stb_pin,byte dio_pin){ + _firstVpin = firstVpin; + _nPins = 8; + _clk_pin=clk_pin; + _stb_pin=stb_pin; + _dio_pin=dio_pin; + _buttons=0; + _leds=0; + _pulse=4; + pinMode(stb_pin, OUTPUT); + pinMode(clk_pin, OUTPUT); + pinMode(dio_pin, OUTPUT); + ArduinoPins::fastWriteDigital(stb_pin, HIGH); + ArduinoPins::fastWriteDigital(clk_pin, HIGH); + ArduinoPins::fastWriteDigital(dio_pin, HIGH); + addDevice(this); + } + +public: + static void create(VPIN firstVpin, byte clk_pin,byte stb_pin,byte dio_pin) { + if (checkNoOverlap(firstVpin,8)) + new TM1638(firstVpin, clk_pin,stb_pin,dio_pin); + } + + void _begin() override { + _deviceState = DEVSTATE_NORMAL; + writeCommand(DISPLAY_TURN_ON | _pulse); + _display(); + } + + void strobe(bool on) { + ArduinoPins::fastWriteDigital(_stb_pin, on?LOW:HIGH); // select device + delayMicroseconds(1); + } + + void _loop(unsigned long currentMicros) override { + + // Read the buttons + strobe(true); + writeData(INSTRUCTION_READ_KEY); + pinMode(_dio_pin, INPUT); + + _buttons=0; + for (uint8_t i=0; 4;i++){ + auto data = readData(); + _buttons |= ((data>>4) & 0x01) | (data & 0x01); + _buttons <<= 2; + } + pinMode(_dio_pin,OUTPUT); + strobe(false); + } + + void _display() override { + DIAG(F("TM1638 Configured on Vpins:%u-%u"), _firstVpin, _firstVpin+_nPins-1); + } + +// digital read gets button state +int _read(VPIN vpin) override { + return bitRead(_buttons,vpin - _firstVpin); +} + +// digital write sets led state +void _write(VPIN vpin, int value) override { + auto pin=vpin-_firstVpin; + writeCommand(INSTRUCTION_WRITE_DATA | INSTRUCTION_ADDRESS_FIXED); + strobe(true); + writeData(FIRST_DISPLAY_ADDRESS + (pin*2+1)); + writeData(value?0x01:0x00); + strobe(false); + } + +void writeCommand(uint8_t val) +{ + strobe(true); + writeData(val); + strobe(false); +} + +void writeData(uint8_t val) +{ + ArduinoPins::fastWriteDigital(_clk_pin, LOW); + for (uint8_t i = 0; i < 8; i++) { + ArduinoPins::fastWriteDigital(_dio_pin, val & 1); + val >>= 1; + ArduinoPins::fastWriteDigital(_clk_pin, HIGH); + ArduinoPins::fastWriteDigital(_clk_pin, LOW); + } +} + +uint8_t readData() { + uint8_t value = 0; + ArduinoPins::fastWriteDigital(_clk_pin, LOW); + for (uint8_t i = 0; i < 8; ++i) { + ArduinoPins::fastWriteDigital(_clk_pin, HIGH); + value |= ArduinoPins::fastReadDigital(_dio_pin) << i; + ArduinoPins::fastWriteDigital(_clk_pin, LOW); + } + return value; +} + + +}; +#endif // IO_I2CRailcom_h diff --git a/TM1638x.cpp b/TM1638x.cpp new file mode 100644 index 0000000..8ae343c --- /dev/null +++ b/TM1638x.cpp @@ -0,0 +1,139 @@ +#include "Arduino.h" +#include "TM1638x.h" +#include "DIAG.h" + +bool TM1638x::getButton(button_t s){ + _buttons = getButtons(); + return bitRead(_buttons, s); +} + +// buttons K3/KS1-8 +uint8_t TM1638x::getButtons(){ + digitalWrite(_stb_pin, LOW); + writeData(INSTRUCTION_READ_KEY); + //Twait 1µs + pinMode(_dio_pin, INPUT); + digitalWrite(_clk_pin, LOW); + uint8_t data[4]; + for (uint8_t i=0; i7) | (val>15) | (val<0)) return; + setDisplayMode(DISPLAY_TURN_ON | _pulse); + setDataInstruction(INSTRUCTION_WRITE_DATA| INSTRUCTION_ADDRESS_FIXED); + writeDataAt(FIRST_DISPLAY_ADDRESS+14-(digitId*2), _digits[val]); +} + +void TM1638x::displayDig(uint8_t digitId, uint8_t pgfedcba){ + if (digitId>7) return; + setDisplayMode(DISPLAY_TURN_ON | _pulse); + setDataInstruction(INSTRUCTION_WRITE_DATA| INSTRUCTION_ADDRESS_FIXED); + writeDataAt(FIRST_DISPLAY_ADDRESS+14-(digitId*2), pgfedcba); +} + +void TM1638x::displayClear(){ + setDisplayMode(DISPLAY_TURN_ON | _pulse); + setDataInstruction(INSTRUCTION_WRITE_DATA | INSTRUCTION_ADDRESS_FIXED); + for (uint8_t i=0;i<15;i+=2){ + writeDataAt(FIRST_DISPLAY_ADDRESS+i,0x00); + } +} + +void TM1638x::writeLed(uint8_t num,bool state){ + //DIAG(F("TM1638x writeLed(%d,%d)"),num,state); + if ((num<1) | (num>8)) return; + setDisplayMode(DISPLAY_TURN_ON | _pulse); + setDataInstruction(INSTRUCTION_WRITE_DATA | INSTRUCTION_ADDRESS_FIXED); + writeDataAt(FIRST_DISPLAY_ADDRESS + (num*2-1), state); +} + +void TM1638x::writeLeds(uint8_t val){ + setDisplayMode(DISPLAY_TURN_ON | _pulse); + setDataInstruction(INSTRUCTION_WRITE_DATA | INSTRUCTION_ADDRESS_FIXED); + for(uint8_t i=1;i<9;i++){ + writeDataAt(FIRST_DISPLAY_ADDRESS + (i*2-1), val & 0x01); + val >>= 1; + } +} + +void TM1638x::displayTurnOn(){ + setDisplayMode(DISPLAY_TURN_ON | _pulse); + _isOn = true; +} + +void TM1638x::displayTurnOff(){ + setDisplayMode(DISPLAY_TURN_OFF | _pulse); + _isOn = false; +} + +void TM1638x::displaySetBrightness(pulse_t newpulse){ + if ((newpulsePULSE14_16)) return; + _pulse = newpulse; + uint8_t data = (_isOn) ? DISPLAY_TURN_ON : DISPLAY_TURN_OFF; + data |= _pulse; + setDisplayMode(data); +} + +void TM1638x::writeData(uint8_t data){ + shiftOut(_dio_pin,_clk_pin,LSBFIRST,data); +} + +void TM1638x::writeDataAt(uint8_t displayAddress, uint8_t data){ + digitalWrite(_stb_pin, LOW); + writeData(displayAddress); + writeData(data); + digitalWrite(_stb_pin, HIGH); + delayMicroseconds(1); +} + +void TM1638x::setDisplayMode(uint8_t displayMode){ + digitalWrite(_stb_pin, LOW); + writeData(displayMode); + digitalWrite(_stb_pin, HIGH); + delayMicroseconds(1); +} +void TM1638x::setDataInstruction(uint8_t dataInstruction){ + digitalWrite(_stb_pin, LOW); + writeData(dataInstruction); + digitalWrite(_stb_pin, HIGH); + delayMicroseconds(1); +} + +void TM1638x::test(){ + DIAG(F("TM1638x test")); + uint8_t val=0; + for(uint8_t i=0;i<5;i++){ + //setDisplayMode(DISPLAY_TURN_ON | _pulse); + displayTurnOn(); + setDataInstruction(INSTRUCTION_WRITE_DATA| INSTRUCTION_ADDRESS_AUTO); + digitalWrite(_stb_pin, LOW); + writeData(FIRST_DISPLAY_ADDRESS); + for(uint8_t i=0;i<16;i++) + writeData(val); + digitalWrite(_stb_pin, HIGH); + delay(1000); + val = ~val; + } + +} \ No newline at end of file diff --git a/TM1638x.h b/TM1638x.h new file mode 100644 index 0000000..baaa9d6 --- /dev/null +++ b/TM1638x.h @@ -0,0 +1,170 @@ +/*! + * @file TM1638.h + * @brief Arduino library for interface with TM1638 chip. + * @n read buttons, switch leds, display on 7segment. + * @author [Damien](web@varrel.fr) + * @version V1.0.1 + * @date 2024-02-06 + * @url https://github.com/dvarrel/TM1638.git + * @module https://fr.aliexpress.com/item/32832772646.html + */ + +#ifndef _TM1638_H +#define _TM1638_H +#include "Arduino.h" + +#ifndef ON +#define ON 1 +#endif +#ifndef OFF +#define OFF 0 +#endif + + typedef enum{ + PULSE1_16, + PULSE2_16, + PULSE4_16, + PULSE10_16, + PULSE11_16, + PULSE12_16, + PULSE13_16, + PULSE14_16 + } pulse_t; + + typedef enum{ + S1,S2,S3,S4, + S5,S6,S7,S8 + } button_t; + +class TM1638x{ + private: + #define INSTRUCTION_WRITE_DATA 0x40 + #define INSTRUCTION_READ_KEY 0x42 + #define INSTRUCTION_ADDRESS_AUTO 0x40 + #define INSTRUCTION_ADDRESS_FIXED 0x44 + #define INSTRUCTION_NORMAL_MODE 0x40 + #define INSTRUCTION_TEST_MODE 0x48 + + #define FIRST_DISPLAY_ADDRESS 0xC0 + + #define DISPLAY_TURN_OFF 0x80 + #define DISPLAY_TURN_ON 0x88 + + uint8_t _digits[16]={ + 0b00111111,0b00000110,0b01011011,0b01001111, + 0b01100110,0b01101101,0b01111101,0b00000111, + 0b01111111,0b01101111,0b01110111,0b01111100, + 0b00111001,0b01011110,0b01111001,0b01110001 + }; + + uint8_t _clk_pin; + uint8_t _stb_pin; + uint8_t _dio_pin; + uint8_t _buttons; + uint8_t _pulse; + bool _isOn; + + public: + TM1638x(uint8_t clk_pin, uint8_t dio_pin, uint8_t stb_pin){ + _clk_pin = clk_pin; + _stb_pin = stb_pin; + _dio_pin = dio_pin; + _pulse = PULSE1_16; + _isOn = false; + + pinMode(stb_pin, OUTPUT); + pinMode(clk_pin, OUTPUT); + pinMode(dio_pin, OUTPUT); + digitalWrite(stb_pin, HIGH); + digitalWrite(clk_pin, HIGH); + digitalWrite(dio_pin, HIGH); + } + + /** + * @fn getButton + * @param s num of button (S1-S8) + * @return state of button + */ + bool getButton(button_t s); + /** + * @fn getButtons + * @return state of 8 buttons + */ + uint8_t getButtons(); + + /** + * @fn writeLed + * @brief put led ON or OFF + * @param num num of led(1-8) + * @param state (true or false) + */ + void writeLed(uint8_t num, bool state); + + /** + * @fn writeLeds + * @brief set all 8 leds ON or OFF + * @param val 8bits + */ + void writeLeds(uint8_t val); + + /** + * @fn displayVal + * @brief put value on 7 segment display + * @param digitId num of digit(0-7) + * @param val value(0->F) + */ + void displayVal(uint8_t digitId, uint8_t val); + + + /** + * @fn displayDig + * @brief set 7 segment display + dot + * @param digitId num of digit(0-7) + * @param val value 8 bits + */ + void displayDig(uint8_t digitId, uint8_t pgfedcba); + + /** + * @fn displayClear + * @brief switch off all leds and segment display + */ + void displayClear(); + + /** + * @fn displayTurnOff + * @brief turn on lights + */ + void displayTurnOff(); + + /** + * @fn displayTurnOn + * @brief turn off lights + */ + void displayTurnOn(); + + /** + * @fn displaySetBrightness + * @brief set display brightness + * @param pulse_t (0-7) + */ + void displaySetBrightness(pulse_t pulse); + + /** + * @fn reset + * @brief switch off all displays-leds + */ + void reset(); + + /** + * @fn test + * @brief blink all displays and leds + */ + void test(); + + private: + void writeData(uint8_t data); + void writeDataAt(uint8_t displayAddress, uint8_t data); + void setDisplayMode(uint8_t displayMode); + void setDataInstruction(uint8_t dataInstruction); +}; +#endif \ No newline at end of file