最近编写了一个 CH567 SPI 读写测试的代码,测试的 SPI NOR 是 gd25q128e(特别注意:这款在Taobao上有假货,使用几次之后就无法读写,包括 READ ID 都无法响应)。这款 SPI 每个Page有256 Bytes ;每个 Sector有 4K Bytes; Block有 32K或者64K字节。
CH567 支持2个 SPI Host,这次测试的是 SPI0。
硬件连接:硬件连接:
引脚 | ||||
1 CS# | PA12 | 3.3V | 8 VCC | |
2 MISO | PA15 | 3.3V | 7 HOLD#/RESET# | |
3 WP# | 3.3V | PA13 | 6 SCLK | |
4 VSS | GND | PA14 | 5 MOSI |
一些常用的命令定义如下:
Function | Command Name | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 |
Void WriteEnable() | Write Enable | 06H | ||||
Void WriteDisable() | Write Disable | 04H | ||||
UINT8 ReadStatus1() | Read Status Register-1 | 05H | (S7-S0) | (cont.) | ||
UINT8 ReadStatus2() | Read Status Register-2 | 35H | (S15-S8) | (cont.) | ||
UINT8 ReadStatus3() | Read Status Register-3 | 15H | (S23-S16) | (cont.) | ||
Void WriteStatus1(UINT8) | Write Status Register-1 | 01H | S7-S0 | |||
Void WriteStatus2(UINT8) | Write Status Register-2 | 31H | S15-S8 | |||
Void WriteStatus3(UINT8) | Write Status Register-3 | 11H | S23-S16 | |||
Void ReadData(UINT32, UINT16, PUINT8) | Read Data | 03H | A23-A16 | A15-A8 | A7-A0 | (D7-D0) |
Void PageProgram() | Page Program | 02H | A23-A16 | A15-A8 | A7-A0 | D7-D0 |
Void SectorErase() | Sector Erase | 20H | A23-A16 | A15-A8 | A7-A0 | |
Void BlockErase32K() | Block Erase (32K) | 52H | A23-A16 | A15-A8 | A7-A0 | |
Void BlockErase64K() | Block Erase (64K) | D8H | A23-A16 | A15-A8 | A7-A0 | |
Void ChipErase() | Chip Erase | C7H | ||||
UINT32 ReadID() | Read Identification | 9FH | (M7-M0) | (JDID15-JDID8) | (JDID7- JDID0) | (cont.) |
特别注意这颗SPI NOR 有三个8Bits的 Status寄存器。
测试如下三个操作:
1.读取 0 开始的 16字节内容;
2.擦除 0 开始的一个 Sector(4K);
3.在 0 开始写入 16字节内容。
/*
============================================================================
Name : main_SDIO_TF.c
Author : wch_cc
Version : V1.0.0
Copyright :
Description : ²Ù×÷TF¿¨
============================================================================
*/
#include <stdio.h>
#include <string.h>
#include <nds32_isr.h>
#include <nds32_intrinsic.h>
#include "CH568SFR.H"
#include "MYdebug.H"
__attribute__ ((aligned(8))) UINT8 Sendbuff[512*2] ; //8×Ö½Ú¶ÔÆë
__attribute__ ((aligned(8))) UINT8 Recvbuff[512*2] ; //8×Ö½Ú¶ÔÆë
#define GD25Q_CMD_WRITEENABLE 0x06
#define GD25Q_CMD_WRITEDISABLE 0x04
#define GD25Q_CMD_READSTATUS1 0x05
#define GD25Q_CMD_READSTATUS2 0x35
#define GD25Q_CMD_READSTATUS3 0x15
#define GD25Q_CMD_WRITESTATUS1 0x01
#define GD25Q_CMD_WRITESTATUS2 0x31
#define GD25Q_CMD_WRITESTATUS3 0x11
#define GD25Q_CMD_READDATA 0x03
#define GD25Q_CMD_PAGEPROGRAM 0x02
#define GD25Q_CMD_SECTORERASE 0x20
#define GD25Q_CMD_BLOCKERASE32K 0x52
#define GD25Q_CMD_BLOCKERASE64K 0xD8
#define GD25Q_CMD_CHIPERASE 0xC7
#define GD25Q_CMD_READID 0x9F
/********************************* 引脚定义 ************************************
* PA12 <===========> SCS0
* PA13 <===========> SCK0
* PA14 <===========> MOSI0
* PA15 <===========> MISO0
*******************************************************************************/
#define SPI0_CS_LOW() R32_PA_CLR |= 1<<12
#define SPI0_CS_HIGH() R32_PA_OUT |= 1<<12
/*******************************************************************************
* Function Name : SPI_MASTER_INIT
* Description : SPI0主机模式初始化
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void SPI_MASTER_INIT ( void )
{
R8_SPI0_CTRL_MOD = RB_SPI_MOSI_OE|RB_SPI_SCK_OE; /* MOSI,SCK输出使能,主机模式,方式0 */
R8_SPI0_CLOCK_DIV = 0x0a; /* 10分频,100/10=10M */
R32_PA_DIR |= (1<<14 | 1<<13 | 1<<12); /* MOSI(PA14),SCK0(PA13),SCS(PA12)为输出*/
R32_PA_PU |= 1<<12 ;
R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE;
}
/*******************************************************************************
* Function Name : SPI0_Trans
* Description : 发送一字节数据
* Input : data -要发送的数据
* Output : None
* Return : None
*******************************************************************************/
void SPI0_Trans( UINT8 data )
{
// R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR;
// R8_SPI0_BUFFER = data;
// while( !(R8_SPI0_INT_FLAG & RB_SPI_FREE) );
R32_SPI0_FIFO = data;
R16_SPI0_TOTAL_CNT = 0x01;
while( R8_SPI0_FIFO_COUNT != 0 ); /* 等待数据发送完毕 */
}
/*******************************************************************************
* Function Name : SPI0_Recv
* Description : 接收一字节数据
* Input : None
* Output : None
* Return : None
*******************************************************************************/
UINT8 SPI0_Recv( void )
{
// R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR;
// R8_SPI0_BUFFER = 0xFF; //启动传输
// while( !(R8_SPI0_INT_FLAG & RB_SPI_FREE) );
// return ( R8_SPI0_BUFFER );
UINT8 data;
R32_SPI0_FIFO = 0xff;
R16_SPI0_TOTAL_CNT = 0x01;
while( R8_SPI0_FIFO_COUNT != 0 ); /* 等待数据回来 */
data = R8_SPI0_BUFFER;
return data;
}
/*******************************************************************************
* Function Name : SPIFlash_ReadID
* Description : SPI Flash读取芯片ID
* Input : None
* Output : None
* Return :
*******************************************************************************/
UINT32 SPIFlash_ReadID()
{
UINT32 temp = 0;
R32_PA_CLR |= 1<<12 ;
SPI0_Trans(GD25Q_CMD_READID); //读取ID命令
temp = SPI0_Recv();
temp = temp<<8;
temp |= SPI0_Recv();
temp = temp<<8;
temp |= SPI0_Recv();
R32_PA_OUT |= 1<<12 ;
return temp;
}
/*******************************************************************************
* Function Name : WriteEnable
* Description : 写使能
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void WriteEnable( void )
{
SPI0_CS_LOW();
SPI0_Trans( GD25Q_CMD_WRITEENABLE ); //发送写使能命令
SPI0_CS_HIGH();
}
/*******************************************************************************
* Function Name : WriteDisable
* Description : 关闭写
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void WriteDisable( void )
{
SPI0_CS_LOW();
SPI0_Trans( GD25Q_CMD_WRITEDISABLE ); //发送写禁止命令
SPI0_CS_HIGH();
}
/*******************************************************************************
* Function Name : ReadStatus1
* Description : 读取状态寄存器1
* Input : None
* Output : None
* Return :
*******************************************************************************/
UINT8 ReadStatus1()
{
UINT8 Status;
SPI0_CS_LOW();
SPI0_Trans( GD25Q_CMD_READSTATUS1 ); //发送读状态寄存器的命令
Status = SPI0_Recv(); //读取状态寄存器
SPI0_CS_HIGH();
return Status;
}
/*******************************************************************************
* Function Name : ReadStatus2
* Description : 读取状态寄存器2
* Input : None
* Output : None
* Return :
*******************************************************************************/
UINT8 ReadStatus2()
{
UINT8 Status;
SPI0_CS_LOW();
SPI0_Trans( GD25Q_CMD_READSTATUS2 ); //发送读状态寄存器的命令
Status = SPI0_Recv(); //读取状态寄存器
SPI0_CS_HIGH();
return Status;
}
/*******************************************************************************
* Function Name : ReadStatus3
* Description : 读取状态寄存器3
* Input : None
* Output : None
* Return :
*******************************************************************************/
UINT8 ReadStatus3()
{
UINT8 Status;
SPI0_CS_LOW();
SPI0_Trans( GD25Q_CMD_READSTATUS3 ); //发送读状态寄存器的命令
Status = SPI0_Recv(); //读取状态寄存器
SPI0_CS_HIGH();
return Status;
}
/*******************************************************************************
* Function Name : WriteStatus1
* Description : 写状态寄存器1
* Input : None
* Output : None
* Return :
*******************************************************************************/
void WriteStatus1( UINT8 Status )
{
SPI0_CS_LOW();
SPI0_Trans( GD25Q_CMD_WRITESTATUS1 ); //发送写状态寄存器的命令
SPI0_Trans( Status ); //发状态寄存器的数值
SPI0_CS_HIGH();
return ;
}
/*******************************************************************************
* Function Name : WriteStatus2
* Description : 写状态寄存器2
* Input : None
* Output : None
* Return :
*******************************************************************************/
void WriteStatus2( UINT8 Status )
{
SPI0_CS_LOW();
SPI0_Trans( GD25Q_CMD_WRITESTATUS2 ); //发送写状态寄存器的命令
SPI0_Trans( Status ); //发状态寄存器的数值
SPI0_CS_HIGH();
return ;
}
/*******************************************************************************
* Function Name : WriteStatus3
* Description : 写状态寄存器3
* Input : None
* Output : None
* Return :
*******************************************************************************/
void WriteStatus3( UINT8 Status )
{
SPI0_CS_LOW();
SPI0_Trans( GD25Q_CMD_WRITESTATUS3 ); //发送写状态寄存器的命令
SPI0_Trans( Status ); //发状态寄存器的数值
SPI0_CS_HIGH();
return ;
}
/*******************************************************************************
* Function Name : ReadExternalFlash_SPI
* Description : 读数据,注意:地址低8位必须为0
*******************************************************************************/
void ReadData( UINT32 StarAddr, UINT16 Len, PUINT8 RcvBuffer )
{
UINT16 i;
SPI0_CS_LOW();
SPI0_Trans(GD25Q_CMD_READDATA); //读命令'
SPI0_Trans(((StarAddr & 0xFFFFFF) >> 16)); //发送3字节地址
SPI0_Trans(((StarAddr & 0xFFFF) >> 8));
SPI0_Trans(StarAddr & 0xFF);
for (i=0; i<Len; i++)
{
RcvBuffer[i]=SPI0_Recv();
}
SPI0_CS_HIGH();
RcvBuffer[0]=0xFF;
}
/*******************************************************************************
* Function Name : WaitExternalFlashIfBusy
* Description : 等待芯片空闲(在执行Byte-Program, Sector-Erase, Block-Erase, Chip-Erase操作后)
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void WaitExternalFlashIfBusy( void )
{
while ((ReadStatus1()&0x01) == 0x01 )
{
printf("Busy\n");
; //等待直到Flash空闲
}
}
/*******************************************************************************
* Function Name : PageProgram
* Description : 对一个page 进行写入
*******************************************************************************/
void PageProgram( UINT32 StarAddr, UINT16 Len, PUINT8 RcvBuffer )
{
int i;
WriteEnable();
SPI0_CS_LOW();
SPI0_Trans(0x02); //读命令
SPI0_Trans(((StarAddr & 0xFFFFFF) >> 16)); //发送3字节地址
SPI0_Trans(((StarAddr & 0xFFFF) >> 8));
SPI0_Trans(StarAddr & 0xFF);
for (i=0; i<Len; i++)
{
SPI0_Trans(RcvBuffer[i]);
}
SPI0_CS_HIGH();
WaitExternalFlashIfBusy(); //等待写入结束
}
/*******************************************************************************
* Function Name : SectorErase
* Description : 擦除4K Flash 擦除一个扇区
* Input : Dst_Addr 0-1 ffff ffff ,清除任意地址所在的扇区
* Output : None
* Return : None
*******************************************************************************/
void SectorErase( UINT32 Dst_Addr )
{
WriteEnable();
WaitExternalFlashIfBusy();
printf("SectorErase Status1:0x%01x\n",ReadStatus1());
SPI0_CS_LOW();
SPI0_Trans(GD25Q_CMD_SECTORERASE); //扇区擦除命令
SPI0_Trans(((Dst_Addr & 0xFFFFFF) >> 16)); //发送3字节地址
SPI0_Trans(((Dst_Addr & 0xFFFF) >> 8));
SPI0_Trans(Dst_Addr & 0xFF);
SPI0_CS_HIGH();
WaitExternalFlashIfBusy();
}
/*******************************************************************************
* Function Name : Block32KErase
* Description : 擦除32K Flash
* Input : Dst_Addr 0-1 ffff ffff ,清除任意地址所在的扇区
* Output : None
* Return : None
*******************************************************************************/
void Block32KErase( UINT32 Dst_Addr )
{
WriteEnable();
SPI0_CS_LOW();
SPI0_Trans(GD25Q_CMD_BLOCKERASE32K); //扇区擦除命令
SPI0_Trans(((Dst_Addr & 0xFFFFFF) >> 16)); //发送3字节地址
SPI0_Trans(((Dst_Addr & 0xFFFF) >> 8));
SPI0_Trans(Dst_Addr & 0xFF);
SPI0_CS_HIGH();
WaitExternalFlashIfBusy();
}
/*******************************************************************************
* Function Name : Block64KErase
* Description : 擦除64K Flash
* Input : Dst_Addr 0-1 ffff ffff ,清除任意地址所在的扇区
* Output : None
* Return : None
*******************************************************************************/
void Block64KErase( UINT32 Dst_Addr )
{
WriteEnable();
SPI0_CS_LOW();
SPI0_Trans(GD25Q_CMD_BLOCKERASE64K); //扇区擦除命令
SPI0_Trans(((Dst_Addr & 0xFFFFFF) >> 16)); //发送3字节地址
SPI0_Trans(((Dst_Addr & 0xFFFF) >> 8));
SPI0_Trans(Dst_Addr & 0xFF);
SPI0_CS_HIGH();
WaitExternalFlashIfBusy();
}
/*******************************************************************************
* Function Name : ChipErase
* Description : 擦除整片
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void ChipErase()
{
WriteEnable();
SPI0_CS_LOW();
SPI0_Trans(GD25Q_CMD_CHIPERASE); //整片擦除
SPI0_CS_HIGH();
WaitExternalFlashIfBusy();
}
int main()
{
Interrupt_init( 1<<INT_ID_SDC ); /* 系统总中断开启 */
SysClkInit(); /* 初始化系统时钟 */
mInitSTDIO_UR3(); /* 调试信息初始化 printf调用 */
SPI_MASTER_INIT();
mDelaymS(200);
printf("START SPI FLASH\n");
// 读取 ID
printf("id:0x%04x\n",SPIFlash_ReadID() );
// 度状态寄存器
printf("Status1:0x%01x\n",ReadStatus1());
printf("Status2:0x%01x\n",ReadStatus2());
printf("Status3:0x%01x\n",ReadStatus3());
// 在所有的写入操作之前都要设置起来 WEL
WriteEnable();
WaitExternalFlashIfBusy();
WriteStatus1(0x02);
WaitExternalFlashIfBusy();
WriteEnable();
WaitExternalFlashIfBusy();
WriteStatus3(0x00);
WaitExternalFlashIfBusy();
// 输出 Status Register
printf("Status1:0x%01x\n",ReadStatus1());
printf("Status2:0x%01x\n",ReadStatus2());
printf("Status3:0x%01x\n",ReadStatus3());
// 使用 16字节缓冲进行测试
UINT8 Buffer[16];
// 给缓冲区赋初始值便于观察
Buffer[0]=0x12; Buffer[1]=0x34; Buffer[2]=0x56; Buffer[3]=0x78;
Buffer[4]=0x9A; Buffer[5]=0xBC; Buffer[6]=0xDE; Buffer[7]=0xF0;
// 1. 读取测试
// 读取 SPI 起始处 16字节
printf("Read Test\n");
ReadData(0,16,&Buffer[0]);
UINT16 i;
for (i=0;i<16;i++) {
printf("%X ",Buffer[i]);
}
printf("\n");
// 2. Sector(4K Bytes)擦除测试
printf("Sector Erase\n");
SectorErase(0);
ReadData(0,16,Buffer);
for (i=0;i<16;i++) {
printf("%X ",Buffer[i]);
}
printf("\n");
// 3. PagePrgram (最大 一次写入256Bytes)测试
printf("Page Program\n");
Buffer[0]=0x12; Buffer[1]=0x34; Buffer[2]=0x56; Buffer[3]=0x78;
Buffer[4]=0x9A; Buffer[5]=0xBC; Buffer[6]=0xDE; Buffer[7]=0xF0;
PageProgram(0,8,Buffer);
for (i=0;i<16;i++) {
printf("%X ",Buffer[i]);
}
printf("\n");
return 0;
}