基于STM32F103C8T6 的 Bootloder 程序源码(另附百度网盘下载链接)

说明:

1.本例程参考正点原子的IAP的代码。
2.本例程基于STM32F103C8T6单片机编写。
3.本例程使用USART1进行程序下载,波特率115200
4.串口输出”Bootloder“字样后,请在3s内通过串口将APP的.bin文件传入单片机,之后会自动烧写并启动APP。若3s内未传入.bin文件或传入的文件有误,则会自动原来的启动APP。.bin文件最大只能15KByte
5.STM32F103C8T6拥有128KByte的flash20KByte的sram。本程序将0x0800 0000 - 0x0800 FFFF作为Bootloder空间,将0x0801 0000 - 0x0801 FFFF作为APP空间。
6.APP程序需要将flash中的向量表偏移0x10000


工程源码下载(fci5), 工程使用 Keil uVision5 创建

XCOM V2.6下载(48cm)



程序源码:

  • main.c
#include "delay.h"
#include "usart.h"
#include "flash.h"
#include "iap.h"


/***********************************************
*         支持最大15KByte的APP                 *
*				                                       *
*		0x0800 0000 - 0x0800 FFFF 为Bootloder空间  *
*   0x0801 0000 - 0x0801 FFFF 为APP空间        *
*                                              *
***********************************************/

int main ()
{
     
	u8 flag_update = 0; //升级完成后置1
	u16 t = 0;
	u16 oldcount = 0;
	u16 applen = 0;
		
	//初始化
	delay_init();
	USART1_Init(115200);
	delay_ms(20);
	printf("Bootloder\r\n");
	
	while(1)
	{
     
		if(USART1_RX_CNT) //如果接收到了数据
		{
     
			if(oldcount == USART1_RX_CNT) //如果新周期内没收到数据,则认为接收完成
			{
     
				applen = USART1_RX_CNT;
				oldcount = 0;
				USART1_RX_CNT = 0;
				
				if(((*(vu32*)(0x20001000+4)) & 0xFF000000) == 0x08000000) //判断是否为0x08XXXXXX,(是否为APP数据)
				{
     
					printf("APP len : %d\r\n",applen);
					
					//开始升级APP
					printf("Updating...\r\n");
					iap_write_appbin(FLASH_APP1_ADDR,USART1_RX_BUF,applen); //写入flash
					flag_update = 1;
					printf("Updae success !!!\r\n");
				}
				else printf("APP data error!!!\r\n");
			}
			else oldcount = USART1_RX_CNT; //更新接收到的字节数
		}	
		
		if(flag_update || (t == 300 & USART1_RX_CNT == 0)) //如果升级APP完成或3s内没收到正确的APP数据,则启动APP
		{
     
			printf("APP Start\r\n\r\n");
			iap_load_app(FLASH_APP1_ADDR);
		}		
		t++;
		delay_ms(10); 
	}	
}


  • delay.h
#ifndef _DELAY_H
#define _DELAY_H
#include "stm32f10x.h"

void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);

#endif


  • delay.c
#include "delay.h"

static u8 fac_us = 0;     //us延时倍乘数
static u16 fac_ms = 0;    //ms延时倍乘数

void delay_init(void)
{
     
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);  	//选择外部时钟 HCL/8
	fac_us = SystemCoreClock/8000000;                      	//为系统时钟的1/8
	fac_ms = (u16)fac_us * 1000;
}

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;//清空计数器
}

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;//清空计数器
}


  • usart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "stm32f10x.h"
#include "stdarg.h"

#define PRINTF_USARTx USART1      //USARTx 映射到 printf

#define EN_USART1_RX 		   	1		  //1使能接收,0失能接收。

#define USART1_REC_LEN  		15*1024   //最大接收15KByte

extern u8  USART1_RX_BUF[USART1_REC_LEN];  //USART1 储存接收到的数据
extern u16 USART1_RX_STA;         		     //USART1 接收状态即接收到的有效数据个数
extern u16 USART1_RX_CNT;

void USART1_Init(unsigned int BaudRate); //USART1用户初始化函数

void printf1(u8 *data,...);

int fputc(int ch, FILE *f);
void _sys_exit(int x);
static char *itoa(int value,char *string,int radix);
#endif


  • usart.c
#include "usart.h"

/*******************************************************************************************************************************************/
/*                                                          USART1                                                                         */
/*******************************************************************************************************************************************/

void USART1_Init(unsigned int BaudRate)
{
     
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef	USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);			//使能USART1,GPIOA时钟
	
	/*初始化USART1的Tx、Rx引脚*/
	//PA9 复用推挽输出
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//PA10浮空输入
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

//	PA9outAF_PP();   //PA9 复用推挽输出
//	PA10inFLOATING();//PA10浮空输入
	
	
	USART_InitStructure.USART_BaudRate = BaudRate;							                      //波特率
	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;		//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;				          	//收/发模式	
	USART_Init(USART1, &USART_InitStructure);
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);							                      //开启接收中断
	USART_Cmd(USART1, ENABLE);
	
	//中断设置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);				    //中断分组1:1位抢占优先级,3位响应优先级
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;			    //中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		 	  //子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				    //使能中断
	NVIC_Init(&NVIC_InitStructure);

}

/*****************************************************
如果开启了接收
*****************************************************/
#if EN_USART1_RX //如果开启了USART1接收
u8 USART1_RX_BUF[USART1_REC_LEN] __attribute__ ((at(0x20001000)));  //接收缓冲,最大USART1_REC_LEN个字节。 设定起始地址为0x20001000  
u16 USART1_RX_STA = 0;      //接收状态标记
u16 USART1_RX_CNT = 0;      //接收的字节数

void USART1_IRQHandler(void)                	
{
     
	u8 res;
	
	if(USART1->SR & (1<<5))
	{
     
		res = USART1->DR;
		
		if(USART1_RX_CNT<USART1_REC_LEN)
		{
     
			USART1_RX_BUF[USART1_RX_CNT] = res;
			USART1_RX_CNT++;
		}
	}
} 
#endif



/*******************************************************************************************************************************************/
/*                                     以下代码实现printf输出(无需MircoLIB)                                                              */
/*******************************************************************************************************************************************/

/*****************************************************
*function:	写字符文件函数
*param1:	输出的字符
*param2:	文件指针
*return:	输出字符的ASCII码
******************************************************/
int fputc(int ch, FILE *f)
{
     
	while((USART1->SR & 0x40) == 0);
	USART1->DR = (u8) ch;
	return ch;
}


/********** 禁用半主机模式 **********/
#pragma import(__use_no_semihosting)
 
struct __FILE
{
     
	int handle;
};
 
FILE __stdout;
 
void _sys_exit(int x)
{
     
	x=x;
}



/*******************************************************************************************************************************************/
/*                                                                     itoa                                                                */
/*******************************************************************************************************************************************/
static char *itoa(int value,char *string,int radix)
{
     
	int i,d;
	int flag = 0;
	char *ptr = string;
	
	if(radix != 10)
	{
     
		*ptr = 0;
		return string;
	}
	
	if(!value)
	{
     
		*ptr++ = 0x30;
		*ptr = 0;
		return string;
	}
	
	if(value < 0)
	{
     
		*ptr++ = '-';
		value *= -1;
	}
	
	for(i=10000;i>0;i /= 10)
	{
     
		d = value / i;
		
		if(d || flag)
		{
     
			*ptr++ = (char)(d + 0x30);
			value -= (d * i);
			flag = 1;
		}
	}
	
	*ptr = 0;
	return string;
}


/*******************************************************************************************************************************************/
/*                                                  用户自定义的USART1输出函数 printf1                                                     */
/*******************************************************************************************************************************************/
void printf1(u8 *data,...)
{
     
	const char *s;
	int d;
	char buf[16];
	va_list ap;
	va_start(ap,data);
	while( *data != 0)
	{
     
		if( *data == 0x5C ) // '\'
		{
     
			switch ( *++data )
			{
     
				case 'r':
					USART_SendData(USART1,0x0D);
					data++;
					break;
				
				case 'n':
					USART_SendData(USART1,0x0A);
					data++;
					break;
				
				default:
					data++;
					break;
			}
		}
		else if( *data == '%' )
		{
     
			switch ( *++data )
			{
     
				case 's': //字符串
					s = va_arg(ap,const char *);
					for(;*s;s++)
					{
     
					USART_SendData(USART1,*s);
					while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );
					}
					data++;
					break;
					
				case 'd':
					d = va_arg(ap,int);
					itoa(d,buf,10);
				
					for(s = buf;*s;s++)
					{
     
						USART_SendData(USART1,*s);
						while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );
					}
					data++;
					break;
				
				default:
					data++;
					break;
			}
		}
		else USART_SendData(USART1,*data++);
		while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	}
}


  • flash.h
#ifndef _FLASH_H
#define _FLASH_H
#include "stm32f10x.h"

/*********************** 移植 *************************/
#define FLASH_SIZE 128          //FLASH容量(KByte)
#define FLSAH_EN_WRITE 1        //1使能写入,0失能写入
/******************************************************/

#define FLSAH_BASH 0x08000000

//判断页大小
#if FLASH_SIZE < 256
#define SECTOR_SIZE 1024
#else
#define SECTOR_SIZE 2048
#endif

//提供给用户的函数
void FLASH_Write(u32 Addr,u16 *pBuff,u16 len);

u16 FLASH_ReadHalfWord(u32 faddr);                        //读取一个半字(16位)

void FLASH_Read(u32 Addr,u16 *pBuff,u16 len);             //读出一段数据(16位)

void FLASH_Writ_NoCheck(u32 Addr,u16 *pBuff,u16 len);     //不检查的写入

#endif

  • falsh.c
#include "flash.h"

u16 FLASH_BUF[SECTOR_SIZE/2]; //1KByte


//提供给用户的函数
void FLASH_Write(u32 Addr,u16 *pBuff,u16 len)
{
     
	u32 Sector_Number;  //第几扇区
	u32 Sector_Relative;   //扇区内偏移地址(按16位计算)
	u32 Sector_Remain;     //扇区内剩余地址(按16位计算)
	u32 OffAddr;           //减去0x08000000后的地址
	u16 i;
	
	//判断地址合理性
	if(Addr<FLASH_BASE || Addr>=(FLASH_BASE+1024*FLASH_SIZE))return;
	
	FLASH_Unlock();
	
	OffAddr = Addr - FLASH_BASE;
	Sector_Number = OffAddr/SECTOR_SIZE;
	Sector_Relative = (OffAddr%SECTOR_SIZE)/2;
	Sector_Remain = SECTOR_SIZE/2-Sector_Relative;
	
	//如果一页能够写完
	if(len <= Sector_Remain) Sector_Remain = len;
	
	//写入
	while(1)
	{
     
		//整页读出
		FLASH_Read(Sector_Number*SECTOR_SIZE+FLASH_BASE,FLASH_BUF,SECTOR_SIZE/2);
		
		for(i=0;i<Sector_Remain;i++)
		{
     
			if(FLASH_BUF[Sector_Relative+i] != 0xFFFF) break; //需要擦除
		}
		if(i<Sector_Remain) //需要擦除
		{
     
			FLASH_ErasePage(Sector_Number*SECTOR_SIZE+FLASH_BASE);
			
			//替换数据
			for(i=0;i<Sector_Remain;i++)
			{
     
				FLASH_BUF[Sector_Relative+i] = pBuff[i];
			}
			
			//写入FLASH
			FLASH_Writ_NoCheck(Sector_Number*SECTOR_SIZE+FLASH_BASE,FLASH_BUF,SECTOR_SIZE/2);	
		}
		else
		{
     
			FLASH_Writ_NoCheck(Addr,pBuff,Sector_Remain);
		}
		
		if(len == Sector_Remain) break; //写入结束
		else
		{
     
			Sector_Number++;          //页+1
			Sector_Relative = 0;      //页内偏移0
			pBuff += Sector_Remain;   //更新传入的数组指针
			Addr += Sector_Remain*2;    //写地址偏移
			len -= Sector_Remain;     //更新写长度
			
			if(len>(SECTOR_SIZE/2)) Sector_Remain = SECTOR_SIZE/2;
			else Sector_Remain = len;
		}
	}
	
	FLASH_Lock();
}


//读取一个半字(16位)
u16 FLASH_ReadHalfWord(u32 faddr)
{
     
	return *(vu16*)faddr; 
}


//读出一段数据(16位)
void FLASH_Read(u32 Addr,u16 *pBuff,u16 len)
{
     
	u16 i;
	for(i=0;i<len;i++)
	{
     
		pBuff[i] = FLASH_ReadHalfWord(Addr);
		Addr += 2;
	}
}

#if FLSAH_EN_WRITE //如果使能了写

//不检查的写入
void FLASH_Writ_NoCheck(u32 Addr,u16 *pBuff,u16 len)
{
     
	u16 i;
	for(i=0;i<len;i++)
	{
     
		FLASH_ProgramHalfWord(Addr,pBuff[i]);
		Addr += 2;
	}
}

#endif


  • iap.h
#ifndef _IAP_H
#define _IAP_H
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "flash.h"
#define FLASH_APP1_ADDR 0x08010000 //第一个应用程序起始地址

typedef void (*iapfun) (void); //定义一个函数类型的参数(?)

void iap_load_app(u32 addr); //执行flash里的app程序
void iap_load_appsram(u32 addr); //执行sram里的app程序
void iap_write_appbin(u32 addr,u8 *buf,u32 len); //将程序写入到指定flash地址

#endif


  • iap.c
#include "iap.h"

/*******************************************************
    0x0800 0000 - 0x0800 FFFF 为Bootloder空间
    0x0801 0000 - 0x0801 FFFF 为APP空间
*******************************************************/
iapfun jump2app; //(?)

u16 iapbuf[1024]; //按2KByte合并接收到的数据,然后写入flash

//将程序写入到指定flash地址
void iap_write_appbin(u32 addr,u8 *buf,u32 len)
{
     
	u16 t;
	u16 i = 0;
	u16 temp;
	u32 fwaddr = addr;
	u8 *dfu = buf;
	
	for(t=0;t<len;t+=2)
	{
     
		//将8位数据合并为16位数据
		temp = (u16)dfu[1]<<8;
		temp += (u16)dfu[0];
		dfu += 2;
		
		iapbuf[i++] = temp; //将合并完成的16位数据储存在数组中
			
		if(i == 1024) //合并的数据填满iapbuf缓冲区后,开始写入falsh
		{
     
			i=0;
			FLASH_Write(fwaddr,iapbuf,1024);
			fwaddr += 2048;
		}
		
	}
	
	if(i)FLASH_Write(fwaddr,iapbuf,i); //将最后一些内容写入flash

}

//执行flash里的app程序
void iap_load_app(u32 addr)
{
     
	if(((*(vu32*)addr) & 0x2FFE0000) == 0x20000000) //检查栈顶地址是否合法(?)
	{
     
		jump2app = (iapfun)*(vu32*)(addr + 4); //用户代码区第二个字为程序开始地址(复位地址)
		MSR_MSP(*(vu32*)addr); //初始化app栈顶指针(用户区的第一个字用于存放栈顶地址)
		jump2app(); //跳转到APP
	}
}

你可能感兴趣的:(STM32学习笔记,stm32,iap,bootloader)