diff options
Diffstat (limited to 'firmware/microblaze/lib/i2c.c')
-rw-r--r-- | firmware/microblaze/lib/i2c.c | 177 |
1 files changed, 2 insertions, 175 deletions
diff --git a/firmware/microblaze/lib/i2c.c b/firmware/microblaze/lib/i2c.c index 177341267..d230f462c 100644 --- a/firmware/microblaze/lib/i2c.c +++ b/firmware/microblaze/lib/i2c.c @@ -20,7 +20,6 @@ #include "memory_map.h" #include "stdint.h" #include <string.h> -#include "pic.h" #include "nonstdio.h" #define MAX_WB_DIV 4 // maximum wishbone divisor (from 100 MHz MASTER_CLK) @@ -37,18 +36,6 @@ static uint16_t prescaler_values[MAX_WB_DIV+1] = { PRESCALER(4), // 4: 25 MHz }; -//asynchronous (interrupt-driven) i2c state variables -volatile uint8_t i2c_buf[17]; //tx/rx data transfer buffer -volatile uint8_t *volatile i2c_bufptr = i2c_buf; //ptr to current position -volatile uint8_t i2c_len = 0; //length remaining in current transfer -volatile i2c_state_t i2c_state = I2C_STATE_IDLE; //current I2C transfer state -i2c_dir_t i2c_dir; //I2C transfer direction - -void (*volatile i2c_callback)(void); //function pointer to i2c callback to be called when transaction is complete - -static void i2c_irq_handler(unsigned irq); -inline void i2c_async_err(void); - void i2c_init(void) { @@ -64,8 +51,8 @@ i2c_init(void) i2c_regs->ctrl = I2C_CTRL_EN; //| I2C_CTRL_IE; // enable core - // FIXME interrupt driven? - pic_register_handler(IRQ_I2C, i2c_irq_handler); + //now this is done separately to maintain common code for async and sync + //pic_register_handler(IRQ_I2C, i2c_irq_handler); } static inline void @@ -140,163 +127,3 @@ i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len) return false; } -static void i2c_irq_handler(unsigned irq) { -//i2c state machine. - - //printf("I2C irq handler\n"); - //first let's make sure nothing is f'ed up - //TODO: uncomment this error checking when we have some way to handle errors -// if(((i2c_regs->cmd_status & I2C_ST_RXACK) != 0) && i2c_dir == I2C_DIR_WRITE) { //we got a NACK and we didn't send it -// printf("\tNACK received\n"); -// i2c_async_err(); -// return; -// }// else printf("\tACK received, proceeding\n"); - - if(i2c_regs->cmd_status & I2C_ST_AL) { - printf("\tArbitration lost!\n"); - i2c_async_err(); - return; - } - - if(i2c_regs->cmd_status & I2C_ST_TIP) { - //printf("\tI2C still busy in interrupt\n"); - return; - } - - //now decide what to do - switch(i2c_state) { - - case I2C_STATE_IDLE: - //this is an error. in idle state, we shouldn't be transferring data, and the fact that the IRQ fired is terrible bad. - printf("AAAAAHHHHH INTERRUPT IN THE IDLE STATE AAAHHHHHHHHH\n"); - i2c_async_err(); - break; - - case I2C_STATE_CONTROL_BYTE_SENT: //here we've sent the control byte, and we're either clocking data in or out now, but we haven't received a byte yet. - case I2C_STATE_DATA: //here we're sending/receiving data and if we're receiving there's data in the data reg - - //if(i2c_state == I2C_STATE_DATA) printf("\tI2C in state DATA with dir=%d and len=%d\n", i2c_dir, i2c_len); - //else printf("\tI2C in state CONTROL_BYTE_SENT with dir=%d and len=%d\n", i2c_dir, i2c_len); - - if(i2c_dir == I2C_DIR_READ) { - if(i2c_state == I2C_STATE_DATA) *(i2c_bufptr++) = i2c_regs->data; - //printf("\tRead %x\n", *(i2c_bufptr-1)); - //set up another data byte - if(i2c_len > 1) //only one more byte to transfer - i2c_regs->cmd_status = I2C_CMD_RD; - else - i2c_regs->cmd_status = I2C_CMD_RD | I2C_CMD_NACK | I2C_CMD_STOP; - } - else if(i2c_dir == I2C_DIR_WRITE) { - //write a byte - //printf("\tWriting %x\n", *i2c_bufptr); - i2c_regs->data = *(i2c_bufptr++); - if(i2c_len > 1) - i2c_regs->cmd_status = I2C_CMD_WR; - else { - //printf("\tGenerating STOP\n"); - i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_STOP; - } - }; - i2c_len--; - if(i2c_len == 0) i2c_state = I2C_STATE_LAST_BYTE; - else i2c_state = I2C_STATE_DATA; //takes care of the addr_sent->data transition - break; - - - case I2C_STATE_LAST_BYTE: //here we've already sent the last read request and the last data is waiting for us. - //printf("\tI2C in state LAST BYTE\n"); - - if(i2c_dir == I2C_DIR_READ) { - *(i2c_bufptr++) = i2c_regs->data; - //printf("\tRead %x\n", *(i2c_bufptr-1)); - i2c_state = I2C_STATE_DATA_READY; - } else { - i2c_state = I2C_STATE_IDLE; - } - i2c_regs->ctrl &= ~I2C_CTRL_IE; //disable interrupts until next time - - if(i2c_callback) { - i2c_callback(); //if we registered a callback, call it! - } - - break; - - - default: //terrible things have happened. - break; - } - -} - -void i2c_register_callback(void (*volatile callback)(void)) { - i2c_callback = callback; -} - -inline void i2c_async_err(void) { - i2c_state = I2C_STATE_IDLE; - i2c_regs->ctrl &= ~I2C_CTRL_IE; - printf("I2C error\n"); -//TODO: set an error flag instead of just dropping things on the floor - i2c_regs->cmd_status = I2C_CMD_STOP; -} - -bool i2c_async_read(uint8_t addr, unsigned int len) { - //printf("Starting async read\n"); - if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle - if(len == 0) return true; //just idiot-proofing - if(len > sizeof(i2c_buf)) return false; - - //disable I2C interrupts and clear pending interrupts on the I2C device - i2c_regs->ctrl &= ~I2C_CTRL_IE; - i2c_regs->cmd_status |= I2C_CMD_IACK; - - i2c_len = len; - i2c_dir = I2C_DIR_READ; - i2c_bufptr = i2c_buf; - //then set up the transfer by issuing the control byte - i2c_regs->ctrl |= I2C_CTRL_IE; - i2c_regs->data = (addr << 1) | 0x01; //7 bit addr and read bit - i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr - //update the state so the irq handler knows what's going on - i2c_state = I2C_STATE_CONTROL_BYTE_SENT; - return true; -} - -bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len) { - //printf("Starting async write\n"); - if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle - if(len > sizeof(i2c_buf)) return false; - - //disable I2C interrupts and clear pending interrupts on the I2C device - i2c_regs->ctrl &= ~I2C_CTRL_IE; - i2c_regs->cmd_status |= I2C_CMD_IACK; - - //copy the buffer into our own if writing - memcpy((void *)i2c_buf, buf, len); - - i2c_len = len; - i2c_dir = I2C_DIR_WRITE; - i2c_bufptr = i2c_buf; - //then set up the transfer by issuing the control byte - i2c_regs->ctrl |= I2C_CTRL_IE; - i2c_regs->data = (addr << 1) | 0x00; //7 bit addr and read bit - i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr - //update the state so the irq handler knows what's going on - i2c_state = I2C_STATE_CONTROL_BYTE_SENT; - - return true; -} - -//TODO: determine if it's better to read sequentially into the user's buffer, copy on transfer complete, or copy on request (shown below). probably best to copy on request. -bool i2c_async_data_ready(void *buf) { - if(i2c_state == I2C_STATE_DATA_READY) { - i2c_state = I2C_STATE_IDLE; - memcpy(buf, (void *)i2c_buf, (i2c_bufptr - i2c_buf)); //TODO: not really comfortable with this - //printf("Copying %d bytes to user buffer\n", i2c_bufptr-i2c_buf); - return true; - } - return false; -} - - |