/** 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; } }
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; } }