avr_extint.c: prevent spurious interrupts.
avr_ioport.c: allow calling program to control value read from PIN.
void * param)
{
avr_extint_poll_context_t *poll = (avr_extint_poll_context_t *)param;
- avr_extint_t * p = (avr_extint_t *)poll->extint;
+ avr_extint_t * p = poll->extint;
- char port = p->eint[poll->eint_no].port_ioctl & 0xFF;
+ /* Check for change of interrupt mode. */
+
+ if (avr_regbit_get_array(avr, p->eint[poll->eint_no].isc, 2))
+ goto terminate_poll;
+
+ char port = p->eint[poll->eint_no].port_ioctl & 0xFF;
avr_ioport_state_t iostate;
if (avr_ioctl(avr, AVR_IOCTL_IOPORT_GETSTATE( port ), &iostate) < 0)
goto terminate_poll;
avr_extint_t * p = (avr_extint_t *)port;
for (int i = 0; i < EXTINT_COUNT; i++) {
- avr_irq_register_notify(p->io.irq + i, avr_extint_irq_notify, p);
-
if (p->eint[i].port_ioctl) {
+ avr_irq_register_notify(p->io.irq + i, avr_extint_irq_notify, p);
+
if (p->eint[i].isc[1].reg) // level triggering available
p->eint[i].strict_lvl_trig = 1; // turn on repetitive level triggering by default
avr_irq_t * irq = avr_io_getirq(p->io.avr,
p->io = _io;
avr_register_io(avr, &p->io);
- for (int i = 0; i < EXTINT_COUNT; i++)
+ for (int i = 0; i < EXTINT_COUNT; i++) {
+ if (!p->eint[i].port_ioctl)
+ break;
avr_register_vector(avr, &p->eint[i].vector);
-
+ }
// allocate this module's IRQ
+
avr_io_setirqs(&p->io, AVR_IOCTL_EXTINT_GETIRQ(), EXTINT_COUNT, NULL);
}
uint8_t ddr = avr->data[p->r_ddr];
uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
avr->data[addr] = v;
- // made to trigger potential watchpoints
- v = avr_core_watch_read(avr, addr);
avr_raise_irq(p->io.irq + IOPORT_IRQ_REG_PIN, v);
D(if (avr->data[addr] != v) printf("** PIN%c(%02x) = %02x\r\n", p->name, addr, v);)
+ // made to trigger potential watchpoints
+ v = avr_core_watch_read(avr, addr);
return v;
}
[IOPORT_IRQ_PIN5] = "=pin5",
[IOPORT_IRQ_PIN6] = "=pin6",
[IOPORT_IRQ_PIN7] = "=pin7",
- [IOPORT_IRQ_PIN_ALL] = "8=all",
+ [IOPORT_IRQ_PIN_ALL] = "8>all",
[IOPORT_IRQ_DIRECTION_ALL] = "8>ddr",
[IOPORT_IRQ_REG_PORT] = "8>port",
[IOPORT_IRQ_REG_PIN] = "8>pin",
--- /dev/null
+#ifndef F_CPU
+#define F_CPU 8000000
+#endif
+#include <avr/io.h>
+#include <stdio.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+/*
+ * This demonstrate how to use the avr_mcu_section.h file
+ * The macro adds a section to the ELF file with useful
+ * information for the simulator
+ */
+#include "avr_mcu_section.h"
+AVR_MCU(F_CPU, "atmega168");
+
+static int uart_putchar(char c, FILE *stream) {
+ if (c == '\n')
+ uart_putchar('\r', stream);
+ loop_until_bit_is_set(UCSR0A, UDRE0);
+ UDR0 = c;
+ return 0;
+}
+
+static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
+ _FDEV_SETUP_WRITE);
+
+ISR(INT0_vect)
+{
+ printf("I<%02X ", PIND);
+}
+
+ISR(PCINT2_vect)
+{
+ printf("J<%02X ", PORTD);
+ PORTD = 0;
+}
+
+int main()
+{
+ stdout = &mystdout;
+
+ /* Enable output on Port D pins 0-3 and write to them. */
+
+ DDRD = 0xf;
+ PORTD = 0xa;
+
+ printf("P<%02X ", PIND); // Should say P<2A as caller sets bit 5.
+
+ /* Toggle some outputs. */
+
+ PIND = 3;
+
+ /* Change directions. */
+
+ DDRD = 0x3c;
+
+ /* Change output. */
+
+ PORTD = 0xf0;
+
+ /* This should say P<70 - pullups and direct output give 0xF0
+ * but the caller sees that and turns off bit 7 input,
+ * overriding that pullup.
+ */
+
+ printf("P<%02X ", PIND);
+
+ /* Set-up rising edge interrupt on pin 2 (INT 0). */
+
+ EICRA = 3;
+ EIMSK = 1;
+
+ /* Turn off pin 4, signal the caller to raise pin 2. */
+
+ PORTD = 0xe0;
+
+ /* Verify the interrupt flag is set. */
+
+ printf("F<%02X ", EIFR);
+
+ sei();
+
+ /* This duplicates the value in the INT0 handler, but it
+ * takes sufficient time to be sure that there is only one
+ * interrupt. There was a bug that caused continuous interrupts
+ * when this was first tried.
+ */
+
+ printf("P<%02X ", PIND);
+
+ /* TODO: Test the level-triggered interupt. It can be started
+ * by a pin-value change or by writing to either of EICRA and EIMSK.
+ */
+
+ /* TODO: Set-up pin change interrupt on pin 7 (PCINT23) */
+
+ PCICR = (1 << PCIE2); /* Interrupt enable. */
+ PCMSK2 = 0x0a; /* Pins 1 and 3. */
+ DDRD = 3;
+ PORTD = 1; /* No interrupt. */
+ PORTD = 3; /* Interrupt. */
+
+ /* Allow time for second interrupt. */
+
+ printf("P<%02X ", PIND);
+
+ // this quits the simulator, since interupts are off
+ // this is a "feature" that allows running tests cases and exit
+ cli();
+ sleep_cpu();
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "tests.h"
+#include "avr_ioport.h"
+
+/* Start of the IOPORT's IRQ list. */
+
+static avr_irq_t *base_irq;
+
+/* Accumulate log of events for comparison at the end. */
+
+static char log[256];
+static char *fill = log;
+
+#define LOG(...) \
+ (fill += snprintf(fill, (log + sizeof log) - fill, __VA_ARGS__))
+
+/* IRQ call-back function for changes in pin levels. */
+
+static void monitor_5(struct avr_irq_t *irq, uint32_t value, void *param)
+{
+ LOG("5-%02X ", value);
+}
+
+/* This monitors the simulator's idea of the I/O pin states.
+ * Changes this program makes to inputs are not reported,
+ * presumably because the simulator "knows" we made them.
+ */
+
+static void monitor(struct avr_irq_t *irq, uint32_t value, void *param)
+{
+ LOG("P-%02X ", value);
+
+ if (value == 9) {
+ /* Assume this is because bit 0 was left high when its
+ * direction switched to input. Make it low.
+ */
+
+ avr_raise_irq(base_irq + IOPORT_IRQ_PIN0, 0);
+ } if (value == 0xf0) {
+ /* Assume this is a combination of 0x30 (direct) and 0xc0 (pullups).
+ * So change inputs.
+ */
+
+ avr_raise_irq(base_irq + IOPORT_IRQ_PIN4, 0); // Ignored.
+ avr_raise_irq(base_irq + IOPORT_IRQ_PIN7, 0);
+ }
+
+}
+
+/* Writes to output ports and DDR are reported here. */
+
+static void reg_write(struct avr_irq_t *irq, uint32_t value, void *param)
+{
+ static int zero_count;
+ char c;
+
+ if (irq->irq == IOPORT_IRQ_REG_PORT)
+ c = 'o';
+ else if (irq->irq == IOPORT_IRQ_DIRECTION_ALL)
+ c = 'd';
+ else
+ c = '?';
+ LOG("%c-%02X ", c, value);
+
+ if (irq->irq == IOPORT_IRQ_REG_PORT) {
+ if (value == 0xe0) {
+ /* Program request to raise bit 2: external interrupt. */
+
+ avr_raise_irq(base_irq + IOPORT_IRQ_PIN2, 1);
+ } else if (value == 0) {
+ if (zero_count++ == 0) {
+ /* Raise bit 3: pin change interrupt. */
+
+ avr_raise_irq(base_irq + IOPORT_IRQ_PIN3, 1);
+ }
+ }
+ }
+}
+
+/* Called when the AVR reads the input port. */
+
+static void reg_read(struct avr_irq_t *irq, uint32_t value, void *param)
+{
+ LOG("I-%02X ", value);
+
+ /* Change the value read. */
+
+ avr_raise_irq(base_irq + IOPORT_IRQ_PIN5, 1);
+}
+
+/* This string should be sent by the firmware. */
+
+static const char *expected = "P<2A P<70 F<01 I<E0 P<E0 J<03 J<00 P<E8 ";
+
+/* This string is expected in variable log. */
+
+static const char *log_expected =
+ "d-0F P-00 o-0A P-0A I-0A 5-01 o-09 P-29 d-3C 5-00 P-09 o-F0 5-01 P-F0 "
+ "I-70 " // Interrupts off testing.
+ "o-E0 P-E0 I-E0 " // External interrupt test.
+ "d-03 o-01 P-E1 o-03 P-E3 o-00 P-E8 I-E8 "; // Pin change interrupt test.
+
+
+int main(int argc, char **argv) {
+ avr_t *avr;
+
+ tests_init(argc, argv);
+ avr = tests_init_avr("atmega168_ioport.axf");
+ base_irq = avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('D'), 0);
+
+ avr_irq_register_notify(base_irq + IOPORT_IRQ_PIN5,
+ monitor_5, NULL);
+ avr_irq_register_notify(base_irq + IOPORT_IRQ_PIN_ALL,
+ monitor, NULL);
+ avr_irq_register_notify(base_irq + IOPORT_IRQ_DIRECTION_ALL,
+ reg_write, NULL);
+ avr_irq_register_notify(base_irq + IOPORT_IRQ_REG_PORT,
+ reg_write, NULL);
+ avr_irq_register_notify(base_irq + IOPORT_IRQ_REG_PIN,
+ reg_read, NULL);
+
+ tests_assert_uart_receive_avr(avr, 100000, expected, '0');
+
+ if (strcmp(log, log_expected))
+ fail("Internal log: %s.\nExpected: %s.\n", log, log_expected);
+ tests_success();
+ return 0;
+}