打印

使用GD32F10x的SPI0接口读写GD25Q128

[复制链接]
572|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
yutingwei|  楼主 | 2023-10-29 01:40 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

使用特权

评论回复
沙发
yutingwei|  楼主 | 2023-10-29 01:40 | 只看该作者

使用特权

评论回复
板凳
yutingwei|  楼主 | 2023-10-29 01:41 | 只看该作者
#include "SPI0.h"

void SPI0_Init(void);
uint8_t SPI0_ReadWriteByte(uint8_t byte);
void SPI0_SetSpeed(uint32_t SpeedSet);

/*
PA5复用为SPI0_SCK
PA6复用为SPI0_MISO
PA7复用为SPI0_MOSI
SPI0使用NSS软件模式,这里使用PE4
*/
void SPI0_Init(void)
{
        spi_parameter_struct spi_init_struct;

        rcu_periph_clock_enable(RCU_GPIOA); //使能GPIOA的外设时钟
        rcu_periph_clock_enable(RCU_SPI0);  //使能SPI0的外设时钟

        gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5);
        //SPI0_SCK:配置GPIOA5的工作模式为复用功能IO推挽输出,输出速度最大为50MHz
        gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
        //SPI0_MOSI:配置GPIOA7的工作模式为复用功能IO推挽输出,输出速度最大为50MHz
        gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
        //SPI0_MISO:将GPIOA6设置为浮空输入

///配置SPI0的CS引脚开始///
        rcu_periph_clock_enable(RCU_GPIOE);
        gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3);
        SPI0_CS_HIGH();//设置SPI0的CS引脚输出高电平
///配置SPI0的CS引脚结束///

        spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX; //SPI在全双工通讯中接收/发送数据
        spi_init_struct.device_mode          = SPI_MASTER;               //SPI为主机模式且SWNSS=1
        spi_init_struct.nss                  = SPI_NSS_SOFT;             //使用NSS软件模式:NSS电平取决于SWNSS位;
        spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;       //SPI使用8位数据帧格式
        spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
        //在SPI为空闲状态时,CLK引脚拉低,且"在第1个时钟跳变沿时开始采集第1位数据"
        spi_init_struct.prescale             = SPI_PSC_8;                //SPI时钟预分频器值为8
        spi_init_struct.endian               = SPI_ENDIAN_MSB;           //先发送最高位
        spi_init(SPI0, &spi_init_struct);//使用spi_init_struct结构参数初始化SPI0

        spi_crc_polynomial_set(SPI0,7); //将7写入"SPI的CRC多项式寄存器"
        spi_enable(SPI0);               //使能SPI0
}

//函数功能:SPI0发送8位的byte,并将读到的8位数据返回
uint8_t SPI0_ReadWriteByte(uint8_t byte)
{
        uint8_t ret_Data;

        while(RESET == spi_i2s_flag_get(SPI0,SPI_FLAG_TBE))
        {//读取"SPI发送缓冲区空"标志
         //等待SPI发送完成
        }

        spi_i2s_data_transmit(SPI0,byte);
        //将byte写入"SPI0数据寄存器"
        while(RESET == spi_i2s_flag_get(SPI0,SPI_FLAG_RBNE))
        {//读取"SPI接收缓冲区非空"标志
         //等待SPI接收完成
        }

        ret_Data=spi_i2s_data_receive(SPI0);
        //从"SPI数据寄存器"读取数据

        return(ret_Data);
}

//函数功能:SPI0发送16位的half_word,并将读到的16位数据返回
uint16_t spi_flash_send_halfword(uint16_t half_word)
{
        uint16_t ret_Data;

        while(RESET == spi_i2s_flag_get(SPI0,SPI_FLAG_TBE))
        {//读取"SPI发送缓冲区空"标志
         //等待SPI发送完成
        }

        spi_i2s_data_transmit(SPI0,half_word);//将byte写入"SPI0数据寄存器"
        while(RESET == spi_i2s_flag_get(SPI0,SPI_FLAG_RBNE))
        {//读取"SPI接收缓冲区非空"标志
         //等待SPI接收完成
        }

        ret_Data=spi_i2s_data_receive(SPI0);//从"SPI数据寄存器"读取数据
        return(ret_Data);
}

//当使用SPI0时,PCLK=PCLK2,预分频器输入时钟最大为108MHz
//当使用SPI1和SPI2时,PCLK=PCLK1,预分频器输入时钟最大为54MHz
//SPI_PSC_2,SPI时钟预分频器值为2,SPI时钟为54MHz
//SPI_PSC_4,SPI时钟预分频器值为4,SPI时钟为27MHz
//SPI_PSC_8,SPI时钟预分频器值为8,SPI时钟为13.5MHz
//SPI_PSC_16,PI时钟预分频器值为16,SPI时钟为6.75MHz
//SPI_PSC_32,SPI时钟预分频器值为32,SPI时钟为3.375MHz
//SPI_PSC_64,SPI时钟预分频器值为64,SPI时钟为1.6875MHz
//SPI_PSC_128,SPI时钟预分频器值为128,SPI时钟为843.75KHz
//SPI_PSC_256,SPI时钟预分频器值为256,SPI时钟为421.875KHz
//函数功能:将SpeedSet的值写入主机模式中的预分频器
void SPI0_SetSpeed(uint32_t SpeedSet)
{
        uint32_t reg;

        reg=SPI_CTL0(SPI0);//读取SPI控制寄存器0
        reg=(uint32_t)( reg & (uint32_t)( ~SPI_CTL0_PSC ) );//清除主机模式中的预分频器
        reg=(uint32_t)( reg | SpeedSet );                   //将SpeedSet写入到bit3~bit5
}

使用特权

评论回复
地板
yutingwei|  楼主 | 2023-10-29 01:41 | 只看该作者
#include "GD25Qxx.h"
#include "SPI0.h"
#include "LED.h"

uint32_t flash_id;
uint8_t  Flash_Write_Buffer[Flash_Write_Buffer_Size];
uint8_t  Flash_Read_Buffer[Flash_Read_Buffer_Size];

uint8_t spi_flash_send_byte(uint8_t byte);
uint8_t spi_flash_read_byte(void);
void spi_flash_write_enable(void);
void spi_flash_wait_for_write_end(void);
void GD25Qxx_Init(void);

//函数功能:SPI0发送byte,并将读到的数据返回
uint8_t spi_flash_send_byte(uint8_t byte)
{
        uint8_t ret_Data;

        ret_Data=SPI0_ReadWriteByte(byte);

        return(ret_Data);
}

//函数功能:SPI0发送DUMMY_BYTE,为的是读取数据,并返回读到的值
uint8_t spi_flash_read_byte(void)
{
        uint8_t ret_Data;

        ret_Data=SPI0_ReadWriteByte(DUMMY_BYTE);
        //SPI0发送byte,并将读到的数据返回
        return(ret_Data);
}

//函数功能:发送"写使能命令"
void spi_flash_write_enable(void)
{
        SPI0_CS_LOW();
        spi_flash_send_byte(WriteEnable_CMD);//发送"写使能命令"
        SPI0_CS_HIGH();
}

//函数功能:等待空闲
void spi_flash_wait_for_write_end(void)
{
        uint8_t flash_status = 0;

        SPI0_CS_LOW();
        spi_flash_send_byte(ReadStatusRegister1_CMD);//发送"读状态寄存器命令"

        do
        {
                flash_status = spi_flash_send_byte(DUMMY_BYTE);
                //发送DUMMY_BYTE数据为的是读取状态寄存器的值
        }while( (flash_status & WIP_FLAG) == SET );
        //WIP位置1,表示芯片正处于编程/擦除/写状态

        SPI0_CS_HIGH();
}

//函数功能:擦除扇区,其首地址为sector_addr
void spi_flash_sector_erase(uint32_t sector_addr)
{
        union GD32_UINT32_DATA_TYPE addr;

        addr.Uint32_Data=sector_addr;

        spi_flash_write_enable();//发送"写使能命令"

擦出扇区开始///
        SPI0_CS_LOW();
        spi_flash_send_byte(SectorErase_CMD); //发送扇区擦除命令
        spi_flash_send_byte( addr.b[2] );     //发送扇区地址的bit16~bit23
        spi_flash_send_byte( addr.b[1] );     //发送扇区地址的bit8~bit15
        spi_flash_send_byte( addr.b[0] );     //发送扇区地址的bit0~bit7
        SPI0_CS_HIGH();
擦出扇区结束///

        spi_flash_wait_for_write_end();//等待空闲
}

//函数功能:擦除整个芯片
void spi_flash_bulk_erase(void)
{
        spi_flash_write_enable();//发送"写使能命令"

        SPI0_CS_LOW();
        spi_flash_send_byte(ChipErase_CMD);//发送"芯片擦除命令"
        SPI0_CS_HIGH();

        spi_flash_wait_for_write_end();//等待空闲
}

//函数功能:将pbuffer[]中的num_byte_to_write个字节型数据写入首地址为write_addr的Flash空间
void spi_flash_page_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
{
        union GD32_UINT32_DATA_TYPE addr;

        addr.Uint32_Data=write_addr;

        spi_flash_write_enable();//发送"写使能命令"

        SPI0_CS_LOW();

        spi_flash_send_byte(PageProgram_CMD);//发送"页编程命令"
        spi_flash_send_byte( addr.b[2] );   //发送页地址的bit16~bit23
        spi_flash_send_byte( addr.b[1] );   //发送页地址的bit8~bit15
        spi_flash_send_byte( addr.b[0] );   //发送页地址的bit0~bit7
        while(num_byte_to_write--)
        {
                spi_flash_send_byte(*pbuffer);
                pbuffer++;
        }

        SPI0_CS_HIGH();

        spi_flash_wait_for_write_end();//等待空闲
}

//函数功能:将pbuffer[num_byte_to_write]写入Flash,其首地址为write_addr
void spi_flash_buffer_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
{
        uint8_t num_of_page = 0, num_of_single = 0, addr = 0, count = 0, temp = 0;

        addr          = write_addr % SPI_FLASH_PAGE_SIZE;
        count         = SPI_FLASH_PAGE_SIZE - addr; //计算当前页剩余多少个字节空间
        num_of_page   = num_byte_to_write / SPI_FLASH_PAGE_SIZE;//计算需要写多少页
        num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE;//计算不满一页的字节数量

        if(0 == addr)//位于页边界
        {
                if(0 == num_of_page)//所写字节数量不满一页,num_byte_to_write < SPI_FLASH_PAGE_SIZE
                        spi_flash_page_write(pbuffer,write_addr,num_byte_to_write);
                else//所写字节数量超过一页,num_byte_to_write > SPI_FLASH_PAGE_SIZE
                {
                        while(num_of_page--)
                        {
                                spi_flash_page_write(pbuffer,write_addr,SPI_FLASH_PAGE_SIZE);
                                write_addr += SPI_FLASH_PAGE_SIZE;
                                pbuffer += SPI_FLASH_PAGE_SIZE;
                        }
                        spi_flash_page_write(pbuffer,write_addr,num_of_single);
                }
        }
        else
        {
                if(0 == num_of_page)//所写字节数量不满一页
                {
                        if(num_of_single > count)//超过当前页
                        {
                                temp = num_of_single - count;//计算跨页的字节数量
                                spi_flash_page_write(pbuffer,write_addr,count);
                                write_addr += count;//修改Flash地址
                                pbuffer += count;   //修改指针
                                spi_flash_page_write(pbuffer,write_addr,temp);
                        }
                        else//没有超过当前页
                        spi_flash_page_write(pbuffer,write_addr,num_byte_to_write);
                }
                else//所写字节数量超过一页
                {
                        num_byte_to_write -= count;//计算写当前页后的剩余字节总数
                        num_of_page = num_byte_to_write / SPI_FLASH_PAGE_SIZE;  //剩余字节总数需要多少页
                        num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE;//剩余字节总数写完整页后的剩余字节数量

                        spi_flash_page_write(pbuffer,write_addr, count);//向当前页写入count字节,凑成1整页
                        write_addr += count;//修改Flash地址
                        pbuffer += count;   //修改指针

                        while(num_of_page--)
                        {
                                spi_flash_page_write(pbuffer,write_addr,SPI_FLASH_PAGE_SIZE);
                                write_addr += SPI_FLASH_PAGE_SIZE;//修改Flash地址
                                pbuffer += SPI_FLASH_PAGE_SIZE;   //修改指针
                        }

                        if(0 != num_of_single)//最后写剩余的字节
                                spi_flash_page_write(pbuffer,write_addr,num_of_single);
                }
        }
}

//函数功能:从Flash中读取num_byte_to_read个字节,保存到pbuffer[]中
void spi_flash_buffer_read(uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read)
{
        union GD32_UINT32_DATA_TYPE addr;

        addr.Uint32_Data=read_addr;
        SPI0_CS_LOW();

        spi_flash_send_byte(ReadData_CMD);//读数据命令
        spi_flash_send_byte( addr.b[2] );//发送地址的bit16~bit23
        spi_flash_send_byte( addr.b[1] );//发送地址的bit8~bit15
        spi_flash_send_byte( addr.b[0] );//发送地址的bit0~bit7
        while(num_byte_to_read--)
        {
                *pbuffer = spi_flash_send_byte(DUMMY_BYTE);
                pbuffer++;
        }

        SPI0_CS_HIGH();
}

//函数功能:读Flash的ID
uint32_t spi_flash_read_id(void)
{
        union GD32_UINT32_DATA_TYPE ret_Data;

        ret_Data.Uint32_Data=0;

        SPI0_CS_LOW();

        spi_flash_send_byte(ReadIdentificationCMD);      //发送"读Flash的ID命令"
        ret_Data.b[2]=spi_flash_send_byte(DUMMY_BYTE);   //读取数据的bit16~bit23
        ret_Data.b[1]=spi_flash_send_byte(DUMMY_BYTE);   //读取数据的bit8~bit15
        ret_Data.b[0]=spi_flash_send_byte(DUMMY_BYTE);   //读取数据的bit0~bit7

        SPI0_CS_HIGH();

        return (ret_Data.Uint32_Data);
}

//函数功能:若src[]和dst[]的前length个字节相同,则返回1
uint8_t memory_compare(uint8_t* src, uint8_t* dst, uint16_t length)
{
        while(length --)
        {
                if(*src++ != *dst++) return 0;
        }

        return 1;
}

void GD25Qxx_Init(void)
{
        uint16_t i;

        flash_id=0;
        SPI0_Init();
        LED2_Off();

        flash_id = spi_flash_read_id();//读Flash的ID
        if(SFLASH_ID == flash_id)
        {
                for(i=0; i<SPI_FLASH_PAGE_SIZE;i++)
                {
                        Flash_Write_Buffer[i]=i;
                }

                spi_flash_sector_erase(0x000000);//擦除扇区,其首地址为0x000000

                spi_flash_buffer_write(Flash_Write_Buffer,0x000000,SPI_FLASH_PAGE_SIZE);
                //将Flash_Write_Buffer[SPI_FLASH_PAGE_SIZE]写入Flash,其首地址为0x000000

                spi_flash_buffer_read(Flash_Read_Buffer,0x000000,SPI_FLASH_PAGE_SIZE);
                //从Flash首地址为0x000000开始,读取SPI_FLASH_PAGE_SIZE个字节,保存到Flash_Read_Buffer[]中
       
                i=0;
                i=memory_compare(Flash_Write_Buffer,Flash_Read_Buffer,SPI_FLASH_PAGE_SIZE);
                //若Flash_Write_Buffer[]和Flash_Read_Buffer[]的前SPI_FLASH_PAGE_SIZE个字节相同,则返回1
          if(i) LED2_On();//读写Flash正确,则LED2灯点亮
        }
}

使用特权

评论回复
5
yutingwei|  楼主 | 2023-10-29 01:41 | 只看该作者
#ifndef __GD25Qxx_H
#define __GD25Qxx_H

#include "sys.h"
//#include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t,bool

#define WriteStatusRegister1_CMD   0x01  //写状态寄存器1,Write Status Register-1
#define WriteEnable_CMD            0x06  //写使能命令,Write Enable
#define PageProgram_CMD            0x02  //页编程命令,Page Program
#define SectorErase_CMD            0x20  //扇区擦除命令,Sector Erase
#define ChipErase_CMD              0xC7  //芯片擦除命令,Chip Erase
#define ReadStatusRegister1_CMD    0x05  //读状态寄存器1命令,Read Status Register-1
#define ReadData_CMD               0x03  //读数据命令,Read Data
#define ReadIdentificationCMD      0x9F  //读Flash的ID命令,Read Identification

#define WIP_FLAG         0x01     /* write in progress(wip)flag */
//WIP位置1,表示芯片正处于编程/擦除/写状态
//When WIP bit sets to 1, means the device is busy in program/erase/write status register progress

#define DUMMY_BYTE       0xA5

#define  SFLASH_ID       0xC84015  //Flash的ID为0xC84015
#define SPI_FLASH_PAGE_SIZE    0x100 //GD25Qxx每页有256个字节

union GD32_UINT32_DATA_TYPE
{
        u8 b[4];
        //b[3]和Uint32_Data的高8位值相等;
        //b[0]和Uint32_Data的低8位值相等;
  uint32_t Uint32_Data;
};

#define Flash_Write_Buffer_Size  SPI_FLASH_PAGE_SIZE
#define Flash_Read_Buffer_Size   SPI_FLASH_PAGE_SIZE
extern uint8_t  Flash_Write_Buffer[Flash_Write_Buffer_Size];
extern uint8_t  Flash_Read_Buffer[Flash_Read_Buffer_Size];

extern void GD25Qxx_Init(void);
#endif

使用特权

评论回复
6
AloneKaven| | 2023-10-31 22:53 | 只看该作者
这个支持QSPI不?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

47

主题

381

帖子

0

粉丝