| 本帖最后由 正点原子 于 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的读写了。 
 |