STM32F1的SPI实验

SPI

SPI:串行外设设备接口(Serial Peripheral Interface),是一种高速的,全双工,同步的通信总线。[这里主机和从即都有SPI外设吗]
在这里插入图片描述
SPI与IIC对比

SPI结构框图介绍

在这里插入图片描述
① SPI相关引脚:MOSI(输出数据线),MISO(输入数据线),SCK(时钟),NSS(片选)。
② 数据发送和接收:与缓冲区、移位寄存器以及引脚相关
③ 时钟信号:SPI时钟信号是通过SPI_CR1寄存器配置
④ 主控制逻辑:涉及两个控制寄存器SPI_CR1/2用于配置SPI工作,SPI_SR用于查看工作状态。

STM32有三个SPI外设

在这里插入图片描述

主机模式下的数据发送与接收

在这里插入图片描述

SPI工作模式介绍

时钟极性(CPOL):没有数据传输时时钟线的空闲状态电平
时钟相位(CPHA):时钟线在第几个时钟边沿采样数据

在这里插入图片描述

SPI寄存器

在这里插入图片描述

SPI_CR1寄存器(SPI控制寄存器1)

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

SPI_SR寄存器(SPI状态寄存器)

在这里插入图片描述

SPI数据寄存器(SPI_DR)

在这里插入图片描述

SPI相关HAL库驱动介绍(掌握) F1 / F4 / F7 / H7

在这里插入图片描述

初始化过程遇到的结构体

SPI_HandleTypeDef
{
   SPI_TypeDef		*Instance;//SPI外设寄存器结构体
   SPI_InitTypeDef 		Init;
   ....
}

SPI_InitTypeDef
{
   uint32_t Mode				/* SPI模式(主机)  */
	uint32_t Direction			/* 工作方式(全双工) */
	uint32_t DataSize			/* 帧格式(8位) */
	uint32_t CLKPolarity			/* 时钟极性(CPOL = 0) */
	uint32_t CLKPhase			/* 时钟相位 (CPHA = 0)*/
	uint32_t NSS				/* SS控制方式(软件) */
	uint32_t BaudRatePrescaler		/* SPI波特率预分频值 */
	uint32_t FirstBit				/* 数据传输顺序(MSB)*/
	uint32_t TIMode				/* 帧格式:Motorola / TI  */
	uint32_t CRCCalculation		/* 设置硬件CRC校验 */
	uint32_t CRCPolynomial		/*  设置CRC校验多项式 */
	.....
	//(对于F7/H7来说,还有一些附加功能相关成员(NSS/CRC/IOSwap))
}

SPI分类

  1. Standard SPI(标准SPI)全双工方式
    标准SPI,通信线包含:片选CS、时钟线CLK、输入DI、输出DO。驱动SPI FLASH还需要写保护WR以及维持HOLD引脚。
  2. Dual SPI(双线SPI) 半双工方式
    对标准SPI改进,DO和DI改成IO0和IO1,变为双向IO口。一个周期内,通过数据线,传输2位数据。
  3. Qual SPI(四线SPI) 半双工工作方式
    对Dual SPI改进,写保护WR和维持HOLD复用为数据IO口,为IO2和IO3。个周期内,通过数据线,传输4位数据。
    2,3两种方式主要是操作SPI通信协议的flash,速度至上。

NOR FLASH介绍

FLASH是常用的用于储存数据的半导体器件,它具有容量大,可重复擦写、按“扇区/块”擦除、掉电后数据可继续保存的特性。FLASH是有一个物理特性:只能写0,不能写1,写1靠擦除

FLASH主要有NOR Flash和NAND Flash两种类型,NOR和NAND是两种数字门电路。

在这里插入图片描述

NM25Q128介绍

NM25Q128,串行闪存器件,属于NOR FLASH中的一种,容量为128 Mb。擦写周期可达10W次,可以将数据保存达20年之久。使用SPI数据传输时序。
NM25Q128芯片接口示意图

在这里插入图片描述

CS : 片选信号输入
SI : 数据输入
SO : 数据输出 
CLK : 时钟输入
HOLD : 暂停通讯 
WP : 写保护功能

NM25Q128存储结构

在这里插入图片描述

NM25Q128常用指令:需要以命令的方式操作此存储器。

在这里插入图片描述

写使能 Write Enable (06H)

执行 Page Program页写,Sector Erase扇区擦除,Block Erase块擦除,Chip Erase片擦除,
Write Status Register写状态寄存器等指令前,需要写使能
在这里插入图片描述
在这里插入图片描述

读状态寄存器Read Status Reg1(05H)

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

读时序 Read Data Bytes(03H)

在这里插入图片描述
在这里插入图片描述
读操作步骤
在这里插入图片描述

页写时序 PageProgram (02H)

页写命令最多可以向FLASH传输256个字节的数据
在这里插入图片描述
在这里插入图片描述
写操作步骤
在这里插入图片描述

扇区擦除时序 Sector Erase(20H)

写入数据前,检查内存空间情况是否满足,不满足需擦除
FLASH存储器的特性决定了它只能把原来为“1”的数据位改写成“0”,而原来为“0”的数据位不能直接改写为“1”。要改为1,只能使用擦除。
在这里插入图片描述
在这里插入图片描述
擦除步骤
在这里插入图片描述

状态寄存器

SR寄存器:跟踪芯片的状态
在这里插入图片描述
BUSY位 指示当前状态
0:空闲状态(硬件自动清除) 1:当前处于忙碌状态
WEL位 执行WriteEnable指令该位为1,可以页写/扇区or块or片擦除/写状态寄存器
0:写禁止,不能页编程/扇区or块or片擦除/写状态寄存器

NOR FLASH基本驱动步骤

在这里还是没有明白SPI外设与NOR FLASH类型的NM25Q128之间的关系。???还需要了解。
在这里插入图片描述
在这里插入图片描述

实验源码(驱动NM25Q128实现读和写1字节数据)

spi.h

#ifndef __SPI_H
#define __SPI_H
#include "./SYSTEM/sys/sys.h"
/* SPI2 引脚定义 */

//时钟线
#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)  

/* SPI2相关定义 */
#define SPI2_SPI                        SPI2
#define SPI2_SPI_CLK_ENABLE()           do{ __HAL_RCC_SPI2_CLK_ENABLE(); }while(0)    /* SPI2时钟使能 */

void spi2_init(void);
uint8_t spi2_read_write_byte(uint8_t data);
#endif

spi.c

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

SPI_HandleTypeDef g_spi2_handler; /* SPI2¾ä±ú */

void spi2_init(void)
{
    g_spi2_handler.Instance                 = SPI2_SPI;                     /* SPI2 */
    g_spi2_handler.Init.Mode                = SPI_MODE_MASTER;              /* ÉèÖÃSPIģʽ£¨Ö÷»úģʽ£© */
    g_spi2_handler.Init.Direction           = SPI_DIRECTION_2LINES;         /* ÉèÖÃSPI¹¤×÷·½Ê½£¨È«Ë«¹¤£© */
    g_spi2_handler.Init.DataSize            = SPI_DATASIZE_8BIT;            /* ÉèÖÃÊý¾Ý¸ñʽ£¨8bit³¤¶È£© */
    g_spi2_handler.Init.CLKPolarity         = SPI_POLARITY_HIGH;            /* ÉèÖÃʱÖÓ¼«ÐÔ£¨CPOL = 1£© */
    g_spi2_handler.Init.CLKPhase            = SPI_PHASE_2EDGE;              /* ÉèÖÃʱÖÓÏà루CPHA = 1£© */
    g_spi2_handler.Init.NSS                 = SPI_NSS_SOFT;                 /* ÉèÖÃƬѡ·½Ê½£¨Èí¼þƬѡ£¬×Ô¶¨ÒåGPIO£© */
    g_spi2_handler.Init.BaudRatePrescaler   = SPI_BAUDRATEPRESCALER_256;    /* ÉèÖÃSPIʱÖÓ²¨ÌØÂÊ·ÖƵ£¨256·ÖƵ£© */
    g_spi2_handler.Init.FirstBit            = SPI_FIRSTBIT_MSB;             /* ÉèÖôóС¶Ëģʽ£¨MSB¸ßλÔÚÇ°£© */
    g_spi2_handler.Init.TIMode              = SPI_TIMODE_DISABLE;           /* ÉèÖÃÖ¡¸ñʽ£¨¹Ø±ÕTIģʽ£© */
    g_spi2_handler.Init.CRCCalculation      = SPI_CRCCALCULATION_DISABLE;   /* ÉèÖÃCRCУÑ飨¹Ø±ÕCRCУÑ飩 */
    g_spi2_handler.Init.CRCPolynomial       = 7;                            /* ÉèÖÃCRCУÑé¶àÏîʽ£¨·¶Î§£º1~65535£© */

    HAL_SPI_Init(&g_spi2_handler);
}

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    SPI2_SPI_CLK_ENABLE();
    
    GPIO_InitTypeDef gpio_init_struct;

    if (hspi->Instance == SPI2_SPI)
    {
        SPI2_SCK_GPIO_CLK_ENABLE();     /* SPI2_SCK*/
        SPI2_MISO_GPIO_CLK_ENABLE();    /* SPI2_MISO */
        SPI2_MOSI_GPIO_CLK_ENABLE();    /* SPI2_MOSI */

        /* SCK引脚模式设置复用输出() */
        gpio_init_struct.Pin = SPI2_SCK_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        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.Pin = SPI2_MISO_GPIO_PIN;
        HAL_GPIO_Init(SPI2_MISO_GPIO_PORT, &gpio_init_struct);

        /* MOSI...复用输出 */
        gpio_init_struct.Pin = SPI2_MOSI_GPIO_PIN;
        HAL_GPIO_Init(SPI2_MOSI_GPIO_PORT, &gpio_init_struct);
    }
}


/*这个函数没有太明白 */
uint8_t spi2_read_write_byte(uint8_t data)
{
    uint8_t rec_data = 0;
    //1000为超时
    HAL_SPI_TransmitReceive(&g_spi2_handler, &data, &rec_data, 1, 1000);
    
    return rec_data;
}

norflash.h

#include "./SYSTEM/sys/sys.h"

/******************************************************************************************/
/* NORFLASH 片选引脚定义 */
#define NORFLASH_CS_GPIO_PORT           GPIOB
#define NORFLASH_CS_GPIO_PIN            GPIO_PIN_12
#define NORFLASH_CS_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB¿ÚʱÖÓʹÄÜ */

/******************************************************************************************/
/* NORFLASH 片选信号 */
#define NORFLASH_CS(x)      do{ x ? \
                                  HAL_GPIO_WritePin(NORFLASH_CS_GPIO_PORT, NORFLASH_CS_GPIO_PIN, GPIO_PIN_SET) : \
                                  HAL_GPIO_WritePin(NORFLASH_CS_GPIO_PORT, NORFLASH_CS_GPIO_PIN, GPIO_PIN_RESET); \
                            }while(0)
void norflash_init(void);
uint8_t norflash_rd_sr1(void);
uint8_t norflash_read_data(uint32_t addr);
void norflash_erase_sector(uint32_t addr);
void norflash_write_page(uint8_t data, uint32_t addr);

norflash.c

void norflash_init(void)
{
    NORFLASH_CS_GPIO_CLK_ENABLE();      /* NORFLASH CS½Å ʱÖÓʹÄÜ */

    GPIO_InitTypeDef gpio_init_struct;
    gpio_init_struct.Pin = NORFLASH_CS_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(NORFLASH_CS_GPIO_PORT, &gpio_init_struct);    /* SCKÒý½ÅģʽÉèÖÃ(¸´ÓÃÊä³ö) */

    spi2_init();
    spi2_read_write_byte(0xFF); /* 清除DR的作用 */
    
    NORFLASH_CS(1);
}

uint8_t norflash_rd_sr1(void)
{
    uint8_t rec_data = 0;
    
    NORFLASH_CS(0);
    spi2_read_write_byte(0x05);     /* ¶Á״̬¼Ä´æÆ÷1 */
    rec_data = spi2_read_write_byte(0xFF);
    NORFLASH_CS(1);
    
    return rec_data;
}

uint8_t norflash_read_data(uint32_t addr)
{
    uint8_t rec_data = 0;
    
    NORFLASH_CS(0);
    
    /* 1 ·¢ËͶÁÃüÁî */
    spi2_read_write_byte(0x03);
    
    /* 2 ·¢Ë͵ØÖ· */
    spi2_read_write_byte(addr >> 16);
    spi2_read_write_byte(addr >> 8);
    spi2_read_write_byte(addr);
       
    /* 3 ¶ÁÈ¡Êý¾Ý */
    rec_data = spi2_read_write_byte(0xFF);
    
    NORFLASH_CS(1);
    return rec_data;
}

void norflash_erase_sector(uint32_t addr)
{
    /* 1 дʹÄÜ */
    NORFLASH_CS(0);
    spi2_read_write_byte(0x06);
    NORFLASH_CS(1);
    
    /* 2 µÈ´ý¿ÕÏÐ */
    while(norflash_rd_sr1()&0x01);
    
    /* 3 ·¢ËÍÉÈÇø²Á³ýÖ¸Áî */
    NORFLASH_CS(0);
    spi2_read_write_byte(0x20);
    
    /* 4 ·¢Ë͵ØÖ· */
    spi2_read_write_byte(addr >> 16);
    spi2_read_write_byte(addr >> 8);
    spi2_read_write_byte(addr);
    NORFLASH_CS(1);
    
    /* 5 µÈ´ý¿ÕÏÐ */
    while(norflash_rd_sr1()&0x01);
}

void norflash_write_page(uint8_t data, uint32_t addr)
{
    /* 1 ²Á³ýÉÈÇø */
    norflash_erase_sector(addr);
    
    /* 2 дʹÄÜ */
    NORFLASH_CS(0);
    spi2_read_write_byte(0x06);
    NORFLASH_CS(1);
    
    /* 3 ·¢ËÍҳдָÁî */
    NORFLASH_CS(0);
    spi2_read_write_byte(0x02);
    
    /* 4 ·¢Ë͵ØÖ· */
    spi2_read_write_byte(addr >> 16);
    spi2_read_write_byte(addr >> 8);
    spi2_read_write_byte(addr);
    
    /* 5 ҪдÈëµÄÊý¾Ý */
    spi2_read_write_byte(data);
    NORFLASH_CS(1);
    
    /* 6 µÈ´ýдÈëÍê³É(µÈ´ý¿ÕÏÐ) */
    while(norflash_rd_sr1()&0x01);
}

main.c

 uint8_t key;
    uint16_t i = 0;
    uint8_t rec_data = 0;
    
    HAL_Init();                         /* ³õʼ»¯HAL¿â */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* ÉèÖÃʱÖÓ, 72Mhz */
    delay_init(72);                     /* ÑÓʱ³õʼ»¯ */
    usart_init(115200);                 /* ´®¿Ú³õʼ»¯Îª115200 */
    usmart_dev.init(72);                /* ³õʼ»¯USMART */
    led_init();                         /* ³õʼ»¯LED */
    lcd_init();                         /* ³õʼ»¯LCD */
    key_init();                         /* ³õʼ»¯°´¼ü */
    norflash_init();
    
    while (1)
    {
        key = key_scan(0);

        if (key == KEY1_PRES) /* KEY1°´ÏÂ,дÈë */
        {
            norflash_write_page('5', 0x123457); /* µØÖ··¶Î§0~0xFFFFFF */
            printf("write finish \r\n");
        }

        if (key == KEY0_PRES) /* KEY0°´ÏÂ,¶ÁÈ¡Êý¾Ý */
        {
            rec_data = norflash_read_data(0x123456);
            printf("read data : %c \r\n", rec_data);
        }

        i++;

        if (i == 20)
        {
            LED0_TOGGLE(); /* LED0ÉÁ˸ */
            i = 0;
        }

        delay_ms(10);
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值