STM32F103的W25Q64的DMA高效数据访问实现_w25q64有必要用dma吗-程序员宅基地

技术标签: STM32  W25Q64  

STM32F103的W25Q64的DMA高效数据访问实现

1. 关于DMA

首先任何FLASH的写操作都是非常耗时的,体现在擦除FLASH上,且写操作不能太频繁,故而用DMA方式实现写操作程序逻辑会非常复杂,程序逻辑在各种中断处理中容易乱,与之相反采用DMA读大批量数据时及具有优势.
比如36MHzSPI时种,采用轮询方式读一页数据需要花费250us时间,二用了DMA后只需要80us时间,提高了三倍还多,且启动DMA只需要5us,节省了245us时间.

2. W25Q64实用的函数

① 读ID号,用于确定芯片是否能正常访问
② 轮询方式读数据
③ 擦除指令
④ 写一页数据指令,(事先要擦除)
⑤ DMA方式读取一块数据

3. 程序正文W25Q64.c

/*********************************Copyright (c)*********************************
**                               
**                                 FIVE工作组
**
**---------------------------------File Info------------------------------------
** File Name:               w25q64.c
** Last modified Date:      2013/9/10 9:32:33
** Last Version:            V1.2   
** Description:             none
**
**------------------------------------------------------------------------------
** Created By:              wanxuncpx
** Created date:            2013/8/6 21:12:35
** Version:                 V1.2
** Descriptions:            none
**------------------------------------------------------------------------------
** HW_CMU:                  STM32F103ZET6
** Libraries:               STM32F10x_StdPeriph_Driver
** version                  V3.5
*******************************************************************************/


/******************************************************************************
更新说明:
******************************************************************************/




/******************************************************************************
*********************************  应 用 资 料 ********************************
******************************************************************************/




/******************************************************************************
********************************* 文件引用部分 ********************************
******************************************************************************/
#include "w25q64.h"


/******************************************************************************
********************************* 数 据 声 明 *********************************
******************************************************************************/
/*---------------------* 
*     数据定义(输出)
*----------------------*/
uint8_t W25X_Buffer[W25X_SECTOR_SIZE];
volatile bool sem_W25X_DMA_Busy = true;
volatile bool sem_W25X_DMA_RxRdy= false;


/*---------------------* 
*       数据定义(内部用) 
*----------------------*/
static uint8_t  W25X_TX_Byte=0xFF;






/******************************************************************************
********************************* 函 数 声 明 *********************************
******************************************************************************/
/******************************************************************************
/ 函数功能:初始化W25Q64的GPIO口连接
/ 修改日期:2013/9/10 19:04:15
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void W25X_GPIO_Config(void)
{
	/* Private typedef ---------------------------------------------------------*/
	GPIO_InitTypeDef GPIO_InitStruct;




	/** SPI1 GPIO Configuration	
		 PA5	 ------> SPI1_SCK
		 PA6	 ------> SPI1_MISO
		 PA7	 ------> SPI1_MOSI
	*/
	/*Enable or disable APB2 peripheral clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);


	/*Configure GPIO pin */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);


	/** Configure pins as GPIO
		 PA4	 ------> GPIO_Output
		 PB0	 ------> GPIO_Output
	*/
	/*Enable or disable APB2 peripheral clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
    W25X_WP_EN();
	W25X_CS_H();
	
	/*Configure GPIO pin */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);


	/*Configure GPIO pin */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct); 
	
    /*Lock of the gpio */
	GPIO_PinLockConfig(GPIOA,GPIO_Pin_4);
	GPIO_PinLockConfig(GPIOB,GPIO_Pin_0);
}


/******************************************************************************
/ 函数功能:初始化W25Q64
/ 修改日期:2013/9/10 19:04:16
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void W25X_Init(void)
{
	SPI_InitTypeDef  SPI_InitStructure ;
	DMA_InitTypeDef  DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
    
    //配置DMA通道,DMA1_CH2收
    //读取SPI FLASH时多数为空数据故而数据地址无需增加
    //启动DMA1的时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_DeInit(DMA1_Channel2);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&SPI1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)W25X_Buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 0;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel2, &DMA_InitStructure);


    //配置DMA通道,DMA1_CH3发送
    DMA_DeInit(DMA1_Channel3);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&SPI1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(&W25X_TX_Byte);
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 0;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel3, &DMA_InitStructure);
    
    //关闭DMA,清DMA标记,使能DMA1_CH2的传输完成中断
    DMA_Cmd(DMA1_Channel3, DISABLE);            //关闭发送DMA
    DMA_Cmd(DMA1_Channel2, DISABLE);            //关闭接收DMA
    DMA_ClearFlag(DMA1_FLAG_GL3|DMA1_FLAG_TC3|DMA1_FLAG_HT3|DMA1_FLAG_TE3);
    DMA_ClearFlag(DMA1_FLAG_GL2|DMA1_FLAG_TC2|DMA1_FLAG_HT2|DMA1_FLAG_TE2);
    DMA_ITConfig(DMA1_Channel2,DMA_IT_TC,ENABLE);
    
	//初始化SPI时钟	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);


	// SPI配置
	SPI_Cmd(SPI1,DISABLE);
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master ;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b ;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low ;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge ;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft ;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ;    //72MHz分频
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ; //SPI设置成LSB模式
	SPI_InitStructure.SPI_CRCPolynomial = 7 ;
	SPI_Init( SPI1, &SPI_InitStructure ) ;
	SPI_Cmd(SPI1,ENABLE);           //启动SPI
	
    //打开SPI1的DMA发送接收请求
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
    
    //清DMA忙信号
    sem_W25X_DMA_Busy = false;
    
    //使能NVIC中断
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = W25X_DMA_TC_PRIO;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


/******************************************************************************
/ 函数功能:SPI发送一个字节的数据
/ 修改日期:2013/9/10 19:04:16
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
uint8_t W25X_ReadWriteByte(uint8_t dat)
{
    while ((SPI1->SR & SPI_I2S_FLAG_TXE) == (uint16_t)RESET);
    SPI1->DR = dat;
    while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == (uint16_t)RESET);
    return (SPI1->DR);
}


/******************************************************************************
/ 函数功能:读取SPI_FLASH的状态寄存器
/ 修改日期:2013/9/10 20:38:48
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
/   BIT7  6   5   4   3   2   1   0
/   SPR   RV  TB BP2 BP1 BP0 WEL BUSY
/
/   SPR:默认0,状态寄存器保护位,配合WP使用
/   TB,BP2,BP1,BP0:FLASH区域写保护设置
/   WEL:写使能锁定, 1
/   BUSY:忙标记位(1,忙;0,空闲)
/   默认:0x00
******************************************************************************/
uint8_t W25X_ReadSR(void)   
{  
    uint8_t byte=0;   
    W25X_CS_L();                            //使能器件   
    W25X_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令    
    byte=W25X_ReadWriteByte(0Xff);          //读取一个字节  
    W25X_CS_H();                            //使能器件     
    return byte;   
}


/******************************************************************************
/ 函数功能:读取芯片ID
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:
/   返回值如下:       
/   0XEF13,表示芯片型号为W25Q80  
/   0XEF14,表示芯片型号为W25Q16    
/   0XEF15,表示芯片型号为W25Q32  
/   0XEF16,表示芯片型号为W25Q64 
/ 使用说明:none
******************************************************************************/     
uint16_t W25X_ReadID(void)
{
    uint16_t Temp = 0;
      
    W25X_CS_L();       
    W25X_ReadWriteByte(0x90);       //发送读取ID命令     
    W25X_ReadWriteByte(0x00);      
    W25X_ReadWriteByte(0x00);      
    W25X_ReadWriteByte(0x00);         
    Temp|=W25X_ReadWriteByte(0xFF)<<8;  
    Temp|=W25X_ReadWriteByte(0xFF);  
    W25X_CS_H();        
    return Temp;
} 


/******************************************************************************
/ 函数功能:等待芯片执行完毕
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void W25X_Wait_Busy(void)
{
    while((W25X_ReadSR()&0x01)==0x01);   // 等待BUSY位清空 
}


/******************************************************************************
/ 函数功能:读取芯片看是否为忙
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
bool W25X_Read_BusyState(void)
{
    if((W25X_ReadSR()&0x01))return true;
    else return false;  
}


/******************************************************************************
/ 函数功能:SPI_FLASH写使能 
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/ 
void W25X_Write_Enable(void)
{
    W25X_WP_EN();                           //打开硬件写使能
    W25X_CS_L();                            //使能器件   
    W25X_ReadWriteByte(W25X_WriteEnable);   //发送写使能  
    W25X_CS_H();                            //取消片选            
} 


/******************************************************************************
/ 函数功能:SPI_FLASH写禁止 
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void W25X_Write_Disable(void)   
{  
    W25X_WP_DIS();                          //关闭硬件写使能
    W25X_CS_L();                            //使能器件   
    W25X_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令    
    W25X_CS_H();                            //取消片选                
}


/******************************************************************************
/ 函数功能:使SPI FLASH掉电 
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void SPI_Flash_PowerDown(void)   
{ 
    uint16_t i;
        
    W25X_CS_L();                        //使能器件   
    W25X_ReadWriteByte(W25X_PowerDown); //发送掉电命令  
    W25X_CS_H();                        //取消片选            
    i= (72)*3;while(i--);               //等待约3us
}
  
/******************************************************************************
/ 函数功能:唤醒SPI FLASH
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/ 
void SPI_Flash_WakeUp(void)   
{ 
    uint16_t i;
    
    W25X_CS_L();                        //使能器件   
    W25X_ReadWriteByte(W25X_ReleasePowerDown); //发送掉电命令  
    W25X_CS_H();                        //取消片选
    i= (72)*3;while(i--);               //等待约3us
}


/******************************************************************************
/ 函数功能:擦除整个芯片 ,等待时间超长... 
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void W25X_Erase_Chip(bool bwait)   
{                                   
    W25X_Write_Enable();            //SET WEL 
    W25X_Wait_Busy();   
    W25X_CS_L();                    //使能器件   
    W25X_ReadWriteByte(W25X_ChipErase);   //发送片擦除命令  
    W25X_CS_H();                    //取消片选
    if(bwait)W25X_Wait_Busy();      //等待芯片擦除结束
}


/******************************************************************************
/ 函数功能:擦除一个扇区
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:Dst_Addr:扇区地址 根据实际容量设置
/ 输出参数:none
/ 使用说明:none擦除一个扇区的最少时间:65ms
******************************************************************************/
void W25X_Erase_Sector(uint32_t Dst_Addr,bool bwait)   
{  
    W25X_Write_Enable();            //SET WEL   
    W25X_Wait_Busy();   
    W25X_CS_L();                    //使能器件    
    W25X_ReadWriteByte(W25X_SectorErase);           //发送扇区擦除指令 
    W25X_ReadWriteByte((uint8_t)((Dst_Addr)>>16));  //发送24bit地址    
    W25X_ReadWriteByte((uint8_t)((Dst_Addr)>>8));   
    W25X_ReadWriteByte((uint8_t)Dst_Addr);  
    W25X_CS_H();                    //取消片选
    if(bwait)W25X_Wait_Busy();      //等待擦除完成
} 


/******************************************************************************
/ 函数功能:读出一页数据,无限制
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void W25X_Read_Page(uint8_t * pBuffer,uint32_t PageAddr)
{
    uint32_t    ReadAddr;
    uint16_t    i;
    
    if(PageAddr < W25X_PAGE_NUM)
    {               
        ReadAddr = PageAddr *W25X_PAGE_SIZE;
        W25X_CS_L();  
        W25X_ReadWriteByte(W25X_ReadData);         //发送读取命令   
        W25X_ReadWriteByte((uint8_t)((ReadAddr)>>16));  //发送24bit地址    
        W25X_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
        W25X_ReadWriteByte((uint8_t)ReadAddr);   
        for(i=0;i<W25X_PAGE_SIZE;i++)
        { 
            pBuffer[i]=W25X_ReadWriteByte(0XFF);   //循环读数  
        }
        W25X_CS_H();    
    }
}


/******************************************************************************
/ 函数功能:写一页数据到指定的页,必须该页已被擦除!!!
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/ 
void W25X_Write_Page(uint8_t * pBuffer,uint32_t PageAddr)
{
    uint16_t    i; 
    uint32_t    WriteAddr;
    
    //打开写状态,并等待上次写操作完成
    W25X_Write_Enable();
    W25X_Wait_Busy();      //等待擦除完成
    
    //将数据写入FLASH
    WriteAddr =PageAddr* W25X_PAGE_SIZE;
    W25X_CS_L(); 
    W25X_ReadWriteByte(W25X_PageProgram);      //发送写页命令   
    W25X_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址    
    W25X_ReadWriteByte((uint8_t)((WriteAddr)>>8));   
    W25X_ReadWriteByte((uint8_t)WriteAddr);   
    for(i=0;i<W25X_PAGE_SIZE;i++)W25X_ReadWriteByte(pBuffer[i]);//循环写数  
    W25X_CS_H();
    //W25X_Wait_Busy();      //等待擦除完成
}


/******************************************************************************
/ 函数功能:在指定地址开始读取指定长度的数据(最大64KB字节),连续读取模式
/ 修改日期:2013/9/10 20:43:20
/ 输入参数:
/   pBuffer:数据存储区
/   ReadAddr:开始读取的地址(24bit)
/   NumByteToRead:要读取的字节数(最大65535)
/ 输出参数:none
/ 使用说明:none
******************************************************************************/ 
void W25X_Read_Data(uint8_t * pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
{ 
    uint16_t i;                 
    W25X_CS_L();  
    W25X_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    W25X_ReadWriteByte((uint8_t)((ReadAddr)>>16));  //发送24bit地址    
    W25X_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
    W25X_ReadWriteByte((uint8_t)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
    { 
        pBuffer[i]=W25X_ReadWriteByte(0XFF);   //循环读数  
    }
    W25X_CS_H();                 
}


/******************************************************************************
/ 函数功能:DMA方式高效读取一批数据,小余64KB即可
/ 修改日期:2013/9/11 9:06:27
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void W25X_DMARead_Data(uint8_t * pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{
    //判断DMA是否仍处于工作状态,若是则等待传输完成,
    if( (DMA1_Channel2->CCR & DMA_CCR1_EN) || (DMA1_Channel3->CCR & DMA_CCR1_EN) )
    {
        //while( !DMA_GetFlagStatus(DMA1_FLAG_TC2));//高效写法如下
        while( !(DMA1->ISR & DMA1_FLAG_TC2));       //等待传送完成
        sem_W25X_DMA_RxRdy  = true;                 //标记DMA接收数据信号
        W25X_CS_H();                                //结束片选
        __NOP();__NOP();__NOP();__NOP();            //短延时,使CS有足够的拉高时间    
    }
    
    //设置DMA数据载荷,并清DMA标记
    sem_W25X_DMA_Busy = true;               //标记为DMA忙
    DMA1_Channel3->CMAR = (uint32_t)(&W25X_TX_Byte);    //设置发送数据的源SRAM地址
    DMA1_Channel3->CNDTR= NumByteToRead;    //设置发送字节长度,发送SRAM地址不增加
    DMA1_Channel2->CMAR =(uint32_t)pBuffer; //设置接收数据个数
    DMA1_Channel2->CNDTR= NumByteToRead;    //设置接收数据的目标SRAM地址
    
    //发送前导字节
    W25X_CS_L();  
    W25X_ReadWriteByte(W25X_ReadData);         //发送读取命令   
    W25X_ReadWriteByte((uint8_t)((ReadAddr)>>16));  //发送24bit地址    
    W25X_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
    W25X_ReadWriteByte((uint8_t)ReadAddr);
    SPI1->DR ;                                //接送前读一次SPI1->DR,保证接收缓冲区为空
    
    //清DMA标记
    DMA_ClearFlag(DMA1_FLAG_GL3|DMA1_FLAG_TC3|DMA1_FLAG_HT3|DMA1_FLAG_TE2);
    DMA_ClearFlag(DMA1_FLAG_GL2|DMA1_FLAG_TC2|DMA1_FLAG_HT2|DMA1_FLAG_TE2);


    //启动DMA发送数据
    while ((SPI1->SR & SPI_I2S_FLAG_TXE) == (uint16_t)RESET);
    DMA_Cmd(DMA1_Channel3, ENABLE);
    DMA_Cmd(DMA1_Channel2, ENABLE);   


    //等待DMA传送数据完毕
    /*
    while( !DMA_GetFlagStatus(DMA1_FLAG_TC2));  //等待接收DMA的传输完成
    DMA_Cmd(DMA1_Channel3, DISABLE);            //关闭发送DMA
    DMA_Cmd(DMA1_Channel2, DISABLE);            //关闭接收DMA
    sem_W25X_DMA_RxRdy = true;                  //标记DMA接收数据信号
    W25X_CS_H();
    */
    
     
}


/******************************************************************************
/ 函数功能:读唯一ID号,8个字节
/ 修改日期:2013/9/11 9:38:49
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void W25X_Read_UID(uint8_t * pBuffer)
{ 
    uint8_t i;
    
    W25X_CS_L();       
    W25X_ReadWriteByte(W25X_ReadUniqueID);    
    W25X_ReadWriteByte(0x00);      
    W25X_ReadWriteByte(0x00);      
    W25X_ReadWriteByte(0x00);
    W25X_ReadWriteByte(0x00);        
    for(i=0;i<8;i++)
        pBuffer[i]=W25X_ReadWriteByte(0XFF);   //循环读数
    W25X_CS_H();
}


/******************************************************************************
/ 函数功能:DMA数据接收完毕中断
/ 修改日期:2013/9/11 9:38:47
/ 输入参数:none
/ 输出参数:none
/ 使用说明:none
******************************************************************************/
void DMA1_Channel2_IRQHandler(void)
{    
    //空读ISR状态
    DMA1->ISR;                          
    
    //关闭DMA通道
    //DMA_Cmd(DMA1_Channel2, DISABLE);//以下为等效写法
    //DMA_Cmd(DMA1_Channel3, DISABLE);//以下为等效写法
    DMA1_Channel2->CCR &= ~DMA_CCR1_EN;     //关闭DMA1_CH2
    DMA1_Channel3->CCR &= ~DMA_CCR1_EN;     //关闭DMA1_CH2
    
    //清DMA中断标记
    //DMA_ClearITPendingBit(DMA1_IT_GL2|DMA1_IT_TC2|DMA1_IT_HT2|DMA1_IT_TE2);//以下为等待模式
    DMA1->IFCR = DMA1_IT_GL2|DMA1_IT_TC2|DMA1_IT_HT2|DMA1_IT_TE2;


    //置信号量
    DMA_Cmd(DMA1_Channel3, DISABLE);            //关闭发送DMA
    DMA_Cmd(DMA1_Channel2, DISABLE);            //关闭接收DMA
    sem_W25X_DMA_Busy   = false;                //标记为DMA空闲
    sem_W25X_DMA_RxRdy  = true;                 //标记DMA接收数据信号
    W25X_CS_H();                                //结束片选      
}


4. 头文件W25Q64.h

/*********************************Copyright (c)*********************************
**                               
**                                 FIVE工作组
**
**---------------------------------File Info------------------------------------
** File Name:               w25q64.h
** Last modified Date:      2013/9/10 9:32:33
** Last Version:            V1.2   
** Description:             none
**
**------------------------------------------------------------------------------
** Created By:              wanxuncpx
** Created date:            2013/8/6 21:12:35
** Version:                 V1.2
** Descriptions:            none
**------------------------------------------------------------------------------
** HW_CMU:                  STM32F103ZET6
** Libraries:               STM32F10x_StdPeriph_Driver
** version                  V3.5
*******************************************************************************/

/******************************************************************************
更新说明:
******************************************************************************/


/******************************************************************************
*********************************  应 用 资 料 ********************************
*******************************************************************************
    1. W25Q64 拥有32 768个页,每页256个字节故有8MB的容量
    2. W25Q64 16个页为一个扇区
    3. W25Q64 256个页为一个Block
    2. 指令表

┌-----------------------┬--------┬-----------┬-----------┬-----------┬-----------┬-----------┐
|       令名             |  BYTE1  |   BYTE2    |    BYTE3   |   BYTE4    |   BYTE5    |   BYTE6    |
|                        | (CODE)  |            |            |            |            |            |
├-----------------------┼--------┼-----------┼-----------┼-----------┼-----------┼-----------┤
| Write Enable           |   06h   |            |            |            |            |            |
| 允许写状态寄存器       |   50h   |            |            |            |            |            |
| Write Disable          |   04h   |            |            |            |            |            |
| Read Status Register-1 |   05h   | S7–S0     |            |            |            |            |
| Read Status Register-2 |   35h   | S15–S8    |            |            |            |            |
| Write Status Register  |   01h   | S7–S0     | S15-S8     |            |            |            |
| Page Program           |   02h   | A23–A16   | A15–A8    |  A7–A0    |D7–D0      |            |
| Quad Page Program      |   32h   | A23–A16   | A15–A8    |  A7–A0    |D7–D0,..(3)|            |
| Sector Erase (4KB)     |   20h   | A23–A16   | A15–A8    |  A7–A0    |            |            |
| Block Erase (32KB)     |   52h   | A23–A16   | A15–A8    |  A7–A0    |            |            |
| Block Erase (64KB)     |   D8h   | A23–A16   | A15–A8    |  A7–A0    |            |            |
| Chip Erase             | C7h/60h |            |            |            |            |            |
| Erase / Program Suspend|   75h   |            |            |            |            |            |
| Erase / Program Resume |   7Ah   |            |            |            |            |            |
| Power-down             |   B9h   |            |            |            |            |            |
| 复位连续读模式         |   FFh   |    FFh     |            |            |            |            |
├-----------------------┼--------┼-----------┼-----------┼-----------┼-----------┼-----------┤
| Read Data              |   03h   |  A23-A16   | A15-A8     |  A7-A0     |  (D7-D0)   |            |
| Fast Read              |   0Bh   |  A23-A16   | A15-A8     |  A7-A0     |  dummy     |(D7-D0)     |
| Fast Read Dual Output  |   3Bh   |  A23-A16   | A15-A8     |  A7-A0     |  dummy     |(D7-D0, …) |
| Fast Read Quad Output  |   6Bh   |  A23-A16   | A15-A8     |  A7-A0     |  dummy     |(D7-D0, …) |
| Fast Read Dual I/O     |   BBh   |  A23-A8(2) | A7-A0,     |  M7-M0(2)  | (D7-D0,..) |            |
| Fast Read Quad I/O     |   EBh   |A23-A0,M7-M0|xxxx,D7-D0,.|(D7-D0,..)  |            |            |
| Word Read Quad I/O     |   E7h   |A23-A0,M7-M0|xx,D7-D0,.  |(D7-D0,..)  |            |            |
|Octal Word Read Quad I/O|   E3h   |A23-A0,M7-M0|(D7-D0,.)   |            |            |            |
| Set Burst with Wrap    |     77h |xxxxxx,W6-W4|            |            |            |            |
├-----------------------┼--------┼-----------┼-----------┼-----------┼-----------┼-----------┤
|Release Device ID       |  ABh    | dummy      |  dummy     |  dummy     |  (ID7-ID0) |            |
|ID                      |  90h    | dummy      |  dummy     |  00h       |  (MF7-MF0) | (ID7-ID0)  | 
|ID by Dual I/O          |  92h    | A23-A8     |A7-A0,M[7:0]|MF[7:0],ID[7:0] |        |            |   
|ID  by Quad I/O         |  94h    | A23-A0,M[7:0]| xxxx,(MF[7:0],ID[7:0])|(MF[7:0], ID[7:0], …)| ||   
|JEDEC ID                |  9Fh    | (MF7-MF0)  | (ID15-ID8) | (ID7-ID0)  |            |            |
|Read Unique ID          |  4Bh    | dummy      |  dummy     |  dummy     |  dummy     | (ID63-ID0) |
|Erase Security Registers|  44h    | A23–A16   |  A15–A8   |  A7–A0    |            |            |
|Program Security Registers|42h    | A23–A16   |  A15–A8   |  A7–A0    |  D7-D0     | D7-D0      |
|Read Security Registers |  48h    | A23–A16   |  A15–A8   |  A7–A0    |  dummy     | (D7-0)     |
└-----------------------┴--------┴-----------┴-----------┴-----------┴-----------┴-----------┘

******************************************************************************/

#ifndef _W25Q64_H_
#define _W25Q64_H_

/******************************************************************************
********************************* 文件引用部分 ********************************
******************************************************************************/
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_dma.h"

#include <string.h>
#include <stdbool.h>
#include <stdint.h>
/******************************************************************************
******************************* 系 统 参 数 定 义******************************
******************************************************************************/
/*---------------------* 
*     USART优先级定义
*----------------------*/
#define W25X_DMA_TC_PRIO    2           /* 中断优先级          */

/*---------------------* 
*      参数配置定义
*----------------------*/
#define W25X_PAGE_SIZE      256         /* 定义页大小          */
#define W25X_PAGE_NUM       32768       /* 定义页的个数        */
#define W25X_SECTOR_SIZE    4096        /* 每扇区的大小        */    
#define W25X_PAGES_PS       (W25X_SECTOR_SIZE/W25X_PAGE_SIZE)  /* 每扇区的页数 */

#define W25X_BUFF_NUM       2           /* 定义SRAM中缓冲个数  */
#define W25X_DUMMY_BYTE     0xFF        /* 空读字节定义        */

/******************************************************************************
********************************** 需自定义 ***********************************
****************************** 参数配置、引脚定义  ****************************
******************************************************************************/

/*---------------------*
*     本地硬件连接
*----------------------*/


/*---------------------*
*     常用指令定义
*----------------------*/
#define W25X_ReadStatusReg      0x05    //读状态寄存器1
#define W25X_ReadStatusReg2     0x35    //读状态寄存器2
#define W25X_WriteStatusReg     0x01    //写状态寄存器1
#define W25X_ReadUniqueID       0x4B    //读取唯一ID

#define W25X_WriteEnable        0x06    //写使能
#define W25X_WriteDisable       0x04    //写关闭

#define W25X_ReadData           0x03    //读数据
#define W25X_PageProgram        0x02    //写FLASH

#define W25X_ChipErase          0x60    //也可为0x60
#define W25X_SectorErase        0x20    //4KB擦除        


#define W25X_PowerDown          0xB9    //掉电,低功耗
#define W25X_ReleasePowerDown   0xAB    //恢复上电

/******************************************************************************
********************************* 参数宏定义 *********************************
******************************************************************************/
/*---------------------* 
*       参数定义
*----------------------*/
#define W25X_WP_DIS()        (GPIOB->BRR = GPIO_Pin_0)
#define W25X_WP_EN()         (GPIOB->BSRR  = GPIO_Pin_0)
#define W25X_CS_L()          (GPIOA->BRR  = GPIO_Pin_4)
#define W25X_CS_H()          (GPIOA->BSRR = GPIO_Pin_4)
/******************************************************************************
********************************* 数 据 声 明 *********************************
******************************************************************************/
/*---------------------* 
*      输出数据
*----------------------*/
extern uint8_t W25X_Buffer[W25X_SECTOR_SIZE];
extern volatile bool sem_W25X_DMA_Busy;         //用户只读
extern volatile bool sem_W25X_DMA_RxRdy;        //用户读取,清零

/******************************************************************************
********************************* 函 数 声 明 *********************************
******************************************************************************/
/*---------------------* 
*    输出函数
*----------------------*/
//初始化
extern void W25X_GPIO_Config(void);         //配置GPIO口
extern void W25X_Init(void);                //初始化SPI

//获取状态
extern uint8_t W25X_ReadSR(void);           //读取状态寄存器
extern uint16_t W25X_ReadID(void);          //读取ID号
extern void W25X_Wait_Busy(void);           //等待W25X直到空闲
extern bool W25X_Read_BusyState(void);      //读取W25X的忙状态(不等待)

//控制状态
extern void W25X_Write_Enable(void);        //写使能
extern void W25X_Write_Disable(void);       //写禁止
extern void SPI_Flash_PowerDown(void);      //掉电
extern void SPI_Flash_WakeUp(void);         //唤醒

//擦除相关(有等待选择)
extern void W25X_Erase_Chip(bool bwait);    //全片擦除,要等待写完成,需要21秒
extern void W25X_Erase_Sector(uint32_t Dst_Addr,bool bwait);    //扇区擦除,实际需要65ms

extern void W25X_Read_Data(uint8_t * pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);     //250us执行完毕
extern void W25X_DMARead_Data(uint8_t * pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);  //5us执行,75us后结束
extern void W25X_Read_Page(uint8_t * pBuffer,uint32_t PageAddr);    //读出一页,300us
extern void W25X_Write_Page(uint8_t * pBuffer,uint32_t PageAddr);   //写入一页,必先擦除,事先有些等待,600us
extern void W25X_Read_UID(uint8_t * pBuffer);   //读取W25X的唯一MAC,<5us

/******************************************************************************
***********************************   END  ************************************
******************************************************************************/
#endif










  



版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wanxuncpx/article/details/11557535

智能推荐

qt android 开机启动,Qt for android 开机自启动、开机黑屏处理-程序员宅基地

文章浏览阅读436次。1:开机自启动1.1 创建AndroidManifest.xml项目->构建->Build Android APK->Use Gradle->Create Templates->打钩Copy the Gra...->完成1.2 添加内容1.3 编译运行即可开机运行。2:开机黑屏处理一张logo页面运行2.1在工程目录下添加启动图片 Android/res/dra..._qt for android res

金字塔压力-面积-位移图像识别 外接矩形绘制(wcy)_ifwcy-程序员宅基地

文章浏览阅读382次。金字塔压力-面积-位移图像识别 外接矩形绘制(wcy)#include "stdafx.h"#include &lt;stdio.h&gt;#include &lt;stdlib.h&gt;#include &lt;opencv2/opencv.hpp&gt;#include &lt;opencv2/highgui/highgui.hpp&gt;#include &lt;opencv2..._ifwcy

javaassist的用法_javaassist用法classvisitor-程序员宅基地

文章浏览阅读687次。package com.dasenlin.baidu;import java.io.IOException;import javassist.CannotCompileException;import javassist.ClassPool;import javassist.CtClass;import javassist.CtConstructor;import javassist.CtField;import javassist.CtMethod;import javassist.._javaassist用法classvisitor

(11)结构型模式——享元-程序员宅基地

文章浏览阅读229次。结构型模式——享元(Flyweight)目录结构型模式——享元(Flyweight)问题背景解决方案效果缺陷相关模式实现问题背景当使用大量细粒度对象,需要提高系统性能时,考虑使用享元。现在我们要为一个RPG游戏设计装备系统,首先根据需求提取出装备类的属性,可能包括:标识符、名称、描述、装备类型、初始属性取值集合、穿戴条件、属性集合、耐久上限、当前耐久、最高强化等级、强化等级……我们会发现这些...

【KakaJSON手册】01_JSON转Model_01_基本用法-程序员宅基地

文章浏览阅读388次。在iOS开发中,后台返回的数据大多是JSON格式,对应地会被网络框架层解析成Swift中的Dictionary、Array。由于数据类型的复杂、字段的繁多,直接使用Dictionary、Array会比较麻烦,比如items[0]["user"]["name"]这样的使用方式,非常不友善,而且没有智能语法提示。所以很多时候会考虑将JSON转换成Model之后再进行操作,会友善很多,比如i..._kakajson

混合目标检测模块流程_byalarmjsonpictureseparate-程序员宅基地

文章浏览阅读591次。#include <stdio.h>#include <iostream>#include "Windows.h"#include "HCNetSDK.h"using namespace std;//时间解析宏定义#define GET_YEAR(_time_) (((_time_)>>26) + 2000) #define GET_MONTH(_time_) (((_time_)>>22) & 15)#defin._byalarmjsonpictureseparate

随便推点

设备树编译与反汇编-程序员宅基地

文章浏览阅读1.3k次。转载地址:https://blog.csdn.net/fight_onlyfor_you/article/details/740590291.编译最新的内核第一步 tar -xvf .........解压内核第二步 make ARCH=arm xxxx_deconfig第三步 make ARCH=arm CROSS_COMPILE2.如何编译设备树(DTS)d...

PHP $_FILES error码对应错误信息_php $_files error 5-程序员宅基地

文章浏览阅读2.1k次。0:上传成功1:上传文件超出php配置max_upload_filesize限制2:上传文件超出html表单限制3:文件只有部分被上传 4:没有上传文件6:没有找不到临时文件夹 7:文件写入失败(可能是文件权限不足)8:php文件上传扩展file没有打开_php $_files error 5

Android应用 手势密码的实现_android手势锁频界面应用-程序员宅基地

文章浏览阅读223次。http://blog.csdn.net/u013258802/article/details/52959513_android手势锁频界面应用

java断点续传的原理_java 断点为什么接口成功之后还会走-程序员宅基地

文章浏览阅读601次。转载,学习下:2008/05/06 11:33其实原理很简单,只是在 http 请求中加一个文件的偏移量而已,当然这还需要 server 支持这个头才行。 手头上刚好有一篇这样的文档。 (一)断点续传的原理 其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。 打个比方,浏览器请求服务器上的一个文时,所发_java 断点为什么接口成功之后还会走

servlet实现简单登录功能-基于测试驱动开发TDD_servlet测试登录-程序员宅基地

文章浏览阅读4.4k次,点赞8次,收藏3次。今天学习了测试驱动开发(TDD)模式,用这个方法写一个简单的前端登录功能(基于servlet&Jsp的登录功能-三层架构实现)不知道测试驱动开发,可以点这个:https://blog.csdn.net/qq_40542534/article/details/108473822编写service业务逻辑层的测试代码import org.junit.Test;public class UserServiceTest { //1、添加Junit测试 @Test pu_servlet测试登录

skynet mysql 携程_Skynet服务器框架(一) Linux下的安装和启动-程序员宅基地

文章浏览阅读249次。根据云风博客的描述,Skynet的核心功能就是解决一个问题:把一个符合规范的C模块,从动态库(so文件)中启动起来,绑定一个永不重复(即使模块退出)的数字id做为其handle。模块被称为服务(Service),服务间可以自由发送消息。每个模块可以向 Skynet 框架注册一个callback函数,用来接收发给它的消息;每个服务都是被一个个消息包驱动,当没有包到来的时候,..._skynet skynet.db.mysql 插入数据成功判定