SPI:串行外设设备接口,是一种高速的、全双工的、同步的通信总线。
其中,NORFLASH_CS(0)将PB14(CS引脚)写低,NORFLASH_CS(1)将PB14(CS引脚)写高。
#define NORFLASH_CS(x) do{ x ? \
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET); \
}while(0)
/* 写使能 */
NORFLASH_CS(0);
spi1_read_write_byte(0x06);
NORFLASH_CS(1);
/* 读状态寄存器1 */
uint8_t norflash_rd_sr1(void)
{
uint8_t rec_data = 0;
NORFLASH_CS(0);
spi1_read_write_byte(0x05); /* 读状态寄存器1 */
rec_data = spi1_read_write_byte(0xFF);
NORFLASH_CS(1);
return rec_data;
}
/* 读操作 */
uint8_t norflash_read_data(uint32_t addr)
{
uint8_t rec_data;
NORFLASH_CS(0);
/* 1 发送读指令 */
spi1_read_write_byte(0x03);
/* 2 发送地址 */
spi1_read_write_byte(addr>>16);
spi1_read_write_byte(addr>>8);
spi1_read_write_byte(addr);
/* 3 读取数据 */
rec_data = spi1_read_write_byte(0xFF);
NORFLASH_CS(1);
return rec_data;
}
/* 擦除操作,3-5对应上边擦除步骤,注意擦除前需要写使能 */
void norflash_erase_sector(uint32_t addr)
{
/* 1 写使能 */
NORFLASH_CS(0);
spi1_read_write_byte(0x06);
NORFLASH_CS(1);
/* 2 等待空闲 */
while(norflash_rd_sr1()&0x01); /* 接收寄存器是否空闲 */
/* 3 发送扇区擦除指令 */
NORFLASH_CS(0);
spi1_read_write_byte(0x20);
/* 4 发送地址 */
spi1_read_write_byte(addr>>16);
spi1_read_write_byte(addr>>8);
spi1_read_write_byte(addr);
NORFLASH_CS(1);
/* 5 等待空闲 */
while(norflash_rd_sr1()&0x01); /* 接收寄存器是否空闲 */
}
/* 写操作步骤,其中3-5对应上边页写时序,注意最开始需要先擦拭扇区、写使能。 */
void norflash_write_page1(uint8_t data, uint32_t addr)
{
/* 1 擦除扇区 */
norflash_erase_sector(addr);
/* 2 写使能 */
NORFLASH_CS(0);
spi1_read_write_byte(0x06);
NORFLASH_CS(1);
/* 3 发送页写指令 */
NORFLASH_CS(0);
spi1_read_write_byte(0x02);
/* 4 发送地址 */
spi1_read_write_byte(addr>>16);
spi1_read_write_byte(addr>>8);
spi1_read_write_byte(addr);
/* 5 要写入的数据 */
spi1_read_write_byte(data);
NORFLASH_CS(1);
/* 6 等待写入完成 */
while(norflash_rd_sr1()&0x01); /* 接收寄存器是否空闲 */
}
#include "./BSP/SPI/spi.h"
SPI_HandleTypeDef g_spi1_handler; /* SPI1句柄 */
/**
* @brief SPI初始化代码
* @note 主机模式,8位数据,禁止硬件片选
* @param 无
* @retval 无
*/
void spi1_init(void)
{
/* SPI1初始化 */
g_spi1_handler.Instance = SPI1; /* SPI1 */
g_spi1_handler.Init.Mode = SPI_MODE_MASTER; /* 设置SPI模式(主机模式) */
g_spi1_handler.Init.Direction = SPI_DIRECTION_2LINES; /* 设置SPI工作方式(全双工) */
g_spi1_handler.Init.DataSize = SPI_DATASIZE_8BIT; /* 设置数据格式(8bit长度) */
g_spi1_handler.Init.CLKPolarity = SPI_POLARITY_HIGH; /* 设置时钟极性(CPOL = 1) */
g_spi1_handler.Init.CLKPhase = SPI_PHASE_2EDGE; /* 设置时钟相位(CPHA = 1) */
g_spi1_handler.Init.NSS = SPI_NSS_SOFT; /* 设置片选方式(软件片选,自定义GPIO) */
g_spi1_handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; /* 设置SPI时钟波特率分频(256分频) */
g_spi1_handler.Init.FirstBit = SPI_FIRSTBIT_MSB; /* 设置大小端模式(MSB高位在前) */
g_spi1_handler.Init.TIMode = SPI_TIMODE_DISABLE; /* 设置帧格式(关闭TI模式) */
g_spi1_handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 设置CRC校验(关闭CRC校验) */
g_spi1_handler.Init.CRCPolynomial = 7; /* 设置CRC校验多项式(范围:1~65535) */
HAL_SPI_Init(&g_spi1_handler);
__HAL_SPI_ENABLE(&g_spi1_handler); /* 使能SPI1 */
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
/* 时钟使能 */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* 引脚初始化 */
GPIO_InitTypeDef gpio_init_struct;
/* PB3复用输出 */
gpio_init_struct.Pin = GPIO_PIN_3;
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
gpio_init_struct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOB, &gpio_init_struct);
/* PB4复用输出 */
gpio_init_struct.Pin = GPIO_PIN_4;
HAL_GPIO_Init(GPIOB, &gpio_init_struct);
/*PB5复用输出 */
gpio_init_struct.Pin = GPIO_PIN_5;
HAL_GPIO_Init(GPIOB, &gpio_init_struct);
}
/* 发送和接收数据 */
/* data为要写入的字节 */
/* rec_data为读到的字节 */
uint8_t spi1_read_write_byte(uint8_t data)
{
uint8_t rec_data = 0;
HAL_SPI_TransmitReceive(&g_spi1_handler, &data, &rec_data, 1, 1000);
return rec_data;
}
#include "./BSP/SPI/spi.h"
#include "./BSP/NORFLASH/norflash.h"
/**
* @brief 初始化SPI NOR FLASH
* @param 无
* @retval 无
*/
void norflash_init(void)
{
/* 时钟使能 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/* 引脚初始化 */
GPIO_InitTypeDef gpio_init_struct;
/* PB14推挽输出 */
gpio_init_struct.Pin = GPIO_PIN_14; /* NORFLASH CS引脚 */
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(GPIOB, &gpio_init_struct);
spi1_init();
spi1_read_write_byte(0xFF); /* 清除DR的作用 */
NORFLASH_CS(1);
}
/* 读状态寄存器1 */
uint8_t norflash_rd_sr1(void)
{
uint8_t rec_data = 0;
NORFLASH_CS(0);
spi1_read_write_byte(0x05); /* 读状态寄存器1 */
rec_data = spi1_read_write_byte(0xFF);
NORFLASH_CS(1);
return rec_data;
}
/* 读操作 */
uint8_t norflash_read_data(uint32_t addr)
{
uint8_t rec_data;
NORFLASH_CS(0);
/* 1 发送读指令 */
spi1_read_write_byte(0x03);
/* 2 发送地址 */
spi1_read_write_byte(addr>>16);
spi1_read_write_byte(addr>>8);
spi1_read_write_byte(addr);
/* 3 读取数据 */
rec_data = spi1_read_write_byte(0xFF);
NORFLASH_CS(1);
return rec_data;
}
/* 擦除操作 */
void norflash_erase_sector(uint32_t addr)
{
/* 1 写使能 */
NORFLASH_CS(0);
spi1_read_write_byte(0x06);
NORFLASH_CS(1);
/* 2 等待空闲 */
while(norflash_rd_sr1()&0x01); /* 接收寄存器是否空闲 */
/* 3 发送扇区擦除指令 */
NORFLASH_CS(0);
spi1_read_write_byte(0x20);
/* 4 发送地址 */
spi1_read_write_byte(addr>>16);
spi1_read_write_byte(addr>>8);
spi1_read_write_byte(addr);
NORFLASH_CS(1);
/* 5 等待空闲 */
while(norflash_rd_sr1()&0x01); /* 接收寄存器是否空闲 */
}
/* 写操作 */
void norflash_write_page1(uint8_t data, uint32_t addr)
{
/* 1 擦除扇区 */
norflash_erase_sector(addr);
/* 2 写使能 */
NORFLASH_CS(0);
spi1_read_write_byte(0x06);
NORFLASH_CS(1);
/* 3 发送页写指令 */
NORFLASH_CS(0);
spi1_read_write_byte(0x02);
/* 4 发送地址 */
spi1_read_write_byte(addr>>16);
spi1_read_write_byte(addr>>8);
spi1_read_write_byte(addr);
/* 5 要写入的数据 */
spi1_read_write_byte(data);
NORFLASH_CS(1);
/* 6 等待写入完成 */
while(norflash_rd_sr1()&0x01); /* 接收寄存器是否空闲 */
}
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./USMART/usmart.h"
#include "./BSP/KEY/key.h"
#include "./BSP/NORFLASH/norflash.h"
int main(void)
{
uint8_t key;
uint16_t i = 0;
uint8_t rec_data = 0;
//uint8_t datatemp[TEXT_SIZE];
//uint32_t flashsize;
//uint16_t id = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
usmart_dev.init(84); /* 初始化USMART */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
norflash_init(); /* 初始化NORFLASH */
while (1)
{
key = key_scan(0);
if (key == KEY1_PRES) /* KEY1按下,写入 */
{
norflash_write_page1('A', 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);
}
}
/* 发送和接收数据 */
/* data为要写入的字节 */
/* rec_data为读到的字节 */
uint8_t spi1_read_write_byte(uint8_t data)
{
uint8_t rec_data = 0;
HAL_SPI_TransmitReceive(&g_spi1_handler, &data, &rec_data, 1, 1000);
return rec_data;
}
(1)RAM指哪打哪,可以覆盖写入,写比较简单,速度快。缺点为数据易丢失。
(2)NOR 与NAND 在数据写入前都需要有擦除操作,但实际上NOR Flash 的一个bit 可以从1变成0,而要从0 变 1 就要擦除后再写入,NAND Flash 这两种情况都需要擦除。擦除操作的最小单位为“扇区/块”,这意味着有时候即使只写一字节的数据,则这个“扇区/块”上之前的数据都可能会被擦除。写入速度较慢,但有掉电不丢失的特点。