1. IIC与SPI对比

  1. IIC 是半双工通讯,无法同时收发信息;SPI 是全双工通讯,可以同时收发信息;
  2. IIC 通讯协议较复杂,而 SPI 通讯协议较简单;
  3. IIC 需要通过地址选择从机,而 SPI 只需一个引脚即可选中从机;
  4. IIC 通讯速率一般为 100kHz 左右,而 SPI 可以达到 50MHz ;
  5. IIC 需要的通讯线较少,而 SPI 需要较多。

2. SPI是什么?

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如 AT91RM9200 。

3. SPI物理架构

在这里插入图片描述

SPI 总线包含 4 条通讯线,分别为 CS、SCK、MOSI、MISO。它们的作用介绍如下 :
(1) MISO – Master Input Slave Output,主设备数据输入,从设备数据输出
(2) MOSI – Master Output Slave Input,主设备数据输出,从设备数据输入
(3) SCK – Serial Clock,时钟信号,由主设备产生
(4) CS – Chip Select,片选信号,由主设备控制
STM32F1 系列芯片有 3 个SPI 接口。

4. SPI工作原理

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5. SPI工作模式

时钟极性(CPOL):

没有数据传输时时钟线的空闲状态电平
0:SCK在空闲状态保持低电平
1:SCK在空闲状态保持高电平

时钟相位(CPHA):

时钟线在第几个时钟边沿采样数据
0:SCK的第一(奇数)边沿进行数据位采样,数据在第一个时钟边沿被锁存
1:SCK的第二(偶数)边沿进行数据位采样,数据在第二个时钟边沿被锁存

在这里插入图片描述

模式 0 和模式 3 最常用。

模式 0 时序图:

在这里插入图片描述

模式 3 时序图:

在这里插入图片描述

6. SPI寄存器及库函数介绍

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

7. 小实验:读写W25Q128实验

在这里插入图片描述

实验目的

读写W25Q128

硬件接线

在这里插入图片描述
在这里插入图片描述

代码

main.c

#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "w25q128.h"

uint8_t data_write[4] = {0xAA, 0xBB, 0xCC, 0xDD};
uint8_t data_read[4] = {0};

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //串口1初始化
    uart1_init(115200);
    //W25Q128初始化
    w25q128_init();
    printf("打印测试:hello world\r\n");
    
    uint16_t device_id = w25q128_read_id();
    printf("device id: %X\r\n", device_id);
    
    //w25q128_erase_sector(0x000000);
    //w25q128_write_page(0x000000, data_write, 4);
    w25q128_read_data(0x000000, data_read, 4);
    printf("data read: %X, %X, %X, %X\r\n",data_read[0],data_read[1],data_read[2],data_read[3]);
    
    while(1)
    {
        led1_on();
        led2_off();
        delay_ms(500);
        led1_off();
        led2_on();
        delay_ms(500);
    }
}

w25q128.c

#include "w25q128.h"

SPI_HandleTypeDef spi_handle = {0};
//SPI初始化
void w25q128_spi_init(void)
{
    spi_handle.Instance = SPI1;                                     //使用SPI1
    spi_handle.Init.Mode = SPI_MODE_MASTER;                         //作为主设备
    spi_handle.Init.Direction = SPI_DIRECTION_2LINES;               //全双工传输
    spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;                   //数据位长度位8bit
    spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;                 //时钟极性为低电平
    spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;                     //奇数边沿采样
    spi_handle.Init.NSS = SPI_NSS_SOFT;                             //软件控制
    spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;  //波特率256分频
    spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;                    //高位先行
    spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
    spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    spi_handle.Init.CRCPolynomial = 7;
    HAL_SPI_Init(&spi_handle);
}

//SPI硬件相关初始化
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    if(hspi->Instance == SPI1){
        GPIO_InitTypeDef gpio_initstruct;
    
        //使能GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();
        //使能SPI1时钟
        __HAL_RCC_SPI1_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_4;               //NSS对应引脚
        gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
        gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        gpio_initstruct.Pin = GPIO_PIN_5 | GPIO_PIN_7;  //CLK、MOSI对应引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;         //复用推挽输出
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        gpio_initstruct.Pin = GPIO_PIN_6;              //MISO对应引脚
        gpio_initstruct.Mode = GPIO_MODE_INPUT;        //输入模式
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    }
}
//SPI字节数据交换(数据收发)
uint8_t w25q128_spi_swap_byte(uint8_t data)
{
    uint8_t recv_data = 0;
    
    HAL_SPI_TransmitReceive(&spi_handle, &data, &recv_data, 1, 1000);   //数据收发
    return recv_data;
}
//W25Q128初始化
void w25q128_init(void)
{
    w25q128_spi_init();
}
//读取设备ID
uint16_t w25q128_read_id(void)
{
    uint16_t device_id = 0;
    //拉低片选
    W25Q128_CS(0);
    //写指令
    w25q128_spi_swap_byte(FLASH_ManufactDeviceID);
    w25q128_spi_swap_byte(0x00);
    w25q128_spi_swap_byte(0x00);
    w25q128_spi_swap_byte(0x00);
    //获取device_id
    device_id = w25q128_spi_swap_byte(FLASH_DummyByte) << 8;
    device_id |= w25q128_spi_swap_byte(FLASH_DummyByte);
    //拉高片选
    W25Q128_CS(1);
    return device_id;
}
//W25Q128写使能
void w25q128_write_enable(void)
{
    //拉低片选
    W25Q128_CS(0);
    //写入指令
    w25q128_spi_swap_byte(FLASH_WriteEnable);
    //拉高片选
    W25Q128_CS(1);
    
}
//W25Q128读取SR1寄存器
uint8_t w25q128_read_sr1(void)
{
    uint8_t recv_data = 0;
    
    //拉低片选
    W25Q128_CS(0);
    //发送指令
    w25q128_spi_swap_byte(FLASH_ReadStatusReg1);
    //读取SR1
    recv_data = w25q128_spi_swap_byte(FLASH_DummyByte);
    //拉高片选
    W25Q128_CS(1);
    
    return recv_data;
}

//发送地址
void w25q128_send_address(uint32_t address)
{
    //从高位到低位依次发送
    w25q128_spi_swap_byte(address >> 16);
    w25q128_spi_swap_byte(address >> 8);
    w25q128_spi_swap_byte(address);
}

//W25Q128读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size)
{
    uint32_t i = 0;
    
     //拉低片选
    W25Q128_CS(0);
    //发送指令
    w25q128_spi_swap_byte(FLASH_ReadData);
    //发送地址
    w25q128_send_address(address);
    //读取SR1
    for(i = 0;i < size;i++){
        data[i] = w25q128_spi_swap_byte(FLASH_DummyByte);
    }
    
    //拉高片选
    W25Q128_CS(1);
}

//忙等待
void w25q128_wait_busy(void)
{
    while((w25q128_read_sr1() & 0x01) == 0x01);
}

//W25Q128页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size)
{
    uint16_t i = 0;
    
    //写使能
    w25q128_write_enable();
    //拉低片选
    W25Q128_CS(0);
    //发送指令
    w25q128_spi_swap_byte(FLASH_PageProgram);
    //发送地址
    w25q128_send_address(address);
    //写入数据
    for(i = 0;i < size;i++){
        w25q128_spi_swap_byte(data[i]);
    }
    //拉高片选
    W25Q128_CS(1);
    //忙等待
    w25q128_wait_busy();
}
//W25Q128擦除扇区
void w25q128_erase_sector(uint32_t address)
{
    //写使能
    w25q128_write_enable();
    //忙等待
    w25q128_wait_busy();
    //拉低片选
    W25Q128_CS(0);
    //发送指令
    w25q128_spi_swap_byte(FLASH_SectorErase);
    //发送地址
    w25q128_send_address(address);
    //拉高片选
    W25Q128_CS(1);
    //忙等待
    w25q128_wait_busy();
}

w25q128.h

#ifndef __W25Q128_H__
#define __W25Q128_H__

#include "sys.h"

#define W25Q128_CS(x)   do{ x ?  \
                                 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) : \
                                 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); \
                        }while(0)
//指令表
#define FLASH_ManufactDeviceID      0x90
#define FLASH_WriteEnable           0x06
#define FLASH_ReadStatusReg1        0x05
#define FLASH_ReadData              0x03
#define FLASH_PageProgram           0x02
#define FLASH_SectorErase           0x20
#define FLASH_DummyByte             0xFF

//W25Q128初始化
void w25q128_init(void);
//读取设备ID
uint16_t w25q128_read_id(void);
//W25Q128读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size);
//忙等待
void w25q128_wait_busy(void);
//W25Q128页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size);
//W25Q128擦除扇区
void w25q128_erase_sector(uint32_t address);

                        
#endif
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐