SPI协议 & Flash

SPI简介:

SPI是一种串行外围设备接口高速全双工同步的通信总线。与I2C总线存在不同之处,如下:

功能说明

SPI总线

IIC总线

通信方式

同步 串行 全双工

同步 串行 半双工

总线接口

MOSIMISOSCLCS

SDASCL

拓扑结构

一主多从/一主一从

多主从

从机选择

片选引脚选择

SDA上设备地址片选

通信速率

一般50MHz以下

100kHz400kHz3.4MHz

数据格式

8/16

8

传输顺序

MSB/LSB

MSB

广泛应用于MCU、FLASH、AD转换器和LCD之间。

SPI的引脚信息:

CS:片选信号,主设备产生;

SCLK:时钟信号,主设备产生;

MISO:主机输入,从机输出;
MOSI:主机输出,从机输入;

SPI的工作原理:

在主机和从机都有一个串行移位寄存器,主机通过向它的 SPI 串行寄存器写入一个字节来发起一次传输。串行移位寄存器通过 MOSI 信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过 MISO 信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外设的写操作和读操作是同步完成的。如果只是进行写操作,主机只需忽略接收到的字节。反之,若主机要读取从机的一个字节,就必须发送一个空字节引发从机传输。

SPI的工作模式:

SPI 通信协议就具备 4 种工作模式,在讲这 4 种工作模式前,首先先知道两个单词 CPOL 和 CPHA。

CPOL,详称 Clock Polarity,就是时钟极性,当主从机没有数据传输的时候即空闲状态,
SCL 线的电平状态,假如空闲状态是高电平, CPOL=1;若空闲状态时低电平,那么 CPOL = 0。
CPHA,详称 Clock Phase,就是时钟相位。在这里先科普一下数据传输的常识: 同步通信时,数据的变化和采样都是在时钟边沿上进行的,每一个时钟周期都会有上升沿和下降沿两个边沿,那么数据的变化和采样就分别安排在两个不同的边沿,由于数据在产生和到它稳定是需要一定的时间,那么假如我们在第 1 个边沿信号把数据输出了,从机只能从第 2 个边沿信号去采样这个数据。CPHA=1 的情况就是表示数据采样是从第 2 个边沿即偶数边沿。

四种工作模式表,常用的是模式0和模式3:

代码:

spi.h

#ifndef _SPI_H
#define _SPI_H
#include "./SYSTEM/sys/sys.h"


#define SPI2_SCK_GPIO_PORT      GPIOB
#define SPI2_SCK_GPIO_Pin       GPIO_PIN_13
#define SPI2_SCK_GPIO_CLK_ENABLE()      do{__HAL_RCC_GPIOB_CLK_ENABLE();}while(0)

#define SPI2_MISO_GPIO_PORT      GPIOB
#define SPI2_MISO_GPIO_Pin       GPIO_PIN_14
#define SPI2_MISO_GPIO_CLK_ENABLE()      do{__HAL_RCC_GPIOB_CLK_ENABLE();}while(0)

#define SPI2_MOSI_GPIO_PORT      GPIOB
#define SPI2_MOSI_GPIO_Pin       GPIO_PIN_15
#define SPI2_MOSI_GPIO_CLK_ENABLE()      do{__HAL_RCC_GPIOB_CLK_ENABLE();}while(0)


#define SPI2_SPI        SPI2
#define SPI2_SPI_CLK_ENABLE()   do{__HAL_RCC_SPI2_CLK_ENABLE();}while(0)

void spi_init();
uint8_t spi2_swapbyte(uint8_t tbyte);

#endif

 

spi.c

#include "./BSP/SPI/spi.h"

SPI_HandleTypeDef g_spi2_handle;

void spi_init()
{
    GPIO_InitTypeDef gpio_init_struct;

    SPI2_SPI_CLK_ENABLE();
    SPI2_SCK_GPIO_CLK_ENABLE();
    SPI2_MISO_GPIO_CLK_ENABLE();
    SPI2_MOSI_GPIO_CLK_ENABLE();

    /*SCK引脚配置*/
    gpio_init_struct.Mode=GPIO_MODE_AF_PP;
    gpio_init_struct.Pin=SPI2_SCK_GPIO_Pin;
    gpio_init_struct.Pull=GPIO_PULLUP;
    gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SPI2_SCK_GPIO_PORT,&gpio_init_struct);

    /*MISO引脚配置*/
    gpio_init_struct.Mode=GPIO_MODE_AF_PP;
    gpio_init_struct.Pin=SPI2_MISO_GPIO_Pin;
    HAL_GPIO_Init(SPI2_MISO_GPIO_PORT,&gpio_init_struct);

    /*MOSI引脚配置*/
    gpio_init_struct.Mode=GPIO_MODE_AF_PP;
    gpio_init_struct.Pin=SPI2_MOSI_GPIO_Pin;
    HAL_GPIO_Init(SPI2_MOSI_GPIO_PORT,&gpio_init_struct);

    /*配置SPI2*/
    g_spi2_handle.Instance=SPI2_SPI;    /*SPI2*/
    g_spi2_handle.Init.Mode=SPI_MODE_MASTER;    /*设置SPI2为主机模式*/
    g_spi2_handle.Init.Direction=SPI_DIRECTION_2LINES;  /*设置SPI2工作方式:全双工*/
    g_spi2_handle.Init.DataSize=SPI_DATASIZE_8BIT;  /*数据格式:8bit*/
    g_spi2_handle.Init.CLKPolarity=SPI_POLARITY_LOW;    /*时钟极性*/
    g_spi2_handle.Init.CLKPhase=SPI_PHASE_1EDGE;    /*时钟相位*/
    g_spi2_handle.Init.NSS=SPI_NSS_SOFT;    /*设置片选方式,软件片选:自定义GPIO*/
    g_spi2_handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256; /*设置时钟波特率分频*/
    g_spi2_handle.Init.FirstBit=SPI_FIRSTBIT_MSB;   /*高位先发*/
    g_spi2_handle.Init.TIMode=SPI_TIMODE_DISABLE;   /*设置帧格式,关闭TI模式*/
    g_spi2_handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;   /*关闭CRC校验*/
    g_spi2_handle.Init.CRCPolynomial=7; /*设置校验多项式*/
    HAL_SPI_Init(&g_spi2_handle);

    __HAL_SPI_ENABLE(&g_spi2_handle); 
}

uint8_t spi2_swapbyte(uint8_t tbyte)
{
    uint8_t rbyte=0;
    HAL_SPI_TransmitReceive(&g_spi2_handle,&tbyte,&rbyte,1,1000);
    return rbyte;
}
flash.h

#ifndef _NoFrash_H
#define _NoFrash_H
#include "./SYSTEM/sys/sys.h"

#define NORflash_GPIO_PORT      GPIOB
#define NORflash_GPIO_Pin       GPIO_PIN_12
#define NORflash_GPIO_CLK_ENABLE()      do{__HAL_RCC_GPIOB_CLK_ENABLE();}while(0)

#define NORflash_CS(x)      do{x?\
                               HAL_GPIO_WritePin(NORflash_GPIO_PORT,NORflash_GPIO_Pin,GPIO_PIN_SET):\
                               HAL_GPIO_WritePin(NORflash_GPIO_PORT,NORflash_GPIO_Pin,GPIO_PIN_RESET);\
                               }while(0)

void norflash_init();
uint8_t norflash_rd_srl();
uint8_t read_data(uint32_t addr);
void norflash_erase_sector(uint32_t addr);
void write_page(uint8_t data, uint32_t addr); 
                              
#endif
flash.c

#include "./BSP/NoFlash/norfrash.h"
#include "./BSP/SPI/spi.h"


void norflash_init()
{
    NORflash_GPIO_CLK_ENABLE();

    GPIO_InitTypeDef gpio_init_struct;
    gpio_init_struct.Mode=GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pin=NORflash_GPIO_Pin;
    gpio_init_struct.Pull=GPIO_PULLUP;
    gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(NORflash_GPIO_PORT,&gpio_init_struct);

    spi_init();
    spi2_swapbyte(0XFF);    /*清除数据寄存器DR*/
    NORflash_CS(1);
}

uint8_t norflash_rd_srl()
{
    uint8_t rec_data=0;

    NORflash_CS(0);
    spi2_swapbyte(0X05);    /*读状态寄存器1*/
    rec_data = spi2_swapbyte(0XFF);
    NORflash_CS(1);
    return rec_data;
}

uint8_t read_data(uint32_t addr)
{
    uint8_t rec_data=0;
    NORflash_CS(0);
    /*发送读命令*/
    spi2_swapbyte(0x03);

    /*发送读地址*/
    spi2_swapbyte(addr>>16);
    spi2_swapbyte(addr>>8);
    spi2_swapbyte(addr);

    /*读取数据*/
    rec_data=spi2_swapbyte(0xff);
    NORflash_CS(1);

    return rec_data;
}


void norflash_erase_sector(uint32_t addr)
{
    /*写使能*/
    NORflash_CS(0);
    spi2_swapbyte(0x06);
    NORflash_CS(1);

    /*等待空闲*/
    while(norflash_rd_srl() & 0x01);

    /*发送扇区擦除指令*/
    NORflash_CS(0);
    spi2_swapbyte(0x20);

    /*发送擦除地址*/
    spi2_swapbyte(addr>>16);
    spi2_swapbyte(addr>>8);
    spi2_swapbyte(addr);
    NORflash_CS(1);

    /*等待空闲*/
    while(norflash_rd_srl() & 0x01);
}


void write_page(uint8_t data,uint32_t addr)
{
    /*擦除扇区*/
    norflash_erase_sector(addr);

    /*写使能*/
    NORflash_CS(0);
    spi2_swapbyte(0x06);
    NORflash_CS(1);

    /*发送页写指令*/
    NORflash_CS(0);
    spi2_swapbyte(0x02);

    spi2_swapbyte(addr>>16);
    spi2_swapbyte(addr>>8);
    spi2_swapbyte(addr);

    /*要写入的数据*/
    spi2_swapbyte(data);
    NORflash_CS(1);

    /*等待空闲(写入完成)*/
    while(norflash_rd_srl() & 0x01);    
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值