現象:單個讀取字符正確,連續讀取字符出錯環境:stc89c52單片機 24C02的epprom
我的解決辦法:
IIC_wirte 8bit時等待ack,如無ack,則stop-->start 重新發起信號,結果解決問題,下面貼出代碼:
- #include <reg52.h>
- #include "intrins.h"
- #include "type.h"
- #include "i2c.h"
- sbit SCL=P2^1;
- sbit SDA=P2^0;
- #define IIC_WRITE 0xA0
- #define IIC_READ 0xA1
- static void delay10Us(u16 i)
- {
- while(i--);
- }
- /*******************************************************************************
- * 函數名 : Delay10us()
- * 函數功能 : 延時10us
- * 輸入 : 無
- * 輸出 : 無
- *******************************************************************************/
- /*
- void Delay10us()
- {
- }
- */
- /*******************************************************************************
- * 函數名 : I2cStart()
- * 函數功能 : 起始信號:在SCL時鐘信號在高電平期間SDA信號產生一個下降沿
- * 輸入 : 無
- * 輸出 : 無
- * 備注 : 起始之后SDA和SCL都為0
- *******************************************************************************/
- void I2cStart()
- {
- SCL=0;
- delay10Us(1);
- SDA=1;
- SCL=1;
- delay10Us(1);//建立時間是SDA保持時間>4.7us
- SDA=0;
- delay10Us(1);//保持時間是>4us
- }
- /*******************************************************************************
- * 函數名 : I2cStop()
- * 函數功能 : 終止信號:在SCL時鐘信號高電平期間SDA信號產生一個上升沿
- * 輸入 : 無
- * 輸出 : 無
- * 備注 : 結束之后保持SDA和SCL都為1;表示總線空閑
- *******************************************************************************/
- void I2cStop()
- {
- SCL=0;
- delay10Us(1);
- SDA=0;
- SCL=1;
- delay10Us(1);
- SDA=1;
- delay10Us(1);
- }
- /*******************************************************************************
- * 函數名 : I2cSendByte(unsigned char dat)
- * 函數功能 : 通過I2C發送一個字節。在SCL時鐘信號高電平期間,保持發送信號SDA保持穩定
- * 輸入 : num
- * 輸出 : 0或1。發送成功返回1,發送失敗返回0
- * 備注 : 發送完一個字節SCL=0,SDA=1
- *******************************************************************************/
- unsigned char I2cSendByte(unsigned char dat)
- {
- unsigned char a=0,b=0;//最大255,一個機器周期為1us,最大延時255us。
- SCL=0;
- delay10Us(1);
- for(a=0;a<8;a++)//要發送8位,從最高位開始
- {
- SDA = dat>>7; //起始信號之后SCL=0,所以可以直接改變SDA信號
- dat = dat<<1;
- delay10Us(1);
- SCL=1;
- delay10Us(2);
- SCL=0;
- delay10Us(1);
- }
- SDA=1;
- delay10Us(1);
- SCL=1;
- while(SDA)//等待應答,也就是等待從設備把SDA拉低
- {
- b++;
- if(b>200) //如果超過2000us沒有應答發送失敗,或者為非應答,表示接收結束
- {
- return 0;
- }
- delay10Us(1);
- }
-
- return 1;
- }
- /*******************************************************************************
- * 函數名 : I2cReadByte()
- * 函數功能 : 使用I2c讀取一個字節
- * 輸入 : 無
- * 輸出 : dat
- * 備注 : 接收完一個字節SCL=0,SDA=1.
- *******************************************************************************/
- unsigned char I2cReadByte()
- {
- unsigned char a=0,dat=0;
- SCL = 0;
- delay10Us(1);
- SDA=1; //起始和發送一個字節之后SCL都是0
- delay10Us(1);
- for(a=0;a<8;a++)//接收8個字節
- {
- SCL=1;
- delay10Us(2);
- dat<<=1;
- dat|=SDA;
- SCL=0;
- delay10Us(1);
- }
- return dat;
- }
- /*******************************************************************************
- * 函數名 : void At24c02Write(unsigned char addr,unsigned char dat)
- * 函數功能 : 往24c02的一個地址寫入一個數據
- * 輸入 : 無
- * 輸出 : 無
- *******************************************************************************/
- void At24c02Write(unsigned char addr,unsigned char dat)
- {
- unsigned char ret;
- again:
- I2cStart();
- ret = I2cSendByte(IIC_WRITE);//發送寫器件地址
- if(!ret){
- I2cStop();
- goto again;
- }
- ret = I2cSendByte(addr);//發送要寫入內存地址
- if(!ret){
- I2cStop();
- goto again;
- }
- ret = I2cSendByte(dat); //發送數據
- if(!ret){
- I2cStop();
- goto again;
- }
- I2cStop();
- }
- /*******************************************************************************
- * 函數名 : unsigned char At24c02Read(unsigned char addr)
- * 函數功能 : 讀取24c02的一個地址的一個數據
- * 輸入 : 無
- * 輸出 : 無
- *******************************************************************************/
- unsigned char At24c02Read(unsigned char addr)
- {
- unsigned char num;
- unsigned char ret;
- again:
- I2cStart();
- ret = I2cSendByte(IIC_WRITE); //發送器件寫地址
- if(!ret){
- I2cStop();
- goto again;
- }
- ret = I2cSendByte(addr); //發送要讀取的地址
- if(!ret){
- I2cStop();
- goto again;
- }
- I2cStart();
- ret = I2cSendByte(IIC_READ); //發送讀器件地址
- if(!ret){
- I2cStop();
- goto again;
- }
- num=I2cReadByte(); //讀取數據
- NoAck();
- I2cStop();
- return num;
- }
- /*
- 非應答
- */
- void NoAck(void)
- {
- SCL=0;
- SDA=1;
- delay10Us(1);
- SCL=1;
- delay10Us(2);
- }
- /*
- 應答信號
- */
- void Ack(void)
- {
- SCL=0;
- SDA=0;
- delay10Us(1);
- SCL=1;
- delay10Us(2);
- }
- /*******************************************************************************
- * 函數名 : unsigned char At24c02Read(unsigned char addr)
- * 函數功能 : 讀取24c02的一個地址的一個數據
- * 輸入 : 無
- * 輸出 : -1:error 0:success
- *******************************************************************************/
- void At24c02ReadBytes(unsigned char addr,unsigned char *dat,unsigned char n)
- {
- //unsigned char num = n;
- unsigned char ret;
- again:
- I2cStart();
- ret=I2cSendByte(0xa0); //發送寫器件地址
- if(!ret){
- I2cStop();
- goto again;
- }
- ret=I2cSendByte(addr); //發送要讀取的地址
- if(!ret){
- I2cStop();
- goto again;
- }
- I2cStart();
- ret=I2cSendByte(0xa1); //發送讀器件地址
- if(!ret){
- I2cStop();
- goto again;
- }
- if(addr + n >256)
- {
- n = 256 - addr;
- }
- while(n--)
- {
- *dat = I2cReadByte(); //讀取數據
- if(n!=0) Ack();
- dat++;
- }
- I2cStop();
- //return num;
- }
- /*******************************************************************************
- * 函數名 : unsigned char At24c02Read(unsigned char addr)
- * 函數功能 : 讀取24c02的一個地址的一個數據
- * 輸入 : 無
- * 輸出 : 無
- *******************************************************************************/
- /*******************************************************************************
- * 函數名 : void At24c02ReadBytes2(unsigned char addr,unsigned char *dat,unsigned char n)
- * 函數功能 : 從地址addr開始,連續讀取n個字符,讀取字符放入dat中
- * 輸入 : 無
- * 輸出 : 無
- *******************************************************************************/
- void At24c02ReadBytes2(unsigned char addr,unsigned char *dat,unsigned char n)
- {
- unsigned char i;
- for(i=0;i<n;i++)
- dat[i]= At24c02Read(addr+i);
- }
- /*******************************************************************************
- * 函數名 : void At24c02ReadBytes2(unsigned char addr,unsigned char *dat,unsigned char n)
- * 函數功能 : 將dat中的字符寫入addr開始的epprom中去,寫入n個
- * 輸入 : 無
- * 輸出 : 無
- *******************************************************************************/
- void At24c02WriteBytes(unsigned char addr,unsigned char *dat,unsigned char n)
- {
- unsigned char i;
- for(i=0;i<n;i++)
- {
- At24c02Write(addr+i,dat[i]);
- }
- }
復制代碼 上訴代碼的關鍵點在 I2cSendByte 這個方法有返回值,當I2cSendByte的返回值是1說明成功
1、在At24c02Write函數中利用goto語句來處理寫出錯;
2、At24c02Read 利用goto語句來處理地址寫出錯
3、At24c02ReadBytes 同上
總結:
1、在iic的連續read過程一定要有ACK
2、起始信號之后的器件地址寫,存儲器地址、器件地址讀 這三個地址時,確保收到epprom發來的ack
單片機交流學習群: |