×

博客分享:单片机内部FLASH的字节操作

消耗积分:3 | 格式:pdf | 大小:327.5KB | 2021-01-29

自我清欢

分享资料个

一般32位单片机的内部FALSH是不支持字节操作的,有的可以按字节读取,但是不能按字节写入。

而且,一般单片机内部FALSH擦除的最小单位都是页,如果向某页中的某个位置写入数据,恰好这个位置的前面存了其他数据,那么就必须把这页擦除,存的其他数据也会丢失。

实际上就是说内部的FALSH不好做改写的操作,如果有很多数据需要存放,最好是分页存储。这也是FALSH与E2PROM最大的区别,后者支持按字节操作且无需擦除,即使某一个地址写坏了,也不影响其他地址。

下面介绍一种方法让内部FLASH"支持"字节操作,且同一页的其他数据不受影响。

方法原理很简单,下面简单介绍下原理:

1.根据要写入地址,计算出该地址位于哪一页;

2.读出整个页,存入缓存BUF;

3.将要写入的数据按位置更新到BUF中;

4.擦除该页;

5.写入整个BUF。

可以看出这种方法弊端很明显:

1.耗时长 每次写都要读整个BUF,然后还要先把数据存到BUF里,然后再写入整个BUF;

2.FALSH擦写次数增加,降低使用寿命;

下面给出测试代码:

#include 
#include 
#include //C语言标准库
#include "flash.h"
 
#define USER_FLASH_START_ADDR   0x01070000   //FLASH最后两个扇区  供用户使用
 
 
u32tou8 u32data;//定义一个联合体
 
//==================================================================================
// 获取某个地址所在的页首地址
// addr:FLASH地址
// 返回:该地址所在的页 共128页(0~127)
//==================================================================================
unsigned int FLASH_GetFlashPage(unsigned int addr)
{
	  if (IS_FLASH_ADDRESS(addr))
		{
		   return  (addr&(~0xFFF));//清0低12位就是该页的起始地址
  	} 
}
//==================================================================================
// 从FLASH中读取 一个字(32位)
// addr:读取地址
// 返回: 读到的字数据
//备注: 地址为4字节对齐
//==================================================================================
unsigned int FLSAH_ReadWord(unsigned int addr)
{
    return (*(unsigned int *)addr);
}
 
 
//==================================================================================
//从FLASH指定地址 读取数据
//备注: 读取数据类型为32位  读取地址为4字节对齐
//==================================================================================
void  FLASH_Read(unsigned int	ReadAddr,unsigned char *pBuffer,unsigned int NumToRead)
{
    unsigned int i;
	  u32tobyte cache;
    for(i=0; iRO = 0;//去掉所有扇区写保护
    //==================================================================================
    // 判断写入地址是否非法  起始地址或者结束地址不在FALSH范围内则退出
    //==================================================================================
    if(!(IS_FLASH_ADDRESS(startaddr)&& IS_FLASH_ADDRESS(endaddr))) return FLASH_ERROR_PG;
   
	   while(startaddr  remain)//需要写入的数据量大于缓冲buf剩余字节数
				{
					for(i=index;i<4096;i++)//将需要写入FALSH的数据写入缓冲buff
					{
							 buffer[i]=*(pBuffer++);				
					}
					NumToWrite-=remain;//需要写入的数据长度-本次已经写入的数据长度	
          startaddr+=remain;//地址向后偏移本次写入的字节数					
			  }
				else
				{
				  for(i=index;i

其中还有个联合体的定义:

typedef union
{
    unsigned int  data;
    unsigned char buf[4];
}
u32tou8;

FLASH_ErasePage、FLASH_ProgramWord、IS_FLASH_ADDRESS 这三个都是单片机FLASH的库函数

各家单片机不同,但功能基本相同,这里不再提供源码。

最后提供以下两个FLASH接口即可:

FLASH_Write(unsigned int    WriteAddr,unsigned char *pBuffer,unsigned int NumToWrite);
 
FLASH_Read(unsigned int    ReadAddr,unsigned char *pBuffer,unsigned int NumToRead)

演示:

1.为方便查看结果,测试从0x1070FFC的位置开始写入数据,FLASH地址分布如下图所示:

这里展示了FLASH连续两页的地址,首先将这两页全部擦除。


2.接着从1070FFC的位置开始写入56个1,这样就保证了数据跨越了1页。

unsigned char write[]= {"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111"};
FLASH_Write(0x01070FFC,write,sizeof(write));

注意:最后的00是因为字符串的结尾字符是“/0”

3.紧接着,在0x1070FFE位置写入新的字符串,也要保证写入长度跨越1页。

unsigned char write2[]={"23456789"};
FLASH_Write(0x01070FFE,write2,sizeof(write2));

可以看出,0x1070FFE~0x1071006的位置被写入了新的字节,但这两页的其他位置数据保持不变。

总结:

1、实际使用时,如果不是受限于成本或者FLASH大小,不建议这样读写内部FLASH,以为stm32内部FLASH也就

10W次寿命,这样频繁擦写会大大降低FLASH寿命。

2、如果保存的数据不多,建议每个数据都单独存1页,这样不用考虑擦除时会把其他数据也一并擦除。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_24835087/article/details/103541322



声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'博客分享:单片机内部FLASH的字节操作',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.obk20.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);