大家好,今天和大家分享一下stm32f103c8t6读写内部flash,关于103系列的单片机大家可以参考选项手册查看flash的容量。
一、芯片flash容量分类:
可以看到我们今天介绍的这款芯片的flash大小是64k的,网上也有人说它可以支持到128k,但是官方给出的解释是前64k是有保证的,后面的无法保证,所以想要使用的小伙伴需要慎重。
现在芯片的flash大小我们知道了,下面就可以看看这个flash是怎么划分的了,通过芯片数据手册,我们能看到今天说的stm32f103c8t6是属于中等容量的设备。
既然是中等容量的设备了,那我们就来看看flash划分吧,在stm32的闪存编程手册中有这样一段话:按照不同容量,存储器组织成:
32个1k字节/页(小容量)128个1k字节/页(中容量)256个2k字节/页(大容量)
这段话怎么理解呢,就是告诉我们小容量的设备(内存是6k和32k)的设备是由1k字节每页组成的。
中容量的设备(内存是64k和128k)的设备是由1k字节每页组成的。大容量的设备(内存是256k、384k和512k)的设备是由2k字节每页组成的。
举个例子吧:
一个芯片的存储容量是64k,这64k是什么呢,就是64*1024个byte,一个byte是由8位0或1组成的,(比如0000 1111 这8个二进制数组成了一个字节,用十进制来说就是15)
小结一下:64k的flash可以存储64*1024个字节的数据。
咱们继续说,这64k的数据怎么划分,存储是按照页为单位进行存储的,一页1k的容量,也就说一页可以存储1024个字节。
一共是多少页?
答案是:64页,我们看一下官方是不是这么说的。
在闪存编程手册里确实是这么说的,所以我们刚才说是64页是正确的
二、 读写步骤:
上面我们知道了芯片是怎么分类的,下面我们就重点来讲解一下芯片是怎么读写的。
内部flash我们参照hal库或者标准库,直接调用st公司给我们封装好的库进行编程就可以了,这里我用的是标准库,有兴趣的小伙伴可以去看看hal库。
是不是有小伙伴会疑问什么是标准库,什么是hal库?
在这里给大家解释一下,这两个库都是st公司,直接把寄存器封装成函数,供大家直接调用某一个函数,就可以完成各种寄存器的配置,不容大家直面芯片的寄存器,方便阅读和使用,因为每个函数的名称功能都是不一样的,在调用前可以参考函数的注释,在f0和f4的标准库里甚至有每个函数的用法,不知道为什么在f1的库里把使用步骤去掉了。
咱们继续,读写的话库函数分为:
/*------------ functions used for all stm32f10x devices -----*/void flash_setlatency(uint32_t flash_latency);void flash_halfcycleaccesscmd(uint32_t flash_halfcycleaccess);void flash_prefetchbuffercmd(uint32_t flash_prefetchbuffer);void flash_unlock(void);void flash_lock(void);
flash_status flash_erasepage(uint32_t page_address);flash_status flash_eraseallpages(void);
flash_status flash_eraseoptionbytes(void);flash_status flash_programword(uint32_t address, uint32_t data);flash_status flash_programhalfword(uint32_t address, uint16_t data);flash_status flash_programoptionbytedata(uint32_t address, uint8_t data);flash_status flash_enablewriteprotection(uint32_t flash_pages);flash_status flash_readoutprotection(functionalstate newstate);
flash_status flash_useroptionbyteconfig(uint16_t ob_iwdg, uint16_t ob_stop, uint16_t ob_stdby);uint32_t flash_getuseroptionbyte(void);uint32_t flash_getwriteprotectionoptionbyte(void);
flagstatus flash_getreadoutprotectionstatus(void);flagstatus flash_getprefetchbufferstatus(void);void flash_itconfig(uint32_t flash_it, functionalstate newstate);flagstatus flash_getflagstatus(uint32_t flash_flag);void flash_clearflag(uint32_t flash_flag);flash_status flash_getstatus(void);
flash_status flash_waitforlastoperation(uint32_t timeout);
/*------------ new function used for all stm32f10x devices -----*/void flash_unlockbank1(void);void flash_lockbank1(void);flash_status flash_eraseallbank1pages(void);flash_status flash_getbank1status(void);flash_status flash_waitforlastbank1operation(uint32_t timeout);
在这里就不一个一个的详细说了,我们说一下常用的就行
1. 解锁void flash_unlock(void);
2. 上锁void flash_lock(void);
3. 页擦除flash_status flash_erasepage(uint32_t page_address);
4. 半字写入flash_status flash_programhalfword(uint32_t address, uint16_t data);
上面这4个函数就是我们最常用的。
下面说一下数据写入的步骤:
第一步:解锁。
第二步:判断写入的数据是否被擦除过,也就是判断写入的地址内存放的是不是0xffff 这里要重点说一下,为什么要判断是不是0xffff而不是判断是不是0xff呢?因为我们每次写入数据都要写入半字,也就是两个字节的数据才行,而且写入的地址只能是2的整数倍,不能是奇数。这里大家注意一下。
第三步:写入数据 stm32f103c8t6只能按照半字的方式进行数据写入,写入前的数据必须是0xffff,因为flash数据写入,只能写0,不能写1,这也就是为什么我们要先确保写入前的数据是被擦除了的原因。
第四步:上锁。
第五步:验证写入是否正确。
其实第五步可以省略。
我们看看官方给的写入过程:
好了,其实是一样的。下面我就和大家来分享一下(百分之九十九参考的正点原子的例程)。
//不检查的写入//writeaddr:起始地址//pbuffer:数据指针//numtowrite:半字(16位)数 void stmflash_write_nocheck(u32 writeaddr,u16 *pbuffer,u16 numtowrite) { u16 i; for(i=0;i《numtowrite;i++) { flash_programhalfword(writeaddr,pbuffer); writeaddr+=2;//地址增加2. } }
//从指定地址开始写入指定长度的数据//writeaddr:起始地址(此地址必须为2的倍数!!)//pbuffer:数据指针//numtowrite:半字(16位)数(就是要写入的16位数据的个数。)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;//非法地址 flash_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++)//校验数据// for(i=0;i《(stm_sector_size/2);i++)//校验数据 { if(stmflash_buf[secoff+i]!=0xffff)break;//需要擦除 // if(stmflash_buf!=0xffff)break;//需要擦除 } flash_clearflag(flash_flag_eop | flash_flag_pgerr | flash_flag_wrprterr);
if(i《secremain)//需要擦除// if(i《(stm_sector_size/2))//需要擦除 { flash_clearflag(flash_flag_eop | flash_flag_pgerr | flash_flag_wrprterr); flash_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*2); //写地址偏移 numtowrite-=secremain; //字节(16位)数递减 if(numtowrite》(stm_sector_size/2)) { secremain=stm_sector_size/2;//下一个扇区还是写不完 } else { secremain=numtowrite;//下一个扇区可以写完了 } } } flash_lock();//上锁}
最终我们调用stmflash_write()函数进行数据的写入,是不是有没看懂的小伙伴,我给大家解释一下写入的过程吧。
这个stmflash_write()函数,是说给定一个写入的地址、数据和写入的个数,然后按照给定的地址开始写数据,注意红色字体。
写数据是怎么做的呢?
首先是整理一下写入的页地址和需要写入多少页,每一页写入的话起始地址是什么然后开始一页一页的写,当遇到跨页写入的时候,把第二页的地址写进去,写的个数继续写入就行。
还有一个地方很重要,就是我修改了库函数:
/** * [url=home.php?mod=space&uid=247401]@brief[/url] programs a half word at a specified address. * [url=home.php?mod=space&uid=536309]@note[/url] this function can be used for all stm32f10x devices. * @param address: specifies the address to be programmed. * @param data: specifies the data to be programmed. * @retval flash status: the returned value can be: flash_error_pg, * flash_error_wrp, flash_complete or flash_timeout. */flash_status flash_programhalfword(uint32_t address, uint16_t data){ flash_clearflag(flash_flag_eop | flash_flag_pgerr | flash_flag_wrprterr);
flash_status status = flash_complete; /* check the parameters */ assert_param(is_flash_address(address));#ifdef stm32f10x_xl /* wait for last operation to be completed */ status = flash_waitforlastoperation(programtimeout);
if(address 《 flash_bank1_end_address) { if(status == flash_complete) { /* if the previous operation is completed, proceed to program the new data */ flash-》cr |= cr_pg_set; *(__io uint16_t*)address = data; /* wait for last operation to be completed */ status = flash_waitforlastbank1operation(programtimeout);
/* disable the pg bit */ flash-》cr &= cr_pg_reset; } } else { if(status == flash_complete) { /* if the previous operation is completed, proceed to program the new data */ flash-》cr2 |= cr_pg_set; *(__io uint16_t*)address = data; /* wait for last operation to be completed */ status = flash_waitforlastbank2operation(programtimeout);
/* disable the pg bit */ flash-》cr2 &= cr_pg_reset; } }#else /* wait for last operation to be completed */ status = flash_waitforlastoperation(programtimeout); if(status == flash_complete) { /* if the previous operation is completed, proceed to program the new data */ flash-》cr |= cr_pg_set; *(__io uint16_t*)address = data;
/* wait for last operation to be completed */ status = flash_waitforlastoperation(programtimeout); /* disable the pg bit */ flash-》cr &= cr_pg_reset; } #endif /* stm32f10x_xl */ /* return the program status */ return status;}
大家能看出来吗?就是红色字体部分,增加了一个每次写入前清除所有异常状态。为什么添加这个呢?
因为,如果你写入的数据的地址没有擦除,你就写入的话会导致异常状态的发生,而这个异常状态时要手动清除的,如果你没有清除这个异常状态,而继续写入数据的话,那么你后面写入任何数据都会报错,均写不进去,所以我在这里增加了一个异常状态清除,如果前面写入的数据报错了,不会影响我接下来的数据写入。
这里大家就清除为什么了吧。
写数据会了,那么再说一下读数据,其实这里读数据要比外部flash读取容易的多,我们直接读取地址,返回的就是地址存放的数据,是不是很简单。
看下面的函数:
//读取指定地址的半字(16位数据)//faddr:读地址(此地址必须为2的倍数!!)//返回值:对应数据.u16 stmflash_readhalfword(u32 faddr){ return *(vu16*)faddr; }//从指定地址开始读出指定长度的数据//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个字节。 }}
有没有很开心,读写数据就是这么简单就完成了。
以后如果我们想开发bootloader、把剩余的flash利用起来,就都很简单了。我会把用到的数据手册当成附件挂到下面,大家可以自行下载。(点击“阅读原文”下载)
以后我们再一起学习其他的功能,最后打个广告,st的芯片很给力,大家应该多支持,如果你觉得学到了知识的话,那么请留意评论谢谢。
LED芯片需求爆炸,扩产计划刻不容缓
MAX44284检流放大电路的介绍及设计时需注意的问题
苹果方向迷失 库克屈从利益背叛用户 苹果魅力加消退产品销量遭遇断崖式下滑
极狐GitLab一体化 DevOps平台 助力派拉软件守护千家企业安全
区块链行业创业时常见的几个误区阐述
STM32F103C8T6读写内部flash
电气控制线路图的画法及特点
DFRobot初级套件介绍
基于成像亮度计的路面亮度测试方法研究
云计算是什么?为什么要叫云计算?云计算服务有什么?
自主移动机器人的智能化表现在哪些方面
英国拟使用动物呼吸机解决医疗设备短缺问题
云原生方法为下一代5G网络基础设施提供了哪些潜在好处?
“AutoSW 2021智能汽车软件开发大会” 在沪圆满落幕
高电子迁移率晶体管在通信行业的应用
苹果A14处理器订单补齐台积电缺口,单核性能提升25%
基于无线传感技术的油气井地面测试系统[图]
苹果试图扼杀华为鸿蒙OS成型 华为或将再次遭受到打击
新一代加密货币ETHEREUM CLASSIC VISION介绍
vivoZ5x详细评测 性价比相当高