完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
记录,分享,进步。
最近在做一个定位的项目,里面用到flash是超捷的SST25LF020,资料很少啊。遇到不少问题,现在将遇到的两个问题进行分析。 0.遇到的问题: a.正确的读出数据:gaobaoqing SPI_FLASH TEST,但是从flash内读出来的数据不完全正确,之间有问号。 b.无论是整个sector擦除后读取数据,还是写入后读取数据,第一个数据总是不对,read1是sector erase后,read2是写入数据后,读出的数据,它俩都存在一个问题,在数据的前面总是有个未知的符号。 …过了很久…过了很久…过了很久…我才找见问题出在哪里。 对于a问题,这个flash每写入一个字节数据(Byte program)之前,都要取消写保护,而且还要等待这个取消命令完成后才能进行写入操作。见后面函数BytePro_SPIFlash()里面。(厂家难道不怕写入速度太慢了有人投诉他吗) 对于b问题,这个flash读数据之前,要先读一个空操作。见后面的函数Read_SPIFlash()里面。(厂家难道不怕这个额外的读操作会有人投诉他吗) 好啦好啦,既然这两个棘手的问题解决了,我就能愉快的和大家分享我的代码啦,啦啦啦~。 1.先介绍一下这个flash的几个关键参数。 a.这个flash容量有2Mbit b.Single 3.0-3.6V Read and Write Operations c. Uniform 4 KByte sectors,Uniform 32(or 64)KByte overlay blocks d.Software Write Protection(在这个上面吃了亏)。 2.电气连接 先介绍一下这颗flash的引脚,好绘制他的原理图似乎所有的flash的原理图都是和下面这样设计。 3.代码 因为板子刚刚打回来,不清楚spi3的配置好坏,所以只能先读一下device ID来验证一下spi3和flash之间的通信。 下面是有关SPI3的所有函数。 // 下面的代码均在spi.c中 //SPI3_Init初始化函数 void SPI3_Init(void) { RCC->APB2ENR |= 0x01 << 3; //PORTB时钟使能 RCC->APB1ENR |= 1 << 15; //SPI3时钟使能 RCC->APB2ENR |= 1 << 0; //开启辅助时钟 JTAG_Set(SWD_ENABLE); // 因为SPI3和JTAG冲突,所以要把JTAG关闭,改为swd下载。 //这里只针对SPI口初始化 GPIOB->CRL &= 0XFF000FFF; GPIOB->CRL |= 0X00BBB000; //PB3,4,5复用 GPIOB->ODR |= 0X7 << 3; //PB3,4,5上拉 SPI3->CR1 |= 0 << 10;//全双工模式 SPI3->CR1 |= 1 << 9; //软件nss管理 SPI3->CR1 |= 1 << 8; SPI3->CR1 |= 1 << 2; //SPI主机 SPI3->CR1 |= 0 << 11;//8bit数据格式 // SPI3->CR1 |= 1 << 1; //空闲模式下SCK为1 CPOL=1 // SPI3->CR1 |= 1 << 0; //数据采样从第二个时间边沿开始,CPHA=1 SPI3->CR1 &= ~(1 << 1); //空闲模式下SCK为0 CPOL=0 SPI3->CR1 &= ~(1 << 0); //数据采样从第一个时间边沿开始,CPHA=0 SPI3->CR1 |= 7 << 3; //Fsck=Fcpu/256 SPI3->CR1 |= 0 << 7; //MSBfirst SPI3->CR1 |= 1 << 6; //SPI设备使能 //SPI3_ReadWriteByte(0xff); //启动传输(主要作用:维持MOSI为高) } //SPI3 速度设置函数 //SpeedSet:0~7 //SPI速度=fAPB2/2^(SpeedSet+1) //APB2时钟一般为72Mhz void SPI3_SetSpeed(u8 SpeedSet) { SpeedSet &= 0X07; //限制范围 SPI3->CR1 &= 0XFFC7; SPI3->CR1 |= SpeedSet<<3; //设置SPI3速度 SPI3->CR1 |= 1<<6; //SPI设备使能 } //SPI3 读写一个字节 //TxData:要写入的字节 //返回值:读取到的字节 u8 SPI3_ReadWriteByte(u8 TxData) { u16 retry = 0; while((SPI3->SR & 1 << 1) == 0)//等待发送区空 { retry++; if(retry>0XFFFE)return 0; } SPI3->DR = TxData; //发送一个byte retry = 0; while((SPI3->SR&1 << 0) == 0) //等待接收完一个byte { retry++; if(retry>0XFFFE)return 0; } return SPI3->DR; //返回收到的数据 } 下面这段代码在main.c中,整段代码的逻辑是:验证了flash的device id,待验证成功后,先擦除对应的块地址,然后读一下,验证擦除是否成功,然后使用Byte program命令,对flash进行字符串写入,写成功后再用read命令读一次flash,查看写入的数据是否正确。 // 下面的函数均在spiFlash.c中 // 待写入flash里的数据 const u8 TEXT_Buffer[]={"gaobaoqing SPI_FLASH TEST"}; #define SIZE sizeof(TEXT_Buffer) u8 datatemp[SIZE] = {0,}; // 数组初始化 while(Read_Flash_ID() != SPI_FLASH_TYPE) //检测不到SST25LF020 { printf("25LF020 Check Failed!"); delay_ms(1000); } delay_ms(100); SecErase_SPIFlash(0x5); // 擦除第6个sector,从0开始 delay_ms(500); // 擦写后,延时500ms Read_SPIFlash(0x5F20, datatemp, sizeof(datatemp)); // 从第0x5F20个地址处开始,读出SIZE个字节 printf("read1%srn", datatemp); memset(datatemp, 0, sizeof(datatemp)); // 擦除缓冲数组 if (BytePro_SPIFlash(0x5F20, (u8 *)TEXT_Buffer, SIZE)) // 从第0x5F20个地址处开始,写入SIZE长度的数据 { printf("write failedrn"); } printf("Write Finished!rn"); // 提示传送完成 Read_SPIFlash(0x5F20, datatemp, SIZE); // 从第0x5F20个地址处开始,读出SIZE个字节 printf("read2:%srn", datatemp); memset(datatemp, 0, sizeof(datatemp)); // 擦除缓冲数组 delay_ms(1000); // 根据上面的逻辑,这里是读ID unsigned int Read_Flash_ID(void) { unsigned short temp = 0; SPI_FLASH_ENABLE(); /* 片选SPI Flash */ Send_Byte(SPI_FLASH_RDID); /* 发送读device ID命令 */ Send_Byte(0x00); // device id 地址 Send_Byte(0x00); Send_Byte(0x01); temp |= Get_Byte() << 16; /* 读取ID */ temp |= Get_Byte() << 8; temp |= Get_Byte(); SPI_FLASH_DISABLE(); /* 禁止SPI Flash */ return temp; } /*********************************************** ** 函数名称 : SecErase_SPIFlash ** 函数功能 : 扇区擦除Flash ** 入口参数 : iDes:该扇区所在的地址 ** 出口参数 : TRUE/FALSE **********************************/ unsigned char SecErase_SPIFlash(unsigned int iDes) { unsigned char state; unsigned int iDes_addr = iDes * 4096; // 一个sector 是4096个字节。 WriteStatReg(0x00); // 关闭写保护 WirteEnable(); SPI_Flash_Wait_Busy(); SPI_FLASH_ENABLE(); // 写出命令 Send_Byte(SPI_FLASH_ERASE_SECTOR); // 写出地址 Send_Byte((unsigned char)((iDes_addr >>16) & 0xff)); /* 高地址先发送 */ Send_Byte((unsigned char)((iDes_addr >> 8) & 0xff)); Send_Byte((unsigned char)((iDes_addr >> 0) & 0xff)); SPI_FLASH_DISABLE(); SPI_Flash_Wait_Busy(); return 0; } /*flash读函数*/ unsigned char Read_SPIFlash(unsigned int iDes, unsigned char *pucData, unsigned int iLen) { int i = 0; if(pucData == NULL) return 1; SPI_FLASH_ENABLE(); Send_Byte(SPI_FLASH_READ); // 发送读命令 // 写出地址 Send_Byte((unsigned char)((iDes>>16) & 0xff)); /* 高地址先发送 */ Send_Byte((unsigned char)((iDes>> 8) & 0xff)); Send_Byte((unsigned char)((iDes>> 0) & 0xff)); Get_Byte(); // 在循环读之前先读一次,解决第一个地址出现一个多余的数据。 while(iLen--) { *pucData++ = Get_Byte(); } SPI_FLASH_DISABLE(); return 0; } // 一个字节一个字节的发送 unsigned char BytePro_SPIFlash(unsigned int iDes, unsigned char *pucData, unsigned int iLen) { unsigned char state; if(pucData == NULL) return 1; while(iLen > 0) { WriteStatReg(0x00); // 关闭写保护 这里是我踩得第一个坑。稍后给大家分析一下 WirteEnable(); /* 使能状态寄存器的写使能 */ SPI_FLASH_ENABLE(); Send_Byte(SPI_FLASH_PROGRAM_BYTE); /* 选中从设备 */ // 写出地址 Send_Byte((unsigned char)((iDes >>16) & 0xff)); /* 高地址先发送 */ Send_Byte((unsigned char)((iDes >> 8) & 0xff)); Send_Byte((unsigned char)((iDes >> 0) & 0xff)); Send_Byte(*pucData++); SPI_FLASH_DISABLE(); /* CS为高电平 */ iDes++; /* 地址增加 */ iLen--; SPI_Flash_Wait_Busy(); } return 0; } void SSP0_Init(void) { RCC->APB2ENR |= 1 << 5; //PORTD时钟使能 GPIOD->CRL &= 0XFFFFF0FF; GPIOD->CRL |= 0X00000300; //PD2 推挽 GPIOD->ODR |= 0X1 << 2; //PD2 SPI3_Init(); //初始化SPI3 SPI3_SetSpeed(SPI_SPEED_4); //设置为18M时钟,高速模式 Send_Byte(0xff); // 维持mosi为高 } unsigned char Send_Byte(unsigned char data) { u16 retry = 0; SPI3->DR = data; //发送一个byte while((SPI3->SR & 1 << 1) == 0)//等待发送区空 { retry++; if(retry > 0XFFFE)return 0; } return 0; } unsigned char Get_Byte(void) { return SPI3_ReadWriteByte(0xff); } void SPI_FLASH_ENABLE(void) {SPI_FLASH_CS=0;} void SPI_FLASH_DISABLE(void) {SPI_FLASH_CS=1;} //其余的就是一些宏定义了 spiflash.h: #define SPI_FLASH_READ 0x03 #define SPI_FLASH_ERASE_SECTOR 0x20 #define SPI_FLASH_PROGRAM_BYTE 0x02 #define SPI_FLASH_Read_Status 0x05 #define SPI_FLASH_EWSR 0x50 #define SPI_FLASH_WRSR 0x01 #define SPI_FLASH_WREN 0x06 #define SPI_FLASH_WRDI 0x04 #define SPI_FLASH_RDID 0x90 #define SPI_FLASH_TYPE 0x43bf #define SPI_FLASH_CS PDout(2) spiflash.c : #define WirteEnable() SPI_FLASH_ENABLE(); Send_Byte(SPI_FLASH_WREN); SPI_FLASH_DISABLE() #define WirteDisable() SPI_FLASH_ENABLE(); Send_Byte(SPI_FLASH_WRDI); SPI_FLASH_DISABLE() #define ReadStatReg(state) SPI_FLASH_ENABLE(); Send_Byte(SPI_FLASH_Read_Status); state = Get_Byte(); SPI_FLASH_DISABLE() #define WriteStatReg(state) SPI_FLASH_ENABLE(); Send_Byte(SPI_FLASH_EWSR); SPI_FLASH_DISABLE(); SPI_FLASH_ENABLE(); Send_Byte(SPI_FLASH_WRSR); Send_Byte(state); SPI_FLASH_DISABLE(); SPI_Flash_Wait_Busy(); // 这一行,又是一个大坑啊,醉了 |
|
|
|
只有小组成员才能发言,加入小组>>
3319 浏览 9 评论
2997 浏览 16 评论
3495 浏览 1 评论
9065 浏览 16 评论
4088 浏览 18 评论
1185浏览 3评论
611浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
601浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2337浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1897浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 17:17 , Processed in 1.211598 second(s), Total 50, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号