精华内容
下载资源
问答
  • 采用MFC+winapi编写的一个串口调试工具,支持程序升级,以及shell命令调试,日志保存等功能。采用多线程通信机制来控制升级过程界面刷新以及后台升级数据包发送解析等,对于想要做C++上位机工具的同学来说可以从种学...
  • STM32_IAP上位机程序

    热门讨论 2011-03-08 19:24:17
    STM32_IAP上位机程序,通过·串口更新升级基于stm32系列单片机应用程序,可以配合官方的例程使用,替代超级终端,本程序和ST官方的单片机IAP例程一样采用YMODEM协议。
  • QT上位机给STM32设备串口IAP升级固件

    千次阅读 2018-07-30 15:47:45
    QT上位机给STM32设备串口IAP升级固件 目录 QT上位机给STM32设备串口IAP升级固件 1,实现原理 2,程序流程 3,关键代码解析 4,相关工具及代码 废话不多说看看效果先 上位机源码连接:...

    QT上位机给STM32设备串口IAP升级固件

    目录

    QT上位机给STM32设备串口IAP升级固件

    1,实现原理

    2,程序流程

    3,关键代码解析

    4,相关工具及代码


    废话不多说看看效果先


    上位机源码连接:https://download.csdn.net/download/qq_28643619/10922262

    1,实现原理

    应用编程IAP(In-Application-Programming)是应用在Flash程序存储器的一种编程模式,它可以在应用程序正常运行的情况下,通过调用特定的IAP程序对另外一段程序Flash空间进行读/写操作,甚至可以控制对某段、某页甚至某个字节的读/写操作。主要用于数据存储和固件升级。对于IAP应用,通常会有两个程序,第一个程序Bootloader程序不执行正常功能,只是通过某种方式(串口,usb,SD卡)接收第二个程序,并进行更新。第二个程序APP程序是执行的主体,用于实现应用功能。
           对于stm32闪存模块,主要由主存储器、信息块和闪存存储器接口寄存器三部分构成。
                             
           主存储器,该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。注意, 小容量和中容量产品则每页只有 1K 字节。看出主存储器的起始地址就是 0X08000000, B0、 B1 都接GND 的时候,就是从 0X08000000开始运行代码的。

           信息块,该部分分为 2 个小部分,其中启动程序代码,是用来存储 ST 自带的启动程序,用于串口下载代码,当 B0 接 V3.3, B1 接 GND的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能,本章不作介绍。

           闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。

           对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理;编程与擦除的高电压由内部产生。

     

        对于flash的读写的流程

        这里要特别留意一个闪存等待时间,因为 CPU 运行速度比 FLASH 快得多, STM32F103的 FLASH 最快访问速度≤24Mhz,如果 CPU 频率超过这个速度,那么必须加入等待时间,比如我们一般使用72Mhz的主频,那么FLASH等待周期就必须设置为 2,该设置通过 FLASH_ACR寄存器设置。

    2,程序流程

    编程流程

    1、检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁,(向KEYR寄存器中写入特定序列KEY1和KEY2)

       (实际中需要查看当前要写入的扇区是否有数据,如果有数据,则需要进行擦除操作,然后再进行下面的步骤)

    2、检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的编程操作
    3、设置 FLASH_CR 寄存器的 PG 位为’1’,用于表示接下来进行写操作
    4、在指定的地址写入要编程的半字
    5、等待 BSY 位变为’0’,表示编程完成
    6、读出写入的地址并验证数据

     

    我们在 STM32 的 FLASH 编程的时候,要先判断缩写地址是否被擦除了

     

    下面介绍页擦除过程

     


    读出被擦除的页并做验证
    1、检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁(向KEYR寄存器中写入特定序列KEY1和KEY2)
    2、检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作
    3、设置 FLASH_CR 寄存器的 PER 位为’1’,用于表示进行页擦除操作
    4、用 FLASH_AR 寄存器选择要擦除的页
    5、置 FLASH_CR 寄存器的 STRT 位为‘1’表示开始一次擦除操作
    6、等待 BSY 位变为’ 0’
    读出被擦除的页并做验证
     

    我们来了解下stm32的程序运行流程,如下图所示

    程序运行的地址从0x08000000(FLASH)开始运行

    1程序开始运行后,从中断向量表中取出复位中断向量,并执行服务程序。

    2执行完中断向量程序会跳转至主main函数入口执行,并在死循环中一直执行。

    3当在主函数中发生中断时间时,系统强制PC指针指向对应中断向量表对应位置。

    4PC指针在中断向量表处取出中断服务程序入口地址,并跳转至对应位置执行。

    5中断服务程序执行完成后,PC指针跳回发生中断时系统在main函数中的位置,继续往下执行。

     

    当加入IAP应用后stm32的程序流程变为下图所示

    程序运行的地址从0x08000000(FLASH)开始运行

     

    1程序开始运行后,从中断向量表中取出复位中断向量,并执行服务程序。执行完中断向量程序会跳转至Bootloader程序main函数入口执行。

    2在main中系统检查是否需要对第二部分代码进行更新,如果需要则执行更新操作,如果不需要则跳过更新操作,跳转至APP程序的入口

    3在APP程序入口首先进入重新映射的中断向量口,根据新的中断复位向量,执行复位中断程序,然后跳转至APP程序main入口。

    4当在主函数中发生中断时间时,系统强制PC指针指向对应中断向量表对应位置(这里还是强制跳转到地址0x0800004中断向量表位置,而不是APP程序的中断向量表)。

    两个注意点:

    1) 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;

    2) 必须将新程序的中断向量表相应的移动,移动的偏移量为 x;

     

     

    对于程序的设置

    IAP程序

    通过串口设置程序的接收

    根据要求设置存储APP程序的地址

     

    APP程序

    设置程序存储区,与数据存储区 在Target->Read/Only Memory Areas-> IROM与IRAM处设置,这里APP程序的存储首地址设为0x08010000,大小为0x70000(即前0x10000为存放IAP程序)

    设置中断向量表偏移地址SCB->VTOR = FLASH_BASE | 0x10000; 

    生成BIN文件

     

    3,关键代码解析

    STM32F103的IAP关键代码:

    int main(void)
    {      
    	uint32_t tick1;
    	tick1 = SysTick_GetCurrent();
            MCU_Init();	
    	AreaFlagFirstLoadInit();
    	NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x8000000);  //iap初始都从0x8000000开始运行
    	printf("init success\r\n");	   
    	__enable_irq();	
    	while(1)		
    	{
    		iapLoop();
    		if((remainSend  == 0x00) && (upgradeDoneFlag == 0xaa55aa55)) 
    		{			
    	
    			(currentAreaNum==areaA) ? (FlashDestination=ApplicationAddress_A) : (FlashDestination=ApplicationAddress_B);
    			if(((*(__IO uint32_t*)FlashDestination) & 0x2FFE0000 ) == 0x20000000)				//binÎļþÉý¼¶
    			{											 
    				printf("Execute user APP1 Program upgradeDoneFlag=%x\r\n",upgradeDoneFlag);
    				USART_Cmd(USART1, DISABLE);
    				__disable_irq();	
    				JumpAddress = *(__IO uint32_t*) (FlashDestination + 4);
    				Jump_To_Application = (pFunction) JumpAddress;						
    				MSR_MSP(*(vu32*)FlashDestination);
    				Jump_To_Application();								
    			}
    			else 
    			{			
    				if(SysTick_GetLapse(tick1)>1000)
    				{
    					printf("ÎÞFLASHÓ¦ÓóÌÐò,ÎÞ·¨Ö´ÐÐ!\r\n");	
    					tick1 = SysTick_GetCurrent();
    				}
    				
    			}
    		}
    	}
    }
    
    
    uint8_t AreaFlagFirstLoadInit(void)
    {
    	uint8_t ret = 0;
    	STMFLASH_Read(ADDR_FLASH_LAST_PAGE,&upgradeDoneFlag,1);  
    	FLASH_Unlock();
    	if(upgradeDoneFlag==0xffffffff || upgradeDoneFlag==0x00000000)
    	{
    		upgradeDoneFlag = 0xaa55aa55;
    		FLASH_ProgramWord(ADDR_FLASH_LAST_PAGE,0xaa55aa55);
    	}
    	
    
    	STMFLASH_Read(AREA_NUM_ADDR,&currentAreaNum,1);
    	
    	if(currentAreaNum == 0xffffffff || currentAreaNum==0x00000000)   
    	{	
    		printf("ÇøÓò±ê־λ³õʼ»¯");
    		currentAreaNum = areaA;			
    		if(FLASH_COMPLETE!=FLASH_ProgramWord(AREA_NUM_ADDR,areaA))
    		{
    			printf("ÇøÓòA±ê־λÉϵç³õʼ»¯Ê§°Ü");
    			ret = 1;
    		}
    	}	
    	FLASH_Lock();
    	
    	switch(currentAreaNum)
    	{
    		case areaA:
    			addrCur = ApplicationAddress_B;
    			break;
    		
    		case areaB:
    			addrCur = ApplicationAddress_A;
    			break;	
    			
    		default:
    			break;
    	}
    
    	printf("addrCur=%x currentAreaNum=%x upgradeDoneFlag=%x\n",addrCur,currentAreaNum,upgradeDoneFlag);
    	
    	return ret;
    }

    单片机iap及app工程连接:

    4,相关工具及代码

    用到的工具软件:

    1、固件编译生成工具(编译IAP和两个区域运行的app程序生产bin文件):keil5

    2、固件拼接工具(初始iap和app1拼接,用于工厂生产烧写):

    3、固件下载工具:STM32 ST-LINK Utility

     

     

    展开全文
  • 串口类CSerial为某公司做的一个串口调试的工具,它可以解析输入的命令,用户可以象使用超级终端一样使用它。我在这里面使用了加载前奏,密码保护功能,Edit控件滚屏显示,多线程加载升级软件,CRC32校验等技巧,...
  • 与我博客里面写的STM32串口iap升级相关博文关联,单片机下载iap和app固件后,通过QT上位机连接STM32设备串口可实现单片机固件的交替升级功能,亲测稳定可用
  • 该软件用于串口升级,使用YModem协议,通过串口对产片进行升级,方便调试测试自己所写的升级程序,也可以直接作为升级工具使用
  • 1.内含avr单片机下位机源码c程序,本人在atmega128单片机上已经成功运行,能够和资源内的上位机程序配合进行程序升级。 2.c程序里有修改指南;大家根据自己的cpu型号,对c程序进行相应的修改。 3.c程序的运行环境为...
  • c-sharp-串口版本.zip

    2019-12-23 10:43:29
    最近给朋友做一个串口升级上位机程序,在晚上找了资料,第一版本发现serialport.read丢包于是更改结构重新写一版,搞定,不在丢包,不再出现升级过程中数据丢失问题,一次升级成功。杠杠滴
  • 在实际工程项目中使用的STM32F2xx的通过串口升级程序的源码,串口通信协议采用常见的ymodem。注意传输块大小选择1024字节,带crc16校验。如采用的是该系列单片机,可直接使用(非常稳定)。其它系列的单片机也有极大...
  • 串口收发程序源码

    2018-06-11 08:33:36
    非常棒的串口收发源程序,类似串口调试精灵,自动化上位机开发必备,VC6源码,可以通过VC2010升级到高版本使用
  • 基于STM32F030R8Tx为例来剖析串口升级,本例程分为三个部分 STM32应用程序部分 STM32 bootloader部分 上位机串口通信分发升级包部分 上位机与STM32之间的串口数据通信协议约定 包头 包长度 命令 0~...
    • 基于STM32F030R8Tx为例来剖析串口升级,本例程分为三个部分

    1. STM32应用程序部分
    2. STM32 bootloader部分
    3. 上位机串口通信分发升级包部分
    • 上位机与STM32之间的串口数据通信协议约定 

    包头 包长度 命令 0~n字节数据 校验和
    0xff      0x**    0x**    0x**....0x**    0x**
    •  校验和计算方法

           从包头开始到校验和之前的所有数据累加和,取低8位数据作为整个包的检验和

       例如 0xff 0x4 0x01,无数据部分,则检验和为

                0xff + 0x04 + 0x01 = 0x104

      取低8位数据作为整包的检验和,则校验和是0x04,那么完整的数据包是 0xff 0x04 0x01 0x04

    • STM32用户应用程序部分

       1. 整个程序由bootloader引导程序和用户应用程序两部分组成,这里分配12KB的flash空间给bootloader使用,那么用户应用

           程序flash地址则要从0x8000000的基础上再偏移0x3000,则flash的起始地址为0x8003000,由于STM32F030R8Tx的flash

           的大小为 64KB,那么应用程序可用的flash空间为0x10000减去bootloader的大小0x3000等于0xd000

        2.应用程序的RAM起始地址计算,由于bootloader跳转后要复制启动文件中的向量表到RAM中,所以至少要分配的RAM大小

          根据启动文件中的向量表决定,stm32f030R8Tx启动文件startup_stm32f030x8.s中的向量部分内部如下,共有45个DCD

          定义,每个占4个字节,则复制向量表总共所需占用的RAM为45*4=180个字节(0xb4),那么应用程序的起始RAM要从

         0x20000000加上0xb4,即0x200000B4,如果stm32f030R8Tx的RAM大小为8KB,那么应用程序可用RAM大小

          为0x2000-0xb4=0x1F4C

    __Vectors       DCD     __initial_sp                   ; Top of Stack       1
                    DCD     Reset_Handler                  ; Reset Handler      
                    DCD     NMI_Handler                    ; NMI Handler
                    DCD     HardFault_Handler              ; Hard Fault Handler
                    DCD     0                              ; Reserved
                    DCD     0                              ; Reserved
                    DCD     0                              ; Reserved
                    DCD     0                              ; Reserved
                    DCD     0                              ; Reserved
                    DCD     0                              ; Reserved
                    DCD     0                              ; Reserved
                    DCD     SVC_Handler                    ; SVCall Handler
                    DCD     0                              ; Reserved
                    DCD     0                              ; Reserved
                    DCD     PendSV_Handler                 ; PendSV Handler
                    DCD     SysTick_Handler                ; SysTick Handler     16
    
                    ; External Interrupts
                    DCD     WWDG_IRQHandler                ; Window Watchdog
                    DCD     0                              ; Reserved
                    DCD     RTC_IRQHandler                 ; RTC through EXTI Line
                    DCD     FLASH_IRQHandler               ; FLASH
                    DCD     RCC_IRQHandler                 ; RCC
                    DCD     EXTI0_1_IRQHandler             ; EXTI Line 0 and 1
                    DCD     EXTI2_3_IRQHandler             ; EXTI Line 2 and 3
                    DCD     EXTI4_15_IRQHandler            ; EXTI Line 4 to 15
                    DCD     0                              ; Reserved
                    DCD     DMA1_Channel1_IRQHandler       ; DMA1 Channel 1
                    DCD     DMA1_Channel2_3_IRQHandler     ; DMA1 Channel 2 and Channel 3
                    DCD     DMA1_Channel4_5_IRQHandler     ; DMA1 Channel 4 and Channel 5
                    DCD     ADC1_IRQHandler                ; ADC1 
                    DCD     TIM1_BRK_UP_TRG_COM_IRQHandler ; TIM1 Break, Update, Trigger and Commutation     30
                    DCD     TIM1_CC_IRQHandler             ; TIM1 Capture Compare
                    DCD     0                              ; Reserved
                    DCD     TIM3_IRQHandler                ; TIM3
                    DCD     TIM6_IRQHandler                ; TIM6
                    DCD     0                              ; Reserved
                    DCD     TIM14_IRQHandler               ; TIM14            36
                    DCD     TIM15_IRQHandler               ; TIM15
                    DCD     TIM16_IRQHandler               ; TIM16
                    DCD     TIM17_IRQHandler               ; TIM17
                    DCD     I2C1_IRQHandler                ; I2C1
                    DCD     I2C2_IRQHandler                ; I2C2           41
                    DCD     SPI1_IRQHandler                ; SPI1
                    DCD     SPI2_IRQHandler                ; SPI2
                    DCD     USART1_IRQHandler              ; USART1
                    DCD     USART2_IRQHandler              ; USART2     45
    
    __Vectors_End

    3.根据上述计算,在keil中配置flash和RAM如下

    4. STM32F030R8Tx从bootloader跳转到应用程序,需要在进入main函数第一时间将0x8003000起始地址开始复制向量表

        到0x20000000起始的RAM中,复制内容的大小是上面计算出的0xb4,那么添加以下代码实现

     5.串口通信校验和计算

    /**
    * @brief 计算checksum
    * @param [in] uint8_t const*
    *  - 待计算的数据
    * @param [in] uint8_t 
    *  - len 计算数据的长度
    * @return uint8_t
    *  - checksum计算结果
    *
    */
    static uint8_t get_checksum(uint8_t const*dat, uint8_t len)
    {
    	uint8_t chksum = 0;
     
    	for (uint8_t i = 0; i < len; i++)
    	    chksum += dat[i];
    
    	return chksum;
    }

    6. 通信实现2个命令,一个是获取硬件版本命令,另一个是进入OTA升级命令,定义如下

    
    typedef enum
    {
    	
    	UART_GET_VERSION = 0x01,
    	STM32_ENTER_DFU = 0X02,
    	UART_HEAD = 0xff,
    }E_UART_COMMAND;
    

    7.本例程版本使用4个字节的字符串格式,格式为Vx.x,其中V和.为固定格式,x为任意字符,版本在sdk_config.h中定义,

    定义如下

      

    8.上位机发送UART_GET_VERSION到stm32,收到此命令后会回复硬件版本给上位机

    
    /**
    * @brief 回复版本
    */
    void reply_stm32_version(void)
    {
    	
    		uint8_t buf[5];
    		uint8_t version_buf[] = FIRMWARE_VERSION_DEF;
    		for(uint8_t i=0; i<4; i++)
    		{
    			buf[i] = version_buf[i];
    		}
    		buf[4] = enter_stm32_dfu;
    		USART_Send_Data(UART_GET_VERSION,buf,5);
    	 
    }

    9. 上位机发送STM32_ENTER_DFU命令到stm32,收到此命令后会在flash最后一个扇区将升级包的版本和升级标志写入到

      flash中,然后启动软件复位进入bootloader引导程序,写入flash实现如下

    
    /**
    * @brief 写入版本
    */
    void write_version_to_flash(uint8_t *dfu_version)
    {
    	HAL_FLASH_Unlock();
    	EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
      EraseInitStruct.PageAddress = FLASH_USER_START_ADDR;
      EraseInitStruct.NbPages = (FLASH_USER_END_ADDR - FLASH_USER_START_ADDR) / FLASH_PAGE_SIZE;
      uint32_t PageError;
      HAL_StatusTypeDef err_code = HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);
    	APP_ERROR_CHECK(err_code);
    	
    	 Address = FLASH_USER_START_ADDR;
    
    	  uint8_t version_buf[] = FIRMWARE_VERSION_DEF;
        uint32_t content = *(uint32_t*)version_buf;
        err_code = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, content);
        APP_ERROR_CHECK(err_code);
        Address += 4;
    	
    	  if(dfu_version)
    			content = *(uint32_t*)dfu_version;
    		else
    			content = U32_MAX_VALUE;
    	  err_code = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, content);
        APP_ERROR_CHECK(err_code);
      
    
      /* Lock the Flash to disable the flash control register access (recommended
         to protect the FLASH memory against possible unwanted operation) *********/
      HAL_FLASH_Lock();
    		
    }
    
    
    
    /**
    * @brief 进入DFU
    */
    static void enter_dfu(void)
    {
    		if(enter_stm32_dfu)
    		{
    			enter_stm32_dfu = 0;
    			write_version_to_flash(from_app_stm32_version);
    			NRF_LOG_INFO("STM32_ENTER_DFU");
    			delay_ms(100);
    			NVIC_SystemReset();
    		}
    }
    • STM32 bootloader部分

        1. 由于bootloader打算分配12KB空间,则flash和RAM配置如下

    2. bootloader程序会先从最后一个扇区读取OTA升级标志

    #define FLASH_USER_START_ADDR   ADDR_FLASH_PAGE_63   /* Start @ of user Flash area */
    #define FLASH_USER_END_ADDR     ADDR_FLASH_PAGE_63 + FLASH_PAGE_SIZE   /* End @ of user Flash area */
    
    uint32_t JumpAddress;
    pFunction Jump_To_Application;
    
    uint32_t Address = 0;
    __IO uint32_t read_flash_value = 0;
    
    /*Variable used for Erase procedure*/
    static FLASH_EraseInitTypeDef EraseInitStruct;
    
    
    
    uint8_t version_buffer[4];
    uint8_t dfu_flag;
    
    

     3.如果dfu_flag为0表示无需升级,由以下代码进行跳转到0x08003000地址

    if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
    {
    	NRF_LOG_INFO("enter app"); 
    	//跳转至用户代码
    	EXTI->PR = 0x1fffff;
    	JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
    	Jump_To_Application = (pFunction) JumpAddress;
    
    	//初始化用户程序的堆栈指针
    	__set_MSP(*(__IO uint32_t*) ApplicationAddress);
    	Jump_To_Application();
    }
    else
    {
    	   NRF_LOG_INFO("no user Program");
    }
    
      while(1); 

    4.如果dfu_flag为1表示需要升级,会先初时化LCD并显示0%,然后擦除从0x08003000地址到最后一个扇区前的所有空间

     5. bootloader串口命令定义

    typedef enum
    {
    	
    	UART_GET_VERSION = 0x01,
    	UART_DFU_FILE = 0XD9,
    	UART_DFU_DOWNLOAD = 0XDA,
    	UART_HEAD = 0xff,
    }E_UART_COMMAND;

    6.初时化串口,并发送硬件版本和OTA标志到上位机,请求上位机发送升级包内容

    7.上位机收到OTA标志,将升级包的文件名称和文件长度发给stm32,例如 spi.bin升级文件,假如文件长度为0x7294,则发送的数据如下              

    包头 包长 命令   s  p  i   . b  i  n  \0         升级包文件长度29332 校验和
    0xff 0x10 0xd9 0x73 0x70 0x69 0x2e 0x62 0x69 0x6e 0x00 0x00 0x00 0x72 0x94 0xa1

    8.stm收到UART_DFU_FILE命令会对文件名和长度的有效性等进行判断

     

    9.stm32回复UART_DFU_DOWNLOAD命令给上位机,上位机收到此命令后开始传输升级包内容,升级包内容格式内如下

                      数据长度超过128,则按(128+8)的包长发送,不足128的数据,则发送(余下的数据+8)的包长发送

    包头 包长 命令   包序号从0开始递增            升级包数据(1~128Byte) 校验和
    0xff 0x10 0xda 0x00 0x00 0x00 0x00 0x** 0x** 0x** 0x** 0x** 0x** 0x** 0x** 0x**

     10.stm32收到升级包后会对包序号是否连续进行判断,如果不连续则说明丢包,升级会超时重启

    /**
    * @brief 升级超时
    */
    void dfu_timeout_handler(void)
    {
    	if(dfu_timeout)
    	{
    		if(--dfu_timeout == 0)
    		{
    			NVIC_SystemReset();
    		}
    	}
    }

    11.如果接收的包连续,则会一直写入到flash中

    12.升级包百分比计算 

        当前所写入的总长度与升级包文件长度的比,得出升级进度百分比

      calcPercent = readTotalLen;     
      calcPercent *= 100;                     
      dfu_percent = calcPercent / fileLength; 

    13.升级完成后,dfu_flag清0,result设成0表示升级完成

     if(readTotalLen == fileLength)
    {
        dfu_flag = 0;
        result = 0;
     }

    14.dfu_flag为0,退出循环

    15.擦除OTA标志,发送OTA完成标志给上位机,最后stm32复位重启

    • 上位机串口通信分发升级包部分

        1.基于github上的例程进行修改,使用VS2019打开解决方案

    2.定义串口类对象

    	CSerialPort m_SerialPort;//About CSerialPort 

    3.获取已插入的有效串口号

    
    	//获取串口号
    	vector<SerialPortInfo> m_portsList = CSerialPortInfo::availablePortInfos();
    	TCHAR m_regKeyValue[255];
    	for (unsigned int i = 0; i < m_portsList.size(); i++)
    	{
    #ifdef UNICODE
    		int iLength;
    		const char * _char = m_portsList[i].portName.c_str();
    		iLength = MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, NULL, 0);
    		MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, m_regKeyValue, iLength);
    #else
    		strcpy_s(m_regKeyValue, 255, m_portsList[i].portName.c_str());
    #endif
    		m_PortNr.AddString(m_regKeyValue);
    		m_PortNr2.AddString(m_regKeyValue);
    	}
    	m_PortNr.SetCurSel(0);
    	m_PortNr2.SetCurSel(0);

    3. 准备串口连接

    m_SerialPort.readReady.connect(this, &CCommDlg::OnReceive);

    4.打开和关闭串口,波特率固定为115200,无奇偶校验,8位数据长度,1位停止位

    void CCommDlg::OnBnClickedButtonOpenClose()
    {
    	// TODO:  在此添加控件通知处理程序代码
    	//GetDlgItem(IDC_SendEdit)->SetFocus();
    	CString temp;
    	m_OpenCloseCtrl.GetWindowText(temp);///获取按钮的文本
    	UpdateData(true);
    	if (temp == _T("关闭串口"))///表示点击后是"关闭串口",也就是已经关闭了串口
    	{
    		m_SerialPort.close();
    		m_OpenCloseCtrl.SetWindowText(_T("打开串口"));///设置按钮文字为"打开串口"
    	}
    	///打开串口操作
    	else if (m_PortNr.GetCount() > 0)///当前列表的内容个数
    	{
    		string portName;
    		int SelBaudRate;
    		int SelParity;
    		int SelDataBits;
    		int SelStop;
    
    		UpdateData(true);
    		m_PortNr.GetWindowText(temp);///CString temp
    #ifdef UNICODE
    		portName = CW2A(temp.GetString());
    #else
    		portName = temp.GetBuffer();
    #endif	
    
    		SelBaudRate = 115200;	
    
    		SelParity = 0;
    
    		SelDataBits = 8;
    
    		SelStop = 0;
    
    		m_SerialPort.init(portName, SelBaudRate, itas109::Parity(SelParity), itas109::DataBits(SelDataBits), itas109::StopBits(SelStop));
    		m_SerialPort.open();
    
    		if (m_SerialPort.isOpened())
    		{
    			m_OpenCloseCtrl.SetWindowText(_T("关闭串口"));
    		}
    		else
    		{
    			AfxMessageBox(_T("串口已被占用!"));
    		}
    	}
    	else
    	{
    		AfxMessageBox(_T("没有发现串口!"));
    	}
    }

    5.关闭窗口释放串口资源

    
    void CCommDlg::OnClose()
    {
    	// TODO:  在此添加消息处理程序代码和/或调用默认值
    	m_SerialPort.close();
    	m_SerialPort2.close();
            if (pOTA_file_buf != NULL) {
    		delete pOTA_file_buf;
    		pOTA_file_buf = NULL;
    	}
    	CDialogEx::OnClose();
    }

      6. 获取升级包文件名和长度

    
    void CCommDlg::OnBnClickedButtonOpenClose4()
    {
    	// TODO: 在此添加控件通知处理程序代码
    	  // 设置过滤器   
    	TCHAR szFilter[] = _T("文本文件(*.bin)|*.bin|所有文件(*.*)|*.*||");
    
    	// 构造打开文件对话框
    	CFileDialog fileDlg(TRUE, _T("txt"), NULL, 0, szFilter, this);
    	CString strFilePath;
    	if (IDOK == fileDlg.DoModal())
    	{
    		// 如果点击了文件对话框上的“打开”按钮,则将选择的文件路径显示到编辑框里   
    		strFilePath = fileDlg.GetPathName();
    		SetDlgItemText(IDC_EDIT2, strFilePath);
    		CFile file;
    		file.Open(strFilePath, CFile::typeBinary, NULL);
    		file_len = (DWORD)file.GetLength();
    		char buf[200];
    		sprintf(buf, "%d", file_len);
    		CString str_temp((char*)buf);
    		m_file_size.SetWindowTextW(str_temp);
    		m_ota_file_name = file.GetFileName();  //获取升级包文件名
    	
    		if (pOTA_file_buf)   //如果指针为非空,先释放内存
    		{
    			delete pOTA_file_buf;
    			pOTA_file_buf = NULL;
    		}
    		if (pOTA_file_buf == NULL) // 分配内存
    		{
    			pOTA_file_buf = new char[file_len];
    			file.Read(pOTA_file_buf, file_len);  // 读取升级包所有内容
    
    		}
    		file.Close();
    
    	}
    
    }

    7.串口接收监听OnReceive,

    void CCommDlg::OnReceive()
    {
    
    	int iRet = m_SerialPort.readAllData(uart_receive_buf);
    
    	if (iRet > 0)
    	{
    		
    
    		if (uart_receive_buf[0] != (char)0xff)  //包头检查
    			return;
    		BYTE len = uart_receive_buf[1];
    		if (len < 4 || len > 150)        // 长度限制检查
    			return;
    		len -= 1;
    		char checksum = calc_checksum((BYTE*)uart_receive_buf, len);
    		if (checksum != uart_receive_buf[len])  // 校验和检查
    			return;
    		switch ((BYTE)uart_receive_buf[2])
    		{
    		case 0x01:  // 获取版本及OTA标志
    		{
    			if (uart_receive_buf[3 + 4])  // 升级标志
    			{
    				m_dfu_status.SetWindowTextW(_T("升级中..."));
    				if ((pOTA_file_buf != NULL) && (file_len > 0)) {
    				  m_dfu_index = 0;
    				  package_index = 0;
    				  BYTE len = m_ota_file_name.GetLength(); 
    				  char send_buf[128];
    				  BYTE index = 0;
    				  send_buf[index++] = (char)0xff;
    				  send_buf[index++] = len + 9;
    				  send_buf[index++] = (char)0XD9;  //文件名和长度发送命令
    				  for (DWORD i = 0; i < len; i++)
    				  {
    					  send_buf[index++] = (char)m_ota_file_name.GetAt(i);
    				  }
    				  send_buf[index++] = '\0';
    				  send_buf[index++] = (file_len >> 24) & 0xff;
    				  send_buf[index++] = (file_len >> 16) & 0xff;
    				  send_buf[index++] = (file_len >> 8) & 0xff;
    				  send_buf[index++] =  file_len & 0xff;
    				  send_buf[index] = calc_checksum((BYTE*)send_buf, index);
    				  m_SerialPort.writeData(send_buf, index+1);
    				}
    				else
    				{
    					m_dfu_status.SetWindowTextW(_T("请选择升级bin文件"));
    				}
    			}
    			else
    			{
    				m_dfu_status.SetWindowTextW(_T("应用程序"));
    			}
    			uart_receive_buf[3 + 4] = '\0';
    			CString str((char*)&uart_receive_buf[3]);
    			m_version_name.SetWindowTextW(str);
    			
    		}
    			break;
    
    		case 0XDA:   // 发送升级包内容
    			if ((pOTA_file_buf != NULL) && (file_len > 0)) {
    				switch (uart_receive_buf[4])
    				{
    				case 0:
    				case 1:
    				{
    					char buf[200];
    					sprintf(buf, "升级完成 %d%%", uart_receive_buf[3]);
    					CString temp((char*)buf);
    					m_dfu_status.SetWindowTextW(temp); //升级进度
    					if (m_dfu_index >= file_len)
    						break;
    					DWORD len = 128;
    					if ((m_dfu_index + 128) > file_len) // 不够128
    					{
    						len = file_len - m_dfu_index;
    					}
    					
    					buf[0] = (char)0xff;
    					buf[1] = (char)(len+8);
    					buf[2] = (char)0xda;
    					buf[3] = (package_index >> 24) & 0xff;
    					buf[4] = (package_index >> 16) & 0xff;
    					buf[5] = (package_index >> 8) & 0xff;
    					buf[6] = package_index & 0xff;
    					for (DWORD i = 0; i < len; i++) {
    						buf[7 + i] = pOTA_file_buf[m_dfu_index + i];
    					}
    					m_dfu_index += len;
    					package_index++;  //包序号递增
    					len += 7;
    					buf[len] = (char)calc_checksum((BYTE*)buf, (BYTE)len);
    					
    					m_SerialPort.writeData(buf, len+1);  //发送升级包
    
    
    				}
    					break;
    				case 2:
    					m_dfu_status.SetWindowTextW(_T("升级失败!"));
    					break;
    				default:
    					break;
    				}
    			}
    			else
    			{
    				m_dfu_status.SetWindowTextW(_T("请选择升级bin文件"));
    			}
    			
    			break;
    		default:
    			break;
    		}
    		CString str1;
    		if (m_nHexCheck)
    		{
    			char buf[2048];
    			hex_disp_buffer(uart_receive_buf, buf, iRet);
    			 str1 = ((char*)buf);
    			 str1 += "\n";
    		}
    		else
    		{
    			str1 = (char*)uart_receive_buf;
    		}
    		
    		m_ReceiveCtrl.SetSel(-1, -1);
    		m_ReceiveCtrl.ReplaceSel(str1);
    	}
    	
    }
    • 运行结果:

    • DEMO下载地址:

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

     

     

     

     

     

     

     

     

    展开全文
  • 此IAP程序通过串口通信实现对C8051F340目标板固件升级,代码经测试验证,上位机测试工具选用dnw_v0.60c,该资源可以直接使用。
  • 一、 说明: ...1、 如果之前没有烧录bootloader.hex进开发板,先烧写BootLoader文件:bootloader 上位机不能向 mcu 烧写bootloader 引导程序,需使用仿真器事先将引导程序烧写进mcu。 2、 烧写完BootLoader程

    一、 说明:
    bootloader.hex文件里,支持 UART0(PF3,PF4), UART1(PB0, PB1), UART2(PB2,PB3)三路串口进行升级,默认通信参数 115200bps,无校验。我进行烧写时使用的是TTL串口。
    二、串口烧录步骤:
    1、 如果之前没有烧录bootloader.hex进开发板,先烧写BootLoader文件:bootloader 上位机不能向 mcu 烧写bootloader 引导程序,需使用仿真器事先将引导程序烧写进mcu。
    2、 烧写完BootLoader程序之后,烧写用户程序,如果是普通的程序需要修改两处将之修改成可升级程序:
    ① 在system_FM33G0XX.c(Starup)中配置向量表偏移量寄存器:
    在这里插入图片描述
    ② 修改的这个偏移量数值要和.sct文件里面的设置对应:
    在这里插入图片描述
    点击Edit,在文档里做相应修改:

    在这里插入图片描述
    为什么用户代码运行地址是0x4000:
    0x4000=16k,即使用16k的大小(0-0x4000)给BootLoader引导程序用,如果引导程序比16k更大,可以修改此值,比如扩大到32k,此时将0x4000修改为0x8000,相应的,在文件define_all.h 中将APPLICATION_ADDRESS 0x4000 改为 0x8000:
    在这里插入图片描述
    但通常,运行引导程序之后可知0x4000已足够。
    修改完之后运行程序,生成hex文件。
    3、 打开fm33Gx_bootload.exe软件:
    在这里插入图片描述
    分别打开hex文件,设置偏移地址0x4000,选择正确的串口,波特率,校验位;然后打开电源上电,点击下载,直到完成。
    需注意:
    1、 不推荐使用BootLoader.hex文件和用户程序的hex文件合并成一个文件之后烧录,如果对接成功并擦除成功,但写入却失败时,此时原本存在的BootLoader文件已被擦除,得重新下载BootLoader文件才能再一次下载。
    2、 需要上电才能写入成功,偏移地址需对应。

    上述是通过复旦微相关文档和自己实践整理的,更详细的论坛上应该有文档介绍。顺便附上烧录工具。
    链接:https://pan.baidu.com/s/1B7o7VgFLd-Wgec0qnPjTpA
    提取码:6cx9

    展开全文
  • 关于上位机的说明,可参考《[Macro]3D8-2012 上位机程序升级~》 视频中提到的2款上位机程序,运行需要Microsoft .Net Framework 4 Client Profile支持,目前以放入3D8光立方技术交流群(165068863)共享。 上位机...
  • STM32下位这边分为两个程序,一个是BootLoader,一个是正常启动的APP 流程 正常启动流程 Created with Raphaël 2.1.2开始 读取内部Flash OTA_STATUS==0?跳转APP 等待固件升级 yesno 固件升级流程 上位机...

    STM32下位机这边分为两个程序,一个是BootLoader,一个是正常启动的APP

    ##流程
    ###正常启动流程

    Created with Raphaël 2.2.0开始读取内部Flash OTA_STATUS==0?跳转APP等待固件升级yesno

    ###固件升级流程
    上位机流程详情见上一篇博客

    Created with Raphaël 2.2.0接收串口数据(ModBus写寄存器)OTA_STATUS=1(修改内部Flash) 跳转回BootLoader

    总的来说,下位机运行在APP是常态,通过ModBus指令,让其跳转回BootLoader。
    在BootLoader中进行接收固件,覆盖APP程序,以达到固件升级的目的。

    ##BootLoader程序
    以SMT32F103ZET6为例,有512k flash,总共有0x80000个地址。
    切割成两块。BootLoader占据64k,APP占据448k。

    | 程序块 | start | size | end |
    | ------------- |:-------------😐 -----😐
    | BootLoader | 0x8000000 | 0x10000 | 0x8010000 |
    | App | 0x8010000 | 0x70000 | 0x8080000 |

    ###ROM配置
    这里写图片描述

    ###main.c

    #include "main.h"
    
    uint8_t rx_buffer[USART_MAX_LEN];
    uint8_t tx_buffer[USART_MAX_LEN];
    uint16_t rx_buffer_len=0;
    
    uint16_t otaStatus=0;				//OTA״̬
    uint16_t softwareVersion=0;		//¹Ì¼þ°æ±¾
    _W2Byte softwareLength;			//¹Ì¼þ´óС
    uint16_t _recordNumber;			//¼Ç¼ºÅ
    
    
    int main(void)
    { 
    	HAL_Init();                    	 		//³õʼ»¯ HAL¿â     
    	Stm32_Clock_Init(RCC_PLL_MUL9);   	//³õʼ»¯ ʱÖÓ,72M 
    	IWDG_Init(IWDG_PRESCALER_64,2000);  //³õʼ»¯ ÄÚ²¿¿´ÃŹ· ·ÖƵÊýΪ64,ÖØÔØֵΪ1000,Òç³öʱ¼äΪ4s	
    	SP706SE_Init();											//³õʼ»¯ Íⲿ¿´ÃŹ·
    	BUZZ_Init();
    	MX_USART1_UART_Init(); 							//³õʼ»¯ ´®¿Ú1
    	USP_USART_RINGBUFFER_Init();	  		//³õʼ»¯ ´®¿Ú»·Ðλº³åÇø
    	 
    	Boot2App();  
    	
    	while(1)
    	{
    		HAL_Delay(10);
    		IWDG_Feed();		//ι¹· ÄÚ²¿¿´ÃŹ·
    		SP706SE_WDOG;		//ι¹· Íⲿ¿´ÃŹ· 
    		
    		USART_Receive_Data(&huart1,rx_buffer,&rx_buffer_len);
    		if(rx_buffer_len>0)
    		{
    			ModbusTask();
    		} 
    	} 
    }
    
    void Boot2App(void)
    {
    	STMFLASH_Read(FLASH_OTA_STATUS_ADDR,&otaStatus,1);//¶ÁÈ¡µ±Ç°OTA״̬  
    	if(otaStatus==0)
    	{  
    		DBG_DEBUG("Boot2App");
    		if(((*(vu32*)(FLASH_APP_ADDR+4))&0xFF000000)==0x08000000)//ÅжÏÊÇ·ñΪ0X08XXXXXX.	 
    			iap_load_app(FLASH_APP_ADDR);//Ö´ÐÐFLASH APP´úÂë
    		else  
    			DBG_ERROR("·ÇFLASHÓ¦ÓóÌÐò,ÎÞ·¨Ö´ÐÐ!\r\n");   
    	}
    	DBG_DEBUG("%d,BootLoader...",otaStatus);
    }
    
    void ModbusTask(void)
    {
    	uint16_t crc;
    	uint8_t cmd;
    	
    	if(rx_buffer[0] == 0x09)	                        
    	{
    		crc = CRC16(rx_buffer, rx_buffer_len -2);
    		if(crc == ((rx_buffer[rx_buffer_len-1]<<8)|rx_buffer[rx_buffer_len-2]))//УÑéCRC
    		{
    			cmd = rx_buffer[1];
    			switch(cmd)
    			{
    				case 0x03:
    					Modbus_Function_3();     
    				break;
    				case 0x10:
    					Modbus_Function_10();
    				break;
    				case 0x15:
    					Modbus_Function_15();
    				break;
    			}
    		} 
    	} 
    }
    
    //¶Á¶à¸ö¼Ä´æÆ÷
    void Modbus_Function_3(void)   
    {
    	u16 reg_addr;
    	u16 reg_count; 
    	u16 crc16;  
    	u16 index=3;
    	reg_addr = (rx_buffer[2]<<8) + rx_buffer[3];
    	reg_count = (rx_buffer[4]<<8) + rx_buffer[5]; 
    	
    	if(reg_addr == 0xFF00 && reg_count==0x01)								
    	{ //¶ÁÈ¡µ±Ç°OTA״̬  
    		index=3;
    		STMFLASH_Read(FLASH_OTA_STATUS_ADDR,&otaStatus,1);
    		tx_buffer[index++] = otaStatus>>8;
    		tx_buffer[index++] = otaStatus;
    		DBG_DEBUG("otaStatus=%d",otaStatus);
    	}
    	if(reg_addr == 0xFF02 && reg_count==0x01)
    	{ //¶ÁÈ¡¹Ì¼þ°æ±¾
    		index=3;
    		STMFLASH_Read(FLASH_SOFTWARE_VERSION_ADDR,&softwareVersion,1);
    		tx_buffer[index++] = softwareVersion>>8;
    		tx_buffer[index++] = softwareVersion;
    		DBG_DEBUG("softwareVersion=%d",softwareVersion);
    	}
    	if(reg_addr == 0xFF04 && reg_count==0x02)
    	{ //¶ÁÈ¡¹Ì¼þ´óС
    		index=3;
    		STMFLASH_Read(FLASH_SOFTWARE_LENGTH_ADDR,softwareLength.w,2);
    		tx_buffer[index++] = softwareLength.b[0];
    		tx_buffer[index++] = softwareLength.b[1];
    		tx_buffer[index++] = softwareLength.b[2];
    		tx_buffer[index++] = softwareLength.b[3];
    		DBG_DEBUG("softwareLength=%d",softwareLength.w2);
    	}
    	
    	tx_buffer[0] = rx_buffer[0];//modbusµØÖ·
    	tx_buffer[1] = 0x03;
    	tx_buffer[2] =  index-3; //×Ö½ÚÊý
    	
    	crc16 = CRC16(tx_buffer, index);
    	tx_buffer[index++] = crc16;
    	tx_buffer[index++] = crc16>>8;
    	HAL_UART_Transmit(&huart1, tx_buffer, index, 3000); 
    }
    
    //дÈë¶à¼Ä´æÆ÷
    void Modbus_Function_10(void)           
    {
    	u16 reg_addr, reg_count;
    	u16 crc16, i;  
    	
    	reg_addr = (rx_buffer[2]<<8) + rx_buffer[3];
    	reg_count = (rx_buffer[4]<<8) + rx_buffer[5];  
    	
    	if(reg_addr == 0xFF00 && reg_count==0x0001)  
    	{	//дOTA״̬   
    		otaStatus = (rx_buffer[7]<<8) + rx_buffer[8];
    		STMFLASH_Write(FLASH_OTA_STATUS_ADDR,&otaStatus,1);
    		DBG_DEBUG("дOTA STATUS : %d",otaStatus);
    		Boot2App();
    	}  
    	if(reg_addr == 0xFF02 && reg_count==0x0001)  
    	{	//д¹Ì¼þ°æ±¾
    		softwareVersion = (rx_buffer[7]<<8) + rx_buffer[8];
    		STMFLASH_Write(FLASH_SOFTWARE_VERSION_ADDR,&softwareVersion,1);
    		DBG_DEBUG("дSoftware Version : %d",softwareVersion); 
    	}  
    	if(reg_addr == 0xFF04 && reg_count==0x0002)  
    	{	//д¹Ì¼þ´óС 
    		softwareLength.b[0] = rx_buffer[7];
    		softwareLength.b[1] = rx_buffer[8];
    		softwareLength.b[2] = rx_buffer[9];
    		softwareLength.b[3] = rx_buffer[10];
    		STMFLASH_Write(FLASH_SOFTWARE_LENGTH_ADDR,softwareLength.w,2);
    		DBG_DEBUG("дSoftware Length : %d",softwareLength.w2); 
    	}  
    	
    	for(i = 0; i < 6; i++)
    		tx_buffer[i] = rx_buffer[i];
    	
    	crc16 = CRC16(tx_buffer, 6);
    	tx_buffer[6] = crc16;
    	tx_buffer[7] = crc16>>8; 
    	
    	HAL_UART_Transmit(&huart1, tx_buffer, 8, 3000); 
    } 
    
    //дÈëÎļþ
    void Modbus_Function_15(void)           
    {
    	u8 refType;					//²Î¿¼ÀàÐÍ
    	u16 fileNumber;			//ÎļþºÅ
    	u16 recordNumber;		//¼Ç¼ºÅ 
    	u16 recordLength;		//¼Ç¼³¤¶È
    	u16 i; 
    	
    	refType = (rx_buffer[3]);
    	fileNumber = (rx_buffer[4]<<8) + rx_buffer[5];
    	recordNumber = (rx_buffer[6]<<8) + rx_buffer[7]; 
    	recordLength = (rx_buffer[8]<<8) + rx_buffer[9]; 
    	 
    	if(refType!=0x06)
    	{
    		DBG_ERROR("дÎļþ ²Î¿¼ÀàÐÍ ERROR");
    		return;
    	}
    	if(recordLength!=0xf0)
    	{
    		DBG_ERROR("дÎļþ ¼Ç¼ºÅ³¤¶È ERROR");		
    		return;
    	}
    	if(softwareLength.w==0)
    	{
    		DBG_ERROR("дÎļþ ¹Ì¼þ´óСΪ¿Õ");		
    		return;
    	}
    	
    	if(recordNumber==0)
    		_recordNumber=0;
    	else
    	{
    		if(recordNumber-_recordNumber==1 || recordNumber==_recordNumber)
    		{	//дÈëÏÂÒ»¸öÎļþ¼Ç¼
    			_recordNumber=recordNumber; 
    		}
    		else
    		{
    			DBG_ERROR("дÎļþ ¼Ç¼ºÅ Error");
    			return;
    		}
    	}  
    	
    	iap_write_appbin(FLASH_APP_ADDR+_recordNumber*0xf0,rx_buffer+10,recordLength);//¸üÐÂFLASH´úÂë   
    	
    	for(i = 0; i < rx_buffer_len; i++)
    		tx_buffer[i] = rx_buffer[i];
    	 
    	
    	HAL_UART_Transmit(&huart1, tx_buffer, rx_buffer_len, 3000); 
    	
    	if( (_recordNumber == (softwareLength.w2/0xf0-1) && (softwareLength.w2%0xf0==0)) ||
    			(_recordNumber == (softwareLength.w2/0xf0) && (softwareLength.w2%0xf0!=0)) )
    	{	//¼Ç¼ºÅ==¹Ì¼þ´óС£¬¹Ì¼þ½ÓÊÕÍê³É£¬¿ªÊ¼Éý¼¶
    		otaStatus = 0; 
    		softwareVersion=fileNumber; 
    		
    		STMFLASH_Write(FLASH_OTA_STATUS_ADDR,&otaStatus,1);
    		STMFLASH_Write(FLASH_SOFTWARE_VERSION_ADDR,&softwareVersion,1); 
    		Boot2App();
    	}
    } 
    
    /* 
     * º¯ÊýÃû£ºCRC16
     * ÃèÊö  : ¼ÆËãCRC16
     * ÊäÈë  £ºaData---Êý¾Ý£¬ aSize---Êý¾Ý³¤¶È
     * Êä³ö  : УÑéÖµ
     */
    uint16_t CRC16(uint8_t *ptr,uint16_t len)
    {
    	 unsigned char i;
    	 unsigned short crc = 0xFFFF;
    	 if(len==0)
    	 {
    		 len = 1;
    	 }
    	 while(len--) 
    	 {
    		 crc ^= *ptr;
    		 for(i=0; i<8; i++)
    		 {
    			 if(crc&1)
    			 {
    				 crc >>= 1;
    				 crc ^= 0xA001;
    			 }
    			 else
    			 {
    					crc >>= 1;
    			 }
    		 }
    		 ptr++;
    	 }
    	 return(crc);
    } 
    
    
    

    ##APP程序

    ###ROM配置
    这里写图片描述

    ###生成bin文件

    这里写图片描述

    D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe   --bin -o  ..\OBJ\RTU_V1_0.bin ..\OBJ\RTU_V1_0.axf
    

    ###main.c

    #include "main.h"
    
    uint8_t rx_buffer[USART_MAX_LEN];
    uint8_t tx_buffer[USART_MAX_LEN];
    uint16_t rx_buffer_len=0; 
    
    
    int main(void)
    { 
    	SCB->VTOR = ((uint32_t)0x08010000);
    	
    	HAL_Init();                    	 		//³õʼ»¯ HAL¿â     
    	Stm32_Clock_Init(RCC_PLL_MUL9);   	//³õʼ»¯ ʱÖÓ,72M 
      IWDG_Init(IWDG_PRESCALER_64,2000);  //³õʼ»¯ ÄÚ²¿¿´ÃŹ· ·ÖƵÊýΪ64,ÖØÔØֵΪ1000,Òç³öʱ¼äΪ4s	
    	SP706SE_Init();											//³õʼ»¯ Íⲿ¿´ÃŹ·
    	BUZZ_Init();
      MX_USART1_UART_Init(); 							//³õʼ»¯ ´®¿Ú1
      USP_USART_RINGBUFFER_Init();	  		//³õʼ»¯ ´®¿Ú»·Ðλº³åÇø 
    	 
    	while(1)
    	{
    		HAL_Delay(10);
    		IWDG_Feed();		//ι¹· ÄÚ²¿¿´ÃŹ·
    		SP706SE_WDOG;		//ι¹· Íⲿ¿´ÃŹ·
    		 
    		DBG_WARN("APP..."); 
    		
    		USART_Receive_Data(&huart1,rx_buffer,&rx_buffer_len);
    		if(rx_buffer_len>0)
    		{
    			ModbusTask();
    		} 
    	} 
    }
    
    void APP2Boot(void)
    {
    	uint16_t otaStatus=0; 
    	STMFLASH_Read(FLASH_OTA_STATUS_ADDR,&otaStatus,1);//¶ÁÈ¡µ±Ç°OTA״̬  
    	if(otaStatus==1)
    	{  
    		DBG_DEBUG("APP2Boot");
    		iap_load_app(FLASH_BOOT_ADDR);//Ìøת»ØBOOT³ÌÐò 
    	}
    }
    
    void ModbusTask(void)
    {
    	uint16_t crc;
    	uint8_t cmd;
    	
    	if(rx_buffer[0] == 0x09)	                        
    	{
    		crc = CRC16(rx_buffer, rx_buffer_len -2);
    		if(crc == ((rx_buffer[rx_buffer_len-1]<<8)|rx_buffer[rx_buffer_len-2]))//УÑéCRC
    		{
    			cmd = rx_buffer[1];
    			switch(cmd)
    			{
    				case 0x03:
    					Modbus_Function_3();     
    				break;
    				case 0x10:
    					Modbus_Function_10();
    				break; 
    			}
    		} 
    	} 
    }
    
    //¶Á¶à¸ö¼Ä´æÆ÷
    void Modbus_Function_3(void)   
    {
    	u16 reg_addr;
    	u16 reg_count; 
    	u16 crc16;  
    	u16 index=3;
    	reg_addr = (rx_buffer[2]<<8) + rx_buffer[3];
    	reg_count = (rx_buffer[4]<<8) + rx_buffer[5]; 
    	if(reg_addr == 0xFF00 && reg_count == 0x0001)								
    	{ //¶ÁÈ¡µ±Ç°OTA״̬ 
    		uint16_t otaStatus=0; 
    		index=3;
    		STMFLASH_Read(FLASH_OTA_STATUS_ADDR,&otaStatus,1);
    		tx_buffer[index++] = otaStatus>>8;
    		tx_buffer[index++] = otaStatus;
    	}
    	if(reg_addr == 0xFF02 && reg_count == 0x0001)
    	{ //¶ÁÈ¡¹Ì¼þ°æ±¾
    		uint16_t softwareVersion=0;
    		index=3;
    		STMFLASH_Read(FLASH_SOFTWARE_VERSION_ADDR,&softwareVersion,1);
    		tx_buffer[index++] = softwareVersion>>8;
    		tx_buffer[index++] = softwareVersion;
    	}
    	if(reg_addr == 0xFF04 && reg_count == 0x0002)
    	{ //¶ÁÈ¡¹Ì¼þ´óС
    		_W2Byte softwareLength;
    		index=3;
    		STMFLASH_Read(FLASH_SOFTWARE_LENGTH_ADDR,softwareLength.w,2);
    		tx_buffer[index++] = softwareLength.b[0];
    		tx_buffer[index++] = softwareLength.b[1];
    		tx_buffer[index++] = softwareLength.b[2];
    		tx_buffer[index++] = softwareLength.b[3];
    	}
    	
    	tx_buffer[0] = rx_buffer[0];//modbusµØÖ·
    	tx_buffer[1] = 0x03;
    	tx_buffer[2] =  index-3; //×Ö½ÚÊý
    	
    	crc16 = CRC16(tx_buffer, index);
    	tx_buffer[index++] = crc16;
    	tx_buffer[index++] = crc16>>8;
    	HAL_UART_Transmit(&huart1, tx_buffer, index, 3000); 
    }
    
    //дÈë¶à¼Ä´æÆ÷
    void Modbus_Function_10(void)           
    {
    	u16 reg_addr, reg_count;
    	u16 crc16, i;  
    	
    	reg_addr = (rx_buffer[2]<<8) + rx_buffer[3];
    	reg_count = (rx_buffer[4]<<8) + rx_buffer[5];  
    	
    	if(reg_addr == 0xFF00 && reg_count == 0x0001)  
    	{	//дOTA״̬   
    		uint16_t otaStatus=0; 
    		otaStatus = (rx_buffer[7]<<8) + rx_buffer[8];
    		STMFLASH_Write(FLASH_OTA_STATUS_ADDR,&otaStatus,1);
    		DBG_DEBUG("дOTA STATUS : %d",otaStatus);
    		APP2Boot();
    	}   
    	
    	for(i = 0; i < 6; i++)
    		tx_buffer[i] = rx_buffer[i];
    	
    	crc16 = CRC16(tx_buffer, 6);
    	tx_buffer[6] = crc16;
    	tx_buffer[7] = crc16>>8; 
    	
    	HAL_UART_Transmit(&huart1, tx_buffer, 8, 3000); 
    }  
    
    /* 
     * º¯ÊýÃû£ºCRC16
     * ÃèÊö  : ¼ÆËãCRC16
     * ÊäÈë  £ºaData---Êý¾Ý£¬ aSize---Êý¾Ý³¤¶È
     * Êä³ö  : УÑéÖµ
     */
    uint16_t CRC16(uint8_t *ptr,uint16_t len)
    {
    	 unsigned char i;
    	 unsigned short crc = 0xFFFF;
    	 if(len==0)
    	 {
    		 len = 1;
    	 }
    	 while(len--) 
    	 {
    		 crc ^= *ptr;
    		 for(i=0; i<8; i++)
    		 {
    			 if(crc&1)
    			 {
    				 crc >>= 1;
    				 crc ^= 0xA001;
    			 }
    			 else
    			 {
    					crc >>= 1;
    			 }
    		 }
    		 ptr++;
    	 }
    	 return(crc);
    } 
    
    
    

    这里写图片描述

    展开全文
  • STM32 串口IAP在线升级

    2020-09-09 09:44:14
    我们需要做的就是,写单片机FLASH的读写接口,程序可以通过上位机进行下发,然后单片机程序调用FLASH写函数,把下发的代码写到对于FLASH进行覆盖,即实现更新。当然这只是一个大概思路,具体实现还是要注意很多细节...
  • 串口升级的优点有硬件稳定,驱动简单,但是传输速度慢。网络升级的优点传输速度快,但是驱动复杂,升级场地局限(必须要有上位机)。U 盘升级的优点不受场地限制,传输速度快,但是驱动有点复杂。不过 ST官方已经将 ...
  • 基于Ymodem协议的TI C2000 DSP串口在线升级方法,其特征在于,包括如下步骤:S1、上位机根据Ymodem通信协议通过对应的串行通信接口SCI,将应用程序的镜像文件以数据帧的形式加载/缓存到目标系统DSP;S2、目标系统DSP...
  • 此版为方形点阵DYS388,显示面积更大,支持当前16位真彩图片EEPROM保存,支持3~5V宽电源输入,升级算法使显示更稳定。 DYS388真彩点阵LED屏使用教学视频: 【模块地址的设置方法】 1,上电后用线短接背面的”SET...
  • STM32串口IAP固件升级探讨--Ymodem协议仿真Ymodem协议疑惑Ymodem协议仿真总结 ...所以,我利用串口调试助手(模拟待升级设备)和secureCRT软件(上位机软件)搭建了一个仿真通信通道,以了解整个通信的各帧内...
  • 串口IAP升级在正点原子的例程中有讲解,正点原子的方法是:在RAM中开辟一个120K的数据空间,用来存放bin文件,bin文件通过串口一次性发送到单片机,然后再实现程序的跳转。但是这种方法在实际项目中并不实用,因为...
  • 在进行升级操作时,将单片机系统的串口与PC机相连,打开上位机软件,装入烧录文件。选择串口,点击连接键,这时会弹出进在尝试连接的对话框。再将你的系统复位或加电重启。此时,上位机软件会显示已连接上,此时点击...
  • 做了2G模块的IAP程序升级和基于串口上位机的IAP程序升级。写下一点自己的浅见。 概况: 软件上: 具备远程升级的单片机的程序整体具体包含两个部分,一部分是(APP)应用程序,另一部分是(BOOT)引导程序。APP...
  • VS串口通讯

    2018-08-25 21:55:42
    VS2015串口通讯 开发一个简单的上位机界面程序,实现上位机与下位机串口通信,从而对下位机进行程序升级。 win10系统; VS2015社区版(免费);Comm
  • AIR724模块DTU配置上位机的设计 代码仓库:点我下载 代码质量一般。编译好的安装程序:安装包下载 功能特性 用于4G、NB等模块通讯调试和配置。 基础串口助手功能 DTU配置生成Json DTU快捷指令 Json导入导出官方...
  •   了解了LZMA SDK的基本用法,接下来就是要 "移植解压部分到目标板程序,实现[上位机压缩程序]->[串口传输]->[目标板解压升级程序]的串口升级流程"   首先,还是按部就班,不急着把代码马上...
  • 在网上找到一个串口MFC程序,实现了蓝牙串口的收发功能 开发环境:Visual Studio 2013 语言:MFC C++ 注意:打开工程升级之后不要马上就生成可执行程序,首先:菜单》》生成》》重新生成解决方案 然后就可以点击生成...
  • 与固件中的虚拟串口服务器程序一起用于实现无线串口功能 其它 用户手册 全部AT+指令集的使用方法Demo源码 2.功能定义 支持802.11b-only、802.11b/g mix 支持1~13信道 支持1M、2M、5.5M、6M...
  • 固件升级 通过配置管理程序 上位机软件 配置管理程序 用于模块的参数配置、联网、传输功能演示等 生产测试程序 用于生产阶段的出厂检测,包括物理层测试、功率校正、MAC地址烧写 虚拟串口客户端程序 与固件中的虚拟...

空空如也

空空如也

1 2 3
收藏数 56
精华内容 22
关键字:

串口升级上位机程序