zl程序教程

您现在的位置是:首页 >  其它

当前栏目

STM32串口升级

升级 STM32 串口
2023-09-14 09:06:41 时间
  • 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中地址配置如下

程序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跳转到用户程序的地址

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