[活动专区]

赛元易码魔盒体验——13测试拓展板的SPI-FLASH

[复制链接]
581|19
手机看帖
扫描二维码
随时随地手机跟帖
一路向北lm|  楼主 | 2020-7-21 08:34 | 显示全部楼层
上图原理图可知,W25Q64的SPI管脚接到了SC95F8617的USCI2—SPI,我们只需要对USCI2操作即可完成对W25Q64的读写。打开魔盒简单配置一下,其它的相关配置我就不多说了,比如串口、定时器、LED指示灯等。这里重点讲述USCI2-SPI的配置,具体如下,此外还需要配置一个CS片选使能管脚,这里是P5.5。
549035f1638184c253.png

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:35 | 显示全部楼层
在魔盒配置中我们将SC95F-SPI配置为主设备,数据的格式设置为MSB在前,时钟极性设置为低电平,时钟相位设置为第一沿采集数据,传输数据位设置为8位,且使能了发送中断和SPI。生成的代码具体配置如下:
/**************************************************
*函数功能:SPI初始化配置函数
*入口参数:FirstBit                                优先传送位选择(MSB/LSB)
                   BaudRatePrescaler        SPI时钟频率选择
                   Mode                                        SPI工作模式选择
                   ClockPolarity                SPI时钟极性选择
                   ClockPhase                        SPI时钟相位选择
                   SPI_TXE_INT                        发送缓存器中断允许选择
                   TransmissionMode                SPI传输模式选择 8/16e位
*出口参数:void                                                                                                                                                                                               
**************************************************/
void USCI2_SPI_Init(USCI2_SPI_FirstBit_TypeDef FirstBit, USCI2_SPI_BaudRatePrescaler_TypeDef BaudRatePrescaler, USCI2_SPI_Mode_TypeDef Mode,USCI2_SPI_ClockPolarity_TypeDef ClockPolarity, USCI2_SPI_ClockPhase_TypeDef ClockPhase, USCI2_SPI_TXE_INT_TypeDef SPI_TXE_INT,USCI2_TransmissionMode_TypeDef TransmissionMode)
{
        TMCON = (TMCON & 0X3F) | 0X40;
        US2CON1 = US2CON1 & (~0X05) | FirstBit | SPI_TXE_INT | TransmissionMode;
        US2CON0 = US2CON0 & 0X80 | BaudRatePrescaler | Mode | ClockPolarity | ClockPhase;
}


使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:35 | 显示全部楼层
在中断函数USCI2Interrupt()          中添加标志位,具体代码如下:
void USCI2Interrupt()                interrupt 16                
{
    /*<UserCodeStart>*/
        if(USCI2_GetFlagStatus(USCI2_SPI_FLAG_SPIF))                //SPIF标志位
        {
                US2CON1 &= (~0x80);
          SPI** = 1;
        }
    /*<UserCodeEnd>*/
        /*USCI2_it write here*/               
}


使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:37 | 显示全部楼层
W25Q64的驱动代码部分
/**************************************************
*函数名称:void SPI_WriteByte(uint8_t DATA)
*函数功能:SPI写一BYTE
*入口参数:DATA           发送数据(取值范围:uint8_t)
*出口参数:void
*功能说明:
**************************************************/
void SPI_WriteByte(uint8_t DATA)
{
        USCI2_SPI_SendData_8(DATA);
        while(!SPI**);
        SPI** = 0;
}


使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:37 | 显示全部楼层
/**************************************************
*函数名称:void SPI_ReadByte(void)
*函数功能:SPI读一BYTE
*入口参数:void                       
*出口参数:DATA        读得数据(取值范围:uint8_t)
*功能说明:
**************************************************/
uint8_t SPI_ReadByte(void)
{
        uint8_t DATA;
        SPI_WriteByte(0X00);
        DATA = USCI2_SPI_ReceiveData_8();       
        return DATA;
}

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:37 | 显示全部楼层
/**************************************************
*函数名称:void WriteEnable(void)
*函数功能:写允许
*入口参数:void                       
*出口参数:void
*功能说明:
**************************************************/
void WriteEnable(void)
{
    CS = 0;
    SPI_WriteByte(W25_WriteEnable);  
    CS = 1;
}

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:38 | 显示全部楼层
/**************************************************
*函数名称:uint8_t W25Q16_ReadStatus()
*函数功能:写允许
*入口参数:void                       
*出口参数:status     W25寄存器当前状态值
*功能说明:第0位=0表示空闲,第0位=1表示忙
**************************************************/
uint8_t W25Q16_ReadStatus()
{
    uint8_t status = 0;
    CS = 0;
    SPI_WriteByte(W25_ReadStatus);   // 0x05读取状态的命令字
    status = SPI_ReadByte();        // 读取状态字节
    CS = 1;                         // 关闭片选
    return status;
}

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:42 | 显示全部楼层
/**************************************************
*函数名称:void W25Q16_WriteStatus(uint8_t Status)
*函数功能:写状态寄存器
*入口参数:Status           写入数据(取值范围:uint8_t)            
*出口参数:void
*功能说明:
**************************************************/
void W25Q16_WriteStatus(uint8_t Status)
{
    CS = 0;
    SPI_WriteByte(W25_WriteStatus);  // 0x01读取状态的命令字
    SPI_WriteByte(Status);            // 写入一个字节
    CS = 1;                         // 关闭片选
}

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:43 | 显示全部楼层
/**************************************************
*函数名称:void W25_Flash_Write_Page(uint8_t *pbuf,uint32_t WriteAddr,uint16_t Len)
*函数功能:页写
*入口参数:pbuf           写入指向数据的指针
                   WriteAddr  写入地址(取值范围:0~FFFFFF)
                   Len               写入的字节数(取值范围:0~256)
*出口参数:void
*功能说明:要写入的字节数不应超过该页剩余字节数
**************************************************/
void W25_Flash_Write_Page(uint8_t *pbuf,uint32_t WriteAddr,uint16_t Len)
{
        uint16_t i;
        WriteEnable();                  //SET WEL
        while(W25Q16_ReadStatus()&0x01);    //判断是否忙
        WriteEnable();                      //SET WEL
        CS  =0;                            //使能器件   
        SPI_WriteByte(W25_Writepage);      //发送写页命令
        SPI_WriteByte((uint8_t)((WriteAddr) >> 16)); //发送24bit地址   
        SPI_WriteByte((uint8_t)((WriteAddr) >> 8));   
        SPI_WriteByte((uint8_t)WriteAddr);  
    for(i=0;i<Len;i++)               //循环写数
    {
        SPI_WriteByte(*pbuf++);      
    }
    CS = 1;                              //取消片选
    while(W25Q16_ReadStatus() & 0x01);   //等待写入结束   
}

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:43 | 显示全部楼层
/**************************************************
*函数名称:void SPI_Flash_Write_NoCheck(uint8_t *pbuf,uint32_t WriteAddr,uint16_t Len)
*函数功能:无自检写任意长度数据
*入口参数:pbuf           写入指向数据的指针
                   WriteAddr  写入地址(取值范围:0~FFFFFF)
                   Len               写入的字节数
*出口参数:void
*功能说明:
**************************************************/
void SPI_Flash_Write_NoCheck(uint8_t *pbuf,uint32_t WriteAddr,uint16_t Len)
{
    uint16_t PageLen;                  //页内写入字节长度
    PageLen = 256 - (WriteAddr % 256); //单页剩余的字节数 (单页剩余空间)
    if(Len <= PageLen)
                PageLen = Len;                     //不大于256 个字节
    while(1)
    {
        W25_Flash_Write_Page(pbuf,WriteAddr,PageLen);
        if(PageLen == Len)
                        break;             //写入结束了
        else
        {
            pbuf += PageLen;
            WriteAddr += PageLen;
            Len -= PageLen;    //减去已经写入了的字节数
            if(Len > 256)
                        {
                                PageLen = 256; //此时可以进行整页写
                        }
            else
                        {
                                PageLen = Len; //页写小于256字节数
                        }
        }
    }
}

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:43 | 显示全部楼层
/**************************************************
*函数名称:void SPI_Flash_Read(uint8_t *pbuf,uint32_t ReadAddr,uint16_t Len)   
*函数功能:读任意长度数据
*入口参数:pbuf           所读数据缓存数组指针
                   WriteAddr  所读地址(取值范围:0~FFFFFF)
                   Len               所读的字节数
*出口参数:void
*功能说明:
**************************************************/
void SPI_Flash_Read(uint8_t *pbuf,uint32_t ReadAddr,uint16_t Len)   
{
    uint16_t i;   
    while(W25Q16_ReadStatus()&0x01);      //判断是否忙
        WriteEnable();                                                     
    CS = 0;                                 //使能器件   
    SPI_WriteByte(W25_ReadDATA8);         //发送读取命令   
    SPI_WriteByte((uint8_t)(ReadAddr >> 16));  //发送24bit地址   
        SPI_WriteByte((uint8_t)(ReadAddr >> 8));   
        SPI_WriteByte((uint8_t)ReadAddr);  
        for(i=0;i<Len;i++)
        {
           *pbuf++ = SPI_ReadByte();           //读一个字节   
        }
        CS = 1;                                 //取消片选            
}

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:44 | 显示全部楼层
/**************************************************
*函数名称:void W25_SectorErase(uint32_t Addr4KB)
*函数功能:擦除4KB数据
*入口参数:Addr4KB    擦除地址(取值范围:0~FFFFFF)
*出口参数:void
*功能说明:擦除一个扇区的最少时间:150ms
**************************************************/
void W25_SectorErase(uint32_t Addr4KB) //擦除4KB空间
{                    
    while(W25Q16_ReadStatus()&0x01);   //判断是否忙   
    WriteEnable();                     //写允许
    CS = 0;
    SPI_WriteByte(W25_S_Erase);        //整扇擦除命令
    SPI_WriteByte((uint8_t)(Addr4KB >> 16));
    SPI_WriteByte((uint8_t)(Addr4KB >> 8));
    SPI_WriteByte((uint8_t)Addr4KB);
    CS = 1;
    while(W25Q16_ReadStatus() & 0x01);   // 等待擦除完成
}

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:44 | 显示全部楼层
#ifndef _BSP_W25Q64_H
#define _BSP_W25Q64_H

#include "sc95f8x1x_conf.h"

#define W25_ReadStatus       0x05      //读状态寄存器
#define W25_WriteStatus      0x01      //写状态寄存器
#define W25_ReadDATA8        0x03      //普读_数据
#define W25_FastRead         0x0B      //快读_数据
#define W25_DualOutput       0x3B      //快读_双输出
#define W25_Writepage        0x02      //写_数据_0~255个字节
#define W25_S_Erase          0x20      //扇区擦除4KB
#define W25_B_Erase          0xD8      //块区擦除64KB
#define W25_C_Erase          0xC7      //整片格式化
#define W25_PowerDown        0xB9      //待机
#define W25_PowerON_ID       0xAB      //开机或是读ID
#define W25_JEDEC_ID         0x9F      //十六位的JEDEC_ID
#define W25_WriteEnable      0x06      //写允许
#define W25_WriteDisable     0x04      //写禁止

void SPI_WriteByte(uint8_t DATA);
uint8_t SPI_ReadByte(void);
void WriteEnable(void);
uint8_t W25Q16_ReadStatus();
void W25Q16_WriteStatus(uint8_t Status);
void W25_Flash_Write_Page(uint8_t *pbuf,uint32_t WriteAddr,uint16_t Len);
void SPI_Flash_Write_NoCheck(uint8_t *pbuf,uint32_t WriteAddr,uint16_t Len);
void SPI_Flash_Read(uint8_t *pbuf,uint32_t ReadAddr,uint16_t Len);
void W25_SectorErase(uint32_t Addr4KB);

#endif

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:55 | 显示全部楼层
测试代码部分:
unsigned char Send_DATA[10];
unsigned char Rec_DATA[10];

void main(void)
{       
    /*<UserCodeStart>*/
        uint16_t i;
        for(i=0;i<10;i++)
        {
                Send_DATA[i] = i;  //发送数组赋值
                Rec_DATA[i] = 0;   //接收数组赋值       
        }
    /*<UserCodeEnd>*/
        /*** MCU初始化函数 ***/
        SC_Init();
       
        W25_SectorErase(0x000000);          //W25Q16擦除0x0000起始的4k空间
        SPI_Flash_Write_NoCheck(Send_DATA,0x000000,10);    //往0X0000地址写10个数,数值为Send_DATA[i]内值
  SPI_Flash_Read(Rec_DATA,0x000000,10);   //从0X0000读10个数,存放到Rec_DATA[10]

        while(1)
        {
        Uart_Send_String("\n Rec_DATA[10] value = \n{"); //串口打印Rec_DATA数组值,观察是否为写入值
                for(i=0;i<10;i++)
                {
                        Uart_Send_Byte(Rec_DATA[i]+0x30);              //转化为ACII值
                }
         Uart_Send_String("}");
}
}


使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:56 | 显示全部楼层
经过不断的调试,崩溃就是检测硬件一直忙,后面发现问题啦,原来把两根数据线接反了,唉!这块板子,你说串口线接反就算了,SPI口也接反了,我已经在在怀疑人生了,好了,赶紧抢救,飞的线惨不忍睹,大家看一下。
543505f163d349a017.png

使用特权

评论回复
一路向北lm|  楼主 | 2020-7-21 08:56 | 显示全部楼层
好了,飞过线测试一下,一切OK!
321445f163d50e8f96.png

使用特权

评论回复
观海| | 2020-8-7 14:04 | 显示全部楼层
感谢楼主分享

使用特权

评论回复
八层楼| | 2020-8-7 14:05 | 显示全部楼层
介绍的很详细

使用特权

评论回复
一路向北lm|  楼主 | 2020-8-7 14:05 | 显示全部楼层

不客气,应该的

使用特权

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

本版积分规则

256

主题

3639

帖子

72

粉丝