Skip to content

Reading from a pulled-up port after writing to it fails #204

Open
@gergoerdi

Description

@gergoerdi

I have the following code, reduced as far as I could:

#include <avr/io.h>
#include <avr/cpufunc.h>
#include <stdbool.h>

#define nanowait() _NOP()

int main ()
{
    /* Input trigger: PD0 */
    DDRD |= _BV(DDD0);
    PORTD |= _BV(PD0);

    /* Input trigger: PC0 */
    DDRC |= _BV(DDC0);
    PORTC |= _BV(PC0);

    /* Set up input pin PC5 with pull-up resistor */
    DDRC &= ~_BV(DDC5);
    PORTC |= _BV(PC5);

    /* Debug output: PB0 */
    DDRB |= _BV(DDB0);
    PORTB &= ~_BV(PB0);

    for (;;)
    {
        /* Read input */
        uint8_t x = 0;

        PORTD &= ~_BV(PD0);
        PORTC &= ~_BV(PC0);
        nanowait();
        x = PINC;
        PORTD |= _BV(PD0);
        PORTC |= _BV(PC0);

        x = (~x & _BV(PC5)) ? 1 : 0;

        /* Write out result to debug port */
        if (x)
            PORTB |= _BV(PB0);
        else
            PORTB &= ~(_BV(PB0));
    }
}

What it tries to do, is to pulse low PD0 and PC0 before reading in some input on PC5. Originally, this came up in the context of a 4x4 keypad where there are four row selectors and four column readers, but the problem can be shown even on this smaller example.

In SimAVR, I set PC5 in response to PD0 going low:

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>

#include "sim_avr.h"
#include "avr_ioport.h"
#include "sim_elf.h"

void debug_cb(struct avr_irq_t* irq, uint32_t value, void* closure)
{
    printf("PIN%s%d = %d\n", (char*)closure, irq->irq, value);
}

int count = 0;

void selector_cb(struct avr_irq_t* irq, uint32_t value, void* closure)
{
    avr_irq_t* output = (avr_irq_t*)(closure);

    if (value) return;

    if (++count == 7)
    {
        avr_raise_irq(output, false);
        count = 0;
    } else {
        avr_raise_irq(output, true);
    }
}

int main(int argc, char *argv[])
{
    elf_firmware_t f;
    elf_read_firmware("image.elf", &f);
    f.frequency = 16e6;

    const char *mmcu = "atmega328p";
    avr_t * avr = avr_make_mcu_by_name(mmcu);
    if (!avr)
        return 1;
    avr_init(avr);
    avr_load_firmware(avr, &f);

    const char* keypadName = "keypad";
    avr_irq_t *keypad = avr_alloc_irq(&(avr->irq_pool), 0, 1, &keypadName);

    /* Input selector PD0 */
    avr_irq_register_notify(
        avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('D'), 0),
        selector_cb, keypad);

    /* Input signal PC5 */
    avr_connect_irq(keypad, avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('C'), 5));

    /* Debug output PB0 */
    avr_irq_register_notify(
        avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 0),
        debug_cb, "B");

    for (;;)
    {
        avr_run(avr);
    }
}

If I run this, I see that the debug output on PB0 never goes high.

However, if I comment out the line PORTC &= ~_BV(PC0); i.e. I don't write to PORTC before reading PINC, then it works as expected and I see PB0 changing between 0 and 1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions