From 0f508fb424f9fd0ff5071300afe49d9e1e52329d Mon Sep 17 00:00:00 2001 From: Gavan Fantom Date: Thu, 26 May 2011 01:57:16 +0000 Subject: [PATCH] Offload I2C work onto interrupts. For now, we just spin while waiting for the I2C activity to complete. --- i2c.c | 157 ++++++++++++++++++++++++++++++++++++-------------------- i2c.h | 26 +++++++--- main.c | 15 +----- types.h | 2 + wmp.c | 124 +++++++++++++++++++++++++------------------- 5 files changed, 193 insertions(+), 131 deletions(-) diff --git a/i2c.c b/i2c.c index adf5ddf..ea4c693 100644 --- a/i2c.c +++ b/i2c.c @@ -1,5 +1,6 @@ #include "i2c.h" +#include "interrupt.h" #define I2CBASE 0xE001C000 @@ -21,6 +22,11 @@ #define SI (IREG(I2CONSET) & SIFLAG) +void __attribute__((interrupt("IRQ"))) i2c_interrupt_handler(void); + +struct i2c_transaction *i2c_transaction; +int i2c_bytes; + void init_i2c(void) { IREG(I2CONSET) = 0x40; /* Enable I2C ready for Master Tx */ @@ -32,17 +38,7 @@ void init_i2c(void) IWREG(I2SCLL) = 73; /* ~100kHz */ IWREG(I2SCLH) = 73; #endif -} - -int i2cstat; - -int i2c_wait(void) -{ - int stat; - while (!SI); - stat = IREG(I2STAT); - i2cstat = stat; - return stat; + interrupt_register(I2C0, i2c_interrupt_handler); } int i2c_conreg(void) @@ -55,61 +51,110 @@ int i2c_statreg(void) return IREG(I2STAT); } -bool i2c_send_start(void) +bool i2c_busy(void) { - IREG(I2CONCLR) = STOFLAG | STAFLAG; - IREG(I2CONSET) = STAFLAG; - switch (i2c_wait()) { - case 0x08: - case 0x10: - return TRUE; - default: - i2c_send_stop(); - return FALSE; - } + return i2c_transaction != NULL; } -bool i2c_send_address(int addr, bool write) +bool i2c_start_transaction(struct i2c_transaction *t) { - return i2c_send_data((addr<<1) + (write?0:1)); + if (i2c_transaction) + return FALSE; + + i2c_transaction = t; + *(t->result) = I2C_IN_PROGRESS; + + /* Paranoia in case we left things in a bad state */ + IREG(I2CONCLR) = STOFLAG | STAFLAG; + + /* Set it all going */ + IREG(I2CONSET) = STAFLAG; + + return TRUE; } -bool i2c_send_data(unsigned int data) +void __attribute__((interrupt("IRQ"))) i2c_interrupt_handler(void) { - IREG(I2DAT) = data; - IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; - switch (i2c_wait()) { - case 0x18: - case 0x28: - case 0x40: - return TRUE; - default: - i2c_send_stop(); - return FALSE; + int stat = IREG(I2STAT); + + if (!i2c_transaction) { + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + interrupt_clear(); + return; } -} -bool i2c_receive_data(unsigned int *data, bool last) -{ - if (!last) - IREG(I2CONSET) = AAFLAG; - IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG | (last?AAFLAG:0); - switch (i2c_wait()) { - case 0x50: - case 0x58: - *data = IREG(I2DAT); - return TRUE; - default: - i2c_send_stop(); - return FALSE; + switch (stat) { + + case 0x08: /* START transmitted */ + case 0x10: /* repeated START transmitted */ + IREG(I2DAT) = i2c_transaction->address; + IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; + i2c_bytes = 0; + break; + case 0x18: /* SA+W transmitted, ACK received */ + case 0x28: /* data transmitted, ACK received */ + if (i2c_bytes < i2c_transaction->bytes) { + IREG(I2DAT) = i2c_transaction->data[i2c_bytes++]; + IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; + } else { + *(i2c_transaction->result) = I2C_SUCCESS; + if (i2c_transaction->next) { + i2c_transaction = i2c_transaction->next; + i2c_bytes = 0; + IREG(I2CONSET) = STAFLAG; + IREG(I2CONCLR) = STOFLAG | AAFLAG | SIFLAG; + } else { + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + } + } + break; + + case 0x50: /* data received, ACK returned */ + i2c_transaction->data[i2c_bytes++] = IREG(I2DAT); + /* fall through */ + + case 0x40: /* SA+R transmitted, ACK received */ + if (i2c_bytes < (i2c_transaction->bytes-1)) { + IREG(I2CONSET) = AAFLAG; + IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; + } else { + IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG + | AAFLAG; + } + break; + + case 0x58: /* data received, NACK returned */ + i2c_transaction->data[i2c_bytes] = IREG(I2DAT); + *(i2c_transaction->result) = I2C_SUCCESS; + if (i2c_transaction->next) { + i2c_transaction = i2c_transaction->next; + i2c_bytes = 0; + IREG(I2CONSET) = STAFLAG; + IREG(I2CONCLR) = STOFLAG | AAFLAG | SIFLAG; + } else { + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + } + break; + + case 0x20: /* SA+W transmitted, NACK received */ + case 0x30: /* data transmitted, NACK received */ + case 0x48: /* SA+R transmitted, NACK received */ + case 0x38: /* arbitration lost during SA+W or data */ + case 0x00: /* bus error */ + *(i2c_transaction->result) = I2C_FAIL; + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + break; + + /* We don't handle slave mode */ } -} -void i2c_send_stop(void) -{ - IREG(I2CONCLR) = STAFLAG | AAFLAG; - IREG(I2CONSET) = STOFLAG; - IREG(I2CONCLR) = SIFLAG; - /* Don't think we need to wait here. Could be wrong. */ + interrupt_clear(); } diff --git a/i2c.h b/i2c.h index 6746acf..ad49e5d 100644 --- a/i2c.h +++ b/i2c.h @@ -2,16 +2,26 @@ #define __I2C_H #include "types.h" + +#define I2C_IN_PROGRESS 0 +#define I2C_SUCCESS 1 +#define I2C_FAIL 2 + +typedef int i2c_result; + +struct i2c_transaction { + int address; /* i2c first byte, including address and r/w flag */ + int bytes; /* number of bytes to read or write */ + unsigned char *data; /* pointer to the data */ + i2c_result *result; /* pointer to store the result */ + struct i2c_transaction *next; /* NULL or next transaction */ +}; + + void init_i2c(void); -extern int i2cstat; -int i2c_wait(void); -void i2c_go(void); int i2c_conreg(void); int i2c_statreg(void); -bool i2c_send_start(void); -bool i2c_send_address(int addr, bool write); -bool i2c_send_data(unsigned int data); -bool i2c_receive_data(unsigned int *data, bool last); -void i2c_send_stop(void); +bool i2c_start_transaction(struct i2c_transaction *t); +bool i2c_busy(void); #endif /* __I2C_H */ diff --git a/main.c b/main.c index 16fc845..0930754 100644 --- a/main.c +++ b/main.c @@ -160,6 +160,7 @@ void average_sample(void) int main(void) { int i; + init_interrupt(); init_uart(); init_i2c(); @@ -193,8 +194,6 @@ int main(void) { reply("Help is not available. Try a psychiatrist."); break; case 'T': - putstr("I2C status was: "); - puthex(i2cstat); putstr(" I2C status is: "); puthex(i2c_statreg()); reply("."); @@ -202,18 +201,6 @@ int main(void) { puthex(i2c_conreg()); reply("."); break; - case 'S': - putstr("Sending START... "); - if (i2c_send_start()) - reply("OK"); - else - reply("FAIL"); - break; - case 'O': - putstr("Sending STOP... "); - i2c_send_stop(); - reply("sent"); - break; case 'I': putstr("Initialising WMP... "); if (wmp_init()) diff --git a/types.h b/types.h index 6468b8d..bfb4fd1 100644 --- a/types.h +++ b/types.h @@ -5,4 +5,6 @@ typedef int bool; #define TRUE 1 #define FALSE 0 +#define NULL 0 + #endif /* __TYPES_H */ diff --git a/wmp.c b/wmp.c index 44d0e0f..f535c94 100644 --- a/wmp.c +++ b/wmp.c @@ -2,46 +2,77 @@ #include "wmp.h" #include "i2c.h" +unsigned char wmp_init_command[2] = {0xfe, 0x04}; + +i2c_result wmp_result; + +struct i2c_transaction wmp_init_transaction = { + (0x53 << 1) + 0, /* write */ + 2, + wmp_init_command, + &wmp_result, + NULL +}; + +unsigned char wmp_read_cal_command[1] = {0x20}; + +struct i2c_transaction wmp_read_cal_transaction2; + +struct i2c_transaction wmp_read_cal_transaction = { + (0x53 << 1) + 0, /* write */ + 1, + wmp_read_cal_command, + &wmp_result, + &wmp_read_cal_transaction2 +}; + +struct i2c_transaction wmp_read_cal_transaction2 = { + (0x53 << 1) + 1, /* read */ + 0x20, + wmp_calibration_data, + &wmp_result, + NULL +}; + +unsigned char wmp_sample_command[1] = {0x00}; + +unsigned char wmp_sample_data[6]; + +struct i2c_transaction wmp_sample_transaction2; + +struct i2c_transaction wmp_sample_transaction = { + (0x52 << 1) + 0, /* write */ + 1, + wmp_sample_command, + &wmp_result, + &wmp_sample_transaction2 +}; + +struct i2c_transaction wmp_sample_transaction2 = { + (0x52 << 1) + 1, /* read */ + 6, + wmp_sample_data, + &wmp_result, + NULL +}; + + bool wmp_init(void) { - if (!i2c_send_start()) - return FALSE; - if (!i2c_send_address(0x53, TRUE)) - return FALSE; - if (!i2c_send_data(0xfe)) + if (!i2c_start_transaction(&wmp_init_transaction)) return FALSE; - if (!i2c_send_data(0x04)) - return FALSE; - i2c_send_stop(); - return TRUE; + while (i2c_busy()) ; + return (wmp_result == I2C_SUCCESS); } unsigned char wmp_calibration_data[0x20]; bool wmp_read_calibration_data(void) { - int i; - - if (!i2c_send_start()) - return FALSE; - if (!i2c_send_address(0x53, TRUE)) - return FALSE; - if (!i2c_send_data(0x20)) - return FALSE; - i2c_send_stop(); - - if (!i2c_send_start()) + if (!i2c_start_transaction(&wmp_read_cal_transaction)) return FALSE; - if (!i2c_send_address(0x53, FALSE)) - return FALSE; - for (i = 0; i < 0x20; i++) { - unsigned int data; - if (!i2c_receive_data(&data, (i == 0x1f))) - return FALSE; - wmp_calibration_data[i] = data; - } - i2c_send_stop(); - return TRUE; + while (i2c_busy()); + return (wmp_result == I2C_SUCCESS); } unsigned int wmp_yaw; @@ -66,35 +97,22 @@ bool wmp_roll_fast; bool wmp_sample(void) { - int i; - unsigned int b[6]; - - if (!i2c_send_start()) - return FALSE; - if (!i2c_send_address(0x52, TRUE)) + if (!i2c_start_transaction(&wmp_sample_transaction)) return FALSE; - if (!i2c_send_data(0x00)) - return FALSE; - i2c_send_stop(); - if (!i2c_send_start()) - return FALSE; - if (!i2c_send_address(0x52, FALSE)) + while (i2c_busy()); + + if (wmp_result != I2C_SUCCESS) return FALSE; - for (i = 0; i < 6; i++) { - if (!i2c_receive_data(&(b[i]), (i == 5))) - return FALSE; - } - i2c_send_stop(); - wmp_yaw = ((b[3]>>2)<<8) + b[0]; - wmp_pitch = ((b[4]>>2)<<8) + b[1]; - wmp_roll = ((b[5]>>2)<<8) + b[2]; + wmp_yaw = ((wmp_sample_data[3]>>2)<<8) + wmp_sample_data[0]; + wmp_pitch = ((wmp_sample_data[4]>>2)<<8) + wmp_sample_data[1]; + wmp_roll = ((wmp_sample_data[5]>>2)<<8) + wmp_sample_data[2]; /* XXX We don't take into account the fast/slow mode flag here */ - wmp_yaw_fast = !(b[3] & 0x2); - wmp_pitch_fast = !(b[3] & 0x1); - wmp_roll_fast = !(b[4] & 0x2); + wmp_yaw_fast = !(wmp_sample_data[3] & 0x2); + wmp_pitch_fast = !(wmp_sample_data[3] & 0x1); + wmp_roll_fast = !(wmp_sample_data[4] & 0x2); return TRUE; }