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

所用芯片:cc2530f256+W25Q64JVSIQ

硬件连接图:

代码在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.仿真截图

6.求助

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

CC2530(SPI)驱动FLASH芯片W25Qxx相关推荐

  1. 低功耗SD\SPI NAND Flash芯片

    目前市面上很多SD/ SPI 的NAND FLASH芯片普遍面临功耗过高的情况,尤其是应用在穿戴类产品,包括手环,手表,耳机,心电贴等产品的时候,由于用户对于穿戴产品的待机时长要求很高,从而导致所有穿 ...

  2. SPI驱动XPT2046芯片读取其内部ADC信息笔记

    前言 这次来复习一下SPI以及ADC的有关知识,之前介绍到的磁编码器也是基于SPI协议通信才能读取其内部角度信息,ADC就更不用说了,在电压检测,电机驱动的方面都会涉及到采样量化编码的知识,趁此机会好 ...

  3. 【FLASH存储器系列六】SPI NOR FLASH芯片使用指导之二

  4. 【FLASH存储器系列五】SPI NOR FLASH芯片使用指导之一

  5. Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)

    一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...

  6. Linux spi驱动分析----SPI设备驱动(W25Q32BV)

    转载地址:http://blog.chinaunix.net/uid-25445243-id-4026974.html 一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它 ...

  7. STM32F103标准库开发---SPI实验---W25Qxx系列外部Flash芯片

    STM32F103标准库开发----目录 STM32F103标准库开发----SPI实验----基本原理 STM32F103标准库开发----SPI实验----底层驱动程序 W25Qxx全系列---- ...

  8. linux下spi flash驱动程序,关于spi flash芯片m25p80驱动以及其简单的mtd驱动分析

    项目中用到了spi flash芯片MX25L25635E,之前在uboot下简单分析了驱动代码,调试该flash擦除的bug,一直没有时间分 析内核中关于该芯片的驱动,以下是对该芯片驱动的一个简单分析 ...

  9. 使用 STM32 的 SPI 来读取外部 SPI FLASH 芯片(W25Qxx)

    SPI简介 SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口.是 Motorola 首先在其 MC68HCXX 系列处理器上定义的.SPI ...

最新文章

  1. C++知识点26——使用C++标准库(常用的泛型算法1)
  2. C++中const——由一个例子想到的
  3. python 访问 zookeeper
  4. python循环五要素_python常见单词在手,编程入门不愁
  5. 大型网站技术架构(一)大型网站架构演化
  6. 【EOJ Monthly 2019.02 - D】进制转换(思维,取模,高精度大数)
  7. HP刀片带外管理系统OA各功能实例示范
  8. python模拟gps定位_python 模拟 GPS, $GPRMC $GPRMC
  9. 手把手教大家如何优化长尾关键词
  10. 三极管放大电路基本原理
  11. 微信无法绑定手机号的解决方案
  12. 儿童手表语音物联卡贵吗?如何办理?
  13. aras innovator: 分类筛选如何做?
  14. 通过AWS创建无服务器的动态DNS系统
  15. 32位16进制转换为10进制数
  16. SQL Server 数据库表修改主键字段长度
  17. 全球人口突破80亿!免费分享全球人口分布数据
  18. HDU - 5965 扫雷(dp[好理解,但不那么优的题解])
  19. 与阿里云整个生态体系共同成长,更快更好的为房地产行业客户提供高价值的服务。...
  20. Python sql插入的简便写法

热门文章

  1. android改变系统语言,Android 9.0设置系统语言
  2. 二项式定理与杨辉三角
  3. 数字转换成大写人民币
  4. 计算机二级能加综合测评,2016综合测评加分细则.doc
  5. 网易资深安卓架构师:2021年Android常见面试题,面试必问
  6. Cesium 监听地图缩放
  7. 网络中的计算机如果加入家庭组,win7如何加入家庭组
  8. 如何做好软件系统的需求调研,七种武器让你轻松搞定
  9. 数据结构:八大常见数据结构
  10. Android app和系统应用实现截屏功能