當前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > 基于S5PC100裸機程序之SPI(下)
3 SPI接口應(yīng)用示例
這里將介紹一種通過SPI通信的Flash,該芯片是M24PXX,如圖5所示為該芯片的原理圖,每根接線的意義已經(jīng)清楚地標識出來了。
圖5 M25PXX原理圖
這一款芯片內(nèi)部集成了12條指令,包括了通用的讀、寫、配置等命令,還有一個內(nèi)置的狀態(tài)寄存器,可以通過該寄存器獲取芯片當前狀態(tài)。
如表7所示為M5DPXX芯片指令集。
表7 M25PXX芯片指令集
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 | 讀狀態(tài)寄存器 | 0000 0101 | 05h | 0 | 0 | 1 to ∞ |
WRSR | 寫狀態(tài)寄存器 | 0000 0001 | 01h | 0 | 0 | 1 |
READ | 字節(jié)數(shù)據(jù)讀取 | 0000 0011 | 03h | 3 | 0 | 1 to ∞ |
FAST_READ | 高速的字節(jié)讀取 | 0000 1011 | 0Bh | 3 | 1 | 1 to ∞ |
PP | 頁編程 | 0000 0010 | 02h | 3 | 0 | 1 to 256 |
SE | 扇區(qū)擦除 | 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 |
有了上文的知識做鋪墊,現(xiàn)在我們先來看一下SPI控制器的基本編程模型:
(1)設(shè)置時鐘源并配置分頻值等參數(shù)。
(2)軟復(fù)位后,并設(shè)置SPI配置寄存器(SPI CONFIGURATION REGISTER)。
(3)設(shè)置模式寄存器。
(4)設(shè)置從機選擇寄存器。
(5)收發(fā)數(shù)據(jù)。
根據(jù)以上信息,這里分成若干模塊來逐一實現(xiàn)。
1.相關(guān)寄存器結(jié)構(gòu)體定義及宏定義。
/*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 /*讀狀態(tài)寄存器*/
#define OPCODE_WRSR 0x01 /*寫狀態(tài)寄存器*/
#define OPCODE_NORM_READ 0x03 /*低頻率的數(shù)據(jù)讀取*/
#define OPCODE_FAST_READ 0x0b /*高頻率的數(shù)據(jù)讀取*/
#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 /*扇區(qū)擦除*/
#define OPCODE_RDID 0x9f /*讀ID */
/*狀態(tài)寄存器*/
#define SR_WIP 1 /*寫狀態(tài)中*/
#define SR_WEL 2 /*寫保護鎖*/
2.延時函數(shù),使能芯片及禁用芯片的實現(xiàn)如下。
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.軟件復(fù)位的代碼如下
void soft_reset(void)
{
SPI0.CHCFG |= 0x1 << 5;
delay(1);
SPI0.CHCFG &= ~(0x1 << 5);
}
4.接收(字節(jié)讀)的實現(xiàn)。
在實現(xiàn)讀字節(jié)功能的時候,第一次發(fā)送讀指令后,緊接著芯片就會回送數(shù)據(jù),這時芯片會自動累加地址,因此需要人為控制所讀數(shù)據(jù)的范圍,讀時序如圖6所示。
圖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為空時,讀緩存產(chǎn)生SPI時鐘,否則需要發(fā)送數(shù)據(jù)產(chǎn)生時鐘
delay(1);
}
SPI0.CHCFG &= ~(0x1 << 1);
}
5.發(fā)送(寫頁面)的實現(xiàn)。
在發(fā)出寫指令后,要緊跟著發(fā)出第一個數(shù)據(jù)要存放的地址,這個時候再順序?qū)懭霐?shù)據(jù),和讀的時候一樣,芯片會自動累加地址,芯片的頁面為256Bytes,寫時序如圖7所示。
圖7 寫時序
關(guān)鍵代碼如下。
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.擦除芯片相關(guān)操作如下。
這塊芯片提供了兩種擦除方式,第一種是分扇區(qū)來進行擦除,重點在于選好指定的地址,然后進行擦除,結(jié)合如圖8所示的時序圖。
圖8 擦除扇區(qū)時序圖
第二種是整片芯片擦除,如圖9所示為整片芯片的擦除時的時序情況。
圖9 整片芯片擦除時序圖
關(guān)鍵代碼如下。
/*擦除扇區(qū)*/
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.解析狀態(tài)。
判斷芯片工作是否結(jié)束,如圖10所示為狀態(tài)寄存器讀時序。
圖10 狀態(tài)寄存器讀時序
關(guān)鍵代碼如下。
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時序
關(guān)鍵代碼如下。
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個字節(jié)數(shù)據(jù)
read_spi(data, 4, 0); //從目標芯片0地址讀出4個字節(jié)數(shù)據(jù)
printf("read from spi :%s", data);
}
return 0;
}