本次所做实验为SPI读写开发板上的spiflash器件(W25Q32),可以实现SPI主机的简单应用,更复杂的论坛里面已经有大神发过了。
本次实验实现功能为:
1.通过SPI擦除W25Q32上的数据。程序只实现的简单的擦出,还有许多擦除指令不一一再试,有兴趣的可以调试一下;
2.通过SPI向W25Q32写入一个数组的数据,即从00地址开始的20给数据。
3.通过SPI读取W25Q32中从00地址开始的20给数据
4.读写的数据会通过串口显示,并且在成功读写后会使LED点亮。
程序如下:
主程序:
int main (void)
{
INT8U spiid,i = 0;
SystemCoreClockUpdate(); /* 时钟配置 */
uart0Init(9600,0,0,8,1);
printf("SPI Test Star...\r\n");
gpioInit(); /* 管脚初始化 */
spiInit(SPI0_BASE_PTR , Master); /* SPI0主机模式初始化 */
spiid = flash_read_id(); //读ID
printf("ID=0x%x08\r\n",spiid);
printf("Start to erase sector 0...\r\n");
flash_sector_erase (0x000000); //擦除W25Q32
myDelay(300); /* 延时 */
ledSet(0);
printf("Start to write sector 0...\r\n");
flash_write_sector(0x000000, SNDBUFPT, 20); /* 以0x0为起始地址,将WrBuf 写入 */
myDelay(30);
printf("Start to read sector 0...\r\n"); /* 数组里的20个数据写入芯片 */
flash_read_data(0x000000, RCVBUFPT,20); /* 以0x0为起始地址,读20个 */
for (i = 0;i < 19;i++) {
if (GucRdBuf != GucWrBuf ) { /* 若SPI读写不正确则程序D3常亮 */
ledSet(1);
printf("SPI ERR...\r\n");
while (1);
}
}
ledSet(2); /* 若SPI读写正确,D4常亮 */
printf("Verify ok...\r");
while(1) {
};
}
SPI初始化:
void spiInit (SPI_MemMapPtr SPI,BOOLEAN bMode)
{
SIM_SCGC4 |= SIM_SCGC4_SPI0_MASK; /* 开启SPI0 */
/* CPOL = 0, SCK 为低有效 */
/* MSTR = 1, SPI 处于主模式 */
/* LSBF = 0, MSB (位7)在先 */
/* SPIE = 0, SPI 中断被禁止 */
/* CPHA = 0, SCK第一个时钟沿采样*/
SPI->C1 |= SPI_C1_SPE_MASK ; /* SPI使能 */
SPI->C1 &= ~(1 << SPI_C1_CPHA_SHIFT);
if(bMode == Master) { /* 主模式 */
SPI->C1 |= SPI_C1_MSTR_MASK;
} else { /* 从模式 */
SPI->C1 &= ~(1 << SPI_C1_MSTR_SHIFT);
}
SPI->BR |= SPI_BR_SPPR(0) ; /* 时钟分频 */
}
SPI发送一字节数据:
INT8U Send_Byte (INT8U ucdata)
{
INT8U ucTemp;
while((SPI0_S & SPI_S_SPTEF_MASK) != SPI_S_SPTEF_MASK); /* 发送前要先判断寄存器 */
SPI0_DL = ucdata;
while((SPI0_S & SPI_S_SPRF_MASK) != SPI_S_SPRF_MASK); /* 接收数据有效 */
ucTemp = SPI0_DL; /* 清空接收标记寄存器 */
ucTemp = ucTemp;
return ucTemp;
}
读W25Q32状态:
uint8_t flash_read_status ( void )
{
uint8_t status;
SPI_FLASH_CS_LOW(); /* 选中SPI Flash */
Send_Byte(0x05);
status = Send_Byte(0xff);
SPI_FLASH_CS_HIGH(); /* P0.2--1,CS = 1 释放SPI Flash */
return status; /* Return Reg 1's Content */
}
读W25Q32地址:
INT16U flash_read_id (void)
{
INT16U IDcode;
SPI_FLASH_CS_LOW(); /* P0.2--0,CS = 0 选中SPI Flash */
Send_Byte(0x4b);
Send_Byte(0x00);
Send_Byte(0x00);
Send_Byte(0x00);
Send_Byte(0x00);
IDcode = Send_Byte(0xff);
SPI_FLASH_CS_HIGH(); /* P0.2--1,CS = 1 释放SPI Flash */
return IDcode;
}
读W25Q32数据:
uint8_t flash_read_data (uint32_t RAddr, uint8_t *buf, uint32_t RLength)
{
uint8_t Temp;
uint32_t i;
if (RLength == 0)
{
return 0;
}
/*
* Check the state register. If it's busy , wait until it's free
*/
while(1)
{
Temp = flash_read_status( );
Temp &= 0x01;
if(Temp == 0x00)
break;
for(i=0; i<10; i++);
}
SPI_FLASH_CS_LOW(); /* P0.2--0,CS = 0 选中SPI Flash */
Send_Byte(0x03);
Send_Byte((RAddr & 0xFF0000) >> 16);
Send_Byte((RAddr & 0x00FF00) >> 8);
Send_Byte((RAddr & 0x0000FF));
for (i=0; i<RLength; i++)
{
buf = Send_Byte(0xff);
printf("Read Data = %d\r\n",buf);
}
SPI_FLASH_CS_HIGH(); /* P0.2--1,CS = 1 释放SPI Flash */
return 1;
}
W25Q32区域擦除:
uint8_t flash_sector_erase (uint32_t addr)
{
flash_write_enable(); /* Write enable */
SPI_FLASH_CS_LOW(); /* P0.2--0,CS = 0 选中SPI Flash */
Send_Byte(0x20);
Send_Byte((addr & 0xFF0000) >> 16);
Send_Byte((addr & 0x00FF00) >> 8);
Send_Byte(addr & 0x0000FF);
SPI_FLASH_CS_HIGH(); /* P0.2--1,CS = 1 释放SPI Flash */
while (flash_read_status() & 0x01 != 0x00); /* Wait for the flash free */
return 1;
}
W25Q32区域写:
uint8_t flash_write_sector (uint32_t WAddr, uint8_t *buf, uint32_t WLength)
{
uint32_t i;
if (WLength == 0)
{
return 0;
}
flash_write_enable(); /* Write enable */
SPI_FLASH_CS_LOW(); /* P0.2--0,CS = 0 选中SPI Flash */
Send_Byte(0x02);
Send_Byte((WAddr & 0xFF0000) >> 16);
Send_Byte((WAddr & 0x00FF00) >> 8);
Send_Byte((WAddr & 0x0000FF));
for (i=0; i<WLength; i++)
{
Send_Byte(buf);
printf("Send Data = %d\r\n",buf);
}
SPI_FLASH_CS_HIGH(); /* P0.2--1,CS = 1 释放SPI Flash */
while ((flash_read_status() & 0x01) != 0x00);
return 1;
}
主程序就不多说了,很好理解,就是将要实现的功能整合到一起。还是惯例红色的部分比较重要。
在这里重点说一下SPI写程序:
起初从主函数按照一步步调试观察的时候,发现程序只用到了SPI的写操作(程序上的说明是写操作),而没有用到读操作(同左),非常费解。
后来发现其实它的写操作里面是包含读操作的。
当需要写数据时就将数据给SPI,函数返回值不去理会,将标志位清零即可。
当需要读数据时就发送一帧0xff,然后将函数返回值读出来。
在这里需要说明为什么要发送0xff,我个人的认为是由于W25Q32器件的指令问题
|