這不,看了OURAVR網站的範例,稍微修改了一下,帖在這裏當做個筆記.
//usart.h
#include <avr/io.h>
//管腳定義
#define PIN_RXD 0 //PD0 RXD
#define PIN_TXD 1 //PD1 TXD
//常量定義
#define BAUDRATE 9600 //波特率
void put_c(unsigned char c);
void put_s(unsigned char *ptr);
void init_USART(void);
#include <avr/io.h>
//管腳定義
#define PIN_RXD 0 //PD0 RXD
#define PIN_TXD 1 //PD1 TXD
//常量定義
#define BAUDRATE 9600 //波特率
void put_c(unsigned char c);
void put_s(unsigned char *ptr);
void init_USART(void);
//usart.c
#include "usart.h"
void put_c(unsigned char c) //發送採用查詢方式
...{
while( !(UCSRA & (1<<UDRE)) );
UDR=c;
}
void put_s(unsigned char *ptr)
...{
while (*ptr)
...{
put_c(*ptr++);
}
put_c(0x0D);
put_c(0x0A); //結尾發送回車換行
}
void init_USART(void)//USART 初始化
...{
//USART 9600 8, n,1 PC上位機軟件(超級終端等)也要設成同樣的設置才能通訊
UCSRC = (1<<URSEL) | 0x06;
UBRRL= (F_CPU/BAUDRATE/16-1)%256;
UBRRH= (F_CPU/BAUDRATE/16-1)/256;
UCSRA = 0x00;
UCSRB = (1<<TXEN);
//使能接收中斷,使能接收,使能發送
}
#include "usart.h"
void put_c(unsigned char c) //發送採用查詢方式
...{
while( !(UCSRA & (1<<UDRE)) );
UDR=c;
}
void put_s(unsigned char *ptr)
...{
while (*ptr)
...{
put_c(*ptr++);
}
put_c(0x0D);
put_c(0x0A); //結尾發送回車換行
}
void init_USART(void)//USART 初始化
...{
//USART 9600 8, n,1 PC上位機軟件(超級終端等)也要設成同樣的設置才能通訊
UCSRC = (1<<URSEL) | 0x06;
UBRRL= (F_CPU/BAUDRATE/16-1)%256;
UBRRH= (F_CPU/BAUDRATE/16-1)/256;
UCSRA = 0x00;
UCSRB = (1<<TXEN);
//使能接收中斷,使能接收,使能發送
}
//TWI_main.c
/**//***********************************************
**** 名 稱:AVR TWI使用範例_AT24C02
****
**** 作 者:zhiyu
**** 編譯器:WINAVR20070525
****
**** 參 考:http://www.ouravr.com/guide_index.html(TWI範例(AT24C02))
**** 日 期:2007.07.26
****
**** 芯 片:M16L
**** 時鐘源:外部4M晶振
****
**** 結 果:測試成功
**** 問 題:中斷裏面的state部分不理解,如自加操作,其他基本沒什麼問題
***********************************************/
/**//*
本程序簡單的示範瞭如何使用ATMEGA16的TWI 讀寫AT24C02 IIC EEPROM
TWI協議
(即IIC協議,請認真參考IIC協議的內容,否則根本就不能掌握)
一主多從的應用,M16作主機
(M16做從機和多主多從的應用不多,請自行參考相關文檔)
中斷模式
(因爲AVR的速度很高,而IIC的速度相對較低,
採用查詢模式會長時間獨佔CPU,令CPU的利用率明顯下降。
特別是IIC速度受環境影響只能低速通訊時,對系統的實時性產生嚴重的影響。
查詢模式可以參考其它文檔和軟件模擬IIC的文檔)
AT24C02/04/08的操作特點
出於簡化程序考慮,各種數據沒有對外輸出,學習時建議使用JTAG ICE硬件仿真器
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "usart.h"
#include <util/twi.h>
//定義了各種模式下的狀態碼列表(TWSR已屏蔽預分頻位),本文後面附上中文描述
//管腳定義
#define pinSCL 0 //PC0 SCL
#define pinSDA 1 //PC1 SDA
//爲保險起見,最好在SCL/SDA接上1~10K的外部上拉電阻到VCC。
#define FREQ 4 //FREQ爲系統振盪週期(以MHZ爲單位,因爲現在用的是外部4M晶振,所以FREQ=4)
#define fSCL 100000 //TWI時鐘爲100KHz
//預分頻係數=1(TWPS=0)
#if F_CPU < fSCL*36
#define TWBR_SET 10; //TWBR必須大於等於10
#else
#define TWBR_SET (F_CPU/fSCL-16)/2; //計算TWBR值
#endif
#define TW_ACT (1<<TWINT)|(1<<TWEN)|(1<<TWIE)
//TWCR只能IN/OUT,直接賦值比邏輯運算(|= &=)更節省空間
#define SLA_24CXX 0xA0 //24Cxx系列的廠商器件地址(高四位)
#define ADDR_24C02 0x00
// AT24C02的地址線A2/1/0全部接地,SLAW=0xA0+0x00<<1+0x00,SLAR=0xA0+0x00<<1+0x01
//TWI_操作狀態
#define TW_BUSY 0
#define TW_OK 1
#define TW_FAIL 2
//TWI_讀寫命令狀態
#define OP_BUSY 0
#define OP_RUN 1
//TWI讀寫操作公共步驟
#define ST_FAIL 0 //出錯狀態
#define ST_START 1 //START狀態檢查
#define ST_SLAW 2 //SLAW狀態檢查
#define ST_WADDR 3 //ADDR狀態檢查
//TWI讀操作步驟
#define ST_RESTART 4 //RESTART狀態檢查
#define ST_SLAR 5 //SLAR狀態檢查
#define ST_RDATA 6 //讀取數據狀態檢查,循環n字節
//TWI寫操作步驟
#define ST_WDATA 7 //寫數據狀態檢查,循環n字節
#define FAIL_MAX 20 //重試次數最大值
//定義全局變量
unsigned char ORGDATA[8]=
...{65,66,67,68,69,70,71,72}; //原始數據:64:A; 66:B; 67:c……
unsigned char CMPDATA[8]; //比較數據
unsigned char BUFFER[256]; //緩衝區,可以裝載整個AC24C02的數據
struct str_TWI //TWI數據結構
...{
volatile unsigned char STATUS; //TWI_操作狀態
unsigned char SLA; //從設備的器件地址
unsigned int ADDR; //從設備的數據地址
unsigned char *pBUF; //數據緩衝區指針
unsigned int DATALEN; //數據長度
unsigned char STATE; //TWI讀寫操作步驟
unsigned char FAILCNT; //失敗重試次數
};
struct str_TWI strTWI; //TWI的數據結構變量
//仿真時在watch窗口,監控這些全局變量。
//AT24C02的讀寫函數(包括隨機讀,連續讀,字節寫,頁寫)
//根據sla的最低位決定(由中斷程序中判斷)
//bit0=1 TW_READ 讀
//bit0=0 TW_WRITE 寫
// sla 器件地址(不能搞錯)
// addr EEPROM地址(0~1023)
// *ptr 讀寫數據緩衝區
// len 讀數據長度(1~1024),寫數據長度(1 or 8 or 16)
// 返回值 是否能執行當前操作
//此函數參考《AVR單片機GCC程序設計》P72和P120
void DelayMs(unsigned int t)
...{
unsigned int i;
for(i=0;i<t;i++)
_delay_loop_2(FREQ*4-1);
}
unsigned char TWI_RW(unsigned char sla,unsigned int addr,unsigned char *ptr,unsigned int len)
...{
unsigned char i;
if (strTWI.STATUS==TW_BUSY)
...{//TWI忙,不能進行操作
return OP_BUSY;
}
strTWI.STATUS=TW_BUSY;
i=(addr>>8)<<1;
i&=0x06; //考慮了24C04/08的EEPROM地址高位放在SLA裏面
strTWI.SLA=sla+i;
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中斷函數
這個函數流程只是考慮了器件地址後有一個字節數據(命令)地址的IIC器件
(大部分IIC接口器件都是這種類型,常見的例如AT24C01/02/04/08/16,DS1307,DS1721等)
對於有兩個字節數據地址的IIC器件(例如AT24C32/64/128/256等大容量EEPROM),請稍作改動
//根據strTWI.SLA的最低位決定
//bit0=1 TW_READ 讀
//bit0=0 TW_WRITE 寫
雖然中斷服務程序很長,但每次只執行一個 case,所以耗時並不長。
*/
ISR(TWI_vect)
...{//IIC中斷
unsigned char action,state,status;
action=strTWI.SLA&TW_READ; //取操作模式
state=strTWI.STATE;
status=TWSR&0xF8; //屏蔽預分頻位
if ((status>=0x60)||(status==0x00))
...{//總線錯誤或從機模式引發的中斷,不予處理
return;
}
switch(state)
...{
case ST_START: //START狀態檢查
if(status==TW_START)
...{//發送start信號成功
TWDR=strTWI.SLA&0xFE; //發送器件地址寫SLAW
TWCR=TW_ACT; //觸發下一步動作,同時清start發送標誌
}
else
...{//發送start信號出錯
state=ST_FAIL;
}
break;
case ST_SLAW: //SLAW狀態檢查
if(status==TW_MT_SLA_ACK)
...{//發送器件地址成功
TWDR=strTWI.ADDR; //發送eeprom地址
TWCR=TW_ACT; //觸發下一步動作
}
else
...{//發送器件地址出錯
state=ST_FAIL;
}
break;
case ST_WADDR: //ADDR狀態檢查
if(status==TW_MT_DATA_ACK)
...{//發送eeprom地址成功
if (action==TW_READ)
...{//讀操作模式
TWCR=(1<<TWSTA)|TW_ACT; //發送restart信號,下一步將跳到RESTART分支
}
else
...{//寫操作模式
TWDR=*strTWI.pBUF++; //寫第一個字節
strTWI.DATALEN--;
state=ST_WDATA-1; //下一步將跳到WDATA分支
TWCR=TW_ACT; //觸發下一步動作
}
}
else
...{//發送eeprom地址出錯
state=ST_FAIL;
}
break;
case ST_RESTART: //RESTART狀態檢查,只有讀操作模式才能跳到這裏
if(status==TW_REP_START)
...{//發送restart信號成功
TWDR=strTWI.SLA|TW_READ; //發器件地址讀SLAR(原來的程序只是TWDR=strTWI.SLA,不得)
TWCR=TW_ACT; //觸發下一步動作,同時清start發送標誌
}
else
...{//重發start信號出錯
state=ST_FAIL;
}
break;
case ST_SLAR: //SLAR狀態檢查,只有讀操作模式才能跳到這裏
if(status==TW_MR_SLA_ACK)
...{//發送器件地址成功
if (strTWI.DATALEN--)
...{//多個數據
TWCR=(1<<TWEA)|TW_ACT; //設定ACK,觸發下一步動作
}
else
...{//只有一個數據
TWCR=TW_ACT; //設定NAK,觸發下一步動作
}
}
else
...{//發送器件地址出錯
state=ST_FAIL;
}
break;
case ST_RDATA: //讀取數據狀態檢查,只有讀操作模式才能跳到這裏
state--; //循環,直到讀完指定長度數據
if(status==TW_MR_DATA_ACK)
...{//讀取數據成功,但不是最後一個數據
*strTWI.pBUF++=TWDR;
if (strTWI.DATALEN--)
...{//還有多個數據
TWCR=(1<<TWEA)|TW_ACT; //設定ACK,觸發下一步動作
}
else
...{//準備讀最後一個數據
TWCR=TW_ACT; //設定NAK,觸發下一步動作
}
}
else if(status==TW_MR_DATA_NACK)
...{//已經讀完最後一個數據
*strTWI.pBUF++=TWDR;
TWCR=(1<<TWSTO)|TW_ACT; //發送停止信號,不會再產生中斷了
strTWI.STATUS=TW_OK;
}
else
...{//讀取數據出錯
state=ST_FAIL;
}
break;
case ST_WDATA: //寫數據狀態檢查,只有寫操作模式才能跳到這裏
state--; //循環,直到寫完指定長度數據
if(status==TW_MT_DATA_ACK)
...{//寫數據成功
if (strTWI.DATALEN)
...{//還要寫
TWDR=*strTWI.pBUF++;
strTWI.DATALEN--;
TWCR=TW_ACT; //觸發下一步動作
}
else
...{//寫夠了
TWCR=(1<<TWSTO)|TW_ACT; //發送停止信號,不會再產生中斷了
strTWI.STATUS=TW_OK;
//啓動寫命令後需要10ms(最大)的編程時間才能真正的把數據記錄下來
//編程期間器件不響應任何命令
}
}
else
...{//寫數據失敗
state=ST_FAIL;
}
break;
default:
//錯誤狀態
state=ST_FAIL;
break;
}
if (state==ST_FAIL)
...{//錯誤處理
strTWI.FAILCNT++;
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;
unsigned int m,n,k,t;
//上電默認DDRx=0x00,PORTx=0x00 輸入,無上拉電阻
PORTA=0xFF; //不用的管腳使能內部上拉電阻。
PORTB=0xFF;
PORTC=0xFF; //SCL,SDA使能了內部的10K上拉電阻
//串口初始化
DDRD =(1<<PIN_TXD); //TXD爲輸出
PORTD =0xFF;
init_USART();
//TWI初始化
TWSR=0x00; //預分頻=4^0=1
TWBR=TWBR_SET;
TWAR=0x00; //主機模式,該地址無效
TWCR=0x00; //關閉TWI模塊
sei(); //使能全局中斷
put_s("Hello!");
put_s("這是一個簡單TWI程序");
put_s("請你仔細對照數據");
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)
...{
put_s("TWI寫操作FAIL!");
}
DelayMs(10); //延時等待編程完成
put_s("TWI寫操作succeed!");
while(1)
...{
//從0x10地址開始讀出8個字節數據放入CMPDATA數組中
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x10,&CMPDATA[0],8);
while(strTWI.STATUS==TW_BUSY); //等待操作完成
// 如果不加等待,則需要檢測返回值i才能知道當前操作是否執行了
// 0 OP_BUSY 之前的操作沒完成,沒執行當前操作
// 1 OP_RUN 當前操作執行中
if (strTWI.STATUS==TW_FAIL)
...{
put_s("TWI讀操作FAIL!");
}
//讀取成功,對比ORGDATA和CMPDATA的數據
put_s("原來ORGDATA的數據:");
for(m=0;m<8;m++)
...{
put_c(ORGDATA[m]);
put_c(' ');
}
put_c(0x0D);
put_c(0x0A);
put_s("新的CMPDATA的數據:");
for(n=0;n<8;n++)
...{
put_c(CMPDATA[n]);
put_c(' ');
}
put_c(0x0D);
put_c(0x0A);
//讀取整個AC24C02的數據
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x00,&BUFFER[0],256);
//從0x00地址開始讀出256個字節數據(整個ATC24C02)
while(strTWI.STATUS==TW_BUSY); //等待操作完成
put_s("整個AC24C02的數據:");
for(k=0,t=1;k<256;k++,t++)
...{
if(BUFFER[k]>=65 && BUFFER[k]<=72)
put_c(BUFFER[k]);
else
put_c(48);
put_c(' ');
if(t==16)
...{
put_c(0x0D);
put_c(0x0A);
t=0;
}
}
put_c(0x0D);
put_c(0x0A);
DelayMs(50000); //延時,好看數據
};
}
/**//***********************************************
**** 名 稱:AVR TWI使用範例_AT24C02
****
**** 作 者:zhiyu
**** 編譯器:WINAVR20070525
****
**** 參 考:http://www.ouravr.com/guide_index.html(TWI範例(AT24C02))
**** 日 期:2007.07.26
****
**** 芯 片:M16L
**** 時鐘源:外部4M晶振
****
**** 結 果:測試成功
**** 問 題:中斷裏面的state部分不理解,如自加操作,其他基本沒什麼問題
***********************************************/
/**//*
本程序簡單的示範瞭如何使用ATMEGA16的TWI 讀寫AT24C02 IIC EEPROM
TWI協議
(即IIC協議,請認真參考IIC協議的內容,否則根本就不能掌握)
一主多從的應用,M16作主機
(M16做從機和多主多從的應用不多,請自行參考相關文檔)
中斷模式
(因爲AVR的速度很高,而IIC的速度相對較低,
採用查詢模式會長時間獨佔CPU,令CPU的利用率明顯下降。
特別是IIC速度受環境影響只能低速通訊時,對系統的實時性產生嚴重的影響。
查詢模式可以參考其它文檔和軟件模擬IIC的文檔)
AT24C02/04/08的操作特點
出於簡化程序考慮,各種數據沒有對外輸出,學習時建議使用JTAG ICE硬件仿真器
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "usart.h"
#include <util/twi.h>
//定義了各種模式下的狀態碼列表(TWSR已屏蔽預分頻位),本文後面附上中文描述
//管腳定義
#define pinSCL 0 //PC0 SCL
#define pinSDA 1 //PC1 SDA
//爲保險起見,最好在SCL/SDA接上1~10K的外部上拉電阻到VCC。
#define FREQ 4 //FREQ爲系統振盪週期(以MHZ爲單位,因爲現在用的是外部4M晶振,所以FREQ=4)
#define fSCL 100000 //TWI時鐘爲100KHz
//預分頻係數=1(TWPS=0)
#if F_CPU < fSCL*36
#define TWBR_SET 10; //TWBR必須大於等於10
#else
#define TWBR_SET (F_CPU/fSCL-16)/2; //計算TWBR值
#endif
#define TW_ACT (1<<TWINT)|(1<<TWEN)|(1<<TWIE)
//TWCR只能IN/OUT,直接賦值比邏輯運算(|= &=)更節省空間
#define SLA_24CXX 0xA0 //24Cxx系列的廠商器件地址(高四位)
#define ADDR_24C02 0x00
// AT24C02的地址線A2/1/0全部接地,SLAW=0xA0+0x00<<1+0x00,SLAR=0xA0+0x00<<1+0x01
//TWI_操作狀態
#define TW_BUSY 0
#define TW_OK 1
#define TW_FAIL 2
//TWI_讀寫命令狀態
#define OP_BUSY 0
#define OP_RUN 1
//TWI讀寫操作公共步驟
#define ST_FAIL 0 //出錯狀態
#define ST_START 1 //START狀態檢查
#define ST_SLAW 2 //SLAW狀態檢查
#define ST_WADDR 3 //ADDR狀態檢查
//TWI讀操作步驟
#define ST_RESTART 4 //RESTART狀態檢查
#define ST_SLAR 5 //SLAR狀態檢查
#define ST_RDATA 6 //讀取數據狀態檢查,循環n字節
//TWI寫操作步驟
#define ST_WDATA 7 //寫數據狀態檢查,循環n字節
#define FAIL_MAX 20 //重試次數最大值
//定義全局變量
unsigned char ORGDATA[8]=
...{65,66,67,68,69,70,71,72}; //原始數據:64:A; 66:B; 67:c……
unsigned char CMPDATA[8]; //比較數據
unsigned char BUFFER[256]; //緩衝區,可以裝載整個AC24C02的數據
struct str_TWI //TWI數據結構
...{
volatile unsigned char STATUS; //TWI_操作狀態
unsigned char SLA; //從設備的器件地址
unsigned int ADDR; //從設備的數據地址
unsigned char *pBUF; //數據緩衝區指針
unsigned int DATALEN; //數據長度
unsigned char STATE; //TWI讀寫操作步驟
unsigned char FAILCNT; //失敗重試次數
};
struct str_TWI strTWI; //TWI的數據結構變量
//仿真時在watch窗口,監控這些全局變量。
//AT24C02的讀寫函數(包括隨機讀,連續讀,字節寫,頁寫)
//根據sla的最低位決定(由中斷程序中判斷)
//bit0=1 TW_READ 讀
//bit0=0 TW_WRITE 寫
// sla 器件地址(不能搞錯)
// addr EEPROM地址(0~1023)
// *ptr 讀寫數據緩衝區
// len 讀數據長度(1~1024),寫數據長度(1 or 8 or 16)
// 返回值 是否能執行當前操作
//此函數參考《AVR單片機GCC程序設計》P72和P120
void DelayMs(unsigned int t)
...{
unsigned int i;
for(i=0;i<t;i++)
_delay_loop_2(FREQ*4-1);
}
unsigned char TWI_RW(unsigned char sla,unsigned int addr,unsigned char *ptr,unsigned int len)
...{
unsigned char i;
if (strTWI.STATUS==TW_BUSY)
...{//TWI忙,不能進行操作
return OP_BUSY;
}
strTWI.STATUS=TW_BUSY;
i=(addr>>8)<<1;
i&=0x06; //考慮了24C04/08的EEPROM地址高位放在SLA裏面
strTWI.SLA=sla+i;
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中斷函數
這個函數流程只是考慮了器件地址後有一個字節數據(命令)地址的IIC器件
(大部分IIC接口器件都是這種類型,常見的例如AT24C01/02/04/08/16,DS1307,DS1721等)
對於有兩個字節數據地址的IIC器件(例如AT24C32/64/128/256等大容量EEPROM),請稍作改動
//根據strTWI.SLA的最低位決定
//bit0=1 TW_READ 讀
//bit0=0 TW_WRITE 寫
雖然中斷服務程序很長,但每次只執行一個 case,所以耗時並不長。
*/
ISR(TWI_vect)
...{//IIC中斷
unsigned char action,state,status;
action=strTWI.SLA&TW_READ; //取操作模式
state=strTWI.STATE;
status=TWSR&0xF8; //屏蔽預分頻位
if ((status>=0x60)||(status==0x00))
...{//總線錯誤或從機模式引發的中斷,不予處理
return;
}
switch(state)
...{
case ST_START: //START狀態檢查
if(status==TW_START)
...{//發送start信號成功
TWDR=strTWI.SLA&0xFE; //發送器件地址寫SLAW
TWCR=TW_ACT; //觸發下一步動作,同時清start發送標誌
}
else
...{//發送start信號出錯
state=ST_FAIL;
}
break;
case ST_SLAW: //SLAW狀態檢查
if(status==TW_MT_SLA_ACK)
...{//發送器件地址成功
TWDR=strTWI.ADDR; //發送eeprom地址
TWCR=TW_ACT; //觸發下一步動作
}
else
...{//發送器件地址出錯
state=ST_FAIL;
}
break;
case ST_WADDR: //ADDR狀態檢查
if(status==TW_MT_DATA_ACK)
...{//發送eeprom地址成功
if (action==TW_READ)
...{//讀操作模式
TWCR=(1<<TWSTA)|TW_ACT; //發送restart信號,下一步將跳到RESTART分支
}
else
...{//寫操作模式
TWDR=*strTWI.pBUF++; //寫第一個字節
strTWI.DATALEN--;
state=ST_WDATA-1; //下一步將跳到WDATA分支
TWCR=TW_ACT; //觸發下一步動作
}
}
else
...{//發送eeprom地址出錯
state=ST_FAIL;
}
break;
case ST_RESTART: //RESTART狀態檢查,只有讀操作模式才能跳到這裏
if(status==TW_REP_START)
...{//發送restart信號成功
TWDR=strTWI.SLA|TW_READ; //發器件地址讀SLAR(原來的程序只是TWDR=strTWI.SLA,不得)
TWCR=TW_ACT; //觸發下一步動作,同時清start發送標誌
}
else
...{//重發start信號出錯
state=ST_FAIL;
}
break;
case ST_SLAR: //SLAR狀態檢查,只有讀操作模式才能跳到這裏
if(status==TW_MR_SLA_ACK)
...{//發送器件地址成功
if (strTWI.DATALEN--)
...{//多個數據
TWCR=(1<<TWEA)|TW_ACT; //設定ACK,觸發下一步動作
}
else
...{//只有一個數據
TWCR=TW_ACT; //設定NAK,觸發下一步動作
}
}
else
...{//發送器件地址出錯
state=ST_FAIL;
}
break;
case ST_RDATA: //讀取數據狀態檢查,只有讀操作模式才能跳到這裏
state--; //循環,直到讀完指定長度數據
if(status==TW_MR_DATA_ACK)
...{//讀取數據成功,但不是最後一個數據
*strTWI.pBUF++=TWDR;
if (strTWI.DATALEN--)
...{//還有多個數據
TWCR=(1<<TWEA)|TW_ACT; //設定ACK,觸發下一步動作
}
else
...{//準備讀最後一個數據
TWCR=TW_ACT; //設定NAK,觸發下一步動作
}
}
else if(status==TW_MR_DATA_NACK)
...{//已經讀完最後一個數據
*strTWI.pBUF++=TWDR;
TWCR=(1<<TWSTO)|TW_ACT; //發送停止信號,不會再產生中斷了
strTWI.STATUS=TW_OK;
}
else
...{//讀取數據出錯
state=ST_FAIL;
}
break;
case ST_WDATA: //寫數據狀態檢查,只有寫操作模式才能跳到這裏
state--; //循環,直到寫完指定長度數據
if(status==TW_MT_DATA_ACK)
...{//寫數據成功
if (strTWI.DATALEN)
...{//還要寫
TWDR=*strTWI.pBUF++;
strTWI.DATALEN--;
TWCR=TW_ACT; //觸發下一步動作
}
else
...{//寫夠了
TWCR=(1<<TWSTO)|TW_ACT; //發送停止信號,不會再產生中斷了
strTWI.STATUS=TW_OK;
//啓動寫命令後需要10ms(最大)的編程時間才能真正的把數據記錄下來
//編程期間器件不響應任何命令
}
}
else
...{//寫數據失敗
state=ST_FAIL;
}
break;
default:
//錯誤狀態
state=ST_FAIL;
break;
}
if (state==ST_FAIL)
...{//錯誤處理
strTWI.FAILCNT++;
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;
unsigned int m,n,k,t;
//上電默認DDRx=0x00,PORTx=0x00 輸入,無上拉電阻
PORTA=0xFF; //不用的管腳使能內部上拉電阻。
PORTB=0xFF;
PORTC=0xFF; //SCL,SDA使能了內部的10K上拉電阻
//串口初始化
DDRD =(1<<PIN_TXD); //TXD爲輸出
PORTD =0xFF;
init_USART();
//TWI初始化
TWSR=0x00; //預分頻=4^0=1
TWBR=TWBR_SET;
TWAR=0x00; //主機模式,該地址無效
TWCR=0x00; //關閉TWI模塊
sei(); //使能全局中斷
put_s("Hello!");
put_s("這是一個簡單TWI程序");
put_s("請你仔細對照數據");
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)
...{
put_s("TWI寫操作FAIL!");
}
DelayMs(10); //延時等待編程完成
put_s("TWI寫操作succeed!");
while(1)
...{
//從0x10地址開始讀出8個字節數據放入CMPDATA數組中
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x10,&CMPDATA[0],8);
while(strTWI.STATUS==TW_BUSY); //等待操作完成
// 如果不加等待,則需要檢測返回值i才能知道當前操作是否執行了
// 0 OP_BUSY 之前的操作沒完成,沒執行當前操作
// 1 OP_RUN 當前操作執行中
if (strTWI.STATUS==TW_FAIL)
...{
put_s("TWI讀操作FAIL!");
}
//讀取成功,對比ORGDATA和CMPDATA的數據
put_s("原來ORGDATA的數據:");
for(m=0;m<8;m++)
...{
put_c(ORGDATA[m]);
put_c(' ');
}
put_c(0x0D);
put_c(0x0A);
put_s("新的CMPDATA的數據:");
for(n=0;n<8;n++)
...{
put_c(CMPDATA[n]);
put_c(' ');
}
put_c(0x0D);
put_c(0x0A);
//讀取整個AC24C02的數據
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x00,&BUFFER[0],256);
//從0x00地址開始讀出256個字節數據(整個ATC24C02)
while(strTWI.STATUS==TW_BUSY); //等待操作完成
put_s("整個AC24C02的數據:");
for(k=0,t=1;k<256;k++,t++)
...{
if(BUFFER[k]>=65 && BUFFER[k]<=72)
put_c(BUFFER[k]);
else
put_c(48);
put_c(' ');
if(t==16)
...{
put_c(0x0D);
put_c(0x0A);
t=0;
}
}
put_c(0x0D);
put_c(0x0A);
DelayMs(50000); //延時,好看數據
};
}
//Makefile,主要的幾項,只是針對我這裏的程序,要靈活運用哦
MCU = atmega16
F_CPU = 4000000
TARGET = main
SRC = TWI_main.c usart.c //多文件編譯纔會用到這一項,可以參考這個帖子:
http://www.mcublog.com/blog/user1/4266/archives/2006/6145.html
MCU = atmega16
F_CPU = 4000000
TARGET = main
SRC = TWI_main.c usart.c //多文件編譯纔會用到這一項,可以參考這個帖子:
http://www.mcublog.com/blog/user1/4266/archives/2006/6145.html
當然了,看一下協議本身是很重要的,也是一定的.這個例子不錯,值得一看.