本帖最后由 正点原子 于 2013-3-28 23:05 编辑
//等待操作完成 //time:要延时的长短 //返回值:状态. u8 STMFLASH_WaitDone(u16 time) { u8 res; do { res=STMFLASH_GetStatus(); if(res!=1)break;//非忙,无需等待了,直接退出. delay_us(1); time--; }while(time); if(time==0)res=0xff;//TIMEOUT return res; } //擦除页 //paddr:页地址 //返回值:执行情况 u8 STMFLASH_ErasePage(u32 paddr) { u8 res=0; res=STMFLASH_WaitDone(0X5FFF);//等待上次操作结束,>20ms if(res==0) { FLASH->CR|=1<<1;//页擦除 FLASH->AR=paddr;//设置页地址 FLASH->CR|=1<<6;//开始擦除 res=STMFLASH_WaitDone(0X5FFF);//等待操作结束,>20ms if(res!=1)//非忙 { FLASH->CR&=~(1<<1);//清除页擦除标志. } } return res; } //在FLASH指定地址写入半字 //faddr:指定地址(此地址必须为2的倍数!!) //dat:要写入的数据 //返回值:写入的情况 u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat) { u8 res; res=STMFLASH_WaitDone(0XFF); if(res==0)//OK { FLASH->CR|=1<<0;//编程使能 *(vu16*)faddr=dat;//写入数据 res=STMFLASH_WaitDone(0XFF);//等待操作完成 if(res!=1)//操作成功 { FLASH->CR&=~(1<<0);//清除PG位. } } return res; } //读取指定地址的半字(16位数据) //faddr:读地址 //返回值:对应数据. u16 STMFLASH_ReadHalfWord(u32 faddr) { return *(vu16*)faddr; } #if STM32_FLASH_WREN //如果使能了写 //不检查的写入 //WriteAddr:起始地址 //pBuffer:数据指针 //NumToWrite:半字(16位)数 void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u16 i; for(i=0;i<NumToWrite;i++) { STMFLASH_WriteHalfWord(WriteAddr,pBuffer); WriteAddr+=2;//地址增加2. } } //从指定地址开始写入指定长度的数据 //WriteAddr:起始地址(此地址必须为2的倍数!!) //pBuffer:数据指针 //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.) #if STM32_FLASH_SIZE<256 #define STM_SECTOR_SIZE 1024 //字节 #else #define STM_SECTOR_SIZE 2048 #endif u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u32 secpos; //扇区地址 u16 secoff; //扇区内偏移地址(16位字计算) u16 secremain; //扇区内剩余地址(16位字计算) u16 i; u32 offaddr; //去掉0X08000000后的地址 if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024* STM32_FLASH_SIZE)))return;//非法地址 STMFLASH_Unlock(); //解锁 offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址. secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6 secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.) secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小 if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围 while(1) { STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE, STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容 for(i=0;i<secremain;i++)//校验数据 { if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除 } if(i<secremain)//需要擦除 { STMFLASH_ErasePage(secpos*STM_SECTOR_SIZE+ STM32_FLASH_BASE);//擦除这个扇区 for(i=0;i<secremain;i++) STMFLASH_BUF[i+secoff]=pBuffer;//复制 STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+ STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2); //写入整个扇区 }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain); //写已经擦除了的,直接写入扇区剩余区间. if(NumToWrite==secremain)break;//写入结束了 else//写入未结束 { secpos++; //扇区地址增1 secoff=0; //偏移位置为0 pBuffer+=secremain; //指针偏移 WriteAddr+=secremain; //写地址偏移 NumToWrite-=secremain; //字节(16位)数递减 if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2; //下一个扇区还是写不完 else secremain=NumToWrite;//下一个扇区可以写完了 } }; STMFLASH_Lock();//上锁 } #endif //从指定地址开始读出指定长度的数据 //ReadAddr:起始地址 //pBuffer:数据指针 //NumToWrite:半字(16位)数 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) { u16 i; for(i=0;i<NumToRead;i++) { pBuffer=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节. ReadAddr+=2;//偏移2个字节. } } 该部分代码,我们重点介绍一下STMFLASH_Write函数,该函数用于在STM32的指定地址写入指定长度的数据,该函数的实现基本类似第28章的SPI_Flash_Write函数,不过该函数对写入地址是有要求的,必须保证以下两点: 1, 该地址必须是用户代码区以外的地址。 2, 该地址必须是2的倍数。 条件1比较好理解,如果把用户代码给卡擦了,可想而知你运行的程序可能就被废了,从而很可能出现死机的情况。条件2则是STM32 FLASH的要求,每次必须写入16位,如果你写的地址不是2的倍数,那么写入的数据,可能就不是写在你要写的地址了。 另外,该函数的STMFLASH_BUF数组,也是根据所用STM32的FLASH容量来确定的,战舰STM32开发板的FLASH是512K字节,所以STM_SECTOR_SIZE的值为512,故该数组大小为2K字节。其他函数我们就不做介绍了,保存stmflash.c文件,并加入到HARDWARE组下,然后打开stmflash.h,在该文件里面输入如下代码: #ifndef __STMFLASH_H__ #define __STMFLASH_H__ #include "sys.h" ////////////////////////////////////////////////////////////////////////////////////////////////////// //用户根据自己的需要设置 #define STM32_FLASH_SIZE 512 //所选STM32的FLASH容量大小(单位为K) #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不是能;1,使能) ////////////////////////////////////////////////////////////////////////////////////////////////////// //FLASH起始地址 #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址 //FLASH解锁键值 #define FLASH_KEY1 0X45670123 #define FLASH_KEY2 0XCDEF89AB void STMFLASH_Unlock(void); //FLASH解锁 void STMFLASH_Lock(void); //FLASH上锁 u8 STMFLASH_GetStatus(void); //获得状态 u8 STMFLASH_WaitDone(u16 time); //等待操作结束 u8 STMFLASH_ErasePage(u32 paddr); //擦除页 u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat);//写入半字 u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字 void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据 u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度数据 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读出指定长度的数据 #endif 保存此部分代码。最后,打开test.c文件,修改main函数如下: //要写入到STM32 FLASH的字符串数组 const u8 TEXT_Buffer[]={"STM32 FLASH TEST"}; #define SIZE sizeof(TEXT_Buffer) ///数组长度 #define FLASH_SAVE_ADDR 0X08070000 //设置FLASH 保存地址(必须为偶数, //且其值要大于本代码所占用FLASH的大小+0X08000000) int main(void) { u8 key; u16 i=0; u8 datatemp[SIZE]; Stm32_Clock_Init(9); //系统时钟设置 uart_init(72,9600); //串口初始化为9600 delay_init(72); //延时初始化 LED_Init(); //初始化与LED连接的硬件接口 LCD_Init(); //初始化LCD usmart_dev.init(72); //初始化USMART KEY_Init(); //按键初始化 NRF24L01_Init(); //初始化NRF24L01 POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(60,50,200,16,16,"WarShip STM32"); LCD_ShowString(60,70,200,16,16,"FLASH EEPROM TEST"); LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(60,110,200,16,16,"2012/9/13"); LCD_ShowString(60,130,200,16,16,"WK_UP:Write KEY1:Read"); POINT_COLOR=BLUE; //显示提示信息 POINT_COLOR=BLUE;//设置字体为蓝色 while(1) { key=KEY_Scan(0); if(key==KEY_UP)//WK_UP按下,写入STM32 FLASH { LCD_Fill(0,150,239,319,WHITE);//清除半屏 LCD_ShowString(60,150,200,16,16,"Start Write FLASH...."); STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE); LCD_ShowString(60,150,200,16,16,"FLASH Write Finished!");//提示传送完成 } if(key==KEY_DOWN)//KEY1按下,读取字符串并显示 { LCD_ShowString(60,150,200,16,16,"Start Read FLASH.... "); STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE); LCD_ShowString(60,150,200,16,16,"The Data Readed Is: ");//提示传送完成 LCD_ShowString(60,170,200,16,16,datatemp);//显示读到的字符串 } i++; delay_ms(10); if(i==20) { LED0=!LED0;//提示系统正在运行 i=0; } } } 至此,我们的软件设计部分就结束了。 39.4 下载验证 在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,通过先按WK_UP按键写入数据,然后按KEY1读取数据,得到如图39.4.1所示:
图39.4.1 程序运行效果图
伴随DS0的不停闪烁,提示程序在运行。本章的测试,我们还可以借助USMART,在USMART里面添加STMFLASH_ReadHalfWord函数,既可以读取任意地址的数据。当然,你也可以将STMFLASH_Write稍微改造下,这样就可以在USMART里面验证STM32 FLASH的读写了。
|