SPI

一,定义

 

数据传输速度总体来说比I2C总线要快,速度可达到几Mbps。

缺点:没有指定的流控制,没有应答机制确认是否接收到数据。

二,单机与多机通信

1,单机通信

                                                             

 

2,多机通信

            

三,通信细节与过程

在点对点的通信中, SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从器件的系统中,每个从器件需要独立的使能信号,

硬件上比I2C系统要稍微复杂一些。

SPI接口在内部硬件实际上是两个简单的移位寄存器,传输的数据为8位,在主器件产生的从器件使能信号和移位脉冲下,按位传输,高位在前,低位在后

如下图所示,在SCLK的上升沿上数据改变,同时一位数据被存入移位寄存器

 

 

 

半双工通信追求的是距离,RS485 传输的距离可达1公里多

 

时序图

 

W25Q128_读ID

 

W25Q128_读数据.

四,硬件原理图

 

 

 

五,工作模式

SPI有四种工作模式,各个工作模式的不同在于SCLK不同, 具体工作由CPOL,CPHA决定:

CPOL: (Clock Polarity),时钟极性

当CPOL为0时,时钟空闲时电平为低;

当CPOL为1时,时钟空闲时电平为高;

CPHA:(Clock Phase),时钟相位

CPHA为0时,时钟周期的前一边缘采集数据,时钟周期的后一边缘输出数据;

CPHA为1时,时钟周期的后一边缘采集数据,时钟周期的前一边缘输出数据;

CPOL和CPHA,分别都可以是0或时1,对应的四种组合就是:

 

#include <stdio.h>
#include "stm32f4xx.h"
#include "sys.h"
#include "string.h"

static GPIO_InitTypeDef   GPIO_InitStructure;

static NVIC_InitTypeDef   NVIC_InitStructure;

static USART_InitTypeDef  USART_InitStructure;

static SPI_InitTypeDef      SPI_InitStructure;

#define W25QXX_SS            PBout(14)

//重定义fputc函数 
int fputc(int ch, FILE *f)
{     
    USART_SendData(USART1,ch);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  
    
    return ch;
}   

void delay_us(uint32_t nus)
{        
    uint32_t temp;             
    SysTick->LOAD =SystemCoreClock/8/1000000*nus;     //时间加载               
    SysTick->VAL  =0x00;                            //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;         //使能滴答定时器开始倒数      
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));            //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;         //关闭计数器
    SysTick->VAL =0X00;                               //清空计数器 
}

void delay_ms(uint16_t nms)
{                     
    uint32_t temp;           
    SysTick->LOAD=SystemCoreClock/8/1000*nms;        //时间加载(SysTick->LOAD为24bit)
    SysTick->VAL =0x00;                               //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //能滴答定时器开始倒数 
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));            //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
    SysTick->VAL =0X00;                               //清空计数器              
} 

void USART1_Init(uint32_t baud)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);                             //使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);                            //使能USART1时钟
 
    //串口1对应引脚复用映射
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);                         //GPIOA9复用为USART1
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);                         //GPIOA10复用为USART1
    
    //USART1端口配置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;                         //GPIOA9与GPIOA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                    //复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                //速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                                     //推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                                     //上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure);                                             //初始化PA9,PA10

    //USART1 初始化设置
    USART_InitStructure.USART_BaudRate = baud;                                        //波特率设置
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                        //字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                            //一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;                                //无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    //无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                    //收发模式
    USART_Init(USART1, &USART_InitStructure);                                         //初始化串口
    
    USART_Cmd(USART1, ENABLE);                                                      //使能串口1 
    
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);                                    //开启相关中断

    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;                                //串口1中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;                            //抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;                                //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                    //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);                                                    //根据指定的参数初始化VIC寄存器
}

void w25qxx_init(void)
{
    /*!< Enable the SPI clock ,使能SPI1硬件时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    
    /*!< Enable GPIO clocks ,使能GPIOB的硬件时钟*/
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
    /*!< SPI pins configuration *************************************************/
    
    /*!< Connect SPI pins to AF5 */  
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;
        
    /*!< SPI SCK pin configuration */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /*!< SPI MOSI pin configuration */
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /*!< SPI MISO pin configuration */
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /*!< Configure sFLASH Card CS pin in output pushpull mode ,配置片选引脚PB14为输出模式*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    //由于M4芯片还没有真正配置好,先不让外部从机工作
    W25QXX_SS=1;

    /*!< SPI configuration ,SPI的配置*/
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;        //设置SPI为全双工双线双向通信
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                            //配置M4为主机角色
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                        //SPI的发送和接收都是8bit
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                                //串行时钟线(SCLK)空闲的时候是高电平还是低电平,该电平的设置是跟外围设备密切关系的。
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                            //串行时钟的第二跳变沿进行数据采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                                //片选引脚是由软件控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;        //SPI通信时钟=84MHz/16=5.25MHz
    
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                        //最高有效位优先,根据外围设备的通信进行设定        
    SPI_Init(SPI1, &SPI_InitStructure);
    
    /*!< Enable the sFLASH_SPI  ,使能SPI1*/
    SPI_Cmd(SPI1, ENABLE);

}

uint8_t SPI1_SendByte(uint8_t byte)
{
    /*!< Loop while DR register in not emplty ,等待发送缓冲区为空*/
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

    /*!< Send byte through the SPI1 peripheral,通过SPI1发送字节 */
    SPI_I2S_SendData(SPI1, byte);

    /*!< Wait to receive a byte ,等待接收字节*/
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

    /*!< Return the byte read from the SPI bus,成功接收从机的字节并返回 */
    return SPI_I2S_ReceiveData(SPI1);
}


uint16_t w25qxx_read_id(void)
{
    uint16_t id=0;
    
    //片选引脚拉低
    W25QXX_SS=0;
    
    
    //发送0x90
    SPI1_SendByte(0x90);
    
    //发送24bit地址,全都是0
    SPI1_SendByte(0x00);    
    SPI1_SendByte(0x00);
    SPI1_SendByte(0x00);
    
    //读取厂商ID,传递任意的参数
    id=SPI1_SendByte(0xFF)<<8;
    
    //读取设备ID,传递任意的参数
    id|=SPI1_SendByte(0xFF);

    //片选引脚拉高
    W25QXX_SS=1;    
    
    return id;

}

void w25qxx_read_data(uint32_t addr,uint8_t *pbuf,uint32_t len)
{
    //片选引脚拉低
    W25QXX_SS=0;

    //发送0x03
    SPI1_SendByte(0x03);
    
    //发送24bit地址
    SPI1_SendByte((addr>>16)&0xFF);    
    SPI1_SendByte((addr>>8)&0xFF);
    SPI1_SendByte( addr&0xFF);
    
    //读取数据
    while(len--)
        *pbuf++ = SPI1_SendByte(0xFF);
    
    //片选引脚拉高
    W25QXX_SS=1;    
}

//解除写保护
void w25qxx_write_enable(void)
{
    //片选引脚拉低
    W25QXX_SS=0;

    //发送0x06
    SPI1_SendByte(0x06);
    
    //片选引脚拉高
    W25QXX_SS=1;    

}

//写保护
void w25qxx_write_disable(void)
{
    //片选引脚拉低
    W25QXX_SS=0;

    //发送0x04
    SPI1_SendByte(0x04);
    
    //片选引脚拉高
    W25QXX_SS=1;    


}

uint8_t w25qxx_read_status1(void)
{
    uint8_t status;
    
    //片选引脚拉低
    W25QXX_SS=0;

    //发送0x05
    SPI1_SendByte(0x05);
    
    //读取状态寄存器1的值
    status = SPI1_SendByte(0xFF);
    
    //片选引脚拉高
    W25QXX_SS=1;    
    
    
    return status;

}

void w25qxx_erase_sector(uint32_t addr)
{
    uint8_t sta=0;
    //解除写保护
    w25qxx_write_enable();
    
    //延时1ms,让W25Q128识别到CS引脚的变化
    delay_ms(1);
    
    //片选引脚拉低
    W25QXX_SS=0;    
    
    //发送0x20
    SPI1_SendByte(0x20);
    
    //发送24bit地址
    SPI1_SendByte((addr>>16)&0xFF);    
    SPI1_SendByte((addr>>8)&0xFF);
    SPI1_SendByte( addr&0xFF);    
    
    //片选引脚拉高
    W25QXX_SS=1;    

    //去判断当前是否已经擦除成功
    while(1)
    {
        sta = w25qxx_read_status1();
        
        if((sta & 0x01)==0)
            break;
        
        delay_ms(1);
    }
    
    //开启写保护
    w25qxx_write_disable();
}


void w25qxx_write_data(uint32_t addr,uint8_t *pbuf,uint32_t len)
{
    uint8_t sta=0;
    //解除写保护
    w25qxx_write_enable();
    
    //延时1ms,让W25Q128识别到CS引脚的变化
    delay_ms(1);
    
    //片选引脚拉低
    W25QXX_SS=0;    
    
    //发送0x02
    SPI1_SendByte(0x02);
    
    //发送24bit地址
    SPI1_SendByte((addr>>16)&0xFF);    
    SPI1_SendByte((addr>>8)&0xFF);
    SPI1_SendByte( addr&0xFF);    
    
    //发送数据
    while(len--)
        SPI1_SendByte(*pbuf++);
    
    //片选引脚拉高
    W25QXX_SS=1;    

    //去判断当前是否已经写入成功
    while(1)
    {
        sta = w25qxx_read_status1();
        
        if((sta & 0x01)==0)
            break;
        
        delay_ms(1);
    }
    
    //开启写保护
    w25qxx_write_disable();    
}


int main(void)
{
    uint16_t w25qxx_id=0;
    uint8_t buf_wr[64]={0};
    uint8_t buf_rd[64]={0};    
    uint32_t i=0;

    //系统定时器初始化,时钟源来自HCLK,且进行8分频,
    //系统定时器时钟频率=168MHz/8=21MHz
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
        
    //设置中断优先级分组2
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //串口1,波特率115200bps,开启接收中断
    USART1_Init(115200);

    //w25qxx初始化
    w25qxx_init();
    
    printf("This is spi flash test\r\n");
    
    w25qxx_id=w25qxx_read_id();
    
    printf("w25qxx id=%X\r\n",w25qxx_id);
    
    
    //进行扇区擦除
    printf("w25qxx erase from 0\r\n");
    w25qxx_erase_sector(0);
    
    
    //写入数据
    printf("w25qxx write at 0,data all is 1\r\n");
    memset(buf_wr,'1',sizeof buf_wr);
    w25qxx_write_data(0,buf_wr,64);
    
    //读取数据
    printf("w25qxx read at 0\r\n");
    memset(buf_rd,0,sizeof buf_rd);
    w25qxx_read_data(0,buf_rd,64);

    for(i=0; i<64; i++)
    {
        printf("%c ",buf_rd[i]);
    }
    
    while(1)
    {
        

    }

}
main.c
#ifndef __SYS_H__
#define __SYS_H__

//位带操作,实现51类似的GPIO控制功能
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入

#endif
sys.h

 

#include <stdio.h>
#include "stm32f4xx.h"
#include "sys.h"
#include "string.h"

static GPIO_InitTypeDef   GPIO_InitStructure;

static NVIC_InitTypeDef   NVIC_InitStructure;

static USART_InitTypeDef  USART_InitStructure;

static SPI_InitTypeDef      SPI_InitStructure;

#define W25QXX_SS            PBout(14)
#define W25QXX_SCLK            PBout(3)
#define W25QXX_MOSI            PBout(5)
#define W25QXX_MISO            PBin(4)

//重定义fputc函数 
int fputc(int ch, FILE *f)
{     
    USART_SendData(USART1,ch);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  
    
    return ch;
}   

void delay_us(uint32_t nus)
{        
    uint32_t temp;             
    SysTick->LOAD =SystemCoreClock/8/1000000*nus;     //时间加载               
    SysTick->VAL  =0x00;                            //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;         //使能滴答定时器开始倒数      
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));            //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;         //关闭计数器
    SysTick->VAL =0X00;                               //清空计数器 
}

void delay_ms(uint16_t nms)
{                     
    uint32_t temp;           
    SysTick->LOAD=SystemCoreClock/8/1000*nms;        //时间加载(SysTick->LOAD为24bit)
    SysTick->VAL =0x00;                               //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //能滴答定时器开始倒数 
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));            //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
    SysTick->VAL =0X00;                               //清空计数器              
} 

void USART1_Init(uint32_t baud)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);                             //使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);                            //使能USART1时钟
 
    //串口1对应引脚复用映射
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);                         //GPIOA9复用为USART1
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);                         //GPIOA10复用为USART1
    
    //USART1端口配置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;                         //GPIOA9与GPIOA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                    //复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                //速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                                     //推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                                     //上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure);                                             //初始化PA9,PA10

    //USART1 初始化设置
    USART_InitStructure.USART_BaudRate = baud;                                        //波特率设置
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                        //字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                            //一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;                                //无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    //无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                    //收发模式
    USART_Init(USART1, &USART_InitStructure);                                         //初始化串口
    
    USART_Cmd(USART1, ENABLE);                                                      //使能串口1 
    
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);                                    //开启相关中断

    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;                                //串口1中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;                            //抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;                                //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                    //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);                                                    //根据指定的参数初始化VIC寄存器
}

void w25qxx_init(void)
{

    /*!< Enable GPIO clocks ,使能GPIOB的硬件时钟*/
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
    
    /* 配置片选引脚PB3 PB5 PB14为输出模式*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_5|GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /* 配置片选引脚PB4为输入模式*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);    

    //由于M4芯片还没有真正配置好,先不让外部从机工作
    W25QXX_SS=1;
}

uint8_t SPI1_SendByte(uint8_t byte)
{
    uint32_t i=0;
    uint8_t d=0;
    
    for(i=0; i<8; i++)
    {
        //MSB,最高有效位发送数据
        if(byte & (1<<(7-i)))
            W25QXX_MOSI =1;
        else
            W25QXX_MOSI =0;
        
        //设置SCLK引脚为低电平
        W25QXX_SCLK=0;
        delay_us(2);
        
        
        //由于是同步事件
        if(W25QXX_MISO)
            d|=1<<(7-i);
    
    
        //设置SCLK引脚为高电平
        W25QXX_SCLK=1;
        delay_us(2);    
    
    
    
    }
    
    return d;
}


uint16_t w25qxx_read_id(void)
{
    uint16_t id=0;
    
    //片选引脚拉低
    W25QXX_SS=0;
    
    
    //发送0x90
    SPI1_SendByte(0x90);
    
    //发送24bit地址,全都是0
    SPI1_SendByte(0x00);    
    SPI1_SendByte(0x00);
    SPI1_SendByte(0x00);
    
    //读取厂商ID,传递任意的参数
    id=SPI1_SendByte(0xFF)<<8;
    
    //读取设备ID,传递任意的参数
    id|=SPI1_SendByte(0xFF);

    //片选引脚拉高
    W25QXX_SS=1;    
    
    return id;

}

void w25qxx_read_data(uint32_t addr,uint8_t *pbuf,uint32_t len)
{
    //片选引脚拉低
    W25QXX_SS=0;

    //发送0x03
    SPI1_SendByte(0x03);
    
    //发送24bit地址
    SPI1_SendByte((addr>>16)&0xFF);    
    SPI1_SendByte((addr>>8)&0xFF);
    SPI1_SendByte( addr&0xFF);
    
    //读取数据
    while(len--)
        *pbuf++ = SPI1_SendByte(0xFF);
    
    //片选引脚拉高
    W25QXX_SS=1;    
}

//解除写保护
void w25qxx_write_enable(void)
{
    //片选引脚拉低
    W25QXX_SS=0;

    //发送0x06
    SPI1_SendByte(0x06);
    
    //片选引脚拉高
    W25QXX_SS=1;    

}

//写保护
void w25qxx_write_disable(void)
{
    //片选引脚拉低
    W25QXX_SS=0;

    //发送0x04
    SPI1_SendByte(0x04);
    
    //片选引脚拉高
    W25QXX_SS=1;    


}

uint8_t w25qxx_read_status1(void)
{
    uint8_t status;
    
    //片选引脚拉低
    W25QXX_SS=0;

    //发送0x05
    SPI1_SendByte(0x05);
    
    //读取状态寄存器1的值
    status = SPI1_SendByte(0xFF);
    
    //片选引脚拉高
    W25QXX_SS=1;    
    
    
    return status;

}

void w25qxx_erase_sector(uint32_t addr)
{
    uint8_t sta=0;
    //解除写保护
    w25qxx_write_enable();
    
    //延时1ms,让W25Q128识别到CS引脚的变化
    delay_ms(1);
    
    //片选引脚拉低
    W25QXX_SS=0;    
    
    //发送0x20
    SPI1_SendByte(0x20);
    
    //发送24bit地址
    SPI1_SendByte((addr>>16)&0xFF);    
    SPI1_SendByte((addr>>8)&0xFF);
    SPI1_SendByte( addr&0xFF);    
    
    //片选引脚拉高
    W25QXX_SS=1;    

    //去判断当前是否已经擦除成功
    while(1)
    {
        sta = w25qxx_read_status1();
        
        if((sta & 0x01)==0)
            break;
        
        delay_ms(1);
    }
    
    //开启写保护
    w25qxx_write_disable();
}


void w25qxx_write_data(uint32_t addr,uint8_t *pbuf,uint32_t len)
{
    uint8_t sta=0;
    //解除写保护
    w25qxx_write_enable();
    
    //延时1ms,让W25Q128识别到CS引脚的变化
    delay_ms(1);
    
    //片选引脚拉低
    W25QXX_SS=0;    
    
    //发送0x02
    SPI1_SendByte(0x02);
    
    //发送24bit地址
    SPI1_SendByte((addr>>16)&0xFF);    
    SPI1_SendByte((addr>>8)&0xFF);
    SPI1_SendByte( addr&0xFF);    
    
    //发送数据
    while(len--)
        SPI1_SendByte(*pbuf++);
    
    //片选引脚拉高
    W25QXX_SS=1;    

    //去判断当前是否已经写入成功
    while(1)
    {
        sta = w25qxx_read_status1();
        
        if((sta & 0x01)==0)
            break;
        
        delay_ms(1);
    }
    
    //开启写保护
    w25qxx_write_disable();    
}


int main(void)
{
    uint16_t w25qxx_id=0;
    uint8_t buf_wr[64]={0};
    uint8_t buf_rd[64]={0};    
    uint32_t i=0;

    //系统定时器初始化,时钟源来自HCLK,且进行8分频,
    //系统定时器时钟频率=168MHz/8=21MHz
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
        
    //设置中断优先级分组2
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //串口1,波特率115200bps,开启接收中断
    USART1_Init(115200);

    //w25qxx初始化
    w25qxx_init();
    
    printf("This is spi flash test\r\n");
    
    w25qxx_id=w25qxx_read_id();
    
    printf("w25qxx id=%X\r\n",w25qxx_id);
    
    
    //进行扇区擦除
    printf("w25qxx erase from 0\r\n");
    w25qxx_erase_sector(0);
    
    
    //写入数据
    printf("w25qxx write at 0,data all is 1\r\n");
    memset(buf_wr,'1',sizeof buf_wr);
    w25qxx_write_data(0,buf_wr,64);
    
    //读取数据
    printf("w25qxx read at 0\r\n");
    memset(buf_rd,0,sizeof buf_rd);
    w25qxx_read_data(0,buf_rd,64);

    for(i=0; i<64; i++)
    {
        printf("%c ",buf_rd[i]);
    }
    
    while(1)
    {
        

    }

}
软件模拟spi

 

 

练习:

通过ubuntu编写一个串口程序,通过该程序下载该文件到M4的SPI FLASH,实现移动小U盘,详细要求如下:

1)终端输入“down 文件名”,下载过程当中要显示当前的进度

2)终端输入“up 文件名”,上传过程当中要显示当前的进度

 

实现RFID代码的移植,实现卡的读写!

 

思考题1:读取数据一次性最多能够读取多少个字节?

 

思考题2:在进行读取数据的时候,SPI1_SendByte函数的参数能不能任意值?

 

 

 

posted @ 2019-05-28 22:29  狂奔~  阅读(499)  评论(0编辑  收藏  举报