ESP32 external interrupt latency
Posted: Mon Aug 21, 2023 10:31 pm
Lately, I've been working on a project that consists of programming a Z80 with 8 address and data lines, the clock is done with ledc, it has two external interrupts on the Z80's WR and RD pins --> ESP32. The code is functional, but I can't work with frequencies (Z80 clock) higher than 10000Hz because the ISR somehow can't keep up with the pace of the Z80, my z80 can operate up to 20MHz. I'm sure the problem is not related to the ledc, as it can generate signals up to 40MHz. I would like a suggestion of change(s) in my code, I appreciate it
Code: Select all
#include <Arduino.h>
#include <esp_task_wdt.h>
#include "driver/ledc.h"
byte pinDados[8] = {13, 12, 14, 27, 26, 25, 33, 32};
byte pinEndereco[8] = {35, 34, 39, 36, 15, 2, 4, 16};
byte pinIORQ = 22;
byte pinRST = 21;
byte pinWR = 19;
byte pinRD = 18;
byte pinMREQ = 5;
byte pinZ80 = 23;
byte memory[0x100];
byte program[] = {
0x21, 0x0a, 0x00, // ld hl, ??
0x0e, 0x00, // ld c, 0
0x06, 0x26, // ld b, 38
0xed, 0xb3, // otir
0x76, // halt
'\x1B', '[', '3', '8', ';', '2', ';', '0', '7', '0', ';', '2', '0', '0', ';', '1', '8', '0', 'm',
'O', 'i', ' ', 'Z', '8', '0', '!', '\n',
'T', 'c', 'h', 'a', 'u', '!', '\n',
'\x1B', '[', '0', 'm'};
const int timerPin = 17;
String portaZ80_buf;
#define RESETAR(estado) estado ? digitalWrite(pinRST, 0) : digitalWrite(pinRST, 1);
bool running = false;
void IRAM_ATTR intWriteCPU();
void IRAM_ATTR intReadCPU();
void IRAM_ATTR interrupcoes();
uint16_t readAddress();
void writeData(byte data);
byte readData();
void setDataDir(int dir)
{
for (int i = 0; i < sizeof(pinDados); i++)
{
pinMode(pinDados[i], dir);
}
}
uint16_t readAddress()
{
uint16_t address = 0;
for (int i = 0; i < sizeof(pinEndereco); i++)
{
address |= (digitalRead(pinEndereco[i]) & 1) << i;
}
return address;
}
void writeData(byte data)
{
setDataDir(OUTPUT);
for (int i = 0; i < sizeof(pinDados); i++)
{
digitalWrite(pinDados[i], (data >> i) & 1);
}
}
byte readData()
{
byte data = 0;
setDataDir(INPUT);
for (int i = 0; i < sizeof(pinDados); i++)
{
data |= (digitalRead(pinDados[i]) & 1) << i;
}
return data;
}
uint32_t oscilador = 10000; // Frequencia inicial do oscilador - 12543 Hz
uint32_t mDuty = 0; // Valor calculado do ciclo de trabalho
uint32_t resolucao = 0; // Valor calculado da resolucao
void setup()
{
Serial.begin(115200);
while (!Serial)
;
memset(memory, 0, sizeof(memory));
memcpy(memory, program, sizeof(program));
for (int i = 0; i < sizeof(pinEndereco); i++)
{
pinMode(pinEndereco[i], INPUT);
}
writeData(0x00);
pinMode(timerPin, OUTPUT);
pinMode(pinRST, OUTPUT);
pinMode(pinZ80, OUTPUT);
pinMode(pinWR, INPUT_PULLUP);
pinMode(pinRD, INPUT_PULLUP);
pinMode(pinMREQ, INPUT_PULLUP);
pinMode(pinIORQ, INPUT_PULLUP);
digitalWrite(pinZ80, LOW);
/*esp_timer_create_args_t timerConfig = {
.callback = onTimer,
.arg = NULL,
.name = "clock_timer"};
esp_timer_handle_t timer;
esp_timer_create(&timerConfig, &timer);
esp_timer_start_periodic(timer, 90);*/
resolucao = (log(80000000 / oscilador) / log(2)) / 2; // Calculo da resolucao para o oscilador
if (resolucao < 1)
resolucao = 1; // Resoluçao mínima
// Serial.println(resolucao); // Print
mDuty = (pow(2, resolucao)) / 2; // Calculo do ciclo de trabalho 50% do pulso
// Serial.println(mDuty); // Print
ledc_timer_config_t ledc_timer = {}; // Instancia a configuracao do timer do LEDC
ledc_timer.duty_resolution = ledc_timer_bit_t(resolucao); // Configura resolucao
ledc_timer.freq_hz = oscilador; // Configura a frequencia do oscilador
ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; // Modo de operacao em alta velocidade
ledc_timer.timer_num = LEDC_TIMER_0; // Usar timer0 do LEDC
ledc_timer_config(&ledc_timer); // Configura o timer do LEDC
ledc_channel_config_t ledc_channel = {}; // Instancia a configuracao canal do LEDC
ledc_channel.channel = LEDC_CHANNEL_0; // Configura canal 0
ledc_channel.duty = mDuty; // Configura o ciclo de trabalho
ledc_channel.gpio_num = timerPin; // Configura GPIO da saida do LEDC - oscilador
ledc_channel.intr_type = LEDC_INTR_DISABLE; // Desabilita interrupção do LEDC
ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; // Modo de operacao do canal em alta velocidade
ledc_channel.timer_sel = LEDC_TIMER_0; // Seleciona timer 0 do LEDC
ledc_channel_config(&ledc_channel); // Configura o canal do LEDC
interrupcoes();
printf("\x1B[32mBem-vindo ao Subsistema Z80!\x1B[0m\n");
printf("Pressione a tecla Enter para ligar o Z80...\n");
}
void loop()
{
if (Serial.available())
{
char keyPressed = Serial.read();
switch (keyPressed)
{
case 0x1B:
if (running)
{
running = false;
printf("Resetando Z80...\n");
RESETAR(1);
delay(1);
RESETAR(0);
running = true;
}
break;
case 0x0D:
if (!running)
{
running = true;
printf("Ligando Z80...\n");
digitalWrite(pinZ80, HIGH);
RESETAR(1);
delay(1);
RESETAR(0);
}
break;
case 0x70:
printf("Imprimindo dados da porta...\n");
printf(portaZ80_buf.c_str());
portaZ80_buf = "";
break;
default:
break;
}
}
}
void IRAM_ATTR interrupcoes()
{
attachInterrupt(pinWR, intWriteCPU, FALLING);
attachInterrupt(pinRD, intReadCPU, FALLING);
}
void IRAM_ATTR intWriteCPU()
{
int MREQ = !gpio_get_level((gpio_num_t)pinMREQ);
int IORQ = !gpio_get_level((gpio_num_t)pinIORQ);
if (MREQ && running)
{
memory[readAddress()] = readData();
}
else if (IORQ && running)
{
portaZ80_buf += (char)readData();
}
}
void IRAM_ATTR intReadCPU()
{
int MREQ = !gpio_get_level((gpio_num_t)pinMREQ);
// int IORQ = !gpio_get_level((gpio_num_t)pinIORQ);
if (MREQ && running)
{
// ets_printf("%d\n", memory[readAddress()]);
writeData(memory[readAddress()]);
}
}