基于STM8单片机的M25P16读写驱动程序
时间:10-02
整理:3721RD
点击:
单片机可通过SPI接口对M25P16存储芯片进行读写操作。STM8系列单片机自带有SPI接口,在实际应用中,有时考虑到PCB板布线的方便,所以编者没有采用单片机自带的SPI接口,而是采用I/O口模拟的SPI接口,这样也便于移植给没有带SPI接口的单片机。本例程采用IAR FOR STM8。 程序源码如下:
#i nclude <iostm8s105s4.h>
#define uchar unsigned char
#define uint unsigned int
#define u32 unsigned long
#define nop() asm("nop")
#define LED_OUT_EN() (PB_ODR_ODR3=1) //PB3=1
#define LED_OUT_ST() (PB_ODR_ODR3=0) //PB3=0
#define SPI_CS_L() (PG_ODR_ODR0=0)
#define SPI_CS_H() (PG_ODR_ODR0=1)
#define SPI_SDO_L() (PB_ODR_ODR5=0)
#define SPI_SDO_H() (PB_ODR_ODR5=1)
#define SPI_SCK_L() (PB_ODR_ODR6=0)
#define SPI_SCK_H() (PB_ODR_ODR6=1)
#define SPI_SDI PB_IDR_IDR4
#define WREN 0x06
#define WRDI 0x04
#define RDID 0x9F
#define RDSR 0x05
#define WRSR 0x01
#define READ 0x03
#define FAST_READ 0x0B
#define WRITE 0x02
#define SE 0xD8
#define BE 0xC7
#define Dummy_Byte 0xA5 //哑读字节
#define SPI_Flash_PageSize 256
uchar WriteBuffer[300];
uchar ReadBuffer[300];
u32 WriteAddr;
u32 ReadAddr;
/***************************************
* 延时程序
***************************************/
void delay(void)
{
nop();
nop();
nop();
nop();
nop();
nop();
nop();
nop();
nop();
nop();
}
/************************************************
* SPI_Flash初始化
************************************************/
void SPI_Flash_Init(void)
{
PG_DDR_DDR0 = 1; //CS
PG_CR1_C10 = 1;
PG_CR2_C20 = 0;
PB_DDR_DDR6 = 1; //SCK
PB_CR1_C16 = 1;
PB_CR2_C26 = 0;
PB_DDR_DDR5 = 1; //MOSI
PB_CR1_C15 =1;
PB_CR2_C25 = 0;
PB_DDR_DDR4 = 0; //MISO
PB_CR1_C14 = 1;
PB_CR2_C24 = 0;
}
/************************************************
* 发送一字节数据到SPI_Flash
************************************************/
uchar SPI_Flash_SendByte(uchar byte)
{
uchar i;
for(i=0;i<8;i++) // output 8-bit
{
if(byte & 0x80)
{
SPI_SDO_H();
}
else
{
SPI_SDO_L();
}
delay();
byte = (byte << 1); // shift next bit into MSB..
SPI_SCK_H();
delay();
if(SPI_SDI)
{
byte |= 1;
}
SPI_SCK_L();
delay();
}
return(byte); // return read byte
}
/*************************************************
* 从SPI_Flash读回一字节数据
*************************************************/
uchar SPI_Flash_ReadByte(void)
{
return(SPI_Flash_SendByte(Dummy_Byte));
}
uchar temp0,temp1,temp2,temp3;
/************************************************
* 读SPI_Flash三字节标识码
************************************************/
void SPI_Read_ID(void)
{
SPI_CS_L();
SPI_Flash_SendByte(RDID);
temp0 = SPI_Flash_ReadByte();
temp1 = SPI_Flash_ReadByte();
temp2 = SPI_Flash_ReadByte();
SPI_CS_H();
}
/************************************************
* 等待SPI_Flash完成写入数据
************************************************/
void SPI_Flash_WaitReady(void)
{
uchar state;
SPI_CS_L();
SPI_Flash_SendByte(RDSR);
do
{
state = SPI_Flash_ReadByte();
}
while(state & 0x01);
SPI_CS_H();
}
/************************************************
* 读SPI_Flash数据到缓冲区
* pBuffer: 存放从SPI_Flash读出来的数据
* ReadAddr: 被读的SPI_Flash地址
* ReadNum: 读数据的个数.
************************************************/
void SPI_Flash_ReadBuffer(uchar* pBuffer, u32 ReadAddr, uint ReadNum)
{
SPI_CS_L();
SPI_Flash_SendByte(READ);
SPI_Flash_SendByte((ReadAddr & 0xFF0000) >> 16);
SPI_Flash_SendByte((ReadAddr & 0xFF00) >> 8);
SPI_Flash_SendByte(ReadAddr & 0xFF);
while(ReadNum--)
{
*pBuffer = SPI_Flash_ReadByte();
pBuffer++;
}
SPI_CS_H();
}
/*************************************************
* SPI_Flash写使能
*************************************************/
void SPI_Flash_WREN()
{
SPI_CS_L();
SPI_Flash_SendByte(WREN);
SPI_CS_H();
}
/************************************************
* SPI_Flash写禁止
************************************************/
void SPI_Flash_WRDI()
{
SPI_CS_L();
SPI_Flash_SendByte(WRDI);
SPI_CS_H();
}
/************************************************
* SPI_Flash整块擦除
************************************************/
void SPI_Flash_BlukErase()
{
SPI_Flash_WREN();
SPI_CS_L();
SPI_Flash_SendByte(BE);
SPI_CS_H();
SPI_Flash_WRDI();
SPI_Flash_WaitReady();
}
/*************************************************
* SPI_Flash扇区擦除
*************************************************/
void SPI_Flash_SE(u32 SectorAddr)
{
SPI_Flash_WREN();
SPI_CS_L();
SPI_Flash_SendByte(SE);
SPI_Flash_SendByte((SectorAddr & 0xFF0000) >> 16); //接下来发送3字节地址
SPI_Flash_SendByte((SectorAddr & 0xFF00) >> 8);
SPI_Flash_SendByte(SectorAddr & 0xFF);
SPI_CS_H();
SPI_Flash_WRDI();
SPI_Flash_WaitReady();
}
/*************************************************
* 写一页数据到SPI_Flash
* pBuffer: 存放写入SPI_Flash的数据
* WriteAddr: 指定写入SPI_Flash的地址
* WriteNum: 写数据的个数.
**************************************************/
void SPI_Flash_WritePage(uchar* pBuffer, u32 WriteAddr, uint WriteNum)
{
SPI_Flash_WREN(); //写使能
SPI_CS_L();
delay();
SPI_Flash_SendByte(WRITE); //写指令
SPI_Flash_SendByte((WriteAddr & 0xFF0000) >> 16); //接下来发送3字节地址
SPI_Flash_SendByte((WriteAddr & 0xFF00) >> 8);
SPI_Flash_SendByte(WriteAddr & 0xFF);
while(WriteNum--)
{
SPI_Flash_SendByte(*pBuffer);
pBuffer++;
}
SPI_CS_H();
SPI_Flash_WRDI(); //写禁止
SPI_Flash_WaitReady();
}
/************************************************
* 写数据到SPI_Flash
* pBuffer: 存放写入SPI_Flash的数据
* WriteAddr: 指定写入SPI_Flash的地址
* WriteNum: 写数据的个数.
*************************************************/
void SPI_FLASH_WriteBuffer(uchar* pBuffer, u32 WriteAddr, uint WriteNum)
{
uchar NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
Addr = WriteAddr % SPI_Flash_PageSize;
count = SPI_Flash_PageSize - Addr;
NumOfPage = WriteNum / SPI_Flash_PageSize;
NumOfSingle = WriteNum % SPI_Flash_PageSize;
if (Addr == 0) //如果WriteAddr的低字节为0,即是页的整数倍
{
if (NumOfPage == 0) // WriteNum < SPI_Flash_PageSize,直接页写入
{
SPI_Flash_WritePage(pBuffer, WriteAddr, WriteNum);
}
else // WriteNum > SPI_Flash_PageSize,先页写入,最后写入不足一页的
{
while (NumOfPage--)
{
SPI_Flash_WritePage(pBuffer, WriteAddr, SPI_Flash_PageSize);
WriteAddr += SPI_Flash_PageSize;
pBuffer += SPI_Flash_PageSize;
}
SPI_Flash_WritePage(pBuffer, WriteAddr, NumOfSingle);
}
}
else //如果WriteAddr的低字节不为0,即不是页的整数倍
{
if (NumOfPage == 0) // WriteNum <SPI_FLASH_PAGESIZE
{
if (NumOfSingle > count) // 如果WriteNum大于地址低字节的剩余空间
{
temp = NumOfSingle - count; //则先写完地址低字节的剩余空间
SPI_Flash_WritePage(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
SPI_Flash_WritePage(pBuffer, WriteAddr, temp); //再写下一页
}
else // 如果WriteNum小于地址低字节的剩余空间,直接写入
{
SPI_Flash_WritePage(pBuffer, WriteAddr, WriteNum);
}
}
else // WriteNum > SPI_Flash_PageSize
{
WriteNum -= count;
NumOfPage = WriteNum / SPI_Flash_PageSize;
NumOfSingle = WriteNum % SPI_Flash_PageSize;
SPI_Flash_WritePage(pBuffer, WriteAddr, count); //先写完地址低字节的剩余空间
WriteAddr += count;
pBuffer += count;
while (NumOfPage--) //再进行页写入
{
SPI_Flash_WritePage(pBuffer, WriteAddr, SPI_Flash_PageSize);
WriteAddr += SPI_Flash_PageSize;
pBuffer += SPI_Flash_PageSize;
}
if (NumOfSingle != 0) //如果有不足一页,最后写入不足一页的
{
SPI_Flash_WritePage(pBuffer, WriteAddr, NumOfSingle);
}
}
}
} </SPI_FLASH_PAGESIZE void CLK_Init(void)
{
//CLK_CKdivR &= ~0x10;//时钟分频寄存器
//CLK_CKdivR |= 0x01;
CLK_ECKR=0x03;//外部时钟寄存器 外部时钟准备就绪,外部时钟开
CLK_SWCR=0x02;//切换控制寄存器 使能切换机制
CLK_SWR=0xB4;//主时钟切换寄存器 选择HSE为主时钟源
while (!(CLK_SWCR & 0x08));
//Wait for switch done 该语句如果没有则不能切
//换成功,在仿真时使用内部时钟,停止仿真时,切换到外部时钟,但重新上电或复位后
//又回到内部时钟状态(Debug模式)
//verify the external clock is selected (optional)
//if (CLK_CMSR != 0xB4)
//while(1);
CLK_CSSR=0x01;//时钟安全系统寄存器
}
/******************************************************
* 主程序
******************************************************/
void main( void )
{
CLK_Init();
SPI_Flash_Init();
SPI_Flash_SE(0x000000); //擦除第一扇区
WriteAddr = 0x000000;
ReadAddr = 0x000000;
SPI_READ_ID();
SPI_FLASH_WriteBuffer(WriteBuffer,WriteAddr,300); //写入300字节数据
SPI_Flash_ReadBuffer(ReadBuffer,ReadAddr,300); //读出300字节数据
}
#i nclude <iostm8s105s4.h>
#define uchar unsigned char
#define uint unsigned int
#define u32 unsigned long
#define nop() asm("nop")
#define LED_OUT_EN() (PB_ODR_ODR3=1) //PB3=1
#define LED_OUT_ST() (PB_ODR_ODR3=0) //PB3=0
#define SPI_CS_L() (PG_ODR_ODR0=0)
#define SPI_CS_H() (PG_ODR_ODR0=1)
#define SPI_SDO_L() (PB_ODR_ODR5=0)
#define SPI_SDO_H() (PB_ODR_ODR5=1)
#define SPI_SCK_L() (PB_ODR_ODR6=0)
#define SPI_SCK_H() (PB_ODR_ODR6=1)
#define SPI_SDI PB_IDR_IDR4
#define WREN 0x06
#define WRDI 0x04
#define RDID 0x9F
#define RDSR 0x05
#define WRSR 0x01
#define READ 0x03
#define FAST_READ 0x0B
#define WRITE 0x02
#define SE 0xD8
#define BE 0xC7
#define Dummy_Byte 0xA5 //哑读字节
#define SPI_Flash_PageSize 256
uchar WriteBuffer[300];
uchar ReadBuffer[300];
u32 WriteAddr;
u32 ReadAddr;
/***************************************
* 延时程序
***************************************/
void delay(void)
{
nop();
nop();
nop();
nop();
nop();
nop();
nop();
nop();
nop();
nop();
}
/************************************************
* SPI_Flash初始化
************************************************/
void SPI_Flash_Init(void)
{
PG_DDR_DDR0 = 1; //CS
PG_CR1_C10 = 1;
PG_CR2_C20 = 0;
PB_DDR_DDR6 = 1; //SCK
PB_CR1_C16 = 1;
PB_CR2_C26 = 0;
PB_DDR_DDR5 = 1; //MOSI
PB_CR1_C15 =1;
PB_CR2_C25 = 0;
PB_DDR_DDR4 = 0; //MISO
PB_CR1_C14 = 1;
PB_CR2_C24 = 0;
}
/************************************************
* 发送一字节数据到SPI_Flash
************************************************/
uchar SPI_Flash_SendByte(uchar byte)
{
uchar i;
for(i=0;i<8;i++) // output 8-bit
{
if(byte & 0x80)
{
SPI_SDO_H();
}
else
{
SPI_SDO_L();
}
delay();
byte = (byte << 1); // shift next bit into MSB..
SPI_SCK_H();
delay();
if(SPI_SDI)
{
byte |= 1;
}
SPI_SCK_L();
delay();
}
return(byte); // return read byte
}
/*************************************************
* 从SPI_Flash读回一字节数据
*************************************************/
uchar SPI_Flash_ReadByte(void)
{
return(SPI_Flash_SendByte(Dummy_Byte));
}
uchar temp0,temp1,temp2,temp3;
/************************************************
* 读SPI_Flash三字节标识码
************************************************/
void SPI_Read_ID(void)
{
SPI_CS_L();
SPI_Flash_SendByte(RDID);
temp0 = SPI_Flash_ReadByte();
temp1 = SPI_Flash_ReadByte();
temp2 = SPI_Flash_ReadByte();
SPI_CS_H();
}
/************************************************
* 等待SPI_Flash完成写入数据
************************************************/
void SPI_Flash_WaitReady(void)
{
uchar state;
SPI_CS_L();
SPI_Flash_SendByte(RDSR);
do
{
state = SPI_Flash_ReadByte();
}
while(state & 0x01);
SPI_CS_H();
}
/************************************************
* 读SPI_Flash数据到缓冲区
* pBuffer: 存放从SPI_Flash读出来的数据
* ReadAddr: 被读的SPI_Flash地址
* ReadNum: 读数据的个数.
************************************************/
void SPI_Flash_ReadBuffer(uchar* pBuffer, u32 ReadAddr, uint ReadNum)
{
SPI_CS_L();
SPI_Flash_SendByte(READ);
SPI_Flash_SendByte((ReadAddr & 0xFF0000) >> 16);
SPI_Flash_SendByte((ReadAddr & 0xFF00) >> 8);
SPI_Flash_SendByte(ReadAddr & 0xFF);
while(ReadNum--)
{
*pBuffer = SPI_Flash_ReadByte();
pBuffer++;
}
SPI_CS_H();
}
/*************************************************
* SPI_Flash写使能
*************************************************/
void SPI_Flash_WREN()
{
SPI_CS_L();
SPI_Flash_SendByte(WREN);
SPI_CS_H();
}
/************************************************
* SPI_Flash写禁止
************************************************/
void SPI_Flash_WRDI()
{
SPI_CS_L();
SPI_Flash_SendByte(WRDI);
SPI_CS_H();
}
/************************************************
* SPI_Flash整块擦除
************************************************/
void SPI_Flash_BlukErase()
{
SPI_Flash_WREN();
SPI_CS_L();
SPI_Flash_SendByte(BE);
SPI_CS_H();
SPI_Flash_WRDI();
SPI_Flash_WaitReady();
}
/*************************************************
* SPI_Flash扇区擦除
*************************************************/
void SPI_Flash_SE(u32 SectorAddr)
{
SPI_Flash_WREN();
SPI_CS_L();
SPI_Flash_SendByte(SE);
SPI_Flash_SendByte((SectorAddr & 0xFF0000) >> 16); //接下来发送3字节地址
SPI_Flash_SendByte((SectorAddr & 0xFF00) >> 8);
SPI_Flash_SendByte(SectorAddr & 0xFF);
SPI_CS_H();
SPI_Flash_WRDI();
SPI_Flash_WaitReady();
}
/*************************************************
* 写一页数据到SPI_Flash
* pBuffer: 存放写入SPI_Flash的数据
* WriteAddr: 指定写入SPI_Flash的地址
* WriteNum: 写数据的个数.
**************************************************/
void SPI_Flash_WritePage(uchar* pBuffer, u32 WriteAddr, uint WriteNum)
{
SPI_Flash_WREN(); //写使能
SPI_CS_L();
delay();
SPI_Flash_SendByte(WRITE); //写指令
SPI_Flash_SendByte((WriteAddr & 0xFF0000) >> 16); //接下来发送3字节地址
SPI_Flash_SendByte((WriteAddr & 0xFF00) >> 8);
SPI_Flash_SendByte(WriteAddr & 0xFF);
while(WriteNum--)
{
SPI_Flash_SendByte(*pBuffer);
pBuffer++;
}
SPI_CS_H();
SPI_Flash_WRDI(); //写禁止
SPI_Flash_WaitReady();
}
/************************************************
* 写数据到SPI_Flash
* pBuffer: 存放写入SPI_Flash的数据
* WriteAddr: 指定写入SPI_Flash的地址
* WriteNum: 写数据的个数.
*************************************************/
void SPI_FLASH_WriteBuffer(uchar* pBuffer, u32 WriteAddr, uint WriteNum)
{
uchar NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
Addr = WriteAddr % SPI_Flash_PageSize;
count = SPI_Flash_PageSize - Addr;
NumOfPage = WriteNum / SPI_Flash_PageSize;
NumOfSingle = WriteNum % SPI_Flash_PageSize;
if (Addr == 0) //如果WriteAddr的低字节为0,即是页的整数倍
{
if (NumOfPage == 0) // WriteNum < SPI_Flash_PageSize,直接页写入
{
SPI_Flash_WritePage(pBuffer, WriteAddr, WriteNum);
}
else // WriteNum > SPI_Flash_PageSize,先页写入,最后写入不足一页的
{
while (NumOfPage--)
{
SPI_Flash_WritePage(pBuffer, WriteAddr, SPI_Flash_PageSize);
WriteAddr += SPI_Flash_PageSize;
pBuffer += SPI_Flash_PageSize;
}
SPI_Flash_WritePage(pBuffer, WriteAddr, NumOfSingle);
}
}
else //如果WriteAddr的低字节不为0,即不是页的整数倍
{
if (NumOfPage == 0) // WriteNum <SPI_FLASH_PAGESIZE
{
if (NumOfSingle > count) // 如果WriteNum大于地址低字节的剩余空间
{
temp = NumOfSingle - count; //则先写完地址低字节的剩余空间
SPI_Flash_WritePage(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
SPI_Flash_WritePage(pBuffer, WriteAddr, temp); //再写下一页
}
else // 如果WriteNum小于地址低字节的剩余空间,直接写入
{
SPI_Flash_WritePage(pBuffer, WriteAddr, WriteNum);
}
}
else // WriteNum > SPI_Flash_PageSize
{
WriteNum -= count;
NumOfPage = WriteNum / SPI_Flash_PageSize;
NumOfSingle = WriteNum % SPI_Flash_PageSize;
SPI_Flash_WritePage(pBuffer, WriteAddr, count); //先写完地址低字节的剩余空间
WriteAddr += count;
pBuffer += count;
while (NumOfPage--) //再进行页写入
{
SPI_Flash_WritePage(pBuffer, WriteAddr, SPI_Flash_PageSize);
WriteAddr += SPI_Flash_PageSize;
pBuffer += SPI_Flash_PageSize;
}
if (NumOfSingle != 0) //如果有不足一页,最后写入不足一页的
{
SPI_Flash_WritePage(pBuffer, WriteAddr, NumOfSingle);
}
}
}
} </SPI_FLASH_PAGESIZE void CLK_Init(void)
{
//CLK_CKdivR &= ~0x10;//时钟分频寄存器
//CLK_CKdivR |= 0x01;
CLK_ECKR=0x03;//外部时钟寄存器 外部时钟准备就绪,外部时钟开
CLK_SWCR=0x02;//切换控制寄存器 使能切换机制
CLK_SWR=0xB4;//主时钟切换寄存器 选择HSE为主时钟源
while (!(CLK_SWCR & 0x08));
//Wait for switch done 该语句如果没有则不能切
//换成功,在仿真时使用内部时钟,停止仿真时,切换到外部时钟,但重新上电或复位后
//又回到内部时钟状态(Debug模式)
//verify the external clock is selected (optional)
//if (CLK_CMSR != 0xB4)
//while(1);
CLK_CSSR=0x01;//时钟安全系统寄存器
}
/******************************************************
* 主程序
******************************************************/
void main( void )
{
CLK_Init();
SPI_Flash_Init();
SPI_Flash_SE(0x000000); //擦除第一扇区
WriteAddr = 0x000000;
ReadAddr = 0x000000;
SPI_READ_ID();
SPI_FLASH_WriteBuffer(WriteBuffer,WriteAddr,300); //写入300字节数据
SPI_Flash_ReadBuffer(ReadBuffer,ReadAddr,300); //读出300字节数据
}
真的是很不错的!
强大!
不错,学习了。
真的是很不错的!好资料!
正需要,谢谢小编无私奉献
也正需要呢,本来想随便找个移植到STM8上的,现在正好有个stm8的,多谢了