Skip to content
Snippets Groups Projects
Select Git revision
  • 8756dffade2f6251b869ffeaf6ed2c920aefbedd
  • master default protected
2 results

i2c_master.h

Blame
  • i2c_master.h 8.49 KiB
    #ifndef __I2C_MASTER_H__
    #define __I2C_MASTER_H__
    
    #include <util/atomic.h>
    #include <util/twi.h>
    
    enum i2c_op {
      I2C_R_OP=0x01,
      I2C_W_FLASH_OP,
      I2C_W_RAM_OP,
      I2C_W_1_OP,
      I2C_W_2_OP,
      I2C_W_3_OP,
      I2C_CALL_OP,
      I2C_CHAIN_OP,
      I2C_END_OP,
    };
    
    struct i2c_transcation {
      enum i2c_op op:8;
      union {
        struct i2c_read {
          unsigned char address;
          unsigned char count;
          void volatile *data;
        } read;
        struct i2c_write_flash {
          unsigned char address;
          unsigned char count;
          const volatile unsigned __flash char *data;
        } write_flash;
        struct i2c_write_ram {
          unsigned char address;
          unsigned char count;
          const volatile unsigned char *data;
        } write_ram;
        struct i2c_write {
          unsigned char address;
          const volatile unsigned char data[3];
        } write;
        struct i2c_callback {
          unsigned char (*function)(const volatile void *context);
          const volatile void *context;
        } callback;
        struct i2c_chain {
          const __flash struct i2c_transcation *transaction;
        } chain;
      };
    };
    
    static volatile struct i2c_ctx {
      const __flash struct i2c_transcation *transaction;
    //  unsigned char length;
      unsigned char pos;
      char status;
    } i2c_ctx;
    
    #define I2C_READ(ADDR, COUNT, DATA)                                     \
      {.op=I2C_R_OP,{.read={.address=ADDR,.count=COUNT, .data=DATA}}}
    #define I2C_WRITE_FLASH(ADDR, COUNT, DATA) \
      {.op=I2C_W_FLASH_OP,{.write_flash={.address=ADDR,.count=COUNT,.data=DATA}}}
    #define I2C_WRITE_RAM(ADDR, COUNT, DATA) \
      {.op=I2C_W_RAM_OP,{.write_ram={.address=ADDR,.count=COUNT,.data=DATA}}}
    #define I2C_WRITE_1(ADDR, DATA0) \
      {.op=I2C_W_1_OP,{.write={.address=ADDR,       \
                               .data[0]=DATA0}}}
    #define I2C_WRITE_2(ADDR, DATA0, DATA1)         \
      {.op=I2C_W_2_OP,{.write={.address=ADDR,       \
                               .data[0]=DATA0,      \
                               .data[1]=DATA1}}}
    #define I2C_WRITE_3(ADDR, DATA0, DATA1, DATA2)  \
      {.op=I2C_W_3_OP,{.write={.address=ADDR,       \
                               .data[0]=DATA0,      \
                               .data[1]=DATA1,      \
                               .data[2]=DATA2}}}
    #define I2C_CALLBACK(FUNC, CONTEXT) \
      {.op=I2C_CALL_OP,{.callback={.function=FUNC,.context=CONTEXT}}}
    #define I2C_CHAIN(TRANSACTION) \
      {.op=I2C_CHAIN_OP,{.chain={.transaction=TRANSACTION}}}
    #define I2C_END()                                     \
      {.op=I2C_END_OP,{}}
    
    static unsigned char i2c_chain(
      const  __flash struct i2c_transcation *transaction)
    {
      i2c_ctx.transaction = transaction; 
      i2c_ctx.pos = 0;
      i2c_ctx.status = 0;
      return 0;
    }
    
    static unsigned char i2c_start(
      const  __flash struct i2c_transcation *transaction)
    {
      i2c_chain(transaction);
      TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWIE);
      return 0;
    }
    
    /* Return number of bytes in current transaction */
    static unsigned char i2c_count()
    {
      struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
      switch (tr.op) {
        case I2C_CALL_OP:    return 0; // ERROR
        case I2C_R_OP:       return tr.read.count;
        case I2C_W_FLASH_OP: return tr.write_flash.count;
        case I2C_W_RAM_OP:   return tr.write_ram.count;
        case I2C_W_1_OP:     return 1;
        case I2C_W_2_OP:     return 2;
        case I2C_W_3_OP:     return 3;
        case I2C_CHAIN_OP:   return 0; // ERROR
        case I2C_END_OP:     return 0; // ERROR
      }
      return 0;
    }
    
    static unsigned char i2c_get_data(unsigned char index)
    {
      struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
      switch (tr.op) {
        case I2C_CALL_OP:    return 0; // ERROR
        case I2C_R_OP:       return 0; // ERROR 
        case I2C_W_FLASH_OP: return tr.write_flash.data[index];
        case I2C_W_RAM_OP:   return tr.write_ram.data[index];
        case I2C_W_1_OP:     return tr.write.data[index];
        case I2C_W_2_OP:     return tr.write.data[index];
        case I2C_W_3_OP:     return tr.write.data[index];
        case I2C_CHAIN_OP:   return 0; // ERROR
        case I2C_END_OP:     return 0; // ERROR
      }
      return 0;
    }
    
    static void i2c_put_data(unsigned char index, unsigned char data)
    {
      struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
      switch (tr.op) {
        case I2C_CALL_OP:    break;
        case I2C_R_OP: {
          if (index < tr.read.count) {
            ((unsigned char *)tr.read.data)[index] = data;
          }
        } break;
        case I2C_W_FLASH_OP: break;
        case I2C_W_RAM_OP:   break;
        case I2C_W_1_OP:     break;
        case I2C_W_2_OP:     break;
        case I2C_W_3_OP:     break;
        case I2C_CHAIN_OP:   break;
        case I2C_END_OP:     break;
      }
    }
    static char i2c_callback()
    {
      while (i2c_ctx.status == 0) {
        enum i2c_op op = i2c_ctx.transaction[i2c_ctx.pos].op;
        if (op == I2C_END_OP) {
          break;
        } else if (i2c_ctx.transaction[i2c_ctx.pos].op == I2C_CALL_OP) {
          struct i2c_callback cb = i2c_ctx.transaction[i2c_ctx.pos].callback;
          i2c_ctx.status = cb.function(cb.context);
          if (i2c_ctx.status == 0) {
            i2c_ctx.pos++;
            continue;
          }
        } else if (i2c_ctx.transaction[i2c_ctx.pos].op == I2C_CHAIN_OP) {
          i2c_chain(i2c_ctx.transaction[i2c_ctx.pos].chain.transaction);
          continue;
        } else {
          break;
        } 
      } 
      return i2c_ctx.status;
    }
    
    static char i2c_next()
    {
    #if 0
      putstr("POS: ");
      puthex(i2c_ctx.pos);
      putstr(" OP: ");
      puthex( i2c_ctx.transaction[i2c_ctx.pos].op);
      putstr("\r\n");
    #endif
      if (i2c_ctx.status != 0) {
        return i2c_ctx.status;
      }
    
      if (i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP) {
        i2c_ctx.pos++;
        i2c_callback();
      }
      
      if (i2c_ctx.status != 0) {
        return i2c_ctx.status;
      }
      return i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP;
    }
    
    static char i2c_more(unsigned char index)
    {
      if (i2c_ctx.status != 0) {
        return i2c_ctx.status;
      }
      return index < i2c_count();
    }
    
    static char i2c_idle()
    {
      char result = 1;
      ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        if (i2c_ctx.transaction &&
            i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP) {
          result = 0;
        }
      }
      return result;
    }
    
    SIGNAL(TWI_vect)
    {
    //  unsigned char twsr = TWSR;
      unsigned char twcr = 0x00;
      static unsigned char data_pos = 0x00;
    
      // Consume callback operations
      if (i2c_callback() != 0) {
    //    putstr("Error\r\n");
      }
    
      switch (TW_STATUS) {
        case TW_START:
        case TW_REP_START:
        {
          switch (i2c_ctx.transaction[i2c_ctx.pos].op) {
            case I2C_CALL_OP:
            case I2C_CHAIN_OP:
            case I2C_END_OP:
            {
              // ERROR
            } break;
            case I2C_R_OP: {
              // send SLA+R
              TWDR = (i2c_ctx.transaction[i2c_ctx.pos].read.address << 1) | 1;
            } break;
            case I2C_W_FLASH_OP: {
              // send SLA+W
              TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write_flash.address << 1);
            } break;
            case I2C_W_RAM_OP: {
              // send SLA+W
              TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write_ram.address << 1);
            } break;
            case I2C_W_1_OP:
            case I2C_W_2_OP:
            case I2C_W_3_OP: {
              // send SLA+W
              TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write.address << 1);
            } break;
          }
          twcr = 0;
          data_pos = 0;
        } break;
        case TW_MT_SLA_ACK:
        case TW_MT_DATA_ACK:
        {
          // Send next byte
          if (i2c_more(data_pos)) {
            TWDR = i2c_get_data(data_pos);
            twcr = 0;
            data_pos++;
          } else {
            if (i2c_next()) {
              // Repeated start
              twcr = (1 << TWSTA);
            } else {
              // Done
              twcr = (1 << TWSTO);          
            }
          }
        } break;
        case TW_MR_SLA_ACK:
        {
          if (i2c_more(data_pos + 1)) {
            // Receive byte, send ACK
            twcr = (1<<TWEA);
          } else {
            // Receive last byte, send NOT ACK
            twcr = 0;
          }
        } break;
        case TW_MR_DATA_ACK: 
        case TW_MR_DATA_NACK:
        {
          // Data received:
          i2c_put_data(data_pos, TWDR);
          data_pos++;
          if (i2c_more(data_pos+1)) {
            // Receive byte, send ACK
            twcr = (1<<TWEA);
          } else if (i2c_more(data_pos)) {
            // Receive last byte, send NOT ACK
            twcr = 0;
         } else if (i2c_next()) {
            // Send  repeated START
            twcr = (1 << TWSTA);
          } else {
            // Send STOP
            twcr = (1<<TWSTO);
          }
        } break;
        default: {
          i2c_ctx.status = TW_STATUS | 0x01;
          twcr = (1<<TWSTO);
          // Done
    #if 0
      putstr("TWSR: ");
      puthex(TW_STATUS);
      putstr(" TWCR: ");
      puthex(twcr);
      putstr("\r\n");
    #endif
        } break;
      }
    #if 0
      putstr("TWSR: ");
      puthex(TW_STATUS);
      putstr(" TWCR: ");
      puthex(twcr);
      putstr("\r\n");
    #endif
      TWCR = twcr | (1<<TWINT) | (1<<TWEN) |(1<<TWIE);
    }
    
    #endif