diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a8ac54 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/ +.vscode/ diff --git a/makefile b/makefile new file mode 100644 index 0000000..ea0f724 --- /dev/null +++ b/makefile @@ -0,0 +1,29 @@ +MCU=atmega32 +F_CPU=8000000 +PROG=dragon_jtag +DEBUGGER = dragon +PORT=/dev/ttyUSB0 + +CC=avr-g++ +OBJCOPY=avr-objcopy +CFLAGS=-Wall -g -mmcu=${MCU} -DF_CPU=${F_CPU} -I. +TARGET=main +SRCS=src/*.cpp + +default: build flash + +build: + ${CC} ${CFLAGS} -o bin/${TARGET}.bin ${SRCS} + ${CC} ${CFLAGS} -o bin/${TARGET}.elf ${SRCS} + ${OBJCOPY} -j .text -j .data -O ihex bin/${TARGET}.bin bin/${TARGET}.hex + +flash: + avrdude -p ${MCU} -c ${PROG} -U flash:w:bin/main.hex + +clean: + rm -f bin/* + +debug: flash + sleep 1 + avarice --program --file bin/main.elf --part ${MCU} --${DEBUGGER} :4242 & + avr-gdb -ex "target remote :4242" bin/main.elf diff --git a/src/TM1637Display.cpp b/src/TM1637Display.cpp new file mode 100644 index 0000000..5dddb16 --- /dev/null +++ b/src/TM1637Display.cpp @@ -0,0 +1,296 @@ + +// Author: avishorp@gmail.com +// +// 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 + +extern "C" { + #include + #include + #include +} + +#include "TM1637Display.h" +#include +#include + +#define TM1637_I2C_COMM1 0x40 +#define TM1637_I2C_COMM2 0xC0 +#define TM1637_I2C_COMM3 0x80 + +// +// A +// --- +// F | | B +// -G- +// E | | C +// --- +// D +const uint8_t digitToSegment[] = { + // XGFEDCBA + 0b00111111, // 0 + 0b00000110, // 1 + 0b01011011, // 2 + 0b01001111, // 3 + 0b01100110, // 4 + 0b01101101, // 5 + 0b01111101, // 6 + 0b00000111, // 7 + 0b01111111, // 8 + 0b01101111, // 9 + 0b01110111, // A + 0b01111100, // b + 0b00111001, // C + 0b01011110, // d + 0b01111001, // E + 0b01110001 // F + }; + +static const uint8_t minusSegments = 0b01000000; + + + + +TM1637Display::TM1637Display(uint8_t pinClk, uint8_t pinDIO, volatile uint8_t *port, int bitDelay) +{ + // Copy the pin numbers + m_pinClk = pinClk; + m_pinDIO = pinDIO; + m_bitDelay = bitDelay; + m_port = port; + m_ddr = port-1; + m_pin = port-2; + + // Set the pin direction and default value. + // Both pins are set as inputs, allowing the pull-up resistors to pull them up + //CLK_Input(); + //pinMode(m_pinDIO,INPUT); + DIO_Input(); + CLK_Input(); + //digitalWrite(m_pinClk, LOW); + //digitalWrite(m_pinDIO, LOW); + DIO_Low(); + CLK_Low(); +} + +void TM1637Display::setBrightness(uint8_t brightness, bool on) +{ + m_brightness = (brightness & 0x7) | (on? 0x08 : 0x00); +} + +void TM1637Display::setSegments(const uint8_t segments[], uint8_t length, uint8_t pos) +{ + // Write COMM1 + start(); + writeByte(TM1637_I2C_COMM1); + stop(); + + // Write COMM2 + first digit address + start(); + writeByte(TM1637_I2C_COMM2 + (pos & 0x03)); + + // Write the data bytes + for (uint8_t k=0; k < length; k++) + writeByte(segments[k]); + + stop(); + + // Write COMM3 + brightness + start(); + writeByte(TM1637_I2C_COMM3 + (m_brightness & 0x0f)); + stop(); +} + +void TM1637Display::clear() +{ + uint8_t data[] = { 0, 0, 0, 0 }; + setSegments(data); +} + +void TM1637Display::showNumberDec(int num, bool leading_zero, uint8_t length, uint8_t pos) +{ + showNumberDecEx(num, 0, leading_zero, length, pos); +} + +void TM1637Display::showNumberDecEx(int num, uint8_t dots, bool leading_zero, + uint8_t length, uint8_t pos) +{ + showNumberBaseEx(num < 0? -10 : 10, num < 0? -num : num, dots, leading_zero, length, pos); +} + +void TM1637Display::showNumberHexEx(uint16_t num, uint8_t dots, bool leading_zero, + uint8_t length, uint8_t pos) +{ + showNumberBaseEx(16, num, dots, leading_zero, length, pos); +} + +void TM1637Display::showNumberBaseEx(int8_t base, uint16_t num, uint8_t dots, bool leading_zero, + uint8_t length, uint8_t pos) +{ + bool negative = false; + if (base < 0) { + base = -base; + negative = true; + } + + + uint8_t digits[4]; + + if (num == 0 && !leading_zero) { + // Singular case - take care separately + for(uint8_t i = 0; i < (length-1); i++) + digits[i] = 0; + digits[length-1] = encodeDigit(0); + } + else { + //uint8_t i = length-1; + //if (negative) { + // // Negative number, show the minus sign + // digits[i] = minusSegments; + // i--; + //} + + for(int i = length-1; i >= 0; --i) + { + uint8_t digit = num % base; + + if (digit == 0 && num == 0 && leading_zero == false) + // Leading zero is blank + digits[i] = 0; + else + digits[i] = encodeDigit(digit); + + if (digit == 0 && num == 0 && negative) { + digits[i] = minusSegments; + negative = false; + } + + num /= base; + } + + if(dots != 0) + { + showDots(dots, digits); + } + } + setSegments(digits, length, pos); +} + +void TM1637Display::bitDelay() +{ + _delay_us(m_bitDelay); +} + +void TM1637Display::start() +{ + DIO_Output(); + bitDelay(); +} + +void TM1637Display::stop() +{ + DIO_Output(); + bitDelay(); + CLK_Input(); + bitDelay(); + DIO_Input(); + bitDelay(); +} + +bool TM1637Display::writeByte(uint8_t b) +{ + uint8_t data = b; + + // 8 Data Bits + for(uint8_t i = 0; i < 8; i++) { + // CLK low + CLK_Output(); + bitDelay(); + + // Set data bit + if (data & 0x01) + DIO_Input(); + else + DIO_Output(); + + bitDelay(); + + // CLK high + CLK_Input(); + bitDelay(); + data = data >> 1; + } + + // Wait for acknowledge + // CLK to zero + CLK_Output(); + DIO_Input(); + bitDelay(); + + // CLK to high + CLK_Input(); + bitDelay(); + uint8_t ack = DIO_Read(); + if (ack == 0) + DIO_Output(); + + + bitDelay(); + CLK_Output(); + bitDelay(); + + return ack; +} + +void TM1637Display::showDots(uint8_t dots, uint8_t* digits) +{ + for(int i = 0; i < 4; ++i) + { + digits[i] |= (dots & 0x80); + dots <<= 1; + } +} + +uint8_t TM1637Display::encodeDigit(uint8_t digit) +{ + return digitToSegment[digit & 0x0f]; +} + +inline void TM1637Display::DIO_Output() { + *(m_ddr) |= (1<> m_pinDIO; + } + + inline void TM1637Display::CLK_Output() { + *(m_ddr) |= (1< +#include + +#define SEG_A 0b00000001 +#define SEG_B 0b00000010 +#define SEG_C 0b00000100 +#define SEG_D 0b00001000 +#define SEG_E 0b00010000 +#define SEG_F 0b00100000 +#define SEG_G 0b01000000 + +#define DEFAULT_BIT_DELAY 100 + +class TM1637Display { + +public: + //! Initialize a TM1637Display object, setting the clock and + //! data pins. + //! + //! @param pinClk - The number of the digital pin connected to the clock pin of the module + //! @param pinDIO - The number of the digital pin connected to the DIO pin of the module + //! @param bitDelay - The delay, in microseconds, between bit transition on the serial + //! bus connected to the display + TM1637Display(uint8_t pinClk, uint8_t pinDIO, volatile uint8_t *port, int bitDelay = DEFAULT_BIT_DELAY); + + //! Sets the brightness of the display. + //! + //! The setting takes effect when a command is given to change the data being + //! displayed. + //! + //! @param brightness A number from 0 (lowes brightness) to 7 (highest brightness) + //! @param on Turn display on or off + void setBrightness(uint8_t brightness, bool on = true); + + //! Display arbitrary data on the module + //! + //! This function receives raw segment values as input and displays them. The segment data + //! is given as a byte array, each byte corresponding to a single digit. Within each byte, + //! bit 0 is segment A, bit 1 is segment B etc. + //! The function may either set the entire display or any desirable part on its own. The first + //! digit is given by the @ref pos argument with 0 being the leftmost digit. The @ref length + //! argument is the number of digits to be set. Other digits are not affected. + //! + //! @param segments An array of size @ref length containing the raw segment values + //! @param length The number of digits to be modified + //! @param pos The position from which to start the modification (0 - leftmost, 3 - rightmost) + void setSegments(const uint8_t segments[], uint8_t length = 4, uint8_t pos = 0); + + //! Clear the display + void clear(); + + //! Display a decimal number + //! + //! Dispaly the given argument as a decimal number. + //! + //! @param num The number to be shown + //! @param leading_zero When true, leading zeros are displayed. Otherwise unnecessary digits are + //! blank. NOTE: leading zero is not supported with negative numbers. + //! @param length The number of digits to set. The user must ensure that the number to be shown + //! fits to the number of digits requested (for example, if two digits are to be displayed, + //! the number must be between 0 to 99) + //! @param pos The position of the most significant digit (0 - leftmost, 3 - rightmost) + void showNumberDec(int num, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0); + + //! Display a decimal number, with dot control + //! + //! Dispaly the given argument as a decimal number. The dots between the digits (or colon) + //! can be individually controlled. + //! + //! @param num The number to be shown + //! @param dots Dot/Colon enable. The argument is a bitmask, with each bit corresponding to a dot + //! between the digits (or colon mark, as implemented by each module). i.e. + //! For displays with dots between each digit: + //! * 0.000 (0b10000000) + //! * 00.00 (0b01000000) + //! * 000.0 (0b00100000) + //! * 0.0.0.0 (0b11100000) + //! For displays with just a colon: + //! * 00:00 (0b01000000) + //! For displays with dots and colons colon: + //! * 0.0:0.0 (0b11100000) + //! @param leading_zero When true, leading zeros are displayed. Otherwise unnecessary digits are + //! blank. NOTE: leading zero is not supported with negative numbers. + //! @param length The number of digits to set. The user must ensure that the number to be shown + //! fits to the number of digits requested (for example, if two digits are to be displayed, + //! the number must be between 0 to 99) + //! @param pos The position of the most significant digit (0 - leftmost, 3 - rightmost) + void showNumberDecEx(int num, uint8_t dots = 0, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0); + + //! Display a hexadecimal number, with dot control + //! + //! Dispaly the given argument as a hexadecimal number. The dots between the digits (or colon) + //! can be individually controlled. + //! + //! @param num The number to be shown + //! @param dots Dot/Colon enable. The argument is a bitmask, with each bit corresponding to a dot + //! between the digits (or colon mark, as implemented by each module). i.e. + //! For displays with dots between each digit: + //! * 0.000 (0b10000000) + //! * 00.00 (0b01000000) + //! * 000.0 (0b00100000) + //! * 0.0.0.0 (0b11100000) + //! For displays with just a colon: + //! * 00:00 (0b01000000) + //! For displays with dots and colons colon: + //! * 0.0:0.0 (0b11100000) + //! @param leading_zero When true, leading zeros are displayed. Otherwise unnecessary digits are + //! blank + //! @param length The number of digits to set. The user must ensure that the number to be shown + //! fits to the number of digits requested (for example, if two digits are to be displayed, + //! the number must be between 0 to 99) + //! @param pos The position of the most significant digit (0 - leftmost, 3 - rightmost) + void showNumberHexEx(uint16_t num, uint8_t dots = 0, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0); + + //! Translate a single digit into 7 segment code + //! + //! The method accepts a number between 0 - 15 and converts it to the + //! code required to display the number on a 7 segment display. + //! Numbers between 10-15 are converted to hexadecimal digits (A-F) + //! + //! @param digit A number between 0 to 15 + //! @return A code representing the 7 segment image of the digit (LSB - segment A; + //! bit 6 - segment G; bit 7 - always zero) + uint8_t encodeDigit(uint8_t digit); + +protected: + void bitDelay(); + + void start(); + + void stop(); + + bool writeByte(uint8_t b); + + void showDots(uint8_t dots, uint8_t* digits); + + void showNumberBaseEx(int8_t base, uint16_t num, uint8_t dots = 0, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0); + + +private: + uint8_t m_pinClk; + uint8_t m_pinDIO; + uint8_t m_brightness; + volatile uint8_t *m_port; + volatile uint8_t *m_ddr; + volatile uint8_t *m_pin; + unsigned int m_bitDelay; + inline void DIO_Output(); + inline void DIO_Input(); + inline void DIO_Low(); + inline bool DIO_Read(); + inline void CLK_Output(); + inline void CLK_Input(); + inline void CLK_Low(); +}; + +#endif // __TM1637DISPLAY__ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..40337d8 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,29 @@ +/* + * Blink.cpp + * + * Created: 18/12/2018 5:11:34 PM + * Author : jimmy + */ +#define F_CPU 8000000 +#include +#include +#include +#include "TM1637Display.h" + +int main(void) { + //*(&PORTA-1) |= (1<