第一部分:W25Q128代码头文件 (W25Q128.h)
#ifndef W25Q128_H
#define W25Q128_H
#include "stm32f10x.h"
#include "stdio.h"
#include "sys.h"
#include "delay.h"
#define W25Q128_CS PBout(12)
#define W25Q128_SCLK PBout(13)
#define W25Q128_MOSI PBout(14)
#define W25Q128_MISO PBin(15)
extern u8 W25Q128_ID[2]; //制造商/设备 ID
void W25Q128_Init(void);
u8 W25Q128_SPI_ReadWriteOneByte(u8 tx_data);
void W25Q128_WritePageData(u32 addr ,u8 * p ,u32 len );
void W25Q128_ReadData(u32 addr ,u8 * p ,u32 len );
void W25Q128_WriteDataNoCheck(u32 addr ,u8 * p ,u32 len);
void W25Q128_WriteData(u32 addr ,u8 * p ,u32 len);
void W25Q128_SectorErase(u32 addr,u8 cmd);
void W25Q128_BusyStateWait(void);
void W25Q128_WriteEnable(void);
void W25Q128_Read_ID(void);
#endif
第二部分:W25Q128代码文件 (W25Q128.c)
#include "W25Q128.h"
u8 W25Q128_ID[2]; //制造商/设备 ID
/*
硬件连接:
CS---->GPIOB_12
SCLK-->GPIOB_13
MOSI-->GPIOB_14
MISO-->GPIOB_15
*/
void W25Q128_Init(void)
{
/*1.开时钟*/
RCC->APB2ENR |= 1<<3;
/*2.配置GPIO口*/
GPIOB->CRH &= 0x0000FFFF;
GPIOB->CRH |= 0x83330000;
/*3.SCLK,CS空闲时为高电平(上拉)*/
GPIOB->ODR |= 0xF<<12;
printf("W25Q128 GPIO初始化成功!\n");
}
/*
函数功能: SPI底层时序、读写一个字节(第4种SPI协议 CPOL=1 CPHA=1 )
*/
u8 W25Q128_SPI_ReadWriteOneByte(u8 tx_data)
{
u8 rx_data=0,i=0;
W25Q128_SCLK=1;
for(i=0;i<8;i++)
{
W25Q128_SCLK=0;
/*主机发送数据*/
if(tx_data&0x80) W25Q128_MOSI=1; //先发高位再发低位
else W25Q128_MOSI=0;
tx_data<<=1;
/*从机接收数据*/
rx_data<<=1; //默认接收为0
if(W25Q128_MISO) rx_data |= 0x1; //先接高位再接低位
W25Q128_SCLK=1;
}
return rx_data;
}
/*
函数功能:在W25Q128指定位置写入指定长度数据(页写)
函数参数:
addr:要写入W25Q128的起始地址
p:源数据地址
len:要写入的数据长度(字节)
*/
void W25Q128_WritePageData(u32 addr ,u8 * p ,u32 len )
{
u32 i=0;
W25Q128_WriteEnable(); //W25Q128写使能
W25Q128_CS=0; //拉低片选脚,选中设备
W25Q128_SPI_ReadWriteOneByte(0x02); //页写命令
W25Q128_SPI_ReadWriteOneByte(addr>>16); //A23~A16 要写入W25Q128的24位地址 (先发高位)
W25Q128_SPI_ReadWriteOneByte(addr>>8); //A15~A8
W25Q128_SPI_ReadWriteOneByte(addr); //A7~A0
for(i=0;i>16); //A23~A16 要写入W25Q128的24位地址 (先发高位)
W25Q128_SPI_ReadWriteOneByte(addr>>8); //A15~A8
W25Q128_SPI_ReadWriteOneByte(addr); //A7~A0
for(i=0;i=len) page_tmp=len;
while(1) //写数据
{
W25Q128_WritePageData(addr,p,page_tmp);
if(page_tmp==len) break; //数据已经全部写入完成
addr+=page_tmp; //写地址偏移
p+=page_tmp; //源数据地址偏移
len-=page_tmp; //计算剩下的数据
if(len>=256) page_tmp=256; //计算下一次页写的数据大小
else page_tmp=len;
}
}
/*
函数功能:在W25Q128指定位置写入指定长度数据(考虑到了数据误擦除)
函数参数:
p:要写入的数据
addr:写入数据的地址
len:要写入的数据长度(字节)
*/
void W25Q128_WriteData(u32 addr ,u8 * p ,u32 len)
{
u8 W25Q128_Buff[4096]; //备份缓冲区
u32 page_tmp,sector_num,Erase_Len=len,Erase_Addr=addr,buff_len;
sector_num=addr/4096; //计算当前扇区数
buff_len=addr-sector_num*4096;
// printf("sector_num=%d buff_len=%d \n",sector_num,buff_len);
page_tmp=256-addr%256; //目标地址页剩余可以写的空间
if(page_tmp>=len) page_tmp=len;
W25Q128_ReadData(sector_num*4096,W25Q128_Buff,buff_len); //备份源扇区的数据
while(1) //以扇区的方式擦除要写数据的地方
{
if(Erase_Len<=4096-Erase_Addr%4096)
{
W25Q128_SectorErase(Erase_Addr,0x20);
break;
}
else if(Erase_Len) //如果擦除的空间不够写入数据,继续擦除空间
{
W25Q128_SectorErase(Erase_Addr,0x20);
Erase_Addr+=4096; //擦除地址偏移
Erase_Len-=4096; //还剩下待擦除的空间大小
}
else
{
break;
}
}
W25Q128_WriteDataNoCheck(sector_num*4096,W25Q128_Buff,buff_len); //还原之前备份的数据
while(1) //写数据
{
W25Q128_WritePageData(addr,p,page_tmp);
if(page_tmp==len) break; //数据已经全部写入完成
addr+=page_tmp; //写地址偏移
p+=page_tmp; //源数据地址偏移
len-=page_tmp; //计算剩下的数据
if(len>=256) page_tmp=256; //计算下一次页写的数据大小
else page_tmp=len;
}
}
/*
函数功能: 擦除W25Q128指定扇区
函数参数:
addr:要擦除W25Q128的地址
说明:擦除命令不能字节擦除,只能块/扇区擦除(一个扇区4096字节)
0~4095 :第一个扇区
4096~..:第二个扇区
..... : ...
块擦除(64KB) D8h
块擦除(32KB) 52h
扇区擦除(4KB) 20h
*/
void W25Q128_SectorErase(u32 addr,u8 cmd)
{
W25Q128_WriteEnable(); //W25Q128写使能
W25Q128_CS=0; //拉低片选脚,选中设备
W25Q128_SPI_ReadWriteOneByte(cmd); //擦除扇区命令
W25Q128_SPI_ReadWriteOneByte(addr>>16); //A23~A16 要擦除W25Q128的24位地址 (先发高位)
W25Q128_SPI_ReadWriteOneByte(addr>>8); //A15~A8
W25Q128_SPI_ReadWriteOneByte(addr); //A7~A0
W25Q128_CS=1; //释放设备
W25Q128_BusyStateWait();//等待W25Q128擦除扇区接结束
}
/*
函数功能:等待 W25Q128直到为空闲状态
*/
void W25Q128_BusyStateWait(void)
{
u8 tmp=1;
u16 cnt=0;
while(tmp&0x01)
{
W25Q128_CS=0; //拉低片选脚,选中设备
W25Q128_SPI_ReadWriteOneByte(0x05); //读状态寄存器命令
tmp=W25Q128_SPI_ReadWriteOneByte(0xFF); //读状态寄存器
W25Q128_CS=1; //释放设备
DelayMs(1);
if(cnt++>=500) break; //防止卡死
}
}
/*
函数功能: W25Q128写使能
*/
void W25Q128_WriteEnable(void)
{
W25Q128_CS=0; //拉低片选脚,选中设备
W25Q128_SPI_ReadWriteOneByte(0x06); //写使能
W25Q128_CS=1; //释放设备
}
/*函数功能:读取制造商/芯片 ID
制造商ID= EF
设备ID= 17 (W25Q64 的是16H)
*/
void W25Q128_Read_ID(void)
{
W25Q128_CS=0; //拉低片选脚,选中设备
W25Q128_SPI_ReadWriteOneByte(0x90); //读取制造商/设备 ID 指令
W25Q128_SPI_ReadWriteOneByte(0x00);
W25Q128_SPI_ReadWriteOneByte(0x00);
W25Q128_SPI_ReadWriteOneByte(0x00);
W25Q128_ID[0] = W25Q128_SPI_ReadWriteOneByte(0xFF); //0xFF不是一个指令 只是为了不让函数报错
W25Q128_ID[1] = W25Q128_SPI_ReadWriteOneByte(0xFF);
W25Q128_CS=1; //释放片选脚
printf("制造商ID= %X\n设备ID= %X \n",W25Q128_ID[0],W25Q128_ID[1]);
}
第三部分:延时代码头文件 (delay.h)
#ifndef DELAY_H
#define DELAY_H
void DelayMs(int ms);
void DelayUs(int us);
#endif
第四部分:延时代码文件 (delay.c)
#include "delay.h"
#include "sys.h"
#include "config.h"
/*函数功能: 延时ms单位*/
void DelayMs(int ms)
{
int i,j,n;
for(i=0;iVAL=0; //CNT计数器值
SysTick->LOAD=9*us; //9表示1us
SysTick->CTRL|=1<<0; //开启定时器
do
{
tmp=SysTick->CTRL; //读取状态
}while((!(tmp&1<<16))&&(tmp&1<<0));
SysTick->VAL=0; //CNT计数器值
SysTick->CTRL&=~(1<<0); //关闭定时器
#endif
}
第五部分:系统代码头文件 (sys.h)
#ifndef SYS_H
#define SYS_H
#include "stm32f10x.h"
//公式参考: Cortex-M3权威指南92页
//完成位带地址的转换
//addr表示寄存器的基地址
//bitnum表示寄存器里的第几位
#define BITBAND(addr,bitnum) ((addr&0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2))
//将地址转换为指针类型
#define MEM_ADDR(addr) *((volatile u32 *)addr)
/*定义GPIO口输入输出寄存器的地址*/
#define GPIOA_IDR (0x40010800+0x8)
#define GPIOA_ODR (0x40010800+0xC)
#define GPIOB_IDR (0x40010C00+0x8)
#define GPIOB_ODR (0x40010C00+0xC)
#define GPIOC_IDR (0x40011000+0x8)
#define GPIOC_ODR (0x40011000+0xC)
#define GPIOD_IDR (0x40011400+0x8)
#define GPIOD_ODR (0x40011400+0xC)
#define GPIOE_IDR (0x40011800+0x8)
#define GPIOE_ODR (0x40011800+0xC)
#define GPIOF_IDR (0x40011C00+0x8)
#define GPIOF_ODR (0x40011C00+0xC)
#define GPIOG_IDR (0x40012000+0x8)
#define GPIOG_ODR (0x40012000+0xC)
/*定义GPIO口位操作宏*/
#define PAin(bitnum) MEM_ADDR(BITBAND(GPIOA_IDR,bitnum))
#define PAout(bitnum) MEM_ADDR(BITBAND(GPIOA_ODR,bitnum))
#define PBin(bitnum) MEM_ADDR(BITBAND(GPIOB_IDR,bitnum))
#define PBout(bitnum) MEM_ADDR(BITBAND(GPIOB_ODR,bitnum))
#define PCin(bitnum) MEM_ADDR(BITBAND(GPIOC_IDR,bitnum))
#define PCout(bitnum) MEM_ADDR(BITBAND(GPIOC_ODR,bitnum))
#define PDin(bitnum) MEM_ADDR(BITBAND(GPIOD_IDR,bitnum))
#define PDout(bitnum) MEM_ADDR(BITBAND(GPIOD_ODR,bitnum))
#define PEin(bitnum) MEM_ADDR(BITBAND(GPIOE_IDR,bitnum))
#define PEout(bitnum) MEM_ADDR(BITBAND(GPIOE_ODR,bitnum))
#define PFin(bitnum) MEM_ADDR(BITBAND(GPIOF_IDR,bitnum))
#define PFout(bitnum) MEM_ADDR(BITBAND(GPIOF_ODR,bitnum))
#define PGin(bitnum) MEM_ADDR(BITBAND(GPIOG_IDR,bitnum))
#define PGout(bitnum) MEM_ADDR(BITBAND(GPIOG_ODR,bitnum))
/*NVIC中断优先级分组定义*/
#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
0 bits for subpriority */
void STM32_NVIC_SetPriority(IRQn_Type IRQn,uint32_t PreemptPriority, uint32_t SubPriority);//设置中断优先级
void SysTick_Init(void);//嘀嗒定时器初始化函数
#endif
/**
@code
The table below gives the allowed values of the pre-emption priority and subpriority according
to the Priority Grouping configuration performed by NVIC_PriorityGroupConfig function
============================================================================================================================
NVIC_PriorityGroup | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannel | Description
============================================================================================================================
NVIC_PriorityGroup_0 | 0 | 0-15 | 0 bits for pre-emption priority
| | | 4 bits for subpriority
----------------------------------------------------------------------------------------------------------------------------
NVIC_PriorityGroup_1 | 0-1 | 0-7 | 1 bits for pre-emption priority
| | | 3 bits for subpriority
----------------------------------------------------------------------------------------------------------------------------
NVIC_PriorityGroup_2 | 0-3 | 0-3 | 2 bits for pre-emption priority
| | | 2 bits for subpriority
----------------------------------------------------------------------------------------------------------------------------
NVIC_PriorityGroup_3 | 0-7 | 0-1 | 3 bits for pre-emption priority
| | | 1 bits for subpriority
----------------------------------------------------------------------------------------------------------------------------
NVIC_PriorityGroup_4 | 0-15 | 0 | 4 bits for pre-emption priority
| | | 0 bits for subpriority
============================================================================================================================
@endcode
*/