123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /*
- * USI I2C bus slave
- *
- * Copyright (c) 2016-2018 Michael Buesch <m@bues.ch>
- *
- * This program 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 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include "i2c_slave.h"
- #include "util.h"
- #include <string.h>
- #include <avr/io.h>
- #include <avr/interrupt.h>
- #define SDA_PORT PORTB
- #define SDA_PIN PINB
- #define SDA_DDR DDRB
- #define SDA_BIT PB0
- #define SCL_PORT PORTB
- #define SCL_PIN PINB
- #define SCL_DDR DDRB
- #define SCL_BIT PB2
- #ifndef I2CS_MAX_NR_SLAVES
- # error "I2CS_MAX_NR_SLAVES is not defined"
- #endif
- #ifndef I2CS_EXPECTED_KHZ
- # error "I2CS_EXPECTED_KHZ is not defined"
- #endif
- enum i2cs_state {
- I2CS_STATE_ADDR, /* Handle received address */
- I2CS_STATE_PREP_SND, /* Prepare sending of data */
- I2CS_STATE_PREP_RCV, /* Prepare receiving of data */
- I2CS_STATE_SND, /* Data was sent */
- I2CS_STATE_RCV, /* Handle received data */
- I2CS_STATE_RCVPROC, /* Process received data */
- I2CS_STATE_RCV_ACK, /* Handle received ack after sent data */
- };
- #define I2CS_NO_ADDR 0xFFu
- #define I2CS_NO_SLAVE 0xFFu
- static enum i2cs_state _used i2cs_state;
- static uint8_t _used i2cs_addrs[I2CS_MAX_NR_SLAVES];
- static uint8_t _used i2cs_active_slave;
- static uint8_t _used i2cs_rx_byte;
- static bool i2cs_had_start;
- static const struct i2c_slave_ops __flash _used *i2cs_ops[I2CS_MAX_NR_SLAVES];
- #ifdef I2CS_CLKSTRETCH_WORKAROUND
- /* Raspberry Pi I2C clock stretching bug workaround. */
- static void clkstretch_timer_init(void)
- {
- TCCR0B = 0u;
- TCNT0 = 0u;
- OCR0A = 0u;
- OCR0B = 0u;
- TIMSK &= (uint8_t)~((1u << OCIE0A) | (1u << OCIE0B) | (1u << TOIE0));
- TIFR = (1u << OCF0A) | (1u << OCF0B) | (1u << TOV0);
- /* Normal mode, prescaler 8. */
- build_assert(F_CPU == 16000000UL);
- GTCCR &= (uint8_t)(~(1u << PSR0) & 0xFFu);
- GTCCR &= (uint8_t)(~(1u << TSM) & 0xFFu);
- TCCR0A = (0u << COM0A1) | (0u << COM0A0) |
- (0u << COM0B1) | (0u << COM0B0) |
- (0u << WGM01) | (0u << WGM00);
- TCCR0B = (0u << FOC0A) | (0u << FOC0B) |
- (0u << WGM02) |
- (0u << CS02) | (1u << CS01) | (0u << CS00);
- #define TCNT0_KHZ 2000u
- }
- /* Prepare the clock stretching workaround timer.
- * r31 is the only register allowed to be clobbered.
- * Everything else (including SREG) must be left untouched!
- */
- #define CLKSTRETCH_TIMER_PREPARE() \
- " ldi r31, %[_STRETCH_TIMER_PRELOAD] \n" \
- " out %[_TCNT0], r31 \n" \
- " ldi r31, (1 << %[_TOV0]) \n" \
- " out %[_TIFR], r31 \n"
- /* Wait until we are in the safe range. */
- #define CLKSTRETCH_TIMER_WAIT() \
- "1: in r31, %[_TIFR] \n" \
- " sbrs r31, %[_TOV0] \n" \
- " rjmp 1b \n"
- #else /* I2CS_CLKSTRETCH_WORKAROUND */
- static void clkstretch_timer_init(void) { }
- #define CLKSTRETCH_TIMER_PREPARE() ""
- #define CLKSTRETCH_TIMER_WAIT() ""
- #define TCNT0_KHZ 0u
- #endif /* I2CS_CLKSTRETCH_WORKAROUND */
- #define USICR_BASE ( \
- (1u << USISIE) | \
- (0u << USIOIE) | \
- (1u << USIWM1) | \
- (0u << USIWM0) | \
- (1u << USICS1) | \
- (0u << USICS0) | \
- (0u << USICLK) | \
- (0u << USITC) \
- )
- #define USISR_BASE ( \
- (0u << USISIF) | \
- (1u << USIOIF) | \
- (1u << USIPF) | \
- (0u << USICNT2) | \
- (0u << USICNT1) | \
- (0u << USICNT0) \
- )
- ISR(USI_START_vect)
- {
- memory_barrier();
- SDA_DDR &= (uint8_t)~(1u << SDA_BIT);
- /* Wait for SCL low (or stop condition). */
- while ((SCL_PIN & (1u << SCL_BIT)) &&
- !((SDA_PIN & (1u << SDA_BIT)))) {
- /* Wait.
- * We depend on the WDT to restart the MCU, if this loop never
- * terminates due to external issues.
- */
- }
- /* Check whether we do not have a stop condition. */
- if (!(SDA_PIN & (1u << SDA_BIT))) {
- /* Enable counter overflow interrupt. */
- USICR = USICR_BASE | (1u << USIOIE) | (0u << USIWM0);
- }
- USISR = USISR_BASE | (1u << USISIF) | (1u << USICNT0);
- i2cs_state = I2CS_STATE_ADDR;
- i2cs_active_slave = I2CS_NO_SLAVE;
- i2cs_had_start = true;
- memory_barrier();
- }
- static uint8_t _used slaveop_transmit(void)
- {
- const struct i2c_slave_ops __flash *ops;
- uint8_t data;
- ops = i2cs_ops[i2cs_active_slave];
- data = ops->transmit(i2cs_had_start);
- i2cs_had_start = false;
- return data;
- }
- static uint8_t _used slaveop_receive(uint8_t data)
- {
- const struct i2c_slave_ops __flash *ops;
- bool continue_rx;
- ops = i2cs_ops[i2cs_active_slave];
- continue_rx = ops->receive(i2cs_had_start, data);
- i2cs_had_start = false;
- return continue_rx ? 1u : 0u;
- }
- /* Save all caller-saved regs, except r0 and SREG. */
- #define SAVE_CALLER_REGS() \
- " push r18 \n" \
- " push r19 \n" \
- " push r20 \n" \
- " push r21 \n" \
- " push r22 \n" \
- " push r23 \n" \
- " push r24 \n" \
- " push r25 \n" \
- " push r26 \n" \
- " push r27 \n" \
- " push r30 \n" \
- " push r31 \n"
- /* Restore all caller-saved regs, except r0 and SREG. */
- #define RESTORE_CALLER_REGS() \
- " pop r31 \n" \
- " pop r30 \n" \
- " pop r27 \n" \
- " pop r26 \n" \
- " pop r25 \n" \
- " pop r24 \n" \
- " pop r23 \n" \
- " pop r22 \n" \
- " pop r21 \n" \
- " pop r20 \n" \
- " pop r19 \n" \
- " pop r18 \n"
- #define IN_CONSTR_BASE \
- [_SREG] "I" (_SFR_IO_ADDR(SREG)), \
- [_TCNT0] "I" (_SFR_IO_ADDR(TCNT0)), \
- [_TIFR] "I" (_SFR_IO_ADDR(TIFR)), \
- [_TOV0] "M" (TOV0), \
- [_STRETCH_TIMER_PRELOAD] "M" (256u - ((TCNT0_KHZ / I2CS_EXPECTED_KHZ) + 1u)), \
- [_USIDR] "I" (_SFR_IO_ADDR(USIDR)), \
- [_USICR] "I" (_SFR_IO_ADDR(USICR)), \
- [_USISR] "I" (_SFR_IO_ADDR(USISR)), \
- [_SDA_DDR] "I" (_SFR_IO_ADDR(SDA_DDR)), \
- [_SDA_BIT] "M" (SDA_BIT), \
- [_SCL_PIN] "I" (_SFR_IO_ADDR(SCL_PIN)), \
- [_SCL_BIT] "M" (SCL_BIT), \
- [_STATE_ADDR] "M" (I2CS_STATE_ADDR), \
- [_STATE_PREP_SND] "M" (I2CS_STATE_PREP_SND), \
- [_STATE_PREP_RCV] "M" (I2CS_STATE_PREP_RCV), \
- [_STATE_SND] "M" (I2CS_STATE_SND), \
- [_STATE_RCV] "M" (I2CS_STATE_RCV), \
- [_STATE_RCVPROC] "M" (I2CS_STATE_RCVPROC), \
- [_STATE_RCV_ACK] "M" (I2CS_STATE_RCV_ACK), \
- [_I2CS_NO_SLAVE] "M" (I2CS_NO_SLAVE)
- static void _naked _used switch_to_start_condition_state(void)
- {
- __asm__ __volatile__(
- " ; Set USI to wait for start condition. \n"
- " ldi r31, %[_cr_scond] \n"
- " out %[_USICR], r31 \n"
- " ldi r31, %[_sr_scond] \n"
- " out %[_USISR], r31 \n"
- " \n"
- " ; Reset active slave index. \n"
- " ldi r31, %[_I2CS_NO_SLAVE] \n"
- " sts i2cs_active_slave, r31 \n"
- " \n"
- " rjmp isr_ovf_ret \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE,
- [_cr_scond] "M" (USICR_BASE | (0u << USIOIE) | (0u << USIWM0)),
- [_sr_scond] "M" (USISR_BASE | (0u << USISIF) | (0u << USICNT0))
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- #define CHECK_ONE_ADDR(index) \
- " lds r31, i2cs_addrs + " tostr(index) " \n" \
- " cp r31, __tmp_reg__ \n" \
- " ldi r31, " tostr(index) " \n" \
- " breq handle_addr \n" \
- static void _naked _used handle_state_addr(void)
- {
- __asm__ __volatile__(
- " ; Read the received address. \n"
- " in r30, %[_USIDR] \n"
- " \n"
- " ; Check if the address is ours. \n"
- " mov __tmp_reg__, r30 \n"
- " lsr __tmp_reg__ \n"
- #if I2CS_MAX_NR_SLAVES >= 1
- CHECK_ONE_ADDR(0)
- #endif
- #if I2CS_MAX_NR_SLAVES >= 2
- CHECK_ONE_ADDR(1)
- #endif
- #if I2CS_MAX_NR_SLAVES >= 3
- CHECK_ONE_ADDR(2)
- #endif
- #if I2CS_MAX_NR_SLAVES >= 4
- # error "I2CS_MAX_NR_SLAVES is too big"
- #endif
- " \n"
- " ; Unknown address. \n"
- " rjmp switch_to_start_condition_state \n"
- " \n"
- "handle_addr: \n"
- " ; Save the slave index. \n"
- " sts i2cs_active_slave, r31 \n"
- " \n"
- " ; Wait for SCK low \n"
- "1: sbic %[_SCL_PIN], %[_SCL_BIT] \n"
- " rjmp 1b \n"
- " \n"
- " ; Set SDA low for ACK. \n"
- " out %[_USIDR], __zero_reg__ \n"
- " sbi %[_SDA_DDR], %[_SDA_BIT] \n"
- " \n"
- " ; Set USI to send ACK. \n"
- " ldi r31, %[_cr_ack] \n"
- " out %[_USICR], r31 \n"
- " ldi r31, %[_sr_ack] \n"
- " out %[_USISR], r31 \n"
- " \n"
- " ; Set the next state. \n"
- " ldi r31, %[_STATE_PREP_SND] \n"
- " sbrs r30, 0 ; Check R/W bit \n"
- " ldi r31, %[_STATE_PREP_RCV] \n"
- " sts i2cs_state, r31 \n"
- " \n"
- " rjmp isr_ovf_ret \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE,
- [_cr_ack] "M" (USICR_BASE | (1u << USIOIE) | (1u << USIWM0)),
- [_sr_ack] "M" (USISR_BASE | (0u << USISIF) | (14u << USICNT0))
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- static void _naked _used handle_state_prep_snd(void)
- {
- __asm__ __volatile__(
- SAVE_CALLER_REGS()
- " \n"
- " ; Call the slave op to get the next TX byte. \n"
- " rcall slaveop_transmit \n"
- " \n"
- " ; Write the TX byte to USI. \n"
- " out %[_USIDR], r24 \n"
- " \n"
- " ; Enable SDA driver. \n"
- " sbi %[_SDA_DDR], %[_SDA_BIT] \n"
- " \n"
- RESTORE_CALLER_REGS()
- " \n"
- CLKSTRETCH_TIMER_WAIT()
- " \n"
- " ; Set USI to send data \n"
- " ldi r31, %[_cr_send] \n"
- " out %[_USICR], r31 \n"
- " ldi r31, %[_sr_send] \n"
- " out %[_USISR], r31 \n"
- " ; Write counter again to make sure it's \n"
- " ; written after the pos. SCL edge. \n"
- " out %[_USISR], r31 \n"
- " \n"
- " ; Set the next state \n"
- " ldi r31, %[_STATE_SND] \n"
- " sts i2cs_state, r31 \n"
- " \n"
- " rjmp isr_ovf_ret \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE,
- [_cr_send] "M" (USICR_BASE | (1u << USIOIE) | (0u << USIWM0)),
- [_sr_send] "M" (USISR_BASE | (0u << USISIF) | (2u << USICNT0))
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- static void _naked _used handle_state_prep_rcv(void)
- {
- __asm__ __volatile__(
- " ; Stop pulling SDA. \n"
- " cbi %[_SDA_DDR], %[_SDA_BIT] \n"
- " \n"
- CLKSTRETCH_TIMER_WAIT()
- " \n"
- " ; Set USI to read data \n"
- " ldi r31, %[_cr_rddata] \n"
- " out %[_USICR], r31 \n"
- " ldi r31, %[_sr_rddata] \n"
- " out %[_USISR], r31 \n"
- " ; Write counter again to make sure it's \n"
- " ; written after the pos. SCL edge. \n"
- " out %[_USISR], r31 \n"
- " \n"
- " ; Set the next state \n"
- " ldi r31, %[_STATE_RCV] \n"
- " sts i2cs_state, r31 \n"
- " \n"
- " rjmp isr_ovf_ret \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE,
- [_cr_rddata] "M" (USICR_BASE | (1u << USIOIE) | (0u << USIWM0)),
- [_sr_rddata] "M" (USISR_BASE | (0u << USISIF) | (2u << USICNT0))
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- static void _naked _used handle_state_rcv(void)
- {
- __asm__ __volatile__(
- " ; Get the received data \n"
- " in r30, %[_USIDR] \n"
- " \n"
- " ; Wait for SCK low \n"
- "1: sbic %[_SCL_PIN], %[_SCL_BIT] \n"
- " rjmp 1b \n"
- " \n"
- " ; Store the received byte for later processing. \n"
- " ; Can't process it now due to possible \n"
- " ; Raspi SCL stretch bug. \n"
- " sts i2cs_rx_byte, r30 \n"
- " \n"
- " ; Set SDA low for ACK. \n"
- " out %[_USIDR], __zero_reg__ \n"
- " sbi %[_SDA_DDR], %[_SDA_BIT] \n"
- " \n"
- " ; Set USI to send ack. \n"
- " ldi r31, %[_cr_ack] \n"
- " out %[_USICR], r31 \n"
- " ldi r31, %[_sr_ack] \n"
- " out %[_USISR], r31 \n"
- " \n"
- " ; Set the next state \n"
- " ldi r31, %[_STATE_RCVPROC] \n"
- " sts i2cs_state, r31 \n"
- " \n"
- " rjmp isr_ovf_ret \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE,
- [_cr_ack] "M" (USICR_BASE | (1u << USIOIE) | (1u << USIWM0)),
- [_sr_ack] "M" (USISR_BASE | (0u << USISIF) | (14u << USICNT0))
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- static void _naked _used handle_state_rcvproc(void)
- {
- __asm__ __volatile__(
- SAVE_CALLER_REGS()
- " \n"
- " ; Call the slave op to receive the byte. \n"
- " lds r24, i2cs_rx_byte \n"
- " rcall slaveop_receive \n"
- " mov __tmp_reg__, r24 \n"
- " \n"
- RESTORE_CALLER_REGS()
- " \n"
- " ; If 'continue-rx' is set, prepare next RX \n"
- " sbrc __tmp_reg__, 0 \n"
- " rjmp handle_state_prep_rcv \n"
- " \n"
- " ; We expect a new address transmission. \n"
- " \n"
- " ; Stop pulling SDA. \n"
- " cbi %[_SDA_DDR], %[_SDA_BIT] \n"
- " \n"
- CLKSTRETCH_TIMER_WAIT()
- " \n"
- " ; Set USI to read addr \n"
- " ldi r31, %[_cr_addrread] \n"
- " out %[_USICR], r31 \n"
- " ldi r31, %[_sr_addrread] \n"
- " out %[_USISR], r31 \n"
- " ; Write counter again to make sure it's \n"
- " ; written after the pos. SCL edge. \n"
- " out %[_USISR], r31 \n"
- " \n"
- " ; Set the next state \n"
- " ldi r31, %[_STATE_ADDR] \n"
- " sts i2cs_state, r31 \n"
- " \n"
- " rjmp isr_ovf_ret \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE,
- [_cr_addrread] "M" (USICR_BASE | (1u << USIOIE) | (0u << USIWM0)),
- #if 0
- [_sr_addrread] "M" (USISR_BASE | (0u << USISIF) | (2u << USICNT0))
- #else
- [_sr_addrread] "M" (USISR_BASE | (0u << USISIF) | (1u << USICNT0))
- #endif
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- static void _naked _used handle_state_snd(void)
- {
- __asm__ __volatile__(
- " ; Release SDA. \n"
- " cbi %[_SDA_DDR], %[_SDA_BIT] \n"
- " \n"
- " ; Wait for SCK low \n"
- "1: sbic %[_SCL_PIN], %[_SCL_BIT] \n"
- " rjmp 1b \n"
- " \n"
- " ; Set USI to read ack \n"
- " ldi r31, %[_cr_rdack] \n"
- " out %[_USICR], r31 \n"
- " ldi r31, %[_sr_rdack] \n"
- " out %[_USISR], r31 \n"
- " \n"
- " ; Set the next state \n"
- " ldi r31, %[_STATE_RCV_ACK] \n"
- " sts i2cs_state, r31 \n"
- " \n"
- " rjmp isr_ovf_ret \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE,
- [_cr_rdack] "M" (USICR_BASE | (1u << USIOIE) | (1u << USIWM0)),
- [_sr_rdack] "M" (USISR_BASE | (0u << USISIF) | (14u << USICNT0))
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- static void _naked _used handle_state_rcv_ack(void)
- {
- __asm__ __volatile__(
- " ; Read the state of SDA to get ACK/NACK \n"
- " in r30, %[_USIDR] \n"
- " \n"
- " ; Check whether we have ACK or NACK \n"
- " andi r30, 0x01 \n"
- " brne chk_reply_got_nack \n"
- " \n"
- " ; We got an ACK. \n"
- " ; Just directly go to send. \n"
- " rjmp handle_state_prep_snd \n"
- " \n"
- "chk_reply_got_nack: \n"
- " rjmp switch_to_start_condition_state \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- static uint16_t _used state_handlers[] = {
- [I2CS_STATE_ADDR] = (uint16_t)&handle_state_addr,
- [I2CS_STATE_PREP_SND] = (uint16_t)&handle_state_prep_snd,
- [I2CS_STATE_PREP_RCV] = (uint16_t)&handle_state_prep_rcv,
- [I2CS_STATE_SND] = (uint16_t)&handle_state_snd,
- [I2CS_STATE_RCV] = (uint16_t)&handle_state_rcv,
- [I2CS_STATE_RCVPROC] = (uint16_t)&handle_state_rcvproc,
- [I2CS_STATE_RCV_ACK] = (uint16_t)&handle_state_rcv_ack,
- };
- ISR(USI_OVF_vect, ISR_NAKED)
- {
- __asm__ __volatile__(
- " push r31 \n"
- CLKSTRETCH_TIMER_PREPARE()
- " in r31, %[_SREG] \n"
- " push r31 \n"
- " push __tmp_reg__ \n"
- " push __zero_reg__ \n"
- " push r30 \n"
- " clr __zero_reg__ \n"
- " \n"
- " ; Branch to the current state handler. \n"
- " ldi r30, lo8(state_handlers) \n"
- " ldi r31, hi8(state_handlers) \n"
- " lds __tmp_reg__, i2cs_state \n"
- " lsl __tmp_reg__ \n"
- " add r30, __tmp_reg__ \n"
- " adc r31, __zero_reg__ \n"
- " ld __tmp_reg__, Z+ \n"
- " ld r31, Z \n"
- " mov r30, __tmp_reg__ \n"
- " ijmp \n"
- " \n"
- "isr_ovf_ret: \n"
- " pop r30 \n"
- " pop __zero_reg__ \n"
- " pop __tmp_reg__ \n"
- " pop r31 \n"
- " out %[_SREG], r31 \n"
- " pop r31 \n"
- " reti \n"
- : /* outputs */
- : /* inputs */
- IN_CONSTR_BASE
- : /* clobbers */
- "memory"
- );
- unreachable();
- }
- void i2cs_add_slave(uint8_t addr, const struct i2c_slave_ops __flash *ops)
- {
- uint8_t i;
- for (i = 0u; i < ARRAY_SIZE(i2cs_addrs); i++) {
- if (i2cs_addrs[i] == I2CS_NO_ADDR) {
- i2cs_addrs[i] = addr;
- i2cs_ops[i] = ops;
- break;
- }
- }
- }
- void i2cs_init(void)
- {
- i2cs_state = I2CS_STATE_ADDR;
- i2cs_active_slave = I2CS_NO_SLAVE;
- i2cs_had_start = false;
- memset(i2cs_addrs, I2CS_NO_ADDR, sizeof(i2cs_addrs));
- clkstretch_timer_init();
- /* SDA */
- SDA_PORT |= (1u << SDA_BIT);
- SDA_DDR &= (uint8_t)~(1u << SDA_BIT);
- /* SCL */
- SCL_PORT |= (1u << SCL_BIT);
- SCL_DDR |= (1u << SCL_BIT);
- /* Initialize USI in TWI slave mode. */
- USICR = USICR_BASE | (0u << USIOIE) | (0u << USIWM0);
- USISR = USISR_BASE | (1u << USISIF) | (0u << USICNT0);
- }
|