STM32+Python用XMODEM协议升级固件之STM32固件开发

本文章针对有嵌入式编程基础的技术人员,Python制作上位机软件工具需要会使用Pycharm工具。文章不再具体描述嵌入式开发的基础和Python的基础语法等问题,有些语法会在文章中间说明,其中Python制作的软件升级工具成功升级STM32F103CT6和STC8H8K64U的固件,着重讲解Python+PYQT5+STM32F103C8T6使用XMODEM协议升级固件的开发过程。

目录

一: XMODEM的STM32F103C8初始化代码。

XMODEM的原理就不多说了,各个博主都有分享,本代码通过Windows系统自带的超级终端测试,本人使用Python编写的代码也测试过,都能正常使用。很多人可能有疑问,为何Windows超级终端可以使用,为何还要自己写个工具呢?原因如下:

1: Windows系统超级终端不能自由的发数据,例如发单个字节或者多少个字节。

2: Windows系统超级终端使用不友好,对于非专业技术人员,有难度,更别说让客户使用了。

3: 一般嵌入式产品做好后,后续需要维护升级,可以通过发命令方式进入Boot。根据个人喜好,也可以在代码中添加自己的调试命令,Windows超级终端对这方面不大友好,而通过的串口工具,例如SSCOM又不具备下载升级功能。

1.1:代码整体框架

     ​​​  

Main.C代码如下:

#include "main.h"

void SystemClock_Config(void);

int main(void)
{
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_AFIO);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

  NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  LL_GPIO_AF_DisableRemap_SWJ();

  SystemClock_Config();

  InitLedGPIO();                              // 初始化LED的GPIO
  CheckAPPFirmware();                 // 检测是否有固件

  Delay1us(30000);                         // 延时
  
  InitUsart1();                                  // 初始化串口1
  InitTimer3();                                 // 初始化定时器3
  
  InitXModem();                            // 初始化XMODEM的参数
  while (1)
     {
         LEDTask();                         // LED任务
        XModemTask();                  // XMODEM任务
     } 
}

1.2: SystemClock_Config()代码

这部分代码由STM32生成,简单配置时钟频率即可

void SystemClock_Config(void)
{
  LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
  while(LL_FLASH_GetLatency()!= LL_FLASH_LATENCY_2)
  {
  }
  LL_RCC_HSI_SetCalibTrimming(16);
  LL_RCC_HSI_Enable();

   /* Wait till HSI is ready */
  while(LL_RCC_HSI_IsReady() != 1)
  {

  }
  LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI_DIV_2, LL_RCC_PLL_MUL_16);
  LL_RCC_PLL_Enable();

   /* Wait till PLL is ready */
  while(LL_RCC_PLL_IsReady() != 1)
  {

  }
  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
  LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);

   /* Wait till System clock is ready */
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
  {

  }
  LL_Init1msTick(64000000);                        // 配置Tick时钟频率为64MHz
  LL_SetSystemCoreClock(64000000);        // 配置系统主时钟为64MHz
}

1.3:InitLedGPIO() 代码

void InitLedGPIO(void)
{
    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};  
    /* GPIO Ports Clock Enable */
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOC);
    LL_GPIO_ResetOutputPin(LED_GPIO_Port, LED_Pin);
    GPIO_InitStruct.Pin = LED_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;    // 此模式可选其它
    LL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
  
    LedOff();                //关闭LED灯,进入boot后,会快速闪烁

}

1.4: CheckAPPFirmware()代码

检查是否需要更新固件。

void    CheckAPPFirmware(void)
{
    u16    APPData;

    APPData = Flash_ReadData16Bit( FLASH_APP_CHECK_ADDRESS );    

    if((APPData == APP_NEED_TO_UPDATE_DATA) || (APPData == 0xFFFF) )        //检查到Flash升级标识,表示需要升级
        {            
            return;    
        }

    BootloaderJumpToApp();       
}

1.5: InitUsart1()串口1初始化代码

void InitUsart1(void)
{

      LL_USART_InitTypeDef USART_InitStruct = {0};

      LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

      LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
      LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
      
      GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
      GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
      GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
      GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
      LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

      GPIO_InitStruct.Pin = LL_GPIO_PIN_10;
      GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
      LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

      /* USART1 interrupt Init */
      NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
      NVIC_EnableIRQ(USART1_IRQn);

      USART_InitStruct.BaudRate = BAUDRATE;
      USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
      USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
      USART_InitStruct.Parity = LL_USART_PARITY_NONE;
      USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
      USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
      USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
      LL_USART_Init(USART1, &USART_InitStruct);
      LL_USART_ConfigAsyncMode(USART1);
     
      LL_USART_DisableIT_TC( USART1);          
      LL_USART_DisableIT_TXE(USART1);          
      LL_USART_EnableIT_RXNE(USART1);   
      LL_USART_Enable(USART1);      
}

1.6: InitTimer3()代码   

// 在Xmodem中,不使用定时器3中断,这里仅示例定时器的配置,1ms定时器中断

void InitTimer3(void)
{

  LL_TIM_InitTypeDef TIM_InitStruct = {0};

  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);

  NVIC_SetPriority(TIM3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(TIM3_IRQn);

  TIM_InitStruct.Prescaler = 6400-1;                            //100us
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 10;                                //计数器重装载值,10*100us,产生1ms定时中断
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM3, &TIM_InitStruct);
  LL_TIM_EnableARRPreload(TIM3);
  LL_TIM_SetClockSource(TIM3, LL_TIM_CLOCKSOURCE_INTERNAL);
  LL_TIM_SetTriggerOutput(TIM3, LL_TIM_TRGO_RESET);
  LL_TIM_DisableMasterSlaveMode(TIM3);

  LL_TIM_EnableIT_UPDATE(TIM3);
  LL_TIM_EnableCounter(TIM3);

  LL_SYSTICK_EnableIT();
  
  SystemTime = 0;                //此变量做系统时钟实,在Tick时钟对此变量+1操作

}

1.7: InitXModem()代码

void     InitXModem(void)
{
    InitXModemParameter();    
    Flash_ErasePages(FLASH_APP_START_ADDRESS, STM32_FLASH_PAGES-          BOOT_LOADER_PAGE_SIZE);
}

void     InitXModemParameter(void)
{
    XModemStructure.XModemReceiveDataPackTimeStart = DISABLE;
    XModemStructure.XModemReceiveDataPackSuccess = ERROR;    
    
    XModemStructure.XModemDataErrorTimes = 0;    
    XModemStructure.XModemReceiveDataPackTime = 0;    
    XModemStructure.XModemReceiveDataPackEnable = DISABLE;
    XModemStructure.XModemPackDataLength = 0;
    XModemStructure.XModemDataPackNumber = 0;
    
    memset(&XModemStructure.XModemPackDataBuffer,0,XMODEM_PACK_DATA_SIZE);
    //XModemStructure.XModemProcessStep = YM_STEP_NULL;
    XModemStructure.XModemProcessStep = XM_STEP0_REQUEST_START_DATA;    

}

二:中断代码分析

2.1: Tick时钟代码

void SysTick_Handler(void)
{    
    SystemTime++;    

    if(XModemStructure.XModemReceiveDataPackTimeStart == ENABLE)        // 串口接收数据超时,启动下一包数据时清零
        XModemStructure.XModemReceiveDataPackTime++;    
}

2.2: 定时器3产生1ms时钟代码

void TIM3_IRQHandler(void)
{
    LL_TIM_ClearFlag_UPDATE( TIM3 );         

}

2.3: 串口1中断代码

串口中断接收数据,并做简单的数据处理,确保数据不丢失,包及时处理。

上位机只在接收到下位机的ACK后才发送下包数据,所以可以在串口做简单的数据处理。

void USART1_IRQHandler(void)
{   
    u8 Datatemp;
    
    if( LL_USART_IsActiveFlag_RXNE(USART1) == RESET )                                  //未检测到串口中断
        return;

    Datatemp = LL_USART_ReceiveData8(USART1);                                               // 收串口数据

    if( XModemStructure.XModemPackDataLength == 0 )                                          //当前缓存无数据,表示收到新的数据包
        {
            if(Datatemp == XMODEM_SOH)                                                                    // 接收一包数据起始字符    
                XModemStructure.XModemReceiveDataPackEnable = ENABLE;            // 允许后续正常接收数据        
                            
            if(Datatemp == XMODEM_EOT)                                                                    // 收到传输结束符 
                {    
                    XModemStructure.XModemProcessStep = XM_STEP4_RECEIVE_EOT;                                                    
                    XModemStructure.XModemPackDataBuffer[XModemStructure.XModemPackDataLength] = XMODEM_EOT;                    
                    XModemStructure.XModemReceiveDataPackSuccess = SUCCESS;
                }    

            if( Datatemp == XMODEM_ETB )                //传输快结束
                {
                    
                    XModemStructure.XModemProcessStep = XM_STEP6_RECEIVE_ETB;
                    XModemStructure.XModemPackDataBuffer[XModemStructure.XModemPackDataLength] = XMODEM_ETB;                    
                    XModemStructure.XModemReceiveDataPackSuccess = SUCCESS;
                }
                    
            if( Datatemp == XMODEM_CAN )               // 收到取消传输符号
                {
                    XModemStructure.XModemProcessStep = XM_STEP_RECEIVE_CANNEL;
                    XModemStructure.XModemReceiveDataPackSuccess = SUCCESS;                    
                }
        }

    if( XModemStructure.XModemReceiveDataPackEnable == ENABLE )                    //收到数据帧头信息后,才接收并缓存数据
        {
            XModemStructure.XModemPackDataBuffer[XModemStructure.XModemPackDataLength] = Datatemp;
            XModemStructure.XModemPackDataLength += 1;                
        }    
    
    if( XModemStructure.XModemPackDataLength == XMODEM_PACK_DATA_SIZE )             //收到133字节的数据包           
        {
            XModemStructure.XModemReceiveDataPackSuccess = SUCCESS;
            XModemStructure.XModemReceiveDataPackEnable = DISABLE;                               // 暂停接收数据
        }            
}

三: 主循环代码

3.1: LED任务代码

void LEDTask(void)
{    
    static    u32 LEDTaskTime;    
    
    if( (SystemTime - LEDTaskTime) < LED_TASK_TIME  )            //LED任务时间到;         #define  LED_TASK_TIME   100
        return;    
    
    LL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);                      // LED引脚取反
    
    LEDTaskTime = SystemTime;                                                    // 更新LED任务时间点
}

3.2: XModemTask()任务代码

Xmodem采用状态机的方式接收数据,这样思路清晰,方便阅读代码和维护。

void     XModemTask(void)
{
    ErrorStatus XmodemReceiveDataPackSuccess;    
    u8 XModemStep;    

    if( XModemStructure.XModemReceiveDataPackTime > XMODEM_TIME_OUT)            // 收帧数据包超时,3秒超时    
        {
            InitXModemParameter();
            return;
        }    

    XmodemReceiveDataPackSuccess = XModemStructure.XModemReceiveDataPackSuccess;
    XModemStep = XModemStructure.XModemProcessStep;    

    switch( XModemStep )
        {
            case XM_STEP0_REQUEST_START_DATA:            //第一步:向主机请求数据包    
                {    
                    XModemStep0Process();                    
                    break;
                }

            case    XM_STEP1_RECEIVE_PACK_DATA:
                    XModemStep1Process( XmodemReceiveDataPackSuccess );
                    break;

            case    XM_STEP2_RESPOND_PACK_ACK:
                    XModemStep2Process();
                    break;

            case    XM_STEP3_RESPOND_PACK_NACK:
                    XModemStep3Process();
                    break;

            case    XM_STEP4_RECEIVE_EOT:
                    XModemStep4Process(XmodemReceiveDataPackSuccess);
                    break;
            case    XM_STEP5_RESPOND_EOT_ACK:
                    XModemStep5Process();    
                    break;
            case    XM_STEP6_RECEIVE_ETB:
                    XModemStep6Process(XmodemReceiveDataPackSuccess);
                    break;

            case    XM_STEP7_RECEIVE_DATA_SUCCESS:
                    XModemStep7Process();
                    break;
            case    XM_STEP_RECEIVE_CANNEL:
                    XModemStepCancelProcess();
                    break;
            default:
                    break;
        }
}

3.2.1: XMODEM请求发送数据状态。

XMODEM状态:XM_STEP0_REQUEST_START_DATA,此状态表示请求上位机发送数据。

void XModemStep0Process(void)
{
    XModemStructure.XModemReceiveDataPackTimeStart = ENABLE;                    //接收一帧数据超时计时开启
    XModemStructure.XModemReceiveDataPackEnable = DISABLE;                        //接收正确的帧头信息才允许収数据,避免収到其它数据

    XModemStructure.XModemReceiveDataPackTime = 0;                                        //帧接收数据计时器清零    
    XModemStructure.XModemPackDataLength =0;                                                    //帧数据长度初始化为0

    memset(&XModemStructure.XModemPackDataBuffer,0,XMODEM_PACK_DATA_SIZE);        //缓存数组清零
    
    XModemStructure.XModemReceiveDataPackSuccess = ERROR;                        //成功接收一帧数据标记,初始化为ERROR

    XModemStructure.XModemProcessStep = XM_STEP1_RECEIVE_PACK_DATA;                //XModem当前处于何种工作状态
    XModemStructure.XModemDataErrorTimes =0;                              //数据出错次数清零,初始化为0                                            

    USART1Send8Bit(XMODEM_C);                                                    //向主机请求数据,发送信号“C”           
}

3.2.2:XMODEM接收数据

XMODEM状态:XM_STEP1_RECEIVE_PACK_DATA,此状态表示接收上位机数据。

void     XModemStep1Process(ErrorStatus XmodemReceiveOneFrameDataSuccess)
{
    ErrorStatus XModemDataSuccess;                                                            // 接收数据包成功标记
    if( XmodemReceiveOneFrameDataSuccess == ERROR )                        // 未接收到数据包
        return;    

    XModemDataSuccess = AnalyzeOfficialDataPack();                                 //处理数据包

    if( XModemDataSuccess == ERROR )                                                     //收到数据有误,请求重发
        {                
                        
            XModemStructure.XModemDataErrorTimes++;                                     //数据包错误计数         
            
            if( XModemStructure.XModemDataErrorTimes > XMODEM_ERROR_TIMES )             //数据错误超过设定次数
                {
                    XModemStructure.XModemProcessStep = XM_STEP0_REQUEST_START_DATA;    //重新回第一步
                    return;
                }
            
            XModemStructure.XModemProcessStep = XM_STEP3_RESPOND_PACK_NACK;             //重发数据
            return;
        }
    
    XModemStructure.XModemReceiveDataPackTimeStart = DISABLE;                            //接收数据超时计时关闭
    XModemStructure.XModemProcessStep = XM_STEP2_RESPOND_PACK_ACK;                         //响应收到正确数据    
}

ErrorStatus AnalyzeOfficialDataPack(void)        // 分析正式数据包
{
    u8 HeadData;
    u16   i,FlashData,FlashPageOder,FlashPageNumber;
    u32  FlashAddress,FlashPageAddress,PackStartAddress;
    //ErrorStatus    WriteFlashError;
    FlashStatusTypedef FlashStatus;   

    HeadData = XModemStructure.XModemPackDataBuffer[XMODEM_PACK_SOH_POS];

    if( HeadData != XMODEM_SOH )        //数据包以XMODEM_SOH开头,否则就不是数据包
        return ERROR;  

    HeadData = XModemStructure.XModemPackDataBuffer[XMODEM_PACK_ORDER_CODE_POS] & XModemStructure.XModemPackDataBuffer[XMODEM_PACK_ORDER_nCODE_POS];
    if( HeadData != 0x00 )                //数据包序号与序号反码是否正确                
        return ERROR;    
    
    if( XModemDataCRC() == ERROR )            //数据做CRC校验
        return ERROR;    

    FlashPageOder = XModemStructure.XModemDataPackNumber % XMODEM_PACKS_OF_ONE_FLASH_PAGE;                // 1K=1024Bytes=128Bytes*8
    FlashPageNumber = (u16)(XModemStructure.XModemDataPackNumber / XMODEM_PACKS_OF_ONE_FLASH_PAGE);        // 计算当前页码,8个数据=1页数据(1024Bytes)
    
    if( FlashPageOder == 0 )                        
        {            
            FlashPageAddress = FlashPageNumber*STM32_FLASH_PAGE_SIZE;               // 接收1K字节后,算出Flash的相对地址
            FlashStatus = Flash_EraseOnePage(FLASH_APP_START_ADDRESS + FlashPageAddress);    // 先擦除需要编写入数据的页
            if( FlashStatus != FLASH_STATUS_COMPLETE )
                LedOn();
        }

    PackStartAddress = XModemStructure.XModemDataPackNumber*XMODEM_REAL_DATA_SIZE;       // 包数据在Flash的起始地址

    i=0;    
    while( i < XMODEM_REAL_DATA_SIZE )
        {                                
            FlashAddress = FLASH_APP_START_ADDRESS + PackStartAddress + i;
            
            FlashData = (u16)((XModemStructure.XModemPackDataBuffer[XMODEM_PACK_DATA_START_POS+i+1]<<8) & 0xFF00) |         // 缓存高8位数据
                        (u16)((XModemStructure.XModemPackDataBuffer[XMODEM_PACK_DATA_START_POS+i]) & 0x00FF);                // 缓存低8位    数据                        

            Flash_WriteData16Bit(FlashAddress,FlashData);
            
            //WriteFlashError = Flash_WriteData16Bit(FlashAddress,FlashData);                                                    //数据写入Flash            
            //if( WriteFlashError == ERROR )
            //    return ERROR;   
            i += 2;                                    
        }

    XModemStructure.XModemDataPackNumber += 1;    
    return SUCCESS;           
}

ErrorStatus    XModemDataCRC(void)                //已测试通过
{     
    u16 i,j,CRCData;
    u16 DataTemp;
    
     i=0;     
     CRCData = 0;
     
    while( i < XMODEM_REAL_DATA_SIZE )
        {
            CRCData = CRCData ^ ((int) (XModemStructure.XModemPackDataBuffer[i+XMODEM_PACK_DATA_START_POS] << 8));  
            j = 8 ;   
            do
                {
                    if ( CRCData & 0x8000 )   
                           CRCData = (CRCData << 1) ^ 0x1021;    
                       else
                           CRCData = CRCData << 1;
                }
            while (--j);   
            i++;
        }
    
    DataTemp = (int)(XModemStructure.XModemPackDataBuffer[XMODEM_PACK_DATA_CRCH_POS]<<8) | (int)(XModemStructure.XModemPackDataBuffer[XMODEM_PACK_DATA_CRCL_POS]);    
    if( CRCData == DataTemp )
        return SUCCESS;

    DataTemp = (int)(XModemStructure.XModemPackDataBuffer[XMODEM_PACK_DATA_CRCL_POS]<<8) | (int)(XModemStructure.XModemPackDataBuffer[XMODEM_PACK_DATA_CRCH_POS]);
    if( CRCData == DataTemp )
        return SUCCESS;    
    
    //return ERROR;

    return SUCCESS;    // 此处关闭CRC校验  
}

3.2.3:XMODEM反馈数据包ACK

XMODEM状态:XM_STEP2_RESPOND_PACK_ACK,此状态表示向上位机反馈ACK信号。

void    XModemStep2Process(void)
{
    XModemStructure.XModemReceiveDataPackTimeStart = ENABLE;                            //超时计时开启
    XModemStructure.XModemReceiveDataPackEnable = DISABLE;    
    XModemStructure.XModemReceiveDataPackTime = 0;                                         //超时计时器清零
    XModemStructure.XModemPackDataLength =0;                                            //接收到的数据长度初始化为0     
    memset(&XModemStructure.XModemPackDataBuffer,0,XMODEM_PACK_DATA_SIZE);    
    XModemStructure.XModemReceiveDataPackSuccess = ERROR;                                //初始化为ERROR    
    XModemStructure.XModemDataErrorTimes = 0;                                            //收到错误数据清零                    
    XModemStructure.XModemProcessStep = XM_STEP1_RECEIVE_PACK_DATA;                     //继续接收数据            
    USART1Send8Bit(XMODEM_ACK);                                                            //发送“ACK”信号,确认上包数据正确

}

3.2.4:XMODEM反馈数据包NACK

XMODEM状态:XM_STEP3_RESPOND_PACK_NACK,此状态表示向上位机反馈NACK信号。

void    XModemStep3Process(void)
{
    XModemStructure.XModemReceiveDataPackTimeStart = ENABLE;                            //超时计时开启
    XModemStructure.XModemReceiveDataPackEnable = DISABLE;    
    XModemStructure.XModemReceiveDataPackTime = 0;                                         //超时计时器清零
    XModemStructure.XModemPackDataLength =0;                                            //接收到的数据长度初始化为0     
    memset(&XModemStructure.XModemPackDataBuffer,0,XMODEM_PACK_DATA_SIZE);    
    XModemStructure.XModemReceiveDataPackSuccess = ERROR;                                //初始化为ERROR                
    XModemStructure.XModemProcessStep = XM_STEP1_RECEIVE_PACK_DATA;                     //继续接收数据            
    USART1Send8Bit(XMODEM_NACK);                                                        //发送“NACK”信号,请求重传数据包

}

3.2.5:XMODEM反馈收到结束符

XMODEM状态:XM_STEP4_RECEIVE_EOT,此状态表示向上位机反馈收到结束符。

void    XModemStep4Process(ErrorStatus XmodemReceiveOnePackDataSuccess)
{
    if( XmodemReceiveOnePackDataSuccess == ERROR )
            return;                 
    if( XModemStructure.XModemPackDataBuffer[XMODEM_PACK_SOH_POS] != XMODEM_EOT ) 
            return;                                                                
    InitXModemParameter();
    XModemStructure.XModemProcessStep = XM_STEP5_RESPOND_EOT_ACK;
}

3.2.6:XMODEM收到结束符后,反馈NAK信号

XMODEM状态:XM_STEP5_RESPOND_EOT_ACK,此状态表示收到结束符后,向上位机反馈NAK信号。

void    XModemStep5Process(void)
{
    InitXModemParameter();
    //XModemStructure.XModemProcessStep = XM_STEP6_RECEIVE_ETB;                //超级终端不发送ETB

    XModemStructure.XModemProcessStep = XM_STEP7_RECEIVE_DATA_SUCCESS;
    USART1Send8Bit(XMODEM_ACK);    
}

3.2.7:XMODEM收到第二个结束符

XMODEM状态:XM_STEP6_RECEIVE_ETB,此状态表示收到上位机第二个结束符。

void    XModemStep6Process(ErrorStatus XmodemReceiveOnePackDataSuccess)
{
    if( XmodemReceiveOnePackDataSuccess == ERROR )
            return;            
    
    if(XModemStructure.XModemPackDataBuffer[XMODEM_PACK_SOH_POS] != XMODEM_ETB)    
        return;

    USART1Send8Bit(XMODEM_ACK);        //结束XModem传输数据
    
    InitXModemParameter();
    
    XModemStructure.XModemProcessStep = XM_STEP7_RECEIVE_DATA_SUCCESS;
}

3.2.8:XMODEM成功接收完整的文件

XMODEM状态:XM_STEP7_RECEIVE_DATA_SUCCESS,此状态表示成功接收完整的文件。

void    XModemStep7Process(void)
{
    InitXModemParameter();
    ReceiveXModemPackSuccess();
}

void     ReceiveXModemPackSuccess(void)
{
    USART1Send8Bit(0x41);
    USART1Send8Bit(0x42);
    USART1Send8Bit(0x43);
    USART1Send8Bit(0x44);
    
    XModemStructure.XModemProcessStep = XM_STEP_NULL;    
    SetFirmwareSuccessSign();              // 设置Firmware成功标记
    BootloaderJumpToApp();                  //跳转到APP  
}

void BootloaderJumpToApp( void )  // 跳转部分代码
{    
    u32 JumpAddress; 
    CloseIQHard();
    if(((*(vu32*)FLASH_APP_START_ADDRESS)&0x2FFE0000) == 0x20000000)                    //检查栈顶地址是否合法.
         {
                  __set_CONTROL(0);                      //特权模式
                    __ASM("CPSID  I");                     //关中断
        
                 JumpAddress = *(vu32*)(FLASH_APP_START_ADDRESS+4);    //用户代码区第二个字为程序开始地址(复位地址)  
                JumpToApp = (IapFunction)JumpAddress;                                        
                __set_MSP(*(vu32*)FLASH_APP_START_ADDRESS);     //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)                        
               JumpToApp();                                         //跳转到APP.
         } 
    
}

void CloseIQHard(void)   //关闭所有中断和端口时钟
{
        LL_GPIO_DeInit(GPIOA);
        
        LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_GPIOA);          //关使能PA端口时钟           
        LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_AFIO);        //关辅助时能时钟

        LL_TIM_DisableCounter(TIM3);

          __set_PRIMASK(1);
        __disable_irq();   // 关闭总中断
}

3.2.9:XMODEM收到取消传输

XMODEM状态:XM_STEP_RECEIVE_CANNEL,此状态表示收到取消传输文件。

void     XModemStepCancelProcess(void)
{
    InitXModemParameter();
}

四: 关于APP代码和Keil C的设置

4.1 APP代码设置

APP的main()函数入口代码设置。

/*************************** 需要设置 system_stm32f1xx.c 中的偏移地址。********************

#define    VECT_TAB_OFFSET        0x2000            //APP起始地址,0x2000为APP的起始地址,0~0x2000时BootLoader空间

************************************************************************************/


#ifdef    BOOTLOADER
  SetVectorTableAandIQR();        
#endif

void SetVectorTableAandIQR(void)
{       
    //SCB->VTOR = FLASH_BASE | APP_FLASH_ADDRESS;      //修改中断向量表
    WRITE_REG(SCB->VTOR, FLASH_BASE | APP_FLASH_ADDRESS);
    __ASM("CPSIE  I");                                 //开中断  
}

4.2  Keil C设置

五: Windows端测试

介绍使用两种工具的测试情况:

一:使用Windows超级终端,但在APP运行时,需要通过串口发送升级指令,然后切换到超级终端升级固件。

二:使用Python制作的工具,可以发送命令和接收数据,并升级固件,对用户友好。

4.1: 超级终端测试

APP代码采集AD数据,通过串口发送。

发送升级命令: 0xAA 0xBB 0xCC后进入Boot模式,SSCOM接收到发送数据请求。

关闭串口,转到超级终端升级固件。

配置好超级终端的串口参数后,收到字符“C”,如下图。

选择传输协议和文件,文件只支持.bin格式。

点击“发送”,如下图

发送成功后跳转到APP,间隔1S发送AD采样的数据。

4.2: Python串口工具测试

使用Python制作的工具发送文件

首先配置串口参数,如下图。

打开串口后,收到APP发送的AD数据,如下图。

点击“发送数据”按钮,串口收到数据后进入Boot状态,并发送请求数据。

点击“打开文件”按钮,选择要升级的固件,仅支持.bin文件,如下图。

文件打开后,点击“升级固件”按钮,进入升级固件流程,如下图。

右上角可观测升级固件进度,升级完成后会弹出对话框。

升级完成后,APP发送AD采样的数据。

至此,BootLoader功能完成,有误地方欢迎留言指正,一起学习。

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值