week 13 - networking

i didnt have time to make my own circuit in fab lab so i started with arduino this week..

AGAIN???

  1. pulled the bus lines (sda , scl) to vcc (5v) through 10k resistors.

  2. the code:


#include <Wire.h>

char c = '1';

void setup() {
  // put your setup code here, to run once:
  Wire.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  Wire.beginTransmission(10);
  Wire.write(c);
  if (c == '1') //BLINK THE c
    c= '0';
  else
    c= '1';
  Wire.endTransmission();
  delay(1000);
}
  

#include <Wire.h>

void setup() {
  pinMode(13,OUTPUT);
  Wire.begin(10); //i2c slave addr 10
  Wire.onReceive(rcvISR);
}

void loop() 
{
  /* NOTHING */
}

void rcvISR(int x)
{
  char c = Wire.read();
  Wire.flush();
  if (c == '1')
    digitalWrite(13,1);
  else
    digitalWrite(13,0);
}
  
  1. Result:

I2c driver implementation:

i want to implement i2c driver through playing with the registers directly and understand how to do it with the microcontroller..

i was going to use I2C in Attiny44A but i didnt have it at the development time with me..
so i choose to go with arduino uno, atmega328 to understand how this peripheral works…

in atmega328, the TWI peripheral is standalone not like attiny44A where ATMEL combined two wire mode (TWI/I2C) with three wire mode (SPI) into one peripheral

26.3.3 Address Packet Format:

  • nine bits long
    • 7 address bits
    • 1 Read/Write control bit
    • 1 ACK bit (slave pull this line down)

registers:

  • TWBR - Two wire Baud Rate: in this register you select baud rate. its calculated with the following equation: F_CPU/(16+2*TWBR*4^TWPS)

where TWPS is prescaler that can be set in TWSR.

  • TWSR - Two wire Status resgister:

    • bits [0:1] -> prescaler
    • bits [3:7] -> Status bits
    • bit [2] -> reserved
  • TWAR - Two wire Address register: this bit you load it with the address of the slave (in slave mode) so when the master sends to the node it sends ACK and can respond…

  • TWDR - Two wire Data register: In Transmit mode, TWDR contains the next byte to be transmitted. In Receive mode, the TWDR contains the last byte received.

  • TWCR - Two wire Control resgister:

    • bit [7] TWINT TWI Interrupt Flag -> This bit is set by hardware when the TWI has finished its current job and expects application software response
    • bit [5] TWSTA TWI START Condition -> The application writes the TWSTA bit to one when it desires to become a master on the 2-wire serial bus
    • bit [4] TWSTO TWI STOP Condition -> Writing the TWSTO bit to one in Master mode will generate a STOP condition on the 2-wire serial bus
    • bit [2] TWEN TWI Enable -> enables TWI operation and activates the TWI interface. When TWEN is written to one, the TWI takes control over the I/O pins connected to the SCL and SDA pins.

code:

im using ATMEL STUDIO, after compiling im uploading with AVRDUDE


/*
    I2C master
    sends ‘1’ and ‘0’ to slave address 10
    and delay in-between (~1sec)
*/

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <util/twi.h>

int main(void)
{
    char c = '1';
    TWSR &= 0xF8; //set TWPS[1:0] to 0 -&gt; 1 prescaler value
    TWBR = 0x30; //10khz @ 16mhz & 1 prescaler

    while (1) 
    {

        //start TWI:
        TWCR = (1 << TWINT)|(1 << TWSTA)|(1 << TWEN); //set enable , reset int, set start
        while(!(TWCR & (1 << TWINT))); //wait for int flag
        if ( (TWSR & 0xf8) != TW_START)
            PORTB ^= 1 << PB5; //error dbg toggle led

        //send to address 10
        TWDR = 10  <<  1; //to send to addr 10 (address 7 bit mode + 0 write mode)
        TWCR = (1 <<  TWINT | 1 << TWEN); //start send
        while(!(TWCR & (1 << TWINT))); //wait for int flag
        if ((TWSR & 0xf8) != TW_MT_SLA_ACK ) 
            PORTB ^= 1 << PB5; //error dbg toggle led

        //send data (char c)
        TWDR = c;
        if (c == '1')
            c= '0';
        else
            c= '1';
        TWCR = (1 <<  TWINT | 1 << TWEN); //start send
        while(!(TWCR & (1 << TWINT))); //wait for int flag
        if ((TWSR & 0xf8) != TW_MT_DATA_ACK )
                PORTB ^= 1 << PB5; //error dbg toggle led


        //send stop condition
        TWCR = (1&lt &lt TWINT) | (1 &lt &ltTWEN) | (1  << TWSTO);
       for (int i=0; i<10; i++)
        _delay_ms(100);
    }

}

after uploading the code, i found out it works exactly as the simpler arduino version. so i was happy to implement master i2c through the registers and understand the details of the TWI peripheral.


references:

I2C

to implement the code into fab boards, i decided to go for Attiny boards where they have i2c and spi. you can find sample code in microchip documentation of attiny44. However, the code is written for IAR not gcc the code was edited to be compiled by gcc also and can be found here:

https://github.com/puuu/USIWire/tree/master/src

Master Code:


// This file has been prepared for Doxygen automatic documentation generation.
/*! \file ********************************************************************
*
* Atmel Corporation
*
* File              : USI_TWI_Slave.c
* Compiler          : IAR EWAAVR 3.20c
* Revision          : $Revision: 1.14 $
* Date              : $Date: Friday, December 09, 2005 17:25:38 UTC $
* Updated by        : $Author: jtyssoe $
****************************************************************************/
#define F_CPU 20000000UL

#include "avr/io.h"
#include "avr/interrupt.h"
#include "avr/cpufunc.h"
#include 

#include "USI_TWI_Master.h"

//SLAVE ADDR
#define SLAVE_ADDR 0x10


unsigned char TWI_Act_On_Failure_In_Last_Transmission(unsigned char TWIerrorMsg)
{
    asm("nop");
    return TWIerrorMsg;
}

int main(  )
{
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

  unsigned char temp=0;
  unsigned char TWI_slaveAddress = 0x10;
  //unsigned char TWI_targetSlaveAddress;
  unsigned char messageBuf[2];
    messageBuf[0] = (TWI_slaveAddress << TWI_ADR_BITS) | (FALSE << TWI_READ_BIT);
    DDRB = 1<<2;
    PORTB ^= 1<<2;
  
    USI_TWI_Master_Initialise();
    sei();

    while(1)
    {
            _delay_ms(1000);
                if (messageBuf[1] != '1')
                    messageBuf[1] = '1';      // The first byte is used for commands.
                else
                    messageBuf[1] = '0';
                PORTB ^= 1<<2;
                temp = USI_TWI_Start_Transceiver_With_Data(messageBuf, 2);  
                
                if (!temp) // operation failed.
                {
                    TWI_Act_On_Failure_In_Last_Transmission(USI_TWI_Get_State_Info());
                asm("nop"); // Put own code here.
                }
    }

    return 0;
}
  

/*
  usi_io.h - definitions for TWI/I2C via USI
  Copyright (c) 2017 Puuu.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 3.0 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef USI_IO_h
#define USI_IO_h

#include <avr/io.h>

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) \
    || defined(__AVR_ATtiny84__)
#  define DDR_USI DDRA
#  define PORT_USI PORTA
#  define PIN_USI PINA
#  define PORT_USI_SDA PORTA6
#  define PORT_USI_SCL PORTA4
#  define PIN_USI_SDA PINA6
#  define PIN_USI_SCL PINA4
#  define USI_START_VECTOR USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVF_vect
#endif
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) \
    || defined(__AVR_ATtiny85__) \
    || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
#  define DDR_USI DDRB
#  define PORT_USI PORTB
#  define PIN_USI PINB
#  define PORT_USI_SDA PORTB0
#  define PORT_USI_SCL PORTB2
#  define PIN_USI_SDA PINB0
#  define PIN_USI_SCL PINB2
#  define USI_START_VECTOR USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVF_vect
#endif
#if defined(__AVR_ATtiny26__)
#  define DDR_USI DDRB
#  define PORT_USI PORTB
#  define PIN_USI PINB
#  define PORT_USI_SDA PORTB0
#  define PORT_USI_SCL PORTB2
#  define PIN_USI_SDA PINB0
#  define PIN_USI_SCL PINB2
#  define USI_START_VECTOR USI_STRT_vect
#  define USI_OVERFLOW_VECTOR USI_OVF_vect
#endif
#if defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__)
#  define DDR_USI DDRB
#  define PORT_USI PORTB
#  define PIN_USI PINB
#  define PORT_USI_SDA PORTB5
#  define PORT_USI_SCL PORTB7
#  define PIN_USI_SDA PINB5
#  define PIN_USI_SCL PINB7
#  define USI_START_VECTOR USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
#endif
#if defined( __AVR_ATtiny261__ ) || defined( __AVR_ATtiny461__ ) \
    || defined( __AVR_ATtiny861__ )
#  define DDR_USI DDRB
#  define PORT_USI PORTB
#  define PIN_USI PINB
#  define PORT_USI_SDA PB0
#  define PORT_USI_SCL PB2
#  define PIN_USI_SDA PINB0
#  define PIN_USI_SCL PINB2
#  define USI_START_VECTOR USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVF_vect
#endif
#if defined(__AVR_ATtiny43U__)
#  define DDR_USI DDRB
#  define PORT_USI PORTB
#  define PIN_USI PINB
#  define PORT_USI_SDA PORTB4
#  define PORT_USI_SCL PORTB6
#  define PIN_USI_SDA PINB4
#  define PIN_USI_SCL PINB6
#  define USI_START_VECTOR USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVF_vect
#endif
#if defined(__AVR_ATtiny1634__)
#  define DDR_USI DDRB
#  define PORT_USI PORTB
#  define PIN_USI PINB
#  define DDR_USI_CL DDRC
#  define PORT_USI_CL PORTC
#  define PIN_USI_CL PINC
#  define PORT_USI_SDA PORTB1
#  define PORT_USI_SCL PORTC1
#  define PIN_USI_SDA PINB1
#  define PIN_USI_SCL PINC1
#  define USI_START_VECTOR USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVF_vect
#endif
#if defined(__AVR_ATmega165__) || defined(__AVR_ATmega165P__) \
    || defined(__AVR_ATmega165PA__) \
    || defined(__AVR_ATmega169__) || defined(__AVR_ATmega169P__) \
    || defined(__AVR_ATmega169PA__) \
    || defined(__AVR_ATmega645__) || defined(__AVR_ATmega6450__) \
    || defined(__AVR_ATmega649__) || defined(__AVR_ATmega6490__) \
    || defined(__AVR_ATmega649P__) \
    || defined(__AVR_ATmega325__) || defined(__AVR_ATmega325PA__) \
    || defined(__AVR_ATmega3250__) || defined(__AVR_ATmega3250PA__) \
    || defined(__AVR_ATmega329__) || defined(__AVR_ATmega329P__) \
    || defined(__AVR_ATmega3290PA__) || defined(__AVR_ATmega3290__)
#  define DDR_USI DDRE
#  define PORT_USI PORTE
#  define PIN_USI PINE
#  define PORT_USI_SDA PORTE5
#  define PORT_USI_SCL PORTE4
#  define PIN_USI_SDA PINE5
#  define PIN_USI_SCL PINE4
#  define USI_START_VECTOR USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
#endif

#ifndef USI_START_COND_INT
#  define USI_START_COND_INT USISIF
#endif

#ifndef DDR_USI_CL
#  define DDR_USI_CL DDR_USI
#  define PORT_USI_CL PORT_USI
#  define PIN_USI_CL PIN_USI
#endif

#endif

  

/*****************************************************************************
*
* Atmel Corporation
*
* File              : USI_TWI_Master.c
* Date              : $Date: 2016-7-15 $
* Updated by        : $Author: Atmel $
*
* Support mail      : avr@atmel.com
*
* Supported devices : All device with USI module can be used.
*                     The example is written for the ATmega169, ATtiny26 and ATtiny2313
*
* AppNote           : AVR310 - Using the USI module as a TWI Master
*
* Description       : This is an implementation of an TWI master using
*                     the USI module as basis. The implementation assumes the AVR to
*                     be the only TWI master in the system and can therefore not be
*                     used in a multi-master system.
* Usage             : Initialize the USI module by calling the USI_TWI_Master_Initialise()
*                     function. Hence messages/data are transceived on the bus using
*                     the USI_TWI_Transceive() function. The transceive function
*                     returns a status byte, which can be used to evaluate the
*                     success of the transmission.
*
****************************************************************************/
#if __GNUC__
#include 
#else
#include 
#include 
#endif
#include "USI_TWI_Master.h"

unsigned char USI_TWI_Master_Transfer(unsigned char);
unsigned char USI_TWI_Master_Stop(void);

union USI_TWI_state {
    unsigned char errorState; // Can reuse the TWI_state for error states due to that it will not be need if there
                              // exists an error.
    struct {
        unsigned char addressMode : 1;
        unsigned char masterWriteDataMode : 1;
        unsigned char unused : 6;
    };
} USI_TWI_state;

/*---------------------------------------------------------------
 USI TWI single master initialization function
---------------------------------------------------------------*/
void USI_TWI_Master_Initialise(void)
{
    PORT_USI |= (1 << PIN_USI_SDA); // Enable pullup on SDA, to set high as released state.
    PORT_USI_CL |= (1 << PIN_USI_SCL); // Enable pullup on SCL, to set high as released state.

    DDR_USI_CL |= (1 << PIN_USI_SCL); // Enable SCL as output.
    DDR_USI |= (1 << PIN_USI_SDA); // Enable SDA as output.

    USIDR = 0xFF;                                           // Preload dataregister with "released level" data.
    USICR = (0 << USISIE) | (0 << USIOIE) |                 // Disable Interrupts.
            (1 << USIWM1) | (0 << USIWM0) |                 // Set USI in Two-wire mode.
            (1 << USICS1) | (0 << USICS0) | (1 << USICLK) | // Software stobe as counter clock source
            (0 << USITC);
    USISR = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC) | // Clear flags,
            (0x0 << USICNT0);                                             // and reset counter.
}

/*---------------------------------------------------------------
Use this function to get hold of the error message from the last transmission
---------------------------------------------------------------*/
unsigned char USI_TWI_Get_State_Info(void)
{
    return (USI_TWI_state.errorState); // Return error state.
}

/*---------------------------------------------------------------
 USI Transmit and receive function. LSB of first byte in data
 indicates if a read or write cycles is performed. If set a read
 operation is performed.

 Function generates (Repeated) Start Condition, sends address and
 R/W, Reads/Writes Data, and verifies/sends ACK.

 Success or error code is returned. Error codes are defined in
 USI_TWI_Master.h
---------------------------------------------------------------*/
unsigned char USI_TWI_Start_Transceiver_With_Data(unsigned char *msg, unsigned char msgSize) {
    return USI_TWI_Start_Transceiver_With_Data_Stop(msg, msgSize, TRUE);
}

/*---------------------------------------------------------------
 USI Transmit and receive function.

 Same as USI_TWI_Start_Transceiver_With_Data() but with an additional
 parameter that defines if a Stop Condition should be send at the end
 of the transmission.
---------------------------------------------------------------*/
#ifndef __GNUC__
__x // AVR compiler
#endif
    unsigned char
    USI_TWI_Start_Transceiver_With_Data_Stop(unsigned char *msg, unsigned char msgSize, unsigned char stop)
{
    unsigned char tempUSISR_8bit = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC)
                                   |                 // Prepare register value to: Clear flags, and
                                   (0x0 << USICNT0); // set USI to shift 8 bits i.e. count 16 clock edges.
    unsigned char tempUSISR_1bit = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC)
                                   |                 // Prepare register value to: Clear flags, and
                                   (0xE << USICNT0); // set USI to shift 1 bit i.e. count 2 clock edges.

    USI_TWI_state.errorState  = 0;
    USI_TWI_state.addressMode = TRUE;

#ifdef PARAM_VERIFICATION
    if (msg > (unsigned char *)RAMEND) // Test if address is outside SRAM space
    {
        USI_TWI_state.errorState = USI_TWI_DATA_OUT_OF_BOUND;
        return (FALSE);
    }
    if (msgSize <= 1) // Test if the transmission buffer is empty
    {
        USI_TWI_state.errorState = USI_TWI_NO_DATA;
        return (FALSE);
    }
#endif

#ifdef NOISE_TESTING // Test if any unexpected conditions have arrived prior to this execution.
    if (USISR & (1 << USISIF)) {
        USI_TWI_state.errorState = USI_TWI_UE_START_CON;
        return (FALSE);
    }
    if (USISR & (1 << USIPF)) {
        USI_TWI_state.errorState = USI_TWI_UE_STOP_CON;
        return (FALSE);
    }
    if (USISR & (1 << USIDC)) {
        USI_TWI_state.errorState = USI_TWI_UE_DATA_COL;
        return (FALSE);
    }
#endif

    if (!(*msg
          & (1 << TWI_READ_BIT))) // The LSB in the address byte determines if is a masterRead or masterWrite operation.
    {
        USI_TWI_state.masterWriteDataMode = TRUE;
    }

    /* Release SCL to ensure that (repeated) Start can be performed */
    PORT_USI_CL |= (1 << PIN_USI_SCL); // Release SCL.
    while (!(PIN_USI_CL & (1 << PIN_USI_SCL)))
        ; // Verify that SCL becomes high.
#ifdef TWI_FAST_MODE
    DELAY_T4TWI; // Delay for T4TWI if TWI_FAST_MODE
#else
    DELAY_T2TWI; // Delay for T2TWI if TWI_STANDARD_MODE
#endif

    /* Generate Start Condition */
    PORT_USI &= ~(1 << PIN_USI_SDA); // Force SDA LOW.
    DELAY_T4TWI;
    PORT_USI_CL &= ~(1 << PIN_USI_SCL); // Pull SCL LOW.
    PORT_USI |= (1 << PIN_USI_SDA);  // Release SDA.

#ifdef SIGNAL_VERIFY
    if (!(USISR & (1 << USISIF))) {
        USI_TWI_state.errorState = USI_TWI_MISSING_START_CON;
        return (FALSE);
    }
#endif

    /*Write address and Read/Write data */
    do {
        /* If masterWrite cycle (or inital address tranmission)*/
        if (USI_TWI_state.addressMode || USI_TWI_state.masterWriteDataMode) {
            /* Write a byte */
            PORT_USI_CL &= ~(1 << PIN_USI_SCL);         // Pull SCL LOW.
            USIDR = *(msg++);                        // Setup data.
            USI_TWI_Master_Transfer(tempUSISR_8bit); // Send 8 bits on bus.

            /* Clock and verify (N)ACK from slave */
            DDR_USI &= ~(1 << PIN_USI_SDA); // Enable SDA as input.
            if (USI_TWI_Master_Transfer(tempUSISR_1bit) & (1 << TWI_NACK_BIT)) {
                if (USI_TWI_state.addressMode)
                    USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_ADDRESS;
                else
                    USI_TWI_state.errorState = USI_TWI_NO_ACK_ON_DATA;
                return (FALSE);
            }
            USI_TWI_state.addressMode = FALSE; // Only perform address transmission once.
        }
        /* Else masterRead cycle*/
        else {
            /* Read a data byte */
            DDR_USI &= ~(1 << PIN_USI_SDA); // Enable SDA as input.
            *(msg++) = USI_TWI_Master_Transfer(tempUSISR_8bit);

            /* Prepare to generate ACK (or NACK in case of End Of Transmission) */
            if (msgSize == 1) // If transmission of last byte was performed.
            {
                USIDR = 0xFF; // Load NACK to confirm End Of Transmission.
            } else {
                USIDR = 0x00; // Load ACK. Set data register bit 7 (output for SDA) low.
            }
            USI_TWI_Master_Transfer(tempUSISR_1bit); // Generate ACK/NACK.
        }
    } while (--msgSize); // Until all data sent/received.

    if (stop) {
        USI_TWI_Master_Stop(); // Send a STOP condition on the TWI bus.
    }

    /* Transmission successfully completed*/
    return (TRUE);
}

/*---------------------------------------------------------------
 Core function for shifting data in and out from the USI.
 Data to be sent has to be placed into the USIDR prior to calling
 this function. Data read, will be return'ed from the function.
---------------------------------------------------------------*/
unsigned char USI_TWI_Master_Transfer(unsigned char temp)
{
    USISR = temp;                                          // Set USISR according to temp.
                                                           // Prepare clocking.
    temp = (0 << USISIE) | (0 << USIOIE) |                 // Interrupts disabled
           (1 << USIWM1) | (0 << USIWM0) |                 // Set USI in Two-wire mode.
           (1 << USICS1) | (0 << USICS0) | (1 << USICLK) | // Software clock strobe as source.
           (1 << USITC);                                   // Toggle Clock Port.
    do {
        DELAY_T2TWI;
        USICR = temp; // Generate positve SCL edge.
        while (!(PIN_USI_CL & (1 << PIN_USI_SCL)))
            ; // Wait for SCL to go high.
        DELAY_T4TWI;
        USICR = temp;                   // Generate negative SCL edge.
    } while (!(USISR & (1 << USIOIF))); // Check for transfer complete.

    DELAY_T2TWI;
    temp  = USIDR;                 // Read out data.
    USIDR = 0xFF;                  // Release SDA.
    DDR_USI |= (1 << PIN_USI_SDA); // Enable SDA as output.

    return temp; // Return the data from the USIDR
}

/*---------------------------------------------------------------
 Function for generating a TWI Stop Condition. Used to release
 the TWI bus.
---------------------------------------------------------------*/
unsigned char USI_TWI_Master_Stop(void)
{
    PORT_USI &= ~(1 << PIN_USI_SDA); // Pull SDA low.
    PORT_USI_CL |= (1 << PIN_USI_SCL);  // Release SCL.
    while (!(PIN_USI_CL & (1 << PIN_USI_SCL)))
        ; // Wait for SCL to go high.
    DELAY_T4TWI;
    PORT_USI |= (1 << PIN_USI_SDA); // Release SDA.
    DELAY_T2TWI;

#ifdef SIGNAL_VERIFY
    if (!(USISR & (1 << USIPF))) {
        USI_TWI_state.errorState = USI_TWI_MISSING_STOP_CON;
        return (FALSE);
    }
#endif

    return (TRUE);
}

  

/*****************************************************************************
*
* Atmel Corporation
*
* File              : USI_TWI_Master.h
* Date              : $Date: 2016-7-15 $
* Updated by        : $Author: Atmel $
*
* Support mail      : avr@atmel.com
*
* Supported devices : All device with USI module can be used.
*                     The example is written for the ATmega169, ATtiny26 and ATtiny2313
*
* AppNote           : AVR310 - Using the USI module as a TWI Master
*
* Description       : This is an implementation of an TWI master using
*                     the USI module as basis. The implementation assumes the AVR to
*                     be the only TWI master in the system and can therefore not be
*                     used in a multi-master system.
* Usage             : Initialize the USI module by calling the USI_TWI_Master_Initialise()
*                     function. Hence messages/data are transceived on the bus using
*                     the USI_TWI_Start_Transceiver_With_Data() function. If the transceiver
*                     returns with a fail, then use USI_TWI_Get_Status_Info to evaluate the
*                     couse of the failure.
*
****************************************************************************/
#if __GNUC__
#ifndef F_CPU
#define F_CPU 4000000
#endif
#include 
#include 
#endif
//********** Defines **********//
// Defines controlling timing limits
#define TWI_FAST_MODE

#define SYS_CLK 4000.0 // [kHz]

#ifdef TWI_FAST_MODE                            // TWI FAST mode timing limits. SCL = 100-400kHz
#define T2_TWI ((SYS_CLK * 1300) / 1000000) + 1 // >1,3us
#define T4_TWI ((SYS_CLK * 600) / 1000000) + 1  // >0,6us

#else                                           // TWI STANDARD mode timing limits. SCL <= 100kHz
#define T2_TWI ((SYS_CLK * 4700) / 1000000) + 1 // >4,7us
#define T4_TWI ((SYS_CLK * 4000) / 1000000) + 1 // >4,0us
#endif

// Defines controling code generating
//#define PARAM_VERIFICATION
//#define NOISE_TESTING
#define SIGNAL_VERIFY

// USI_TWI messages and flags and bit masks
//#define SUCCESS   7
//#define MSG       0
/****************************************************************************
  Bit and byte definitions
****************************************************************************/
#define TWI_READ_BIT 0 // Bit position for R/W bit in "address byte".
#define TWI_ADR_BITS 1 // Bit position for LSB of the slave address bits in the init byte.
#define TWI_NACK_BIT 0 // Bit position for (N)ACK bit.

#define USI_TWI_NO_DATA 0x00           // Transmission buffer is empty
#define USI_TWI_DATA_OUT_OF_BOUND 0x01 // Transmission buffer is outside SRAM space
#define USI_TWI_UE_START_CON 0x02      // Unexpected Start Condition
#define USI_TWI_UE_STOP_CON 0x03       // Unexpected Stop Condition
#define USI_TWI_UE_DATA_COL 0x04       // Unexpected Data Collision (arbitration)
#define USI_TWI_NO_ACK_ON_DATA 0x05    // The slave did not acknowledge  all data
#define USI_TWI_NO_ACK_ON_ADDRESS 0x06 // The slave did not acknowledge  the address
#define USI_TWI_MISSING_START_CON 0x07 // Generated Start Condition not detected on bus
#define USI_TWI_MISSING_STOP_CON 0x08  // Generated Stop Condition not detected on bus

// Device dependant defines
#if __GNUC__
#include "usi_io.h"
#else //__GNUC__
#if defined(__AT90Mega169__) || defined(__ATmega169__) || defined(__AT90Mega165__) || defined(__ATmega165__)           \
    || defined(__ATmega325__) || defined(__ATmega3250__) || defined(__ATmega645__) || defined(__ATmega6450__)          \
    || defined(__ATmega329__) || defined(__ATmega3290__) || defined(__ATmega649__) || defined(__ATmega6490__)
#define DDR_USI DDRE
#define PORT_USI PORTE
#define PIN_USI PINE
#define PORT_USI_SDA PORTE5
#define PORT_USI_SCL PORTE4
#define PIN_USI_SDA PINE5
#define PIN_USI_SCL PINE4
#endif

#if defined(__ATtiny25__) || defined(__ATtiny45__) || defined(__ATtiny85__) || defined(__AT90Tiny26__)                 \
    || defined(__ATtiny26__)
#define DDR_USI DDRB
#define PORT_USI PORTB
#define PIN_USI PINB
#define PORT_USI_SDA PORTB0
#define PORT_USI_SCL PORTB2
#define PIN_USI_SDA PINB0
#define PIN_USI_SCL PINB2
#endif

#if defined(__AT90Tiny2313__) || defined(__ATtiny2313__)
#define DDR_USI DDRB
#define PORT_USI PORTB
#define PIN_USI PINB
#define PORT_USI_SDA PORTB5
#define PORT_USI_SCL PORTB7
#define PIN_USI_SDA PINB5
#define PIN_USI_SCL PINB7
#endif
#ifndef DDR_USI_CL
#define DDR_USI_CL DDR_USI
#define PORT_USI_CL PORT_USI
#define PIN_USI_CL PIN_USI
#endif
#endif //__GNUC__

// General defines
#define TRUE 1
#define FALSE 0

#if __GNUC__
#define DELAY_T2TWI (_delay_us(T2_TWI / 4))
#define DELAY_T4TWI (_delay_us(T4_TWI / 4))
#else
#define DELAY_T2TWI (__delay_cycles(T2_TWI))
#define DELAY_T4TWI (__delay_cycles(T4_TWI))
#endif
//********** Prototypes **********//

void USI_TWI_Master_Initialise(void);
#ifndef __GNUC__
__x // AVR compiler
#endif
    unsigned char
    USI_TWI_Start_Transceiver_With_Data_Stop(unsigned char *, unsigned char, unsigned char);
unsigned char USI_TWI_Start_Transceiver_With_Data(unsigned char *, unsigned char);

unsigned char USI_TWI_Get_State_Info(void);

  
  1. Result:

I2C between own circuits

The next step is to do I2C between my own circuits For this step I used arduino ide as it is easier and faster to use and I understand the logic behind it now. The common library I found for I2C using arduino ide is called wire library. After some search turns out wire library cannot be used on attiny micro controllers but one core I found allows that to happen. It is called attiny core and I installed it by following the instructions on this link: attinycore repo

For external pull-up resister I didn’t wanna use resistors on breadboard because it is not neat and there can be errors with connection and wires can be disconnected accidentally so i made vero board with the pullup resistors on it for the I2c communication. img img

For the communication i am using one circuit as slave and one as master with adress. the slaves waits for master command and the led on it reponds to the signal it recieves from master.


        #include < Wire.h >
        //const int buttonPin = PA2;

        void setup() {
        Wire.begin();
        //pinMode(buttonPin, INPUT);
        }


        int data = 0;

        void loop() {
            data = data?0:1;
            //data = digitalRead(buttonPin)?1:0;
            if (data == 1)
            {
              Wire.beginTransmission(6); //the address 
              Wire.write(1);
              Wire.endTransmission();
            }
            else {
              Wire.beginTransmission(6); //the address 
              Wire.write(0);
              Wire.endTransmission();
            }
            delay(1000);
        }
  

        #include < Wire.h >
        #include < SoftwareSerial.h >
        //#define led 8
        #define led 13
        void setup() {
          Wire.onReceive(receiveEvent);
        Wire.begin(6); //address

        pinMode(led, OUTPUT);
        }
        void loop() {
        delay(100);
        }
        void receiveEvent(int howMany) {
        int x = Wire.read();
        if (x == 1)
        {
        digitalWrite(led, HIGH);
        }
        if (x == 0)
        {
        digitalWrite(led, LOW);
        }
        }
  

and this is the result