由于之前用的存储芯片价格涨得离谱,需要寻找新的存储芯片,综合考虑找到了W25Qxx来测试替代的可行性。不用DMA的具体代码经过测试可用,但DMA收发测试不成功,仍在测试修改阶段。

所用芯片:cc2530f256+W25Q64JVSIQ

硬件连接图:

CC2530(SPI)驱动FLASH芯片W25Qxx-编程知识网

CC2530(SPI)驱动FLASH芯片W25Qxx-编程知识网

 代码在IAR环境下C语言编程,具体代码如下,仅供参考。

1.W25Qxx

1.1.W25Qxx.h

/********************************************
主控芯片:cc2530f256
模块型号:W25QXX全系列芯片
通讯方式:SPI串口通信
********************************************/
#ifndef _W25Qxx_H
#define _W25Qxx_H#include <iocc2530.h>
#include "hal_types.h"
#include "spi.h"/************W25QXX芯片识别码************/  
//uint16 W25QXX_TYPE; 	//W25QXX器件ID
#define W25QXX_SSZIE 4096	 	//W25QXX扇区大小
#define W25QXX_PSZIE 256	 	//W25QXX页大小
#define W25Q80 	     0XEF13	//容量1M字节
#define W25Q16 	     0XEF14	//容量2M字节
#define W25Q32       0XEF15	//容量4M字节
#define W25Q64       0XEF16	//容量8M字节
#define W25Q128	     0XEF17	//容量16M字节/*如果不是W25Q256,屏蔽下面define*/		
//#define W25Q256	     0XEF18	//容量32M字节/************W25QXX操作指令表************/
#define W25X_WriteEnable	  	0x06 
#define W25X_WriteDisable		  0x04 
#define W25X_ReadStatusReg_1	0x05 
#define W25X_ReadStatusReg_2	0x35 
#define W25X_ReadStatusReg_3	0x15 
#define W25X_WriteStatusReg_1	0x01 
#define W25X_WriteStatusReg_2	0x31 
#define W25X_WriteStatusReg_3	0x11
#define W25X_ReadData		    	0x03 
#define W25X_FastReadData	  	0x0B 
#define W25X_FastReadDual		  0x3B 
#define W25X_PageProgram		  0x02 
#define W25X_BlockErase			  0xD8 
#define W25X_SectorErase		  0x20 
#define W25X_ChipErase		  	0xC7 
#define W25X_PowerDown		  	0xB9 
#define W25X_ReleasePowerDown	0xAB 
#define W25X_DeviceID		    	0xAB 
#define W25X_ManufactDeviceID	0x90 
#define W25X_JedecDeviceID		0x9F 
#define W25X_Entry4ByteMode 	0xB7
#define W25X_Exit4ByteMode 		0xE9
#define W25X_SetReadParam			0xC0 
#define W25X_EnterQPIMode     0x38
#define W25X_ExitQPIMode      0xFF/************W25QXX操作函数表************/
uint16 W25QXX_ReadID(void);  	    	    		//读取FLASH ID
uint8 W25QXX_ReadSR(uint8 regno);       		//读取状态寄存器
void W25QXX_Init(void);							  			//初始化W25QXX
void W25QXX_Write_Enable(void);  		    		//写使能 
void W25QXX_Write_Disable(void);		    		//写保护
void W25QXX_Write_SR(uint8 regno,uint8 sr); //写状态寄存器
void W25QXX_Erase_Chip(void);    	  				//整片擦除
void W25QXX_Erase_Sector(uint32 Dst_Addr);	//扇区擦除
void W25QXX_Wait_Busy(void);           	    //等待空闲
void W25QXX_PowerDown(void);         	    	//进入掉电模式
void W25QXX_WAKEUP(void);		     	    			//唤醒
void W25QXX_Read(uint8* pBuffer,uint32 ReadAddr,uint16 NumByteToRead);   //读取flash
void W25QXX_Write(uint8* pBuffer,uint32 WriteAddr,uint16 NumByteToWrite);//写入flash
void W25QXX_Write_Page(uint8* pBuffer,uint32 WriteAddr,uint16 NumByteToWrite);
void W25QXX_Write_NoCheck(uint8* pBuffer,uint32 WriteAddr,uint16 NumByteToWrite);#endif 

1.2.W25Q64.c

#include "W25Qxx.h"
#include <hal_mcu.h>
#include "spi.h"
#include "bspDma.h"/********************************************
主控芯片:cc2530f256
模块型号:W25QXX全系列芯片//本次测试使用FLASH芯片W25Q64JVSIQ
通讯方式:SPI串口通信
********************************************/
#define  W25QXX_Enable() (W25QXX_CS=0)
#define  W25QXX_Disable() (W25QXX_CS=1)
//#define  SPI_DMA/*
void W25QXX_Enable(void) 
{W25QXX_Enable(); asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
}
*/
void W25QXX_Init(void)
{P1SEL &= ~0x10;		    // P1_4 is GPIO (SSN)P1DIR |= 0x10; 		   	// SSN is set as outputW25QXX_Disable();			//SPI FLASH不选中SPI_Init();		   		//初始化SPI
#ifdef  SPI_DMA//SPI_DMA_Config();
#endif
#ifdef W25Q256	W25QXX_Enable(); SPI_ReadWriteByte(W25X_Entry4ByteMode);//发送进入4字节地址模式指令W25QXX_Disable();
#endif
}  //W25QXX写使能	
//将WEL置位   
void W25QXX_Write_Enable(void)   
{W25QXX_Enable();                            //使能器件 SPI_ReadWriteByte(W25X_WriteEnable);W25QXX_Disable();    	   //取消片选   
} 
//W25QXX写禁止	
//将WEL清零  
void W25QXX_Write_Disable(void)   
{W25QXX_Enable();                            //使能器件  SPI_ReadWriteByte(W25X_WriteDisable);   W25QXX_Disable();                            //取消片选     	      
} 		//读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
//状态寄存器1:
//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:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
//状态寄存器2:
//BIT7  6   5   4   3   2   1   0
//SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
//状态寄存器3:
//BIT7      6    5    4   3   2   1   0
//HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
//regno:状态寄存器号,范:1~3
//返回值:状态寄存器值
//原文链接:https://blog.csdn.net/qq_37600027/article/details/102734498
uint8 W25QXX_ReadSR(uint8 regno)   
{  uint8 byte=0;  static uint8 command=0;  switch(regno){case 1:command=W25X_ReadStatusReg_1;    //读状态寄存器1指令break;case 2:command=W25X_ReadStatusReg_2;    //读状态寄存器2指令break;case 3:command=W25X_ReadStatusReg_3;    //读状态寄存器3指令break;default:command=W25X_ReadStatusReg_1;    break;}W25QXX_Enable();                              //使能器件 SPI_ReadWriteByte(command);            //发送读取状态寄存器命令    byte=SPI_ReadWriteByte(dummyByte);          //读取一个字节 W25QXX_Disable();                            //取消片选  return byte;   
} //写W25QXX状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
void W25QXX_Write_SR(uint8 regno,uint8 sr)   
{   uint8 command=0;switch(regno){case 1:command=W25X_WriteStatusReg_1;    //写状态寄存器1指令break;case 2:command=W25X_WriteStatusReg_2;    //写状态寄存器2指令break;case 3:command=W25X_WriteStatusReg_3;    //写状态寄存器3指令break;default:command=W25X_WriteStatusReg_1;    break;}W25QXX_Enable();                    //使能器件 SPI_ReadWriteByte(command);         //发送写状态寄存器命令 SPI_ReadWriteByte(sr);              //写入一个字节W25QXX_Disable();                   //取消片选     	      
}
//W25QXX读取器件ID,先发送读取ID命令,再发送0x000000,芯片返回两个字节ID
//返回值如下:				   
//0XEF13,表示芯片型号为W25Q80  
//0XEF14,表示芯片型号为W25Q16    
//0XEF15,表示芯片型号为W25Q32  
//0XEF16,表示芯片型号为W25Q64 
//0XEF17,表示芯片型号为W25Q128 
//0XEF18,表示芯片型号为W25Q256	 
uint16 W25QXX_ReadID(void)
{static uint16 Temp = 0;W25QXX_Enable();	SPI_ReadWriteByte(W25X_ManufactDeviceID);//发送读取ID命令0x90	SPI_ReadWriteByte(0x00);SPI_ReadWriteByte(0x00);SPI_ReadWriteByte(0x00);Temp |= SPI_ReadWriteByte(dummyByte)<<8;Temp |= SPI_ReadWriteByte(dummyByte);	 W25QXX_Disable();				    return Temp;
} //读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit或者32bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(uint8* pBuffer,uint32 ReadAddr,uint16 NumByteToRead)   
{W25QXX_Enable();                            //使能器件 SPI_ReadWriteByte(W25X_ReadData);//发送读取命令
#ifdef W25Q256                            //如果是【W25Q256】地址为4字节SPI_ReadWriteByte((uint8)((ReadAddr)>>24));//第4字节地址 
#endifSPI_ReadWriteByte((uint8)((ReadAddr)>>16));//发送24bit地址 ,//第3字节地址 SPI_ReadWriteByte((uint8)((ReadAddr)>>8)); //第2字节地址SPI_ReadWriteByte((uint8)ReadAddr);	       //第1字节地址#ifdef  SPI_DMASPI_DMA_RX(pBuffer,NumByteToRead);
#elsefor(uint16 i=0;i<NumByteToRead;i++){pBuffer[i] = SPI_ReadWriteByte(dummyByte);}
#endif  W25QXX_Disable();  				    	      
} /*//在指定地址开始写入指定长度的数据//不能自动擦除,不能自动换页//请确保该区域数据为0XFF//pBuffer:数据存储区//WriteAddr:开始读取的地址(24bit或者32bit)//NumByteToWrite:要写入的字节数,不允许超过该页的字节数//因为W25QXX不能跨页写入,指针会在页内不停循环
*/
void W25QXX_Write_Byte(uint8* pBuffer,uint32 WriteAddr,uint16 NumByteToWrite)  
{W25QXX_Enable();   SPI_ReadWriteByte(W25X_WriteEnable);
#ifdef W25Q256                            //如果是【W25Q256】地址为4字节SPI_ReadWriteByte((uint8)((WriteAddr)>>24));//第4字节地址 
#endifSPI_ReadWriteByte((uint8)((WriteAddr)>>16));//发送24bit地址 ,//第3字节地址 SPI_ReadWriteByte((uint8)((WriteAddr)>>8)); //第2字节地址SPI_ReadWriteByte((uint8)WriteAddr);	       //第1字节地址#ifdef  SPI_DMASPI_DMA_TX(pBuffer,NumByteToWrite);
#elsefor(uint16 i=0;i<NumByteToWrite;i++){SPI_ReadWriteByte(pBuffer[i]);}
#endif  W25QXX_Disable();  	
}//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit或者32bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
void W25QXX_Write_Page(uint8* pBuffer,uint32 WriteAddr,uint16 NumByteToWrite)
{W25QXX_Write_Enable();                  //SET WEL W25QXX_Enable();                            //使能器件 SPI_ReadWriteByte(W25X_PageProgram);
#ifdef W25Q256                            //如果是【W25Q256】地址为4字节SPI_ReadWriteByte((uint8)((WriteAddr)>>24));//第4字节地址 
#endifSPI_ReadWriteByte((uint8)((WriteAddr)>>16));//发送24bit地址 ,//第3字节地址 SPI_ReadWriteByte((uint8)((WriteAddr)>>8)); //第2字节地址SPI_ReadWriteByte((uint8)WriteAddr);	       //第1字节地址#ifdef  SPI_DMASPI_DMA_TX(pBuffer,NumByteToWrite);
#elsefor(uint16 i=0;i<NumByteToWrite;i++){SPI_ReadWriteByte(pBuffer[i]);}
#endif  W25QXX_Disable();                            //取消片选 W25QXX_Wait_Busy();					   //等待写入结束
} 
/*
//无检验写SPI FLASH 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//不能自动擦除,具有自动换页功能 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit或者32bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
*/
void W25QXX_Write_NoCheck(uint8* pBuffer,uint32 WriteAddr,uint16 NumByteToWrite)   
{ 			 		 uint16 pageremain;pageremain=W25QXX_PSZIE-WriteAddr%W25QXX_PSZIE; //单页剩余的字节数		 	    if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节//while(1)while(NumByteToWrite>0){	   W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);//写入一页数据if(NumByteToWrite==pageremain)break;    //写入结束了else //NumByteToWrite>pageremain{pBuffer+=pageremain;                  //数据指针递增WriteAddr+=pageremain;	              //地址指针递增NumByteToWrite-=pageremain;			      //减去已经写入了的字节数,计算剩余长度if(NumByteToWrite>W25QXX_PSZIE)pageremain=W25QXX_PSZIE; //一次可以写入256个字节,可以写一整页else pageremain=NumByteToWrite; 	    //不够256个字节了,不够写一整页}};	    
} //写SPI FLASH  
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit或者32bit)					
//NumByteToWrite:要写入的字节数(最大65535)   
uint8 W25QXX_BUFFER[4096];	 
void W25QXX_Write(uint8* pBuffer,uint32 WriteAddr,uint16 NumByteToWrite)   
{uint32 secpos;uint16 secoff;uint16 secremain;	   uint16 i;    uint8 * W25QXX_BUF;	W25QXX_BUF=W25QXX_BUFFER;	     secpos=WriteAddr/4096;//扇区地址  secoff=WriteAddr%4096;//在扇区内的偏移secremain=4096-secoff;//扇区剩余空间大小   if(NumByteToWrite<=secremain)secremain=NumByteToWrite;	//不大于剩余空间while(1) {	W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容for(i=0;i<secremain;i++)//校验数据{if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除  	  }if(i<secremain)//需要擦除{W25QXX_Erase_Sector(secpos);//擦除这个扇区for(i=0;i<secremain;i++)	   //复制{W25QXX_BUF[i+secoff]=pBuffer[i];	  }W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   if(NumByteToWrite==secremain)break;//写入结束了else//写入未结束{secpos++;//扇区地址增1secoff=0;//偏移位置为0 	 pBuffer+=secremain;  //指针偏移WriteAddr+=secremain;//写地址偏移	   NumByteToWrite-=secremain;				//字节数递减if(NumByteToWrite>4096)secremain=4096;	//下一个扇区还是写不完else secremain=NumByteToWrite;			//下一个扇区可以写完了}};	 
}//擦除整个芯片
//等待时间超长,W25Q256全片擦除45秒,换算下来擦除1M字节大概1.5秒
void W25QXX_Erase_Chip(void)   
{          W25QXX_Write_Enable();                  //SET WEL W25QXX_Wait_Busy();   W25QXX_Enable();                          	//使能器件 SPI_ReadWriteByte(W25X_ChipErase);   //发送片擦除命令W25QXX_Disable();                            //取消片选     	      W25QXX_Wait_Busy();   				          //等待芯片擦除结束
}  //擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
//W25QXX擦除扇区,地址需要对奇扇区大小,也就是扇区大小的整数倍
void W25QXX_Erase_Sector(uint32 Dst_Addr)   
{Dst_Addr*=W25QXX_SSZIE;W25QXX_Write_Enable();                     //SET WEL 	 W25QXX_Wait_Busy();      				           //等待芯片空闲W25QXX_Enable();                           //使能器件  	SPI_ReadWriteByte(W25X_SectorErase);       //发送扇区擦除指令
#ifdef W25Q256                               //如果是【W25Q256】地址为4字节SPI_ReadWriteByte((uint8)((Dst_Addr)>>24));//第4字节地址 
#endifSPI_ReadWriteByte((uint8)((Dst_Addr)>>16));//发送24bit地址 ,//第3字节地址 SPI_ReadWriteByte((uint8)((Dst_Addr)>>8)); //第2字节地址SPI_ReadWriteByte((uint8)Dst_Addr);	       //第1字节地址W25QXX_Disable();                          //取消片选     	      W25QXX_Wait_Busy();   				             //等待芯片空闲,擦除完成
} //等待空闲
void W25QXX_Wait_Busy(void)   
{   while((W25QXX_ReadSR(1)&0x01)==0x01);	  // 等待BUSY位清空
}//进入掉电模式
void W25QXX_PowerDown(void)   
{W25QXX_Enable();                        //使能器件SPI_ReadWriteByte(W25X_PowerDown);W25QXX_Disable();                       //取消片选     	//halMcuWaitUs(3);                        //等待TPD  
} //唤醒
void W25QXX_WAKEUP(void)   
{ W25QXX_Enable();                           //使能器件  SPI_ReadWriteByte(W25X_ReleasePowerDown);  //  send W25X_PowerDown command 0xAB W25QXX_Disable();                          //取消片选     	      //halMcuWaitUs(3);                           //等待TRES1
}   

2.spi

2.1.spi.h

#ifndef _SPI_H
#define _SPI_H#include <iocc2530.h>
#include "hal_types.h"#define dummyByte 0XFF
/************W25QXX的SPI配置************/
#define W25QXX_CS P1_4void SPI_Init(void);			 //SPI INIT
void SPI_SetSpeed(uint32 baud_rate); //SET SPI baud rate
uint8 SPI_ReadWriteByte(uint8 TxData);//SPI reads and writes one byte
void SPI_ReadData(uint8* rxBufferMaster,uint16 len);
void SPI_WriteData(uint8* txBufferMaster,uint16 len);
#endif

2.2. spi.c

#include "spi.h"
#include "stdio.h"
#include "bspDma.h"
#include "hal_mcu.h"bool DataReceived = 0;
bool DataTransmitted = 0;/******************************************
*    CC253O 32M*
*----------------------------------------*
*  baud    U1BAUD         U1GCR
*  240     59             6              *
*  4800    59             7              *
*  9600    59             8              *
*  14400   216            8              *
*  19200   59             9              *
*  28800   216            9              *
*  38400   59             10             *
*  57600   216            10             *
*  76800   59             11             *
*  115200  216            11             *
*  23040   216            12             *
*****************************************/
void SPI_SetSpeed(uint32 baud_rate)
{//U1GCR and U1BAUD set baud rate.switch(baud_rate){case 9600:{U1BAUD |= 59; // BAUD_M = 89U1GCR |= 8; // BAUD_E = 8 }break;case 19200:{U1BAUD |= 59; // BAUD_M = 89U1GCR |= 9; // BAUD_E = 9 }break;case 115200:{U1BAUD |= 216; // BAUD_M = 534U1GCR |= 11; // BAUD_E = 17 }break;default://115200{U1BAUD |= 216; // BAUD_M = 216U1GCR |= 11; // BAUD_E = 17 }   break;}
}/*--------------------------------------------------------------------------------
--- Master ------------ Slave
---CC2530---      -----W25Q64---
| | | |
|P1_4 SSN |--------->|  1-/CS  |
| | | |
|P1_5 SCK |--------->|  6-CLK  |
| | | |
|P1_6 MOSI|--------->|5-DI(IO0)|
| | | |
|P1_7 MISO|<---------|2-DO(IO1)|
| | | |
-------------      -------------
--------------------------------------------------------------------------------*/ 
/*---------------------------------------------------------------------------------
UxGCR.CPOL 0 SCK low when idle
1 SCK high when idle
UxGCR.CPHA 0 Data centered on first edge of SCK period
1 Data centered on second edge of SCK period 
---- ---- ---- ---- ---- ---- ---- ---- ----
| | | | | | | | | | | | | | | | | |
---- ---- ---- ---- ---- ---- ---- ---- ---- ----
----------------------------------------------------------------------------
| MSB | | | | | | | | LSB |
----------------------------------------------------------------------------
---------------------------------------------------------------------------------*/ 
void SPI_Init(void)//初始化SPI
{/* Set UART1 I/O to alternate 2 location on P1 pins. */PERCFG |= 0x02; // PERCFG.U1CFG = 1P1SEL |= 0xE0; // P1_7, P1_6, and P1_5 are peripherals//P1SEL &= ~0x10; // P1_4 is GPIO (SSN)//P1DIR |= 0x10; // SSN is set as output//P1_4 = 1;/* Give UART1 priority over Timer3. */P2SEL &= ~0x20;  /* PRI2P1 */// Master Mode/* Mode select UART1 SPI Mode as master. *///U1CSR = 0; U1CSR &= ~0xA0; //SPI Master Mode/* Setup for 115200 baud. */U1GCR = 11; U1BAUD = 216; //SPI_SetSpeed(115200);// Set baud rate// Configure phase, polarity, and bit orderU1GCR &= ~0xC0; // CPOL = CPHA = 0U1GCR |= 0x20; // ORDER = 1,U1GCR |= BV(5); /* Set bit order to MSB *//* When SPI config is complete, enable it. *///U1CSR |= 0x40; //U1MODE, U1RE, U1SLAVE, U1FE, U1ERR, U1RX_BYTE, U1TX_BYTE, U1ACTIVE
}uint8 SPI_ReadWriteByte(uint8 TxData)
{uint8 temp=0;U1DBUF = TxData;while (!U1TX_BYTE);//Wait for U1TX_BYTE to be asserted,send completiontemp = U1DBUF;U1TX_BYTE = 0;     //Clear send completion flag//while (U1ACTIVE); // Wait for U1ACTIVE to be de-asserted (U1DBUF can be read)halMcuWaitMs(10);return temp;
}void SPI_WriteData(uint8* txBufferMaster,uint16 len)
{for (uint16 i = 0; i <= len; i++){U1DBUF = txBufferMaster[i];while (!U1TX_BYTE);U1TX_BYTE = 0;halMcuWaitMs(10);}
}void SPI_ReadData(uint8* rxBufferMaster,uint16 len)
{for (uint16 i = 0; i <= len; i++){U1DBUF = dummyByte;//SPI主机必须发送一个空字节(dummy byte,一般是0xFF,也可以是其他数值,用来启动数据传输,无论何值,最终目的都是产生时钟信号而已)来触发从机的数据传输while (!U1TX_BYTE);rxBufferMaster[i] = U1DBUF;U1TX_BYTE = 0;halMcuWaitMs(10);}
}

3.Dma

3.1.bspDma.h

#ifndef __BSP_DMA_H__
#define __BSP_DMA_H__#include "hal_types.h"// Define basic data types:
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned char UINT8;
// Define data
#pragma bitfields = reversed
typedef struct {uint8 SRCADDRH;       //源地址高字节uint8 SRCADDRL;       //源地址低字节uint8 DESTADDRH;       //目的地址高字节uint8 DESTADDRL;       //目的地址低字节uint8 VLEN:3;         //变成传输模式uint8 LENH:5;         //传输长度高字节uint8 LENL:8;         //传输长度低字节uint8 WORDSIZE:1;     //字节传输或字传输uint8 TMODE:2;        //传输模式uint8 TRIG:5;         //触发时间选择uint8 SRCINC:2;       //源地址增量方式选择uint8 DESTINC:2;      //目的地址增量方式选择uint8 IRQMASK:1;      //中断屏蔽位uint8 M8:1;           //字节传输模式时用,8或7bit传输,仅仅适合在字节传输模式下uint8 PRIORITY:2;     //优先级选择
}DMA_DESC;
#pragma bitfields = default#define DMA_IRQ_CH0  1<<0
#define DMA_IRQ_CH1  1<<1
#define DMA_IRQ_CH2  1<<2
#define DMA_IRQ_CH3  1<<3
#define DMA_IRQ_CH4  1<<4#define DMA_AMOUNT             4
#define DMA_CH0                1<<0
#define DMA_CH1                1<<1
#define DMA_CH2                1<<2
#define DMA_CH3                1<<3
#define DMA_CH4                1<<4#define DMA_VLEN_USE_LEN             0x00 // Use LEN for transfer count
#define DMA_VLEN_USE_LEN1            0x01 // Transfer number of bytes commanded by LEN + 1#define DMA_WORDSIZE_BYTE            0x00 // Transfer a byte at a time
#define DMA_WORDSIZE_WORD            0x01 // Transfer a 16-bit word at a time#define DMA_TMODE_SINGLE             0x00 // Transfer a single byte/word after each DMA trigger           单个传输模式
#define DMA_TMODE_BLOCK              0x01 // Transfer block of data (length len) after each DMA trigger   块传输模式
#define DMA_TMODE_SINGLE_REPEATED    0x02 // Transfer single byte/word (after len transfers, rearm DMA)   重复单个传输模式
#define DMA_TMODE_BLOCK_REPEATED     0x03 // Transfer block of data (after len transfers, rearm DMA)      重复块传输模式#define DMA_TRIG_NONE                0    // No trigger, setting DMAREQ.DMAREQx bit starts transfer       无触发
#define DMA_TRIG_URX0                0x10   /* USART0 RX complete. */
#define DMA_TRIG_UTX0                0x0F   /* USART0 TX complete. */
#define DMA_TRIG_URX1                0x10   /* USART1 RX complete. */
#define DMA_TRIG_UTX1                0x11   /* USART1 TX complete. */
#define DMA_SRCINC_0                 0x00 // Increment source pointer by 0 bytes/words after each transfer源地址递增0字节/字
#define DMA_SRCINC_1                 0x01 // Increment source pointer by 1 bytes/words after each transfer源地址递增1字节/字
#define DMA_DESTINC_0                0x00 // Increment source pointer by 0 bytes/words after each transfer源地址递增0字节/字
#define DMA_DESTINC_1                0x01 // Increment destination pointer by 1 bytes/words after each transfer目标地址递增1字节/字#define DMA_IRQMASK_DISABLE          0x00 // Disable interrupt generation                                 通道的中断屏蔽
#define DMA_IRQMASK_ENABLE           0x01 // Enable interrupt generation upon DMA channel done            通道的中断使能#define DMA_M8_USE_8_BITS            0x00 // Use all 8 bits for transfer count                            采用所有8位作为传输长度
#define DMA_M8_USE_7_BITS            0x01 // Use 7 LSB for transfer count                                 采用字节地7位作为传输长度#define DMA_PRI_LOW                  0x00 // Low, CPU has priority                                        低优先级,CPU优先
#define DMA_PRI_GUARANTEED           0x01 // Guaranteed, DMA at least every second try                    保证级,DMA至少在每秒一次的尝试中优先
#define DMA_PRI_HIGH                 0x02 // High, DMA has priority                                       高优先级,DMA优先
#define DMA_PRI_ABSOLUTE             0x03 // Highest, DMA has priority. Reserved for DMA port access.     #define ABORT 0x80 // Bit mask for DMA abort bit (DMAARM)
#define DMAARM_DMAARM0               0x01// Bit mask for DMA arm channel 0 bit (DMAARM)
#define DMAARM_DMAARM1               0x02
#define DMAREQ_DMAREQ0               0x01 
#define DMAREQ_DMAREQ1               0x02 
#define DMAIRQ_DMAIF0                0x01// Bit mask for DMA channel 0 interrupt flag (DMAIRQ)
#define DMAIRQ_DMAIF1                0x02#define NOP()  asm("NOP")
// Define macro for splitting 16 bits in 2 x 8 bits:
#define HIBYTE(a) (BYTE) ((WORD)(a) >> 8)
#define LOBYTE(a) (BYTE) (WORD)(a)
#define SET_WORD(regH, regL, word) \do { \(regH) = HIBYTE( word ); \(regL) = LOBYTE( word ); \} while(0) void SPI_DMA_RX(uint8 *rx_buf,uint16 NumByte);
void SPI_DMA_TX(uint8 *tx_buf,uint16 NumByte);
#endif

3.2.bspDma.c

#include "hal_types.h"
#include "bspDma.h"
#include "ioCC2530.h"
#include "hal_dma.h"DMA_DESC __xdata dmaConfigTx;// DMA进入工作模式通道0
DMA_DESC __xdata dmaConfigRx;// DMA进入工作模式通道1void dmaInterruptEnable(void)
{DMAIE = 1;
}void dmaInterruptDisable(void)
{DMAIE = 0;
}/*
//DMA 
由于SSN片选信号必须由应用程序进行置位和复位,并且不由USART(主模式)处理,
因此只有在多个字节应连续传输的情况下,在主模式下使用DMA才有意义,
而不是在每个字节传输之间拉高SSN。
*/
void SPI_DMA_TX(uint8 *tx_buf,uint16 NumByte)
{dmaConfigTx.SRCADDRH  = ((uint16)&tx_buf >> 8) & 0x00FF;    // load HIGH address of the dma sourcedmaConfigTx.SRCADDRL  = (uint16)&tx_buf & 0x00FF;               // load LOW address of the dma sourcedmaConfigTx.DESTADDRH = ((uint16)&X_U1DBUF >> 8) & 0x00FF;      // load HIGH address of the dma distanationdmaConfigTx.DESTADDRL = (uint16)&X_U1DBUF & 0x00FF;                  // load LOW address of the dma distanation//SET_WORD(dmaConfigTx.SRCADDRH, dmaConfigTx.SRCADDRL, &tx_buf);//SET_WORD(dmaConfigTx.DESTADDRH, dmaConfigTx.DESTADDRL, &X_U1DBUF);dmaConfigTx.VLEN = DMA_VLEN_USE_LEN; // Transfer number of bytes commanded by nSET_WORD(dmaConfigTx.LENH, dmaConfigTx.LENL, NumByte); //LEN = nmaxdmaConfigTx.WORDSIZE = DMA_WORDSIZE_BYTE; // Each transfer is 8 bitsdmaConfigTx.TRIG = DMA_TRIG_UTX1; // Use UTX1 trigger//dmaConfigTx.TRIG      = DMA_TRIG_NONE;                    //无触发模式,即无触发源dmaConfigTx.TMODE = DMA_TMODE_BLOCK;// Transfer block of data (length len) after each DMA triggerdmaConfigTx.SRCINC = DMA_SRCINC_1; // Increase source addr. by 1 between transfersdmaConfigTx.DESTINC = DMA_DESTINC_1; // Keep the same dest. addr. for all transfersdmaConfigTx.IRQMASK = DMA_IRQMASK_ENABLE; // Allow IRCON.DMAIF to be asserted when the transfer count is reacheddmaConfigTx.M8 = DMA_M8_USE_8_BITS; // Use all 8 bits of first byte in source data to determine the transfer countdmaConfigTx.PRIORITY = DMA_PRI_HIGH; // DMA memory access has high priorityDMA0CFGH = ((uint16)&dmaConfigTx >> 8) & 0x00FF;DMA0CFGL = (uint16)&dmaConfigTx & 0x00FF;// Save pointer to the DMA config. struct into DMA ch. 0 config. registers//SET_WORD(DMA0CFGH, DMA0CFGL, &dmaConfigTx);// 1. Clear interrupt flagsDMAIF = 0;DMAIRQ &= ~DMAARM_DMAARM0;// 2. Set individual interrupt enable bit in the peripherals SFR, if any No flag for the DMA (Set in the DMA struct (IRQMASK = 1))//dmaConfigTx.IRQMASK = DMA_IRQMASK_ENABLE;// 3. Set the corresponding individual, interrupt enable bit in the IEN0, IEN1, or IEN2 registers to 1DMAIE = 1;// 4. Enable global interruptEA = 1;/*//DMA进入工作模式通道0DMAARM |= DMAARM_DMAARM0;//为了任何DMA传输能够在该通道上发生,该位必须置1,对于非重复传输模式,一旦完成传送,该位自动清0//一个通道准备工作(即获得配置数据)的时间需要9个系统时钟NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP(); //DMA通道0传送请求,即触发DMA传送DMAREQ |= DMAREQ_DMAREQ0;//设置为1,激活DMA通道0(与一个触发事件具有相同的效果),当DMA传输开始清除该位//等待DMA通道0传送完毕for (; !(DMAIRQ & DMAIRQ_DMAIF0););//当DMA通道0传送完成,DMAIRQ:DMAIF1位置1,与上DMAIRQ_DMAIF1(0x01),取非后为0退出循环//清除中断标志DMAIRQ = ~DMAIRQ_DMAIF0;*/
}void SPI_DMA_RX(uint8 *rx_buf,uint16 NumByte)
{SET_WORD(dmaConfigRx.SRCADDRH, dmaConfigRx.SRCADDRL, &X_U1DBUF);SET_WORD(dmaConfigRx.DESTADDRH, dmaConfigRx.DESTADDRL, (uint16)rx_buf);dmaConfigRx.VLEN = DMA_VLEN_USE_LEN; // Transfer number of bytes commanded by nSET_WORD(dmaConfigRx.LENH, dmaConfigRx.LENL, NumByte); //LEN = nmaxdmaConfigRx.WORDSIZE = DMA_WORDSIZE_BYTE; // Each transfer is 8 bitsdmaConfigRx.TRIG = DMA_TRIG_URX1; // Use URX1 trigger//dmaConfigRx.TRIG      = DMA_TRIG_NONE;                    //无触发模式,即无触发源dmaConfigRx.TMODE = DMA_TMODE_BLOCK; // One byte transferred per trigger eventdmaConfigRx.SRCINC = DMA_SRCINC_1; // Keep the same source addr. for all transfersdmaConfigRx.DESTINC = DMA_DESTINC_1; // Increase dest. addr. by 1 between transfersdmaConfigRx.IRQMASK = DMA_IRQMASK_ENABLE; // Allow IRCON.DMAIF to be asserted when the transfer// count is reacheddmaConfigRx.M8 = DMA_M8_USE_8_BITS; // Use all 8 bits of first byte in source data to// determine the transfer countdmaConfigRx.PRIORITY = DMA_PRI_HIGH; // DMA memory access has high priority// Save pointer to the DMA config. struct into DMA ch. 0 config. registers//SET_WORD(DMA1CFGH, DMA1CFGL, &dmaConfigRx);DMA1CFGH = ((uint16)&dmaConfigRx >> 8) & 0x00FF;DMA1CFGL = (uint16)&dmaConfigRx & 0x00FF;// Enable UARTx RX//U1CSR |= 0x40;  // 1. Clear interrupt flagsDMAIF = 0;DMAIRQ &= ~DMAARM_DMAARM1;// 2. Set individual interrupt enable bit in the peripherals SFR, if any No flag for the DMA (Set in the DMA struct (IRQMASK = 1))//dmaConfigRx.IRQMASK = DMA_IRQMASK_ENABLE;// 3. Set the corresponding individual, interrupt enable bit in the IEN0, IEN1, or IEN2 registers to 1DMAIE = 1;// 4. Enable global interruptEA = 1; /*//DMA进入工作模式通道1DMAARM |= DMAARM_DMAARM1;//为了任何DMA传输能够在该通道上发生,该位必须置1,对于非重复传输模式,一旦完成传送,该位自动清0//一个通道准备工作(即获得配置数据)的时间需要9个系统时钟NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP(); //DMA通道1传送请求,即触发DMA传送DMAREQ |= DMAREQ_DMAREQ1;//设置为1,激活DMA通道1(与一个触发事件具有相同的效果),当DMA传输开始清除该位//等待DMA通道1传送完毕for (; !(DMAIRQ & DMAIRQ_DMAIF1););//当DMA通道1传送完成,DMAIRQ:DMAIF1位置1,与上DMAIRQ_DMAIF1(0x01),取非后为0退出循环//清除中断标志DMAIRQ = ~DMAIRQ_DMAIF1;*/
}#pragma vector=DMA_VECTOR
__interrupt void dma_IRQ(void)
{DMAIF = 0; // Clear the CPU DMA interrupt flagDMAIRQ &= ~DMAIRQ_DMAIF0;DMAIRQ &= ~DMAIRQ_DMAIF1;
}

4.main.c

#include <iocc2530.h>
#include "hal_types.h"
#include "hal_board.h"
#include "hal_mcu.h"
#include "string.h"
#include "W25Qxx.h"#define LED P2_0
#define FLASH_SIZE 100 //FLASH 大小为8M字节
uint16 IDTYPE = 0;//W25QXX器件ID//此数据是用来复制到内存的其他区域
uint8 data[] = "W25Q64 R&W test!";
//数据长度
#define DATA_AMOUNT sizeof(data)
//用来保存复制来的数据区域
uint8 copy[DATA_AMOUNT];void InitClockTo32M(void)
{   CLKCONCMD &= ~0x40;              //设置系统时钟源为 32MHZ晶振while(!(CLKCONSTA & 0x40));      //等待晶振稳定CLKCONCMD &= ~0x47;              //设置系统主时钟频率为 32MHZ
}
void InitLed(void)
{P2DIR |= 0x01;P2_0   = 0;
}void main(void)
{InitClockTo32M();InitLed();W25QXX_Init();IDTYPE = W25QXX_ReadID();//IDTYPE = 0xEF16while(1){W25QXX_Write_Page(data,FLASH_SIZE,DATA_AMOUNT);//Test successfulLED = ~LED;halMcuWaitMs(1000);W25QXX_Read(copy,FLASH_SIZE,DATA_AMOUNT);//W25QXX_Read_ByteLED = ~LED;halMcuWaitMs(1000);memset(copy,0,DATA_AMOUNT);}
}

5.仿真截图

CC2530(SPI)驱动FLASH芯片W25Qxx-编程知识网

 6.求助

如果有成功用DMA读写数据的网友,希望您能在评论区给与我一些帮助。