手上的一塊S3C2410的開發板搭載VxWorks 5.5操作系統,今天開始繼續學習BSP源碼和驅動程序,爭取在碩士畢業前能自己實現BSP的配置。
IIC接口
這裏簡單複習一下IIC接口的相關知識。
IIC/I2C(Inter-Integrated Circuit)總線由Philips公司針對MCU需要研製的二線式串行總線,用於MCU及外圍設備通信。
IIC總線主要特點:
- IIC總線長度可高達7.6m,並能夠以100kb/s的最大傳輸速率支持40個組件。
- IIC總線支持多主控。主控能控制信息傳輸和時鐘頻率。任一時刻只有一個主控。
IIC總線要求:
- 各個設備必須共地
- 兩根信號線必須接上拉電阻
多IIC設備接口示意圖:
IIC總線的狀態及信號
由於上拉電阻的存在,SCL和SDA在總線處於空閒狀態均爲高電平。
主控器想使用總線就應當先拉低SCL,控制總線,傳送完成後應當釋放總線。
啓動信號:SCL爲高,SDA上產生下降沿
結束信號:SCL爲高,SDA上產生上升沿
應答/響應信號(A/NA) — 數據接收者接收到1字節後,應當主動向數據發出者發送一個應答信號。對應SCL的第9個應答時鐘脈衝,SDA爲低表示應答,繼續發送;SDA爲高表示非應答,結束髮送。
地址信號(尋址字節) —
bit | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
DA3 | DA2 | DA1 | DA0 | A2 | A1 | A0 | R/nW |
其中,(DA3-DA0)爲器件的固有地址編碼,由器件生產廠家給定;(A2、A1、A0)爲器件地址引腳電平,高爲1,接地爲0;讀寫控制位(R/nW),爲1表示主機讀,爲0表示主機寫。
等待狀態: 從機接收完一個數據字節後,可以拉低SCL使總線系統進入等待狀態;當從機完成數據處理後,再釋放SCL線,主機方可繼續發送數據。
IIC總線數據傳輸格式
- 一般格式
- 主控制器寫操作格式
- 主控制器讀操作格式
- 主控制器讀/寫操作格式
好了,IIC總線接口相關知識就複習到此,現在來看看S3C2410的IIC接口
S3C2410 IIC接口
先來看看S3C2410下的IIC串行總線控制器框圖:
S3C2410的端口E的GPE15用作數據線SDA,GPE14用作連續時鐘線SCL。使用時要記得外部加上拉電阻。
發送數據寫入到 IICDS
接收數據從IICDS讀取
S3C2410的4個IIC專用寄存器:
Register | Address | Read or Write | Description |
---|---|---|---|
IICCON | 0x54000000 | R/W | IIC總線控制寄存器 |
IICSTAT | 0x54000004 | R/W | IIC總線控制/狀態寄存器 |
IICADD | 0x54000008 | R/W | IIC總線地址寄存器 |
IICDS | 0x5400000C | R/W | IIC數據發送/接收寄存器 |
IICSTAT的常用控制字:
- 啓動主設備發送的控制字爲 0xF0 (Tx)
- 結束主設備發送的控制字爲 0xD0 (Tx)
- 啓動主設備接收的控制字爲 0xB0 (Rx)
- 結束主設備接收的控制字爲 0x90 (Rx)
IICADD 地址寄存器僅對從設備有意義。
S3C2410 IIC串行總線編程
直接看圖吧:
IIC主發送模式流程
IIC主接收模式流程
S3C2410 IIC 驅動程序
/* 文件模塊說明:
* 2410Iic.c S3C2410內部驅動,初始化驅動器,外部EERPROM採用
* ATMEL AT24C128-128kbit
* 文件版本:
* 開發人員:
* 創建時間:
* Copyright(c) t0 - t1 CompanyName Limited Co.
*/
#include "intLib.h"
#include "2410addr.h" // 寄存器地址宏頭文件
// 24C128IIC總線定義
#define CN_IIC_VOLUME (0x4000) // IIC容量,16KB
#define AT24128256_w (0xa0 ) // 寫地址:原理圖中 A2A1A0 均接地
#define AT24128256_r (0xa1 ) // 讀地址
#define IIC_mode_mask (0x3f )
#define IIC_mtx (0xc0 ) // 主機發送模式
#define IIC_mrx (0x80 ) // 主機接收模式
#define pagelength ( 64 )
//------------------------------------------------------------------------------
// 初始化IIC程序
void IIC_init(void)
{
UINT32 dwValReg;
// GPE14 ~ GPE15爲IIC接口
// GPECON[31:28] = 0b 1010 = 配置GPE14爲IICSCL, GPE15爲IICSDA
SNGS3C_REG_READ( rGPECON, dwValReg );
dwValReg = (dwValReg & 0xAFFFFFFF);
dwValReg = (dwValReg | 0xA0000000);
SNGS3C_REG_WRITE( rGPECON, dwValReg );
// IICCON[7] ENABLE
// IICCON[6] Tx Clock Source, 0 = IICCLK = fPCLK / 16; 1 = IICCLK = fPCLK / 512
// IICCON[5] Interrupt Enable
// 50Mhz/16/(9+1) = 312.5Khz
// AT24C128 在 3.3V 時的最大工作頻率爲 400KHz
*(volatile UINT32 *)rIICCON=(1<<7)|(0<<6)|(1<<5)|9; // = 0xa9
*(volatile UINT32 *)rIICSTAT=0xd0; // 狀態控制字:0xd0 結束主設備發送
*(volatile UINT32 *)rIICADD=0x00; // 器件地址 0x00
}
// 函數名稱:IIC_WaitBus
// 函數功能:等待IIC總線操作結束
// 返回值: 若操作等待超時返回 false; 正確則返回 true
char IIC_WaitBus(void)
{
UINT i=0;
// IICCON[4] - Interrupt is depending
while( !( (*(volatile UINT32*)rIICCON) & 0x10 ) ) // no interruption
{
i++;
if(i > 1000) // 超時
return (FALSE);
}
return (TRUE); // 還未超時就有中斷產生
}
// 函數名稱:IIC_WaitAck
// 函數功能:等待IIC總線應答信號
// 返回值: 若操作等待超時或得不到應答信號,返回 false; 正確則返回 true
// 說明: 先等待操作結束信號,然後判斷是否有應答信號
char IIC_WaitAck(void)
{
UINT i=0;
while( !( (*(volatile UINT32*)rIICCON) & 0x10) ) // 查詢中斷信號
{
i++;
if(i>1000)
return (FALSE);
}
if( (*(volatile UINT32*)rIICSTAT) & 0x1 ) // ACK was not received
return (FALSE);
return (TRUE);
}
// 函數名稱:IIC_WaitEnd
// 函數功能:等待寫操作結束
// 返回值:
char IIC_WaitEnd(void)
{
int i, k;
// 嘗試100次寫地址並等待應答,只要收到一次即認爲寫操作成功,否則寫操作失敗
for(i=0; i<100; i++)
{
*(volatile UINT32*) rIICDS = AT24128256_w; // IIC器件AT24C128寫地址
for(k=0; k<100; k++) ; // 延時
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
*(volatile UINT32*) rIICSTAT = 0xf0; // 0XF0 啓動發送狀態字
k = 0;
if(TRUE == IIC_WaitAck())
return (TRUE);
}
return (FALSE);
}
// 函數名稱:IIC_Read
// 函數功能:讀芯片
// 返回值: 0 讀成功; 1 讀失敗
// 讀24C128
UINT32 IIC_Read(UINT32 start, char * buf, UINT length)
{
int i, iicbusy, k;
// 讀出起始地址+數據長度大於芯片容量(字節)則退出
if( (start+length) > CN_IIC_VOLUME ) return;
iicbusy = IIC_WaitEnd(); // 等待寫操作結束
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICDS = (UINT8) (start/256); // 地址高字節
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
if(FALSE == IIC_WaitAck())
return 1;
*(volatile UINT32*) rIICDS = (UINT8) (start%256); // 地址低字節
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
if(FALSE == IIC_WaitAck())
return 1;
*(volatile UINT32*) rIICSTAT = 0xb0; // 狀態控制字: 0XB0 啓動主設備接收
*(volatile UINT32*) rIICDS = AT24128256_r; // IIC器件AT24C128讀地址
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9; // = 0xa9
if(FALSE == IIC_WaitAck())
return 1;
for(i=0; i<length; i++)
{
if(i == length - 1)
{
*(volatile UINT32*) rIICCON = 0xa9; // (1<<7)|(0<<6)|(1<<5)|9
*(volatile UINT32*) rIICCON = 0x29; // 禁止應答
}
else
{
*(volatile UINT32) rIICCON = 0xa9;
}
if(FALSE == IIC_WaitBus()) // 等待總線操作結束
return ;
buf[i] = *(volatile UINT32*) rIICDS;
}
*(volatile UINT32*) rIICSTAT = 0x90; // 狀態控制字: 0x90 停止主設備接收
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICCON = 0xa9; // (1<<7)|(0<<6)|(1<<5)|9
return 0;
}
// 函數名稱:IIC_Write
// 函數功能:寫芯片
// 返回值: 0 讀成功; 1 讀失敗
// 寫24C128
UINT32 IIC_Write(UINT32 start, char * buf, UINT length)
{
int i, j, iicbusy, k, addr = start;
// 寫入地址大於芯片容量(字節)則退出
if( (addr+length) > CN_IIC_VOLUME ) return;
iicbusy = IIC_WaitEnd(); // 等待寫操作結束
*(volatile UINT32*) rIICDS = (UINT8) (addr/256); // 地址高字節
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
if(FALSE == IIC_WaitAck())
return 1;
*(volatile UINT32*) rIICDS = (UINT8) (addr%256); // 地址低字節
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
if(FALSE == IIC_WaitAck())
return 1;
j = FALSE;
for(i=0; i<length; i++)
{
if(j)
{
j = FALSE;
iicbusy = IIC_WaitEnd(); // 等待寫操作結束
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICDS = (UINT8) ((addr+i)/256); // 地址高字節
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
if(FALSE == IIC_WaitAck())
return 1;
*(volatile UINT32*) rIICDS = (UINT8) (addr+i); // 地址低字節
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
if(FALSE == IIC_WaitAck())
return 1;
}
*(volatile UINT32*) rIICDS = buf[i]; // 發送字節數據
for(k=0; k<100; k++) ;
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
if(FALSE == IIC_WaitAck())
return 1;
// 沒有寫完全部length字節長度的數據 並且還沒有寫滿一整頁,則繼續寫入
if( (i<length) && (0 == ((addr+i+1) % pagelength)) )
{
*(volatile UINT32*) rIICSTAT = 0xd0; // 狀態控制字:0xd0 主設備發送結束, 產生停止信號
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
taskDelay(1);
j = TRUE;
}
}
if(!j)
{
*(volatile UINT32*) rIICSTAT = 0xd0; // 狀態控制字:0xd0 主設備發送結束, 產生停止信號
*(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
}
taskDelay(1);
return 0;
}
// 函數功能:向定值區寫入數據,先讀,再擦除,最後寫
// 輸入參數:wAddress 寫入區地址 0-0x10 0000
// pbyBuff 爲待寫入字節流指針
// dwLen 爲字節流長度
// 返回值: 0 成功; 1 失敗
STATUS Iic_Write_Data(UINT32 wAddress, UINT8* pbyBuf, UINT32 dwLen)
{
UINT32 ret;
ret = IIC_Write(wAddress, pbyBuf, dwLen);
return ret;
}
// 函數功能:從定值區讀取數據
// 輸入參數:wAddress 爲讀取區地址
// pbyBuf 爲待讀取字節流指針
// dwLen 爲字節流長度
// 返回值: 0 成功; 1 失敗
STATUS Iic_Read_Data(UINT32 wAddress, UINT8 *pbyBuf, UINT32 dwLen)
{
UINT32 ret;
ret = IIC_Read(wAddress, pbyBuf, dwLen);
return ret;
}
// 函數功能:向定值區寫入半字(16位)
// 輸入參數:wAddress 爲寫入區地址, wBuf 爲待寫入數據
// 返回值: 0 成功; 1 失敗
STATUS Iic_Write_Word(UINT32 wAddress, UINT16 wData)
{
UINT32 ret;
UINT8 buf[2];
buf[0] = wData & 0x00ff;
buf[1] = (wData >> 8) & 0x00ff;
ret = Iic_Write_Data(wAddress, &buf[0], 2);
return ret;
}
// 函數功能:從定值區讀取半字(16位)
// 輸入參數:wAddress 爲讀取區地址
// 返回值: 待讀取數據,低16位有效
UINT16 Iic_Read_Word(UINT32 wAddress)
{
UINT8 buf[2];
UINT16 wData, wData1, wData2;
Iic_Read_Data(wAddress, &buf[0], 2);
wData1 = buf[0];
wData2 = buf[1];
wData = (wData1 & 0x00ff | (wData2&0x00ff) << 8);
return (wData);
}
今晚就把IIC驅動代碼分享到這裏,明天到系統上實際運行試試,看看能否成功寫入並讀取數據。
驗證結果
2016/09/28
在這裏做了一個簡單的測試,申請一個長度爲50的字符數組並清空,向IIC器件寫入字符串 “I love China!”,短暫延時後,從IIC器件裏面讀回來存到 字符數組中,並打印出來。可以看到,結果正確。
證明以上驅動程序,完全正確。