#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
}
#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灯点亮
}
}
#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