按照良许嵌入式的代码引用,需要了解详细请移步良许嵌入式32部分良许嵌入式。
引脚定义:
首先是头文件部分声明以及定义
1.1头文件
#ifndef __W25Q128_H
#define __W25Q128_H
#include "sys.h"
//定义片选信号,1为选中,0为未选中
#define W25Q128_CS(X) do{X? \
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET):\
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);\
}while(0)
//下面是W25Q128的指令表,在W25Q128官方手册第八章可以找到
#define FLASH_ManufactDeviceID 0x90 //查询厂商设备号:16位
#define FLASH_WriteEnable 0x06 //开启写使能
#define FLASH_ReadStatusReg1 0x05 //读取SPI状态寄存器1
#define FLASH_ReadData 0x03 //读取数据
#define FLASH_PageProgram 0x02 //页
#define FLASH_SectorErase 0x20 //扇区擦除
#define FLASH_DummyByte 0xFF //写入指令
void w25q128_init(void); //初始化
uint16_t w25q128_read_id(void); //读取芯片ID
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size); //读取spi接收缓冲区数据
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size); //写页
void w25q128_erase_sector(uint32_t address); //擦除扇区
#endif
2.1 SPI的初始化(void spi1_init(void))
SPI_HandleTypeDef SPI_HandleInitStructure={0}; //定义spi句柄并且初始化
void spi1_init(void)
{
SPI_HandleInitStructure.Instance=SPI1; //当前模块使用的是spi1
SPI_HandleInitStructure.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256; //SPI波特率分频 256
SPI_HandleInitStructure.Init.CLKPhase=SPI_PHASE_1EDGE; //奇数边沿触发
SPI_HandleInitStructure.Init.CLKPolarity=SPI_POLARITY_LOW; //CPOL,低电平为空闲
SPI_HandleInitStructure.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //关闭crc校验
SPI_HandleInitStructure.Init.CRCPolynomial=7; //CRC校验多项式 4+2+1=7,spi默认
SPI_HandleInitStructure.Init.DataSize=SPI_DATASIZE_8BIT; //8位数据传输模式
SPI_HandleInitStructure.Init.Direction=SPI_DIRECTION_2LINES; //双工模式
SPI_HandleInitStructure.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI_HandleInitStructure.Init.NSS=SPI_NSS_SOFT; //从模式选择,软件
SPI_HandleInitStructure.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI_HandleInitStructure.Init.TIMode=SPI_TIMODE_DISABLE; //关闭
HAL_SPI_Init(&SPI_HandleInitStructure);
}
2.2 SPI的Msp_Init,这个函数主要作用是配置spi引脚,还有开启时钟,设置中断优先级
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance == SPI1) //如果是SPI1
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOB时钟
__HAL_RCC_SPI1_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = W25Q128_CS_PIN; //片选
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = W25Q128_CLK_PIN | W25Q128_DI_PIN; //时钟,w25q128数据输入
gpio_initstruct.Mode = GPIO_MODE_AF_PP;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = W25Q128_DO_PIN; //w25q128数据输出
gpio_initstruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
}
}
2.3 spi交换数据(给spi发送数据,返回spi接收数据后返回的数据)
uint8_t w25q128_spi_swap_byte(uint8_t data)
{
uint8_t recv_data;
HAL_SPI_TransmitReceive(&SPI_HandleInitStructure,&data,&recv_data,1,1000);
return recv_data;
}
2.4 spi 初始化
void w25q128_init(void)
{
w25q128_spi_init();
}
2.5 w25q128读取设备ID
查看数据手册,先拉低片选,然后发送查询设备ID指令,接着看第二段第三行follow by24 bit address,发送0x00三次。然后就是对spi发送dump_by。先求低位,左移八位,高位补零,然后求高位,并且或上。
uint16_t w25q128_spi_read_DevId()
{
uint16_t dev_id;
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_ManufactDeviceID); //发送查询ID指令
w25q128_spi_swap_byte(0x00); //根据手册,连续发送三个0x00
w25q128_spi_swap_byte(0x00);
w25q128_spi_swap_byte(0x00);
dev_id = w25q128_spi_swap_byte(FLASH_DummyByte)<<8; //低八位
dev_id |= w25q128_spi_swap_byte(FLASH_DummyByte); //高八位
W25Q128_CS(1); //释放片选信号
return dev_id; //返回设备id号
}
2.6等待忙(w25q128_spi_wait_busy),这是为了往页里面写数据还有擦除扇区做准备
uint8_t w25q128_spi_readSR1(void)
{
uint8_t recv_data;
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_ReadStatusReg1); //打开读状态寄存器
W25Q128_CS(1); //释放片选信号
return recv_data;
}
void w25q128_spi_wait_busy(void)
{
while( (w25q128_spi_readSR1()&0x01)==0x01);
}
2.7发送spi地址到w25q128,为读写和擦除数据做准备
//w25q128有24位地址,多以要用uint32_t 传递
void w25q128_spi_sendAddress(uint32_t address)
{
w25q128_spi_swap_byte(address>>16); //发送最高八位地址
w25q128_spi_swap_byte(address>>8); //发送中间八位地址
w25q128_spi_swap_byte(address); //发送最低八位地址
}
2.8w25q128读取数据
1.拉低片选 2.发送读数据信号, 3.发送要读取数据在w25q128的地址 4.根据大小循环读取数据(通过往w25q128发送数据,并接收返回的值) 5拉高片选。
//读取保存在w25q128的数据
void w25q128_spi_readData(uint8_t *data,uint32_t address,uint32_t size)
{
uint32_t i=0;
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_ReadData); //发送读取数据信号
w25q128_spi_sendAddress(address); //发送读取数据信号
for(i=0;i<size;i++){
data[i]= w25q128_spi_swap_byte(FLASH_DummyByte); //发送预设字节
}
W25Q128_CS(1); //释放片选信号
}
2.9按页往w25q128写入数据
1.开启写使能 2.拉低片选 3.发送写数据信号, 4.发送要写数据在w25q128的地址 5.根据大小循环写入数据 6. 拉高片选 7.等待空闲
void w25q128_spi_writeData(uint8_t *data,uint32_t address,uint16_t size)
{
uint16_t i=0;
w25q128_spi_write_enable(); //开启写使能
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_PageProgram); //发送写入数据信号
w25q128_spi_sendAddress(address); //发送写入数据地址
for(i=0;i<size;i++){
w25q128_spi_swap_byte(data[i]); //发送预设字节
}
W25Q128_CS(1); //释放片选信号
w25q128_spi_wait_busy(); //等待空闲
}
2.10扇区擦除
void w25q128_spi_eraseSector(uint32_t address)
{
w25q128_spi_write_enable(); //开启写使能
w25q128_spi_wait_busy(); //等待空闲
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_SectorErase); //发送读取数据信号
w25q128_spi_sendAddress(address); //发送读取数据信号
W25Q128_CS(1); //释放片选信号
w25q128_spi_wait_busy(); //等待空闲
}
w25q128.c
#include "w25q128.h"
SPI_HandleTypeDef SPI_HandleInitStructure={0}; //定义spi句柄并且初始化
void spi1_init(void)
{
SPI_HandleInitStructure.Instance=SPI1; //当前模块使用的是spi1
SPI_HandleInitStructure.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256; //SPI波特率分频 256
SPI_HandleInitStructure.Init.CLKPhase=SPI_PHASE_1EDGE; //奇数边沿触发
SPI_HandleInitStructure.Init.CLKPolarity=SPI_POLARITY_LOW; //CPOL,低电平为空闲
SPI_HandleInitStructure.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //关闭crc校验
SPI_HandleInitStructure.Init.CRCPolynomial=7; //CRC校验多项式 4+2+1=7,spi默认
SPI_HandleInitStructure.Init.DataSize=SPI_DATASIZE_8BIT; //8位数据传输模式
SPI_HandleInitStructure.Init.Direction=SPI_DIRECTION_2LINES; //双工模式
SPI_HandleInitStructure.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI_HandleInitStructure.Init.NSS=SPI_NSS_SOFT; //从模式选择,软件
SPI_HandleInitStructure.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI_HandleInitStructure.Init.TIMode=SPI_TIMODE_DISABLE; //关闭
HAL_SPI_Init(&SPI_HandleInitStructure);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance == SPI1) //如果是SPI1
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOB时钟
__HAL_RCC_SPI1_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = W25Q128_CS_PIN; //片选
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = W25Q128_CLK_PIN | W25Q128_DI_PIN; //时钟,w25q128数据输入
gpio_initstruct.Mode = GPIO_MODE_AF_PP;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = W25Q128_DO_PIN; //w25q128数据输出
gpio_initstruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
}
}
void w25q128_spi_init(void)
{
spi1_init(); //初始化spi1
}
uint8_t w25q128_spi_swap_byte(uint8_t data)
{
uint8_t recv_data;
HAL_SPI_TransmitReceive(&SPI_HandleInitStructure,&data,&recv_data,1,1000);
return recv_data;
}
uint16_t w25q128_spi_read_DevId(void)
{
uint16_t dev_id;
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_ManufactDeviceID); //发送查询ID指令
w25q128_spi_swap_byte(0x00); //根据手册,连续发送三个0x00
w25q128_spi_swap_byte(0x00);
w25q128_spi_swap_byte(0x00);
dev_id = w25q128_spi_swap_byte(FLASH_DummyByte)<<8; //低八位
dev_id |= w25q128_spi_swap_byte(FLASH_DummyByte); //高八位
W25Q128_CS(1); //释放片选信号
return dev_id; //返回设备id号
}
void w25q128_spi_write_enable(void)
{
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_WriteEnable); //打开写使能
W25Q128_CS(1); //释放片选信号
}
uint8_t w25q128_spi_readSR1(void)
{
uint8_t recv_data=0;
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_ReadStatusReg1); //打开写使能
recv_data=w25q128_spi_swap_byte(FLASH_DummyByte); //发送dummy byte
W25Q128_CS(1); //释放片选信号
return recv_data;
}
void w25q128_spi_wait_busy(void)
{
while( (w25q128_spi_readSR1()&0x01)==0x01);
}
//w25q128有24位地址,多以要用uint32_t 传递
void w25q128_spi_sendAddress(uint32_t address)
{
w25q128_spi_swap_byte(address>>16); //发送最高八位地址
w25q128_spi_swap_byte(address>>8); //发送中间八位地址
w25q128_spi_swap_byte(address); //发送最低八位地址
}
//读取保存在w25q128的数据
void w25q128_spi_readData(uint8_t *data,uint32_t address,uint32_t size)
{
uint32_t i=0;
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_ReadData); //发送读取数据信号
w25q128_spi_sendAddress(address); //发送地址信号
for(i=0;i<size;i++){
data[i]= w25q128_spi_swap_byte(FLASH_DummyByte); //发送预设字节
}
W25Q128_CS(1); //释放片选信号
}
//按页往w25q128写入数据
void w25q128_spi_writeData(uint8_t *data,uint32_t address,uint16_t size)
{
uint16_t i=0;
w25q128_spi_write_enable(); //开启写使能
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_PageProgram); //页
w25q128_spi_sendAddress(address); //发送地址信号
for(i=0;i<size;i++){
w25q128_spi_swap_byte(data[i]); //发送预设字节
}
W25Q128_CS(1); //释放片选信号
w25q128_spi_wait_busy(); //等待空闲
}
void w25q128_spi_eraseSector(uint32_t address)
{
w25q128_spi_write_enable(); //开启写使能
w25q128_spi_wait_busy(); //等待空闲
W25Q128_CS(0); //拉低片选,表示被选中
w25q128_spi_swap_byte(FLASH_SectorErase); //发送读取数据信号
w25q128_spi_sendAddress(address); //发送读取数据信号
W25Q128_CS(1); //释放片选信号
w25q128_spi_wait_busy(); //等待空闲
}
main.c
#include "sys.h"
#include <string.h>
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "w25q128.h"
int main(void)
{
uint8_t data_temp[100];
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* 初始化LED灯 */
uart1_init(115200);
w25q128_spi_init();
printf("SPI 读取实验!\r\n");
uint16_t dev_id=w25q128_spi_read_DevId();
printf("W25Q128 DEV_ID:%X\r\n",dev_id);
printf("往SPI写入数据!\r\n");
sprintf( (char*)data_temp,"这是写入芯片的数据,真的应该这次没有乱码了吧放假哦i我就佛i今晚i哦飞机哦");
w25q128_spi_eraseSector(0x000000);
w25q128_spi_writeData(data_temp,0x000000,100);
memset(data_temp,0,100); //清空w25q128的数组
w25q128_spi_readData(data_temp,0x000000,100); //从w25q128再次读取
printf("读取出的数据:%s.\r\n",data_temp);
while(1)
{
led1_on();
led2_off();
delay_ms(500);
led1_off();
led2_on();
delay_ms(500);
}
}
个人使用不足请指正。