Bootstrap

STM32 SPI通信 F407外设控制器 W25Q64

SPI介绍

                  SPI:串行外部设备接口 --- 通信接口
                           特点:高速同步串行全双工   --- 40MHz
                           场景:存储器   OLED   无线通信   传感器
         硬件连线:
                         SCL     时钟线     ---------------------   只能由主机提供
                        MOSI   发送线     ----------------------  主机输出从机输入    
                        MISO   接收线     ----------------------  主机输入从机输出
                         CS      片选线     ----------------------  开始/停止信号

SPI通信

                特点:

                         数据位:  8 /16位          //取决于从器件支持
                         MSB 在前/LSB 在前      //取决于从器件支持

           传输过程:

                         1、MCU选中芯片       //拉低片选
                         2、传输数据 
                         3、MCU释放芯片     //拉高片选
                         即 数据帧  :    片选拉低  +  数据位  +  片选拉高

SPI模式

                  SPI有四种标准模式,其主要区别体现:
                                什么时候输出数据,什么时候采样数据

 不同模式由以下两个参数决定:

           时钟极性CPOL:在空闲状态是,SCK的默认电平

           时钟相位CPHA:决定数据在哪个边沿采样 //可以理解为时钟格式

           CPHA = 0      时钟线的第一个跳变沿数据线可以采样

           CPHA = 1      时钟线的第二个跳变沿数据线可以采样

     两位参数可以组成4种模式,常用的是0模式和3模式

           SPI通信传输,双方必须在相同模式

以主机视角:         

        边沿输出数据:即将发送移位寄存器的最高位的数据输出ODR

        边沿采样数据:即将读取IDR数据移到接收移位寄存器的最低位

        以下为模式0的时序图,进行分析

                数据的第一个位bit1在第一个上升沿之前,即SCK在低电平时

                就已经将bit1的数据输出到ODR

                接下来的时序
                   上升沿到来,读取数据
                   下降沿到来,输出数据

SPI外设       

以下为STM32F4系列的SPI资源

图源《野火科技》

SPI数据

SPI发送数据和接收数据的规则:

                   发送一个位数据一定会接收到一位数据

                   接收一个位数据之前一定要发送一位数据     //必须主机主动发送数据

                          所以,发送函数和接收函数要写成一个函数.

       注意:

              要发送有意义的数据,同时会接受到无意义的数据,接收数据不用理会

              要接受有意义的数据,同时需要发送无意义的数据,发送数据随意即可

        

 SPI代码


/***************************************
*函数名            :spi1_init
*函数功能        :spi所用IO口初始化配置函数
*函数参数        :无
*函数返回值    :无
*函数描述        :SCK------PA5  复用输出
                            MOSI-----PA7  复用推挽输出
                            MISO-----PA6  复用输入
****************************************/
void spi1_init(void)
{
    /*IO口配置*/
    //端口时钟使能
    RCC->AHB1ENR |= (1<<0);
    //端口模式配置
    GPIOA->MODER &= ~((3<<10) | (3<<14) | (3<<12));
    GPIOA->MODER |= ((2<<10) | (2<<14) | (2<<12));
    //端口输出类型
    GPIOA->OTYPER &= ~((1<<5) | (1<<7));
    //端口输出速度
    GPIOA->OSPEEDR &= ~((3<<10) | (3<<14));
    GPIOA->OSPEEDR |= ((2<<10) | (2<<14));
    //无上下拉
    GPIOA->PUPDR &= ~((3<<10) | (3<<14) | (3<<12));
    //SPI1与IO口复用关系
    GPIOA->AFR[0] &= ~((0xf<<20) | (0xf<<24) | (0xf<<28));
    GPIOA->AFR[0] |= ((5<<20) | (5<<24) |(5<<28));
    
    /*SPI控制器配置*/
    //SPI1时钟使能
    RCC->APB2ENR |= (1<<12);
    //CR1
    SPI1->CR1 &= ~(1<<15);           //双线单向
    SPI1->CR1 &= ~(1<<11);           //使用8位数据格式
    SPI1->CR1 &= ~(1<<10);           //全双工模式
    SPI1->CR1 |= (1<<9);                //SSM置1 软件片选(NSS)控制 
    SPI1->CR1 |= (1<<8);                //SSI置1 禁止软件从设备,即做主机
    SPI1->CR1 &= ~(1<<7);            //MSB高位先行
    SPI1->CR1 &= ~(7<<3);            //2分频
    SPI1->CR1 |= (1<<2);                //主机模式
    SPI1->CR1 &= ~(1<<1);            //CPOL=0    空闲状态下, SCK保持低电平
    SPI1->CR1 &= ~(1<<0);            //CPHA=0    数据采集从第一个时钟边沿开始采样
    //CR2
    SPI1->CR2 &= ~(1<<4);            //使用Motorrla模式
    
    //使能SPI
    SPI1->CR1 |= (1<<6);
}

/***************************************
*函数名            :spi1_byte
*函数功能        :spi1收发一字节函数
*函数参数        :u8 data
*函数返回值    :u8
*函数描述        :0,0模式
****************************************/
u8 spi1_byte(u8 data)
{
    u8 val;
    //等待之前的数据发送完成
    while(!(SPI1->SR & (1<<1)));
    //把要发送的数据给数据寄存器
    SPI1->DR = data;
    //等待接收数据完成
    while(!(SPI1->SR & (1<<0)));
    //把数据寄存器的值给到一个变量
    val = SPI1->DR;
    
    return val;
}
 

W25Q64介绍               

                W25Q64是一款Flash类型存储芯片.
                内存大小64Mbit ==8Mbyte
                通信接口是标准SPI, 支持 (0,0)   和   (1,1) 模式    MSB
                手动擦除数据(写数据前要擦除空间)
                不允许跨页写

存储结构:
                存储空间是8M字节
                内存区域划分:  块   扇区   页
                块  :一共有128块,每块有16扇区 
                扇区:一个扇区有16页
                页  :一页有256byte

写入地址: 
               十六进制:   0~0x7F F F FF 范围
               ​​​​​ 某块某扇区某页某个字节
                 XX          X             X            XX
    如写入  0x12 3 4 56   即18号块的3号扇区的4号页的86号字节

W25Q64框图

W25Q64指令

                        0x06        写使能

                        0x05        读控制及状态寄存器

                        0x01        写控制及状态寄存器

                        0x02        页写操作

                        0x03        连续读数据操作
                        0x20        扇区擦除

                        0xD8        块区擦除

                        0xC7        芯片擦除

            

W25Q64页写

                      根据以上指令结合W25Q64手册说明,即可封装相应的函数
                      以下为对W25Q64的页写函数
                      其中写使能和等待写周期完成函数的如何编写可以通过手册查看


/***************************************
*函数名            :w25q64_page_write
*函数功能        :页写功能
*函数参数        :u32 inner_addr        要写入的起始地址
                            u16 len                        写入数据的长度
                            u8 *data                    要写人数据的首地址
*函数返回值    :无
*函数描述        :0x02
****************************************/
void w25q64_page_write(u32 inner_addr,u16 len,u8 *data)
{
    write_enable();                //写使能
    
    //片选拉低
    W25Q64_CS_L;
    
    spi1_byte(0x02);                //发送指令
    
    //发送地址
    spi1_byte((u8)(inner_addr>>16));
    spi1_byte((u8)(inner_addr>>8));
    spi1_byte((u8)(inner_addr));
    
    //循环发送数据
    while(len)
    {
        spi1_byte(*data);
        data++;
        len--;
    }

    //片选拉高
    W25Q64_CS_H;
    
    //等待写入完成
    wait_busy();            //等待BUSY位清零
    
}

       通过页写函数,还可以封装成自由连续写函数
       其算法和前篇《STM32 IIC通信 开漏模式 & AT24C02》 AT24C02连续写函数相同

              

;