ARM9的IIC
實(shí)驗(yàn)前須知:
I. IIC中斷發(fā)生的三種情況:
1>當(dāng)發(fā)出地址信息或接收到一個(gè)從機(jī)地址并且吻合時(shí)
2>當(dāng)總線仲裁失敗時(shí)
3>當(dāng)發(fā)送或接收完一個(gè)字節(jié)的數(shù)據(jù)(包括響應(yīng)位)時(shí)
II.啟動(dòng)或恢復(fù)IIC傳輸?shù)膬煞N方法:
1>當(dāng)IICCON[4]即中斷狀態(tài)為0時(shí),通過寫IICSTAT寄存器啟動(dòng)IIC操作。
2>當(dāng)IICCON[4]即中斷狀態(tài)為1時(shí),表示IIC操作被暫停。在這期間設(shè)置好其他寄存器之后,向IICCON[4]寫入0即可恢復(fù)IIC操作。
具體操作可參考s3c2440芯片手冊的流程。在這里僅分析IIC主機(jī)發(fā)送和IIC主機(jī)讀取。
實(shí)驗(yàn)的目的:
從型號(hào)為AT24C02C的EEPROM中寫數(shù)據(jù),然后去讀寫入的數(shù)據(jù)。s3c2440中的IIC主控制器作為IIC主設(shè)備。
實(shí)驗(yàn)的源程序:
at24cxx.rar
實(shí)驗(yàn)的問題總結(jié):
I. 我們重點(diǎn)分析一下IIC.C程序,程序如下:
/*
* FILE: i2c.c
* 用于主機(jī)發(fā)送/接收
*/
#include
#include "s3c24xx.h"
#include "i2c.h"
void Delay(int time);
#define WRDATA (1)
#define RDDATA (2)
typedef struct tI2C {
unsigned char *pData; /* 數(shù)據(jù)緩沖區(qū) */
volatile int DataCount; /* 等待傳輸?shù)臄?shù)據(jù)長度 */
volatile int Status; /* 狀態(tài) */
volatile int Mode; /* 模式:讀/寫 */
volatile int Pt; /* pData中待傳輸數(shù)據(jù)的位置 */
}tS3C24xx_I2C, *ptS3C24xx_I2C;
static tS3C24xx_I2C g_tS3C24xx_I2C;
/*
* I2C初始化
*/
void i2c_init(void)
{
GPEUP "= 0xc000; // 禁止內(nèi)部上拉
GPECON |= 0xa0000000; // 選擇引腳功能:GPE15:IICSDA, GPE14:IICSCL
INTMSK &= ~(BIT_IIC);
/* bit[7] = 1, 使能ACK
* bit[6] = 0, IICCLK = PCLK/16
* bit[5] = 1, 使能中斷
* bit[3:0] = 0xf, Tx clock = IICCLK/16
* PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz
*/
IICCON = (1<<7) | (0<<6) | (1<<5) | (0xf); // 0xaf
IICADD = 0x10; // S3C24xx slave address = [7:1]
IICSTAT = 0x10; // I2C串行輸出使能(Rx/Tx)
}
/*
* 主機(jī)發(fā)送
* slvAddr : 從機(jī)地址,buf : 數(shù)據(jù)存放的緩沖區(qū),len : 數(shù)據(jù)長度
*/
void i2c_write(unsigned int slvAddr, unsigned char *buf, int len)
{
g_tS3C24xx_I2C.Mode = WRDATA; // 寫操作
g_tS3C24xx_I2C.Pt = 0; // 索引值初始為0
g_tS3C24xx_I2C.pData = buf; // 保存緩沖區(qū)地址
g_tS3C24xx_I2C.DataCount = len; // 傳輸長度
IICDS = slvAddr;
IICSTAT = 0xf0; // 主機(jī)發(fā)送,啟動(dòng)
/* 等待直至數(shù)據(jù)傳輸完畢 */
while (g_tS3C24xx_I2C.DataCount != -1);
}
/*
* 主機(jī)接收
* slvAddr : 從機(jī)地址,buf : 數(shù)據(jù)存放的緩沖區(qū),len : 數(shù)據(jù)長度
*/
void i2c_read(unsigned int slvAddr, unsigned char *buf, int len)
{
g_tS3C24xx_I2C.Mode = RDDATA; // 讀操作
g_tS3C24xx_I2C.Pt = -1; // 索引值初始化為-1,表示第1個(gè)中斷時(shí)不接收數(shù)據(jù)(地址中斷)
g_tS3C24xx_I2C.pData = buf; // 保存緩沖區(qū)地址
g_tS3C24xx_I2C.DataCount = len; // 傳輸長度
IICDS = slvAddr;
IICSTAT = 0xb0; // 主機(jī)接收,啟動(dòng)
/* 等待直至數(shù)據(jù)傳輸完畢 */
while (g_tS3C24xx_I2C.DataCount != 0);
}
/*
* I2C中斷服務(wù)程序
* 根據(jù)剩余的數(shù)據(jù)長度選擇繼續(xù)傳輸或者結(jié)束
*/
void I2CIntHandle(void)
{
unsigned int iicSt,i;
// 清中斷
SRCPND = BIT_IIC;
INTPND = BIT_IIC;
iicSt = IICSTAT;
if(iicSt & 0x8){ printf("Bus arbitration failednr"); }
switch (g_tS3C24xx_I2C.Mode)
{
case WRDATA:
{
if((g_tS3C24xx_I2C.DataCount--) == 0)
{
// 下面兩行用來恢復(fù)I2C操作,發(fā)出P信號(hào)
IICSTAT = 0xd0;
IICCON = 0xaf;
Delay(10000); // 等待一段時(shí)間以便P信號(hào)已經(jīng)發(fā)出
break;
}
IICDS = g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++];
// 將數(shù)據(jù)寫入IICDS后,需要一段時(shí)間才能出現(xiàn)在SDA線上
for (i = 0; i < 10; i++);
IICCON = 0xaf; // 恢復(fù)I2C傳輸
break;
}
case RDDATA:
{
if (g_tS3C24xx_I2C.Pt == -1)
{
// 這次中斷是發(fā)送I2C設(shè)備地址后發(fā)生的,沒有數(shù)據(jù)
// 只接收一個(gè)數(shù)據(jù)時(shí),不要發(fā)出ACK信號(hào)
g_tS3C24xx_I2C.Pt = 0;
if(g_tS3C24xx_I2C.DataCount == 1)
IICCON = 0x2f; // 恢復(fù)I2C傳輸,開始接收數(shù)據(jù),接收到數(shù)據(jù)時(shí)不發(fā)出ACK
else
IICCON = 0xaf; // 恢復(fù)I2C傳輸,開始接收數(shù)據(jù)
break;
}
g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++] = IICDS;
g_tS3C24xx_I2C.DataCount--;
if (g_tS3C24xx_I2C.DataCount == 0)
{
// 下面兩行恢復(fù)I2C操作,發(fā)出P信號(hào)
IICSTAT = 0x90;
IICCON = 0xaf;
Delay(10000); // 等待一段時(shí)間以便P信號(hào)已經(jīng)發(fā)出
break;
}
else
{
// 接收最后一個(gè)數(shù)據(jù)時(shí),不要發(fā)出ACK信號(hào)
if(g_tS3C24xx_I2C.DataCount == 1)
IICCON = 0x2f; // 恢復(fù)I2C傳輸,接收到下一數(shù)據(jù)時(shí)無ACK
else
IICCON = 0xaf; // 恢復(fù)I2C傳輸,接收到下一數(shù)據(jù)時(shí)發(fā)出ACK
}
break;
}
default:
break;
}
}
/*
* 延時(shí)函數(shù)
*/
void Delay(int time)
{
for (; time > 0; time--);
}
/****************************************************************************
代碼分析
關(guān)于IIC的初始化,在這里就不贅述了,我們先進(jìn)入main.c中看一下,我們會(huì)有一個(gè)at24cxx_write()函數(shù)的調(diào)用,
其實(shí)質(zhì)是調(diào)用了i2c_write()函數(shù)。接著我們進(jìn)入到i2c_write()函數(shù)去看看。在i2c_write()函數(shù)中,slvAddr
表示要發(fā)送的設(shè)備地址,當(dāng)代碼執(zhí)行IICSTAT = 0xf0這條語句之后,就會(huì)發(fā)送S信號(hào)及設(shè)備地址到從機(jī)上,
待從機(jī)應(yīng)答之后,從機(jī)就會(huì)做出一個(gè)ACK信號(hào)給主機(jī),通過第一張流程圖我們可以看到,在響應(yīng)之后,
就會(huì)進(jìn)入到中斷處理函數(shù)里邊。接著我們進(jìn)到I2CIntHandle()里邊看一下。由于我們的Mode是WRDATA,
所以會(huì)進(jìn)入到case WRDATA里邊。在if((g_tS3C24xx_I2C.DataCount--) == 0)這條語句中,
由于我們的g_tS3C24xx_I2C.DataCount的初值是為2的。所以這條if語句不會(huì)執(zhí)行。接下來我們看到:
在IICDS = g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++]這條語句中,由于g_tS3C24xx_I2C.pData[0]和g_tS3C24xx_I2C.pData[1]中
存放的數(shù)據(jù)分別為要寫入數(shù)據(jù)的存儲(chǔ)地址和寫入的數(shù)據(jù),所以第一次在執(zhí)行這條語句時(shí),是將要寫入數(shù)據(jù)的存儲(chǔ)地址賦給IICDS寄存器。
接下來是IICCON = 0xaf這條語句,在執(zhí)行這條語句之后,IIC傳輸恢復(fù)。此時(shí)從機(jī)接收到要寫入數(shù)據(jù)的存儲(chǔ)地址之后,做出ACK響應(yīng)。
那么又再一次進(jìn)入中斷。往后分析就大體類似,在此就不贅述了。i2c_read()函數(shù)的分析過程相似。
*****************************************************************************/
II. 在IICDS = g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++]中,g_tS3C24xx_I2C.pData[]的長度len是不包括設(shè)備地址的。
它表示的是數(shù)據(jù)長度,在AT24XX中,就是包括word address 和 要傳輸?shù)臄?shù)據(jù)。你可以查看一下AT24XX芯片手冊。
word address 是指AT24XX內(nèi)部將要寫數(shù)據(jù)的存儲(chǔ)地址,而不是AT24XX的設(shè)備地址。它其實(shí)也是包含在數(shù)據(jù)那一塊。這是根據(jù)AT24XX來的。在發(fā)送完設(shè)備地址之后,需要再發(fā)送你要寫入數(shù)據(jù)的存儲(chǔ)地址。最后再發(fā)送你想寫入的數(shù)據(jù)。
III.思考:AT24XX中,如何用8位尋遍其所有的地址?
這在設(shè)備地址中有用到關(guān)于頁的選擇,所以就達(dá)到了8位尋遍所有的地址的要求。
IV.思考:讀數(shù)據(jù)的時(shí)候,是從從機(jī)的什么地址開始讀的呢?
參考一下AT24XX芯片手冊,本程序中使用的是當(dāng)前地址讀,即在你發(fā)送完設(shè)備地址之后,從設(shè)備就傳輸當(dāng)前地址的數(shù)據(jù)給主設(shè)備