Atmel Tiny88 - software I2C master
Software I2C (master)
Microchip Tiny-88 只有一組 TWI master/slave 功能,在系統設計中,Tiny-88 一般做為電源管理,系統開關機,還有和系統主晶片組溝通。Tiny-88 和主晶片組溝通的介面會採用 TWI slave,當還需要再讀取系統溫度,系統電流,系統生產資料或者系統電池時,還需要一組 I2C master 去讀取系統的資料,只能使用 GPIO 來模擬 software I2C.
Software I2C header
宣告程式碼中,需要用到的變數和子函式,使用宣告的好處是日後若要更換成其他組的 GPIO 容易更換,也不容易有遺漏。
- 宣告 GPIO PORT 和接腳位置去模擬 software I2C
- 宣告 I2C 寛度的時間設定值
- 宣告 GPIO High / Low / Input 子函式
/* Constant Variable Define */
#define SW_I2C_DDR DDRD
#define SW_I2C_DIN PIND
#define SW_I2C_DOUT PORTD
#define SW_I2C_SDA 6
#define SW_I2C_SCL 7
/** Bit to or with address for read start and read restart */
#define I2C_READ 1
/** Bit to or with address for write start and write restart */
#define I2C_WRITE 0
/** Delay used for software I2C */
#define I2C_DELAY_USEC 10
#define I2C_DELAY_CLKH 6 /* I2C Clock High Width */
/* I2C Clock Low Width (by instruction run time) */
#define I2C_DELAY_CLKL 0
/* I2C Clock Low Width for finial bit */
#define I2C_DELAY_CLKFL 4
#define I2C_DELAY_DATBR 3 /* I2C Data Delay to before read */
#define I2C_DELAY_DATAR 2 /* I2C Data Delay to after read */
/* Declare Function */
#define SET_I2C_SCL_HI() (SW_I2C_DOUT |= (1 << SW_I2C_SCL))
#define SET_I2C_SCL_LO() (SW_I2C_DOUT &= ~(1 << SW_I2C_SCL))
#define SET_I2C_SDA_HI() (SW_I2C_DOUT |= (1 << SW_I2C_SDA))
#define SET_I2C_SDA_LO() (SW_I2C_DOUT &= ~(1 << SW_I2C_SDA))
#define SET_I2C_SDA_OUT() (SW_I2C_DDR |= (1 << SW_I2C_SDA))
#define SET_I2C_SDA_IN() (SW_I2C_DDR &= ~(1 << SW_I2C_SDA))
#define GET_I2C_SDA_IN() (SW_I2C_DIN & (1 << SW_I2C_SDA))
I2C protocol 分為 start-bit,stop-bit,repeat start-bit,I2C-read,I2C-write 的子函式來組合成主函式。因此,在編寫程式碼前,建議先使用流程圖規劃一下,再開始編寫程式碼,先完成子函式,再組成主函式。
- SW_I2C_start(address):
子函式主要是用在一開始傳送 start-bit 和 slave address,也用於 read protocol 時的 repeat start-bit 只是傳遞的 8-bit 位址的 bit 0 要改為 read.
/* ! \brief SW I2C issue a start condition
* This function use to send start condition of SW I2C
* \note
* (input) slave device address
* \return
* 1 : the address has ACK
* 0 : the address without ACK
*/
uint8_t SW_I2C_start(uint8_t addressRW)
{
uint8_t result;
SET_I2C_SDA_LO(); /* set data to low */
_delay_us(I2C_DELAY_USEC);
SET_I2C_SCL_LO(); /* set clock to low */
_delay_us(I2C_DELAY_CLKFL);
result = SW_I2C_write(addressRW);
return result;
}
- SW_I2C_restart(address) :
子函式用於 read protocol 的 repeat start bit ,需記得更改 slave address 為 read。
/* ! \brief SW I2C issue a re-start condition
* This function use to send re-start condition of SW I2C
* \note
* (input) slave device address
* \return
* 1 : the address has ACK
* 0 : the address without ACK
*/
uint8_t SW_I2C_restart(uint8_t addressRW)
{
SET_I2C_SDA_HI();
SET_I2C_SCL_HI();
_delay_us(I2C_DELAY_USEC);
return SW_I2C_start(addressRW);
}
- SW_I2C_stop(void):
子函式 stop-bit 結束 I2C packet
/* ! \brief SW I2C issue a stop condition
* This function use to send stop condition of SW I2C
* \note
* (input) slave device address
* \return
* 1 : the address has ACK
* 0 : the address without ACK
*/
void SW_I2C_stop(void)
{
SET_I2C_SDA_LO(); /* set data to low */
_delay_us(I2C_DELAY_USEC);
SET_I2C_SCL_HI();
_delay_us(I2C_DELAY_USEC);
SET_I2C_SDA_HI();
_delay_us(I2C_DELAY_USEC);
}
- SW_I2C_read(last):
子函式讀取一個 byte 和回應 ack /nack 給 host
/* ! \brief SW_I2C_read
* This function use to read a byte and send ack/nack to device
* \note
* decide the terminate bit is ACK or NACK.
* \return
* data byte of I2C read
*/
uint8_t SW_I2C_read(uint8_t last)
{
uint8_t b = 0;
/* make sure pull-up enabled */
SET_I2C_SDA_HI();
SET_I2C_SDA_IN(); /* change to input mode */
/* read byte */
for (uint8_t i = 0; i < 8; i++) {
/* do not change this loop unless you verify the change with a
* scope */
_delay_us(I2C_DELAY_CLKFL);
b <<= 1;
SET_I2C_SCL_HI(); /* set clock to high */
_delay_us(I2C_DELAY_DATBR);
if (GET_I2C_SDA_IN())
b |= 1; /* read bit data */
_delay_us(I2C_DELAY_DATAR);
SET_I2C_SCL_LO(); /* set clock to low */
}
/* send Ack or Nak */
SET_I2C_SDA_OUT();
if (last)
SET_I2C_SDA_HI();
else
SET_I2C_SDA_LO();
SET_I2C_SCL_HI(); /* set clock to high */
_delay_us(I2C_DELAY_CLKH);
SET_I2C_SCL_LO(); /* set clock to low */
SET_I2C_SDA_LO();
return b;
}
- SW_I2C_write(data):
子函式寫入一個 byte 並讀回 slave 的 ack/nack
/* ! \brief SW_I2C_write
* This function use to write a byte to device
* \note
* (input) data byte
* \return
* 1 : slave return ACK
* 0 : slave return NACK
*/
uint8_t SW_I2C_write(uint8_t data)
{
uint8_t rtn;
/* write byte */
for (uint8_t m = 0x80; m != 0; m >>= 1) {
/* don't change this loop unless you verify the change with a
* scope */
if (m & data)
SET_I2C_SDA_HI();
else
SET_I2C_SDA_LO();
SET_I2C_SCL_HI(); /* set clock to high */
_delay_us(I2C_DELAY_CLKH);
SET_I2C_SCL_LO(); /* set clock to low */
_delay_us(I2C_DELAY_CLKL);
}
_delay_us(I2C_DELAY_CLKFL);
SET_I2C_SCL_HI();
_delay_us(I2C_DELAY_DATBR);
/* get Ack or Nak */
SET_I2C_SDA_IN(); /* change to input mode */
rtn = GET_I2C_SDA_IN();
_delay_us(I2C_DELAY_DATAR);
SET_I2C_SCL_LO(); /* set clock to low */
_delay_us(I2C_DELAY_CLKFL);
SET_I2C_SDA_OUT();
SET_I2C_SDA_LO(); /* set data to low */
return rtn == 0;
}
- SW_I2C_buffer_read:
主函式完成 I2C read byte 功能,包含 start-bit,I2C-slave address,I2C-command,repeat start-bit,address,I2C-data buffer,stop-bit 完整的 I2C read protocol.
/* ! \brief SW I2C read protocol
* This function use to SW I2C read protocol
* \note
* (input) address-slave device address
* (input) cmd-read command
* (input) buf-read protocol buffer
* (input) count-read protocol byte count
* \return
* 1 : success of read protocol
* 0 : fail of read protocol
*/
uint8_t SW_I2C_buffer_read(uint8_t address, uint8_t cmd, uint8_t *buf,
uint8_t count)
{
cli();
/* issue a start condition, send device address and write direction
* bit */
if (!SW_I2C_start(address | I2C_WRITE)) {
SW_I2C_stop();
sei();
return FALSE;
}
/* send the DS1307 address */
if (!SW_I2C_write(cmd)) {
SW_I2C_stop();
sei();
return FALSE;
}
/* issue a repeated start condition, send device address and read
* direction bit */
if (!SW_I2C_restart(address | I2C_READ)) {
SW_I2C_stop();
sei();
return FALSE;
}
/* read data from the DS1307 */
for (uint8_t i = 0; i < count; i++) {
/* send Ack until last byte then send Ack */
buf[i] = SW_I2C_read(i == (count - 1));
}
/* issue a stop condition */
SW_I2C_stop();
sei();
return TRUE;
}
- SW_I2C_buffer_write:
主函式完成 I2C read write 功能,包含 start-bit,I2C-slave address,I2C-command,I2C-data buffer,stop-bit 完整的 I2C write protocol.
/* ! \brief SW I2C write protocol
* This function use to SW I2C write protocol
* \note
* (input) address-slave device address
* (input) cmd-write command
* (input) buf-write protocol buffer
* (input) count-write protocol byte count
* \return
* 1 : success of write protocol
* 0 : fail of write protocol
*/
uint8_t SW_I2C_buffer_write(uint8_t address, uint8_t cmd, uint8_t *buf,
uint8_t count)
{
cli();
/* issue a start condition, send device address and write direction
* bit */
if (!SW_I2C_start(address | I2C_WRITE)) {
SW_I2C_stop();
sei();
return FALSE;
}
/* send the DS1307 address */
if (!SW_I2C_write(cmd)) {
SW_I2C_stop();
sei();
return FALSE;
}
/* send data to the DS1307 */
for (uint8_t i = 0; i < count; i++) {
if (!SW_I2C_write(buf[i])) {
SW_I2C_stop();
sei();
return FALSE;
}
}
/* issue a stop condition */
SW_I2C_stop();
sei();
return TRUE;
}
留言
張貼留言