?? twi.c
字號:
/***********************************************
**** AVR TWI讀寫讀寫范例 ***
**** ***
**** 作者: HJJourAVR ***
**** 編譯器:WINAVR20050214 ***
**** ***
**** www.OurAVR.com 2005.10.18 ***
***********************************************/
/*
本程序簡單的示范了如何使用ATMEGA16的TWI 讀寫AT24C02 IIC EEPROM
TWI協(xié)議
(即IIC協(xié)議,請認(rèn)真參考IIC協(xié)議的內(nèi)容,否則根本就不能掌握)
一主多從的應(yīng)用,M16作主機(jī)
(M16做從機(jī)和多主多從的應(yīng)用不多,請自行參考相關(guān)文檔)
中斷模式
(因?yàn)锳VR的速度很高,而IIC的速度相對較低,
采用查詢模式會長時間獨(dú)占CPU,令CPU的利用率明顯下降。
特別是IIC速度受環(huán)境影響只能低速通訊時,對系統(tǒng)的實(shí)時性產(chǎn)生嚴(yán)重的影響。
查詢模式可以參考其它文檔和軟件模擬IIC的文檔)
AT24C02/04/08的操作特點(diǎn)
出于簡化程序考慮,各種數(shù)據(jù)沒有對外輸出,學(xué)習(xí)時建議使用JTAG ICE硬件仿真器
*/
#include "config.h"
//定義全局變量
uint8 BUFFER[10]; //緩沖區(qū),可以裝載整個AC24C02的數(shù)據(jù)
struct str_TWI //TWI數(shù)據(jù)結(jié)構(gòu)
{
volatile uint8 STATUS; //TWI_操作狀態(tài)
uint8 SLA; //從設(shè)備的器件地址
uint16 ADDR; //從設(shè)備的數(shù)據(jù)地址
uint8 *pBUF; //數(shù)據(jù)緩沖區(qū)指針
uint16 DATALEN; //數(shù)據(jù)長度
uint8 STATE; //TWI讀寫操作步驟
uint8 FAILCNT; //失敗重試次數(shù)
};
struct str_TWI strTWI; //TWI的數(shù)據(jù)結(jié)構(gòu)變量
//仿真時在watch窗口,監(jiān)控這些全局變量。
//AT24C02的讀寫函數(shù)(包括隨機(jī)讀,連續(xù)讀,字節(jié)寫,頁寫)
//根據(jù)sla的最低位決定(由中斷程序中判斷)
//bit0=1 TW_READ 讀
//bit0=0 TW_WRITE 寫
// sla 器件地址(不能搞錯)
// addr EEPROM地址(0~1023)
// *ptr 讀寫數(shù)據(jù)緩沖區(qū)
// len 讀數(shù)據(jù)長度(1~1024),寫數(shù)據(jù)長度(1 or 8 or 16)
// 返回值 是否能執(zhí)行當(dāng)前操作
uint8 TWI_RW(uint8 sla,uint16 addr,uint8 *ptr,uint16 len)
{
// uint8 i;
if (strTWI.STATUS==TW_BUSY)
{//TWI忙,不能進(jìn)行操作
return OP_BUSY;
}
strTWI.STATUS=TW_BUSY;
// i=(addr>>8)<<1;
// i&=0x06; strTWI.SLA=sla+i; //考慮了24C04/08的EEPROM地址高位放在SLA里面
strTWI.SLA=sla;
strTWI.ADDR=addr;
strTWI.pBUF=ptr;
strTWI.DATALEN=len;
strTWI.STATE=ST_START;
strTWI.FAILCNT=0;
TWCR=(1<<TWSTA)|TW_ACT; //啟動start信號
return OP_RUN;
}
/*
TWI中斷函數(shù)
這個函數(shù)流程只是考慮了器件地址后有一個字節(jié)數(shù)據(jù)(命令)地址的IIC器件
(大部分IIC接口器件都是這種類型,常見的例如AT24C01/02/04/08/16,DS1307,DS1721等)
對于有兩個字節(jié)數(shù)據(jù)地址的IIC器件(例如AT24C32/64/128/256等大容量EEPROM),請稍作改動
//根據(jù)strTWI.SLA的最低位決定
//bit0=1 TW_READ 讀
//bit0=0 TW_WRITE 寫
雖然中斷服務(wù)程序很長,但每次只執(zhí)行一個 case,所以耗時并不長。
*/
SIGNAL(SIG_2WIRE_SERIAL)
{//IIC中斷
uint8 action,state,status;
action=strTWI.SLA&TW_READ; //取操作模式
state=strTWI.STATE;
status=TWSR&0xF8; //屏蔽預(yù)分頻位
if ((status>=0x60)||(status==0x00))
{//總線錯誤或從機(jī)模式引發(fā)的中斷,不予處理
return;
}
switch(state)
{
case ST_START: //START狀態(tài)檢查
if(status==TW_START)
{//發(fā)送start信號成功
TWDR=strTWI.SLA&0xFE; //發(fā)送器件地址寫SLAW
TWCR=TW_ACT; //觸發(fā)下一步動作,同時清start發(fā)送標(biāo)志
}
else
{//發(fā)送start信號出錯
state=ST_FAIL;
}
break;
case ST_SLAW: //SLAW狀態(tài)檢查
if(status==TW_MT_SLA_ACK)
{//發(fā)送器件地址成功
TWDR=(strTWI.ADDR>>8); //發(fā)送eeprom地址高位
strTWI.ADDR <<=8;
TWCR=TW_ACT; //觸發(fā)下一步動作
}
else
{//發(fā)送器件地址出錯
state=ST_FAIL;
}
break;
case ST_WADDR: //ADDR狀態(tài)檢查
if(status==TW_MT_DATA_ACK)
{//發(fā)送eeprom地址成功
if (action==TW_READ)
{//讀操作模式
TWCR=(1<<TWSTA)|TW_ACT; //發(fā)送restart信號,下一步將跳到RESTART分支
}
else
{//寫操作模式
TWDR=*strTWI.pBUF++; //寫第一個字節(jié)
strTWI.DATALEN--;
state=ST_WDATA-1; //下一步將跳到WDATA分支
TWCR=TW_ACT; //觸發(fā)下一步動作
}
}
else
{//發(fā)送eeprom地址出錯
state=ST_FAIL;
}
break;
case ST_RESTART: //RESTART狀態(tài)檢查,只有讀操作模式才能跳到這里
if(status==TW_REP_START)
{//發(fā)送restart信號成功
TWDR=strTWI.SLA; //發(fā)器件地址讀SLAR
TWCR=TW_ACT; //觸發(fā)下一步動作,同時清start發(fā)送標(biāo)志
}
else
{//重發(fā)start信號出錯
state=ST_FAIL;
}
break;
case ST_SLAR: //SLAR狀態(tài)檢查,只有讀操作模式才能跳到這里
if(status==TW_MR_SLA_ACK)
{//發(fā)送器件地址成功
if (strTWI.DATALEN--)
{//多個數(shù)據(jù)
TWCR=(1<<TWEA)|TW_ACT; //設(shè)定ACK,觸發(fā)下一步動作
}
else
{//只有一個數(shù)據(jù)
TWCR=TW_ACT; //設(shè)定NAK,觸發(fā)下一步動作
}
}
else
{//發(fā)送器件地址出錯
state=ST_FAIL;
}
break;
case ST_RDATA: //讀取數(shù)據(jù)狀態(tài)檢查,只有讀操作模式才能跳到這里
state--; //循環(huán),直到讀完指定長度數(shù)據(jù)
if(status==TW_MR_DATA_ACK)
{//讀取數(shù)據(jù)成功,但不是最后一個數(shù)據(jù)
*strTWI.pBUF++=TWDR;
if (strTWI.DATALEN--)
{//還有多個數(shù)據(jù)
TWCR=(1<<TWEA)|TW_ACT; //設(shè)定ACK,觸發(fā)下一步動作
}
else
{//準(zhǔn)備讀最后一個數(shù)據(jù)
TWCR=TW_ACT; //設(shè)定NAK,觸發(fā)下一步動作
}
}
else if(status==TW_MR_DATA_NACK)
{//已經(jīng)讀完最后一個數(shù)據(jù)
*strTWI.pBUF++=TWDR;
TWCR=(1<<TWSTO)|TW_ACT; //發(fā)送停止信號,不會再產(chǎn)生中斷了
strTWI.STATUS=TW_OK;
}
else
{//讀取數(shù)據(jù)出錯
state=ST_FAIL;
}
break;
case ST_WDATA: //寫數(shù)據(jù)狀態(tài)檢查,只有寫操作模式才能跳到這里
state--; //循環(huán),直到寫完指定長度數(shù)據(jù)
if(status==TW_MT_DATA_ACK)
{//寫數(shù)據(jù)成功
if (strTWI.DATALEN)
{//還要寫
TWDR=*strTWI.pBUF++;
strTWI.DATALEN--;
TWCR=TW_ACT; //觸發(fā)下一步動作
}
else
{//寫夠了
TWCR=(1<<TWSTO)|TW_ACT; //發(fā)送停止信號,不會再產(chǎn)生中斷了
strTWI.STATUS=TW_OK;
//啟動寫命令后需要10ms(最大)的編程時間才能真正的把數(shù)據(jù)記錄下來
//編程期間器件不響應(yīng)任何命令
}
}
else
{//寫數(shù)據(jù)失敗
state=ST_FAIL;
}
break;
default:
//錯誤狀態(tài)
state=ST_FAIL;
break;
}
if (state==ST_FAIL)
{//錯誤處理
strTWI.FAILCNT++;
if (strTWI.FAILCNT<FAIL_MAX)
{//重試次數(shù)未超出最大值,
TWCR=(1<<TWSTA)|TW_ACT; //發(fā)生錯誤,啟動start信號
}
else
{//否則停止
TWCR=(1<<TWSTO)|TW_ACT; //發(fā)送停止信號,不會再產(chǎn)生中斷了
strTWI.STATUS=TW_FAIL;
}
}
state++;
strTWI.STATE=state; //保存狀態(tài)
}
/*
int main(void)
{
unsigned char i;
//上電默認(rèn)DDRx=0x00,PORTx=0x00 輸入,無上拉電阻
PORTA=0xFF; //不用的管腳使能內(nèi)部上拉電阻。
PORTB=0xFF;
PORTC=0xFF; //SCL,SDA使能了內(nèi)部的10K上拉電阻
PORTD=0xFF;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -