/*************************************************************************\ ** filename: iic.c ** author: rui ** creat date:2008/05/01 ** version: 1.0 ** modify record: *************************************************************************/ #include "iic.h" #include /****************type define*************************/ typedef unsigned char u_char; enum { FALSE, TRUE, ILLEGAL }; /***************macro define*************************/ #define WAIT_SLAVE(time) {while((time-- > 0) && (!SCL));} //wait slave device #define WRADDR(sevenbits_addr) ((sevenbits_addr << 1) & 0xfe) #define RDADDR(sevenbits_addr) ((sevenbits_addr << 1) | 0x01) /************************iic driver**************************************/ /** * notice: you should ensure: * 1. setup time of start and stop * 2. setup time of data * 3. bus free time between two operations * * so, always, it must have delay before "scl = 1", it needn't to delay after "scl = 0" * because hold time can be zero. * * you should be warn when bus control right will be changed. * **/ static void start() { SCL = 1; SDA = 1; delay_5us(); SDA = 0; delay_5us(); SCL = 0; } static void send_one_byte(u_char content) { u_char msb, cnt,tmp; cnt = 8; while (cnt > 0) { msb = (content & 0x80); SDA = ((msb == 0x80) ? 1 : 0); //send from msb to lsb delay_5us(); //setup time of data SCL = 1; tmp = 100; WAIT_SLAVE(tmp); delay_5us(); SCL = 0; content = (content << 1); cnt--; } SDA = 1; //give the right of bus control to slave device } static u_char wait_ack() { u_char cnt; cnt = 100; delay_5us(); SCL = 1; WAIT_SLAVE(cnt); delay_5us(); if (SCL != 1) return ILLEGAL; if (SDA == 0) { SCL = 0; return TRUE; } else { SCL = 0; return FALSE; } } static u_char receive_one_byte() { u_char rcv_content, cnt, tmp; cnt = 8; rcv_content = 0; while (cnt > 0) { delay_5us(); //setup time of data SCL = 1; tmp = 100; WAIT_SLAVE(tmp); delay_5us(); rcv_content <<= 1; rcv_content |= ((SDA == 1) ? 0x01 : 0x00); //rcv from msb to lsb SCL = 0; cnt--; } return rcv_content; } static void send_ack() { SDA = 0; delay_5us(); SCL = 1; delay_5us(); SCL = 0; SDA = 1; //release data line for slave device to use } static void send_nack() { SDA = 1; delay_5us(); SCL = 1; delay_5us(); SCL = 0; } static void stop() { SDA = 0; delay_5us(); SCL = 1; delay_5us(); //setup time of stop SDA = 1; delay_5us(); //ensure free time of bus > 4.7us between operation on iic twice } /*****************iic bus level driver*******************************/ static u_char send_data_to_iic(u_char num, u_char *piicdata, u_char flag_start, u_char flag_stop) { if (TRUE == flag_start) { start(); } while (num > 0) { send_one_byte(*piicdata); if (TRUE != wait_ack() ) { stop(); return FALSE; } else { piicdata++; num--; } } if (TRUE == flag_stop) stop(); return TRUE; } static void receive_data_from_iic(u_char num, u_char *paddr_data_save) { while (num > 1) { *paddr_data_save = receive_one_byte(); send_ack(); paddr_data_save++; num--; } *paddr_data_save = receive_one_byte(); send_nack(); stop(); } /***************************device level driver*******************/ u_char write_device_by_iic0(u_char svnbits_dev_addr, u_char *reg_addr, u_char num_addr, u_char *pcontent, u_char num_data) { u_char tmp; u_char addr[3]; addr[0] = (svnbits_dev_addr << 1) & 0xfe; addr[1] = *reg_addr; addr[2] = *(reg_addr + 1); if (2 == num_addr) { tmp = send_data_to_iic(3, (u_char *)addr, TRUE, FALSE); } else if (1 == num_addr) { tmp = send_data_to_iic(2, (u_char *)addr, TRUE, FALSE); } else { return FALSE; } if (FALSE == tmp) return FALSE; tmp = send_data_to_iic(num_data, pcontent, FALSE, TRUE); if (FALSE == tmp) return FALSE; else return TRUE; } u_char read_device_by_iic0(u_char svnbits_dev_addr, u_char *reg_addr, u_char num_addr, u_char *paddr_data_save, u_char num_data) { u_char tmp; u_char addr[3]; addr[0] = (svnbits_dev_addr<< 1) & 0xfe; addr[1] = *reg_addr; addr[2] = *(reg_addr+1); if (2 == num_addr) { tmp = send_data_to_iic(3, (u_char *)addr, TRUE, FALSE); } else if (1 == num_addr) { tmp = send_data_to_iic(2, (u_char *)addr, TRUE, FALSE); } else { return FALSE; } if (FALSE == tmp) return FALSE; addr[0] = (svnbits_dev_addr << 1) | 0x01; tmp = send_data_to_iic(1, (u_char *)addr, TRUE, FALSE); if (FALSE == tmp) return FALSE; receive_data_from_iic(num_data, paddr_data_save); return TRUE; } u_char iic_bus0_initial() { u_char tmp; tmp = 0; SCL = 1; SDA = 1; while (tmp < 9) { SCL = 0; delay_5us(); SCL = 1; delay_5us(); if (SDA == 1) { start(); break; } tmp++; } if (9 == tmp) return FALSE; else { stop(); //here maybe need modify return TRUE; } }