一、背景
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
评论
查看更多