GPIO軟體模擬i2c
阿新 • • 發佈:2022-12-02
結構及巨集定義
struct i2c { unsigned int scl; unsigned int sda; };
#define I2C_ACK 0 /* PD_SDA level to ack a byte */
#define I2C_NOACK 1 /* PD_SDA level to noack a byte */
#define CONFIG_SOFT_I2C_READ_REPEATED_START
各函式詳細實現
clock動作函式
void i2c_scl(struct i2c *i2c,intbit) { gpio_direction_output(i2c->scl, bit); }
data動作函式
這裡需要注意的是,當i2c master想要讓data為高,是把data的gpio設定為輸入,這是因為SCL和SDA都是預設硬體上拉的。同時也是為了master寫完資料以後去獲取ACK。
void i2c_sda(struct i2c *i2c, int bit) { if (bit) { gpio_direction_input(i2c->sda); } else { gpio_direction_output(i2c->sda, bit); } }
獲取data線的狀態
int i2c_read_sda(struct i2c *i2c) { return gpio_get_value(i2c->sda); }
start函式
/*----------------------------------------------------------------------- * START: High -> Low on SDA while SCL is High */ void send_start(struct i2c *i2c) { udelay(5); i2c_sda(i2c,1); udelay(5); i2c_scl(i2c, 1); udelay(5); i2c_sda(i2c, 0); udelay(5); }
stop函式
/*----------------------------------------------------------------------- * STOP: Low -> High on SDA while SCL is High */ static void send_stop(struct i2c *i2c) { i2c_scl(i2c, 0); udelay(5); i2c_sda(i2c, 0); udelay(5); i2c_scl(i2c, 1); udelay(5); i2c_sda(i2c, 1); udelay(5); }
reset函式
/*----------------------------------------------------------------------- * Send a reset sequence consisting of 9 clocks with the data signal high * to clock any confused device back into an idle state. Also send a * <stop> at the end of the sequence for belts & suspenders. */ void send_reset(struct i2c *i2c) { int j; i2c_scl(i2c, 1); i2c_sda(i2c, 1); for (j = 0; j < 9; j++) { i2c_scl(i2c, 0); udelay(5); udelay(5); i2c_scl(i2c, 1); udelay(5); udelay(5); } send_stop(i2c); }
ACK函式
ACK: send_ack(i2c, 0)
NOACK: send_ack(i2c, 1)
/*----------------------------------------------------------------------- * ack should be I2C_ACK or I2C_NOACK */ void send_ack(struct i2c *i2c, int ack) { i2c_scl(i2c, 0); udelay(5); i2c_sda(i2c, ack); udelay(5); i2c_scl(i2c, 1); udelay(5); udelay(5); i2c_scl(i2c, 0); udelay(5); }
i2c寫1byte函式
/*----------------------------------------------------------------------- * Send 8 bits and look for an acknowledgement. */ int write_byte(struct i2c *i2c,unsigned char data) { int j; int nack; for (j = 0; j < 8; j++) { i2c_scl(i2c, 0); udelay(5); i2c_sda(i2c,data & 0x80); udelay(5); i2c_scl(i2c, 1); udelay(5); udelay(5); data <<= 1; } /* * Look for an <ACK>(negative logic) and return it. */ i2c_scl(i2c, 0); udelay(5); i2c_sda(i2c, 1); udelay(5); i2c_scl(i2c, 1); udelay(5); udelay(5); nack = i2c_read_sda(i2c); i2c_scl(i2c, 0); udelay(5); return(nack); /* not a nack is an ack */ }
i2c讀1byte函式
/*----------------------------------------------------------------------- * if ack == I2C_ACK, ACK the byte so can continue reading, else * send I2C_NOACK to end the read. */ static unsigned char read_byte(struct i2c *i2c,int ack) { int data; int j; /* * Read 8 bits, MSB first. */ i2c_sda(i2c, 1); data = 0; for (j = 0; j < 8; j++) { i2c_scl(i2c, 0); udelay(5); i2c_scl(i2c, 1); udelay(5); data <<= 1; data |= i2c_read_sda(i2c); udelay(5); } send_ack(i2c, ack); return(data); }
i2c初始化函式
i2c->scl、i2c->sda分別為clock和data資料線的GPIO號。
/*----------------------------------------------------------------------- * Initialization */ void i2c_init(struct i2c *i2c) { gpio_request(i2c->scl, "soft_i2c"); gpio_request(i2c->sda, "soft_i2c"); gpio_direction_output(i2c->sda, 1); gpio_direction_output(i2c->scl, 1); send_reset(i2c); }
i2c讀len個位元組函式
引數釋義
chip:從裝置地址
addr: 從裝置暫存器地址
alen: 從裝置暫存器地址的長度
buffer: 存放讀取的資料的buffer
len: 讀取len個位元組
/*----------------------------------------------------------------------- * Read bytes */ int i2c_read(struct i2c *i2c, unsigned char chip, unsigned int addr, int alen, unsigned char *buffer, int len) { int shift; #ifdef DEBUG printf("i2c_read: chip %x addr %x alen %d buffer %p len %d\n", chip, addr, alen, buffer, len); #endif /* * Do the addressing portion of a write cycle to set the * chip's address pointer. If the address length is zero, * don't do the normal write cycle to set the address pointer, * there is no address pointer in this chip. */ send_start(i2c); if (alen > 0) { if (write_byte(i2c,chip << 1)) { /* write cycle */ send_stop(i2c); printf("i2c_read, no chip responded %02X\n", chip); return(1); } shift = (alen-1) * 8; while(alen-- > 0) { if(write_byte(i2c,addr >> shift)) { printf("i2c_read, address not <ACK>ed\n"); return(1); } shift -= 8; } /* Some I2C chips need a stop/start sequence here, * other chips don't work with a full stop and need * only a start. Default behaviour is to send the * stop/start sequence. */ #ifdef CONFIG_SOFT_I2C_READ_REPEATED_START send_start(i2c); #else send_stop(i2c); send_start(i2c); #endif } /* * Send the chip address again, this time for a read cycle. * Then read the data. On the last byte, we do a NACK instead * of an ACK(len == 0) to terminate the read. */ write_byte(i2c,(chip << 1) | 1); /* read cycle */ while (len-- > 0) { *buffer++ = read_byte(i2c,len == 0); } send_stop(i2c); return(0); }
i2c寫len個位元組函式
引數釋義
chip:從裝置地址
addr: 從裝置暫存器地址
alen: 從裝置暫存器地址的長度
buffer: 存放要寫入的資料
len: 寫len個位元組
/*----------------------------------------------------------------------- * Write bytes */ int i2c_write(struct i2c *i2c, unsigned char chip,
unsigned int addr, int alen, unsigned char *buffer, int len) { int shift, failures = 0; #ifdef DEBUG printf("i2c_write: chip %x addr %x alen %d buffer %p len %d\n", chip, addr, alen, buffer, len); #endif send_start(i2c); if (write_byte(i2c, chip << 1)) { /* write cycle */ send_stop(i2c); printf("i2c_write, no chip responded %02X\n", chip); return(1); } shift = (alen-1) * 8; while (alen-- > 0) { if (write_byte(i2c, addr >> shift)) { printf("i2c_write, address not <ACK>ed\n"); return(1); } shift -= 8; } while (len-- > 0) { if (write_byte(i2c,*buffer++)) { failures++; } } send_stop(i2c); return(failures); }
i2c探測函式
返回0代表此i2c總線上有地址為addr的裝置
返回1代表此i2c總線上沒偵測到從裝置地址為addr的裝置
int i2c_probe(struct i2c *i2c, unsigned char addr) { int rc; send_start(i2c); rc = write_byte(i2c, (addr << 1) | 0); send_stop(i2c); printf("i2c probe:%d, addr:%x\n", rc, addr); return (rc ? 1 : 0); }