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分类
- Standard SPI(标准SPI)全双工方式
标准SPI,通信线包含:片选CS、时钟线CLK、输入DI、输出DO。驱动SPI FLASH还需要写保护WR以及维持HOLD引脚。 - Dual SPI(双线SPI) 半双工方式
对标准SPI改进,DO和DI改成IO0和IO1,变为双向IO口。一个周期内,通过数据线,传输2位数据。 - 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);
}