From dc427b7efc7905fb825722a67bd2a480615dd3b6 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Thu, 24 Feb 2011 18:02:35 +0000 Subject: [PATCH] twi: New master implementation Yanked the old overengineered version, replaced it with a KISS version. Untested, work in progress. Needs a new testing harness Signed-off-by: Michel Pollet --- simavr/sim/avr_twi.c | 296 ++++++++++++++++++++++++++++++++++--------- simavr/sim/avr_twi.h | 49 +++++-- simavr/sim/sim_twi.c | 158 ----------------------- simavr/sim/sim_twi.h | 104 --------------- 4 files changed, 271 insertions(+), 336 deletions(-) delete mode 100644 simavr/sim/sim_twi.c delete mode 100644 simavr/sim/sim_twi.h diff --git a/simavr/sim/avr_twi.c b/simavr/sim/avr_twi.c index 589e18e..c605011 100644 --- a/simavr/sim/avr_twi.c +++ b/simavr/sim/avr_twi.c @@ -22,92 +22,269 @@ #include #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) @@ -115,16 +292,13 @@ 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); } diff --git a/simavr/sim/avr_twi.h b/simavr/sim/avr_twi.h index 1de1ef4..7dfef64 100644 --- a/simavr/sim/avr_twi.h +++ b/simavr/sim/avr_twi.h @@ -24,29 +24,45 @@ #include "sim_avr.h" -#include "sim_twi.h" +//#include "sim_twi.h" enum { - TWI_IRQ_INPUT = 0, - TWI_IRQ_OUTPUT, + TWI_IRQ_MISO = 0, + TWI_IRQ_MOSI, + TWI_IRQ_STATUS, TWI_IRQ_COUNT }; +enum { + TWI_COND_START = (1 << 0), + TWI_COND_STOP = (1 << 1), + TWI_COND_ADDR = (1 << 2), + TWI_COND_ACK = (1 << 3), + TWI_COND_WRITE = (1 << 4), + TWI_COND_READ = (1 << 5), +}; + +typedef struct avr_twi_msg_t { + uint32_t unused : 8, + msg : 8, + addr : 8, + data : 8; +} avr_twi_msg_t; + +typedef struct avr_twi_msg_irq_t { + union { + uint32_t v; + avr_twi_msg_t twi; + } u; +} avr_twi_msg_irq_t; // add port number to get the real IRQ #define AVR_IOCTL_TWI_GETIRQ(_name) AVR_IOCTL_DEF('t','w','i',(_name)) -// return a pointer to the slave structure related to this TWI port -#define AVR_IOCTL_TWI_GETSLAVE(_name) AVR_IOCTL_DEF('t','w','s',(_name)) -// retutn this twi interface "master" bus -#define AVR_IOCTL_TWI_GETBUS(_name) AVR_IOCTL_DEF('t','w','b',(_name)) typedef struct avr_twi_t { avr_io_t io; char name; - - twi_slave_t slave; // when we are a slave, to be attached to some bus - twi_bus_t bus; // when we are a master, to attach slaves to - + avr_regbit_t disabled; // bit in the PRR avr_io_addr_t r_twbr; // bit rate register @@ -65,9 +81,16 @@ typedef struct avr_twi_t { avr_regbit_t twsr; // status registers, (5 bits) avr_regbit_t twps; // prescaler bits (2 bits) - avr_int_vector_t twi; // spi interrupt + avr_int_vector_t twi; // twi interrupt + + uint8_t state; + uint8_t peer_addr; + uint8_t next_twstate; } avr_twi_t; -void avr_twi_init(avr_t * avr, avr_twi_t * port); +void +avr_twi_init( + avr_t * avr, + avr_twi_t * port); #endif /* AVR_TWI_H_ */ diff --git a/simavr/sim/sim_twi.c b/simavr/sim/sim_twi.c deleted file mode 100644 index 16bfa97..0000000 --- a/simavr/sim/sim_twi.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - sim_twi.c - - Internal TWI/i2c slave/master subsystem - - Copyright 2008, 2009 Michel Pollet - - 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 . - */ - -#include -#include -#include -#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; - } -} - diff --git a/simavr/sim/sim_twi.h b/simavr/sim/sim_twi.h deleted file mode 100644 index dbb6b96..0000000 --- a/simavr/sim/sim_twi.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - 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 - - 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 . - */ - -#ifndef SIM_TWI_H_ -#define SIM_TWI_H_ - -#include -#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_ */ -- 2.39.5