打印
[Cortex-M0技术交流]

菜鸟学习M0第十七帖——SPI

[复制链接]
3875|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lixiaoxu2meng|  楼主 | 2011-9-24 11:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本工程实现SPI读写W25Q16BV 工程也参考了 官方的固件库及三心前辈的帖子 声明并表示感谢
main函数
/*---------------------------------------------------------------------------------------------------------*/
/* */
/* Copyright(c) 2011 Nuvoton Technology Corp. All rights reserved. */
/* */
/*---------------------------------------------------------------------------------------------------------*/
#include "includes.h" //包含所需的头文件
/*************************************************************************************
** Function name: main
** Descriptions:
** input parameters: 无
** output parameters: 无
** Returned value: 无
*************************************************************************************/
int main (void)
{
uint8_t WriteBuffer[256] = {0};
uint8_t ReadBuffer[256] = {0};
uint16_t i, id = 1;
Set_System(); //封装一些初始化模块
value = 4;
for(i=0;i<256;i++) //初始化数组
{
WriteBuffer[i] = i;
}

SPI_WaitReady(); //W25Q16BV忙状态检查
SPI_SectorErase(0x0000); //扇区擦除
SPI_WaitReady(); //W25Q16BV忙状态检查
SPI_PageProgram(WriteBuffer,0,256); //W25Q16BV按页编程函数

SPI_WaitReady(); //W25Q16BV忙状态检查
SPI_ReadData(ReadBuffer,0,256); //W25Q16BV读数据函数

for(i=0;i<256;i++) //将读出来的数据与写进去的数据进行比较
{
if(WriteBuffer[i] != ReadBuffer[i])
{
id = 0;
break;
}
}

if(id ==1)
value = ReadBuffer[125];
else
value = 88;
while(1)
{
}
}


hw_config函数
#include "includes.h" //包含所需的头文件
#define Enable_SPI_CS DrvGPIO_ClrBit(E_GPA,14)
#define DISABLE_SPI_CS DrvGPIO_SetBit(E_GPA,14)
/*************************************************************************************
** Function name: Set_System
** Descriptions: 封装一些初始化模块
** input parameters: count
** output parameters: 无
** Returned value: 无
*************************************************************************************/
void Set_System(void)
{
RCC_Configuration(); //配置系统时钟

GPIO_Configuration(); //配置GPIO

TIMER_Configuration(); //配置TIMER

SPI_Configuration(); //配置SPI1
}
/*************************************************************************************
** Function name: RCC_Configuration
** Descriptions: 系统时钟源设置
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void RCC_Configuration(void)
{
UNLOCKREG(); // 对写保护位操作时 需要一次向0x50000 0100写入 0x59,0x16,0x88,
DrvSYS_SetOscCtrl(E_SYS_XTL12M, 1);// 与其 SYSCLK->PWRCON.XTL12M_EN = 1; 等同
// PWRCON寄存器(这些寄存器在上电复位到用户解锁定之前是锁定的)除了 BIT[6]位其他位都受写保护
// 解除这些需要向 0x50000 0100写入 0x59,0x16,0x88,
// 令PWRCON寄存器的BITP[0]为1(即设定12M外部晶振)
delay_ms(100); // while (DrvSYS_GetChipClockSourceStatus(E_SYS_XTL12M) != 1);//等待外部12MHZ晶振就绪
LOCKREG(); // 向“0x5000_0100”写入任何值,就可以重锁保护寄存器
}
/*************************************************************************************
** Function name: GPIO_Configuration
** Descriptions: GPIO配置
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void GPIO_Configuration()
{
//DrvGPIO_Open( E_GPA, 14, E_IO_OUTPUT );//片选

DrvGPIO_Open( E_GPA, 2, E_IO_OUTPUT );//数码管段选
DrvGPIO_Open( E_GPA, 3, E_IO_OUTPUT );
DrvGPIO_Open( E_GPA, 4, E_IO_OUTPUT );
DrvGPIO_Open( E_GPA, 5, E_IO_OUTPUT );
DrvGPIO_Open( E_GPA, 6, E_IO_OUTPUT );
DrvGPIO_Open( E_GPA, 7, E_IO_OUTPUT );
DrvGPIO_Open( E_GPA, 8, E_IO_OUTPUT );
DrvGPIO_Open( E_GPA, 9, E_IO_OUTPUT );
DrvGPIO_Open( E_GPC, 14, E_IO_OUTPUT );//数码管位选
DrvGPIO_Open( E_GPC, 15, E_IO_OUTPUT );
DrvGPIO_Open( E_GPC, 6, E_IO_OUTPUT );
DrvGPIO_Open( E_GPC, 7, E_IO_OUTPUT );
}
/*************************************************************************************
** Function name: TIMER_Configuration
** Descriptions: TIMER配置
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void TIMER_Configuration()
{
DrvTIMER_Init(); //初始化定时器

DrvSYS_SelectIPClockSource(E_SYS_TMR0_CLKSRC,0); //使用外设时注意必须设置该外设的时钟 设置TIMER0的时钟源为外部12MHZ

DrvTIMER_Open(E_TMR0,1000,E_PERIODIC_MODE); //设定定时器timer0的tick周期,并且启动定时器:定时器通道 TMR0 每秒1000次 周期模式

DrvTIMER_SetTimerEvent(E_TMR0,5,(TIMER_CALLBACK) Timer0_Callback,0); //安装一个定时处理事件到 timer0通道

DrvTIMER_EnableInt(E_TMR0); //使能定时器中断 //TIMER0->TCSR.IE = 1

DrvTIMER_Start(E_TMR0); //定时器timer0开始计数 //TIMER0->TCSR.CEN = 1;
}
/*************************************************************************************
** Function name: Timer0_Callback
** Descriptions: 定时处理事件,LED动态扫描
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void Timer0_Callback (void)
{
static uint8_t count= 0;
static uint8_t i,xx[4];
uint32_t data;
uint16_t ValueBuff ;
ValueBuff = value;
count++;
if(count >= 5)
count = 1;
for(i=0;i<4;i++)
{
xx[i] = ValueBuff%10;
ValueBuff = ValueBuff/10;
}
switch(count)
{
case 1:
DrvGPIO_SetBit(E_GPC,14);
DrvGPIO_ClrBit(E_GPC,15);
DrvGPIO_ClrBit(E_GPC,6);
DrvGPIO_ClrBit(E_GPC,7);
data = Table[xx[0]]<<2;
GPIOA->DOUT = data;
break;
case 2:
DrvGPIO_SetBit(E_GPC,15);
DrvGPIO_ClrBit(E_GPC,14);
DrvGPIO_ClrBit(E_GPC,6);
DrvGPIO_ClrBit(E_GPC,7);
data = Table[xx[1]]<<2;
GPIOA->DOUT = data;
break;
case 3:
DrvGPIO_SetBit(E_GPC,7);
DrvGPIO_ClrBit(E_GPC,14);
DrvGPIO_ClrBit(E_GPC,15);
DrvGPIO_ClrBit(E_GPC,6);
data = Table[xx[2]]<<2;
GPIOA->DOUT = data;
break;
case 4:
DrvGPIO_SetBit(E_GPC,6);
DrvGPIO_ClrBit(E_GPC,14);
DrvGPIO_ClrBit(E_GPC,15);
DrvGPIO_ClrBit(E_GPC,7);
data = Table[xx[3]]<<2;
GPIOA->DOUT = data;
//DrvGPIO_ClrBit(E_GPA,9); //显示小数点
break;
default:break;
}
}
/*************************************************************************************
** Function name: SPI_Configuration
** Descriptions: SPI配置
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void SPI_Configuration()
{
//此功能是用来打开SPI模块。它决定了SPI工作在主或从模式,SPI总线时序 和每传输比特长度
//指定SPI端口为SPI1 并配置SPI1为主模式 TYPE1总线时序(时钟的空闲状态为低;在串行时钟的下降沿驱动器的数据,在串行时钟上升沿锁存数据) 32位传输
DrvSPI_Open(eDRVSPI_PORT1, eDRVSPI_MASTER, eDRVSPI_TYPE1, 32,FALSE);

DrvSPI_SetEndian(eDRVSPI_PORT1, eDRVSPI_MSB_FIRST); //配置SPI1传输比特的顺序:优先发送/接收MSB
DrvSPI_DisableAutoSS(eDRVSPI_PORT1); //禁止自动片选功能
DrvSPI_SetSlaveSelectActiveLevel(eDRVSPI_PORT1, eDRVSPI_ACTIVE_LOW_FALLING); //设定从选择线的激活级别:低电平或者下降沿
DrvSPI_Set2BitTransferMode(eDRVSPI_PORT1, TRUE); //设置2比特串行数据I/O 模式
DrvSPI_SetClockFreq(eDRVSPI_PORT1, 1000000, 0); //设置SPI的时钟频率为1MHz

DrvGPIO_Open(E_GPA,14, E_IO_OUTPUT); //SPI_FLAH_CS引脚(即GPA14)为输出
DISABLE_SPI_CS; //输出低电平 即禁止W25Q16BV 的片选信号

}
/*************************************************************************************
** Function name: SPI_ReadMidDid
** Descriptions: W25Q16BV读制造商ID及设备ID函数
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
uint32_t SPI_ReadMidDid(void)
{
uint32_t au32SourceData;
uint32_t au32DestinationData;

DrvSPI_SetBitLength(eDRVSPI_PORT1, 8); //配置SPI传输的比特长度:8 bits
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0x90; // send Command: 0x90, Read Manufacturer/Device ID
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送命令到 SPI 总线
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲

DrvSPI_SetBitLength(eDRVSPI_PORT1, 24); //配置SPI传输的比特长度:24 bits
au32SourceData = 0x0;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0x00 (24-bit Address)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲

DrvSPI_SetBitLength(eDRVSPI_PORT1, 16); //配置SPI传输的比特长度:16 bits
au32SourceData = 0x0;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //接收数据
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲

DISABLE_SPI_CS; // 禁止W25Q16BV 的片选信号
DrvSPI_DumpRxRegister(eDRVSPI_PORT1, &au32DestinationData, 1); //从RX寄存器中读取数据 此功能将不会触发SPI数据传输
return (au32DestinationData & 0xffff); //返回MidDid
}
/*************************************************************************************
** Function name: SPI_ReadStatusReg1
** Descriptions: W25Q16BV读状态寄存器1函数
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
uint32_t SPI_ReadStatusReg1(void)
{
uint32_t au32SourceData;
uint32_t au32DestinationData;

DrvSPI_SetBitLength(eDRVSPI_PORT1, 16); //配置SPI传输的比特长度:16 bits
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0x0500; //send Command: 0x05, Read status register 1
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0x0500 (Read status register 1)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DISABLE_SPI_CS; //禁止W25Q16BV 的片选信号
DrvSPI_DumpRxRegister(eDRVSPI_PORT1, &au32DestinationData, 1); //从RX寄存器中读取数据 此功能将不会触发SPI数据传输
return (au32DestinationData & 0xFF);
}
/*************************************************************************************
** Function name: SPI_WaitReady
** Descriptions: W25Q16BV忙状态检查函数
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void SPI_WaitReady(void)
{
uint32_t ReturnValue;

do{
ReturnValue = SPI_ReadStatusReg1();
ReturnValue = ReturnValue & 1;
}while(ReturnValue!=0); //检查从设备状态寄存器1的BUSY位 等待其为0
}

/*************************************************************************************
** Function name: SPI_ChipErase
** Descriptions: W25Q16BV片擦除函数
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void SPI_ChipErase(void)
{
uint32_t au32SourceData;

DrvSPI_SetBitLength(eDRVSPI_PORT1, 8); //配置SPI传输的比特长度:8 bits
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0x06;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0x06 (Write enable)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DISABLE_SPI_CS; //禁止W25Q16BV 的片选信号
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0xC7; // send Command: 0xC7, Chip Erase
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0xC7 (Chip Erase)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DISABLE_SPI_CS; //禁止W25Q16BV 的片选信号
}
/*************************************************************************************
** Function name: SPI_ReadData
** Descriptions: W25Q16BV读数据函数
** input parameters: DataBuffer0,StartAddressByteCount
** output parameters: none
** Returned value: none
*************************************************************************************/
void SPI_ReadData(uint8_t *DataBuffer, uint32_t StartAddress, uint32_t ByteCount)
{
uint32_t au32SourceData;
uint32_t au32DestinationData;
uint32_t Counter;

DrvSPI_SetBitLength(eDRVSPI_PORT1, 8); //配置SPI传输的比特长度:8 bits
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0x03;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0x03 (Read data)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DrvSPI_SetBitLength(eDRVSPI_PORT1, 24); //配置SPI传输的比特长度:24 bits
au32SourceData = StartAddress;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: StartAddress
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DrvSPI_SetBitLength(eDRVSPI_PORT1, 8); //配置SPI传输的比特长度:8 bits
for(Counter=0; Counter<ByteCount; Counter++)
{
au32SourceData = 0x0;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //接收数据;
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DrvSPI_DumpRxRegister(eDRVSPI_PORT1, &au32DestinationData, 1);//从RX寄存器中读取数据 此功能将不会触发SPI数据传输
DataBuffer[Counter] = (uint8_t) au32DestinationData;
}
DISABLE_SPI_CS; //禁止W25Q16BV 的片选信号
}

/*************************************************************************************
** Function name: SPI_PageProgram
** Descriptions: W25Q16BV按页编程函数
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void SPI_PageProgram(uint8_t *DataBuffer, uint32_t StartAddress, uint32_t ByteCount)
{
uint32_t au32SourceData;
uint32_t Counter;

DrvSPI_SetBitLength(eDRVSPI_PORT1, 8); //配置SPI传输的比特长度:8 bits
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0x06; // send Command: 0x06, Write enable
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0x06 (Write enable)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DISABLE_SPI_CS; //禁止W25Q16BV 的片选信号
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0x02;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0x02 (Page program)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DrvSPI_SetBitLength(eDRVSPI_PORT1, 24); //配置SPI传输的比特长度:24 bits
au32SourceData = StartAddress;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: StartAddress
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DrvSPI_SetBitLength(eDRVSPI_PORT1, 8); //配置SPI传输的比特长度:8 bits
for(Counter=0; Counter<ByteCount; Counter++)
{
au32SourceData = DataBuffer[Counter];
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
}
DISABLE_SPI_CS; //禁止W25Q16BV 的片选信号
}
/*************************************************************************************
** Function name: SPI_SectorErase
** Descriptions: W25Q16BV扇区擦除函数
** input parameters: none
** output parameters: none
** Returned value: none
*************************************************************************************/
void SPI_SectorErase(uint32_t StartAddress)
{

uint32_t au32SourceData;
DrvSPI_SetBitLength(eDRVSPI_PORT1, 8); //配置SPI传输的比特长度:8 bits
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0x06;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0x06 (Write enable)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DISABLE_SPI_CS; //禁止W25Q16BV 的片选信号
DrvSPI_SetBitLength(eDRVSPI_PORT1, 8); //配置SPI传输的比特长度:8 bits
Enable_SPI_CS; //使能W25Q16BV 的片选信号
au32SourceData = 0x20;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: 0x20 (Sector Erase)
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DrvSPI_SetBitLength(eDRVSPI_PORT1, 24); //配置SPI传输的比特长度:24 bits
au32SourceData = StartAddress&0xFFF000;
DrvSPI_SingleWrite(eDRVSPI_PORT1, &au32SourceData); //发送数据到 SPI 总线: StartAddress
while (DrvSPI_IsBusy(eDRVSPI_PORT1)) {} //等待SPI端口空闲
DISABLE_SPI_CS; //禁止W25Q16BV 的片选信号

}
/*************************************************************************************
** Function name: delay_ms
** Descriptions: 1ms(晶振为12MHZ)延时子程序
** input parameters: count
** output parameters: 无
** Returned value: 无
*************************************************************************************/
void delay_ms(uint32_t count)
{
uint32_t i,j;
for(i=count;i>0;i--)
for(j=2395;j>0;j--);
}

hw_config头文件
#ifndef __HW_CONFIG_H__
#define __HW_CONFIG_H__
void Set_System(void);
void RCC_Configuration(void);
void GPIO_Configuration(void);
void TIMER_Configuration(void);
void Timer0_Callback (void);
void SPI_Configuration(void);
uint32_t SPI_ReadMidDid(void);
uint32_t SPI_ReadStatusReg1(void);
void SPI_WaitReady(void);
void SPI_ChipErase(void);
void SPI_ReadData(uint8_t *DataBuffer0, uint32_t StartAddress, uint32_t ByteCount);
void SPI_PageProgram(uint8_t *DataBuffer, uint32_t StartAddress, uint32_t ByteCount);
void SPI_SectorErase(uint32_t StartAddress);
void delay_ms(uint32_t count);
#endif

已测试通过 OK
工程 LI-SPI.rar (1.29 MB)
芯片资料 W25Q16BV.pdf (2.01 MB)

相关帖子

沙发
老鱼探戈| | 2011-9-26 21:25 | 只看该作者
顶!

使用特权

评论回复
板凳
hotpower| | 2011-9-26 22:03 | 只看该作者
加PDMA才好玩~~~

使用特权

评论回复
地板
yongjun_meng| | 2012-6-28 14:28 | 只看该作者
顶起

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

0

主题

1679

帖子

2

粉丝