This is a follow up on my previous re: Tom Almy's TM1638 library.
I've attached his code, and I replaced his test loop() code (which works) with a simple test that should cycle the indicated LED's on and off. However, it doesn't work. After the second updateDisplay(), the LEDs are never turned off. Strangely, if I insert a Serial.println(), it works as expected. I don't understand the indexing in his ISR(), which is where I think the problem lies. Anybody willing to investigate ?
Thanks
/* TM1638_test -- Test program for LED&KEY board with TM1638. Interrupt driven state machine.
* This code works for Arduino boards with AVR microcontrollers. 16MHz clock.
Copyright (C) 2020 Thomas Almy
This program 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 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Contact the author at tom@almy.us */
#include "Pins.h" // Use the new Pin.h library
// SPI pin assignments depend on board.
// Board MOSI MISO SCK
// Uno/Nano/Nano Every 11 12 13
// Mega 51 50 52
// Leonardo ICSP-4 ICSP-1 ICSP-3 (only on ICSP header)
// Micro 16/MOSI 14/MISO 15/SCK (see arduino.cc for locations!)
// Pro Micro 16 14 15
// You can connect to the ICSP header on all boards if you wish.
// IMPORTANT: MISO and MOSI must be connected together.
// For the TM1638 and LED&KEY board, connect DIO to MISO&MOSI, CLK to SCK,
// and STB to digital pin 2 on the Arduino board.
#define TM1638_STB digital_2 // put STB on pin 2
#if defined (__AVR_ATmega4809__)
// Different SPI module
#define SPDR SPI0.DATA
#endif
static const uint8_t PROGMEM SEGMENT_MAP[] = {
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
0b10111111, // 0.
0b10000110, // 1.
0b11011011, // 2.
0b11001111, // 3.
0b11100110, // 4.
0b11101101, // 5.
0b11111101, // 6.
0b10000111, // 7.
0b11111111, // 8.
0b11101111, // 9.
0b11110111, // A.
0b11111100, // b.
0b10111001, // C.
0b11011110, // d.
0b11111001, // E.
0b11110001, // F.
0b01000000, // - (index 32)
0x0 // blank (index 33)
};
void delay1us(void) { // Gives a 1us delay between driving edges
// trimmed for AVR. Seems still OK for 20MHz clock in Nano Every
_NOP();
_NOP();
_NOP();
_NOP();
_NOP();
_NOP();
};
void setup() {
ddr.digital_SS = 1; // This pin must be output
ddr.TM1638_STB = 1;
port.TM1638_STB = 1;
ddr.digital_MOSI = 1; // Output for now
ddr.digital_SCK = 1;
// Enable all but interrupt, LSB first, 1MHz clock
// Negative clock (CPOL=1) and reversed phase (CPHA=1)
// so that we can do the direction change.
#if defined (__AVR_ATmega4809__)
// Different SPI module
PORTMUX.TWISPIROUTEA |= PORTMUX_SPI0_ALT2_gc;
#if F_CPU >= 20000000
// We need /32 divider with 20MHz clock otherwise timing is at hairy edge
// Use the /16 divider at your own risk!
SPI0.CTRLA = 0x75; // DORD MASTER CLK2X PRESC1 (/32) ENABLE
#else
SPI0.CTRLA = 0x63; // DORD MASTER PRESC0 (/16) ENABLE
#endif
SPI0.CTRLB = 3; // Buffering off, CPOL=1, CPHA=1
SPI0.INTCTRL = 1; // Enable interrupt
#else
SPCR = (1 << SPE) + (1 << SPIE) + (1 << MSTR) + (1 << CPOL) + (1 << CPHA) + (1 << DORD) + 1;
#endif
// Do initialization ISR will reset the SPI interface
port.TM1638_STB = 0;
SPDR = 0x89; // Activate display, low (level 1) brightness
delay(1); // Time to execute above
}
// Values for the row of LEDs and the 7-segment display are stored in
// these variables prior to updating with updateDisplay()
uint8_t buffer_LED = 0; // LSB is leftmost LED
uint8_t buffer_display[8] = {10, 11, 12, 13+16, 5, 6, 7, 8};
// The push button values are returned here with readButtons()
volatile uint8_t button_values = 0;
// These are private variables for the SPI state machine
volatile uint8_t spi_index = 0;
volatile uint8_t tm1638_command = 0;
// We can update the entire display (8 digits and 8 LEDs) with 18 bytes
// transferred. If we use the command to write a specific byte, it takes
// 3 bytes transferred. So this way is only slower if need to update 5 or
// fewer digits/LEDs. But we have the advantage that the entire display is
// updated in the background, which takes about 250us. The call to updateDisplay
// takes under a microsecond if not blocked. The conventional
// TM1638 libraries will block during the update, which takes about 3.4 ms for
// the digits alone.
// The functions updateDisplay and readButtons block only if the SPI is
// still shifting from a previous command. To prevent blocking, check
// for SPI running by calling spi_idle first.
// Call this function to see if the SPI is idle and ready for a command
inline bool spi_idle(void) {
return (tm1638_command == 0);
}
// Starts the SPI state machine to update the LED and 7-segment displays
// Do not update the buffer_LED and buffer_display variables until after call
// and spi_idle() returns true.
void updateDisplay(void) {
while (!spi_idle()); // block if SPI still running
port.TM1638_STB = 0; // start command
tm1638_command = 0x40;
SPDR = 0x40; // command to write data incrementing address
}
// Starts the SPI state machine to update the button_values variable.
// Values are updated when spi_idle() returns true.
void readButtons(void) {
while (!spi_idle()); // block if SPI still running
port.TM1638_STB = 0; // start command
tm1638_command = 0x42;
SPDR = 0x42;
}
// The SPI state machine is implemented in its ISR
#if defined (__AVR_ATmega4809__)
ISR(SPI0_INT_vect) { // Invoked when transfer complete
SPI0.INTFLAGS = 0x80; // clear flag because it doesn't happen automatically in 4809
#else
ISR(SPI_STC_vect) { // Invoked when transfer completes
#endif
if (tm1638_command == 0x42) { // Read switches/buttons
if (spi_index == 0) { // start
// switch direction and only read
ddr.digital_MOSI = 0;
spi_index = 1;
button_values = 0;
SPDR = 0; // start read
} else if (spi_index < 4) { // read first three button bytes
button_values |= SPDR << (spi_index - 1);
SPDR = 0; // start next read
spi_index++;
} else { // last button byte
button_values |= SPDR << 3;
delay1us();
port.TM1638_STB = 1;
delay1us();
ddr.digital_MOSI = 1; // Do last, after SS is high
tm1638_command = 0;
spi_index = 0;
}
}
else if (tm1638_command == 0x40) { // write all display
if (spi_index == 0) { // start
// End command byte and set the address
port.TM1638_STB = 1;
delay1us();
port.TM1638_STB = 0;
SPDR = 0xc0;
spi_index = 2; // index plus 2 when writing
} else if (spi_index == 18) { // we are done
delay1us();
port.TM1638_STB = 1;
delay1us();
spi_index = 0;
tm1638_command = 0;
} else {
uint8_t index = (spi_index >> 1) - 1;
if (spi_index & 1) { // LED
SPDR = (buffer_LED >> index) & 1;
} else { // 7-segment display
SPDR = pgm_read_byte(&SEGMENT_MAP[buffer_display[index]]);
}
spi_index++;
}
}
else { // Single byte command (like RESET) or an error condition
// Just reset the SPI
tm1638_command = 0;
spi_index = 0;
port.TM1638_STB = 1;
}
}
// Test program is in loop()
int counter = 0;
uint8_t last_button_values;
void loop_old()
{
int temp = counter++;
last_button_values = button_values;
readButtons();
buffer_display[7] = temp % 10;
temp /= 10;
buffer_display[6] = temp % 10;
temp /= 10;
buffer_display[5] = temp % 10;
temp /= 10;
buffer_display[4] = temp % 10;
while (!spi_idle()) ; // switch read must complete
// before we use the values
// Toggle LED on button press
buffer_LED ^= button_values & ~last_button_values;
updateDisplay();
delay(100);
}
void loop()
{
delay(1000);
buffer_LED = 0b00000000;
//Serial.println();
updateDisplay(); // do this after every change in display data
delay(1000);
buffer_LED = 0b00100100;
updateDisplay();
}