STM32
直播中

杨平

7年用户 1704经验值
私信 关注
[问答]

求分享基于STM32F103C8T6的Bootloder程序源码

求分享基于STM32F103C8T6的Bootloder程序源码

回帖(1)

李永清

2021-12-13 09:55:40
说明:

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

工程源码下载(fci5), 工程使用 Keil uVision5 创建 ,密码 fci5 。
XCOM V2.6下载(48cm),密码 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("Bootloderrn");
       
        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 : %drn",applen);
                                       
                                        //开始升级APP
                                        printf("Updating...rn");
                                        iap_write_appbin(FLASH_APP1_ADDR,USART1_RX_BUF,applen); //写入flash
                                        flag_update = 1;
                                        printf("Updae success !!!rn");
                                }
                                else printf("APP data error!!!rn");
                        }
                        else oldcount = USART1_RX_CNT; //更新接收到的字节数
                }       
               
                if(flag_update || (t == 300 & USART1_RX_CNT == 0)) //如果升级APP完成或3s内没收到正确的APP数据,则启动APP
                {
                        printf("APP Startrnrn");
                        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_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+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                 {
                        if(FLASH_BUF[Sector_Relative+i] != 0xFFFF) break; //需要擦除
                }
                if(i                 {
                        FLASH_ErasePage(Sector_Number*SECTOR_SIZE+FLASH_BASE);
                       
                        //替换数据
                        for(i=0;i                         {
                                FLASH_BUF[Sector_Relative+i] = pBuff;
                        }
                       
                        //写入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         {
                pBuff = 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         {
                FLASH_ProgramHalfWord(Addr,pBuff);
                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         {
                //将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
        }
}
举报

更多回帖

×
20
完善资料,
赚取积分