buserror/simavr

Reading output pins set by digitalWrite() / Writing inputs pins to be read by digitalRead()

xShadowEagle opened this issue · 1 comments

I am simulating programs for a CONTROLLINO (ATmega2560) written in the Arduino IDE for educational purposes. I can guarantee that input pins are not being written to. Although pins like A0 can be used for analog input, I'm only working with digital reads and writes. The programs for the microcontroller look like this:

#include <Controllino.h>

void setup() {
    pinMode(CONTROLLINO_A0, INPUT); // PORTF[0]
    // ...
    pinMode(CONTROLLINO_D0, OUTPUT); // PORTE[4]
    pinMode(CONTROLLINO_D1, OUTPUT); // PORTE[5]
    // ...
}

void loop() {
    digitalWrite(CONTROLLINO_D0, HIGH); // I need to read this output inside the simulator
    
    if (digitalRead(CONTROLLINO_A0) == HIGH) { // I need to write this input inside the simulator
        digitalWrite(CONTROLLINO_D1, HIGH); // I need to read this output inside the simulator
    }
    // ...
}

The firmware is exported from the Arduino IDE in HEX-format (I am limited to HEX-files). Then I am simply copying the image into the simulated flash. I tried to follow the examples provided in the repository. My main code for the simulator looks like this:

const char *mmcu = "atmega2560";
const unsigned int frequency = 16000000;

avr_t *avr = avr_make_mcu_by_name(mmcu);
avr_init(avr);

const char *hex_path = "controllino_mega.hex";
unsigned int flash_size, flash_start;
unsigned char *flash = read_ihex_file(hex_path, &flash_size, &flash_start);

avr->frequency = frequency;
memcpy(avr->flash + flash_start, flash, flash_size); // Is this correct?
free(flash);
avr->pc = flash_start;
avr->codeend = avr->flashend;

for (unsigned int i = 0; i < 8; ++i)
{
    avr_irq_register_notify(avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('E'), i), pin_changed_hook_e, NULL);
    avr_irq_register_notify(avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('G'), i), pin_changed_hook_g, NULL);
    avr_irq_register_notify(avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('H'), i), pin_changed_hook_h, NULL);
}

int state = cpu_Running;

while ((state != cpu_Done) && (state != cpu_Crashed))
{
    state = avr_run(avr);
}
int8_t port_e = 0;

void pin_changed_hook_e(struct avr_irq_t *irq, uint32_t value, void *param)
{
    port_e = (port_e & ~(1 << irq->irq)) | (value << irq->irq);
}

I think the reading of the output pins works fine, but I'm not sure whether my implementation is really the right way to go. Now I'm not sure how to set the input pins, so that they could be read by digitalRead(). I found this line in another issue but I'm not sure whether this does what I want:

char value = 1;
avr_raise_irq(avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('E'), i), value);

I believe it sets the bit of PORTE at index i to the value passed as the last parameter.

I had some other issues in my program, the functions to set and read pin states work as intended.