reform

MNT Reform: Open Source Portable Computer
Log (Feed) | Files | Refs (Tags) | README

i2c.c (4520B)


      1 /*
      2   MNT Reform 2.0 Keyboard Firmware
      3   See keyboard.c for Copyright
      4   SPDX-License-Identifier: MIT
      5 */
      6 
      7 #include <util/twi.h>
      8 #include <avr/io.h>
      9 #include <stdlib.h>
     10 #include <avr/interrupt.h>
     11 #include <util/twi.h>
     12 #include <stdbool.h>
     13 #include "i2c.h"
     14 
     15 // Limits the amount of we wait for any one i2c transaction.
     16 // Since were running SCL line 100kHz (=> 10μs/bit), and each transactions is
     17 // 9 bits, a single transaction will take around 90μs to complete.
     18 //
     19 // (F_CPU/SCL_CLOCK)  =>  # of μC cycles to transfer a bit
     20 // poll loop takes at least 8 clock cycles to execute
     21 #define I2C_LOOP_TIMEOUT (9+1)*(F_CPU/SCL_CLOCK)/8
     22 
     23 #define BUFFER_POS_INC() (slave_buffer_pos = (slave_buffer_pos+1)%SLAVE_BUFFER_SIZE)
     24 
     25 volatile uint8_t i2c_slave_buffer[SLAVE_BUFFER_SIZE];
     26 
     27 static volatile uint8_t slave_buffer_pos;
     28 static volatile bool slave_has_register_set = false;
     29 
     30 // Wait for an i2c operation to finish
     31 inline static
     32 void i2c_delay(void) {
     33   uint16_t lim = 0;
     34   while(!(TWCR & (1<<TWINT)) && lim < I2C_LOOP_TIMEOUT)
     35     lim++;
     36 
     37   // easier way, but will wait slightly longer
     38   // _delay_us(100);
     39 }
     40 
     41 // Setup twi to run at 100kHz or 400kHz (see ./i2c.h SCL_CLOCK)
     42 void i2c_master_init(void) {
     43   // no prescaler
     44   TWSR = 0;
     45   // Set TWI clock frequency to SCL_CLOCK. Need TWBR>10.
     46   // Check datasheets for more info.
     47   TWBR = ((F_CPU/SCL_CLOCK)-16)/2;
     48 }
     49 
     50 // Start a transaction with the given i2c slave address. The direction of the
     51 // transfer is set with I2C_READ and I2C_WRITE.
     52 // returns: 0 => success
     53 //          1 => error
     54 uint8_t i2c_master_start(uint8_t address) {
     55   TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTA);
     56 
     57   i2c_delay();
     58 
     59   // check that we started successfully
     60   if ( (TW_STATUS != TW_START) && (TW_STATUS != TW_REP_START))
     61     return 1;
     62 
     63   TWDR = address;
     64   TWCR = (1<<TWINT) | (1<<TWEN);
     65 
     66   i2c_delay();
     67 
     68   if ( (TW_STATUS != TW_MT_SLA_ACK) && (TW_STATUS != TW_MR_SLA_ACK) )
     69     return 1; // slave did not acknowledge
     70   else
     71     return 0; // success
     72 }
     73 
     74 
     75 // Finish the i2c transaction.
     76 void i2c_master_stop(void) {
     77   TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
     78 
     79   uint16_t lim = 0;
     80   while(!(TWCR & (1<<TWSTO)) && lim < I2C_LOOP_TIMEOUT)
     81     lim++;
     82 }
     83 
     84 // Write one byte to the i2c slave.
     85 // returns 0 => slave ACK
     86 //         1 => slave NACK
     87 uint8_t i2c_master_write(uint8_t data) {
     88   TWDR = data;
     89   TWCR = (1<<TWINT) | (1<<TWEN);
     90 
     91   i2c_delay();
     92 
     93   // check if the slave acknowledged us
     94   return (TW_STATUS == TW_MT_DATA_ACK) ? 0 : 1;
     95 }
     96 
     97 // Read one byte from the i2c slave. If ack=1 the slave is acknowledged,
     98 // if ack=0 the acknowledge bit is not set.
     99 // returns: byte read from i2c device
    100 uint8_t i2c_master_read(int ack) {
    101   TWCR = (1<<TWINT) | (1<<TWEN) | (ack<<TWEA);
    102 
    103   i2c_delay();
    104   return TWDR;
    105 }
    106 
    107 void i2c_reset_state(void) {
    108   TWCR = 0;
    109 }
    110 
    111 void i2c_slave_init(uint8_t address) {
    112   TWAR = address << 0; // slave i2c address
    113   // TWEN  - twi enable
    114   // TWEA  - enable address acknowledgement
    115   // TWINT - twi interrupt flag
    116   // TWIE  - enable the twi interrupt
    117   TWCR = (1<<TWIE) | (1<<TWEA) | (1<<TWINT) | (1<<TWEN);
    118 }
    119 
    120 ISR(TWI_vect);
    121 
    122 ISR(TWI_vect) {
    123   uint8_t ack = 1;
    124   switch(TW_STATUS) {
    125     case TW_SR_SLA_ACK:
    126       // this device has been addressed as a slave receiver
    127       slave_has_register_set = false;
    128       break;
    129 
    130     case TW_SR_DATA_ACK:
    131       // this device has received data as a slave receiver
    132       // The first byte that we receive in this transaction sets the location
    133       // of the read/write location of the slaves memory that it exposes over
    134       // i2c.  After that, bytes will be written at slave_buffer_pos, incrementing
    135       // slave_buffer_pos after each write.
    136       if(!slave_has_register_set) {
    137         slave_buffer_pos = TWDR;
    138         // don't acknowledge the master if this memory loctaion is out of bounds
    139         if ( slave_buffer_pos >= SLAVE_BUFFER_SIZE ) {
    140           ack = 0;
    141           slave_buffer_pos = 0;
    142         }
    143         slave_has_register_set = true;
    144       } else {
    145         i2c_slave_buffer[slave_buffer_pos] = TWDR;
    146         BUFFER_POS_INC();
    147       }
    148       break;
    149 
    150     case TW_ST_SLA_ACK:
    151     case TW_ST_DATA_ACK:
    152       // master has addressed this device as a slave transmitter and is
    153       // requesting data.
    154       TWDR = i2c_slave_buffer[slave_buffer_pos];
    155       BUFFER_POS_INC();
    156       break;
    157 
    158     case TW_BUS_ERROR: // something went wrong, reset twi state
    159       TWCR = 0;
    160     default:
    161       break;
    162   }
    163   // Reset everything, so we are ready for the next TWI interrupt
    164   TWCR |= (1<<TWIE) | (1<<TWINT) | (ack<<TWEA) | (1<<TWEN);
    165 }