前言
STM32单片机都带有ROM和RAM,其中STM32根据自身的ROM(Flash)可以分为小容量产品、中容量产品、大容量产品。
根据FLASH容量可以分为:
1.小容量:0-32K
2.中容量:64-128K
3.大容量:256K以上(包含256K)
代码
/**
* 函数功能: 读取指定地址的半字(16位数据)
* 输入参数: faddr:读地址(此地址必须为2的倍数!!)
* 返 回 值: 返回值:对应数据.
* 说 明:无
*/
uint16_t STMFLASH_ReadHalfWord (uint32_t faddr)
{
return *(__IO uint16_t*)faddr;
}
/**
* 函数功能: 从指定地址开始读出指定长度的数据
* 输入参数: ReadAddr:起始地址
* pBuffer:数据指针
* NumToRead:半字(16位)数
* 返 回 值: 无
* 说 明:无
*/
void STMFLASH_Read ( uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumToRead )
{
uint16_t i;
for(i = 0; i < NumToRead; i++)
{
pBuffer[i] = STMFLASH_ReadHalfWord(ReadAddr); //读取2个字节.
ReadAddr += 2; //偏移2个字节.
}
}
#if STM32_FLASH_WREN //如果使能了写
/**
* 函数功能: 不检查的写入
* 输入参数: WriteAddr:起始地址
* pBuffer:数据指针
* NumToWrite:半字(16位)数
* 返 回 值: 无
* 说 明:无
*/
void STMFLASH_Write_NoCheck( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite)
{
uint16_t i;
for(i = 0; i < NumToWrite; i++)
{
FLASH_ProgramHalfWord(WriteAddr, pBuffer[i]);
WriteAddr += 2; //地址增加2.
}
}
/**
* 函数功能: 从指定地址开始写入指定长度的数据
* 输入参数: WriteAddr:起始地址(此地址必须为2的倍数!!)
* pBuffer:数据指针
* NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
* 返 回 值:
-1;越界
-2:地址错误
* 说 明:指定长度的多页可写
*/
unsigned char StmFlash_WriteS( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite)
{
uint32_t SECTORError = 0;
uint16_t i;
uint16_t secoff; //扇区内偏移地址(16位字计算)
uint16_t secremain; //扇区内剩余地址(16位字计算)
uint32_t secpos; //扇区地址
uint32_t offaddr; //去掉0X08000000后的地址
uint32_t PageAddress;
uint16_t length_sy=0;
uint16_t length_page_sy=0;
uint16_t length_secremain_sy=0;
/*非法地址,越界:不在该款芯片的限定的可写flash区域*/
if((WriteAddr < STM32_FLASH_START) || ((WriteAddr+NumToWrite) >STM32_FLASH_END))
return -1;
/*地址不是一个半字节的整数*/
if(1 == (WriteAddr%2))
return -2;
DIS_INT
FLASH_Unlock(); //解锁
offaddr = WriteAddr - FLASH_BASE; //实际偏移地址(基于起始地址如:0x0807 0000的偏移地址).
secpos = offaddr/FLASH_PAGE_SIZE; //扇区地址 0~256 for STM32F103RCT6,实际起始地址的页数
secoff = (offaddr%FLASH_PAGE_SIZE)/2; //起始地址在扇区内的偏移(2个字节为基本单位)
secremain = FLASH_PAGE_SIZE/2 - secoff; //当前起始地址所在扇区剩余空间大小
if(NumToWrite <= secremain)//不大于该扇区范围
{
STMFLASH_Read(secpos*FLASH_PAGE_SIZE +FLASH_BASE, STMFLASH_BUF, FLASH_PAGE_SIZE/2); //读出整个扇区的内容
/* 擦除这个扇区 Fill EraseInit structure*/
PageAddress = secpos * FLASH_PAGE_SIZE + FLASH_BASE;//PageAddress:写起始地址的当前页数(扇区)
FLASH_ErasePage(PageAddress);
for(i = 0; i < secremain; i++)//复制实际写入数据插入中间段
{
STMFLASH_BUF[i + secoff] = pBuffer[i];
}
STMFLASH_Write_NoCheck( secpos*FLASH_PAGE_SIZE +FLASH_BASE, STMFLASH_BUF, FLASH_PAGE_SIZE/2); //写入整个扇区,注:将之前为了擦除的页原有的数据(前+后 两段多余空间数据)+需要写入的数据整合后,重新写入原地址。但不会影响到后面的数据
}
if(NumToWrite > secremain)//大于该扇区范围
{
/*前段*/
STMFLASH_Read( secpos*FLASH_PAGE_SIZE +FLASH_BASE, STMFLASH_BUF, FLASH_PAGE_SIZE/2); //读出整个扇区的内容
/* Fill EraseInit structure*/
PageAddress = secpos * FLASH_PAGE_SIZE + FLASH_BASE;//PageAddress:写起始地址的当前页数(扇区)
FLASH_ErasePage(PageAddress);
for(i = 0; i < secremain; i++) //复制
{
STMFLASH_BUF[i + secoff] = pBuffer[i];
}
STMFLASH_Write_NoCheck( secpos*FLASH_PAGE_SIZE +FLASH_BASE, STMFLASH_BUF,FLASH_PAGE_SIZE/2); //写入整个扇区,注:将之前为了擦除的页原有的数据(前+后 两段多余空间数据)+需要写入的数据整合后,重新写入原地址。但不会影响到后面的数据
length_sy = NumToWrite-secremain;
length_page_sy = length_sy/(FLASH_PAGE_SIZE/2);
length_secremain_sy = length_sy%(FLASH_PAGE_SIZE/2);//最后一页多出的字节
/*中段*/
pBuffer+=secremain;
for(i=0;i<length_page_sy;i++)/* Fill EraseInit structure*/
{
PageAddress += FLASH_PAGE_SIZE;
/* Fill EraseInit structure*/
FLASH_ErasePage(PageAddress);
STMFLASH_Write_NoCheck( PageAddress, pBuffer+(i*(FLASH_PAGE_SIZE/2)), FLASH_PAGE_SIZE/2); //写入整个扇区,注:将之前为了擦除的页原有的数据
}
/*后段*/
/* Fill EraseInit structure*/
pBuffer =pBuffer+(length_page_sy*(FLASH_PAGE_SIZE/2));
PageAddress += FLASH_PAGE_SIZE;
STMFLASH_Read(PageAddress, STMFLASH_BUF, FLASH_PAGE_SIZE/2); //读出整个扇区的内容
FLASH_ErasePage(PageAddress);
for(i = 0; i < length_secremain_sy; i++) //复制
{
STMFLASH_BUF[i] = *(pBuffer)++;
}
STMFLASH_Write_NoCheck(PageAddress, STMFLASH_BUF,FLASH_PAGE_SIZE/2); //写入整个扇区,注:将之前为了擦除的页原有的数据(前+后 两段多余空间数据)+需要写入的数据整合后,重新写入原地址。但不会影响到后面的数据
}
FLASH_Lock();//上锁
EN_INT
}
#endif