Files
mz25key/main/main.cpp
2022-01-30 18:44:39 +00:00

588 lines
26 KiB
C++

/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: main.cpp
// Created: Jan 2022
// Version: v1.0
// Author(s): Philip Smart
// Description: MZ2500/2800 Key Matrix logic.
// 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 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 <philip.smart@net2net.org>
//
// History: Jan 2022 - Initial write.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file 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.
//
// This source file 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, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "Arduino.h"
#include "driver/gpio.h"
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
#include "PS2KeyAdvanced.h"
#include "MZKeyTable.h"
#include "ssd1306.h"
#include "font8x8_basic.h"
#include "sdkconfig.h"
// ESP Logging tag.
#define tag "mz25key"
//////////////////////////////////////////////////////////////////////////
// Important:
//
// All configuration is performed via the 'idf.py menuconfig' command.
// Optionally override via the definitions below.
//////////////////////////////////////////////////////////////////////////
//#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.
//
#define NUMELEM(a) (sizeof(a)/sizeof(a[0]))
// 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;
uint32_t strobeAllAsGPIO;
uint8_t keyMatrix[16];
uint32_t keyMatrixAsGPIO[16];
} t_mzControl;
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;
// Debug required objects.
SSD1306_t SSD1306;
// Handle to interact with the mz-2500 interface thread.
TaskHandle_t TaskMZ25IF = NULL;
TaskHandle_t TaskPS2IF = NULL;
// Spin lock mutex to hold a core tied to an uninterruptable method. This only works on dual core ESP32's.
static portMUX_TYPE mzMutex = portMUX_INITIALIZER_UNLOCKED;
#if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED)
// Printf to debug console terminal.
void dbgprintf(const char * format, ...)
{
// 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);
// Output to LED or console
// tbc
}
va_end(ap);
}
#endif
// Method to connect and interact with the MZ-2500/MZ-2800 keyboard controller.
// The basic requirement is to:
// 1. Detect a falling edge on the RTSN signal
// 2. Read the provided ROW number.
// 3. Lookup the matrix data for given ROW.
// 4. Output data to LS257 Mux.
// 5. Loop
//
// The ps2Interface method is responsible for obtaining a PS/2 Keyboard scancode and
// creating the corresponding virtual matrix.
//
// NB: As this method holds Core 1 under spinlock, no FreeRTOS or Arduino access
// 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 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(;;)
{
// 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)
{
// 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)
{
// Set all required KDO bits according to keyMatrix, set state = '0'.
GPIO.out_w1tc = mzControl.keyMatrixAsGPIO[strobeRow]; // Set to '0' active bits.
} else
{
// 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
}
}
// Method to convert the PS2 scan code into a key matrix representation which the MZ-2500/2800 is expecting.
//
IRAM_ATTR unsigned char updateMatrix(uint16_t data)
{
// Locals.
uint8_t idx;
uint8_t idx2;
uint8_t changed = 0;
uint8_t matchExact = 0;
// Loop through the entire conversion table to find a match on this key, if found appy the conversion to the virtual
// switch matrix.
//
for(idx=0, changed=0, matchExact=0; idx < NUMELEM(PS2toMZ) && (changed == 0 || (changed == 1 && matchExact == 0)); idx++)
{
// Match key code?
if(PS2toMZ[idx][PSMZTBL_KEYPOS] == (uint8_t)(data&0xFF))
{
// Match Raw, Shift, Function, Control, ALT or ALT-Gr?
if( (PS2toMZ[idx][PSMZTBL_SHIFTPOS] == 0 && PS2toMZ[idx][PSMZTBL_FUNCPOS] == 0 && PS2toMZ[idx][PSMZTBL_CTRLPOS] == 0 && PS2toMZ[idx][PSMZTBL_ALTPOS] == 0 && PS2toMZ[idx][PSMZTBL_ALTGRPOS] == 0) ||
((data & PS2_SHIFT) && PS2toMZ[idx][PSMZTBL_SHIFTPOS] == 1) ||
((data & PS2_CTRL) && PS2toMZ[idx][PSMZTBL_CTRLPOS] == 1) ||
((data & PS2_ALT) && PS2toMZ[idx][PSMZTBL_ALTPOS] == 1) ||
((data & PS2_ALT_GR) && PS2toMZ[idx][PSMZTBL_ALTGRPOS] == 1) ||
((data & PS2_GUI) && PS2toMZ[idx][PSMZTBL_SHIFTPOS] == 1) ||
((data & PS2_FUNCTION) && PS2toMZ[idx][PSMZTBL_FUNCPOS] == 1) )
{
// Exact entry match, data + control key? On an exact match we only process the first key. On a data only match we fall through to include additional data and control key matches to allow for un-mapped key combinations, ie. Japanese characters.
matchExact = ((data & PS2_SHIFT) && PS2toMZ[idx][PSMZTBL_SHIFTPOS] == 1) ||
((data & PS2_CTRL) && PS2toMZ[idx][PSMZTBL_CTRLPOS] == 1) ||
((data & PS2_ALT_GR) && PS2toMZ[idx][PSMZTBL_ALTGRPOS] == 1) ||
((data & PS2_ALT) && PS2toMZ[idx][PSMZTBL_ALTPOS] == 1) ||
((data & PS2_GUI) && PS2toMZ[idx][PSMZTBL_ALTPOS] == 1) ||
((data & PS2_FUNCTION) && PS2toMZ[idx][PSMZTBL_FUNCPOS] == 1) ? 1 : 0;
// RELEASE (PS2_BREAK == 1) or PRESS?
if((data & PS2_BREAK))
{
// Reset the matrix bit according to the lookup table. 1 = No key, 0 = key in the matrix.
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
{
// Set the matrix bit according to the lookup table. 1 = No key, 0 = key in the matrix.
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;
}
}
}
// Only spend time updating signals if an actual change occurred. Some keys arent valid so no change will be effected.
if(changed)
{
// 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(changed);
}
// Primary PS/2 thread, running on Core 1.
// This thread is responsible for receiving PS/2 scan codes and mapping them to an MZ-2500/2800 keyboard matrix.
// The PS/2 data is received via interrupt.
//
IRAM_ATTR void ps2Interface( void * pvParameters )
{
// Locals.
uint16_t scanCode = 0x0000;
#if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED)
uint8_t dataChange = 0;
static int scanPrtCol = 0;
static uint32_t rfshTimer = 0;
#endif
while(1)
{
// Check for PS/2 keyboard scan codes.
while((scanCode = Keyboard.read()) != 0)
{
printf("%04x\n", scanCode);
#if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED)
// Output the scan code for verification.
dbgprintf("%04x,", scanCode);
if(scanPrtCol++ >= 3) scanPrtCol = 0;
#endif
// Update the virtual matrix with the new key value.
dataChange |= updateMatrix(scanCode);
}
#if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED)
if(dataChange || (rfshTimer > 0 && --rfshTimer == 0))
{
// 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);
}
// Clear timer for next refresh.
rfshTimer = 2000000;
dataChange = 0;
}
#endif
// Let other tasks run.
vTaskDelay(0);
}
}
// Setup method to configure ports, devices and threads prior to application run.
// Configuration:
// PS/2 Keyboard over 2 wire interface
// Power/Status LED
// Optional OLED debug output screen
// 4 bit input - MZ-2500/2800 Row Number
// 8 bit output - MZ-2500/2800 Scan data
// 1 bit input - RTSN strobe line, low indicating a new Row Number available.
// 1 bit input - KD4, High = Key scan data required, Low = AND of all key matrix rows required.
//
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<<CONFIG_PWRLED);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
gpio_set_level((gpio_num_t)CONFIG_PWRLED, 1);
// Start the keyboard, no mouse.
ESP_LOGI(tag, "Initialise PS2 keyboard.");
Keyboard.begin(CONFIG_PS2_HW_DATAPIN, CONFIG_PS2_HW_CLKPIN);
// If OLED connected and enabled, include the screen controller for debug output.
//
#if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED)
#if CONFIG_I2C_INTERFACE
ESP_LOGI(tag, "INTERFACE is i2c");
ESP_LOGI(tag, "CONFIG_SDA_GPIO=%d", CONFIG_SDA_GPIO);
ESP_LOGI(tag, "CONFIG_SCL_GPIO=%d", CONFIG_SCL_GPIO);
ESP_LOGI(tag, "CONFIG_RESET_GPIO=%d", CONFIG_RESET_GPIO);
i2c_master_init(&SSD1306, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);
#endif // CONFIG_I2C_INTERFACE
#if CONFIG_SPI_INTERFACE
ESP_LOGI(tag, "INTERFACE is SPI");
ESP_LOGI(tag, "CONFIG_MOSI_GPIO=%d", CONFIG_MOSI_GPIO);
ESP_LOGI(tag, "CONFIG_SCLK_GPIO=%d", CONFIG_SCLK_GPIO);
ESP_LOGI(tag, "CONFIG_CS_GPIO=%d", CONFIG_CS_GPIO);
ESP_LOGI(tag, "CONFIG_DC_GPIO=%d", CONFIG_DC_GPIO);
ESP_LOGI(tag, "CONFIG_RESET_GPIO=%d", CONFIG_RESET_GPIO);
spi_master_init(&SSD1306, CONFIG_MOSI_GPIO, CONFIG_SCLK_GPIO, CONFIG_CS_GPIO, CONFIG_DC_GPIO, CONFIG_RESET_GPIO);
#endif // CONFIG_SPI_INTERFACE
#if CONFIG_SSD1306_128x64
ESP_LOGI(tag, "Panel is 128x64");
ssd1306_init(&SSD1306, 128, 64);
#endif // CONFIG_SSD1306_128x64
#if CONFIG_SSD1306_128x32
ESP_LOGI(tag, "Panel is 128x32");
ssd1306_init(&SSD1306, 128, 32);
#endif // CONFIG_SSD1306_128x32
ssd1306_clear_screen(&SSD1306, false);
ssd1306_contrast(&SSD1306, 0xff);
#endif
// Configure 4 inputs to be the Strobe Row Number which is used to index the virtual key matrix and the strobe data returned.
#if !defined(CONFIG_MZ_DISABLE_KDB)
ESP_LOGI(tag, "Configuring MZ-2500/2800 4 bit Row Number Inputs.");
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDB0);
io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDB1);
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDB2);
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDB3);
gpio_config(&io_conf);
#endif
#if !defined(CONFIG_MZ_DISABLE_KDO)
ESP_LOGI(tag, "Configuring MZ-2500/2800 8 bit Strobe data Outputs.");
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDO0);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDO1);
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDO2);
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDO3);
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDO4);
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDO5);
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDO6);
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDO7);
gpio_config(&io_conf);
#endif
#if !defined(CONFIG_MZ_DISABLE_KDI)
ESP_LOGI(tag, "Configuring MZ-2500/2800 RTSN Input.");
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_RTSNI);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
#endif
#if !defined(CONFIG_MZ_DISABLE_RTSNI)
ESP_LOGI(tag, "Configuring MZ-2500/2800 KD4 Input.");
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL<<CONFIG_MZ_KDI4);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
#endif
// Check to see if keyboard available, no keyboard = no point, so halt!
// Firstly, ping keyboard to see if it is there.
ESP_LOGI(tag, "Detecting PS2 keyboard.");
Keyboard.echo();
vTaskDelay(6);
uint16_t chr = Keyboard.read();
if( (chr & 0xFF) != PS2_KEY_ECHO && (chr & 0xFF) != PS2_KEY_BAT)
{
ESP_LOGE(tag, "No PS2 keyboard detected, connect and reset to continue.\n");
#if defined(CONFIG_DEBUG_OLED) || !defined(CONFIG_OLED_DISABLED)
ssd1306_display_text(&SSD1306, 0, "No PS2 Keyboard", 15, false);
#endif
while(1);
}
// Create a task pinned to core 0 which will fulfill the MZ-2500/2800 interface. This task has the highest priority
// and it will also hold spinlock and manipulate the watchdog to ensure a scan cycle timing can be met. This means
// all other tasks running on Core 1 will suspend. The PS/2 controller, running on the ULP processor will continue
// to interact with the PS/2 keyboard and buffer scan codes.
//
// Core 1 - MZ Interface
ESP_LOGI(tag, "Starting mz25if thread...");
xTaskCreatePinnedToCore(mz25Interface, "mz25if", 32768, NULL, 25, &TaskMZ25IF, 1);
vTaskDelay(500);
// Core 0 - Application
ESP_LOGI(tag, "Starting ps2if thread...");
xTaskCreatePinnedToCore(ps2Interface, "ps2if", 32768, NULL, 22, &TaskPS2IF, 0);
vTaskDelay(500);
}
// ESP-IDF Application entry point.
//
extern "C" void app_main()
{
// Arduino runtime support isnt needed.
//initArduino();
// Setup hardware and start primary control threads,
setup();
}