串口转IAP,然后到APP,串口发送11次TEST后,再进入IAP,如此循环往复工作。下面的程序经过实测,验证可行,目的是用来交流和学习。
IAP程序的main.c
#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h"
#include "delay.h"
#include "STM32_FLASH.h"
#include "USART3.h"
#include "IAP.h"
#include "CommonVariable.h"
#include "AT24cxx.h"
/********************************************************************
函数: 运行IAP程序. //
输入: 无
返回: 无.不再返回.
说明:
由于APP是在IAP的基础上运行的,因此,IAP一定是有效的,这里不再作IAP有效性检查.
APP跳IAP
************************************************************************/
void app_jump_to_iap(void)
{
u32 IapSpInitVal;//IAP程序的SP初值
u32 IapJumpAddr;//IAP程序的跳转地址.即IAP程序的入口
void (*pIapFun)(void);//定义一个函数指针,用于指向APP程序入口
// RCC_DeInit();
NVIC_SystemReset();//恢复NVIC为复位状态.使中断不再发生.
// INTX_DISABLE();//关闭所有中断
__set_CONTROL(0);//将PSP指针切换为MSP指针
IapSpInitVal = *(vu32 *)STM32_FLASH_BASE_ADDRESS;//取APP的SP初值.
IapJumpAddr = *(vu32 *)(STM32_FLASH_BASE_ADDRESS + 4);//取程序入口.
__set_MSP(IapSpInitVal);//设置SP
pIapFun = (void (*)(void))IapJumpAddr;//生成跳转函数
(*pIapFun)();//跳转,不再返回
}
/*********************************************************************
*函数名:iap_jump_to_app
*描述 :执行跳入APP
*输入 :appAddr
*输出 :无
*返回值:无
*说明:
***********************************************************************/
u8 iap_jump_to_app(u32 appAddr)
{
u32 app_msp_addr;
u32 app_jump_addr;
void (*pAppFun)(void);//定义一个函数指针,用于指向app程序入口
app_msp_addr = (*(vu32 *)FLASH_APP1_ADDR);//取栈顶地址保存的数据
app_jump_addr = (*(vu32 *)(FLASH_APP1_ADDR + 4));//取出APP程序复位中断向量的地址
if ((app_msp_addr & 0x2FFE0000) != 0x20000000)//检查app栈顶地址是否合法,判断是否已经下载程序
return 1;
RCC_DeInit();
INTX_DISABLE();//关闭所有中断
FLASH_Lock();
__set_MSP(app_msp_addr);//设置MSP指针指向app向量表的栈顶地址保存的地址
pAppFun = (void (*)(void))app_jump_addr;//生成跳转函数
(*pAppFun)();//跳转,PC指针执行复位
return 0;
}
const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
const char CPU_Run_REG[]="\r\nIAP Run!\r\n";
int main(void)
{
u16 oldcount=0; //老的串口接收数据值
u16 applenth=0; //接收到的app代码长度
u8 retdata;
SystemInit();
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
USART3_Init(115200);
printf("%s",CPU_Reset_REG);//调试串口输出"\r\nCPU reset!\r\n"
EEPROM_PIN_INIT();
IAPWorkFlag = EEPROM_U8_Data_Read1(EEPROM_IAPWorkFlag_ADDRESS);
if(IAPWorkFlag>1)
{
IAPWorkFlag=1;
EEPROM_U8_Data_Write(IAPWorkFlag,EEPROM_IAPWorkFlag_ADDRESS);
}
// if(IAPWorkFlag==1) Erase_APP_Program(FLASH_APP1_ADDR,USART3_RX_Buffer_Size);//擦除FLASH
printf("%s",CPU_Run_REG);//调试串口输出"\r\nIAP Run!\r\n"
printf("IAPWorkFlag=%u\r\n",IAPWorkFlag);
if(IAPWorkFlag==1)USART3_RX_Buffer_Load_Index=0;
INTX_ENABLE();//开启所有中断
while(1)
{
if(IAPWorkFlag==0)//执行APP程序
{
retdata=iap_jump_to_app(FLASH_APP1_ADDR);//若APP程序不能执行,则返回1
if(retdata)
{
IAPWorkFlag=1;//执行"IAP bootLoader"
}
}
if(IAPWorkFlag==1)//接收APP的bin数据
{
delay_ms(10);//串口1等待新新数据
if(USART3_RX_Buffer_Load_Index)
{
if(oldcount==USART3_RX_Buffer_Load_Index)//新周期内,没有收到任何数据,认为本次数据接收完成.
{
applenth=USART3_RX_Buffer_Load_Index;//接收到的app代码长度
oldcount=0;
USART3_RX_Buffer_Load_Index=0;
printf("用户程序接收完成!\r\n");
printf("代码长度:%dBytes\r\n",applenth);
printf("开始更新固件...\r\n");
if(((*(vu32*)(USART3_RX_Buffer_BASE_ADDRESS+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_write_appbin(FLASH_APP1_ADDR,USART3_RX_Buffer,applenth);//更新FLASH代码
printf("固件更新完成!\r\n");
IAPWorkFlag=2;//允许执行FLASH中的用户代码
}
}
else oldcount=USART3_RX_Buffer_Load_Index;
}
}
if(IAPWorkFlag==2)//执行FLASH中的用户代码
{
printf("开始执行FLASH用户代码!!\r\n");
if( ( ( *(vu32*)(FLASH_APP1_ADDR+4) )&0xFF000000 )==0x08000000 )//判断是否为0X08XXXXXX.
{
USART3_RX_Buffer[0]='\0';USART3_RX_Buffer[1]='\0';USART3_RX_Buffer[2]='\0';USART3_RX_Buffer[3]='\0';
USART3_RX_Buffer[4]='\0';USART3_RX_Buffer[5]='\0';USART3_RX_Buffer[6]='\0';
IAPWorkFlag=0;
EEPROM_U8_Data_Write(IAPWorkFlag,EEPROM_IAPWorkFlag_ADDRESS);
// iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}
else
{
printf("非FLASH应用程序,无法执行!\r\n");
IAPWorkFlag=1;//重新执行IAP
}
}
}
}
IAP的delay.c程序
#include "delay.h"
static u8 fac_us=0;//us延时倍乘数
static u16 fac_ms=0;//ms延时倍乘数,在ucos下,代表每个节拍的ms数
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS).
//当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
//首先是3个宏定义:
// delay_osrunning:用于表示OS当前是否正在运行,以决定是否可以使用相关函数
//delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始哈systick
// delay_osintnesting:用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
//然后是3个函数:
// delay_osschedlock:用于锁定OS任务调度,禁止调度
//delay_osschedunlock:用于解锁OS任务调度,重新开启调度
// delay_ostimedly:用于OS延时,可以引起任务调度.
//本例程仅作UCOSII和UCOSIII的支持,其他OS,请自行参考着移植
//支持UCOSII
#ifdef OS_CRITICAL_METHOD //OS_CRITICAL_METHOD定义了,说明要支持UCOSII
#define delay_osrunning OSRunning //OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec OS_TICKS_PER_SEC //OS时钟节拍,即每秒调度次数
#define delay_osintnesting OSIntNesting //中断嵌套级别,即中断嵌套次数
#endif
//支持UCOSIII
#ifdef CPU_CFG_CRITICAL_METHOD //CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII
#define delay_osrunning OSRunning //OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec OSCfg_TickRate_Hz //OS时钟节拍,即每秒调度次数
#define delay_osintnesting OSIntNestingCtr //中断嵌套级别,即中断嵌套次数
#endif
//us级延时时,关闭任务调度(防止打断us级延迟)
void delay_osschedlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD //使用UCOSIII
OS_ERR err;
OSSchedLock(&err); //UCOSIII的方式,禁止调度,防止打断us延时
#else //否则UCOSII
OSSchedLock(); //UCOSII的方式,禁止调度,防止打断us延时
#endif
}
//us级延时时,恢复任务调度
void delay_osschedunlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD //使用UCOSIII
OS_ERR err;
OSSchedUnlock(&err); //UCOSIII的方式,恢复调度
#else //否则UCOSII
OSSchedUnlock(); //UCOSII的方式,恢复调度
#endif
}
//调用OS自带的延时函数延时
//ticks:延时的节拍数
void delay_ostimedly(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD
OS_ERR err;
OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err); //UCOSIII延时采用周期模式
#else
OSTimeDly(ticks); //UCOSII延时
#endif
}
//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{
if(delay_osrunning==1) //OS开始跑了,才执行正常的调度处理
{
OSIntEnter(); //进入中断
OSTimeTick(); //调用ucos的时钟服务程序
OSIntExit(); //触发任务切换软中断
}
}
#endif
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8
fac_us=SystemCoreClock/8000000; //为系统时钟的1/8
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为M
reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间
//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右
fac_ms=1000/delay_ostickspersec; //代表OS可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/delay_ostickspersec秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else //不支持OS
fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数
#endif
}
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
tcnt=0;
delay_osschedlock(); //阻止OS调度,防止打断us延时
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
};
delay_osschedunlock(); //恢复OS调度
}
//延时nms
//nms:要延时的ms数
void delay_ms(u16 nms)
{
if(delay_osrunning&&delay_osintnesting==0) //如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)
{
if(nms>=fac_ms) //延时的时间大于OS的最少时间周期
{
delay_ostimedly(nms/fac_ms); //OS延时
}
nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时
}
delay_us((u32)(nms*1000)); //普通方式延时
}
#else //不用OS时
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
#endif
IAP的SYS.c程序
#include "sys.h"
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI
void WFI_SET(void)
{
__ASM volatile("wfi");
}
//关闭所有中断
void INTX_DISABLE(void)
{
__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
__ASM volatile("cpsie i");
}
//设置栈顶地址
//addr:栈顶地址
void __asm MSR_MSP(u32 addr)
{
MSR MSP, r0
BX r14
}
sys.h文件如下:
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
#define CLI() __set_PRIMASK(1) //关闭总中断
#define SEI() __set_PRIMASK(0) //打开总中断
typedef enum
{
FALSE = 0, TRUE = !FALSE
}
bool;
#ifndef NULL
#define NULL ((void *)0)
#endif
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
//以下为汇编函数
void WFI_SET(void); //执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void); //开启所有中断
void MSR_MSP(u32 addr); //设置堆栈地址
#endif
IAP的STM32_FLASH.c程序如下:
#include "STM32_FLASH.h"
#include "CommonVariable.h"
void STMFLASH_Write(u32 tWriteAddr,u16 *pBuffer,u16 tSize);
void STMFLASH_Read(u32 tReadAddr,u16 *pBuffer,u16 tSize) ;
//void Erase_APP_Program(u32 tWriteAddr,u16 tSize);
//函数功能:从CPU的FLASH地址tReadAddr处读取半字(16位数据)
//tReadAddr:CPU的FLASH地址(此地址必须为2的倍数!!)
//返回值:返回半字(16位数据)
u16 STMFLASH_ReadHalfWord(u32 tReadAddr)
{
return *(vu16*)tReadAddr;
}
#if STM32_FLASH_WREN //如果使能了写
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
//函数功能:将pBuffer[]中前tSize个半字,从FLASH起始地址为tWriteAddr开始处写入,在写之前不检查能否写入
//tWriteAddr:CPU的FLASH起始地址
//pBuffer:待写数据缓冲区的起始指针
//tSize:需要写入"半字(16位)"的个数
void STMFLASH_Write_NoCheck(u32 tWriteAddr,u16 *pBuffer,u16 tSize)
{
u16 i;
for(i=0;i<tSize;i++)//循环写入tSize个"半字"
{
FLASH_ProgramHalfWord(tWriteAddr,pBuffer[i]);
tWriteAddr=tWriteAddr+2;//CPU的FLASH地址增加2
}
}
//函数功能:将pBuffer[]中前tSize个半字,从FLASH起始地址为tWriteAddr开始处写入,在写之前检查能否写入
//tWriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:数据指针
//tSize:半字(16位)数(就是要写入的16位数据的个数.)
void STMFLASH_Write(u32 tWriteAddr,u16 *pBuffer,u16 tSize)
{
u32 tSectorNumber; //扇区编号
u16 tOffsetAddrWithinSector; //扇区内偏移地址(按照半字计算)
u16 tSizeOfSectorRemainingPart;//扇区内剩余空间大小(按照半字计算)
u32 tOffsetAddrOfFlash; //去掉0X08000000后的地址
u32 tPage_Address;//页地址
u16 t;
u16 i;
if( tWriteAddr<STM32_FLASH_BASE_ADDRESS||(tWriteAddr>=(STM32_FLASH_BASE_ADDRESS+1024*STM32_FLASH_SIZE)) )return;//非法地址
FLASH_Unlock();//解锁
tOffsetAddrOfFlash=tWriteAddr-STM32_FLASH_BASE_ADDRESS; //实际偏移地址.
tSectorNumber=tOffsetAddrOfFlash/STM_SECTOR_SIZE;//扇区编号,对于STM32F103RBT6来说扇区编号范围为0~127
tOffsetAddrWithinSector=(tOffsetAddrOfFlash%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)
tSizeOfSectorRemainingPart=STM_SECTOR_SIZE/2-tOffsetAddrWithinSector; //计算当前扇区的剩余空间大小
if(tSize<=tSizeOfSectorRemainingPart)tSizeOfSectorRemainingPart=tSize;//没有越过当前扇区的范围
while(1)//循环写FLASH
{
tPage_Address=tSectorNumber*STM_SECTOR_SIZE;tPage_Address=tPage_Address+STM32_FLASH_BASE_ADDRESS;//计算页地址
STMFLASH_Read(tPage_Address,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
for(i=0;i<tSizeOfSectorRemainingPart;i++)//校验数据
{
t=tOffsetAddrWithinSector+i;
if(STMFLASH_BUF[t]!=0XFFFF)break;//需要擦除
}
if(i<tSizeOfSectorRemainingPart)//需要擦除
{
FLASH_ErasePage(tPage_Address);//擦除这个扇区
for(i=0;i<tSizeOfSectorRemainingPart;i++)//将"pBuffer[]中的数据"覆盖"STMFLASH_BUF[]中需要修改的部分"
{
t=tOffsetAddrWithinSector+i;
STMFLASH_BUF[t]=pBuffer[i];
}
STMFLASH_Write_NoCheck(tPage_Address,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
}
else STMFLASH_Write_NoCheck(tWriteAddr,pBuffer,tSizeOfSectorRemainingPart);//写已经擦除了的,直接写入扇区剩余区间.
if(tSize==tSizeOfSectorRemainingPart)break;//写入结束了
else//写入未结束
{
tSectorNumber++;//扇区编号增1
tOffsetAddrWithinSector=0;//偏移位置为0
pBuffer=pBuffer+tSizeOfSectorRemainingPart;//修改pBuffer指针,指向需要写的数据
tWriteAddr=tWriteAddr+tSizeOfSectorRemainingPart;//修改FLASH写地址
tSize=tSize-tSizeOfSectorRemainingPart;//计算"待写数据"的个数
if(tSize>(STM_SECTOR_SIZE/2))tSizeOfSectorRemainingPart=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
else tSizeOfSectorRemainingPart=tSize;//下一个扇区可以写完了
}
}
FLASH_Lock();//上锁
}
#endif
//从CPU的FLASH地址tReadAddr处读取tSize个半字(16位数据),保存到首地址为pBuffer的缓冲区中
//tReadAddr:起始地址
//pBuffer:数据指针
//tSize:半字(16位)数
void STMFLASH_Read(u32 tReadAddr,u16 *pBuffer,u16 tSize)
{
u16 i;
for(i=0;i<tSize;i++)
{
pBuffer[i]=STMFLASH_ReadHalfWord(tReadAddr);
//从CPU的FLASH地址tReadAddr处读取半字(16位数据)
tReadAddr=tReadAddr+2;//偏移2个字节.
}
}
STM32_FLASH.h如下:
#ifndef __STMFLASH_H__
#define __STMFLASH_H__
#include "sys.h"
#define STM32_FLASH_WREN 1 //使能FLASH写入(0表示不使能;1表示使能)
#define STM32_FLASH_SIZE 512 //所选STM32的FLASH容量大小(单位为K)
#if STM32_FLASH_SIZE<256 //CPU的FLASH空间小于256K字节
#define STM_SECTOR_SIZE 1024 //每个扇区为1024个字节
#else
#define STM_SECTOR_SIZE 2048 //每个扇区为2048个字节
#endif
extern void STMFLASH_Write(u32 tWriteAddr,u16 *pBuffer,u16 tSize);
extern void STMFLASH_Read(u32 tReadAddr,u16 *pBuffer,u16 tSize) ;
//extern void Erase_APP_Program(u32 tWriteAddr,u16 tSize);
#endif
IAP的CommonVariable.c如下:
#include "CommonVariable.h"
u8 USART3_RX_Buffer[USART3_RX_Buffer_Size] __attribute__ ( (at(USART3_RX_Buffer_BASE_ADDRESS)) );//USART3接收缓冲区
u16 USART3_RX_Buffer_Load_Index __attribute__ ( (at(USART3_RX_Buffer_Load_Index_BASE_ADDRESS)) );//USART3接收的字节数
//#pragma arm section zidata="NO_INIT"
u8 IAPWorkFlag __attribute__ ( ( at(IAPWorkFlag_BASE_ADDRESS),zero_init ) );
CommonVariable.h如下:
#ifndef __CommonVariable_H
#define __CommonVariable_H
#include "sys.h"
#define STM32_FLASH_BASE_ADDRESS 0x08000000 //STM32 FLASH的起始地址
#define FLASH_APP1_ADDR 0x08010000
//第一个应用程序起始地址(存放在FLASH)
//保留0X08000000~0X0800FFFF的空间为Bootloader使用(64KB)
#define STM32_RAM_BASE_ADDRESS 0x20000000 //STM32 RAM的起始地址
#define STM32_RAM_SIZE 64 //所选STM32的RAM容量大小(单位为K)
#define USART3_RX_Buffer_Size (10*1024) //定义最大接收字节数 10K
#define USART3_RX_Buffer_BASE_ADDRESS (STM32_RAM_BASE_ADDRESS + (STM32_RAM_SIZE * 1024) - USART3_RX_Buffer_Size) //USART3_RX_Buffer[]的首地址
extern u8 USART3_RX_Buffer[USART3_RX_Buffer_Size]; //接收缓冲,最大USART3_RX_Buffer_Size个字节.末字节为换行符
#define USART3_RX_Buffer_Load_Index_BASE_ADDRESS USART3_RX_Buffer_BASE_ADDRESS-4 //USART3_RX_Buffer_Load_Index的首地址
extern u16 USART3_RX_Buffer_Load_Index;//接收的字节数
#define IAPWorkFlag_BASE_ADDRESS USART3_RX_Buffer_Load_Index_BASE_ADDRESS-4 //IAPWorkFlag的首地址
extern u8 IAPWorkFlag;
typedef void (*DefineIAPFunction)(void); //定义一个函数类型的参数
#endif
IAP的USART3.c如下:
#include "USART3.h"
#include "stdio.h"
#include "CommonVariable.h"
void USART3_Init(u32 bound);
/*
//函数功能:串口3发送一个字节
void USART3_SendByte( unsigned char ch )
{
USART_SendData(USART3, (unsigned char) ch);
while( USART_GetFlagStatus(USART3,USART_FLAG_TC)!= SET); //等待发送完成标志位被置1
}
//函数功能:串口3发送字符串
void USART3_SendString( unsigned char *str)
{
unsigned int k=0;
do{
USART3_SendByte( *(str + k) );
k++;
}while(*(str + k)!='\0');
}
*/
//函数功能:串口3初始化
void USART3_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //设置USART3的APB1外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //选择PIN10,是USART3的TXD
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置引脚为复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //选择PIN11,是USART3的RXD
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置引脚为输入上拉
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置引脚为输入悬浮
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);
//NVIC_PriorityGroup_4设置NVIC中断分组4:表示抢占优先级为4位,取值为0~15,没有响应优先级,取值为0
//NVIC_PriorityGroup_3设置NVIC中断分组3:表示抢占优先级为3位,取值为0~7,响应优先级只有1位,取值为0~1
//NVIC_PriorityGroup_2设置NVIC中断分组3:表示抢占优先级为2位,取值为0~3,响应优先级只有2位,取值为0~3
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; //选择中断源为USART3_IRQn
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7; //设置抢先优先级为7
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //设置响应优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound; //串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//串口采用8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; //设置停止位数为1位
USART_InitStructure.USART_Parity = USART_Parity_No; //设置无需进行奇偶校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件CTS和RTS流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//串口为收发模式
USART_Init(USART3, &USART_InitStructure); //初始化串口3
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接收中断,接收寄存器非空中断使能
/在串口中断服务函数中发送数据配置开始//
// USART_ITConfig(USART3,USART_IT_TXE,DISABLE); //Disnables Transmit Data Register empty interrupt
// USART_ITConfig(USART3,USART_IT_TC,ENABLE); //Enables Transmission complete interrupt
/在串口中断服务函数中发送数据配置结束//
USART_Cmd(USART3, ENABLE); //使能串口3
USART3_RX_Buffer_Load_Index=0;
// if(IAPWorkFlag==0) IAPWorkFlag=1;
// if(IAPWorkFlag>3) IAPWorkFlag=1;
//1表示执行IAP
//2表示执行SRAM中的用户代码
//3表示执行FLASH中的用户代码
}
//串口3中断服务程序
void USART3_IRQHandler(void)
{
u8 Res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART3);//读串口3
if(USART3_RX_Buffer_Load_Index<USART3_RX_Buffer_Size)
{
USART3_RX_Buffer[USART3_RX_Buffer_Load_Index]=Res;
USART3_RX_Buffer_Load_Index++;
}
}
}
#if USART3_Printf_Enable == 1
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
//函数功能:发送ch的值给USART3串口
int fputc(int ch, FILE *f)
{
USART_SendData(USART3, (unsigned char) ch);
while( USART_GetFlagStatus(USART3,USART_FLAG_TC)!= SET); //等待发送完成标志位被置1
return ch;
}
#elif USART3_Printf_Enable == 2 //使用JLINK虚拟串口的printf功能
#define ITM_Port8(n) (*((volatile unsigned char *)(0xE0000000+4*n)))
#define ITM_Port16(n) (*((volatile unsigned short*)(0xE0000000+4*n)))
#define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*n)))
#define DEMCR (*((volatile unsigned long *)(0xE000EDFC)))
#define TRCENA 0x01000000
struct __FILE
{
int handle; /* Add whatever you need here */
};
FILE __stdout;
FILE __stdin;
int fputc(int ch, FILE *f)
{
if (DEMCR & TRCENA)
{
while (ITM_Port32(0) == 0);
ITM_Port8(0) = ch;
}
return(ch);
}
#endif
USART3.h文件如下:
#ifndef __USART3_H
#define __USART3_H
#include "sys.h"
//#define USART3_Printf_Enable 0 //不使用printf功能
#define USART3_Printf_Enable 1 //使用USART3的printf功能
//#define USART3_Printf_Enable 2 //使用JLINK虚拟串口的printf功能
extern void USART3_Init(u32 bound);
#endif
IAP的IAP.c如下
#include "IAP.h"
#include "STM32_FLASH.h"
#include "CommonVariable.h"
DefineIAPFunction Jump_To_APP;
u16 iapbuf[1024];
//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节)
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
u16 t;
u16 i=0;
u16 temp;
u32 fwaddr=appxaddr;//当前写入的地址
u8 *dfu=appbuf;
for(t=0;t<appsize;t+=2)
{
temp=(u16)dfu[1]<<8;temp+=(u16)dfu[0];//合成半字(16位),生成"待写数据"
dfu+=2;//将dfu指针加2,为下次读数据做准备
iapbuf[i++]=temp;//将"待写数据"保存到iapbuf[]中
if(i==1024)//满1K个半字,则执行写入CPU的FLASH
{
i=0;
STMFLASH_Write(fwaddr,iapbuf,1024);
fwaddr+=2048;//偏移2048 16=2*8.所以要乘以2
}
}
if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去
}
//跳转到应用程序段
//appxaddr:用户代码起始地址
void iap_load_app(u32 appxaddr)
{
if( ( (*(vu32*)appxaddr)&0x2FFE0000 )==0x20000000 ) //检查栈顶地址是否合法
{
Jump_To_APP=(DefineIAPFunction)*(vu32*)(appxaddr+4);//用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr);//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
Jump_To_APP(); //跳转到APP
}
}
IAP.h文件如下:
#ifndef __IAP_H__
#define __IAP_H__
#include "sys.h"
void iap_load_app(u32 appxaddr); //跳转到APP程序执行
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen);//在指定地址开始,写入bin
#endif
IAP的AT24cxx.c如下:
#include "AT24cxx.h"
#include "sys.h"
#include "delay.h"
//函数功能:sel=1设置GPIOB9为输出口,sel=0,则设置GPIOB9为输入上拉
void EEPROM_SDA_PIN_Configuration(u8 sel)
{
GPIO_InitTypeDef GPIO_InitStructure;
if(sel==1)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //选择第9脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO口的最高工作频率为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
}
else
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //选择第9脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择输入上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO口的最高工作频率为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
}
}
//函数功能:sel=1设置GPIOB8为输出口,sel=0,则设置GPIOB8为输入上拉
void EEPROM_SCL_PIN_Configuration(u8 sel)
{
GPIO_InitTypeDef GPIO_InitStructure;
if(sel==1)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //选择第1脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO口的最高工作频率为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
}
else
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //选择第1脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择输入上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO口的最高工作频率为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
}
}
//函数功能:EEPROM引脚初始化
void EEPROM_I2c_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//使用GPIO_InitTypeDef定义一个结构变量GPIO_InitStructure;
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOB, ENABLE ); //在配置外设之前,必须先使能GPIOB的外设时钟
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8; //选择第1脚
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置引脚的最高输出速率为50MHz
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //设置引脚工作模式为推挽输出方式
GPIO_Init( GPIOB, &GPIO_InitStructure);
//根据GPIO_InitStructure结构变量指定的参数初始化GPIOB的外设寄存器
EEPROM_SCL_SET(); //PB1输出高电平
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //选择第9脚
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置引脚的最高输出速率为50MHz
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //设置引脚工作模式为推挽输出方式
GPIO_Init( GPIOB, &GPIO_InitStructure);
//根据GPIO_InitStructure结构变量指定的参数初始化GPIOB的外设寄存器
EEPROM_SDA_SET(); //PB0输出高电平
IWDG_ReloadCounter(); //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
delay_ms(10); //上电等待时间为10ms;
IWDG_ReloadCounter(); //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
}
//主机发送"启动条件"函数;
void I2CStart_EEPROM(void)
{
//EA=0; //屏蔽所有中断
EEPROM_SCL_CLR(); //SCL输出低电平
EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口
EEPROM_SDA_SET(); //SDA输出高电平;
delay_us(10); //空闲时间至少保持4.7us;
EEPROM_SDA_SET(); //EEPROM SDA输出高电平
EEPROM_SCL_SET(); //EEPROM SCL输出高电平,将EEPROM SDA脚和EEPROM SCL置为高电平,为发送启动条件做准备
delay_us(7); //启动建立时间要至少为4.7us
EEPROM_SDA_CLR(); //在EEPROM CL为高电平期间,EEPROM SDA脚出现下降沿,表示"起动I2C"
delay_us(6); //启动保持时间为4us;
EEPROM_SCL_CLR(); //EEPROM SCL脚置低电平,为下次移位做准备;
}
//主机发送"停止条件"函数;
void I2CStop_EEPROM(void)
{
//将EEPROM SDA脚和EEPROM SCL置为低电平,为发送"停止条件"做准备
EEPROM_SCL_CLR(); //SCL输出低电平
EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口
EEPROM_SDA_CLR(); //EEPROM SDA输出低电平
delay_us(5); //时钟低电平时间至少保持4.7us
EEPROM_SCL_SET(); //EEPROM SCL输出高电平
delay_us(10); //停止条件建立时间至少为4us;
EEPROM_SDA_SET(); //EEPROM SDA输出高电平,在EEPROM_SCLK为高电平期间,EEPROM_IO脚出现上降沿,表示"停止I2C"
delay_us(14); //停止条件保持时间至少为4us;
//EA=1; //开所有中断允许位
}
//从I2C输出一个字节
void write_I2C_byte_To_EEPROM(unsigned char txByte)
{
unsigned char mask;
unsigned char tmpValue;
IWDG_ReloadCounter(); //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
EEPROM_SCL_CLR(); //SCL输出低电平
EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口
EEPROM_SDA_SET(); //SDA输出高电平;
delay_us(5); //时钟高电平时间至少保持4us
for(mask=0x80; mask>0;) //shift bit for masking (8 times)
{
tmpValue=(unsigned char)(mask & txByte);
if ( tmpValue == 0 ) EEPROM_SDA_CLR(); //SDA输出低电平
else EEPROM_SDA_SET(); //SDA输出高电平;
delay_us(2); //数据建立时间至少为0.25us
EEPROM_SCL_SET(); //SCL输出高电平 //generate clock pulse on SCL
delay_us(10); //时钟高电平时间至少保持4us
EEPROM_SCL_CLR(); //SCL输出低电平
delay_us(10); //时钟低电平时间至少保持4.7us
mask=(unsigned char)(mask>>1); //将mask的值右移一位,左端补0,为写下一位值做准备
}
}
//从I2C读入一个字节
unsigned char read_I2C_byte_Fr_EEPROM()
{
unsigned char mask,rxByte=0;
IWDG_ReloadCounter(); //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
EEPROM_SCL_CLR(); //SCL输出低电平
EEPROM_SDA_PIN_Configuration(0); //设置SDA为输入口
delay_us(5); //时钟高电平时间至少保持4us
for(mask=0x80;mask>0;) //shift bit for masking (8 times)
{
EEPROM_SCL_SET(); //SCL输出高电平 //start clock on SCL-line
delay_us(4); //时钟高电平时间至少保持4us
if( EEPROM_SDA() ) rxByte=(unsigned char)(rxByte | mask); //read bit
EEPROM_SCL_CLR(); //SCL输出低电平
delay_us(5); //时钟低电平时间至少保持4.7us
mask=(unsigned char)(mask>>1); //将mask的值右移一位,左端补0,为读取下一位值做准备
}
return(rxByte);
}
//函数说明:I2C专用,等待从器件接收方的应答
bool WaitAck_Fr_EEPROM(void)
{
unsigned char errtime=255;//因故障接收方无ACK,超时值为255.
EEPROM_SCL_CLR(); //SCL输出低电平
EEPROM_SDA_PIN_Configuration(0); //设置SDA为输入口
delay_us(2); //数据建立时间至少为0.25us;
EEPROM_SCL_SET(); //SCL输出高电平,//EEPROM SCL上升沿到来时,EEPROM发送ACK信号;
delay_us(10); //时钟高电平时间至少保持4us;
while(EEPROM_SDA())
{
errtime--;
delay_ms(1); //等待1ms
if (!errtime)
{
I2CStop_EEPROM();
delay_ms(3000);
IWDG_ReloadCounter(); //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
return FALSE;
}
}
EEPROM_SCL_CLR(); //SCL输出低电平
delay_us(5); //时钟低电平时间至少保持4.7us;
return TRUE;
}
//函数说明:I2C专用,CPU为接收方,EEPROM为发送方时,CPU发送应答信号;
void SendAck_To_EEPROM(void)
{
EEPROM_SCL_CLR(); //SCL输出低电平
EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口
EEPROM_SDA_CLR(); //将EEPROM_IO脚设置为低电平,该位值是即发出的"应答信号";
delay_us(1); //数据建立时间至少为0.25us;
EEPROM_SCL_SET(); //SCL输出高电平//EEPROM SCL上升沿到来时,EEPROM收到一位值;
delay_us(4); //时钟高电平时间至少保持4us;
EEPROM_SCL_CLR(); //SCL输出低电平//EEPROM_SCLK脚置低电平,一个移位时钟结束;
EEPROM_SDA_PIN_Configuration(0); //设置SDA为输入口
delay_us(5); //时钟低电平时间至少保持4.7us;
}
//函数说明:I2C专用,主器件为接收方,从器件为发送方时,非应答信号。
void SendNotAck_To_EEPROM(void)
{
EEPROM_SCL_CLR(); //SCL输出低电平
EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口//将EEPROM_IO脚设置为高电平,该位值是即发出的"非应答信号";
delay_us(1); //数据建立时间至少为0.25us;
EEPROM_SCL_SET(); //SCL输出高电平//EEPROM_SCLK脚上升沿到来时,EEPROM收到一位值;
delay_us(4); //时钟高电平时间至少保持4us;
EEPROM_SCL_CLR(); //SCL输出低电平//EEPROM_SCLK脚置低电平,一个移位时钟结束;
EEPROM_SDA_PIN_Configuration(0); //设置SDA为输入口
delay_us(5); //时钟低电平时间至少保持4.7us;
}
//开始24LC256驱动程序
/**-------------------------------------------------------------------------------
函数说明:从EEPROM芯片24LC256的某个地址单元addr,读取到rerurn_value中.
并将读到的值返回;
---------------------------------------------------------------------------------*/
u8 EEPROM_U8_Data_Read1(u16 addr)
{
u8 rerurn_value;
union EEPROM_Addr_TYPE temp;
temp.Address=addr;
//delay_ms(1); //在连续进行"字节连续读"时,此处必须加delay_ms(5),否则程序可能读不到EEPROM中的数据;
I2CStart_EEPROM(); //发开始条件
write_I2C_byte_To_EEPROM(0xA0); //发送"写器件地址",24LC256的"写器件地址"为0xA0
WaitAck_Fr_EEPROM(); // 等待从器件接收方的应答
write_I2C_byte_To_EEPROM(temp.b[1]); //发送器件的子地址高8位值;
WaitAck_Fr_EEPROM(); // 等待从器件接收方的应答
write_I2C_byte_To_EEPROM(temp.b[0]); //发送器件的子地址低8位值;
WaitAck_Fr_EEPROM(); //等待EEPROM_IO脚被24LC256拉低;
//delay_ms(1);
I2CStart_EEPROM(); //发重新开始条件
write_I2C_byte_To_EEPROM(0xA1); //发送"读器件地址"
WaitAck_Fr_EEPROM(); //等待EEPROM_IO脚被24LC256拉低;
rerurn_value=read_I2C_byte_Fr_EEPROM();
SendNotAck_To_EEPROM(); //CPU发送不应答信号
I2CStop_EEPROM();
return(rerurn_value);
}
/**-------------------------------------------------------------------------------
函数说明:从EEPROM芯片24LC256的某个地址单元开始,连续读取count个字节;
设置要读的第一个地址addr,并设置读取的字节数count,则会一次把数据读取到buff中.
---------------------------------------------------------------------------------*/
void EEPROM_U8_Data_Readn(u8 *buff,u8 count,u16 addr)
{
unsigned char i;
union EEPROM_Addr_TYPE temp;
temp.Address=addr;
delay_ms(1); //在连续进行"字节连续读"时,此处必须加delay_ms(5),否则程序可能读不到EEPROM中的数据;
I2CStart_EEPROM(); //发开始条件
write_I2C_byte_To_EEPROM(0xA0); //发送"写器件地址",24LC256的"写器件地址"为0xA0
WaitAck_Fr_EEPROM(); // 等待从器件接收方的应答
write_I2C_byte_To_EEPROM(temp.b[1]); //发送器件的子地址高8位值;
WaitAck_Fr_EEPROM(); // 等待从器件接收方的应答
write_I2C_byte_To_EEPROM(temp.b[0]); //发送器件的子地址低8位值;
WaitAck_Fr_EEPROM(); //等待EEPROM_IO脚被24LC256拉低;
I2CStart_EEPROM(); //发重新开始条件
write_I2C_byte_To_EEPROM(0xA1); //发送"读器件地址"
WaitAck_Fr_EEPROM(); //等待EEPROM_IO脚被24LC256拉低;
for (i=0;i<count;i++)
{
buff[i]=read_I2C_byte_Fr_EEPROM();
if (i!=count-1) SendAck_To_EEPROM(); //除最后一个字节外,其他都要从MASTER发应答。
}
SendNotAck_To_EEPROM(); //CPU发送不应答信号
I2CStop_EEPROM();
}
/**-------------------------------------------------------------------------------
函数说明:将x的值写入指定的EEPROM芯片24LC256的某个地址单元addr;
注意:在I2CStop_EEPROM()和I2CStart_EEPROM()两句之间,必须有delay_ms(5)语句,否则,读写可能不正确;
---------------------------------------------------------------------------------*/
void EEPROM_U8_Data_Write(u8 x,u16 addr)
{
union EEPROM_Addr_TYPE temp;
IWDG_ReloadCounter(); //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
CLI(); //关闭总中断
temp.Address=addr;
I2CStart_EEPROM(); //发开始条件
write_I2C_byte_To_EEPROM(0xA0); //发送"写器件地址",24LC256的"写器件地址"为0xA0
WaitAck_Fr_EEPROM(); //等待EEPROM_IO脚被EEPROM拉低;
write_I2C_byte_To_EEPROM(temp.b[1]); //发送器件的子地址高8位值;
WaitAck_Fr_EEPROM(); // 等待从器件接收方的应答
write_I2C_byte_To_EEPROM( temp.b[0] ); //发送器件的子地址低8位值;
WaitAck_Fr_EEPROM(); //等待EEPROM_IO脚被EEPROM拉低;
write_I2C_byte_To_EEPROM(x); //将value的值写入EEPROM;
WaitAck_Fr_EEPROM(); //等待EEPROM_IO脚被EEPROM拉低;
I2CStop_EEPROM();
SEI();//打开总中断
delay_ms(5); //写等待时间至少保持4ms;
}
//函数功能:初始化EEPROM引脚;
void EEPROM_PIN_INIT(void)
{
EEPROM_I2c_Init();
delay_ms(10); //上电等待时间为10ms;
}
//函数功能:将双字节数x保存到EEPROM中addr地址中
void EEPROM_U16_Data_Write(u16 x,u16 addr)
{
union EEPROM_Uint16_Data_TYPE temp;
temp.Data=x;
EEPROM_U8_Data_Write(temp.b[0],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[1],addr);
addr++;
}
//函数功能:从EEPROM中读取双字节数
u16 EEPROM_U16_Data_Read(u16 addr)
{
union EEPROM_Uint16_Data_TYPE temp;
temp.Data=0;
temp.b[0]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[1]=EEPROM_U8_Data_Read1(addr);
addr++;
return(temp.Data);
}
//函数功能:将双字节数x保存到EEPROM中addr地址中
void EEPROM_U32_Data_Write(u32 x,u16 addr)
{
union EEPROM_U32_Data_TYPE temp;
temp.Data=x;
EEPROM_U8_Data_Write(temp.b[0],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[1],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[2],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[3],addr);
addr++;
}
//函数功能:从EEPROM中读取双字节数
u32 EEPROM_U32_Data_Read(u16 addr)
{
union EEPROM_U32_Data_TYPE temp;
temp.Data=0;
temp.b[0]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[1]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[2]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[3]=EEPROM_U8_Data_Read1(addr);
addr++;
return(temp.Data);
}
//函数功能:将浮点数x保存到EEPROM中addr地址中,浮点数占用4个字节
void EEPROM_Float_Data_Write(float x,u16 addr)
{
union EEPROM_FLOAT_DATA_TYPE temp;
temp.float_data=x;
EEPROM_U8_Data_Write(temp.b[0],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[1],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[2],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[3],addr);
}
//函数功能:从EEPROM中读取浮点数,浮点数占用4个字节
float EEPROM_Float_Data_Read(u16 addr)
{
union EEPROM_FLOAT_DATA_TYPE temp;
temp.float_data=0;
temp.b[0]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[1]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[2]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[3]=EEPROM_U8_Data_Read1(addr);
return(temp.float_data);
}
//函数功能:将浮点数x保存到EEPROM中addr地址中,浮点数占用8个字节
void EEPROM_Double_Data_Write(double x,u16 addr)
{
union EEPROM_DOUBLE_DATA_TYPE temp;
temp.float_data=x;
EEPROM_U8_Data_Write(temp.b[0],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[1],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[2],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[3],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[4],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[5],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[6],addr);
addr++;
EEPROM_U8_Data_Write(temp.b[7],addr);
}
//函数功能:从EEPROM中读取浮点数,浮点数占用8个字节
double EEPROM_Double_Data_Read(u16 addr)
{
union EEPROM_DOUBLE_DATA_TYPE temp;
temp.float_data=0;
temp.b[0]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[1]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[2]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[3]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[4]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[5]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[6]=EEPROM_U8_Data_Read1(addr);
addr++;
temp.b[7]=EEPROM_U8_Data_Read1(addr);
return(temp.float_data);
}
void Save_String_To_EEPROM(char *pString,u16 len_max,u16 addr)
{
char *p;
uint16_t i;
uint16_t address;
i=0;
address=addr;
p=pString;
while(i<len_max)
{
if(*p!='\0')
{
EEPROM_U8_Data_Write(*p,address);
}
else
{
EEPROM_U8_Data_Write('\0',address);//写结束符号
i=len_max;
}
address++;
p++;
i++;
}
}
//函数功能:将pString中的字符串保存到EEPROM中,起始地址为addr
void Read_String_From_EEPROM(u8 *pString,unsigned char len_max,u16 addr)
{
EEPROM_U8_Data_Readn(pString,len_max,addr);
}
AT24cxx.h文件如下:
#ifndef _24LC256_H
#define _24LC256_H
#include "stm32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "sys.h" //启用bool定义
#define EEPROM_IAPWorkFlag_ADDRESS 4095
//24LC256共计512页,每页64字节,合计4K字节
#define EEPROM_SCL_SET() GPIO_SetBits(GPIOB,GPIO_Pin_8) //设置PB8输出高电平
#define EEPROM_SCL_CLR() GPIO_ResetBits(GPIOB,GPIO_Pin_8) //设置PB8输出低电平
#define EEPROM_SCL() GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8)//读取PB8
#define EEPROM_SDA_SET() GPIO_SetBits(GPIOB,GPIO_Pin_9) //设置PB9输出高电平
#define EEPROM_SDA_CLR() GPIO_ResetBits(GPIOB,GPIO_Pin_9) //设置PB9输出低电平
#define EEPROM_SDA() GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)//读取PB9
#define EEPROM_Total_Page_Number 512 //24LC256共计512页,每页64字节
#define Byte_Number_Per_Page 64
extern unsigned char Write_to_value[Byte_Number_Per_Page];
union EEPROM_Addr_TYPE
{ unsigned char b[2]; //b[1]和ptr的高8位值相等;b[0]和Address的低8位值相等;
uint16_t Address;
};
union EEPROM_Uint16_Data_TYPE
{ unsigned char b[2]; //b[1]和Data的高8位值相等;b[0]和Data的低8位值相等;
uint16_t Data;
};
union EEPROM_U32_Data_TYPE
{ u8 b[4]; //b[1]和Data的高8位值相等;b[0]和Data的低8位值相等;
u32 Data;
};
union EEPROM_FLOAT_DATA_TYPE
{ u8 b[4]; //b[3]和float_data的高8位值相等;b[0]和float_data的低8位值相等;
float float_data;
};
union EEPROM_DOUBLE_DATA_TYPE
{ u8 b[8]; //b[7]和float_data的高8位值相等;b[0]和float_data的低8位值相等;
double float_data;
};
extern void EEPROM_PIN_INIT(void);
extern void EEPROM_U8_Data_Write(u8 x,u16 addr);
extern u8 EEPROM_U8_Data_Read1(u16 addr);
extern void EEPROM_U8_Data_Readn(u8 *buff,u8 count,u16 addr);
extern void EEPROM_U16_Data_Write(u16 x,u16 addr);
extern u16 EEPROM_U16_Data_Read(u16 addr);
extern void EEPROM_U32_Data_Write(u32 x,u16 addr);
extern u32 EEPROM_U32_Data_Read(u16 addr);
extern void EEPROM_Float_Data_Write(float x,u16 addr);
extern float EEPROM_Float_Data_Read(u16 addr);
extern void EEPROM_Double_Data_Write(double x,u16 addr);
extern double EEPROM_Double_Data_Read(u16 addr);
extern void Save_String_To_EEPROM(char *pString,u16 len_max,u16 addr);
extern void Read_String_From_EEPROM(u8 *pString,unsigned char len_max,u16 addr);
#endif
以上是IAP程序部分;
下面是APP程序:
APP的main.c如下:
#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h"
#include "delay.h"
#include "string.h"
#include "CommonVariable.h"
//#include "USART3.h"
#include "AT24cxx.h"
/********************************************************************
函数: 运行IAP程序. //
输入: 无
返回: 无.不再返回.
说明:
由于APP是在IAP的基础上运行的,因此,IAP一定是有效的,这里不再作IAP有效性检查.
APP跳IAP
************************************************************************/
void app_jump_to_iap(void)
{
u32 IapSpInitVal;//IAP程序的SP初值
u32 IapJumpAddr;//IAP程序的跳转地址.即IAP程序的入口
void (*pIapFun)(void);//定义一个函数指针,用于指向APP程序入口
// RCC_DeInit();
NVIC_SystemReset();//恢复NVIC为复位状态.使中断不再发生.
// INTX_DISABLE();//关闭所有中断
__set_CONTROL(0);//将PSP指针切换为MSP指针
IapSpInitVal = *(vu32 *)STM32_FLASH_BASE_ADDRESS;//取APP的SP初值.
IapJumpAddr = *(vu32 *)(STM32_FLASH_BASE_ADDRESS + 4);//取程序入口.
__set_MSP(IapSpInitVal);//设置SP
pIapFun = (void (*)(void))IapJumpAddr;//生成跳转函数
(*pIapFun)();//跳转,不再返回
}
const char APP_Run_REG[]="\r\nAPP Run!\r\n";
const char CPU_OK_REG[]="\r\nOK!\r\n";
int main(void)
{
u8 cnt;
// SCB->VTOR = FLASH_BASE | 0x10000;
SCB->VTOR = FLASH_APP1_ADDR;//中断向量表重定义
delay_init(); //延时函数初始化
EEPROM_PIN_INIT();
printf("%s",APP_Run_REG);//调试串口输出"\r\nAPP Run!\r\n"
cnt=0;
INTX_ENABLE();//开启所有中断
while(1)
{
if( NULL!=strstr( (char*)USART3_RX_Buffer,"TEST\r\n") )
{
USART3_RX_Buffer[0]='\0';USART3_RX_Buffer[1]='\0';USART3_RX_Buffer[2]='\0';USART3_RX_Buffer[3]='\0';
USART3_RX_Buffer[4]='\0';USART3_RX_Buffer[5]='\0';USART3_RX_Buffer[6]='\0';
USART3_RX_Buffer_Load_Index=0;
printf("%s",CPU_OK_REG);
cnt++;
if(cnt>10)//执行"IAP bootLoader"
{
printf("Ready Update...\r\n");
IAPWorkFlag=1;
EEPROM_U8_Data_Write(IAPWorkFlag,EEPROM_IAPWorkFlag_ADDRESS);
app_jump_to_iap();
}
}
}
}
APP的sys.c和IAP的相同,这里不再添加。
APP的delay.c和IAP的相同,这里不再添加。
APP的CommonVariable.c和IAP的相同,这里不再添加。
APP的USART3.c和IAP的相同,这里不再添加。
以上是APP程序部分;
IAP和APP分别创建工程,这里不再赘述。
STM32F103VET6工程配置介绍如下: