在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持I 2 S音频协议。SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。
在小容量和中容量产品上,不支持I 2 S音频协议。
串行外设接口(SPI)允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。接口还能以多主配置方式工作。
它可用于多种用途,包括使用一条双向数据线的双线单工同步传输,还可使用CRC校验的可靠通信。
I2S也是一种3引脚的同步串行接口通讯协议。它支持四种音频标准,包括飞利浦I 2 S标准,MSB和LSB对齐标准,以及PCM标准。它在半双工通讯中,可以工作在主和从2种模式下。当它作为主设备时,通过接口向外部的从设备提供时钟信号。
?W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景
?存储介质:Nor Flash(闪存)
?时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)
?存储容量(24位地址)
CS: PA4
CLK: PA5
DO: PA6
DI: PA7
spi.h
#ifndef __SPI_H__
#define __SPI_H__
#include "stm32f10x.h"
void spi_init(void);
void spi_start(void);
void spi_stop(void);
uint8_t spi_swap_byte(uint8_t val);
#endif /*__SPI_H__*/
spi.c
#include "spi.h"
/*
CS: PA4
CLK: PA5
DO: PA6
DI: PA7
*/
//SS写 PA4
void spi_W_SS(uint8_t bitval)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)bitval);
}
//CLK写 PA5
void spi_W_SCK(uint8_t bitval)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)bitval);
}
//DI写 MOSI PA7
void spi_W_MOSI(uint8_t bitval)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)bitval);
}
//DO读 MISO PA6
uint8_t spi_R_MISO(void)
{
return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}
void spi_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//A4 A5 A7
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//A6
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
spi_W_SS(1);
spi_W_SCK(0);
}
void spi_start(void)
{
spi_W_SS(0);
}
void spi_stop(void)
{
spi_W_SS(1);
}
uint8_t spi_swap_byte(uint8_t val)
{
uint8_t i = 0;
uint8_t value = 0;
//模式0 第一个边沿移入数据 第二个边沿移出数据
for (i = 0; i < 8; i++)
{
spi_W_MOSI(val & (0x80 >> i));
spi_W_SCK(1);
if (spi_R_MISO() == 1)
{
value |= 0x80 >> i;
}
spi_W_SCK(0);
}
return value;
}
w25q64.h
#ifndef __W25Q64_H__
#define __W25Q64_H__
#include "stm32f10x.h"
#define W25Q64_WRITE_ENABLE 0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS_REGISTER_1 0x05
#define W25Q64_READ_STATUS_REGISTER_2 0x35
#define W25Q64_WRITE_STATUS_REGISTER 0x01
#define W25Q64_PAGE_PROGRAM 0x02
#define W25Q64_QUAD_PAGE_PROGRAM 0x32
#define W25Q64_BLOCK_ERASE_64KB 0xD8
#define W25Q64_BLOCK_ERASE_32KB 0x52
#define W25Q64_SECTOR_ERASE_4KB 0x20
#define W25Q64_CHIP_ERASE 0xC7
#define W25Q64_ERASE_SUSPEND 0x75
#define W25Q64_ERASE_RESUME 0x7A
#define W25Q64_POWER_DOWN 0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID 0x90
#define W25Q64_READ_UNIQUE_ID 0x4B
#define W25Q64_JEDEC_ID 0x9F
#define W25Q64_READ_DATA 0x03
#define W25Q64_FAST_READ 0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
#define W25Q64_FAST_READ_DUAL_IO 0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
#define W25Q64_FAST_READ_QUAD_IO 0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3
#define W25Q64_DUMMY_BYTE 0xFF
void W25Q64_init(void);
void W25Q64_read_id(uint8_t *mid, uint16_t *did);
#endif /*__W25Q64_H__*/
w25q64.c
#include "w25q64.h"
#include "spi.h"
void W25Q64_init(void)
{
spi_init();
}
void W25Q64_read_id(uint8_t *mid, uint16_t *did)
{
spi_start();
spi_swap_byte(W25Q64_JEDEC_ID);
*mid = spi_swap_byte(W25Q64_DUMMY_BYTE);
*did = spi_swap_byte(W25Q64_DUMMY_BYTE);
*did <<= 8;
*did |= spi_swap_byte(W25Q64_DUMMY_BYTE);
spi_stop();
}
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "oled.h"
#include "w25q64.h"
int main(void)
{
uint8_t mid;
uint16_t did;
//初始化
OLED_Init();
W25Q64_init();
//显示一个字符
//OLED_ShowChar(1, 1, 'A');
//显示字符串
//OLED_ShowString(1, 3, "SPI Test");
OLED_ShowString(1, 1, "MID: DID:");
OLED_ShowString(2, 1, "W:");
OLED_ShowString(3, 1, "R:");
W25Q64_read_id(&mid, &did);
OLED_ShowHexNum(1, 5, mid, 2);
OLED_ShowHexNum(1, 12, did, 4);
while(1)
{
}
return 0;
}
spi.h
#ifndef __SPI_H__
#define __SPI_H__
#include "stm32f10x.h"
void spi_init(void);
void spi_start(void);
void spi_stop(void);
uint8_t spi_swap_byte(uint8_t val);
#endif /*__SPI_H__*/
spi.c
#include "spi.h"
/*
CS: PA4
CLK: PA5
DO: PA6
DI: PA7
*/
//SS写 PA4
void spi_W_SS(uint8_t bitval)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)bitval);
}
//CLK写 PA5
void spi_W_SCK(uint8_t bitval)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)bitval);
}
//DI写 MOSI PA7
void spi_W_MOSI(uint8_t bitval)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)bitval);
}
//DO读 MISO PA6
uint8_t spi_R_MISO(void)
{
return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}
void spi_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//A4 A5 A7
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//A6
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
spi_W_SS(1);
spi_W_SCK(0);
}
void spi_start(void)
{
spi_W_SS(0);
}
void spi_stop(void)
{
spi_W_SS(1);
}
uint8_t spi_swap_byte(uint8_t val)
{
uint8_t i = 0;
uint8_t value = 0;
//模式0 第一个边沿移入数据 第二个边沿移出数据
for (i = 0; i < 8; i++)
{
spi_W_MOSI(val & (0x80 >> i));
spi_W_SCK(1);
if (spi_R_MISO() == 1)
{
value |= 0x80 >> i;
}
spi_W_SCK(0);
}
return value;
}
w25q64.h
#ifndef __W25Q64_H__
#define __W25Q64_H__
#include "stm32f10x.h"
#define W25Q64_WRITE_ENABLE 0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS_REGISTER_1 0x05
#define W25Q64_READ_STATUS_REGISTER_2 0x35
#define W25Q64_WRITE_STATUS_REGISTER 0x01
#define W25Q64_PAGE_PROGRAM 0x02
#define W25Q64_QUAD_PAGE_PROGRAM 0x32
#define W25Q64_BLOCK_ERASE_64KB 0xD8
#define W25Q64_BLOCK_ERASE_32KB 0x52
#define W25Q64_SECTOR_ERASE_4KB 0x20
#define W25Q64_CHIP_ERASE 0xC7
#define W25Q64_ERASE_SUSPEND 0x75
#define W25Q64_ERASE_RESUME 0x7A
#define W25Q64_POWER_DOWN 0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID 0x90
#define W25Q64_READ_UNIQUE_ID 0x4B
#define W25Q64_JEDEC_ID 0x9F
#define W25Q64_READ_DATA 0x03
#define W25Q64_FAST_READ 0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
#define W25Q64_FAST_READ_DUAL_IO 0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
#define W25Q64_FAST_READ_QUAD_IO 0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3
#define W25Q64_DUMMY_BYTE 0xFF
void W25Q64_init(void);
void W25Q64_read_id(uint8_t *mid, uint16_t *did);
//写使能
void W25Q64_write_enable(void);
//等待 直到空闲
void W25Q64_wait_busy(void);
void W25Q64_sector_erase(uint32_t addr);
void W25Q64_page_program(uint32_t addr, uint8_t *arr, uint16_t len);
void W25Q64_read_data(uint32_t addr, uint8_t *arr, uint16_t len);
#endif /*__W25Q64_H__*/
w25q64.c
#include "w25q64.h"
#include "spi.h"
void W25Q64_init(void)
{
spi_init();
}
void W25Q64_read_id(uint8_t *mid, uint16_t *did)
{
spi_start();
spi_swap_byte(W25Q64_JEDEC_ID);
*mid = spi_swap_byte(W25Q64_DUMMY_BYTE);
*did = spi_swap_byte(W25Q64_DUMMY_BYTE);
*did <<= 8;
*did |= spi_swap_byte(W25Q64_DUMMY_BYTE);
spi_stop();
}
void W25Q64_write_enable(void)
{
spi_start();
spi_swap_byte(W25Q64_WRITE_ENABLE);
spi_stop();
}
void W25Q64_wait_busy(void)
{
uint32_t timeout;
spi_start();
spi_swap_byte(W25Q64_READ_STATUS_REGISTER_1);
timeout = 100000;
while((spi_swap_byte(W25Q64_DUMMY_BYTE) & 0x1) == 0x01)
{
timeout--;
if (0 == timeout)
{
break;
}
}
spi_stop();
}
void W25Q64_page_program(uint32_t addr, uint8_t *arr, uint16_t len)
{
uint8_t i;
W25Q64_write_enable();
spi_start();
spi_swap_byte(W25Q64_PAGE_PROGRAM);
spi_swap_byte(addr >> 16);
spi_swap_byte(addr >> 8);
spi_swap_byte(addr);
for (i = 0; i < len; i++)
{
spi_swap_byte(arr[i]);
}
spi_stop();
W25Q64_wait_busy();
}
void W25Q64_sector_erase(uint32_t addr)
{
W25Q64_write_enable();
spi_start();
spi_swap_byte(W25Q64_SECTOR_ERASE_4KB);
spi_swap_byte(addr >> 16);
spi_swap_byte(addr >> 8);
spi_swap_byte(addr);
spi_stop();
W25Q64_wait_busy();
}
void W25Q64_read_data(uint32_t addr, uint8_t *arr, uint16_t len)
{
uint8_t i = 0;
spi_start();
spi_swap_byte(W25Q64_READ_DATA);
spi_swap_byte(addr >> 16);
spi_swap_byte(addr >> 8);
spi_swap_byte(addr);
for (i = 0; i < len; i++)
{
arr[i] = spi_swap_byte(W25Q64_DUMMY_BYTE);
}
spi_stop();
}
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "oled.h"
#include "w25q64.h"
int main(void)
{
uint8_t mid;
uint16_t did;
uint8_t array_w[4] = {0x11, 0x22, 0x33, 0x44};
uint8_t array_r[4];
//初始化
OLED_Init();
W25Q64_init();
//显示一个字符
//OLED_ShowChar(1, 1, 'A');
//显示字符串
//OLED_ShowString(1, 3, "SPI Test");
OLED_ShowString(1, 1, "MID: DID:");
OLED_ShowString(2, 1, "W:");
OLED_ShowString(3, 1, "R:");
W25Q64_read_id(&mid, &did);
OLED_ShowHexNum(1, 5, mid, 2);
OLED_ShowHexNum(1, 12, did, 4);
//擦除扇区
W25Q64_sector_erase(0x0);
//写扇区
W25Q64_page_program(0x0, array_w, 4);
//读数据
W25Q64_read_data(0x0, array_r, 4);
OLED_ShowHexNum(2, 3, array_w[0], 2);
OLED_ShowHexNum(2, 6, array_w[1], 2);
OLED_ShowHexNum(2, 9, array_w[2], 2);
OLED_ShowHexNum(2, 12, array_w[3], 2);
OLED_ShowHexNum(3, 3, array_r[0], 2);
OLED_ShowHexNum(3, 6, array_r[1], 2);
OLED_ShowHexNum(3, 9, array_r[2], 2);
OLED_ShowHexNum(3, 12, array_r[3], 2);
while(1)
{
}
return 0;
}