基于S5PC100裸機(ji)程(cheng)序之SPI(下(xia))
時間:2014-08-02 來(lai)源:未知
3 SPI接口應用示例
這(zhe)里將介紹一種通(tong)(tong)過SPI通(tong)(tong)信的Flash,該芯片是(shi)M24PXX,如圖5所示(shi)為(wei)該芯片的原理圖,每根(gen)接線的意義已(yi)經清楚地標識出(chu)來了。

圖5 M25PXX原理圖
這一(yi)款(kuan)芯(xin)片內部集(ji)成了(le)12條指(zhi)令(ling),包括了(le)通(tong)用(yong)的讀、寫、配置等(deng)命令(ling),還有一(yi)個內置的狀(zhuang)(zhuang)態(tai)寄存器,可以通(tong)過該寄存器獲取(qu)芯(xin)片當前狀(zhuang)(zhuang)態(tai)。
如(ru)表7所示(shi)為M5DPXX芯(xin)片指令集。
表(biao)7 M25PXX芯(xin)片指令集
| Instrucion | Description | One-byle Instruction Code | Address Bytes | Dummy Bytes | Date Bytes | |
| WREN | 寫使能 | 0000 0110 | 06h | 0 | 0 | 0 |
| WRDI | 寫禁止 | 0000 0100 | 04h | 0 | 0 | 0 |
| RDID | 讀取ID | 1001 1111 | 9Fh | 0 | 0 | 1 to 3 |
| RDSR | 讀狀態寄存器 | 0000 0101 | 05h | 0 | 0 | 1 to ∞ |
| WRSR | 寫狀態寄存器 | 0000 0001 | 01h | 0 | 0 | 1 |
| READ | 字節數據讀取 | 0000 0011 | 03h | 3 | 0 | 1 to ∞ |
| FAST_READ | 高速的字節讀取 | 0000 1011 | 0Bh | 3 | 1 | 1 to ∞ |
| PP | 頁編程 | 0000 0010 | 02h | 3 | 0 | 1 to 256 |
| SE | 扇區擦除 | 1101 1000 | D8h | 3 | 0 | 0 |
| BE | 塊擦除 | 1100 0111 | C7h | 0 | 0 | 0 |
| DP | 深度擦除 | 1011 1001 | B9h | 0 | 0 | 0 |
| RES | 從睡眠模式喚醒以及讀出電特性 | 1010 1011 | ABh | 0 | 3 | 1 to ∞ |
| 喚醒 | 0 | 0 | 0 |
有了上(shang)文的知識做鋪墊,現(xian)在我(wo)們(men)先來看一(yi)下SPI控(kong)制器的基本(ben)編程模型:
(1)設置時鐘源并配置分頻值等參數。
(2)軟復(fu)位后,并(bing)設置(zhi)SPI配置(zhi)寄存器(SPI CONFIGURATION REGISTER)。
(3)設置模式寄存器。
(4)設置從機選擇寄存器。
(5)收(shou)發數據。
根據(ju)以上信(xin)息,這里(li)分成若干模塊來(lai)逐一(yi)實現。
1.相關寄存器結構體定義及宏定義。
/*SPI總線控制器寄存器定義*/
typedef struct {
unsigned int CHCFG;
unsigned int CLKCFG;
unsigned int MODECFG;
unsigned int SLAVESEL;
unsigned int INTEN;
unsigned int STATUS;
unsigned int TXDATA;
unsigned int RXDATA;
unsigned int PACKETCNT;
unsigned int PENDINGCLR;
unsigned int SWAPCFG;
unsigned int FBCLK;
}spi;
#define SPI0 ( * (volatile spi *)0XEC300000 )
/* Flash opcodes. */
#define OPCODE_WREN 0x06 /*寫使能*/
#define OPCODE_WRDA 0x04 /*寫禁止*/
#define OPCODE_RDSR 0x05 /*讀狀態寄存器*/
#define OPCODE_WRSR 0x01 /*寫狀態寄存器*/
#define OPCODE_NORM_READ 0x03 /*低頻率的數據讀取*/
#define OPCODE_FAST_READ 0x0b /*高頻率的數據讀取*/
#define OPCODE_PP 0x02 /*頁編程*/
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
#define OPCODE_CHIP_ERASE 0xc7 /*擦除整塊芯片*/
#define OPCODE_SE 0xd8 /*扇區擦除*/
#define OPCODE_RDID 0x9f /*讀ID */
/*狀態寄存器*/
#define SR_WIP 1 /*寫狀態中*/
#define SR_WEL 2 /*寫保護鎖*/
2.延時函數,使能芯片及禁用芯片的實現如下。
void delay(int times)
{
volatile int i,j;
for (j = 0; j < times; j++){
for (i = 0; i < 100000; i++);
i = i + 1;
}
}
void disable_chip(void)
{
/* disable chip*/
SPI0.SLAVESEL |= 0x1;
delay(1);
}
void enable_chip(void)
{
/* enable chip*/
SPI0.SLAVESEL &= ~0x1;
delay(1);
}
3.軟件復位的代碼如下
void soft_reset(void)
{
SPI0.CHCFG |= 0x1 << 5;
delay(1);
SPI0.CHCFG &= ~(0x1 << 5);
}
4.接收(字節讀)的實現。
在實現讀字節功能(neng)的時(shi)候(hou),第一次發送(song)讀指令后(hou),緊接(jie)著芯片就會回送(song)數據,這時(shi)芯片會自動累加(jia)地址,因此需(xu)要(yao)人為(wei)控制所(suo)讀數據的范圍(wei),讀時(shi)序如圖6所(suo)示。

圖6 讀時序
void receive(unsigned char *buf, int len)
{
int i;
SPI0.CHCFG &= ~0x1; // disable Tx
SPI0.CHCFG |= 0x1 << 1; // enable Rx
delay(1);
for (i = 0; i < len; i++){
buf[i] = SPI0.RXDATA;
//S5PC100 SPI支持在FiFo為空時,讀緩存產生SPI時鐘,否則需要發送數據產生時鐘
delay(1);
}
SPI0.CHCFG &= ~(0x1 << 1);
}
5.發送(寫頁面)的實現。
在發(fa)出寫指令后,要緊(jin)跟(gen)著發(fa)出第一個數據要存放的地址,這個時候再順(shun)序寫入數據,和讀的時候一樣,芯片(pian)會自動累加地址,芯片(pian)的頁(ye)面為256Bytes,寫時序如圖7所示。

圖7 寫時序
關鍵代碼如下。
void transfer(unsigned char *data, int len)
{
int i;
SPI0.CHCFG &= ~(0x1 << 1);
SPI0.CHCFG = SPI0.CHCFG | 0x1; // enable Tx and disable Rx
delay(1);
for (i = 0; i < len; i++){
SPI0.TXDATA = data[i];
while( !(SPI0.STATUS & (0x1 << 21)) );
delay(1);
}
SPI0.CHCFG &= ~0x1;
}
6.擦除芯片相關操作如下。
這塊芯片提(ti)供了兩種(zhong)擦除(chu)(chu)方式,第一種(zhong)是(shi)分扇區來進行(xing)擦除(chu)(chu),重點(dian)在于選好指(zhi)定的地址,然后進行(xing)擦除(chu)(chu),結合如圖8所示的時(shi)序(xu)圖。

圖(tu)8 擦除扇區時序圖(tu)
第二種是整(zheng)片(pian)芯片(pian)擦(ca)除(chu),如圖9所(suo)示(shi)為整(zheng)片(pian)芯片(pian)的擦(ca)除(chu)時的時序(xu)情況。

圖9 整片芯片擦除(chu)時(shi)序圖
關鍵代碼如下(xia)。
/*擦除扇區*/
void erase_sector(int addr)
{
unsigned char buf[4];
buf[0] = OPCODE_SE;
buf[1] = addr >> 16;
buf[2] = addr >> 8;
buf[3] = addr;
enable_chip();
transfer(buf, 4);
disable_chip();
}
/*擦除芯片*/
void erase_chip()
{
unsigned char buf[4];
buf[0] = OPCODE_CHIP_ERASE;
enable_chip();
transfer(buf, 1);
disable_chip();
}
7.解析狀態。
判(pan)斷芯(xin)片工作是(shi)否結束,如圖10所示為狀態寄(ji)存器讀時序(xu)。

圖10 狀態(tai)寄存(cun)器(qi)讀時(shi)序
關鍵代碼如(ru)下。
void wait_till_write_finished()
{
unsigned char buf[1];
enable_chip();
buf[0] = OPCODE_RDSR;
transfer(buf, 1);
while(1) {
receive(buf, 1);
if(buf[0] & SR_WIP) {
// printf( "Write is still in progress\n" );
}
else {
printf( "Write is finished.\n" );
break;
}
}
disable_chip();
}
8.讀芯片ID。
讀ID時序如圖11所示。

圖11 讀ID時序
關鍵代碼如下。
void read_ID(void)
{
unsigned char buf[3];
int i;
buf[0] = OPCODE_RDID;
soft_reset();
enable_chip();
transfer(buf, 1);
receive(buf, 3);
disable_chip();
printf("MI = %x\tMT = %x\tMC = %x\t\n", buf[0], buf[1], buf[2]);
}
9.主程序
int main()
{
unsigned char buf[10] = "home\n";
unsigned char data[10] = "morning\n";
uart0_init();
printf("aaaaa \n");
cfg_gpio(); //配置SPI IO功能
set_clk(); //使能SPI控制器的時鐘
cfg_spi0(); //配置SPI0控制器
while(1)
{
read_ID(); //讀出SPI Flash的ID號
write_spi(buf, 4, 0); //向目標芯片0地址寫入4個字節數據
read_spi(data, 4, 0); //從目標芯片0地址讀出4個字節數據
printf("read from spi :%s", data);
}
return 0;
}

