?? twi_main.c
字號:
if (strTWI.FAILCNT<FAIL_MAX)
{//重試次數未超出最大值,
TWCR=(1<<TWSTA)|TW_ACT; //發生錯誤,啟動start信號
}
else
{//否則停止
TWCR=(1<<TWSTO)|TW_ACT; //發送停止信號,不會再產生中斷了
strTWI.STATUS=TW_FAIL;
}
}
state++;
strTWI.STATE=state; //保存狀態
}
int main(void)
{
unsigned char i;
//上電默認DDRx=0x00,PORTx=0x00 輸入,無上拉電阻
PORTA=0xFF; //不用的管腳使能內部上拉電阻。
PORTB=0xFF;
PORTC=0xFF; //SCL,SDA使能了內部的10K上拉電阻
PORTD=0xFF;
//TWI初始化
TWSR=0x00; //預分頻=0^4=1
TWBR=TWBR_SET;
TWAR=0x00; //主機模式,該地址無效
TWCR=0x00; //關閉TWI模塊
sei(); //使能全局中斷
strTWI.STATUS=TW_OK;
TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_WRITE,0x10,&ORGDATA[0],8);
//從0x10地址開始寫入8個字節數據
while(strTWI.STATUS==TW_BUSY); //等待操作完成
if (strTWI.STATUS==TW_FAIL)
{
//操作失敗?
}
_delay_ms(10); //延時等待編程完成
while(1)
{
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x10,&CMPDATA[0],8);
//從0x10地址開始讀出8個字節數據
while(strTWI.STATUS==TW_BUSY); //等待操作完成
//如果不加等待,則需要檢測返回值i才能知道當前操作是否執行了
// 0 OP_BUSY 之前的操作沒完成,沒執行當前操作
// 1 OP_RUN 當前操作執行中
if (strTWI.STATUS==TW_FAIL)
{
//操作失敗?
}
//讀取成功,對比ORGDATA和CMPDATA的數據
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x00,&BUFFER[0],256);
//從0x00地址開始讀出256個字節數據(整個ATC24C02)
while(strTWI.STATUS==TW_BUSY); //等待操作完成
};
}
/*
兩線串行接口總線定義
兩線接口TWI很適合于典型的處理器應用。
TWI協議允許系統設計者只用兩根雙向傳輸線就可以將128個不同的設備互連到一起。
這兩根線一是時鐘SCL,一是數據SDA。外部硬件只需要兩個上拉電阻,每根線上一個。
所有連接到總線上的設備都(必須)有自己的地址。
注意:就是說不能有兩個相同地址的設備
TWI協議解決了總線仲裁的問題。
所有 TWI 兼容的器件的總線驅動都是漏極開路或集電極開路的。這樣就實現了對接口操作非常關鍵的線與功能。
TWI器件輸出為"0”時,TWI總線會產生低電平。
當所有的TWI器件輸出為三態時,總線會輸出高電平,允許上拉電阻將電壓拉高。
注意:為保證所有的總線操作,凡是與TWI 總線連接的AVR 器件必須上電。
與總線連接的器件數目受如下條件限制:
總線電容要低于400pF,而且可以用7 位從機地址進行尋址。
兩個不同的規范,一種是總線速度低于100 kHz,而另外一種是總線速度高達400 kHz。
SCL和SDA引腳
SCL與SDA為MCU的 TWI接口引腳。
引腳的輸出驅動器包含一個波形斜率限制器以滿足TWI 規范。
引腳的輸入部分包括尖峰抑制單元以去除小于50ns 的毛刺。
當相應的端口設置為SCL與SDA引腳時,可以使能I/O口內部的10K上拉電阻,這樣可省掉外部的上拉電阻
注意:如果要作高速通訊或者從機數量較多,最好還是外接合適的上拉電阻
比特率發生器單元
TWI工作于主機模式時,比特率發生器控制時鐘信號SCL的周期。
具體由TWI狀態寄存器TWSR的預分頻系數以及比特率寄存器TWBR設定。
當TWI工作在從機模式時,不需要對比特率或預分頻進行設定,但從機的CPU時鐘頻率必須大于TWI時鐘線SCL頻率的16倍。
注意,從機可能會延長SCL 低電平的時間,從而降低TWI 總線的平均時鐘周期。
SCL的頻率根據以下的公式產生:
fSCL=fCPU/((16+2(TWBR)(4^TWPS))
TWBR = TWI比特率寄存器的數值
TWPS = TWI狀態寄存器預分頻的數值
Note:TWI 工作在主機模式時,TWBR 值應該不小于10,否則主機會在SDA 與 SCL 產生錯誤輸出作為提示信號。
問題出現于TWI 工作在主機模式下,向從機發送Start + SLA + R/W 的時候(不需要真的有從機與總線連接)。
控制單元
控制單元監聽TWI 總線,并根據 TWI 控制寄存器TWCR 的設置作出相應的響應。
當TWI總線上產生需要應用程序干預處理的事件時,TWI 中斷標志位TWINT 置位。
在下一個時鐘周期, TWI 狀態寄存器TWSR 被表示這個事件的狀態碼字所更新。
在其它時間里,TWSR 的內容為一個表示無事件發生的特殊狀態字。
一旦TWINT 標志位置"1”,時鐘線SCL 即被拉低,暫停TWI 總線上的數據傳輸,讓用戶程序處理事件。
在下列狀況出現時, TWINT 標志位置位:
? 在TWI 傳送完START/REPEATED START 信號之后
? 在TWI 傳送完SLA+R/W 數據之后
? 在TWI 傳送完地址字節之后
? 在TWI 總線仲裁失敗之后
? 在TWI 被主機尋址之后( 廣播方式或從機地址匹配)
? 在TWI 接收到一個數據字節之后
? 作為從機工作時, TWI 接收到STOP 或REPEATED START 信號之后
? 由于非法的START 或STOP 信號造成總線錯誤時
TWI 寄存器說明
TWI 比特率寄存器- TWBR
? Bits 7..0 – TWI 比特率寄存器
TWBR 為比特率發生器分頻因子。
比特率發生器是一個分頻器,在主機模式下產生SCL時鐘頻率。
比特率計算公式請見前面的[比特率發生器單元]
TWI 控制寄存器- TWCR
TWCR 用來控制TWI操作。
它用來使能TWI,通過施加START到總線上來啟動主機訪問,產生接收器應答,產生STOP 狀態,以及在寫入數據到TWDR 寄存器時控制總線的暫停等。
這個寄存器還可以給出在TWDR 無法訪問期間,試圖將數據寫入到TWDR 而引起的寫入沖突信息。
? Bit 7 – TWINT: TWI 中斷標志
當TWI 完成當前工作,希望應用程序介入時TWINT 置位。
若SREG 的I 標志以及TWCR寄存器的TWIE 標志也置位,則MCU 執行TWI 中斷例程。
當TWINT 置位時, SCL信號的低電平被延長。
TWINT 標志的清零必須通過軟件寫"1” 來完成。
執行中斷時硬件不會自動將其改寫為"0”。
要注意的是,只要這一位被清零,TWI 立即開始工作。
因此,在清零TWINT 之前一定要首先完成對地址寄存器TWAR,狀態寄存器TWSR,以及數據寄存器TWDR 的訪問。
? Bit 6 – TWEA: 使能TWI 應答
TWEA 標志控制應答脈沖的產生。
若TWEA 置位,出現如下條件時接口發出ACK 脈沖:
1. 器件的從機地址與主機發出的地址相符合
2. TWAR 的TWGCE 置位時接收到廣播呼叫
3. 在主機/ 從機接收模式下接收到一個字節的數據
將TWEA 清零可以使器件暫時脫離總線。
置位后器件重新恢復地址識別。
? Bit 5 – TWSTA: TWI START 狀態標志
當CPU 希望自己成為總線上的主機時需要置位TWSTA。
TWI 硬件檢測總線是否可用。
若總線空閑,接口就在總線上產生START 狀態。
若總線忙,接口就一直等待,直到檢測到一個STOP 狀態 ,然后產生START 以聲明自己希望成為主機。
發送START之后軟件必須清零TWSTA。
? Bit 4 – TWSTO: TWI STOP 狀態標志
在主機模式下,如果置位TWSTO,TWI 接口將在總線上產生STOP 狀態,然后TWSTO自動清零。
在從機模式下,置位TWSTO 可以使接口從錯誤狀態恢復到未被尋址的狀態。
此時總線上不會有STOP 狀態產生,但TWI 返回一個定義好的未被尋址的從機模式且釋放SCL 與SDA 為高阻態。
? Bit 3 – TWWC: TWI 寫碰撞標志
當TWINT 為低時寫數據寄存器TWDR 將置位TWWC。
當TWINT 為高時,每一次對TWDR 的寫訪問都將更新此標志。
? Bit 2 – TWEN: TWI 使能
TWEN 位用于使能TWI操作與激活TWI接口。
當TWEN位被寫為"1”時,TWI引腳將I/O引腳切換到SCL 與SDA 引腳,使能波形斜率限制器與尖峰濾波器。
如果該位清零, TWI接口模塊將被關閉,所有TWI 傳輸將被終止。
? Bit 0 – TWIE: 使能TWI 中斷
當SREG 的I 以及TWIE 置位時,只要TWINT 為"1”, TWI 中斷就激活。
TWI 狀態寄存器- TWSR
? Bits 7..3 – TWS: TWI 狀態
這5位用來反映TWI 邏輯和總線的狀態。
不同的狀態代碼將會在后面的部分描述。
注意從TWSR 讀出的值包括5 位狀態值與2 位預分頻值。
檢測狀態位時設計者應屏蔽預分頻位為"0”。這使狀態檢測獨立于預分頻器設置。
? Bits 1..0 – TWPS: TWI 預分頻位
這兩位可讀/ 寫,用于控制比特率預分頻因子。
預分頻系數為4的n次方
計算比特率的公式見前面的[比特率發生器單元]
TWI 數據寄存器- TWDR
在發送模式, TWDR 包含了要發送的字節;
在接收模式, TWDR 包含了接收到的數據。
當TWI 接口沒有進行移位工作(TWINT 置位) 時這個寄存器是可寫的。
在第一次中斷發生之前用戶不能夠初始化數據寄存器。
只要TWINT 置位,TWDR 的數據就是穩定的。
在數據移出時,總線上的數據同時移入寄存器。
TWDR 總是包含了總線上出現的最后一個字節,除非MCU 是從掉電或省電模式被TWI 中斷喚醒。此時TWDR 的內容沒有定義。
總線仲裁失敗時,主機將切換為從機,但總線上出現的數據不會丟失。
ACK 的處理由 TWI邏輯自動管理, CPU 不能直接訪問ACK。
? Bits 7..0 – TWD: TWI 數據寄存器
根據狀態的不同,其內容為要發送的下一個字節,或是接收到的數據。
TWI(從機) 地址寄存器-TWAR
TWAR 的高7 位為從機地址。
工作于從機模式時,TWI 將根據這個地址進行響應。
主機模式不需要此地址。
在多主機系統中, TWAR需要進行設置以便其他主機訪問自己。
TWAR 的LSB 用于識別廣播地址 (0x00)。
器件內有一個地址比較器。一旦接收到的地址和本機地址一致,芯片就請求中斷。
? Bits 7..1 – TWA: TWI 從機地址寄存器
其值為從機地址。
? Bit 0 – TWGCE: 使能TWI 廣播識別
置位后MCU 可以識別TWI 總線廣播。
使用TWI
AVR的TWI接口是面向字節和基于中斷的。
所有的總線事件,如接收到一個字節或發送了一個START 信號等,都會產生一個TWI 中斷。
由于TWI 接口是基于中斷的,因此TWI接口在字節發送和接收過程中,不需要應用程序的干預。
TWCR寄存器的TWI中斷允許位[TWIE]和全局中斷允許位[I]一起決定了應用程序是否響應TWINT標志位產生的中斷請求。
如果TWIE 被清零,應用程序只能采用輪詢TWINT 標志位的方法來檢測TWI 總線狀態。
當TWINT 標志位置"1” 時,表示TWI 接口完成了當前的操作,等待應用程序的響應。
在這種情況下,TWI 狀態寄存器TWSR 包含了表明當前TWI 總線狀態的值。
應用程序可以讀取TWCR 的狀態碼,判別此時的狀態是否正確,并通過設置TWCR 與TWDR 寄存器,決定在下一個TWI 總線周期TWI 接口應該如何工作。
各種模式下的狀態碼列表(TWSR已屏蔽預分頻位)
twi.h里面有定義,現附上中文描述
主機發送狀態碼
#define TW_START 0x08 //START已發送
#define TW_REP_START 0x10 //重復START已發送
#define TW_MT_SLA_ACK 0x18 //SLA+W 已發送收到ACK
#define TW_MT_SLA_NACK 0x20 //SLA+W 已發送接收到NOT ACK
#define TW_MT_DATA_ACK 0x28 //數據已發送接收到ACK
#define TW_MT_DATA_NACK 0x30 //數據已發送接收到NOT ACK
#define TW_MT_ARB_LOST 0x38 //SLA+W 或數據的仲裁失敗
主機接收狀態碼
//#define TW_START 0x08 //START已發送
//#define TW_REP_START 0x10 //重復START已發送
#define TW_MR_ARB_LOST 0x38 //SLA+R 或NOT ACK 的仲裁失敗
#define TW_MR_SLA_ACK 0x40 //SLA+R 已發送接收到ACK
#define TW_MR_SLA_NACK 0x48 //SLA+R 已發送接收到NOT ACK
#define TW_MR_DATA_ACK 0x50 //接收到數據ACK 已返回
#define TW_MR_DATA_NACK 0x58 //接收到數據NOT ACK已返回
從機接收狀態碼
#define TW_SR_SLA_ACK 0x60 //自己的SLA+W 已經被接收ACK已返回
#define TW_SR_ARB_LOST_SLA_ACK 0x68 //SLA+R/W 作為主機的仲裁失敗;自己的SLA+W 已經被接收ACK 已返回
#define TW_SR_GCALL_ACK 0x70 //接收到廣播地址ACK 已返回
#define TW_SR_ARB_LOST_GCALL_ACK 0x78 //SLA+R/W 作為主機的仲裁失敗;接收到廣播地址ACK已返回
#define TW_SR_DATA_ACK 0x80 //以前以自己的SLA+W被尋址;數據已經被接收ACK已返回
#define TW_SR_DATA_NACK 0x88 //以前以自己的SLA+W被尋址;數據已經被接收NOT ACK已返回
#define TW_SR_GCALL_DATA_ACK 0x90 //以前以廣播方式被尋址;數據已經被接收ACK已返回
#define TW_SR_GCALL_DATA_NACK 0x98 //以前以廣播方式被尋址;數據已經被接收NOT ACK已返回
#define TW_SR_STOP 0xA0 //在以從機工作時接收到STOP或重復START
從發送狀態碼
#define TW_ST_SLA_ACK 0xA8 //自己的SLA+R 已經被接收ACK 已返回
#define TW_ST_ARB_LOST_SLA_ACK 0xB0 //SLA+R/W 作為主機的仲裁失敗;自己的SLA+R 已經被接收ACK 已返回
#define TW_ST_DATA_ACK 0xB8 //TWDR 里數據已經發送接收到ACK
#define TW_ST_DATA_NACK 0xC0 //TWDR 里數據已經發送接收到NOT ACK
#define TW_ST_LAST_DATA 0xC8 //TWDR 的一字節數據已經發送(TWAE = “0”);接收到ACK
其它狀態碼
#define TW_NO_INFO 0xF8 //沒有相關的狀態信息;TWINT = “0”
#define TW_BUS_ERROR 0x00 //由于非法的START 或STOP 引起的總線錯誤
AT24C02/04/08 IIC接口EEPROM的特點
(不同公司的24系列EEPROM特性有部分不同,請參考數據手冊)
1 AT24C02/04/08 是一個2K/4K/8K位串行CMOS E2PROM 內部含有256/512/1024 個8位字節
2 AT24C02有一個8 字節頁寫緩沖器,AT24C04/08/16 有一個16字節頁寫緩沖器
3 通過器件地址輸入端A0,A1,A2可以實現將最多
8個24C02器件
4個24C04器件
2個24C08器件
同時連接到總線上
4 寫操作
1 字節寫
2 頁寫 AT24C02是8字節/頁 AT24C04/08是16字節/頁
注意:頁寫的地址只在當前頁自動累加,頁地址范圍內循環。
啟動寫命令后需要10ms(最大)的編程時間才能真正的把數據記錄下來,編程期間器件不響應任何命令
5 讀操作
1 立即地址讀 地址自動累加,即為上次讀/寫操作地址+1(本程序不支持該操作)
2 隨機讀 指定地址讀一個字節
3 連續讀 連續讀操作可通過立即讀或隨機讀操作啟動,由主機發出NAK和STOP來停止讀操作
讀操作時地址計數器在AT24C02/04/08整個地址內增加,這樣整個寄存器區域在可在一個讀操作內全部讀出
循環讀取,讀到最后一個地址后,從第一個地址繼續開始讀
6 寫保護功能,由WP引腳控制,WP=VCC時,24C02的高1K位,24C04的高2K位,24C08的全部8K位都變成只讀,不能寫入
*/
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -