欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32串口升级

程序员文章站 2022-03-21 21:30:01
...
  • STM32升级功能分为2部分,一部分是bootloader程序,另一部分是用户应用程序组成(以STM32F103C8为例来说明 )

bootloader工程分析

启动文件的复位部分

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
     IMPORT  __main
     IMPORT  SystemInit
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

系统复位后会调用SystemInit函数,此函数在system_stm32f10x.c文件中,其中有个宏决定程序flash或SRAM偏移定义的宏

/*!< Uncomment the following line if you need to relocate your vector Table in
     Internal SRAM. */ 
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x0 /*!< Vector Table base offset field. 
                                  This value must be a multiple of 0x200. */

在SystemInit函数的最后设置这个偏移值

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 

启动方试有SRAM和FLASH两种,由宏VECT_TAB_SRAM来决定,SRAM_BASE基地址和FLASH_BASE基地址定义如下

#define FLASH_BASE            ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */
#define SRAM_BASE             ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */

通常bootloader程序会放在偏移为0开始的一段空间,刚好是FLASH_BASE基址与偏移值为0加起来的地址值,在keil中地址配置如下

STM32串口升级

程序SystemInit执行完后进入main函数

int main(void) 	
{   
	  
	FLASH_Unlock();
	UART_Init();
	BspTim2Init();
	Main_Menu ();
	
	if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
         {

                BspTim2Close();     
                JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
                Jump_To_Application = (pFunction) JumpAddress;
              
                __set_MSP(*(__IO uint32_t*) ApplicationAddress);
                Jump_To_Application();
            }
            else
            {
                SerialPutString("no user Program\r\n\n");
            }
	
  while(1); 
}
  1. 首先调用FLASH_Unlock函数解锁flash操作
  2. UART_Init函数初时化,为接收升级文件操作作准备
  3. BspTim2Init函数初时化定时器,提供超时计数用
  4. Main_Menu函数升级菜单操作功能实现
  5. 测试用户app地址是不是在APPLICATION_ADDRESS位置。检测栈顶的地址,来检验app是否下载成功

    if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000){}

        其中ApplicationAddress为用户应用程序起始地址,假如从0x8003000开始,则定义如下

#define ApplicationAddress    0x8003000

      6. APPLICATION_ADDRESS + 4对应的是app中断向量表的第二项,复位地址 

          JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);

      7.把地址强转为函数指针
          Jump_To_Application = (pFunction) JumpAddress;

        JumpAddress 和Jump_To_Application定义如下

typedef  void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;

8.  设置主函数栈指针 

 __set_MSP(*(__IO uint32_t*) ApplicationAddress);

9.调用函数,跳转到app复位地址去执行复位操作

Jump_To_Application();

写升级内容到flash,数据接收采用ymode协议进行数据接收,具体协议可找度娘了解

/*******************************************************************************
  * @函数名称:Ymodem_Receive
  * @函数说明:通过 ymodem协议接收一个文件
  * @输入参数:buf: 首地址指针
  * @输出参数:无
  * @返回参数:文件长度
  * @历史记录:     
     <作者>    <时间>      <修改记录>
*******************************************************************************/
int32_t Ymodem_Receive (uint8_t *buf)
{
    uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
    int32_t i, j, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;

    //初始化Flash地址变量
    FlashDestination = ApplicationAddress;

    for (session_done = 0, errors = 0, session_begin = 0; ;)
    {
        for (packets_received = 0, file_done = 0, buf_ptr = buf; ;)
        {
            switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
            {
            case 0:
                errors = 0;
                switch (packet_length)
                {
                    //发送端终止
                case - 1:
                    Send_Byte(ACK);
                    return 0;
                    //结束传输
                case 0:
                    Send_Byte(ACK);
                    file_done = 1;
                    break;
                    //正常的数据包
                default:
                    if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
                    {
                        Send_Byte(NAK);
                    }
                    else
                    {
                        if (packets_received == 0)
                        {
                            //文件名数据包
                            if (packet_data[PACKET_HEADER] != 0)
                            {
                                //文件名数据包有效数据区域
                                for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
                                {
                                    file_name[i++] = *file_ptr++;
                                }
                                file_name[i++] = '\0';
                                for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
                                {
                                    file_size[i++] = *file_ptr++;
                                }
                                file_size[i++] = '\0';
                                Str2Int(file_size, &size);

                                //测试数据包是否过大
                                if (size > (FLASH_SIZE - 1))
                                {
                                    //结束
                                    Send_Byte(CA);
                                    Send_Byte(CA);
                                    return -1;
                                }

                                //计算需要擦除Flash的页
                                NbrOfPage = FLASH_PagesMask(size);

                                //擦除Flash
                                for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
                                {
                                    FLASHStatus = FLASH_ErasePage(FlashDestination + (PageSize * EraseCounter));
                                }
                                Send_Byte(ACK);
                                Send_Byte(CRC16);
                            }
                            //文件名数据包空,结束传输
                            else
                            {
                                Send_Byte(ACK);
                                file_done = 1;
                                session_done = 1;
                                break;
                            }
                        }
                        //数据包
                        else
                        {
                            memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
                            RamSource = (uint32_t)buf;
                            for (j = 0; (j < packet_length) && (FlashDestination <  ApplicationAddress + size); j += 4)
                            {
                                //把接收到的数据编写到Flash中
                                FLASH_ProgramWord(FlashDestination, *(uint32_t*)RamSource);

                                if (*(uint32_t*)FlashDestination != *(uint32_t*)RamSource)
                                {
                                    //结束
                                    Send_Byte(CA);
                                    Send_Byte(CA);
                                    return -2;
                                }
                                FlashDestination += 4;
                                RamSource += 4;
                            }
                            Send_Byte(ACK);
                        }
                        packets_received ++;
                        session_begin = 1;
                    }
                }
                break;
            case 1:
                Send_Byte(CA);
                Send_Byte(CA);
                return -3;
            default:
                if (session_begin > 0)
                {
                    errors ++;
                }
                if (errors > MAX_ERRORS)
                {
                    Send_Byte(CA);
                    Send_Byte(CA);
                    return 0;
                }
                Send_Byte(CRC16);
                break;
            }
            if (file_done != 0)
            {
                break;
            }
        }
        if (session_done != 0)
        {
            break;
        }
    }
    return (int32_t)size;
}

1.收到文件名及长度会计算大小然后擦除flash

 //计算需要擦除Flash的页
 NbrOfPage = FLASH_PagesMask(size);

 //擦除Flash
 for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
 {
  FLASHStatus = FLASH_ErasePage(FlashDestination + (PageSize * EraseCounter));
 }

2.接收到文件内容写对应的flash,从用户程序 ApplicationAddress地址开始写

uint32_t FlashDestination = ApplicationAddress;

写操作如下:

 memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
 RamSource = (uint32_t)buf;
 for (j = 0; (j < packet_length) && (FlashDestination <  ApplicationAddress + size); j += 4)
{
  //把接收到的数据编写到Flash中
   FLASH_ProgramWord(FlashDestination, *(uint32_t*)RamSource);

    if (*(uint32_t*)FlashDestination != *(uint32_t*)RamSource)
     {
                                    //结束
        Send_Byte(CA);
        Send_Byte(CA);
        return -2;
      }
    FlashDestination += 4;
    RamSource += 4;
    }
    Send_Byte(ACK);

写入后会读回内容,如果写入的与读回的内容不一致,则会终止升级操作

  • 应用程序部分

此部分只需注意以下几点即可

1.  起始地址设置,即bootloader跳转到用户程序的地址

STM32串口升级

2.偏移地址设置

/*!< Uncomment the following line if you need to relocate your vector Table in
     Internal SRAM. */ 
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x3000 /*!< Vector Table base offset field. 
                                  This value must be a multiple of 0x200. */

升级时要使用有ymode协议的串口工具才能正常升级,最后,demo下载地址:

https://download.csdn.net/download/mygod2008ok/12082368

 

 

 

 

相关标签: C/C++ STM32