CH567 SPI NOR 测试

最近编写了一个 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.3V8 VCC
2 MISOPA15 3.3V7 HOLD#/RESET#
3 WP#3.3V PA136 SCLK
4 VSSGND PA145 MOSI
对CH567的连接方法

一些常用的命令定义如下:

FunctionCommand NameByte 1Byte 2Byte 3Byte 4Byte 5
Void WriteEnable()Write Enable06H
Void WriteDisable()Write Disable04H
UINT8 ReadStatus1()Read Status Register-105H(S7-S0)(cont.)
UINT8 ReadStatus2()Read Status Register-235H(S15-S8)(cont.)
UINT8 ReadStatus3()Read Status Register-315H(S23-S16)(cont.)
Void WriteStatus1(UINT8)Write Status Register-101HS7-S0
Void WriteStatus2(UINT8)Write Status Register-231HS15-S8
Void WriteStatus3(UINT8)Write Status Register-311HS23-S16
Void ReadData(UINT32, UINT16, PUINT8)Read Data03HA23-A16A15-A8A7-A0(D7-D0)
Void PageProgram()Page Program02HA23-A16A15-A8A7-A0D7-D0
Void SectorErase()Sector Erase20HA23-A16A15-A8A7-A0
Void BlockErase32K()Block Erase (32K)52HA23-A16A15-A8A7-A0
Void BlockErase64K()Block Erase (64K)D8HA23-A16A15-A8A7-A0
Void ChipErase()Chip EraseC7H
UINT32 ReadID()Read Identification9FH(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;
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注