• 【正点原子STM32连载】 第四十三章 SPI实验 摘自【正点原子】APM32F407最小系统板使用指南


    1)实验平台:正点原子stm32f103战舰开发板V4
    2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
    3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html##

    第四十三章 SPI实验

    本章将介绍使用APM32F407驱动板载的NOR Flash进行读写操作。通过本章的学习,读者将学习到使用SPI驱动NOR Flash的使用。
    本章分为如下几个小节:
    43.1 硬件设计
    43.2 程序设计
    43.3 下载验证

    43.1 硬件设计
    43.1.1 例程功能

    1. 按下KEY_UP和KEY0按键,分别对25Q128进行数据的写入和读取操作,读取到的数据会显示至LCD
    2. 可通过USMART对25Q128进行读ID和芯片/扇区擦除的操作
    3. LED0闪烁,指示程序正在运行
      43.1.2 硬件资源
    4. LED
      LED0 - PF9
    5. 按键
      KEY0 - PE4
      KEY_UP - PA0
    6. USART1(PA9、PA10连接至板载USB转串口芯片上)
    7. 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    8. SPI1
      SPI1_SCK - PB3
      SPI1_MISO - PB4
      SPI1_MOSI - PB5
    9. 25Q128
      F_CS - PB14
      43.1.3 原理图
      本章实验使用了板载的25Q128芯片,该芯片是一个NOR Flash,MCU是通过SPI与该NOR Flash进行连接与通信的,该NOR Flash与MCU的连接原理图,如下如图所示:
      在这里插入图片描述

    图43.1.3.1 NOR Flash与MCU的连接原理图
    43.2 程序设计
    43.2.1 Geehy标准库的SPI驱动
    本章实验通过SPI1驱动NOR Flash,因此需要对SPI1进行相应的配置,并使用SPI1与NOR Flash进行通信,其具体的步骤如下所示:
    ①:配置SPI1
    ②:使能SPI1
    ③:使用SPI1发送数据(接收数据)前,等待SPI1发送缓冲区为空(接收缓冲区非空)
    ④:使用SPI1发送一字节数据
    ⑤:使用SPI1接收一字节数据
    在Geehy标准库中对应的驱动函数如下:
    ①:配置SPI
    该函数用于配置SPI,其函数原型如下所示:
    void SPI_Config(SPI_T* spi, SPI_Config_T* spiConfig);
    该函数的形参描述,如下表所示:
    形参 描述
    spi 指向SPI外设结构体的指针
    例如:SPI1、SPI2等(在apm32f4xx.h文件中有定义)
    spiConfig 指向SPI配置结构体的指针
    需自行定义,并根据SPI的配置参数填充结构体中的成员变量
    表43.2.1.1 函数SPI_Config()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    无 无
    表43.2.1.2 函数SPI_Config()返回值描述
    该函数使用SPI_Config_T类型的结构体变量传入SPI的配置参数,该结构体的定义如下所示:

    typedef enum
    {
        SPI_MODE_SLAVE,								/* 从机模式 */
        SPI_MODE_MASTER								/* 主机模式 */
    } SPI_MODE_T;
    
    typedef enum
    {
        SPI_DATA_LENGTH_8B,							/* 8位数据格式 */
        SPI_DATA_LENGTH_16B							/*16位数据格式 */
    } SPI_DATA_LENGTH_T;
    
    typedef enum
    {
        SPI_CLKPHA_1EDGE,							/* 在第一个时钟边沿进行采样 */
        SPI_CLKPHA_2EDGE								/* 在第二个时钟边沿进行采样 */
    } SPI_CLKPHA_T;
    
    typedef enum
    {
        SPI_CLKPOL_LOW,								/* 时钟信号空闲时为低电平 */
        SPI_CLKPOL_HIGH								/* 时钟信号空闲时为高电平 */
    } SPI_CLKPOL_T;
    
    typedef enum
    {
        SPI_NSS_HARD,								/* 硬件片选 */
        SPI_NSS_SOFT									/* 软件片选 */
    } SPI_NSS_T;
    
    typedef enum
    {
        SPI_FIRSTBIT_MSB,							/* MSB */
        SPI_FIRSTBIT_LSB								/* LSB */
    } SPI_FIRSTBIT_T;
    
    typedef enum
    {
        SPI_DIRECTION_2LINES_FULLDUPLEX	= 0x0000,	/* 双线全双工 */
        SPI_DIRECTION_2LINES_RXONLY		= 0x0100,	/* 双线仅接收 */
        SPI_DIRECTION_1LINE_RX			= 0x0001,	/* 单线仅接受 */
        SPI_DIRECTION_1LINE_TX			= 0x0011	/* 单线仅发送 */
    } SPI_DIRECTION_T;
    
    typedef enum
    {
        SPI_BAUDRATE_DIV_2,							/*波特率2分频 */
        SPI_BAUDRATE_DIV_4,							/*波特率4分频 */
        SPI_BAUDRATE_DIV_8,							/*波特率8分频 */
        SPI_BAUDRATE_DIV_16,						/*波特率16分频 */
        SPI_BAUDRATE_DIV_32,						/*波特率32分频 */
        SPI_BAUDRATE_DIV_64,						/*波特率64分频 */
        SPI_BAUDRATE_DIV_128,						/*波特率128分频 */
        SPI_BAUDRATE_DIV_256,						/*波特率256分频 */
    } SPI_BAUDRATE_DIV_T;
    
    typedef struct
    {
        SPI_MODE_T			mode;			/* 模式 */
        SPI_DATA_LENGTH_T	length;			/* 数据位长度 */
        SPI_CLKPHA_T			phase;			/* 采样阶段 */
        SPI_CLKPOL_T			polarity;		/* 时钟线空闲极性 */
        SPI_NSS_T			nss;			/* 片选信号 */
        SPI_FIRSTBIT_T		firstBit;		/* 数据第一比特 */
        SPI_DIRECTION_T		direction;		/* 方向 */
        SPI_BAUDRATE_DIV_T	baudrateDiv;	/* 波特率分频 */
        uint16_t				crcPolynomial;	/* CRC校验值 */
    } SPI_Config_T;
    该函数的使用示例,如下所示:
    #include "apm32f4xx.h"
    #include "apm32f4xx_spi.h"
    
    void example_fun(void)
    {
        SPI_Config_T spi_init_struct;
    
        /* 配置SPI1 */
        spi_init_struct.mode			= SPI_MODE_MASTER;
        spi_init_struct.length			= SPI_DATA_LENGTH_8B;
        spi_init_struct.phase			= SPI_CLKPHA_2EDGE;
        spi_init_struct.polarity		= SPI_CLKPOL_HIGH;
        spi_init_struct.nss				= SPI_NSS_SOFT;
        spi_init_struct.firstBit		= SPI_FIRSTBIT_MSB;
        spi_init_struct.direction		= SPI_DIRECTION_2LINES_FULLDUPLEX;
        spi_init_struct.baudrateDiv		= SPI_BAUDRATE_DIV_256;
        spi_init_struct.crcPolynomial	= 7;
        SPI_Config(SPI1_SPI, &spi_init_struct);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    ②:使能SPI
    该函数用于使能SPI,其函数原型如下所示:
    void SPI_Enable(SPI_T* spi);
    该函数的形参描述,如下表所示:
    形参 描述
    spi 指向SPI外设结构体的指针
    例如:SPI1、SPI2等(在apm32f4xx.h文件中有定义)
    表43.2.1.3 函数SPI_Enable()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    无 无
    表43.2.1.4 函数SPI_Enable()返回值描述
    该函数的使用示例,如下所示:

    #include "apm32f4xx.h"
    #include "apm32f4xx_spi.h"
    
    void example_fun(void)
    {
        /* 使能SPI1 */
        SPI_Enable(SPI1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ③:读取SPI的状态标志
    该函数用于读取SPI的状态标志,其函数原型如下所示:
    uint8_t SPI_I2S_ReadStatusFlag(SPI_T* spi, SPI_FLAG_T flag);
    该函数的形参描述,如下表所示:
    形参 描述
    spi 指向SPI外设结构体的指针
    例如:SPI1、SPI2等(在apm32f4xx.h文件中有定义)
    flag 指定的SPI状态标志
    例如:SPI_FLAG_RXBNE、SPI_FLAG_TXBE等(在apm32f4xx_spi.h文件中有定义)
    表43.2.1.5 函数SPI_I2S_ReadStatusFlag()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    SET 事件标志发生
    RESET 事件标志未发生
    表43.2.1.6 函数SPI_I2S_ReadStatusFlag()返回值描述
    该函数的使用示例,如下所示:

    #include "apm32f4xx.h"
    #include "apm32f4xx_spi.h"
    
    void example_fun(void)
    {
        uint8_t flag;
    
        /* 读取SPI1的接收缓冲区非空标志 */
        flag = SPI_I2S_ReadStatusFlag(SPI1, SPI_FLAG_RXBNE);
        if (flag == SET)
        {
            /* Do something. */
        }
        else
        {
            /* Do something. */
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    ④:SPI发送数据
    该函数用于使用SPI发送数据,其函数原型如下所示:
    void SPI_I2S_TxData(SPI_T* spi, uint16_t data);
    该函数的形参描述,如下表所示:
    形参 描述
    spi 指向SPI外设结构体的指针
    例如:SPI1、SPI2等(在apm32f4xx.h文件中有定义)
    data 待发送的数据
    表43.2.1.7 函数SPI_I2S_TxData()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    无 无
    表43.2.1.8 函数SPI_I2S_TxData()返回值描述
    该函数的使用示例,如下所示:

    #include "apm32f4xx.h"
    #include "apm32f4xx_spi.h"
    
    void example_fun(void)
    {
        uint8_t data;
        
        data = 0x55;
        
        /* 使用SPI1发送1字节数据 */
        SPI_I2S_TxData(SPI1, (uint16_t)data);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ⑤:SPI接收数据
    该函数用于接收SPI接收的数据,其函数原型如下所示:
    uint16_t SPI_I2S_RxData(SPI_T* spi);
    该函数的形参描述,如下表所示:
    形参 描述
    spi 指向SPI外设结构体的指针
    例如:SPI1、SPI2等(在apm32f4xx.h文件中有定义)
    表43.2.1.9 函数SPI_I2S_RxData()形参描述
    该函数的返回值描述,如下表所示:
    返回值 描述
    uint16_t类型数据 SPI接收的数据
    表43.2.1.10 函数SPI_I2S_TxData()返回值描述
    该函数的使用示例,如下所示:

    #include "apm32f4xx.h"
    #include "apm32f4xx_spi.h"
    
    void example_fun(void)
    {
        uint8_t data;
        
        /* 接收SPI1接收到的1字节数据 */
        data = (uint8_t)SPI_I2S_RxData(SPI1);
        
        /* Do something. */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    43.2.2 SPI驱动
    本章实验的SPI驱动主要负责向NOR Flash驱动提供SPI的各种操作函数,例如:SPI初始化、SPI读写等。本章实验中,SPI的驱动代码包括spi.c和spi.h两个文件。
    SPI驱动中,对SPI、GPIO相关的宏定义,如下所示:

    #define SPI1_SPI					SPI1
    #define SPI1_SPI_CLK_ENABLE()								\
        do {														\
        		RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SPI1);	\
        } while (0)
    
    #define SPI1_SCK_GPIO_PORT			GPIOB
    #define SPI1_SCK_GPIO_PIN			GPIO_PIN_3
    #define SPI1_SCK_GPIO_PIN_SOURCE	GPIO_PIN_SOURCE_3
    #define SPI1_SCK_GPIO_AF			GPIO_AF_SPI1
    #define SPI1_SCK_GPIO_CLK_ENABLE()							\
        do {														\
        		RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);	\
        } while (0)
    
    #define SPI1_MISO_GPIO_PORT			GPIOB
    #define SPI1_MISO_GPIO_PIN			GPIO_PIN_4
    #define SPI1_MISO_GPIO_PIN_SOURCE	GPIO_PIN_SOURCE_4
    #define SPI1_MISO_GPIO_AF			GPIO_AF_SPI1
    #define SPI1_MISO_GPIO_CLK_ENABLE()						\
        do {														\
        		RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);	\
        } while (0)
    
    #define SPI1_MOSI_GPIO_PORT			GPIOB
    #define SPI1_MOSI_GPIO_PIN			GPIO_PIN_5
    #define SPI1_MOSI_GPIO_PIN_SOURCE	GPIO_PIN_SOURCE_5
    #define SPI1_MOSI_GPIO_AF			GPIO_AF_SPI1
    #define SPI1_MOSI_GPIO_CLK_ENABLE()						\
        do {														\
        		RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);	\
        } while (0)
    SPI驱动中,SPI的初始化函数,如下所示:
    /**
     * @brief	初始化SPI1
     * @param	无
     * @retval	无
     */
    void spi1_init(void)
    {
        GPIO_Config_T gpio_init_struct;
        SPI_Config_T spi_init_struct;
        
        /* 使能时钟 */
        SPI1_SPI_CLK_ENABLE();			/* 使能SPI1时钟 */
        SPI1_SCK_GPIO_CLK_ENABLE();		/* 使能SPI1 SCK引脚端口时钟 */
        SPI1_MISO_GPIO_CLK_ENABLE();	/* 使能SPI1 MISO引脚端口时钟 */
        SPI1_MOSI_GPIO_CLK_ENABLE();	/* 使能SPI1 MOSI引脚端口时钟 */
        
        /* 配置SPI1 SCK引脚 */
        GPIO_ConfigPinAF(	SPI1_SCK_GPIO_PORT,			/* 配置SPI1 SCK引脚复用功能 */
        						SPI1_SCK_GPIO_PIN_SOURCE,
        						SPI1_SCK_GPIO_AF);
        gpio_init_struct.pin	= SPI1_SCK_GPIO_PIN;		/* SPI1 SCK引脚 */
        gpio_init_struct.mode	= GPIO_MODE_AF;				/* 复用功能模式 */
        gpio_init_struct.speed	= GPIO_SPEED_100MHz;		/* 高速 */
        gpio_init_struct.otype	= GPIO_OTYPE_PP;			/* 推挽输出 */
        gpio_init_struct.pupd	= GPIO_PUPD_NOPULL;			/* 禁止上拉/下拉 */
        GPIO_Config(SPI1_SCK_GPIO_PORT, &gpio_init_struct);	/* 配置SPI1 SCK引脚 */
        
        /* 配置SPI1 MISO引脚 */
        GPIO_ConfigPinAF(	SPI1_MISO_GPIO_PORT,		/* 配置SPI1 MISO引脚复用功能 */
        						SPI1_MISO_GPIO_PIN_SOURCE,
        						SPI1_MISO_GPIO_AF);
        gpio_init_struct.pin	= SPI1_MISO_GPIO_PIN;			/* SPI1 MISO引脚 */
        gpio_init_struct.mode	= GPIO_MODE_AF;					/* 复用功能模式 */
        gpio_init_struct.speed	= GPIO_SPEED_100MHz;			/* 高速 */
        gpio_init_struct.otype	= GPIO_OTYPE_PP;				/* 推挽输出 */
        gpio_init_struct.pupd	= GPIO_PUPD_NOPULL;				/* 禁止上拉/下拉 */
        GPIO_Config(SPI1_MISO_GPIO_PORT, &gpio_init_struct);	/* 配置SPI1 MISO引脚 */
        
        /* 配置SPI1 MOSI引脚 */
        GPIO_ConfigPinAF(	SPI1_MOSI_GPIO_PORT,		/* 配置SPI1 MOSI引脚复用功能 */
        						SPI1_MOSI_GPIO_PIN_SOURCE,
        						SPI1_MOSI_GPIO_AF);
        gpio_init_struct.pin	= SPI1_MOSI_GPIO_PIN;			/* SPI1 MOSI引脚 */
        gpio_init_struct.mode	= GPIO_MODE_AF;					/* 复用功能模式 */
        gpio_init_struct.speed	= GPIO_SPEED_100MHz;			/* 高速 */
        gpio_init_struct.otype	= GPIO_OTYPE_PP;				/* 推挽输出 */
        gpio_init_struct.pupd	= GPIO_PUPD_NOPULL;				/* 禁止上拉/下拉 */
        GPIO_Config(SPI1_MOSI_GPIO_PORT, &gpio_init_struct);	/* 配置SPI1 MOSI引脚 */
        
        /* 配置SPI1 */
        spi_init_struct.mode			= SPI_MODE_MASTER;
        spi_init_struct.length			= SPI_DATA_LENGTH_8B;
        spi_init_struct.phase			= SPI_CLKPHA_2EDGE;
        spi_init_struct.polarity		= SPI_CLKPOL_HIGH;
        spi_init_struct.nss				= SPI_NSS_SOFT;
        spi_init_struct.firstBit		= SPI_FIRSTBIT_MSB;
        spi_init_struct.direction		= SPI_DIRECTION_2LINES_FULLDUPLEX;
        spi_init_struct.baudrateDiv		= SPI_BAUDRATE_DIV_256;
        spi_init_struct.crcPolynomial	= 7;
        SPI_Config(SPI1_SPI, &spi_init_struct);					/* 配置SPI1 */
        SPI_Enable(SPI1_SPI);									/* 使能SPI1 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    可以看到,该函数会配置并使能SPI1,同时也配置SPI使用的GPIO引脚的复用功能。
    SPI驱动中,使用SPI传输1字节数据的函数,如下所示:

    /**
     * @brief	SPI1读写一个字节数据
     * @param	txdata: 待发送的一字节数据
     * @retval	接收到的一字节数据
     */
    uint8_t spi1_read_write_byte(uint8_t txdata)
    {
        uint8_t rxdata;
        
        /* 等待发送缓冲器为空 */
        while (SPI_I2S_ReadStatusFlag(SPI1_SPI, SPI_FLAG_TXBE) != SET);
        /* 发送一字节数据 */
        SPI_I2S_TxData(SPI1_SPI, txdata);
        
        /* 等待接收缓冲非空 */
        while (SPI_I2S_ReadStatusFlag(SPI1_SPI, SPI_FLAG_RXBNE) != SET);
        /* 接收一字节数据 */
        
        rxdata = SPI_I2S_RxData(SPI1_SPI);
        return rxdata;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    使用SPI传输1字节数据就是先后发送并接收1字节数据。
    43.2.3 NOR Flash驱动
    本章实验的NOR Flash驱动主要负责向应用层提供NOR Flash的初始化和读写操作等函数。本章实验中,NOR Flash的驱动代码包括norflash.c和norflash.h两个文件。
    NOR Flash驱动中,对GPIO的相关宏定义,如下所示:

    #define NORFLASH_CS_GPIO_PORT	GPIOB
    #define NORFLASH_CS_GPIO_PIN	GPIO_PIN_14
    #define NORFLASH_CS_GPIO_CLK_ENABLE()						\
        do {														\
        		RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);	\
        } while (0)
    
    #define NORFLASH_CS(x) do { x ?											\
        		GPIO_SetBit(NORFLASH_CS_GPIO_PORT, NORFLASH_CS_GPIO_PIN) :		\
        		GPIO_ResetBit(NORFLASH_CS_GPIO_PORT, NORFLASH_CS_GPIO_PIN);	\
        } while (0)
    NOR Flash驱动中,初始化NOR Flash的函数,如下所示:
    /**
     * @brief	初始化NOR Flash
     * @param	无
     * @retval	无
     */
    void norflash_init(void)
    {
        GPIO_Config_T gpio_init_struct;
        uint8_t temp;
        
        /* 使能时钟 */
        NORFLASH_CS_GPIO_CLK_ENABLE();				/* 使能NOR Flash片选引脚端口时钟 */
        
        /* 配置NOR Flash片选引脚 */
        gpio_init_struct.pin	= NORFLASH_CS_GPIO_PIN;	/* NOR Flash片选引脚 */
        gpio_init_struct.mode	= GPIO_MODE_OUT;		/* 通用输出模式 */
        gpio_init_struct.speed	= GPIO_SPEED_100MHz;	/* 高速 */
        gpio_init_struct.otype	= GPIO_OTYPE_PP;		/* 推挽输出 */
        gpio_init_struct.pupd	= GPIO_PUPD_UP;			/* 上拉 */
        GPIO_Config(NORFLASH_CS_GPIO_PORT, &gpio_init_struct);
        NORFLASH_CS(1);									/* 失能NOR Flash片选 */
        
        /* 配置SPI1接口 */
        spi1_init();							/* 初始化SPI1 */
        spi1_set_speed(SPI_SPEED_4);		/* 设置SPI1速度,84MHz/4=21MHz */
        
        g_norflash_type = norflash_read_id();/* 读取NOR Flash芯片ID */
        if (g_norflash_type == W25Q256)		/* W25Q256需使能4字节地址模式 */
        {
        		temp = norflash_read_sr(3);		/* 读状态寄存器3,判断地址模式 */
        		if ((temp & 0x01) == 0)			/* 如果不是4字节地址模式,则需进行相应配置 */
        		{
        			norflash_write_enable();	/* NOR Flash写使能 */
        			temp |= (1 << 1);			/* ADP=1,配置上电4字节地址模式 */
        			norflash_write_sr(3, temp);	/* 写状态寄存器3 */
        			
        			NORFLASH_CS(0);				/* 使能NOR Flash片选 */
        			spi1_read_write_byte(NORFLASH_Enable4ByteAddr);/* 使能4字节地址模式 */
        			NORFLASH_CS(1);				/* 失能NOR Flash片选 */
        		}
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    可以看到,在NOR Flash的初始化函数中,先初始化了控制NOR Flash片选的GPIO引脚,然后再是初始化与NOR Flash通讯的SPI并配置其通讯波特率,最后还会根据不同容量的NOR Flash做相应的配置操作。
    NOR Flash驱动中其他对NOR Flash的操作函数,例如,NOR Flash的读写函数、擦除函数等,请读者结合25Q128 NOR Flash芯片的数据手册查看本实验的配套实验源码。
    43.2.4 实验应用代码
    本章实验的应用代码,如下所示:

    /* 待写入NOR Flash的数据 */
    static const uint8_t g_text_buf[] = {"APM32 SPI TEST"};
    
    /* 待写入NOR Flash数据的长度 */
    #define TEXT_SIZE sizeof(g_text_buf)
    
    int main(void)
    {
        uint16_t id;
        uint8_t t = 0;
        uint8_t key;
        uint8_t data[TEXT_SIZE];
        uint32_t flashsize;
        
        NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3);	/* 设置中断优先级分组为组3 */
        sys_apm32_clock_init(336, 8, 2, 7);					/* 配置系统时钟 */
        delay_init(168);										/* 初始化延时功能 */
        usart_init(115200);									/* 初始化串口 */
        usmart_dev.init(84);								/* 初始化USMART */
        led_init();											/* 初始化LED */
        lcd_init();											/* 初始化LCD */
        norflash_init();										/* 初始化NOR Flash */
        
        lcd_show_string(30, 50, 200, 16, 16, "APM32", RED);
        lcd_show_string(30, 70, 200, 16, 16, "SPI TEST", RED);
        lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
        lcd_show_string(30, 110, 200, 16, 16, "KEY_UP:Write  KEY0:Read", RED);
        
        id = norflash_read_id();							/* 读NOR Flash芯片ID */
        while ((id == 0) || (id == 0xFFFF))					/* 检测不到NOR Flash芯片 */
        {
        lcd_show_string(30, 130, 200, 16, 16, "NOR Flash Check Failed!", RED);
        delay_ms(500);
        lcd_show_string(30, 130, 200, 16, 16, "Please Check!          ", RED);
        delay_ms(500);
        LED0_TOGGLE();
        }
        /* NOR Flash检测正常 */
        lcd_show_string(30, 130, 200, 16, 16, "NOR FLASH Ready!", BLUE);
        flashsize = 16 * 1024 * 1204;						/* NOR Flash容量为16MB */
        
        while (1)
        {
        		t++;
        		key = key_scan(0);
        		
        		if (key == WKUP_PRES)		/* 写入数据 */
        		{
        			lcd_fill(0, 150, 239, 319, WHITE);
        			lcd_show_string(30, 150, 200, 16, 16, "Start Write Flash....", BLUE);
        			sprintf((char *)data, "%s%d", (char *)g_text_buf, t);
        			/* 从倒数第100个地址处开始写入TEXT_SIZE个字节的数据 */
        			norflash_write((uint8_t *)data, flashsize - 100, TEXT_SIZE);
        			lcd_show_string(30, 150, 200, 16, 16, "Flash Write Finished!", BLUE);
        		}
        		else if (key == KEY0_PRES)	/* 读取数据 */
        		{
        			lcd_show_string(30, 150, 200, 16, 16, "Start Read FLASH... . ",BLUE);
        			/* 从倒数第100个地址处开始读出TEXT_SIZE个字节的数据 */
        			norflash_read(data, flashsize - 100, TEXT_SIZE);
        			lcd_show_string(30, 150, 200, 16, 16, "The Data Readed Is:   ",BLUE);
        			lcd_show_string(30, 170, 200, 16, 16, (char *)data, BLUE);
        		}
        		
        		if (t == 20)
        		{
        			LED0_TOGGLE();
        			t = 0;
        		}
        		
        		delay_ms(10);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    从本章实验的应用代码中可以看到,在初始化完NOR Flash后,会检测与NOR Flash的连接是否正常,若与NOR Flash的连接正常,则会不断地等待按键输入,若检测到KEY_UP按键被按下,则会往NOR Flash的指定地址中写入指定的数据,若检测KEY0按键被按下,则会从NOR Flash的指定地址中读取数据,并在LCD上进行显示。
    43.3 下载验证
    在完成编译和烧录操作后,若MCU与NOR Flash的连接无误,则可以在LCD上看到“NOR Flash Ready!”的提示信息,此时可以按下KEY_UP按键往NOR Flash的指定地址写入指定的数据,然后再按下KEY_0按键从NOR Flash的指定地址将写入的数据读回来在LCD上进行显示,此时便可以看到LCD上显示“APM32 SPI TESTn”的提示信息,该提示信息就是从NOR Flash中读回的数据。

  • 相关阅读:
    综合电商商城小程序的作用是什么
    RDD的转换Value类型map(func)案例_大数据培训
    ADC驱动怎么应用以及中断顶半部和底半部
    Linux Docker 安装 Elasticsearch Logstash Kibana
    计算机毕业设计Java线上书城系统(源码+系统+mysql数据库+Lw文档)
    基于普鲁克分析对两组相机/三维点(已知对应关系)进行相似变换对齐的方法及python代码
    python获取loki日志
    注意:Spring Boot 2.7开始spring.factories不推荐使用了,接下来这么玩...
    Linux-Cgroup V2 初体验
    Elasticsearch:如何为 CCR 及 CCS 建立带有安全的集群之间的互信
  • 原文地址:https://blog.csdn.net/weixin_55796564/article/details/134075115