diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 001d91b..01275c9 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -288,11 +288,35 @@ menu "MZ25Key Configuration" endmenu - config DEBUG_SERIAL + config MZ_DEBUG_SERIAL bool "Serial debug output" default false help Enable debug output (non ESP logging) on the serial port. + + config MZ_DISABLE_KDB + bool "Disable input mode actuation of the KDB data bus" + default false + help + Disable the MZ Interface KDB input configuration step, useful feature for debugging. + + config MZ_DISABLE_KDO + bool "Disable output mode actuation of the KDO strobe row" + default false + help + Disable the MZ Interface KDO output configuration step, useful feature for debugging. + + config MZ_DISABLE_RTSNI + bool "Disable input mode actuation of the RTSNi signal" + default false + help + Disable the MZ Interface RTSNi input configuration step, useful feature for debugging. + + config MZ_DISABLE_KDI + bool "Disable input mode actuation of the KDI4 signal" + default false + help + Disable the MZ Interface KDI input configuration step, useful feature for debugging. endmenu config PWRLED diff --git a/main/MZKeyTable.h b/main/MZKeyTable.h index 88403e8..53acc89 100644 --- a/main/MZKeyTable.h +++ b/main/MZKeyTable.h @@ -1,12 +1,12 @@ ///////////////////////////////////////////////////////////////////////////////////////////////////////// // -// Name: keytable.h +// Name: MZKeyTable.h // Created: Jan 2022 // Version: v1.0 // Author(s): Philip Smart // Description: The PS/2 Scan Code to MZ-2500/2800 Key Matrix mapping logic. // This source file contains the definitions and tables to convert a PS/2 scan code -// into an MZ 13x8 matrix equivalent for the received key. The matrix is then read +// into an MZ 14x8 matrix equivalent for the received key. The matrix is then read // out to the MZ-2500/2800 as though it was a real keyboard. // Credits: // Copyright: (c) 2019-2022 Philip Smart @@ -52,13 +52,24 @@ // 4 G F E D C B A ? // 5 O N M L K J I H // 6 W V U T S R Q P -// 7 <¿ .>¿ ¿¿ ¿ | '¿ Z ¿ Y X ¿ +// 7 <¿ .>¿ ¿¿ ¿ | '¿ Z ¿ Y X ¿ // 8 7' 6& 5% 4$ 3# 2" 1! 0 // 9 [( @ -= ;+ :* 9) 8( // 10 / * ESC BACKSPACE INST/DEL CLR/HOME COPY ]} -// 11 CTRL ¿¿ SHIFT LOCK GRAPH -// 12 ¿¿ ¿¿¿ +// 11 CTRL ¿¿ SHIFT LOCK GRAPH +// 12 ¿¿ ¿¿¿ // 13 HELP ARGO +// +// Col 0 1 2 3 4 5 6 7 8 9 10 11 12 13 +// -------------------------------------------------------------------------------------------------------------------------------------- +// D0 F1 F9 0 TAB ? H P 0 8( ]} GRAPH ¿¿¿ ARGO +// D1 F2 F10 1 SPACE A I Q T 1! 9) COPY LOCK ¿¿ HELP +// D2 F3 8 2 RETURN B J R Z ¿ 2" :* CLR/HOME SHIFT +// D3 F4 9 3 UP C K S | '¿ 3# ;+ INST/DEL ¿¿ +// D4 F5 , 4 DOWN D L T ¿ 4$ -= BACKSPACE CTRL +// D5 F6 . 5 LEFT E M U ¿¿ 5% @ ESC +// D6 F7 + 6 RIGHT F N V .>¿ 6& [( * +// D7 F8 - 7 BREAK G O W <¿ 7' / #define PSMZTBL_KEYPOS 0 #define PSMZTBL_SHIFTPOS 1 @@ -183,7 +194,4 @@ const unsigned char PS2toMZ[][PSMZTBL_MAXROWS] = 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; -// PS/2 BREAK key code string -const unsigned char BREAK_CODE[8]={0xE1,0x14,0x77,0xE1,0xF0,0x14,0xF0,0x77}; - #endif // KEYTABLE_H diff --git a/main/main.cpp b/main/main.cpp index 5664b09..ea4a14c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,12 +1,30 @@ ///////////////////////////////////////////////////////////////////////////////////////////////////////// // -// Name: mz25key.ino +// Name: main.cpp // Created: Jan 2022 // Version: v1.0 // Author(s): Philip Smart // Description: MZ2500/2800 Key Matrix logic. -// This source file contains the logic to transmit the virtual key matrix, which is -// built from PS/2 scan codes, to the MZ2500/2800. +// This source file contains the application logic to obtain PS/2 scan codes, map them +// into a virtual keyboard matrix as per the Sharp MZ series key matrix and the +// logic to transmit the virtual key matrix to the MZ2500/2800. +// +// The application uses a modified version of the PS2KeyAdvanced +// https://github.com/techpaul/PS2KeyAdvanced class from Paul Carpenter. +// +// The application uses, for debug purposes, the esp-idf-ssd1306 class from nopnop2002 +// https://github.com/nopnop2002/esp-idf-ssd1306. +// +// The application uses the Espressif Development environment with Arduino components. +// This is necessary for the PS2KeyAdvanced class, which I may in future convert to +// use esp-idf library calls rather than Arduino. +// +// The Espressif environment is necessary in order to have more control over the build. +// It is important, for timing, that the Core 1 is dedicated to MZ Interface +// logic and Core 0 is used for all RTOS/Interrupts tasks. +// +// The application is configured via the Kconfig system. Use idf.py menuconfig to +// configure. // Credits: // Copyright: (c) 2022 Philip Smart // @@ -53,12 +71,37 @@ // // All configuration is performed via the 'idf.py menuconfig' command. // Optionally override via the definitions below. -// Debugging options. ////////////////////////////////////////////////////////////////////////// //#define CONFIG_DEBUG_OLED !CONFIG_OLED_DISABLED //#define CONFIG_PWRLED 25 //#define CONFIG_PS2_HW_DATAPIN 14 //#define CONFIG_PS2_HW_CLKPIN 13 +//#define CONFIG_KEYMAP_WYSE_KB3926 +//#define CONFIG_KEYMAP_STANDARD +//#define CONFIG_MZ_KDB0 23 +//#define CONFIG_MZ_KDB1 25 +//#define CONFIG_MZ_KDB2 26 +//#define CONFIG_MZ_KDB3 27 +//#define CONFIG_MZ_KDO0 14 +//#define CONFIG_MZ_KDO1 15 +//#define CONFIG_MZ_KDO2 16 +//#define CONFIG_MZ_KDO3 17 +//#define CONFIG_MZ_KDO4 18 +//#define CONFIG_MZ_KDO5 19 +//#define CONFIG_MZ_KDO6 21 +//#define CONFIG_MZ_KDO7 21 +//#define CONFIG_MZ_RTSNI 35 +//#define CONFIG_MZ_KDI4 13 +//#CONFIG_OLED_DISABLED +//#CONFIG_I2C_INTERFACE +//#CONFIG_SPI_INTERFACE +//#CONFIG_SSD1306_128x32 +//#CONFIG_SSD1306_128x64 +//#CONFIG_OFFSETX 0 +//#CONFIG_FLIP +//#CONFIG_SCL_GPIO 5 +//#CONFIG_SDA_GPIO 4 +//#CONFIG_RESET_GPIO 16 // Macros. // @@ -67,13 +110,19 @@ // Structure to manage the translated key matrix. This is updated by the ps2Interface thread and read by the mzInterface thead. typedef struct { uint8_t strobeAll; - uint8_t keyMatrix[15]; + uint32_t strobeAllAsGPIO; + uint8_t keyMatrix[16]; + uint32_t keyMatrixAsGPIO[16]; } t_mzControl; -volatile t_mzControl mzControl = { 0xFF, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; +volatile t_mzControl mzControl = { 0xFF, 0x00000000, + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } + }; // Instantiate base classes. First, production required objects. PS2KeyAdvanced Keyboard; -// Nex/t, debug required objects. + +// Debug required objects. SSD1306_t SSD1306; // Handle to interact with the mz-2500 interface thread. @@ -84,20 +133,27 @@ TaskHandle_t TaskPS2IF = NULL; static portMUX_TYPE mzMutex = portMUX_INITIALIZER_UNLOCKED; #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) -// Printf to terminal, needed when OLED is connected for debugging. -void terminalPrintf(const char * format, ...) +// Printf to debug console terminal. +void dbgprintf(const char * format, ...) { - va_list ap; + // Locals. + va_list ap; + + // Commence variable argument processing. va_start(ap, format); + // Use vararg printf to expand and return the buffer size needed. int size = vsnprintf(nullptr, 0, format, ap) + 1; if (size > 0) { va_end(ap); + + // Repeat and this time output the expanded string to a buffer for printing. va_start(ap, format); char buf[size + 1]; vsnprintf(buf, size, format, ap); - // u8g2.print(buf); - // u8g2.sendBuffer(); + + // Output to LED or console + // tbc } va_end(ap); } @@ -118,45 +174,72 @@ void terminalPrintf(const char * format, ...) // can be made except for basic I/O ports. The spinlock has to be released for non // I/O work. // +// The MZ timing period is ~600ns RTSN Low to High (Latch Row data coming from MZ machine), +// MPX (connected direct to external Quad 2-1 Mux goes high as RTSN goes low, the MPX +// signal is held high for 300ns then goes low for 300ns. The period when RTSN is low is +// when the MZ machine reads the scan row data. The cycle is ~1.2uS repeating, 14 rows +// so ~16.8uS for one full key matrix, The MZ machine if stuck in a tight loop will take +// approx 100uS to scan the matrix so the Gate Arrays are over sampling 6 times. +// IRAM_ATTR void mz25Interface( void * pvParameters ) { // Locals. - volatile unsigned long idx; + volatile uint32_t gpioIN; + volatile uint8_t strobeRow = 0; + uint32_t rowBitMask = (1 << CONFIG_MZ_KDB3) | (1 << CONFIG_MZ_KDB2) | (1 << CONFIG_MZ_KDB1) | (1 << CONFIG_MZ_KDB0); + uint32_t colBitMask = (1 << CONFIG_MZ_KDO7) | (1 << CONFIG_MZ_KDO6) | (1 << CONFIG_MZ_KDO5) | (1 << CONFIG_MZ_KDO4) | + (1 << CONFIG_MZ_KDO3) | (1 << CONFIG_MZ_KDO2) | (1 << CONFIG_MZ_KDO1) | (1 << CONFIG_MZ_KDO0); + uint32_t pwrLEDMask = (1 << CONFIG_PWRLED); + + ESP_LOGI(tag, "Starting mz25Interface thread, colBitMask=%08x, rowBitMask=%08x.", colBitMask, rowBitMask); // Create, initialise and hold a spinlock so the current core is bound to this one method. portENTER_CRITICAL(&mzMutex); // Permanent loop, just wait for an RTSN strobe, latch the row, lookup matrix and output. + // Timings with Power LED = LED Off to On = 108ns, LED On to Off = 392ns for(;;) { - gpio_set_level((gpio_num_t)CONFIG_PWRLED, 1); - // digitalWrite(CONFIG_PWRLED, HIGH); - for(idx=0; idx < 10000000; idx++) + // Turn on Power LED. + GPIO.out_w1ts = pwrLEDMask; + + // Read the GPIO ports to get latest RTSNi and KDI4 states. + gpioIN = REG_READ(GPIO_IN_REG); + + // Detect RTSN going high, the MZ will send the required row during this cycle. + if(gpioIN & CONFIG_MZ_RTSNI) { - if(idx % 1000 == 0) + // Assemble the required matrix row from the configured bits. + strobeRow = (gpioIN >> (CONFIG_MZ_KDB3-3)) | (gpioIN >> (CONFIG_MZ_KDB2-2)) | (gpioIN >> (CONFIG_MZ_KDB1-1)) | (gpioIN >> CONFIG_MZ_KDB0); + + // Clear all KDO bits - clear state = '1' + GPIO.out_w1ts = colBitMask; // Reset all scan data bits to '1', inactive. + + // KDI4 indicates if row data is needed or a single byte ANDing all the keys together, ie. to detect a key press without strobing all rows. + if(gpioIN & CONFIG_MZ_KDI4) { - TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable - TIMERG0.wdt_feed=1; // feed dog - TIMERG0.wdt_wprotect=0; // write protect - TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable - TIMERG1.wdt_feed=1; // feed dog - TIMERG1.wdt_wprotect=0; // write protect - } - } - // digitalWrite(CONFIG_PWRLED, LOW); - gpio_set_level((gpio_num_t)CONFIG_PWRLED, 0); - for(idx=0; idx < 10000000; idx++) - { - if(idx % 1000 == 0) + // Set all required KDO bits according to keyMatrix, set state = '0'. + GPIO.out_w1tc = mzControl.keyMatrixAsGPIO[strobeRow]; // Set to '0' active bits. + } else { - TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable - TIMERG0.wdt_feed=1; // feed dog - TIMERG0.wdt_wprotect=0; // write protect - TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable - TIMERG1.wdt_feed=1; // feed dog - TIMERG1.wdt_wprotect=0; // write protect + // Set all required KDO bits according to the strobe all value. set state = '0'. + GPIO.out_w1tc = mzControl.strobeAllAsGPIO; // Set to '0' active bits. } + + // Wait for RTSN to go low. + while(REG_READ(GPIO_IN_REG) & CONFIG_MZ_RTSNI); } + + // Turn off Power LED. + GPIO.out_w1tc = pwrLEDMask; + + // Logic to feed the watchdog if needed. Watchdog disabled in menuconfig but if enabled this will need to be used. + //TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG0.wdt_feed=1; // feed dog + //TIMERG0.wdt_wprotect=0; // write protect + //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG1.wdt_feed=1; // feed dog + //TIMERG1.wdt_wprotect=0; // write protect } } @@ -167,6 +250,7 @@ IRAM_ATTR unsigned char updateMatrix(uint16_t data) // Locals. uint8_t idx; uint8_t idx2; + uint8_t changed = 0; // Loop through the entire conversion table to find a match on this key, if found appy the conversion to the virtual // switch matrix. @@ -191,14 +275,17 @@ IRAM_ATTR unsigned char updateMatrix(uint16_t data) if(PS2toMZ[idx][PSMZTBL_MXROW1] != 0xFF) { mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW1]] |= PS2toMZ[idx][PSMZTBL_MXKEY1]; + changed = 1; } if(PS2toMZ[idx][PSMZTBL_MXROW2] != 0xFF) { mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW2]] |= PS2toMZ[idx][PSMZTBL_MXKEY2]; + changed = 1; } if(PS2toMZ[idx][PSMZTBL_MXROW3] != 0xFF) { mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW3]] |= PS2toMZ[idx][PSMZTBL_MXKEY3]; + changed = 1; } } else { @@ -206,27 +293,60 @@ IRAM_ATTR unsigned char updateMatrix(uint16_t data) if(PS2toMZ[idx][PSMZTBL_MXROW1] != 0xFF) { mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW1]] &= ~PS2toMZ[idx][PSMZTBL_MXKEY1]; + changed = 1; } if(PS2toMZ[idx][PSMZTBL_MXROW2] != 0xFF) { mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW2]] &= ~PS2toMZ[idx][PSMZTBL_MXKEY2]; + changed = 1; } if(PS2toMZ[idx][PSMZTBL_MXROW3] != 0xFF) { mzControl.keyMatrix[PS2toMZ[idx][PSMZTBL_MXROW3]] &= ~PS2toMZ[idx][PSMZTBL_MXKEY3]; + changed = 1; } } } - - // Re-calculate the Strobe All (KD4 = 1) signal, this indicates if any bit (key) in the matrix is active. - mzControl.strobeAll = 0xFF; - for(idx2=0; idx2 < 15; idx2++) + + // Only spend timw updating signals if an actual change occurred. Some keys arent valid so no change will be effected. + if(changed) { - mzControl.strobeAll &= mzControl.keyMatrix[idx2]; + // To save time in the MZ Interface, a mirror keyMatrix is built up, 32bit (GPIO Bank 0) wide, with the keyMatrix 8 bit data + // mapped onto the configured pins in the 32bit register. This saves previous time in order to meet the tight 1.2uS cycle. + // + for(int idx=0; idx < 15; idx++) + { + mzControl.keyMatrixAsGPIO[idx] = (((mzControl.keyMatrix[idx] >> 7) & 0x01) ^ 0x01) << CONFIG_MZ_KDO7 | + (((mzControl.keyMatrix[idx] >> 6) & 0x01) ^ 0x01) << CONFIG_MZ_KDO6 | + (((mzControl.keyMatrix[idx] >> 5) & 0x01) ^ 0x01) << CONFIG_MZ_KDO5 | + (((mzControl.keyMatrix[idx] >> 4) & 0x01) ^ 0x01) << CONFIG_MZ_KDO4 | + (((mzControl.keyMatrix[idx] >> 3) & 0x01) ^ 0x01) << CONFIG_MZ_KDO3 | + (((mzControl.keyMatrix[idx] >> 2) & 0x01) ^ 0x01) << CONFIG_MZ_KDO2 | + (((mzControl.keyMatrix[idx] >> 1) & 0x01) ^ 0x01) << CONFIG_MZ_KDO1 | + (((mzControl.keyMatrix[idx] ) & 0x01) ^ 0x01) << CONFIG_MZ_KDO0 ; + } + + // Re-calculate the Strobe All (KD4 = 1) signal, this indicates if any bit (key) in the matrix is active. + mzControl.strobeAll = 0xFF; + mzControl.strobeAllAsGPIO = 0x00000000; + for(idx2=0; idx2 < 15; idx2++) + { + mzControl.strobeAll &= mzControl.keyMatrix[idx2]; + } + + // To speed up the mzInterface logic, pre-calculate the strobeAll value as a 32bit GPIO output value. + mzControl.strobeAllAsGPIO |= (((mzControl.strobeAll >> 7) & 0x01) ^ 0x01) << CONFIG_MZ_KDO7 | + (((mzControl.strobeAll >> 6) & 0x01) ^ 0x01) << CONFIG_MZ_KDO6 | + (((mzControl.strobeAll >> 5) & 0x01) ^ 0x01) << CONFIG_MZ_KDO5 | + (((mzControl.strobeAll >> 4) & 0x01) ^ 0x01) << CONFIG_MZ_KDO4 | + (((mzControl.strobeAll >> 3) & 0x01) ^ 0x01) << CONFIG_MZ_KDO3 | + (((mzControl.strobeAll >> 2) & 0x01) ^ 0x01) << CONFIG_MZ_KDO2 | + (((mzControl.strobeAll >> 1) & 0x01) ^ 0x01) << CONFIG_MZ_KDO1 | + (((mzControl.strobeAll ) & 0x01) ^ 0x01) << CONFIG_MZ_KDO0 ; } } } - return data; + return changed; } // Primary PS/2 thread, running on Core 1. @@ -234,7 +354,6 @@ IRAM_ATTR unsigned char updateMatrix(uint16_t data) // The PS/2 data is received via interrupt. // IRAM_ATTR void ps2Interface( void * pvParameters ) -//IRAM_ATTR void ps2Interface( ) { // Locals. uint16_t scanCode = 0x0000; @@ -248,9 +367,7 @@ IRAM_ATTR void ps2Interface( void * pvParameters ) #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) if((clrTimer > 0 && --clrTimer == 0) || ((scanCode&0xFF) == PS2_KEY_C && scanCode & PS2_BREAK )) { - // Clear old scan code data. - // u8g2.drawStr(0, 8*7, " "); - // u8g2.sendBuffer(); + // Clear old scan code data. Add OLED code if needed. scanPrtCol = 0; } #endif @@ -265,42 +382,58 @@ IRAM_ATTR void ps2Interface( void * pvParameters ) // Clear screen as requested. if(clrScreen == 1) { - ssd1306_clear_screen(&SSD1306, false); + // ssd1306_clear_screen(&SSD1306, false); clrScreen = 0; } // Output the scan code for verification. - // u8g2.setCursor((scanPrtCol*5)*6, 8*7); - terminalPrintf("%04x,", scanCode); + dbgprintf("%04x,", scanCode); if(scanPrtCol++ >= 3) scanPrtCol = 0; - // u8g2.sendBuffer(); clrTimer = 2000000; - dataChange = 1; #endif // Update the virtual matrix with the new key value. - updateMatrix(scanCode); - } + dataChange = updateMatrix(scanCode); - #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) - // Output the MZ virtual keyboard matrix for verification. - uint8_t oledBuf[8][16]; if(dataChange) { + // To save time in the MZ Interface, a mirror keyMatrix is built up, 32bit (GPIO Bank 0) wide, with the keyMatrix 8 bit data + // mapped onto the configured pins in the 32bit register. This saves previous time in order to meet the tight 1.2uS cycle. + // for(int idx=0; idx < 15; idx++) { - for(int idx2=0; idx2 < 8; idx2++) - { - oledBuf[idx2][idx] = ((mzControl.keyMatrix[idx] >> idx2)&0x01) == 1 ? '1' : '0'; - } + mzControl.keyMatrixAsGPIO[idx] = (((mzControl.keyMatrix[idx] >> 7) & 0x01) ^ 0x01) << CONFIG_MZ_KDO7 | + (((mzControl.keyMatrix[idx] >> 6) & 0x01) ^ 0x01) << CONFIG_MZ_KDO6 | + (((mzControl.keyMatrix[idx] >> 5) & 0x01) ^ 0x01) << CONFIG_MZ_KDO5 | + (((mzControl.keyMatrix[idx] >> 4) & 0x01) ^ 0x01) << CONFIG_MZ_KDO4 | + (((mzControl.keyMatrix[idx] >> 3) & 0x01) ^ 0x01) << CONFIG_MZ_KDO3 | + (((mzControl.keyMatrix[idx] >> 2) & 0x01) ^ 0x01) << CONFIG_MZ_KDO2 | + (((mzControl.keyMatrix[idx] >> 1) & 0x01) ^ 0x01) << CONFIG_MZ_KDO1 | + (((mzControl.keyMatrix[idx] ) & 0x01) ^ 0x01) << CONFIG_MZ_KDO0 ; } - for(int idx=0; idx < 8; idx++) - { - ssd1306_display_text(&SSD1306, idx, (char *)oledBuf[idx], 15, false); - } + #if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED) + // Output the MZ virtual keyboard matrix for verification. + uint8_t oledBuf[8][16]; + for(int idx=0; idx < 15; idx++) + { + for(int idx2=0; idx2 < 8; idx2++) + { + oledBuf[idx2][idx] = ((mzControl.keyMatrix[idx] >> idx2)&0x01) == 1 ? '1' : '0'; + } + } + + // Print out the matrix, transposed - see MZKeyTable.h for the map, second table. + for(int idx=0; idx < 8; idx++) + { + ssd1306_display_text(&SSD1306, idx, (char *)oledBuf[idx], 15, false); + } + #endif } - #endif + } + + // Let other tasks run. + vTaskDelay(0); } } @@ -319,6 +452,16 @@ void setup() { // Locals. gpio_config_t io_conf; + + // Setup power LED first to show life. + ESP_LOGI(tag, "Configuring Power LED."); + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = (1ULL<