发新帖我要提问
12
返回列表
[STM32F1]

STM32基础篇FLASH 实验-SPI

[复制链接]
楼主: aizaixiyuanqian
手机看帖
扫描二维码
随时随地手机跟帖
aizaixiyuanqian|  楼主 | 2018-2-26 10:42 | 显示全部楼层
EN25Q64 读取数据
命令表格中,EN25Q64 读取数据的命令是 0x03。第 1 个字节是命令字节,
第 2、3、4 个字节是读取地址,从第 5 个字节开始返回数据,如果接着读,
会返回下一个字节的数据

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:43 | 显示全部楼层
本帖最后由 aizaixiyuanqian 于 2018-2-26 10:44 编辑

检测器件是否未处于忙状态
在器件在执行“页编辑”“扇区擦除”“块区擦除”“芯片擦除”“写状态寄存器”指令的时候,除了“读状态寄存器”指令,其他指令都忽略。所以在执行写入之前我们要先查看器件是否处于忙状态。这个忙状态标志如下图 1.png

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:44 | 显示全部楼层
它是控制和状态寄存器的第 0 位。读状态寄存器的指令是 0x05,从命令
表格中,我们可以看出第 1 个字节是命令字节,第 2 个字节返回状态寄存器,
所以读取状态寄存器的步骤为:
a) 选择片选。
b) 发送一个字节控制指令 0x05。
c) 读取一个字节状态寄存器。
d) 取消片选。

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:47 | 显示全部楼层
程序实现为:
static uint8_t FLASH_CheckBusy(void)
{
uint8_t statusValue;
uint32_t timeCount = 0;
do
{
timeCount++;
if(timeCount > 0xEFFFFFFF) //等待超时
{
return 0xFF;
}
FLASH_CS_CLR; //使能器件
SPI2_WriteReadData(EN25X_ReadStatusReg); //发送读取状态寄
存器命令
statusValue = SPI2_WriteReadData(0xFF); //读取一个字节
FLASH_CS_SET; //取消片选
}
while((statusValue & 0x01) == 0x01); // 等待 BUSY 位清空
return 0;
}

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:50 | 显示全部楼层
2) 选择片选
3) 发送读取命令 0x03
4) 发送三个字节的地址
5) 读取 n 个字节
6) 取消片选

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:50 | 显示全部楼层
程序实现如下:
void FLASH_ReadData(uint8_t *readBuff, uint32_t readAddr, uint16_t
readByteNum)
{
SPI2_SetSpeed(SPI_BaudRatePrescaler_2);
FLASH_CS_CLR; //打开片选
/* 写读取命令 */
SPI2_WriteReadData(EN25X_ReadData);
/* 发送 24 位读取地址 */
SPI2_WriteReadData(readAddr >> 16);
SPI2_WriteReadData(readAddr >> 8);
SPI2_WriteReadData(readAddr);
/* 读取数据 */
while(readByteNum--)
{
*readBuff = SPI2_WriteReadData(0xFF);
readBuff++;
}
FLASH_CS_SET; //关闭片选
}

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:54 | 显示全部楼层
EN25Q64 写入数据
对于 EN25Q64 的写入操作比较复杂,它只能页编辑,也就是必须一次性
写入 256 个字节的数据,而且在进行页编辑的时候,必须保证写入区域位都
为 1,就是说如果有数据必须要擦除掉,所以一般每次写入都要先擦除数据
才能写入。而写入数据的操作步骤为:
1) 注意要保证:要写入的区域,是空的,否则要先擦除。
2) 检测器件是否处于忙状态。
3) 开启写入使能。

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:55 | 显示全部楼层
在命令表中,写使能命令是 0x06。每次执行编辑和擦除操作的时候,
都需要器件开启写使能。开启的操作为:
a) 选择片选
b) 写入一个字节的写使能命令 0x06。
c) 取消片选

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:56 | 显示全部楼层
程序实现为:
static void FLASH_WriteEnable(void)
{
FLASH_CS_CLR; //使能器件
SPI2_WriteReadData(EN25X_WriteEnable); //发送写使能
FLASH_CS_SET; //取消片选
}

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:57 | 显示全部楼层
4) 选择片选
5) 发送 1 个字节的页编辑命令 0x02。
6) 发送写入 3 个字节的写入地址。
7) 写入 256 个字节
8) 取消片选

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 10:58 | 显示全部楼层
例程中的程序实现如下:
static void FLASH_WritePage(uint8_t *writeBuff, uint32_t writeAddr,
uint16_t writeByteNum)
{
uint16_t byteNum, i;
byteNum = writeAddr % 256;
byteNum = 256 - byteNum;  //求出首页剩余地址
if(writeByteNum <= byteNum)  //当写入字节少于首页剩余地址
{
byteNum = writeByteNum;
}
/* 开始写入 */
while(1)
{
/* 写入数据 */
FLASH_CheckBusy();  //确定FLASH是否处于忙状态
FLASH_WriteEnable(); //开启写使能
FLASH_CS_CLR; //使能器件
SPI2_WriteReadData(EN25X_PageProgram); //发送写命令
SPI2_WriteReadData(writeAddr >> 16); //发送 24 位读取地址
SPI2_WriteReadData(writeAddr >> 8);
SPI2_WriteReadData(writeAddr);
for(i=0; i<byteNum; i++) //循环写数
{
SPI2_WriteReadData(*writeBuff);
writeBuff++;
}
FLASH_CS_SET; //取消片选
/* 判断是否写完 */
if(writeByteNum == byteNum) //如果写入字节数等于剩余字节数表
示写入完成
{
break;
}
else //如果未写入完成
{
writeAddr += byteNum; //写入地址偏移
writeByteNum = writeByteNum - byteNum;  //求出剩余字节数
if(writeByteNum >= 256) //如果剩余字节数大于 256,那么一次写
入一页
{
byteNum = 256;
}
else //如果剩余字节数小于 256,那么一次全部写完
{
byteNum = writeByteNum;
}
}
}
}

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 11:03 | 显示全部楼层
EN25Q64 擦除操作
因为 EN25Q64 的写入的时候都要保证写入位置都没有数据,所以每次写
入数据的时候,总会伴随着擦除操作。器件擦除命令有三种,第一种是“块
擦除”,一次擦除 64K 字节数据,即 256 页。第二种是“扇区擦除”,一次擦
除 4K 字节数据,即:16 页。第三种是“芯片擦除”,一次性擦除整块芯片。
我们这里就讲述“扇区擦除”,其实擦除操作方式都差不多。

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 11:04 | 显示全部楼层
操作步骤为:
1) 检测器件是否未处于忙状态
2) 开启写使能。
3) 选择片选。
4) 发送 1 个字节的“扇区擦除”命令。
5) 发送 3 个字节的扇区擦除地址。
6) 取消片选。

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 11:04 | 显示全部楼层
程序实现如下:
static void FLASH_SectorErase(uint32_t eraseAddr)
{
FLASH_CheckBusy();  //确定 FLASH 是否处于忙状态
FLASH_WriteEnable(); //开启写使能
FLASH_CS_CLR; //使能器件
SPI2_WriteReadData(EN25X_SectorErase); //发送命令
SPI2_WriteReadData(eraseAddr >> 16); //发送 24 位地址
SPI2_WriteReadData(eraseAddr >> 8);
SPI2_WriteReadData(eraseAddr);
FLASH_CS_SET; //取消片选
}

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 11:07 | 显示全部楼层
例程主函数
const u8 TEXT_Buffer[19]={"FLASH SPI TEST OK!"};
#define SIZE (19-1)
int main(void)
{
uint8_t buff[SIZE], showChar[SIZE + 1], j, keyValue, ledState = 0;
uint32_t i;
/* 初始化 */
TFT_Init();
KEY_Config();
FLASH_Init();
LED_Config();
/* 显示初始化 */
TFT_ClearScreen(BLACK);
while(FLASH_ReadID() != EN25Q64)
{
GUI_Show12Char(75, 0, "FLASH Init ERROR!", RED, BLACK);
}
TFT_ClearScreen(BLACK);
GUI_Show12Char(75, 0, "FLASH Init OK! ", RED, BLACK);
GUI_Show12Char(0, 32, "KEY_UP: write KEY_DOWN:read", RED,
BLACK);
while(1)
{
keyValue = KEY_Scan();
/* 根据按键做出反应 */
switch(keyValue)
{
case(KEY_UP): //按上键将"FLASH SPI test OK!"从 FLASH 地
址 0 开始写入
FLASH_WriteData((u8*)TEXT_Buffer, 0, SIZE);
GUI_Show12Char(0, 48, "wirte OK! ", RED,
BLACK);
break;
case(KEY_DOWN): //按下键将从 FLASH 地址 0 开始读取 18
位数,将读取到的显示
FLASH_ReadData(buff, 0, SIZE);
for(j=0; j<SIZE; j++)
{
showChar[j] = buff[j];
}
GUI_Show12Char(0, 48, showChar, RED, BLACK);
break;
default:
break;
}
/* LED 灯闪烁 */
i++;
if(i > 0x5FFFF)
{
i = 0;
if(ledState == 0xFE)
{
ledState = 0xFF;
}
else
{
ledState = 0xFE;
}
LED_SetState(ledState);
}
}
}

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 11:10 | 显示全部楼层
程序效果是,按 KEY_UP 写入“FLASH SPI TEST OK!”到 EN25Q64 中,KEY_DOWN,读取写入的数据,并显示到 LCD 彩屏上面

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-26 11:12 | 显示全部楼层
这个实验也算是比较简单,对于练习SPI是比较不错的选择。

使用特权

评论回复
yiy| | 2018-2-26 19:31 | 显示全部楼层
现在单片机都可以开辟个flash空间当内置的EEPROM了吧,还用这么费劲外置吗

使用特权

评论回复
aizaixiyuanqian|  楼主 | 2018-2-28 11:46 | 显示全部楼层
yiy 发表于 2018-2-26 19:31
现在单片机都可以开辟个flash空间当内置的EEPROM了吧,还用这么费劲外置吗

毕竟还要有这么一个过程。

使用特权

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

本版积分规则