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

news2024/9/29 23:26:42

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);
}

②:使能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);
}

③:读取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. */
    }
}

④: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);
}

⑤: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. */
}

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 */
}

可以看到,该函数会配置并使能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;
}

使用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片选 */
    		}
    }
}

可以看到,在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);
    }
}

从本章实验的应用代码中可以看到,在初始化完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中读回的数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1140156.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RPA除了和OCR、NLP技术结合,还能和什么技术结合?

鉴于业内现在也经常把RPA称为数字员工&#xff0c;就虚拟一个人的形象来解答吧。 首先是头部&#xff0c;实现人的“听看说想”能力&#xff1a; 听&#xff1a;ASR&#xff08;语音识别技术&#xff09;&#xff0c;主要用于听取和理解语音输入&#xff0c;让RPA能处理语音数…

电商平台商品API接口知识小结

应用程序接口API&#xff08;Application Programming Interface&#xff09;&#xff0c;是提供特定业务输出能力、连接不同系统的一种约定。这里包括外部系统与提供服务的系统&#xff08;中后台系统&#xff09;或后台不同系统之间的交互点。包括外部接口、内部接口&#xf…

Parity 战略转型引热议,将如何推动波卡生态去中心化?

Polkadot 生态的区块链基础设施公司 Parity Technologies&#xff0c;最近宣布了一项重要的战略调整&#xff0c;即正在寻求在未来几个月内&#xff0c;将部分现有的市场职能转移给 Polkadot 生态系统内的多个去中心化团队&#xff0c;这将影响 Parity Technologies 未来几个月…

人性与理性共赢,真心罐头跃过增长的山海关

在北方不少地方&#xff0c;黄桃罐头是一种抚慰人心的力量。从大连起家&#xff0c;用真材实料打动人心的真心罐头&#xff0c;在朝着国民品牌前进的路上&#xff0c;需要更透彻地洞悉“人性”。 ”人的因素影响太大。我们希望可以告别个人英雄主义&#xff0c;用流程来保证可…

ChinaSoft 论坛巡礼|面向云游戏的云侧软件技术论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

“阿里巴巴API:获取商品详情,掌握市场动态,提升竞争力!“

要使用阿里巴巴接口获得商品详情&#xff0c;需要进行以下步骤&#xff1a; 在开放平台注册账号&#xff0c;申请API调用权限。登录开放平台&#xff0c;进入API管理界面&#xff0c;在接口列表中找到“商品详情”API接口&#xff0c;点击“申请使用”。填写API申请表格&#…

理解V3中的proxy和reflect

现有如下面试题 结合GeexCode和Gpt // 这个函数名为onWatch&#xff0c;接受三个参数obj、setBind和getlogger。 // obj是需要进行监视的对象。 // setBind是一个回调函数&#xff0c;用于在设置属性时进行绑定操作。 // getlogger是一个回调函数&#xff0c;用于在获取属性时…

U盘安装Windows10系统(最新最详细图文教程)

目录 一.简介 二.安装步骤 2.1、用U盘制作PE系统 2.2 安装系统 软件&#xff1a;Windows 10版本&#xff1a;1909语言&#xff1a;简体中文大小&#xff1a;4.95G安装环境&#xff1a;PE系统&#xff0c;7代以上处理器硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xf…

Linux C语言开发(续)

一、二维数组的定义和存储 一、二维数组的定义 1、二维数组的应用&#xff1a;图、方阵 2、数组元素的存放顺序&#xff1a;内存是一维的&#xff0c;二维数组&#xff1a;按行优先 a[1]:表示第一行&#xff0c;也表示a[1][]的所有元素 二、二维数组的初始化、遍历 1、元素的…

吐血整理,服务端性能测试-Docker部署MySQL/Nginx(详细步骤)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 基于Docker部署My…

【CIKM 2023】扩散模型加速采样算法OLSS,大幅提升模型推理速度

近日&#xff0c;阿里云人工智能平台 PAI与华东师范大学陈岑副教授团队合作在深度学习顶级会议 CIKM 2023 上发表 OLSS (Optimal Linear Subspace Search) 算法&#xff0c;这是一种针对扩散模型的采样加速算法。在这篇论文中&#xff0c;扩散模型加速算法的本质被建模成线性子…

【软件安装环境配置】vscode 安装界面没有出现安装路径的选择 的解决,以及vscode的删除的问题

由于vscode 没有删除干净&#xff0c;就会出现vscode 安装的时候&#xff0c;没有出现安装路径的界面&#xff0c;所以可以来到vscode的安装路径&#xff0c;点击 unins000.exe 文件就可以 实现将vscode 相关的文件删除&#xff0c; 如果是删除了整个vscode 安装下的文件&…

Python 算法高级篇:跳跃表和布隆过滤器的应用

Python 算法高级篇&#xff1a;跳跃表和布隆过滤器的应用 引言 1. 跳跃表&#xff08; Skip List &#xff09;1.1 跳跃表的基本结构1.2 跳跃表的操作1.3 Python 中的跳跃表实现 2. 布隆过滤器&#xff08; Bloom Filter &#xff09;2.1 布隆过滤器的基本结构2.2 布隆过滤器的…

看懂分布式追踪系统原理及实践

前言 在微服务架构中,一次请求往往涉及到多个模块,多个中间件,多台机器的相互协作才能完成。这一系列调用请求中,有些是串行的,有些是并行的,那么如何确定这个请求背后调用了哪些应用,哪些模块,哪些节点及调用的先后顺序?如何定位每个模块的性能问题?本文将为你揭晓…

2023年集成电路还缺人吗?集成电路产业人才供需研讨会

10月20日&#xff0c;移知教育创始人团长受邀参与由ARM举办的《集成电路产业人才供需研讨会》&#xff0c;同样受邀参与的还有上海大学、华东理工大学、华东师范大学、上海工程技术大学、上海人社高级职称评审专家等等&#xff0c;高校负责人以及行业专家应邀参加了本次研讨会。…

JVM工具使用(jstack + jstat + jmap)

jstack&#xff1a; jstack是java虚拟机自带的一种堆栈跟踪工具 命令格式&#xff1a; jstack [-l] pid &#xff08;pid 可以使用jps查看&#xff09; 例&#xff1a;jstack 44076 &>$(date %H%M)_44076.jstack.log 线程状态&#xff1a; NEW&#xff0c;未启动的。…

LIO-SAM算法解析

文章目录 简介算法概述1.点云去畸变1.1 主要功能1.2 主要流程 2.特征提取3.IMU预积分4.地图优化5.算法评估 简介 LIO-SAM在lego-loam的基础上新增了对IMU和GPS的紧耦合&#xff0c;采用一个因子图对位姿进行优化&#xff0c;包括IMU因子&#xff0c;激光里程计因子&#xff0c…

企业如何选择设备管理系统?

1、需求为王&#xff0c;列出你的需求清单 每个企业的设备都不尽相同&#xff0c;自然对设备管理系统的需求也不一样。因此&#xff0c;需要充分明确自己的需求和目标&#xff0c;清晰地列出需求清单&#xff0c;然后再逐一对照供应商的产品功能&#xff0c;看是否满足自身各业…

leetCode 76. 最小覆盖子串 + 滑动窗口 + 图解(详细)

76. 最小覆盖子串 - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空字符串 "" 注意&#xff1a; 对于 t 中重复字符&#xff0c;我们寻…

【AICFD案例操作】潜艇阻力AI预测分析

AICFD是由天洑软件自主研发的通用智能热流体仿真软件&#xff0c;用于高效解决能源动力、船舶海洋、电子设备和车辆运载等领域复杂的流动和传热问题。软件涵盖了从建模、仿真到结果处理完整仿真分析流程&#xff0c;帮助工业企业建立设计、仿真和优化相结合的一体化流程&#x…