Exemple #1
0
/** Interrupt handler. */
static void i2c_isr(void) {
  volatile struct i2c_port *p;
  volatile struct i2c_txn_info *t;
  volatile U32 dummy __attribute__ ((unused));
  volatile U32 lines = *AT91C_PIOA_PDSR;
  U32 codr = 0;
  U32 sodr = 0;
  short sensor;

  /* Read the TC0 status register to ack the TC0 timer and allow this
   * interrupt handler to be called again.
   */
  dummy = *AT91C_TC0_SR;

  for (sensor=0; sensor<NXT_N_SENSORS; sensor++) {
    const nx__sensors_pins *pins = nx__sensors_get_pins(sensor);
    p = &i2c_state[sensor];
    t = &(p->txns[p->current_txn]);

    switch (p->bus_state)
      {
      case I2C_OFF:
      case I2C_CONFIG:
        /* Port is OFF or in txn configuration mode, do nothing. */
        break;

      case I2C_RECLOCK0:
        /* First step of reclocking: pull SCL low. */
        codr |= pins->scl;
        p->bus_state = I2C_RECLOCK1;
        break;

      case I2C_RECLOCK1:
        /* Second and last step of reclocking: set SCL high again, and
         * retry transaction.
         */
        sodr |= pins->scl;
        p->bus_state = I2C_SEND_START_BIT0;
        break;

      case I2C_READ_ACK0:
        /* Issue a clock pulse by releasing SCL. */
        sodr |= pins->scl;
        p->bus_state = I2C_READ_ACK1;
        break;

      case I2C_READ_ACK1:
        /* Wait for SCL to go up and let it stabilize. */
        if (lines & pins->scl) {
          p->bus_state = I2C_READ_ACK2;
        }
        break;

      case I2C_READ_ACK2:
        if (lines & pins->sda) {
          /* SDA is still high, this is a ACK fault. Setting
           * transaction status to TXN_STAT_FAILED and sending stop
           * bit.
           */
          i2c_log(" no-ack!\n");

          t->result = TXN_STAT_FAILED;

          /* Always issue a STOP bit after a ACK fault. */
          p->bus_state = I2C_SEND_STOP_BIT0;
          p->txn_state = TXN_STOP;

          /* Bypass remaining sub transactions, leaving the status failed */
          p->n_txns = ++(p->current_txn);
        } else {
          if (p->processed < t->data_size) {
            p->bus_state = I2C_SCL_LOW;
            p->txn_state = TXN_TRANSMIT_BYTE;
          } else {
            t->result = TXN_STAT_SUCCESS;
            p->current_txn++;

            if (t->post_control == I2C_CONTROL_STOP) {
              p->bus_state = I2C_SCL_LOW;
              p->txn_state = TXN_STOP;
            } else {
              p->bus_state = I2C_IDLE;
              p->txn_state = TXN_WAITING;
            }
          }

          /* Pull SCL low to complete the clock pulse. SDA should be
           * released by the slave after that.
           */
          codr |= pins->scl;

          i2c_log(" r-ack.\n");
        }

        break;

      case I2C_WRITE_ACK0:
        /* Release SCL to do a clock pulse. */
        sodr |= pins->scl;
        p->bus_state = I2C_WRITE_ACK1;
        break;

      case I2C_WRITE_ACK1:
        /* Pull SCL low again to complete the clock pulse. */
        codr |= pins->scl;
        p->bus_state = I2C_WRITE_ACK2;
        break;

      case I2C_WRITE_ACK2:
        /* Release SDA for the slave to regain control of it. */
        sodr |= pins->sda;
        p->bus_state = I2C_SCL_LOW;
        p->txn_state = TXN_TRANSMIT_BYTE;

        i2c_log(" w-ack.\n");
        break;

      case I2C_IDLE:
        /* If current_txn < n_txns, we have work to do. */
        if (p->txn_state == TXN_WAITING && p->current_txn < p->n_txns) {
          if (t->pre_control == I2C_CONTROL_NONE) {
            p->txn_state = TXN_TRANSMIT_BYTE;
            p->bus_state = I2C_SCL_LOW;
          } else {
            /* Before issuing a START bit, set both pins high, just to be
             * sure, and proceed to SEND_START_BIT.
             */
            sodr |= pins->sda | pins->scl;

            if (t->pre_control == I2C_CONTROL_RESTART && p->lego_compat) {
              /* In LEGO compatibility mode, issue a reclock before the
               * restart (which is just a new START bit).
               */
              p->bus_state = I2C_RECLOCK0;
            } else {
              p->bus_state = I2C_SEND_START_BIT0;
            }

            p->txn_state = TXN_START;
          }

          /* Prepare the first bit to be sent. */
          p->processed = 0;
          p->current_byte = t->data[p->processed];
          p->current_pos = 7;
        }

        if (p->current_txn >= p->n_txns) {
          p->txn_state = TXN_NONE;
        }

        break;

      case I2C_PAUSE:
        p->p_ticks--;
        if (p->p_ticks == 0) {
          p->bus_state = p->p_next;
        }
        break;

      case I2C_SEND_START_BIT0:
        if (lines & pins->sda) {
          /* Pull SDA low. */
          codr |= pins->sda;

          i2c_set_bus_state(sensor, I2C_SEND_START_BIT1);
        } else {
          /* Something is holding SDA low. Reclock until we get our data
           * line back.
           */
          p->bus_state = I2C_RECLOCK0;
        }

        break;

      case I2C_SEND_START_BIT1:
        /* Pull SCL low. */
        codr |= pins->scl;

        i2c_set_bus_state(sensor, I2C_SCL_LOW);
        p->txn_state = TXN_TRANSMIT_BYTE;
        break;

      case I2C_SCL_LOW:
        /* SCL is low. */

        switch (p->txn_state) {
        case TXN_TRANSMIT_BYTE:
          /* In write mode, it's time to set SDA to the bit
           * value we want. In read mode, let the remote device set
           * SDA.
           */
          if (t->mode == TXN_MODE_WRITE) {
            if ((p->current_byte & (1 << p->current_pos))) {
              sodr |= pins->sda;
              i2c_log_uint(1);
            } else {
              codr |= pins->sda;
              i2c_log_uint(0);
            }
          }

          p->bus_state = I2C_SAMPLE0;
          break;

        case TXN_WRITE_ACK:
          if (lines & pins->sda) {
            /* SDA is high: the slave has released SDA. Pull it low
             * and reclock.
             */
            codr |= pins->sda;
            p->bus_state = I2C_WRITE_ACK0;
          }

          /* Stay in the same state until the slave release SDA. */
          break;

        case TXN_READ_ACK:
          /* Release SDA and pull SCL low to prepare for the clock
           * pulse.
           */
          sodr |= pins->sda;
          codr |= pins->scl;
          p->bus_state = I2C_READ_ACK0;
          break;

        case TXN_STOP:
          /* Pull SDA low, to be able to release it up after SCL went
           * up.
           */
          codr |= pins->sda;
          i2c_set_bus_state(sensor, I2C_SEND_STOP_BIT0);
          break;

        default:
          break;
        }

        break;

      case I2C_SAMPLE0:
        /* Start sampling, rising SCL. */
        sodr |= pins->scl;
        p->bus_state = I2C_SAMPLE1;
        break;

      case I2C_SAMPLE1:
        /* End sampling.  In write mode, let the remote device read
         * the bit set in I2C_SCL_LOW. In read mode, retrieve SDA
         * value and store it.
         */
        if (t->mode == TXN_MODE_READ) {
          U8 value = (lines & pins->sda) ? 1 : 0;
          t->data[p->processed] |= (value << p->current_pos);
          i2c_log_uint(value);
        }

        p->bus_state = I2C_SAMPLE2;
        break;

      case I2C_SAMPLE2:
        /* Finally, pull SCL low. */
        codr |= pins->scl;

        --p->current_pos;
        if (p->current_pos < 0) {
          p->processed++;
          p->current_pos = 7;

          if (t->mode == TXN_MODE_WRITE) {
            /* In write mode, update the current_byte being
             * processed so it can be send next until we reach
             * data_size. Now, we expect a ACK from the slave.
             */
            if (p->processed < t->data_size) {
              p->current_byte = t->data[p->processed];
            }

            p->txn_state = TXN_READ_ACK;
          } else {
            /* In read mode, we need to give ACK to the slave so it can
             * continue transmission.
             */
            if (p->processed < t->data_size) {
              p->txn_state = TXN_WRITE_ACK;
            } else {
              if (t->post_control == I2C_CONTROL_STOP) {
                p->txn_state = TXN_STOP;
              } else {
                p->bus_state = I2C_IDLE;
                p->txn_state = TXN_WAITING;
              }

              t->result = TXN_STAT_SUCCESS;
              p->current_txn++;
            }
          }
        }

        p->bus_state = I2C_SCL_LOW;
        break;

      case I2C_SEND_STOP_BIT0:
        /* First, rise SCL. */
        sodr |= pins->scl;

        i2c_set_bus_state(sensor, I2C_SEND_STOP_BIT1);
        break;

      case I2C_SEND_STOP_BIT1:
        /* Finally, release SDA. */
        sodr |= pins->sda;

        i2c_set_bus_state(sensor, I2C_IDLE);
        p->txn_state = TXN_WAITING;

        /* When the sub-transaction is done, decrement i2c_txn_count.
         * If it reaches 0, disable the I2C interrupt.
         *
         * TODO: find how to make this a critical code section.
         */
        i2c_txn_count--;
        if (i2c_txn_count == 0)
          *AT91C_TC0_IDR = AT91C_TC_CPCS;

        break;
      }

    /** Update CODR and SODR to reflect changes for this sensor's
     * pins. */
    if (codr)
      *AT91C_PIOA_CODR = codr;
    if (sodr)
      *AT91C_PIOA_SODR = sodr;
  }
}
Exemple #2
0
void i2c_timer_isr_C_function(void)
{
  int i;


  U32 codr = 0;
  U32 sodr = 0;
  U32 oer = 0;
  U32 odr = 0;
  U32 inputs = *AT91C_PIOA_PDSR;

  struct i2c_port_struct *p = i2c_port;

  U32 dummy = *AT91C_TC0_SR;

  i2c_int_count++;
  
  for (i = 0; i < I2C_N_PORTS; i++) {

    i2c_log(i,p->state, inputs & p->sda_pin);
    
    switch (p->state) {
    default:
    case I2C_UNINITIALISED:	// Uninitialised
      break;
    case I2C_IDLE:		// Not in a transaction
      break;
    case I2C_BEGIN:		
      // Start the current partial transaction
      p->pt_begun |= (1 << p->pt_num);
      oer |= p->sda_pin;
      oer |= p->scl_pin;
      
      if(p->current_pt && p->current_pt->nbytes){
        p->data = p->current_pt->data;
        p->nbits = p->current_pt->nbytes * 8;
        p->transmitting = p->current_pt->tx;
        p->ack_slot = 0;
        p->ack_slot_pending = 0;
        p->fault = 0;
        
        if(!p->transmitting)
          *(p->data) = 0;

        if(p->current_pt->restart){
          // Make sure both SDA and SCL are high
          sodr |= p->scl_pin;
          sodr |= p->sda_pin;
          p->state = I2C_RESTART1;
        } else if(p->current_pt->start){
          sodr |= p->sda_pin;
          p->state = I2C_START1;
        }
        else {
          codr |= p->scl_pin;
          p->state = I2C_LOW0;
        }
      }
      else {
        p->state = I2C_IDLE;
      }
      break;
    case I2C_RESTART1:
      // SDA high, take SCL Low
      codr |= p->scl_pin;
      p->state = I2C_START1;
      break;
    case I2C_START1:
      // SDA high, take SCL high
      sodr |= p->scl_pin;
      p->state = I2C_START2;
      break;
    case I2C_START2:		
      if(inputs & p->sda_pin){
        // Take SDA low while SCL is high
        codr |= p->sda_pin;
        p->state = I2C_START3;
      }
      else {
        // SDA was not high, so do a clock
        codr |= p->scl_pin;
        p->state = I2C_START_RECLOCK1;
      }
      break;
    case I2C_START_RECLOCK1:
      codr |= p->scl_pin;
      p->state = I2C_START1;
      break;
    case I2C_START3:
      // Take SCL low
      codr |= p->scl_pin;
      p->state = I2C_LOW0;
      break;
    case I2C_LOW0:
      // SCL is low
      if(p->ack_slot_pending){
        p->ack_slot = 1;
        p->ack_slot_pending = 0;
      }
      else
        p->ack_slot = 0;

      if(p->nbits || p->ack_slot){
        if(p->ack_slot) {
        
          if(p->transmitting)
            odr |= p->sda_pin;
          else
          {
            oer |= p->sda_pin;
            codr |= p->sda_pin;
          }
        
        } else if(!p->transmitting)
          odr |= p->sda_pin;
        else {
          // Transmitting, and not an ack slot so send next bit
          oer |= p->sda_pin;
          p->nbits--;
          if(((*(p->data)) >> (p->nbits & 7)) & 0x01)
            sodr |= p->sda_pin;
          else
            codr |= p->sda_pin;
          
          if((p->nbits & 7) == 0){
            p->data++;
            if(p->nbits || p->transmitting)
              p->ack_slot_pending = 1;
          }
        }
        p->state = I2C_LOW1;
      
      } else if(p->current_pt->stop){
        p->state = I2C_STOP0;
      } else {
        p->current_pt++;
        p->pt_num++;
        sodr |= p->sda_pin;
        p->state = I2C_BEGIN;
      }
      break;
    case I2C_LOW1:
      // Take SCL high
      sodr |= p->scl_pin;
      p->state = I2C_HIGH0;
      break;
    case I2C_HIGH0:
      // Wait for high pulse width
      // If someone else is not holding the pin down, then advance
      if(inputs & p->scl_pin)
        p->state = I2C_HIGH1;
      break;
    case I2C_HIGH1:
      if(p->transmitting && p->ack_slot){
        // Expect ack from slave
        if(inputs & p->sda_pin){
          p->n_fault++;
          p->ack_fail++;
          p->fault=1;
          codr |= p->scl_pin;
          p->state = I2C_STOP0;
        }
        else {
          p->ack_good++;
          codr |= p->scl_pin;        
          p->state = I2C_LOW0;
        }
      }
      
      else {
        // Read pin if needed, then take SCL low
        
        if(!p->transmitting && !p->ack_slot){
          // Receive a bit.
          U8 *d = p->data;
          p->nbits--;
          if(inputs & p->sda_pin)
            *d |= (1 << (p->nbits & 7));
        
          if(p->nbits && ((p->nbits & 7) == 0)){
            p->data++;
            d = p->data;
            p->ack_slot_pending = 1;
            *d = 0;
          }
        }

        codr |= p->scl_pin;        
        p->state = I2C_LOW0;
        
      }
      break;
    case I2C_STOP0:
      // Take SDA low (SCL is already low)
      oer |= p->sda_pin;
      codr |= p->sda_pin;
      p->state = I2C_STOP1;
      break;  
    case I2C_STOP1:
      // Take SCL high
      sodr |= p->scl_pin;
      p->state = I2C_STOP2;
      break;
    case I2C_STOP2:
      // Take SDA pin high
      sodr |= p->sda_pin;
      p->state = I2C_STOP3;
      break;
    case I2C_STOP3:
      if(p->current_pt->last_pt){
        p->state = I2C_IDLE;
      } else {
        p->current_pt++;
        p->pt_num++;
        p->state = I2C_BEGIN;
      }
    }