一、背景
1.1 硬件驅動層
工作也有幾年了,越來越覺得有必要做一個類似于LINUX的硬件驅動層,可以屏蔽所有硬件差異,再做新的項目,需要更改主控或者外設時,只需要更改相關的芯片支持包,讓我們更加專注于業務邏輯層的開發。
1.2 目標
RTTHREAD已經有現成的相關軟件框架,到時候可以借鑒一下,目前還是想自己寫代碼實現一遍以下的東西,只有真正做起來的才會發現問題,才會有進步的可能:
bootloader+
hardware_driver_layer+
fatfs+
usb host,usb device+
lcd+touch+lvgl+
freertos(rtthread)+
memory_manage+
(nandflash or norflash)+(psramor+sdram)+
script(java,lua,python,jerryscript)+
openharmony_ui_kit_ace_lite
二、目標狀態
目前已經實現了
bootloader+
fatfs+
usb device(usb mass storage)+
script(java)
下一個目標是lcd+touch+lvgl
三、SPI FLASH(NOR FLASH)
nor flash操作起來非常方便,不需要關系nand flash的虛擬地址映射、壞塊管理、ECC等問題。
3.1 w25q128
w25q128是winbond公司的,顧名思義有128Mbit容量,也就是16M字節大小,每頁有256字節,每個扇區有4096字節,每個塊有64K,每次可擦除一個扇區字節大小。
3.2 驅動程序
這里使用的是spi2作為通信接口
#include "stdio.h"
#include "common.h"
#include "stm32_kernel.h"
#include "stm32f10x_spi.h"
#include "stm32_spi.h"
#include "deviceaccess_spi.h"
#include "deviceaccess_gpio.h"
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17
////////////////////////////////////////////////////////////////////////////
//指令表
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F
void W25QXX_Init(void);
u16 W25QXX_ReadID(void); //讀取FLASH ID
u8 W25QXX_ReadSR(void); //讀取狀態寄存器
void W25QXX_Write_SR(u8 sr); //寫狀態寄存器
void W25QXX_Write_Enable(void); //寫使能
void W25QXX_Write_Disable(void); //寫保護
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead); //讀取flash
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//寫入flash
void W25QXX_Erase_Chip(void); //整片擦除
void W25QXX_Erase_Sector(u32 Dst_Addr); //扇區擦除
void W25QXX_Wait_Busy(void); //等待空閑
void W25QXX_PowerDown(void); //進入掉電模式
void W25QXX_WAKEUP(void); //喚醒
//GPIOx = P*32+N
deviceaccess_spi_t spi_2 = {SPI_2,SPI_MODE_MASTER,8,SPI_BIT_ORDER_MSB,SPI_CPOL_H,SPI_CPHA_EDGE_2};
deviceaccess_gpio_t gpio_ext_flash_cs = {44,GPIO_MODE_PUSHPULL,GPIO_DIRECTION_OUTPUT,TRUE,NULL};
//#define W25QXX_CS PBout(12) //W25QXX的片選信號
#define SPI_FLASH_CS_H {g_deviceaccess_gpio_control(&gpio_ext_flash_cs,CMD_WRITE,TRUE);}
#define SPI_FLASH_CS_L {g_deviceaccess_gpio_control(&gpio_ext_flash_cs,CMD_WRITE,FALSE);}
uint16_t get_flash_id = 0;
u16 W25QXX_ReadID(void)
{
u16 Temp = 0;
uint8_t set_tx_buffer[] = {0x90,0xFF,0xFF,0xFF};
uint8_t set_rx_buffer[2];
SPI_FLASH_CS_L;
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);//發送讀取ID命令
g_deviceaccess_spi_control(&spi_2,CMD_READ,set_rx_buffer,LENGTH_OF_ARRAY(set_rx_buffer),NULL);
Temp|=set_rx_buffer[0]< 8;
Temp|=set_rx_buffer[1];
SPI_FLASH_CS_H;
return Temp;
}
void W25QXX_Init(void)
{
g_deviceaccess_gpio_control(&gpio_ext_flash_cs,CMD_OPEN,NULL);
SPI_FLASH_CS_H; //SPI FLASH不選中
g_deviceaccess_spi_control(&spi_2, CMD_OPEN,NULL,NULL,NULL);
W25QXX_ReadID();//讀取FLASH ID.
get_flash_id=W25QXX_ReadID();//讀取FLASH ID.
printf("get_flash_id=%xrn",get_flash_id);
}
//讀取W25QXX的狀態寄存器
//BIT7 6 5 4 3 2 1 0
//SPR RV TB BP2 BP1 BP0 WEL BUSY
//SPR:默認0,狀態寄存器保護位,配合WP使用
//TB,BP2,BP1,BP0:FLASH區域寫保護設置
//WEL:寫使能鎖定
//BUSY:忙標記位(1,忙;0,空閑)
//默認:0x00
u8 W25QXX_ReadSR(void)
{
u8 byte=0;
SPI_FLASH_CS_L; //使能器件
uint8_t set_tx_buffer[1] = {W25X_ReadStatusReg};
uint8_t set_rx_buffer[1] = {0};
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
g_deviceaccess_spi_control(&spi_2,CMD_READ,set_rx_buffer,LENGTH_OF_ARRAY(set_rx_buffer),NULL);
byte=set_rx_buffer[0]; //讀取一個字節
SPI_FLASH_CS_H; //取消片選
return byte;
}
//寫W25QXX狀態寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以寫!!!
void W25QXX_Write_SR(u8 sr)
{
SPI_FLASH_CS_L; //使能器件
uint8_t set_tx_buffer[] = {W25X_WriteStatusReg,sr};
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
SPI_FLASH_CS_H; //取消片選
}
//W25QXX寫使能
//將WEL置位
void W25QXX_Write_Enable(void)
{
SPI_FLASH_CS_L; //使能器件
uint8_t set_tx_buffer[1] = {W25X_WriteEnable};
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
SPI_FLASH_CS_H; //取消片選
}
//W25QXX寫禁止
//將WEL清零
void W25QXX_Write_Disable(void)
{
SPI_FLASH_CS_L; //使能器件
uint8_t set_tx_buffer[1] = {W25X_WriteDisable};
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
SPI_FLASH_CS_H; //取消片選
}
//讀取SPI FLASH
//在指定地址開始讀取指定長度的數據
//pBuffer:數據存儲區
//ReadAddr:開始讀取的地址(24bit)
//NumByteToRead:要讀取的字節數(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
SPI_FLASH_CS_L; //使能器件
uint8_t set_tx_buffer[] = {W25X_ReadData,(u8)((ReadAddr) >?>16),(u8)((ReadAddr) >?>8),(u8)ReadAddr};
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
g_deviceaccess_spi_control(&spi_2,CMD_READ,pBuffer,NumByteToRead,NULL);
SPI_FLASH_CS_H;
}
//SPI在一頁(0~65535)內寫入少于256個字節的數據
//在指定地址開始寫入最大256字節的數據
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大256),該數不應該超過該頁的剩余字節數!!!
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
uint8_t set_tx_buffer[] = {W25X_PageProgram,(u8)((WriteAddr) >?>16),(u8)((WriteAddr) >?>8),(u8)(WriteAddr)};
W25QXX_Write_Enable(); //SET WEL
SPI_FLASH_CS_L; //使能器件
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,pBuffer,NumByteToWrite,NULL);
SPI_FLASH_CS_H; //取消片選
W25QXX_Wait_Busy(); //等待寫入結束
}
//無檢驗寫SPI FLASH
//必須確保所寫的地址范圍內的數據全部為0XFF,否則在非0XFF處寫入的數據將失敗!
//具有自動換頁功能
//在指定地址開始寫入指定長度的數據,但是要確保地址不越界!
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 pageremain;
pageremain=256-WriteAddr%256; //單頁剩余的字節數
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256個字節
while(1)
{
W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
if(NumByteToWrite==pageremain)break;//寫入結束了
else //NumByteToWrite >pageremain
{
pBuffer+=pageremain;
WriteAddr+=pageremain;
NumByteToWrite-=pageremain; //減去已經寫入了的字節數
if(NumByteToWrite >256)pageremain=256; //一次可以寫入256個字節
else pageremain=NumByteToWrite; //不夠256個字節了
}
}
}
//寫SPI FLASH
//在指定地址開始寫入指定長度的數據
//該函數帶擦除操作!
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大65535)
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u8 * W25QXX_BUF;
W25QXX_BUF=W25QXX_BUFFER;
secpos=WriteAddr/4096;//扇區地址
secoff=WriteAddr%4096;//在扇區內的偏移
secremain=4096-secoff;//扇區剩余空間大小
//printf("ad:%X,nb:%Xrn",WriteAddr,NumByteToWrite);//測試用
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096個字節
while(1)
{
W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//讀出整個扇區的內容
for(i=0;i< secremain;i++)//校驗數據
{
if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除
}
if(i< secremain)//需要擦除
{
W25QXX_Erase_Sector(secpos); //擦除這個扇區
for(i=0;i< secremain;i++) //復制
{
W25QXX_BUF[i+secoff]=pBuffer[i];
}
W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//寫入整個扇區
}else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//寫已經擦除了的,直接寫入扇區剩余區間.
if(NumByteToWrite==secremain)break;//寫入結束了
else//寫入未結束
{
secpos++;//扇區地址增1
secoff=0;//偏移位置為0
pBuffer+=secremain; //指針偏移
WriteAddr+=secremain; //寫地址偏移
NumByteToWrite-=secremain; //字節數遞減
if(NumByteToWrite >4096)secremain=4096;//下一個扇區還是寫不完
else secremain=NumByteToWrite; //下一個扇區可以寫完了
}
};
}
//擦除整個芯片
//等待時間超長...
void W25QXX_Erase_Chip(void)
{
uint8_t set_tx_buffer[1] = {W25X_ChipErase};
W25QXX_Write_Enable(); //SET WEL
W25QXX_Wait_Busy();
SPI_FLASH_CS_L; //使能器件
W25QXX_Write_Enable(); //SET WEL
SPI_FLASH_CS_L; //使能器件
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
SPI_FLASH_CS_H; //取消片選
W25QXX_Wait_Busy(); //等待芯片擦除結束
}
//擦除一個扇區
//Dst_Addr:扇區地址 根據實際容量設置
//擦除一個山區的最少時間:150ms
void W25QXX_Erase_Sector(u32 Dst_Addr)
{
uint8_t set_tx_buffer[] = {W25X_SectorErase,(u8)((Dst_Addr) >?>16),(u8)((Dst_Addr) >?>8),(u8)Dst_Addr};
//監視falsh擦除情況,測試用
//printf("fe:%xrn",Dst_Addr);
Dst_Addr*=4096;
W25QXX_Write_Enable(); //SET WEL
W25QXX_Wait_Busy();
SPI_FLASH_CS_L; //使能器件
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
SPI_FLASH_CS_H; //取消片選
W25QXX_Wait_Busy(); //等待擦除完成
}
//等待空閑
void W25QXX_Wait_Busy(void)
{
while((W25QXX_ReadSR()&0x01)==0x01); // 等待BUSY位清空
}
//進入掉電模式
void W25QXX_PowerDown(void)
{
uint8_t set_tx_buffer[] = {W25X_PowerDown};
SPI_FLASH_CS_L; //使能器件
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
SPI_FLASH_CS_H; //取消片選
//delay_us(3); //等待TPD
}
//喚醒
void W25QXX_WAKEUP(void)
{
uint8_t set_tx_buffer[] = {W25X_ReleasePowerDown};
SPI_FLASH_CS_L; //使能器件
g_deviceaccess_spi_control(&spi_2,CMD_WRITE,set_tx_buffer,LENGTH_OF_ARRAY(set_tx_buffer),NULL);
SPI_FLASH_CS_H; //取消片選
//delay_us(3); //等待TRES1
}
四、 FATFS
4.1 c源文件
這里就用到了2個c文件:diskio.c,ff.c
4.2 配置
打開ffconf.h頭文件,作以下更改
#define _USE_LFN 0
#define _MIN_SS 512
#define _MAX_SS 4096
4.3 接口代碼
DSTATUS disk_initialize
(
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
u8 res=0;
switch(pdrv)
{
case EX_FLASH://外部flash
W25QXX_Init();
break;
default:
res=1;
}
if(res)return STA_NOINIT;
else return 0; //初始化成功
}
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */
)
{
u8 res=0;
if (!count)return RES_PARERR;//count不能等于0,否則返回參數錯誤
switch(pdrv)
{
case EX_FLASH://外部flash
sector+=CONFIG_START_SECTOR;
for(;count >0;count--)
{
W25QXX_Read(buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
sector++;
buff+=FLASH_SECTOR_SIZE;
}
res=0;
break;
default:
res=1;
}
if(res==0x00)return RES_OK;
else return RES_ERROR;
}
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
u8 res=0;
if (!count)return RES_PARERR;//count不能等于0,否則返回參數錯誤
switch(pdrv)
{
case EX_FLASH://外部flash
sector+=CONFIG_START_SECTOR;
SwData_DetFromFlash(sector*FLASH_SECTOR_SIZE,count*FLASH_SECTOR_SIZE);
for(;count >0;count--)
{
// W25QXX_Write((u8*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
W25QXX_Write_NoCheck((u8*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
sector++;
buff+=FLASH_SECTOR_SIZE;
}
res=0;
break;
default:
res=1;
}
//處理返回值,將SPI_SD_driver.c的返回值轉成ff.c的返回值
if(res == 0x00)return RES_OK;
else return RES_ERROR;
}
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
switch(cmd)
{
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = FLASH_SECTOR_SIZE;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = FLASH_BLOCK_SIZE;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = FLASH_SECTOR_COUNT;
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
return res;
}
4.4 注意事項
返回給文件系統的扇區大小必須是flash實際的扇區大小,可以自定義文件系統的大小,
格式化文件系統時要選擇帶引導分區的類型即:res=f_mkfs("",0,4096):
寫操作時不應使用W25QXX_Write,而是先計算需要擦除的大小,來進行擦除再進行寫操作
#define FLASH_SECTOR_SIZE 4096
#define FLASH_BLOCK_SIZE 16
u16 FLASH_SECTOR_COUNT= 2696;
4.5 測試代碼
u8 exf_getfree(u8 *drv,u32 *total,u32 *free)
{
FATFS *fs1;
u8 res;
u32 fre_clust=0, fre_sect=0, tot_sect=0;
//得到磁盤信息及空閑簇數量
res =(u32)f_getfree((const TCHAR*)drv, (DWORD*)&fre_clust, &fs1);
if(res==0)
{
tot_sect=(fs1- >n_fatent-2)*fs1- >csize; //得到總扇區數
fre_sect=fre_clust*fs1- >csize; //得到空閑扇區數
#if _MAX_SS!=512 //扇區大小不是512字節,則轉換為512字節
tot_sect*=fs1- >ssize/512;
fre_sect*=fs1- >ssize/512;
#endif
*total=tot_sect >?>1; //單位為KB
*free=fre_sect >?>1; //單位為KB
}
return res;
}
res = f_mount(&temp_fs,"",1);
if(res!=FR_OK)//FLASH磁盤,FAT文件系統錯誤,重新格式化FLASH
{
printf("Flash Disk Formatting..."); //格式化FLASH
W25QXX_Erase_Chip();
res=f_mkfs("",0,4096);
delay_ms(1000);
}
while(exf_getfree("",&total,&free)) //得到SD卡的總容量和剩余容量
{
printf("EXTFLASH Fatfs Error!");
delay_ms(200);
}
printf("FATFS OK!");
printf("SD Total Size:%dMB",total >?>10);
printf("SD Free Size:%dMB",free >?>10);
char *filepath = "1.txt";
UINT br;
open_result = f_mount(&temp_fs,"",1);
open_result =f_open(&f_txt,(const TCHAR*)filepath,FA_CREATE_ALWAYS|FA_WRITE);
if(open_result==FR_OK)
{
printf("open file okn");
read_result=f_write(&f_txt,data,sizeof(data)/sizeof(data[0]),(UINT*)&br);
if(read_result==FR_OK)
{
printf("write okn");
printf("br=%dn",br);
}
f_close(&f_txt);
open_result =f_open(&f_txt,(const TCHAR*)filepath,FA_READ);
printf("open_result=%dn",open_result);
if(open_result==FR_OK)
{
read_result=f_read(&f_txt,data_read,sizeof(data)/sizeof(data[0]),(UINT*)&br);
printf("read_result=%dn",read_result);
if(read_result==FR_OK)
{
int i = 0;
printf("read okn");
printf("br=%dn",br);
for(i=0;i< sizeof(data_read)/sizeof(data_read[0]);i++)
{
printf("data_read[%d]=%cn",i,data_read[i]);
}
}
f_close(&f_txt);
}
}
else
{
printf("open_result=%dn",open_result);
}
五、USB MASS STORAGE
5.1 上報磁盤數量
u8 Max_Lun =MAX_LUN;通過更愛MAX_LUN的定義來更改磁盤數量
uint8_t *Get_Max_Lun(uint16_t Length)
{
if (Length == 0)
{
pInformation- >Ctrl_Info.Usb_wLength = LUN_DATA_LENGTH;
return 0;
}
else
{
return((uint8_t*)(&Max_Lun));
}
}
5.2 usb緩存大小定義
更改USB緩存大小為flash的實際扇區大小4K
在memory.c中定義Data_Bufferw為1024,也就是4096
u32 Data_Buffer[1024];
5.3 接口代碼
uint16_t MAL_Init(uint8_t lun)
{
u16 Status=MAL_OK;
switch (lun)
{
case 0:
break;
case 1:
break;
default:
return MAL_FAIL;
}
return Status;
}
uint16_t MAL_Write(uint8_t lun, uint64_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length)
{
u8 STA;
switch (lun) //這里,根據lun的值確定所要操作的磁盤
{
case 0: //磁盤0為 SPI FLASH盤
STA=0;
SwData_DetFromFlash(CONFIG_FATFS_START_ADDRESS+Memory_Offset,Transfer_Length);
W25QXX_Write_NoCheck((u8*)Writebuff,CONFIG_FATFS_START_ADDRESS+Memory_Offset,Transfer_Length);
break;
default:
return MAL_FAIL;
}
if(STA!=0)return MAL_FAIL;
return MAL_OK;
}
uint16_t MAL_Read(uint8_t lun, uint64_t Memory_Offset, uint32_t *Readbuff, uint16_t Transfer_Length)
{
u8 STA;
switch (lun) //這里,根據lun的值確定所要操作的磁盤
{
case 0: //磁盤0為 SPI FLASH盤
STA=0;
W25QXX_Read((u8*)Readbuff, CONFIG_FATFS_START_ADDRESS+Memory_Offset, Transfer_Length);
break;
default:
return MAL_FAIL;
}
if(STA!=0)return MAL_FAIL;
return MAL_OK;
}
uint16_t MAL_GetStatus (uint8_t lun)
{
switch(lun)
{
case 0:
return MAL_OK;
case 1:
return MAL_OK;
default:
return MAL_FAIL;
}
}
- 4 初始化代碼
Mass_Memory_Size[0]=1024*1024*10; //前12M字節
Mass_Block_Size[0] =4096; //設置SPI FLASH的操作扇區大小為512
Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];
delay_ms(1800);
usb_port_set(0); //USB先斷開
delay_ms(300);
usb_port_set(1); //USB再次連接
//USB配置
USB_Interrupts_Config();
Set_USBClock();
USB_Init();
delay_ms(1800);
六、遇到的坑
6.1 ID信息
可能是W25Q128的硬件有問題,在上電時需要讀兩次才能讀到ID信息,但ID信息與數據手冊上的對不上,可能IC是盜版的吧。
6.2 文件系統格式化失敗
在格式化時最好加上整個flash或者指定大小的擦除操作再進行格式化
6.3 文件系統讀寫失敗
在進行讀操作時,可能是出現讀到的數據全是錯的問題,后來排查發現是W25QXX_Write的問題,更改為先查出再寫后問題得到解決。
6.4 文件系統讀不到正確的容量大小
將最大扇區和返回扇區大小更改為4096
6.5 USB MASS STORAGE打不開磁盤
將USB緩存區更改為總大小為flash實際扇區大小4096,在USB初始化前設置好扇區大小為實際的flash扇區大小,扇區數量,磁盤容量更改為實際需要
6.6 燒錄文件系統到外部FLASH的方法
6.6.1 制作鏡像文件
使用DiskGenius工具創建虛擬磁盤鏡像文件,由于現有的磁盤分區工具只兼容512字節扇區的硬件,所以在文件系統所使用的寫FLASH接口需要使用帶檢查的寫操作,也就是在寫的時候不影響其他扇區的數據,這樣的話就可以兼容FATFS最大扇區為512字節的配置
6.6.2 燒錄鏡像文件到外部FLASH
6.7 加速FLASH訪問
使用DMA方式進行SPI的讀寫
if(send_ptr != RT_NULL)
{
volatile uint8_t dummy_data;
dma_init_type dma_init_struct;
dma_reset(DMA2_CHANNEL5);
dma_reset(DMA2_CHANNEL4);
dma_default_para_init(&dma_init_struct);
dma_init_struct.buffer_size = size;
dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_base_addr = (uint32_t)&dummy_data;
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
dma_init_struct.memory_inc_enable = FALSE;
dma_init_struct.peripheral_base_addr = (uint32_t)(&spi_instance- >config- >spi_x- >dt);
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_VERY_HIGH;
dma_init_struct.loop_mode_enable = FALSE;
dma_init(DMA2_CHANNEL4, &dma_init_struct);
dma_init_struct.buffer_size = message- >length;
dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_base_addr = (uint32_t)send_ptr;
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
dma_init_struct.memory_inc_enable = TRUE;
dma_init_struct.peripheral_base_addr = (uint32_t)(&spi_instance- >config- >spi_x- >dt);
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_VERY_HIGH;
dma_init_struct.loop_mode_enable = FALSE;
dma_init(DMA2_CHANNEL5, &dma_init_struct);
spi_i2s_dma_transmitter_enable(spi_instance- >config- >spi_x, TRUE);
spi_i2s_dma_receiver_enable(spi_instance- >config- >spi_x, TRUE);
dma_channel_enable(DMA2_CHANNEL4, TRUE);
dma_channel_enable(DMA2_CHANNEL5, TRUE);
while(dma_flag_get(DMA2_FDT4_FLAG) == RESET);
dma_flag_clear(DMA2_FDT4_FLAG);
dma_channel_enable(DMA2_CHANNEL5, FALSE);
dma_channel_enable(DMA2_CHANNEL4, FALSE);
spi_i2s_dma_receiver_enable(spi_instance- >config- >spi_x, FALSE);
spi_i2s_dma_transmitter_enable(spi_instance- >config- >spi_x, FALSE);
}
else if(recv_ptr != RT_NULL)
{
uint8_t write_value = 0xA5;
dma_init_type dma_init_struct;
dma_reset(DMA2_CHANNEL5);
dma_reset(DMA2_CHANNEL4);
dma_default_para_init(&dma_init_struct);
dma_init_struct.buffer_size = size;
dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_base_addr = (uint32_t)&write_value;
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
dma_init_struct.memory_inc_enable = FALSE;
dma_init_struct.peripheral_base_addr = (uint32_t)(&spi_instance- >config- >spi_x- >dt);
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_VERY_HIGH;
dma_init_struct.loop_mode_enable = FALSE;
dma_init(DMA2_CHANNEL5, &dma_init_struct);
dma_init_struct.buffer_size = size;
dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_base_addr = (uint32_t)recv_ptr;
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
dma_init_struct.memory_inc_enable = TRUE;
dma_init_struct.peripheral_base_addr = (uint32_t)(&spi_instance- >config- >spi_x- >dt);
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_VERY_HIGH;
dma_init_struct.loop_mode_enable = FALSE;
dma_init(DMA2_CHANNEL4, &dma_init_struct);
//
spi_i2s_dma_transmitter_enable(spi_instance- >config- >spi_x, TRUE);
spi_i2s_dma_receiver_enable(spi_instance- >config- >spi_x, TRUE);
//
dma_channel_enable(DMA2_CHANNEL4, TRUE);
dma_channel_enable(DMA2_CHANNEL5, TRUE);
while(dma_flag_get(DMA2_FDT4_FLAG) == RESET);
dma_flag_clear(DMA2_FDT4_FLAG);
dma_channel_enable(DMA2_CHANNEL5, FALSE);
dma_channel_enable(DMA2_CHANNEL4, FALSE);
spi_i2s_dma_receiver_enable(spi_instance- >config- >spi_x, FALSE);
spi_i2s_dma_transmitter_enable(spi_instance- >config- >spi_x, FALSE);
}
#endif
評論
查看更多