【摘要】 本篇文章就介紹如何在Linux系統(tǒng)下編寫W25Q64芯片的驅(qū)動,完成數(shù)據(jù)存儲,W25Q64支持標(biāo)準(zhǔn)SPI總線,當(dāng)前驅(qū)動程序底層的代碼寫了兩種方式,一種是采用內(nèi)核提供的SPI子系統(tǒng)框架,一種直接采用軟件模擬SPI時(shí)序的方式驅(qū)動,具體代碼在第3章貼出來了。
1. W25QXX介紹
W25Q64是一顆SPI接口的Flash存儲芯片,是華邦W25QXX系列里的一個(gè)具體型號,這個(gè)系列里包含了W25Q16,W25Q32,W25Q64,W5Q128等等。編程代碼邏輯都差不多,主要是容量的區(qū)別。
本篇文章就介紹如何在Linux系統(tǒng)下編寫W25Q64芯片的驅(qū)動,完成數(shù)據(jù)存儲,W25Q64支持標(biāo)準(zhǔn)SPI總線,當(dāng)前驅(qū)動程序底層的代碼寫了兩種方式,一種是采用內(nèi)核提供的SPI子系統(tǒng)框架,一種直接采用軟件模擬SPI時(shí)序的方式驅(qū)動,具體代碼在第3章貼出來了。
下面是來至W25Qxx中文手冊的介紹
W25Q64 (64M-bit), W25Q16(16M-bit)和 W25Q32(32M-bit)是為系統(tǒng)提供一個(gè)最小的空間、引腳和功耗的存儲器解決方案的串行 Flash 存儲器。 25Q 系列比普通的串行 Flash 存儲器更靈活,性能更優(yōu)越。基于雙倍/四倍的 SPI,它們能夠可以立即完成提供數(shù)據(jù)給 RAM, 包括存儲聲音、文本和數(shù)據(jù)。芯片支持的工作電壓 2.7V 到 3.6V,正常工作時(shí)電流小于 5mA,掉電時(shí)低于 1uA。所有芯片提供標(biāo)準(zhǔn)的封裝。
W25Q64/16/32 由每頁 256 字節(jié)組成。 每頁的 256 字節(jié)用一次頁編程指令即可完成。 每次可以擦除 16 頁(1 個(gè)扇區(qū))、 128 頁(32KB 塊)、 256 頁(64KB 塊)和全片擦除。W25Q64 的內(nèi)存空間結(jié)構(gòu): 一頁 256 字節(jié), 4K(4096 字節(jié))為一個(gè)扇區(qū), 16 個(gè)扇區(qū)為 1 塊, 容量為 8M 字節(jié),共有 128 個(gè)塊,2048 個(gè)扇區(qū)。W25Q64/16/32 支持標(biāo)準(zhǔn)串行外圍接口(SPI),和高速的雙倍/四倍輸出,雙倍/四倍用的引腳:串行時(shí)鐘、片選端、串行數(shù)據(jù) I/O0(DI)、 I/O1(DO)、 I/O2(WP)和 I/O3(HOLD)。 SPI 最高支持 80MHz,當(dāng)用快讀雙倍/四倍指令時(shí),相當(dāng)于雙倍輸出時(shí)最高速率 160MHz,四倍輸出時(shí)最高速率 320MHz。這個(gè)傳輸速率比得上 8 位和 16 位的并行 Flash 存儲器。HOLD 引腳和寫保護(hù)引腳可編程寫保護(hù)。此外,芯片支持 JEDEC 標(biāo)準(zhǔn),具有唯一的 64 位識別序列號。
●SPI 串行存儲器系列
-W25Q64:64M 位/8M 字節(jié)
-W25Q16:16M 位/2M 字節(jié)
-W25Q32:32M 位/4M 字節(jié)
-每256字節(jié)可編程頁
2. 硬件環(huán)境
當(dāng)前測試使用的開發(fā)板采用友善之臂的Tiny4412開發(fā)板,芯片是三星的EXYNOS-4412,最高主頻1.5GHZ。
開發(fā)板引出了SPI的IO口,這里使用的W25Q64是外置的模塊,使用杜邦線與開發(fā)板的IO口連接。
開發(fā)板上引出的IO口都是5V和1.8V,為了方便供電,采用了一個(gè)USB轉(zhuǎn)TTL模塊提供電源,測試驅(qū)動。
W25Q64模塊接在開發(fā)板的SPI0接口上面的。
Linux內(nèi)核自帶有SPI子系統(tǒng)的設(shè)備端示例代碼:
Linux 內(nèi)核自帶的 SPI 驅(qū)動注冊示例代碼: \drivers\spi\spidev.c
Linux 內(nèi)核自帶的 SPI APP 注冊示例代碼: \Documentation\spi
如果要使用內(nèi)核自帶SPI驅(qū)動,可以在內(nèi)核編譯時(shí)配置一下。
root# make menuconfig
Device Drivers --->
[*] SPI support --->
<*> Samsung S3C64XX series type SPI
[*] Samsung S3C64XX Channel 0 Support.
Tiny4412自帶內(nèi)核里的SPI設(shè)備端結(jié)構(gòu):
?
SPI0的具體GPIO口位置:
3. 案例代碼
3.1 模擬SPI時(shí)序-編寫驅(qū)動
下面是W25Q64的驅(qū)動測試代碼,沒有注冊字符設(shè)備框架,只是在驅(qū)動的入口里測試時(shí)序是否OK,打印了ID,讀寫了數(shù)據(jù)進(jìn)行測試。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*--------------------------------W25Q64相關(guān)操作代碼---------------------------------------------*/
/*定義指針,用于接收虛擬地址*/
volatile unsigned int *W25Q64_GPBCON;
volatile unsigned int *W25Q64_GPBDAT;
/*
函數(shù)功能:W25Q64初始化
Tiny4412硬件連接:
DO--MISO :GPB_2 //輸入模式
DI--MOSI :GPB_3 //輸出模式
CLK-SCLK :GPB_0 //時(shí)鐘
CS--CS :GPB_1 //片選
*/
void W25Q64_Init(void)
{
/*1. 初始化GPIO*/
/*映射物理地址*/
W25Q64_GPBCON=ioremap(0x11400040,4);
W25Q64_GPBDAT=ioremap(0x11400044,4);
*W25Q64_GPBCON &= ~(0xf << 0 * 4);*W25Q64_GPBCON |= (0x1 << 0 * 4);
*W25Q64_GPBCON &= ~(0xf << 1 * 4);*W25Q64_GPBCON |= (0x1 << 1 * 4);
*W25Q64_GPBCON &= ~(0xf << 2 * 4);
*W25Q64_GPBCON &= ~(0xf << 3 * 4);*W25Q64_GPBCON |= (0x1 << 3 * 4);
/*2. 上拉GPIO口*/
//*W25Q64_GPBDAT &= ~(1 << 4);//輸出0
*W25Q64_GPBDAT |= (1 << 0); //輸出1
*W25Q64_GPBDAT |= (1 << 1); //輸出1
*W25Q64_GPBDAT |= (1 << 3); //輸出1
}
/*
函數(shù)功能:SPI時(shí)序讀寫一個(gè)字節(jié)
說 明:SPI底層時(shí)序,程序的移植接口
*/
u8 W25Q64_SPI_ReadWriteOneByte(u8 data_tx)
{
u8 data_rx=0;
u8 i;
for(i=0;i<8;i++)
{
*W25Q64_GPBDAT &= ~(1 << 0);//輸出0
if(data_tx&0x80)*W25Q64_GPBDAT |= (1 << 3); //輸出1
else *W25Q64_GPBDAT &= ~(1 << 3);//輸出0
data_tx<<=1; //繼續(xù)發(fā)送下一個(gè)數(shù)據(jù)
*W25Q64_GPBDAT |= (1 << 0); //輸出1
data_rx<<=1;
if((*W25Q64_GPBDAT & (1 << 2)))data_rx|=0x01;
}
return data_rx;
}
/*
函數(shù)功能:寫使能
*/
void W25Q64_WriteEnabled(void)
{
*W25Q64_GPBDAT &= ~(1 << 1); //選中W25Q64
W25Q64_SPI_ReadWriteOneByte(0x06);
*W25Q64_GPBDAT |= (1 << 1); //取消選中W25Q64
}
/*
函數(shù)功能:讀狀態(tài)
*/
void W25Q64_GetBusyStat(void)
{
unsigned char stat=1;
while(stat&0x01) //判斷狀態(tài)最低位
{
*W25Q64_GPBDAT &= ~(1 << 1);
W25Q64_SPI_ReadWriteOneByte(0x05);
stat=W25Q64_SPI_ReadWriteOneByte(0xFF); //讀取狀態(tài)寄存器的值
*W25Q64_GPBDAT |= (1 << 1);
}
}
/*
函數(shù)功能:讀取設(shè)備ID和制造商ID
W25Q64: EF16
W25QQ128:EF17
*/
unsigned short W25Q64_ReadDeviceID(void)
{
unsigned short ID;
*W25Q64_GPBDAT &= ~(1 << 1);
W25Q64_SPI_ReadWriteOneByte(0x90);
W25Q64_SPI_ReadWriteOneByte(0x0);
W25Q64_SPI_ReadWriteOneByte(0x0);
W25Q64_SPI_ReadWriteOneByte(0x0);
ID=W25Q64_SPI_ReadWriteOneByte(0xFF)<<8; //制造商ID
ID|=W25Q64_SPI_ReadWriteOneByte(0xFF); //設(shè)備ID
*W25Q64_GPBDAT |= (1 << 1);
return ID;
}
/*
函數(shù)功能:全片擦除
*/
void W25Q64_ClearAll(void)
{
W25Q64_WriteEnabled(); //寫使能
W25Q64_GetBusyStat(); //檢測狀態(tài)寄存器
*W25Q64_GPBDAT &= ~(1 << 1);
W25Q64_SPI_ReadWriteOneByte(0xC7);
*W25Q64_GPBDAT |= (1 << 1);
W25Q64_GetBusyStat(); //檢測狀態(tài)寄存器
}
/*
函數(shù)功能:頁編程
參 數(shù):
unsigned int addr:寫入的地址
void *p:將要寫入的數(shù)據(jù)
unsigned int len:寫入的長度
說 明:每次最多只能寫入256字節(jié)
*/
void W25Q64_PageWrite(unsigned int addr,void*p,unsigned int len)
{
unsigned short i;
unsigned char *buff=p;
W25Q64_WriteEnabled(); //寫使能
*W25Q64_GPBDAT &= ~(1 << 1);
W25Q64_SPI_ReadWriteOneByte(0x02);
W25Q64_SPI_ReadWriteOneByte(addr>>16);
W25Q64_SPI_ReadWriteOneByte(addr>>8);
W25Q64_SPI_ReadWriteOneByte((unsigned char)addr);
for(i=0;i>16);
W25Q64_SPI_ReadWriteOneByte(addr>>8);
W25Q64_SPI_ReadWriteOneByte((unsigned char)addr);
*W25Q64_GPBDAT |= (1 << 1);
W25Q64_GetBusyStat(); //檢測狀態(tài)寄存器
}
/*
函數(shù)功能:數(shù)據(jù)讀取
參 數(shù):
*/
void W25Q64_ReadData(unsigned int addr,void *p,unsigned int len)
{
unsigned int i=0;
unsigned char *buff=p;
*W25Q64_GPBDAT &= ~(1 << 1);
W25Q64_SPI_ReadWriteOneByte(0x03);
W25Q64_SPI_ReadWriteOneByte(addr>>16);
W25Q64_SPI_ReadWriteOneByte(addr>>8);
W25Q64_SPI_ReadWriteOneByte((unsigned char)addr);
for(i=0;i256)page_remain=256;
else page_remain=len;
}
}
/*
函數(shù)功能:在任意地址寫入任意數(shù)據(jù),對扇區(qū)進(jìn)行校驗(yàn)
參 數(shù):
unsigned int addr:寫入數(shù)據(jù)的地址
void *p :寫入的數(shù)據(jù)
unsigned int len :寫入數(shù)據(jù)的長度
說明:一個(gè)扇區(qū)的空間4096字節(jié)
*/
unsigned char W25Q64_BUFF[1024*4]; //用來檢驗(yàn)一個(gè)扇區(qū)的數(shù)據(jù)是否需要擦除
void W25Q64_WriteData(unsigned int addr,void *p,unsigned int len)
{
unsigned int sector_len=4096-addr%4096; //剩余空間大小
unsigned char *buff=p;
unsigned int i=0;
if(len4096)
{
sector_len=4096;
}
else
{
sector_len=len;
}
}
}
static int __init w25q64_init(void)
{
/*1. 初始化GPIO口*/
W25Q64_Init();
/*2. 打印廠商芯片ID*/
unsigned short id=W25Q64_ReadDeviceID();
printk("id=0x%X\n",id);
/*3. 寫入數(shù)據(jù)*/
char buff[]="W25Q64-test-123456789ABCDEFG";
W25Q64_WriteData(100,buff,strlen(buff));
printk("write-data:%s\n",buff);
/*4. 讀出數(shù)據(jù)*/
char buff_rx[100];
W25Q64_ReadData(100,buff_rx,strlen(buff));
printk("read-data:%s\n",buff_rx);
return 0;
}
static void __exit w25q64_exit(void)
{
/*釋放虛擬地址*/
iounmap(W25Q64_GPBCON);
iounmap(W25Q64_GPBDAT);
printk("w25q64 driver exit ok!\n");
}
module_exit(w25q64_exit);
module_init(w25q64_init);
MODULE_LICENSE("GPL");
)>;i++)>;i++)>
3.2 采用SPI子系統(tǒng)框架-編寫驅(qū)動
下面代碼使用SPI子系統(tǒng)框架編寫的驅(qū)動測試代碼,注冊了字符設(shè)備框架,但是只是做了簡單的測試,目的只是測試W25Q64是否可以正常驅(qū)動,能讀寫存儲。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /*雜項(xiàng)字符設(shè)備頭文件*/
#include /*文件操作集合*/
#include
/*--------------------------------W25Q64相關(guān)操作代碼---------------------------------------------*/
struct spi_device *w25q64_spi_Device;
/*
函數(shù)功能:W25Q64初始化
Tiny4412硬件連接:
DO--MISO :GPB_2 //輸入模式
DI--MOSI :GPB_3 //輸出模式
CLK-SCLK :GPB_0 //時(shí)鐘
CS--CS :GPB_1 //片選
*/
/*
函數(shù)功能:讀取設(shè)備ID和制造商ID
W25Q64: EF16
W25QQ128:EF17
參數(shù):0x90表示讀取ID號的指令
*/
unsigned short W25Q64_ReadDeviceID(void)
{
/*使用硬件SPI同步讀寫時(shí)序*/
char tx_buf[6]={0x90,0x0,0x0,0x0,0xFF,0xFF};
char rx_buf[6];
struct spi_message m;
struct spi_transfer t=
{
.tx_buf=tx_buf,
.rx_buf=rx_buf,
.len=6,
.delay_usecs=0,
.speed_hz=1000000,
.bits_per_word=8
};
spi_message_init(&m);
spi_message_add_tail(&t,&m);
spi_sync(w25q64_spi_Device,&m);
return rx_buf[4]<<8|rx_buf[5]; /*得到ID值*/
}
/*
函數(shù)功能:指定位置讀取指定長度的數(shù)據(jù)
參 數(shù):
0x03 表示讀取數(shù)據(jù)的指令。
*/
void W25Q64_ReadData(unsigned int addr,void *p,unsigned int len)
{
/*使用硬件SPI同步讀寫時(shí)序*/
char tx_buf[4];
tx_buf[0]=0x03; //讀指令
tx_buf[1]=addr>>16; //以下是地址指令
tx_buf[2]=addr>>8;
tx_buf[3]=addr;
spi_write(w25q64_spi_Device,tx_buf,4);
spi_read(w25q64_spi_Device,p,len);
}
/*
函數(shù)功能:寫使能
*/
void W25Q64_WriteEnabled(void)
{
/*使用硬件SPI同步讀寫時(shí)序*/
char tx_buf[1]={0x06};
struct spi_message m;
struct spi_transfer t=
{
.tx_buf=tx_buf,
.len=1,
.delay_usecs=0,
.speed_hz=1000000,
.bits_per_word=8
};
spi_message_init(&m);
spi_message_add_tail(&t,&m);
spi_sync(w25q64_spi_Device,&m);
}
/*
函數(shù)功能:讀狀態(tài)
*/
void W25Q64_GetBusyStat(void)
{
unsigned char stat=1;
/*使用硬件SPI同步讀寫時(shí)序*/
char tx_buf[2]={0x05,0xFF};
char rx_buf[2];
while(stat&0x01) //判斷狀態(tài)最低位
{
struct spi_message m;
struct spi_transfer t=
{
.tx_buf=tx_buf,
.rx_buf=rx_buf,
.len=2,
.delay_usecs=0,
.speed_hz=1000000,
.bits_per_word=8
};
spi_message_init(&m);
spi_message_add_tail(&t,&m);
spi_sync(w25q64_spi_Device,&m);
stat=rx_buf[1]; //得到狀態(tài)寄存器
}
}
/*
函數(shù)功能:扇區(qū)擦除
參 數(shù):
unsigned int addr:扇區(qū)的地址
說 明:一個(gè)扇區(qū)是4096字節(jié),擦除一個(gè)扇區(qū)時(shí)間至少150ms
*/
void W25Q64_ClearSector(unsigned int addr)
{
W25Q64_WriteEnabled(); //寫使能
W25Q64_GetBusyStat(); //檢測狀態(tài)寄存器
/*使用硬件SPI同步讀寫時(shí)序*/
unsigned char tx_buf[4];
tx_buf[0]=0x20;
tx_buf[1]=addr>>16;
tx_buf[2]=addr>>8;
tx_buf[3]=addr;
char rx_buf[4];
struct spi_message m;
struct spi_transfer t=
{
.tx_buf=tx_buf,
.rx_buf=rx_buf,
.len=4,
.delay_usecs=0,
.speed_hz=1000000,
.bits_per_word=8
};
spi_message_init(&m);
spi_message_add_tail(&t,&m);
spi_sync(w25q64_spi_Device,&m);
W25Q64_GetBusyStat(); //檢測狀態(tài)寄存器
}
/*
函數(shù)功能:頁編程
參 數(shù):
unsigned int addr:寫入的地址
void *p:將要寫入的數(shù)據(jù)
unsigned int len:寫入的長度
說 明:每次最多只能寫入256字節(jié)
*/
void W25Q64_PageWrite(unsigned int addr,void*p,unsigned int len)
{
unsigned short i;
unsigned char *buff=p;
W25Q64_WriteEnabled(); //寫使能
/*使用硬件SPI同步讀寫時(shí)序*/
unsigned char tx_buf[4];
tx_buf[0]=0x02; //頁寫指令
tx_buf[1]=(addr>>16)&0xFF; //以下是地址指令
tx_buf[2]=(addr>>8)&0xFF;
tx_buf[3]=(addr&0xFF);
//寫數(shù)據(jù)
spi_write(w25q64_spi_Device,tx_buf,4);
//寫數(shù)據(jù)
spi_write(w25q64_spi_Device,p,len);
W25Q64_GetBusyStat(); //檢測狀態(tài)寄存器
}
/*
函數(shù)功能:在任意地址寫入任意數(shù)據(jù),不進(jìn)行校驗(yàn)
參 數(shù):
unsigned int addr:寫入數(shù)據(jù)的地址
void *p :寫入的數(shù)據(jù)
unsigned int len :寫入數(shù)據(jù)的長度
*/
void W25Q64_WriteDataONCheck(unsigned int addr,void *p,unsigned int len)
{
unsigned char *buff=p;
unsigned short page_remain=256-addr%256; //當(dāng)前地址開始一頁剩下的空間
unsigned short remain_len; //剩余未寫入的長度
if(len256)page_remain=256;
else page_remain=len;
}
}
/*
函數(shù)功能:在任意地址寫入任意數(shù)據(jù),對扇區(qū)進(jìn)行校驗(yàn)
參 數(shù):
unsigned int addr:寫入數(shù)據(jù)的地址
void *p :寫入的數(shù)據(jù)
unsigned int len :寫入數(shù)據(jù)的長度
說明:一個(gè)扇區(qū)的空間4096字節(jié)
*/
static unsigned char W25Q64_BUFF[1024*4]; //用來檢驗(yàn)一個(gè)扇區(qū)的數(shù)據(jù)是否需要擦除
void W25Q64_WriteData(unsigned int addr,void *p,unsigned int len)
{
unsigned int sector_len=4096-addr%4096; //剩余空間大小
unsigned char *buff=p;
unsigned int i=0;
if(len4096)
{
sector_len=4096;
}
else
{
sector_len=len;
}
}
}
/*
雜項(xiàng)字符設(shè)備注冊示例----->LED
*/
static int tiny4412_open(struct inode *my_inode, struct file *my_file)
{
return 0;
}
static int tiny4412_release(struct inode *my_inode, struct file *my_file)
{
return 0;
}
static ssize_t tiny4412_read(struct file *my_file, char __user *buf, size_t len, loff_t *loff)
{
/*2. 打印廠商芯片ID*/
unsigned short id=W25Q64_ReadDeviceID();
printk("-ID=0x%X\n",id);
/*3. 寫入數(shù)據(jù)*/
char buff[100]="打印廠商芯片ID打印廠商芯片ID";
W25Q64_WriteData(0,buff,100);
/*4. 讀出數(shù)據(jù)*/
char buff_rx[100];
W25Q64_ReadData(0,buff_rx,100);
printk("Read=%s\n",buff_rx);
return 0;
}
static ssize_t tiny4412_write(struct file *my_file, const char __user *buf, size_t len, loff_t *loff)
{
return 0;
}
/*文件操作集合*/
static struct file_operations tiny4412_fops=
{
.open=tiny4412_open,
.read=tiny4412_read,
.write=tiny4412_write,
.release=tiny4412_release
};
/*
核心結(jié)構(gòu)體
*/
static struct miscdevice tiny4412_misc=
{
.minor=MISC_DYNAMIC_MINOR, /*自動分配次設(shè)備號*/
.name="tiny4412_W25q64", /*設(shè)備文件,指定/dev/生成的文件名稱*/
.fops=&tiny4412_fops
};
static int __devinit w25q64_probe(struct spi_device *spi)
{
/*配置SPI模式*/
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;
spi->max_speed_hz=1*1000000; //1Mhz
if(spi_setup(spi)<0)//配置
{
printk("SPI配置失敗!\n");
}
/*保存指針指向*/
w25q64_spi_Device=spi;
printk("w25q64 probe ok!\n");
printk("SpiNum=%d\n",spi->dev.id);
/*雜項(xiàng)設(shè)備注冊*/
misc_register(&tiny4412_misc);
return 0;
}
static int __devexit w25q64_remove(struct spi_device *spi)
{
/*雜項(xiàng)設(shè)備注銷*/
misc_deregister(&tiny4412_misc);
return 0;
}
static struct spi_driver w25q64_spi_driver = {
.driver = {
.name = "spidev",
.owner =THIS_MODULE,
},
.probe =w25q64_probe,
.remove = __devexit_p(w25q64_remove),
};
/*-------------------------------------------------------------------------*/
static int __init w25q64_init(void)
{
spi_register_driver(&w25q64_spi_driver);
printk("w25q64 driver install ok!\n");
return 0;
}
static void __exit w25q64_exit(void)
{
spi_unregister_driver(&w25q64_spi_driver);
printk("w25q64 driver exit ok!\n");
}
module_exit(w25q64_exit);
module_init(w25q64_init);
MODULE_LICENSE("GPL");)>)>
-
FlaSh
+關(guān)注
關(guān)注
10文章
1598瀏覽量
147339 -
驅(qū)動
+關(guān)注
關(guān)注
12文章
1790瀏覽量
84910 -
w25Q64
+關(guān)注
關(guān)注
1文章
15瀏覽量
2988
發(fā)布評論請先 登錄
相關(guān)推薦
評論