iic部分源碼:
- #include "ofme_iic.h"
- ////////////////////////////////////////////////////////////////////////////////
- // ofme_iic.c
- // v2012.12.20
- // Copyright(C) ofourme@163.com
- // All rights reserved
- ////////////////////////////////////////////////////////////////////////////////
- // iic 主機模擬程序 for F/S mode
- // 不可重入
- // 所有的函數結束后都是占有scl總線的,故不會有別的主機與之競爭(除非與別的主...
- // 機處于完全同步),但別的主機也可能在程序運行期間加入競爭。
- ////////////////////////////////////////////////////////////////////////////////
- // 詳見《I2C總線規范(周立功翻譯版)》
- // 傳輸格式:P8~P10
- // 同步與仲裁:P10~P11
- // 時序要求:P27~P28
- // tag: 同步發生于主機活躍的所有時間
- // 仲裁發生于主機發送數據的情況,包括ACK位
- ////////////////////////////////////////////////////////////////////////////////
- // 全局變量,用于保存IIC_TIME_OUT等錯誤信息。通過iic_error_check()獲取。
- static int _IIC_ERROR_CODE = IIC_OK;
- ////////////////////////////////////////////////////////////////////////////////
- #ifndef F_SCL
- #err "F_SCL not defined."
- #elif (F_SCL==100)
- ////////////////////////////////////////////////////////////////////////////////
- // (重復)起始條件的保持時間。在這個周期后,產生第一個時間脈沖...
- // 4.0us<t_hd_sta or 0.6us<t_hd_sta
- #define T_HD_STA (4)
- // SCL時鐘的低電平周期 4.7us<t or 1.3us<t
- // SDA should hold at least 300ns while SCL is falling
- #define T_LOW1 (1)
- #define T_LOW2 (4)
- #define T_LOW (T_LOW1+T_LOW2)
- // SCL時鐘的高電平周期 4.0us<t or 0.6us<t
- #define T_HIGH (4)
- // 重復起始條件的建立時間 4.7us<t or 0.6us<t
- #define T_SU_STA (5)
- // 數據保持時間:
- // IIC總線器件:0<t<3.45us or 0<t<0.9us
- // 兼容CUBS的主機:5.0us<t<NULL or NULL<t<NULL
- // SDA should hold at least 300ns while SCL is falling
- #define T_HD_DAT (1)
- // 數據建立時間:250ns<t or 100ns<t
- #define T_SU_DAT (1)
- // 停止條件的建立時間:4.0us<t or 0.6us<t
- #define T_SU_STO (4)
- // 停止和啟動條件之間的總線空閑時間 4.7us<t_buf or 1.3us<t_buf
- #define T_BUF (5)
- ////////////////////////////////////////////////////////////////////////////////
- #elif (F_SCL==400)
- ////////////////////////////////////////////////////////////////////////////////
- #define T_HD_STA (1)
- #define T_LOW1 (1)
- #define T_LOW2 (1)
- #define T_LOW (T_LOW1+T_LOW2)
- #define T_HIGH (1)
- #define T_SU_STA (1)
- #define T_HD_DAT (0)
- #define T_SU_DAT (1)
- #define T_SU_STO (1)
- #define T_BUF (2)
- ////////////////////////////////////////////////////////////////////////////////
- #else
- #err "F_SCL value error."
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- // 提供給iic的延時函數,單位為1微秒。
- #ifndef iic_delay
- #err "iic_delay() not defined."
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- // 主機釋放SCL總線,而且等待外部主機、設備釋放SCL總線。用于SCL同步。
- /* "IIC SCL SYNCHRONIZE." 不屬于異常錯誤,故大寫表示。*/
- #define IIC_SCL_RELEASE(t) \
- {\
- SCL_H();\
- t = 0;\
- while(SCL==0)\
- {\
- t++;\
- if(t==IIC_RAISING_COUNT) IIC_DEBUG("IIC SCL SYNCHRONIZE.\r\n");\
- if(t>=IIC_TIME_OUT_COUNT)\
- {\
- _IIC_ERROR_CODE = IIC_TIME_OUT;\
- IIC_DEBUG("iic scl synchronize time out.\r\n");\
- break;\
- }\
- }\
- }
- ////////////////////////////////////////////////////////////////////////////////
- // 保持時間T的高電平。但是如果總線電平被外部拉低,則提前退出。用于SCL同步。
- #define IIC_SCL_HOLD(T, t) \
- {\
- for(t=0; t<T; t++)\
- {\
- iic_delay(1);\
- if(SCL==0) break;\
- }\
- }
- ////////////////////////////////////////////////////////////////////////////////
- void iic_init(void)
- {
- hw_iic_init(); // 外部函數。配置端口為開漏輸出。
- // _IIC_ERROR_CODE = IIC_OK; // 延后到iic_start()里面設置。
- SCL_H(); // 釋放端口
- SDA_H();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // iic_start()函數在總線忙碌的情況下返回失敗值
- int iic_start(void)
- {
- // 其它主機可能處于<1>start、<2>restart、<3>stop、<4>讀寫SDA且SDA為高電平。
- // 有可能獨占總線或與別的主機同步占有總線,這是我們希望的最好結果。
- // 但iic協議是否有可能導致不同步地占有總線?
- //
- // 程序實際上應該檢查總線在T_BUF期間是否被占用,保證起始標志時序不被打斷,...
- // 但使用軟件查詢方式無法確切認定在延時期間總線電平沒有被外部主機拉低,...
- // 本程序的缺陷有可能導致不同步地占有總線。!
- // 只能寄希望于程序在后面的多主機競爭中失敗退出而避免錯誤了。
- _IIC_ERROR_CODE = IIC_OK;
- SCL_H();
- SDA_H();
- iic_delay(T_BUF+T_BUF_ALT); // 保證SCL與SDA線的高電平維持時間
- if( SCL==0 ) // SCL總線忙
- {
- return IIC_SCL_BUSY;
- }
- if( SDA==0 ) // SDA總線忙
- {
- return IIC_SDA_BUSY;
- }
-
- SDA_L();
- iic_delay(T_HD_STA);
- SCL_L(); // get the SCL & SDA bus
- return IIC_OK;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // 在傳輸完數據后可立即調用iic_restart(),與iic_start()類似。
- void iic_restart(void)
- {
- int t;
- // scl==0
- SDA_H();
- iic_delay(T_LOW);
- IIC_SCL_RELEASE(t);
- iic_delay(T_SU_STA);
- SDA_L();
- iic_delay(T_HD_STA);
- SCL_L(); // get the SCL & SDA bus
- }
- ////////////////////////////////////////////////////////////////////////////////
- void iic_stop(void)
- {
- // scl==0
- SDA_L();
- iic_delay(T_LOW);
- SCL_H(); // release SCL, ignore pulling down by other device.
- iic_delay(T_SU_STO);
- SDA_H(); // release SDA
- }
- ////////////////////////////////////////////////////////////////////////////////
- // 主機接收數據發送ack, 比較響應位進行多主機仲裁,由于ack為低電平,故實際不仲裁
- void iic_ack(void)
- {
- int t;
- // scl==0
- SDA_L(); // ack
- iic_delay(T_LOW);
- IIC_SCL_RELEASE(t); // SCL SYNCHRONIZE
- IIC_SCL_HOLD(T_HIGH, t); // SCL SYNCHRONIZE
- SCL_L(); // get the SCL bus
- }
- ////////////////////////////////////////////////////////////////////////////////
- // 主機接收數據發送nack, 比較響應位進行多主機仲裁
- int iic_nack(void)
- {
- int t;
- // scl==0
- SDA_H(); // nack
- iic_delay(T_LOW);
- IIC_SCL_RELEASE(t); // SCL SYNCHRONIZE
- if(SDA==0)
- { // scl & sda had been released before.
- IIC_DEBUG("iic_nack() arbitrate failed.\r\n");
- // 應該不用再發送時鐘直到nack周期結束?
- return IIC_AARB_FAIL;
- }
- IIC_SCL_HOLD(T_HIGH, t); // SCL SYNCHRONIZE
- SCL_L(); // get the SCL bus
- return IIC_OK;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // 主機發送數據完等待從機響應ack or nack,不進行多主機仲裁
- int iic_wait_ack(void)
- {
- int t, data;
- // scl==0
- SDA_H(); // release SDA
- iic_delay(T_LOW); // wait for SDA to be change
- IIC_SCL_RELEASE(t); // SCL SYNCHRONIZE
- data = SDA;
- IIC_SCL_HOLD(T_HIGH, t); // SCL SYNCHRONIZE
- SCL_L(); // get the SCL bus
- if(data) return IIC_NACK;
- else return IIC_ACK;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // 主機讀取數據,不比較數據位進行多主機仲裁
- u8 iic_read(void)
- {
- u8 d;
- int i, t;
- // sda==0, scl==0;
- SDA_H(); // release SDA, wait for SDA to be change
- for(i=0, d=0; i<8; i++)
- {
- iic_delay(T_LOW);
- IIC_SCL_RELEASE(t); // SCL SYNCHRONIZE
- // read_bit();
- d<<=1;
- if(SDA) d++;
- // 理論上read函數和write函數在這里收發字節的第1位時,應不斷檢測SCL高電平期間,...
- // SDA的電平有無變化以識別restart()或stop()標志,但同時還要檢測SCL有無被外部拉低...
- // 在不使用中斷而采用純粹查詢手段的情況下,實現起來有困難,故不做判斷。
- IIC_SCL_HOLD(T_HIGH, t); // SCL SYNCHRONIZE
- SCL_L(); // get the SCL bus
- }
- return d;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // 主機發送數據,比較數據位進行多主機仲裁
- // 主機在發送數據的第一位且第一位為1時,其它主機可能在SCL高電平期間發送...
- // restart()或是stop()標志,也即電平0->1或是1->0,理論上程序應該檢測這種...
- // 情況的發生,并停止發送數據而發送一樣的restart或是stop標志(見P11)。
- // 為簡化程序,一旦遇到這種情況既轉化為IIC_ARB_FAIL處理。
- int iic_write(u8 data)
- {
- int i, t, err = IIC_OK;
- // sda==0, scl==0;
- for(i=0; i<8; i++, data<<=1)
- {
- iic_delay(T_LOW1);
- // send_bit();
- if(data&0x80)
- SDA_H();
- else
- SDA_L();
- //
- iic_delay(T_LOW2);
- IIC_SCL_RELEASE(t); // SCL SYNCHRONIZE
- if( data&0x80 && (SDA==0) )//仲裁失敗
- { // scl & sda had been released before.
- // 理論上仲裁失敗就由其它主機接管控制器,程序可以停止產生SCL...
- // 在這里我們應該可以直接返回 IIC_DARB_FAIL
- // return IIC_DARB_FAIL;
- // 但我選擇發送0xff直到字節結束
- err = IIC_DARB_FAIL;
- data = 0xFF;
- }
- IIC_SCL_HOLD(T_HIGH, t); // SCL SYNCHRONIZE
- SCL_L(); // get the SCL bus
- }
- return err;
- }
- ////////////////////////////////////////////////////////////////////////////////
- #if 0
- int iic_dev_read(u8 dev, u8 addr, u8* data)
- {
- // 注意將IIC_DEBUG()放iic_stop()后面,以免影響總線時序。
- int i;
- i = iic_start(); // select the device and set address
- if( i != IIC_OK ) goto err_bus_busy;
- i = iic_write(dev);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_dev_fail;
- i = iic_write(addr);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_tar_fail;
- iic_restart();
- i = iic_write(dev|1); // start read
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_dev_fail;
- *data = iic_read();
- i = iic_nack();// write nack to tell the slave stop transfer data.
- if( i != IIC_OK ) goto err_arb_fail;
- //end:
- iic_stop();
- // IIC_DEBUG("R: IIC READ DONE.\r\n");
- if(_IIC_ERROR_CODE & IIC_TIME_OUT)
- {
- IIC_DEBUG("r: iic time out.\r\n");
- return _IIC_ERROR_CODE;
- }
- return IIC_OK;
- err_bus_busy:
- if(i == IIC_SCL_BUSY)
- IIC_DEBUG("r: iic scl bus busy.\r\n");
- else
- IIC_DEBUG("r: iic sda bus busy.\r\n");
- return i | _IIC_ERROR_CODE;
- err_arb_fail:
- // 總線仲裁失敗可能是由于硬件錯誤或是多主機競爭。如果是硬件錯誤,應繼續產生...
- // 時鐘到字節傳輸結束,然后釋放總線?不管怎樣,都不應該再調用iic_stop();
- SDA_H();
- SCL_H();
- IIC_DEBUG("r: iic bus arbitrate failed.\r\n");
- return i | _IIC_ERROR_CODE; // IIC_ARB_FAIL
- err_dev_fail:
- iic_stop();
- IIC_DEBUG("r: iic device not respond.\r\n");
- return IIC_DEVICE_FAIL | _IIC_ERROR_CODE;
- err_tar_fail:
- iic_stop();
- IIC_DEBUG("r: device target not respond.\r\n");
- return IIC_TARGET_FAIL | _IIC_ERROR_CODE;
- }
- #else
- int iic_dev_read(u8 dev, u8 addr, u8* data)
- {
- return iic_dev_gets(dev, addr, data, 1);
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- int iic_dev_gets(u8 dev, u8 addr, u8* data, u16 n)
- {
- int i;
- i = iic_start(); // select the device and set address
- if( i != IIC_OK ) goto err_bus_busy;
- i = iic_write(dev);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_dev_fail;
- i = iic_write(addr);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_tar_fail;
- iic_restart();
- i = iic_write(dev|1); // start read
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_dev_fail;
- if(n<1) n=1;
- while(--n)
- {
- *data++ = iic_read();
- iic_ack();
- }
- *data = iic_read();
- i = iic_nack();// write nack to tell the slave stop transfer data.
- if( i != IIC_OK ) goto err_arb_fail;
- //end:
- iic_stop();
- // IIC_DEBUG("R: IIC READ DONE.\r\n");
- if(_IIC_ERROR_CODE & IIC_TIME_OUT)
- {
- IIC_DEBUG("r: iic time out.\r\n");
- return _IIC_ERROR_CODE;
- }
- return IIC_OK;
- err_bus_busy:
- if(i == IIC_SCL_BUSY)
- IIC_DEBUG("r: iic scl bus busy.\r\n");
- else
- IIC_DEBUG("r: iic sda bus busy.\r\n");
- return i | _IIC_ERROR_CODE;
- err_arb_fail:
- // 總線仲裁失敗可能是由于硬件錯誤或是多主機競爭。如果是硬件錯誤,應繼續產生...
- // 時鐘到字節傳輸結束,然后釋放總線?不管怎樣,都不應該再調用iic_stop();
- SDA_H();
- SCL_H();
- IIC_DEBUG("r: iic bus arbitrate failed.\r\n");
- return i | _IIC_ERROR_CODE; // IIC_ARB_FAIL
- err_dev_fail:
- iic_stop();
- IIC_DEBUG("r: iic device not respond.\r\n");
- return IIC_DEVICE_FAIL | _IIC_ERROR_CODE;
- err_tar_fail:
- iic_stop();
- IIC_DEBUG("r: device target not respond.\r\n");
- return IIC_TARGET_FAIL | _IIC_ERROR_CODE;
- }
- ////////////////////////////////////////////////////////////////////////////////
- #if 0
- int iic_dev_write(u8 dev, u8 addr, u8 data)
- {
- // 注意將IIC_DEBUG()放iic_stop()后面,以免影響總線時序。
- int i;
- i = iic_start();
- if( i != IIC_OK ) goto err_bus_busy;
- i = iic_write(dev);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_dev_fail;
- i = iic_write(addr);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_tar_fail;
- i = iic_write(data);
- if( i != IIC_OK ) goto err_arb_fail;
- // 如果返回IIC_NACK,則不能再繼續往從機寫數據。本函數只寫一字節的數據,故忽略。
- i = iic_wait_ack();
- //end:
- iic_stop();
- // IIC_DEBUG("W: IIC WRITE DONE.\r\n");
- if( i != IIC_ACK)
- IIC_DEBUG("w: IIC DEVICE NO ACK.\r\n");
- if(_IIC_ERROR_CODE & IIC_TIME_OUT)
- {
- IIC_DEBUG("w: iic time out.\r\n");
- return _IIC_ERROR_CODE;
- }
- return IIC_OK;
- err_bus_busy:
- if(i == IIC_SCL_BUSY)
- IIC_DEBUG("w: iic scl bus busy.\r\n");
- else
- IIC_DEBUG("w: iic sda bus busy.\r\n");
- return i | _IIC_ERROR_CODE;
- err_arb_fail:
- // 總線仲裁失敗可能是由于硬件錯誤或是多主機競爭。如果是硬件錯誤,應繼續產生...
- // 時鐘到字節傳輸結束,然后釋放總線?不管怎樣,都不應該再調用iic_stop();
- SDA_H();
- SCL_H();
- IIC_DEBUG("w: iic bus arbitrate failed.\r\n");
- return i | _IIC_ERROR_CODE; // IIC_ARB_FAIL
- err_dev_fail:
- iic_stop();
- IIC_DEBUG("w: iic device not respond.\r\n");
- return IIC_DEVICE_FAIL | _IIC_ERROR_CODE;
- err_tar_fail:
- iic_stop();
- IIC_DEBUG("w: device target not respond.\r\n");
- return IIC_TARGET_FAIL | _IIC_ERROR_CODE;
- }
- #else
- int iic_dev_write(u8 dev, u8 addr, u8 data)
- {
- u8 buf = data;
- return iic_dev_puts(dev, addr, &buf, 1);
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- int iic_dev_puts(u8 dev, u8 addr, u8* data, u16 n)
- {
- // 注意將IIC_DEBUG()放iic_stop()后面,以免影響總線時序。
- int i;
- i = iic_start();
- if( i != IIC_OK ) goto err_bus_busy;
- i = iic_write(dev);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_dev_fail;
- i = iic_write(addr);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_tar_fail;
- if(n<1) n=1;
- while(--n)
- {
- i = iic_write(*data++);
- if( i != IIC_OK ) goto err_arb_fail;
- i = iic_wait_ack();
- if( i != IIC_ACK) goto err_tar_fail; //could not write data.
- }
- i = iic_write(*data);
- if( i != IIC_OK ) goto err_arb_fail;
- iic_wait_ack(); // 最后一個字節,忽略ack。
- //end:
- iic_stop();
- // IIC_DEBUG("W: IIC WRITE DONE.\r\n");
- if(_IIC_ERROR_CODE & IIC_TIME_OUT)
- {
- IIC_DEBUG("w: iic time out.\r\n");
- return _IIC_ERROR_CODE;
- }
- return IIC_OK;
- err_bus_busy:
- if(i == IIC_SCL_BUSY)
- IIC_DEBUG("w: iic scl bus busy.\r\n");
- else
- IIC_DEBUG("w: iic sda bus busy.\r\n");
- return i | _IIC_ERROR_CODE;
- err_arb_fail:
- // 總線仲裁失敗可能是由于硬件錯誤或是多主機競爭。如果是硬件錯誤,應繼續產生...
- // 時鐘到字節傳輸結束,然后釋放總線?不管怎樣,都不應該再調用iic_stop();
- SDA_H();
- SCL_H();
- IIC_DEBUG("w: iic bus arbitrate failed.\r\n");
- return i | _IIC_ERROR_CODE; // IIC_ARB_FAIL
- err_dev_fail:
- iic_stop();
- IIC_DEBUG("w: iic device not respond.\r\n");
- return IIC_DEVICE_FAIL | _IIC_ERROR_CODE;
- err_tar_fail:
- iic_stop();
- IIC_DEBUG("w: device target not respond.\r\n");
- return IIC_TARGET_FAIL | _IIC_ERROR_CODE;
- }
- ////////////////////////////////////////////////////////////////////////////////
復制代碼 |