siuhiong的笔记 https://bbs.21ic.com/?646451 [收藏] [复制] [RSS]

日志

基于STM8单片机的M25P16读写驱动程序

已有 1033 次阅读2012-11-24 01:58 |系统分类:单片机| STM8, m25p16

        单片机可通过SPI接口对M25P16存储芯片进行读写操作。STM8系列单片机自带有SPI接口,在实际应用中,有时考虑到PCB板布线的方便,所以编者没有采用单片机自带的SPI接口,而是采用I/O口模拟的SPI接口,这样也便于移植给没有带SPI接口的单片机。本例程采用IAR FOR STM8。

程序源码如下:


#include <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     {  
      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);  
      }  
    }  
  }  
}  

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字节数据
}


------来源:www.dpjzj.com


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)