#include <stdio.h>
#include "avr_twi.h"
-static uint8_t avr_twi_read(struct avr_t * avr, avr_io_addr_t addr, void * param)
+/*
+ * This block respectfully nicked straight out from the Atmel sample
+ * code for AVR315. Typos and all.
+ * There is no copyright notice on the original file.
+ */
+/****************************************************************************
+ TWI State codes
+****************************************************************************/
+// General TWI Master staus codes
+#define TWI_START 0x08 // START has been transmitted
+#define TWI_REP_START 0x10 // Repeated START has been transmitted
+#define TWI_ARB_LOST 0x38 // Arbitration lost
+
+// TWI Master Transmitter staus codes
+#define TWI_MTX_ADR_ACK 0x18 // SLA+W has been tramsmitted and ACK received
+#define TWI_MTX_ADR_NACK 0x20 // SLA+W has been tramsmitted and NACK received
+#define TWI_MTX_DATA_ACK 0x28 // Data byte has been tramsmitted and ACK received
+#define TWI_MTX_DATA_NACK 0x30 // Data byte has been tramsmitted and NACK received
+
+// TWI Master Receiver staus codes
+#define TWI_MRX_ADR_ACK 0x40 // SLA+R has been tramsmitted and ACK received
+#define TWI_MRX_ADR_NACK 0x48 // SLA+R has been tramsmitted and NACK received
+#define TWI_MRX_DATA_ACK 0x50 // Data byte has been received and ACK tramsmitted
+#define TWI_MRX_DATA_NACK 0x58 // Data byte has been received and NACK tramsmitted
+
+// TWI Slave Transmitter staus codes
+#define TWI_STX_ADR_ACK 0xA8 // Own SLA+R has been received; ACK has been returned
+#define TWI_STX_ADR_ACK_M_ARB_LOST 0xB0 // Arbitration lost in SLA+R/W as Master; own SLA+R has been received; ACK has been returned
+#define TWI_STX_DATA_ACK 0xB8 // Data byte in TWDR has been transmitted; ACK has been received
+#define TWI_STX_DATA_NACK 0xC0 // Data byte in TWDR has been transmitted; NOT ACK has been received
+#define TWI_STX_DATA_ACK_LAST_BYTE 0xC8 // Last data byte in TWDR has been transmitted (TWEA = �0�); ACK has been received
+
+// TWI Slave Receiver staus codes
+#define TWI_SRX_ADR_ACK 0x60 // Own SLA+W has been received ACK has been returned
+#define TWI_SRX_ADR_ACK_M_ARB_LOST 0x68 // Arbitration lost in SLA+R/W as Master; own SLA+W has been received; ACK has been returned
+#define TWI_SRX_GEN_ACK 0x70 // General call address has been received; ACK has been returned
+#define TWI_SRX_GEN_ACK_M_ARB_LOST 0x78 // Arbitration lost in SLA+R/W as Master; General call address has been received; ACK has been returned
+#define TWI_SRX_ADR_DATA_ACK 0x80 // Previously addressed with own SLA+W; data has been received; ACK has been returned
+#define TWI_SRX_ADR_DATA_NACK 0x88 // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned
+#define TWI_SRX_GEN_DATA_ACK 0x90 // Previously addressed with general call; data has been received; ACK has been returned
+#define TWI_SRX_GEN_DATA_NACK 0x98 // Previously addressed with general call; data has been received; NOT ACK has been returned
+#define TWI_SRX_STOP_RESTART 0xA0 // A STOP condition or repeated START condition has been received while still addressed as Slave
+
+// TWI Miscellaneous status codes
+#define TWI_NO_STATE 0xF8 // No relevant state information available; TWINT = �0�
+#define TWI_BUS_ERROR 0x00 // Bus error due to an illegal START or STOP condition
+
+
+static inline void
+_avr_twi_status_set(
+ avr_twi_t * p,
+ uint8_t v,
+ int interrupt)
{
-// avr_twi_t * p = (avr_twi_t *)param;
-// uint8_t v = p->input_data_register;
-// p->input_data_register = 0;
-// printf("avr_twi_read = %02x\n", v);
- return 0;
+ avr_regbit_setto_raw(p->io.avr, p->twsr, v);
+ avr_raise_irq(p->io.irq + TWI_IRQ_STATUS, v);
+ if (interrupt)
+ avr_raise_interrupt(p->io.avr, &p->twi);
}
-static void avr_twi_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+static inline uint8_t
+_avr_twi_status_get(
+ avr_twi_t * p)
+{
+ return avr_regbit_get_raw(p->io.avr, p->twsr);
+}
+
+static avr_cycle_count_t
+avr_twi_set_state_timer(
+ struct avr_t * avr,
+ avr_cycle_count_t when,
+ void * param)
{
-#if 0
avr_twi_t * p = (avr_twi_t *)param;
- if (addr == p->r_spdr) {
-// printf("avr_twi_write = %02x\n", v);
- avr_core_watch_write(avr, addr, v);
-
- if (avr_regbit_get(avr, p->spe)) {
- // in master mode, any byte is sent as it comes..
- if (avr_regbit_get(avr, p->mstr)) {
- avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT, v);
- }
- }
- }
-#endif
+ _avr_twi_status_set(p, p->next_twstate, 1);
+ p->next_twstate = 0;
+ return 0;
}
-#if 0
-static void avr_twi_irq_input(struct avr_irq_t * irq, uint32_t value, void * param)
+static void
+_avr_twi_delay_state(
+ avr_twi_t * p,
+ int twi_cycles,
+ uint8_t state)
+{
+ p->next_twstate = state;
+ // TODO: calculate clock rate, convert to cycles, and use that
+ avr_cycle_timer_register_usec(
+ p->io.avr, twi_cycles, avr_twi_set_state_timer, p);
+}
+
+static void
+avr_twi_write(
+ struct avr_t * avr,
+ avr_io_addr_t addr,
+ uint8_t v,
+ void * param)
{
avr_twi_t * p = (avr_twi_t *)param;
- avr_t * avr = p->io.avr;
- // check to see if we are enabled
- if (!avr_regbit_get(avr, p->twen))
+ uint8_t twen = avr_regbit_get(avr, p->twen);
+ uint8_t twsta = avr_regbit_get(avr, p->twsta);
+ uint8_t twsto = avr_regbit_get(avr, p->twsto);
+ uint8_t twint = avr_regbit_get(avr, p->twi.raised);
+
+ avr_core_watch_write(avr, addr, v);
+
+ if (twen != avr_regbit_get(avr, p->twen)) {
+ twen = !twen;
+ if (!twen) { // if we were running, now now are not
+ avr_regbit_clear(avr, p->twea);
+ avr_regbit_clear(avr, p->twsta);
+ avr_regbit_clear(avr, p->twsto);
+ avr_clear_interrupt(avr, p->twi.vector);
+ avr_core_watch_write(avr, p->r_twdr, 0xff);
+ _avr_twi_status_set(p, TWI_NO_STATE, 0);
+ p->state = 0;
+ p->peer_addr = 0;
+ }
+ }
+ if (!twen)
return;
-#if 0
- // double buffer the input.. ?
- p->input_data_register = value;
- avr_raise_interrupt(avr, &p->twi);
-
- // if in slave mode,
- // 'output' the byte only when we received one...
- if (!avr_regbit_get(avr, p->mstr)) {
- avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT, avr->data[p->r_spdr]);
+
+ int cleared = avr_clear_interupt_if(avr, &p->twi, twint);
+
+ // clear the interrupt if this bit is now written to 1
+ if (cleared) {
+ // interrupt was raised before. The AVR code is acknowledging
+ // something we did, so we go to the next state
+ } else {
+ // interrupt was not raised, were we busy ? we can ignore that
+ // for now...
}
-#endif
-}
- // handle a data write, after a (re)start
-static int twi_slave_write(struct twi_slave_t* p, uint8_t v)
-{
- return 0;
+ if (!twsto && avr_regbit_get(avr, p->twsto)) {
+ // generate a stop condition
+
+ if (p->state) { // doing stuff
+ if (p->state & TWI_COND_START) {
+ avr_twi_msg_irq_t msg = {
+ .u.twi.msg = TWI_COND_STOP,
+ .u.twi.addr = p->peer_addr,
+ };
+ avr_raise_irq(p->io.irq + TWI_IRQ_MOSI, msg.u.v);
+ }
+ }
+ p->state = 0;
+ }
+ if (!twsta && avr_regbit_get(avr, p->twsta)) {
+ // generate a start condition
+ if (p->state & TWI_COND_START)
+ _avr_twi_delay_state(p, 3, TWI_REP_START);
+ else
+ _avr_twi_delay_state(p, 3, TWI_START);
+ p->state |= TWI_COND_START;
+ }
+
+ if (cleared &&
+ !avr_regbit_get(avr, p->twsta) &&
+ !avr_regbit_get(avr, p->twsto)) {
+ // writing or reading a byte
+ if (p->state & TWI_COND_ADDR) {
+ // a normal data byte
+ uint8_t msgv = p->peer_addr & 1 ? TWI_COND_READ : TWI_COND_WRITE;
+ if (avr_regbit_get(avr, p->twea))
+ msgv |= TWI_COND_ACK;
+ avr_twi_msg_irq_t msg = {
+ .u.twi.msg = msgv,
+ .u.twi.addr = p->peer_addr,
+ .u.twi.data = avr->data[p->r_twdr],
+ };
+ // we send an IRQ and we /expect/ a slave to reply
+ // immediately via an IRQ to set the COND_ACK bit
+ // otherwise it's assumed it's been nacked...
+ avr_raise_irq(p->io.irq + TWI_IRQ_MOSI, msg.u.v);
+
+ if (p->peer_addr & 1) { // read ?
+ _avr_twi_delay_state(p, 9,
+ p->state & TWI_COND_ACK ?
+ TWI_MTX_DATA_ACK : TWI_MTX_DATA_NACK);
+ } else {
+ _avr_twi_delay_state(p, 9,
+ p->state & TWI_COND_ACK ?
+ TWI_MTX_DATA_ACK : TWI_MTX_DATA_NACK);
+ }
+
+ } else {
+ // send the address
+ p->state |= TWI_COND_ADDR;
+ p->peer_addr = avr->data[p->r_twdr];
+ avr_twi_msg_irq_t msg = {
+ .u.twi.msg = TWI_COND_START,
+ .u.twi.addr = p->peer_addr,
+ };
+ // we send an IRQ and we /expect/ a slave to reply
+ // immediately via an IRQ tp set the COND_ACK bit
+ // otherwise it's assumed it's been nacked...
+ avr_raise_irq(p->io.irq + TWI_IRQ_MOSI, msg.u.v);
+
+ if (p->peer_addr & 1) { // read ?
+ _avr_twi_delay_state(p, 9,
+ p->state & TWI_COND_ACK ?
+ TWI_MRX_ADR_ACK : TWI_MRX_ADR_NACK);
+ } else {
+ _avr_twi_delay_state(p, 9,
+ p->state & TWI_COND_ACK ?
+ TWI_MTX_ADR_ACK : TWI_MTX_ADR_NACK);
+ }
+ }
+ }
}
- // handle a data read, after a (re)start
-static uint8_t twi_slave_read(struct twi_slave_t* p)
+/*
+ * prevent code from rewriting out status bits, since we actualy use them!
+ */
+static void
+avr_twi_write_status(
+ struct avr_t * avr,
+ avr_io_addr_t addr,
+ uint8_t v,
+ void * param)
{
- return 0;
+ avr_twi_t * p = (avr_twi_t *)param;
+ uint8_t sr = avr_regbit_get(avr, p->twsr);
+ uint8_t c = avr_regbit_get(avr, p->twps);
+
+ avr_core_watch_write(avr, addr, v);
+ avr_regbit_setto(avr, p->twsr, sr); // force restore
+
+ if (c != avr_regbit_get(avr, p->twps)) {
+ // prescaler bits changed...
+ }
}
-#endif
-static int avr_twi_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param)
+static void
+avr_twi_irq_input(
+ struct avr_irq_t * irq,
+ uint32_t value,
+ void * param)
{
- avr_twi_t * p = (avr_twi_t *)port;
- int res = -1;
+ avr_twi_t * p = (avr_twi_t *)param;
+ avr_t * avr = p->io.avr;
- if (ctl == AVR_IOCTL_TWI_GETSLAVE(p->name)) {
- *(twi_slave_t**)io_param = &p->slave;
- } else if (ctl == AVR_IOCTL_TWI_GETBUS(p->name)) {
- *(twi_bus_t**)io_param = &p->bus;
+ // check to see if we are enabled
+ if (!avr_regbit_get(avr, p->twen))
+ return;
+ switch (irq->irq) {
+ case TWI_IRQ_MISO:
+ break;
+ case TWI_IRQ_MOSI:
+ break;
}
-
- return res;
}
void avr_twi_reset(struct avr_io_t *io)
{
-// avr_twi_t * p = (avr_twi_t *)io;
- //avr_irq_register_notify(p->io.irq + TWI_IRQ_INPUT, avr_twi_irq_input, p);
+ avr_twi_t * p = (avr_twi_t *)io;
+ avr_irq_register_notify(p->io.irq + TWI_IRQ_MISO, avr_twi_irq_input, p);
}
static avr_io_t _io = {
.kind = "twi",
.reset = avr_twi_reset,
- .ioctl = avr_twi_ioctl,
};
void avr_twi_init(avr_t * avr, avr_twi_t * p)
p->io = _io;
avr_register_io(avr, &p->io);
avr_register_vector(avr, &p->twi);
-// p->slave = slave_driver; // get default callbacks
- twi_slave_init(&p->slave, 0, p);
- twi_bus_init(&p->bus);
//printf("%s TWI%c init\n", __FUNCTION__, p->name);
// allocate this module's IRQ
avr_io_setirqs(&p->io, AVR_IOCTL_TWI_GETIRQ(p->name), TWI_IRQ_COUNT, NULL);
- avr_register_io_write(avr, p->r_twdr, avr_twi_write, p);
- avr_register_io_read(avr, p->r_twdr, avr_twi_read, p);
+ avr_register_io_write(avr, p->twen.reg, avr_twi_write, p);
+ avr_register_io_write(avr, p->twsr.reg, avr_twi_write_status, p);
}
+++ /dev/null
-/*
- sim_twi.c
-
- Internal TWI/i2c slave/master subsystem
-
- Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
-
- This file is part of simavr.
-
- simavr 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.
-
- simavr 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 simavr. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include "sim_twi.h"
-
-static void twi_bus_master_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
-{
- twi_bus_t * bus = (twi_bus_t *)param;
- switch (irq->irq) {
- case TWI_MASTER_STOP:
- bus->peer = NULL;
- break;
- case TWI_MASTER_START:
- bus->peer = NULL;
- bus->ack = 0;
- break;
- case TWI_MASTER_MISO:
- bus->ack = 0;
- break;
- case TWI_MASTER_MOSI:
- bus->ack = 0;
- break;
- case TWI_MASTER_ACK:
- if (!bus->peer) {
- }
- break;
- }
-}
-
-static void twi_bus_slave_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
-{
- twi_slave_t * slave = (twi_slave_t*)param;
- twi_bus_t * bus = slave->bus;
- switch (irq->irq) {
- case TWI_SLAVE_MISO:
- bus->latch = value;
- break;
- case TWI_SLAVE_ACK:
- if (!bus->peer) {
- bus->peer = slave;
- printf("twi bus: slave %x selected\n", slave->address);
- }
- bus->ack = 0x80 | (value & 1);
- break;
- }
-}
-
-static void twi_slave_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
-{
- twi_slave_t * slave = (twi_slave_t*)param;
- switch (irq->irq) {
- case TWI_MASTER_STOP:
- if (slave->match) {
- // we were target
- }
- slave->match = 0;
- break;
- case TWI_MASTER_START:
- if ((value & 0xfe) == (slave->address & 0xfe)) {
- if (slave->match) {
- // restart
- }
- slave->match = 1;
- avr_raise_irq(slave->irq + TWI_SLAVE_ACK, 1);
- }
- break;
- case TWI_MASTER_MISO:
- break;
- case TWI_MASTER_MOSI:
- break;
- case TWI_MASTER_ACK:
- break;
- }
-}
-
-void twi_bus_init(twi_bus_t * bus)
-{
- memset(bus, 0, sizeof(twi_bus_t));
- //avr_init_irq(bus->irq, 0, TWI_MASTER_STATE_COUNT);
- for (int i = 0; i < TWI_MASTER_STATE_COUNT; i++)
- avr_irq_register_notify(bus->irq + i, twi_bus_master_irq_notify, bus);
-}
-
-void twi_bus_attach(twi_bus_t * bus, twi_slave_t * slave)
-{
- twi_slave_detach(slave);
- slave->bus = bus;
- slave->next = bus->slave;
- bus->slave = slave;
-
- for (int i = 0; i < TWI_SLAVE_STATE_COUNT; i++)
- avr_irq_register_notify(slave->irq + i, twi_bus_slave_irq_notify, slave);
- for (int i = 0; i < TWI_MASTER_STATE_COUNT; i++)
- avr_irq_register_notify(bus->irq + i, twi_slave_irq_notify, slave);
-}
-
-int twi_bus_start(twi_bus_t * bus, uint8_t address)
-{
- avr_raise_irq(bus->irq + TWI_MASTER_START, address);
- return bus->peer != NULL ? 1 : 0;
-}
-
-void twi_bus_stop(twi_bus_t * bus)
-{
- avr_raise_irq(bus->irq + TWI_MASTER_STOP, 0);
-}
-
-
-void twi_slave_init(twi_slave_t * slave, uint8_t address, void * param)
-{
- memset(slave, 0, sizeof(twi_slave_t));
- slave->address = address;
-// slave->param = param;
-}
-
-void twi_slave_detach(twi_slave_t * slave)
-{
- if (!slave || !slave->bus)
- return;
- twi_slave_t *s = slave->bus->slave;
- while (s) {
- if (s->next == slave) {
- // clear that, too
- if (slave->bus->peer == slave)
- slave->bus->peer = NULL;
-
- s->next = slave->next;
- slave->next = NULL;
- slave->bus = NULL;
- return;
- }
- s = s->next;
- }
-}
-
+++ /dev/null
-/*
- sim_twi.h
-
- Internal TWI/i2c slave/master subsystem
-
- You can have a "bus" to talk to a bunch of "slaves"
-
- Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
-
- This file is part of simavr.
-
- simavr 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.
-
- simavr 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 simavr. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef SIM_TWI_H_
-#define SIM_TWI_H_
-
-#include <stdint.h>
-#include "sim_irq.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * The TWI system is designed to be representing the same state as
- * a TWI/i2c bus itself. So each "state" of the bus is an IRQ sent
- * by the master to the slave, with a couple sent from the
- * slave to the master.
- * This is designed to decorelate the operations on the "bus" so
- * the firmware has time to "run" before acknowledging a byte, for
- * example.
- *
- * IRQ Timeline goes as follow with an example transaction that
- * does write address, write register, read a byte after a i2c restart
- * then stops the transaction.
- *
- * Master: START MOSI START MISO ACK STOP
- * Slave: ACK ACK ACK MISO
- */
-enum twi_state_e {
- TWI_MASTER_STOP = 0,
- TWI_MASTER_START, // master does a start with address
- TWI_MASTER_MOSI, // master i2c write
- TWI_MASTER_MISO, // master i2c read
- TWI_MASTER_ACK, // master i2c ACK after a i2c read
- TWI_MASTER_STATE_COUNT,
-
- TWI_SLAVE_MISO = 0, // slave i2c read.
- TWI_SLAVE_ACK, // slave acknowledges TWI_MASTER_MOSI
- TWI_SLAVE_STATE_COUNT,
-};
-
-#define TWI_ADDRESS_READ_MASK 0x01
-
-typedef struct twi_slave_t {
- avr_irq_t irq[TWI_SLAVE_STATE_COUNT];
-
- struct twi_bus_t * bus; // bus we are attached to
- struct twi_slave_t * next; // daisy chain on the bus
-
- uint32_t address; // can specify up to 4 matching addresses here
- int match; // we are selected on the bus
- int index; // byte index in the transaction
-
- uint8_t latch; // last received byte
-} twi_slave_t;
-
-
-typedef struct twi_bus_t {
- avr_irq_t irq[TWI_MASTER_STATE_COUNT];
-
- struct twi_slave_t * slave; // daisy chain on the bus
- struct twi_slave_t * peer; // during a transaction, this is the selected slave
-
- uint8_t latch; // last received byte
- uint8_t ack; // last received ack
-} twi_bus_t;
-
-void twi_bus_init(twi_bus_t * bus);
-int twi_bus_start(twi_bus_t * bus, uint8_t address);
-int twi_bus_write(twi_bus_t * bus, uint8_t data);
-uint8_t twi_bus_read(twi_bus_t * bus);
-void twi_bus_stop(twi_bus_t * bus);
-
-void twi_slave_init(twi_slave_t * slave, uint8_t address, void * param);
-void twi_slave_detach(twi_slave_t * slave);
-
-#ifdef __cplusplus
-};
-#endif
-
-#endif /* SIM_TWI_H_ */