IIC總線用於很多方面,TQ2440上有一塊AT24C02A芯片,可以由這個來學學IIC總線的使用。
這個實驗中,2440CPU作爲主機而AT24C02A作爲從機。下面在來看看IIC通信的時候所用到的基本信號類型。
在傳輸數據過程中有3種類型的信號:開始信號、結束信號、響應信號。
1. 開始信號(S):SCL爲高電平,SDA從高電平向低電平跳變。開始傳送數據。
2. 結束信號(P):SCL爲高電平,SDA從低電平向高電平跳變。結束傳送數據。
3. 響應信號(ACK):接收器在接受到8位數據後,在第9個時鐘週期,拉低SDA電平。
分析:主要要注意響應信號的時序,發送器發出第8位數據後會變成高電平,這個時候在第9個時鐘週期,接收器會將SDA拉低爲低電平。
接下來再來看看AT24C02A芯片對於通信數據格式要求:
要尋址AT24C02A的話,必須要明確其從機地址是如何確定的。這個芯片有A0、A1、A2三個管腳,對這三個管腳進行接地或者接VCC可以確定對其的尋址地址(一條總線上最多可以同時接8個AT24C02A所以必須要通過上述三個管腳來對其進行地址編排)。在尋址的時候還要指明芯片是作爲接收器還是發送器。AT24C02A芯片的前4位必須是1010。所以綜上所述可以得到如下的從機設備地址:
芯片內部是一個存儲器的功能,所以功能上需要發送片內地址和收發數據。其他更多的信息,參考《at24c02a的芯片資料重點翻譯》中的具體描述。
下面再看看,S3C2440對於IIC的具體操作。
S3C2440資料中,對於主機模式收/發和從機模式收/發都做了一個操作流程說明,這裏S3C2440作爲主機,所以就關注主機模式下的收/發。
主機模式發送流程操作
主機模式接受流程操作
後面程序就依照上述流程結合AT24C02A的數據寫和讀的各個流程。
參考了其他成熟程序後,確定本次試驗IIC的操作函數如下。
IIC初始化:void IIC_init(void)
主要負責管腳初始化以及各類寄存器初始化。
AT24C02A的讀函數:void rd24c02a(unsigned char wordAddr,unsigned char *buffer,int sizeofdate)
主要負責從AT24C02A的指定地址讀取數據。封裝了隨機讀和隨機順序讀。區別在於數據長度。
AT24C02A的寫函數:void wr24c02a(unsigned char wordAddr,unsigned char *buffer,int sizeofdate)
主要負責向AT24C02A的指定地址寫數據。封裝了字節寫和頁寫。區別在於數據長度。
AT24C02A的中斷函數:void at24_iic_interrupt(void)
主要負責控制芯片讀寫的控制。
分析:這個IIC的總線控制需要中斷的控制。這個中斷主要是表明數據已經發出或者接收。方便進行下一步操作。
下面是相關的程序:
void IIC_init(void)
{
//GPIO E'initialize
GPEUP = 0xc000;
GPECON = 0xa0000000;
//enable interrupt
//INTMSK &= ~(1 << 27);
//set IICCLK clock
IICCON = ACK_ENABLE | TIME_SRC_CHOOSE(PCLK_16) | TX_RX_INTERRUPT_EN | TX_FRE_COEFFICIENT(0xf);
IICADD = 0x10;//It's the cpu's own slave address,not use here
IICSTAT = 0x10;//enable IIC TX
}
分析:這個函數其實就是配置了IIC管腳所在的寄存器,然後配置了IIC工作的信號以及時鐘源的配置。熟悉這個流程便是。
//read from at24c02a,when sizeofdate equal 1,it will become radom read
void rd24c02a(unsigned char wordAddr,unsigned char *buffer,int sizeofdate)
{
unsigned int i,j;
unsigned char temp;
FLAG = 1;
IICDS = DEVADDR;
IICCON &= ~(0x10);
IICSTAT = 0xf0;
while(FLAG)
for(i = 0;i < 100;i++);
FLAG = 1;
IICDS = wordAddr;
IICCON &= ~(0x10);
while(FLAG)
for(i = 0;i < 100;i++);
FLAG = 1;
IICDS = DEVADDR;
IICCON &= ~(0x10);
IICSTAT = 0xb0;
while(FLAG)
for(i = 0;i < 100;i++);
FLAG = 1;
temp = IICDS;
IICCON &= ~(0x10);
while(FLAG)
for(i = 0;i < 100;i++);
for(i = 0;i < sizeofdate;i++)
{
FLAG = 1;
if(i == sizeofdate - 1)
IICCON &= ~(0x80);
buffer[i] = IICDS;
IICCON &= ~(0x10);
while(FLAG)
for(j = 0;j < 100;j++);
}
IICSTAT = 0x90;
IICCON = 0xaf;
for(i = 0;i < 10000;i++);
}
分析:這個函數封裝了AT24C02A的隨機地址讀和頁地址讀,不過在實踐過程發現使用這個函數進行頁讀取的時候有如下情況(1).地址要從0開始8位對齊,否則會出現折返回讀的情況(不會跳入讀取下一頁的數據)。(2).各個頁非8位對齊地址讀取的時候也有不同的情況的產生(有點無規律,反正讀不出正確數據)。
看看上面函數的流程:首先是發出從設備尋找地址並指定爲寫入操作→寫入要讀取數據的首地址→發出從設備尋找地址並指定爲讀取操作→這時會先返回一個數據(這個是不需要的要拋棄,上面紅色部分)→按照給定的數據長度讀取數據(長度超過8位會折返讀)→結束操作。全部按照資料上給出的操作步驟來,理解透徹就可以。
//at24c02a page write,but when sizeofdate equal 1,it will become byte write
void wr24c02a(unsigned char wordAddr,unsigned char *buffer,int sizeofdate)
{
unsigned int i,j;
FLAG = 1;//when enter the interrupt,it will become 0
IICDS = DEVADDR;//slave addr;
IICCON &= ~(0x10);
IICSTAT = 0xf0;
while(FLAG == 1)
for(i = 0;i < 100;i++);
FLAG = 1;
IICDS = wordAddr;
IICCON &= ~(0x10);
while(FLAG)
for(i = 0;i < 100;i++);
//write data to device
for(i = 0;i < sizeofdate;i++)
{
FLAG = 1;
IICDS = buffer[i];
IICCON &= ~(0x10);
while(FLAG)
for(j = 0;j < 100;j++);
}
IICSTAT = 0xd0;
IICCON = 0xaf;
for(i = 0;i < 10000;i++);
}
分析:這個函數封裝了AT24C02A的隨機寫和頁寫,需要注意的時候是頁寫的時候超出一頁的邊界後會折返回這頁的開頭再寫。函數流程是按照S3C2440芯片資料上給出的程序框架構建。發出從設備尋址地址並指定寫入模式→向從設備寫入要寫入數據區的首地址→寫入數據到設備中(最多8個字節)→結束。
void at24_iic_interrupt(void)
{
unsigned int iicSt,i ;
SRCPND |= (1 << 27);
INTPND |= (1 << 27);
iicSt = IICSTAT;
if(iicSt & 0x08)
{
//bus arbitration failed
uart0_printf("bus arbitration failed/n/r");
}
for(i = 0;i < 100;i++);
FLAG = 0;
}
分析:這個函數是用於處理IIC中斷的函數,可以看出這個函數主要的作用便是將FLAG置爲0,好讓其上面的讀寫函數跳出循環進行下一步操作。這也如同串口一樣,接受到一個數據後產生中斷。至於函數中的某些細節,主要是爲了增強程序的穩定性和正確性(程序中的綠色標註是爲了防止設備在仲裁失敗後依然還進行操作,紅色部分是爲了防止延時不夠導致數據讀取出錯)。
void rdat24c02a(unsigned char wordAddr,unsigned char *buffer,int sizeofdate)
{
int i,j;
unsigned char temp_page[8],temp_byte[1];
if(wordAddr % 8 == 0)
{
for(i = wordAddr;((wordAddr + sizeofdate) - i) >= 8;i+=8)
{
rd24c02a(i,temp_page,8);
for(j = 0;j < 8;j++)
{
buffer[i - wordAddr + j] = temp_page[j];
}
}
for(j = 0;j < (wordAddr + sizeofdate - i);j++)
{
rd24c02a((i + j),temp_byte,1);
buffer[i - wordAddr + j] = temp_byte[0];
}
}
else
{
for(i = wordAddr;i < 8;i++)
{
rd24c02a(i,temp_byte,1);
buffer[i - wordAddr] = temp_byte[0];
}
for(i = wordAddr + 8 - wordAddr%8;wordAddr + sizeofdate - i >= 8;i+=8)
{rd24c02a(i,temp_page,8);
for(j = 0;j < 8;j++)
{
buffer[i - wordAddr + j] = temp_page[j];
}
}
for(j = 0;j < wordAddr + sizeofdate - i;j++)
{
rd24c02a((i + j),temp_byte,1);
buffer[i - wordAddr + j] = temp_byte[0];
}
}
}
分析:這個是對AT24C02A芯片讀取功能的一個封裝,目的是在使用函數的時候不用再注意8字節限制和首地址對齊之類的問題。只要地址的大小加上讀取數據的長度限制在2K之內就可以了。爲了提高效率,所以是頁讀和隨機讀結合的一種構造(雖然只用隨機讀程序結構會更簡單)。
void wrat24c02a(unsigned char wordAddr,unsigned char *buffer,int sizeofdate)
{
int i,j;
unsigned char temp_page[8],temp_byte[1];
if(wordAddr % 8 == 0)
{
for(i = wordAddr;((sizeofdate + wordAddr) - i) >= 8;i+=8)
{
for(j = 0;j < 8;j++)
{
temp_page[j] = buffer[i - wordAddr + j];
}
wr24c02a(i,temp_page,8);
}
for(j = 0;j < (sizeofdate - (i - wordAddr));j++)
{
temp_byte[0] = buffer[i - wordAddr + j];
wr24c02a((i + j),temp_byte,1);
}
}
else
{
for(i = wordAddr;i < 8;i++)
{
temp_byte[0] = buffer[i - wordAddr];
wr24c02a(i,temp_byte,1);
}
for(i = wordAddr + 8 - wordAddr%8;wordAddr + sizeofdate - i >= 8;i+=8)
{
for(j = 0;j < 8;j++)
{
temp_page[j] = buffer[i - wordAddr + j];
}
wr24c02a(i,temp_page,8);
}
for(j = 0;j < wordAddr + sizeofdate - i;j++)
{
temp_byte[0] = buffer[i - wordAddr + j];
wr24c02a(i + j,temp_byte,1);
}
}
}
這個是對wr24c02a函數的一種封裝。目的與讀函數rdat24c02a一樣,構造思路上幾乎是一樣的。
到此,關於IIC器件AT24C02A芯片的各種初始化以及操作函數展示完了。可以建立一個main函數來調用他們。當然,前提是開啓中斷。至於這些函數在本次試驗的後期測試是基於《TQ2440的學習——S3C2440基於串口中斷實現的一個簡單串口控制檯》串口控制檯程序,裏面是不是留出兩個命令“read iic”和“write iic”的處理位置?現在就要將它們用起來。
注意下面的程序片段:
if(!(no_system_strcmp("read iic",cmd_buf)))
{
no_system_memset(cmd_buf,0,50);
uart0_printf("you input the command read iic/n/r");
uart0_printf("the data are:");
no_system_memset(temp_data_page,0,31);
rdat24c02a(6,temp_data_page,30);
uart0_printf(temp_data_page);
uart0_printf("/n/r");
}
else if(!(no_system_strcmp("write iic",cmd_buf)))
{
no_system_memset(cmd_buf,0,50);
unsigned int j;
char word = 'a';
for(j = 0;j < 30;j++,word++)
{
if(word > 'z')
word = 'a';
temp_data_page[j] = word;
}
temp_data_page[30] = '/0';
uart0_printf("you input the command write iic/n/r");
wrat24c02a(6,temp_data_page,30);
uart0_printf("data write complatly/n/r");
}
總結:
這次程序需要總結的主要是AT24C02A的相關特性,在這個器件的基礎上應該要對IIC協議和S3C2440對IIC的各種操作熟悉纔是。在讀寫AT24C02A方面,除了注意中斷要延時以保證正確的讀寫數據意外,在讀或寫操作發出停止信號後要延時較長時間以使停止信號正確的發出。在程序過程中,由於堆棧分配的區域問題,會出現亂碼的情況(如果在main函數定義數組過大,在操作的時候會出現打印不全或者某個功能失效)。全局變量在程序也沒用起來(所以後來使用宏定義來裝載讀寫的一個全局變量值(指FLAG))。總之,讓我更加深刻的理解了操作系統存在的必要性。而且無操作系統狀態的內存分配是個相當嚴重的問題。對於malloc的構建在後面應該要涉及到,方便後面在無操作系統狀態寫更加複雜的程序。代碼不緊湊,堆棧分配沒有策略,內存無法自由分配,全局變量無法再最終程序得到有效組織,都是今後需要解決的問題。
參考書籍:
趙春江《s3c2440的IIC應用——讀寫AT24C02A》http://blog.csdn.net/zhaocj/archive/2010/04/12/5477152.aspx
韋東山《嵌入式Linux應用開發完全手冊》
S3C2440芯片手冊、AT24C02A芯片手冊