stm32cubemx IAP升级(三)

news2024/11/23 8:51:15

stm32cubemx IAP升级- UART+DMA实现不定长收发数据

板卡:Nucleo-L412
平台:macbook pro
工具:vscode stm32cubemx stm32cubeProgramer cmake toolchain

Stm32CubeMx的配置

选择开启一路串口并配置成DMA,并使能中断,配置中断优先级。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述## 代码实现
main.c
1、定义全局接收数组
uint8_t UART2_RX_BUF[UART2_RX_SIZE] = {0}; //串口接收缓存
uint8_t UART2_RX_LEN;
2、使能空闲中断,以及DMA接收数据
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE); //使能空闲中断
HAL_UART_Receive_DMA(&huart2,UART2_RX_BUF,UART2_RX_SIZE); //启动DMA接收,UART1_RX_BUF:数据接收缓冲
3、修改stm32l4xx_it.c,将USART2_IRQHandler函数修改如下

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != RESET))
  {
   	 	__HAL_UART_CLEAR_IDLEFLAG(&huart2);                                     //清除空闲状态标志
    	HAL_UART_DMAStop(&huart2);                                              //关闭DMA传输

	    
 		UART2_RX_LEN = UART2_RX_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); 	//计算接收到的数据长度
		USAR_UART_IDLECallback(&huart2,UART2_RX_LEN );                          //调用回调函数
	}
 /* USER CODE END USART2_IRQn 1 */
}

USAR_UART_IDLECallback函数定义,UART2_RX_BUF即收到的数据,rxlen即为收到数据的长度

void USAR_UART_IDLECallback(UART_HandleTypeDef *huart,uint8_t rxlen )
{
	if(huart == &huart2)                                        //判断是否为串口1产生中断
	{
    if (rxlen >0)
      handle_uart_message(UART2_RX_BUF,rxlen);
		// HAL_UART_Transmit_DMA(&huart2, UART2_RX_BUF,rxlen);    //将接收到的不定长数据发送到上位机
	  rxlen = 0;                                                //清除数据长度计数
    HAL_UART_Receive_DMA(&huart2,UART2_RX_BUF,UART2_RX_SIZE); //重新打开DMA接收	        
	}
}

handle_uart_message函数为收到升级命令,数据,以及升级完成的命令的解析函数。
串口升级协议如下:
cmd + data_lenght + data0 + …+ datax + checksum
1、获取版本号 0x01 0x02 0x00 0x00 checksum
2、升级
1、进入升级模式 0x02 0x02 0x00 0x00 checksum
2、升级文件大小 0x03 0x04 0x00 0x00 0x00 0x00 checksum
3、数据包发送 0x04 0x80 0x00 0x00 0x00 0x00 … checksum
4、数据包发送完成 0x05 0x02 0x00 0x00 checksum

typedef enum uart_update_cmd
{
    UART_GET_SYSTEM_VERSION_CMD                 = 0x01,
    UART_ENTER_SYSTEM_UPDATE_MODE_CMD           = 0x02,
    UART_GET_SYSTEM_FILE_SIZE_CMD               = 0x03,
    UART_RECEIVE_SYSTEM_UPDATE_CMD              = 0x04,
    UART_COMPLETE_SYSTEM_UPDATE_CMD             = 0x05,

}MI_UartUpdateCmd;

MI_BOOL handle_uart_message(MI_U8 *p_buff,MI_U32 len)
{
    switch (p_buff[UART_CMD_INDEX])
    {
    case UART_GET_SYSTEM_VERSION_CMD/* constant-expression */:
        /* 获取系统版本号 */
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            system_info_get_system_version(version);
            HAL_UART_Transmit_DMA(&huart2, version,strlen((const char *)version));       //将接收到的不定长数据发送到上位机
        }
        break;
    case UART_ENTER_SYSTEM_UPDATE_MODE_CMD:
        /* 进入升级模式命令 指示灯变为100ms闪烁一次*/
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            led_freq = LED_TOGGLE_100_MS;
            w_time = 0;
            packets_numer = 0;
            upgrade_file_size = 0;
            remain_packets_numer = 0;
            isRunningUpdate = 1;
            printf("Mcu Receive Update Command and Wait Receive Data Packets \r\n");
            printf("Now to Erase Download Pages \r\n");
            printf("\r\n");
            int ret = stm32_erase_flash(DOWNLOAD_START_SECTOR_ADDR,DOWNLOAD_END_SECTOR_ADDR);

            printf("\r\n");

        }
        break;
    case UART_GET_SYSTEM_FILE_SIZE_CMD:
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            int a = (int)p_buff[2];
            int b = (int)p_buff[3];
            int c = (int)p_buff[4];
            int d = (int)p_buff[5];
            upgrade_file_size = (a<<24) | (b<<16) | (c<<8) | (d);

            if (upgrade_file_size % UPGRADE_DATA_PACKAGES_LENGHT == 0)  //如果整除128 
            {
                packets_numer = upgrade_file_size/128;
            }
            else
            {
                packets_numer = ((upgrade_file_size/128) + 1);
            }
            printf("End Erase Download Pages Down\r\n");
            printf("\r\n");

            printf("receive upgrade file size %d\r\n",upgrade_file_size);
            printf("data packets number  %d\r\n",packets_numer);
            printf("\r\n");
        }
        break;
    case UART_RECEIVE_SYSTEM_UPDATE_CMD:
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            printf("receive packets........................%03d [100] \r\n",(((w_time + 1) * 100) / packets_numer));
            memset(w_buff,0,sizeof(w_buff));
            memcpy(w_buff,&p_buff[2],sizeof(w_buff));

            stm32_flash_write(DOWNLOAD_START_SECTOR_ADDR+(w_time * UPGRADE_DATA_PACKAGES_LENGHT),w_buff,sizeof(w_buff));

            if (w_time + 1 == packets_numer)
            {
                led_freq = LED_TOGGLE_1000_MS;
                printf("receive packets........................ done!\r\n");
            }
            w_time ++;
        }
        break;
    case UART_COMPLETE_SYSTEM_UPDATE_CMD:
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            system_info_set_update_flag(SYSTEM_UPDATE);
            printf("Now Reboot !\r\n");
            app_soft_reset();
        }
        break;    
    default:
        break;
    }
    return MI_TRUE;
}

校验采用CRC16

static MI_U16 CRC16(MI_U8 * buf, MI_U16 len)
{
	MI_U16 i;
	MI_U16 crc = 0xffff;
 
	if (len == 0) {
		len = 1;
	}
	while (len--) {
        
		crc ^= *buf;
		for (i = 0; i<8; i++) 
		{            
			if (crc & 1) {               
				crc >>= 1;        
				crc ^= 0xA001;            
			}      
			else {               
				crc >>= 1;            
			}       
		}     
		buf++;
	}
	return(crc);
}

static MI_BOOL calculate_checksum_and_length(MI_U8 * buf, MI_U16 len)
{
    MI_U16 crc16 = CRC16(buf,len-2);
    if ((crc16 & 0x00ff) == buf[len-2] && ((crc16 >> 8) & 0x00ff) == buf[len-1])
    {
        if (buf[UART_CMD_LENGTH] == (len-2-2))
        {
            return MI_TRUE;
        }
        else
        {
            printf("crc16 right ,but cmd error\r\n"); 
            //_Error_Handler(__FUNCTION__,__LINE__);
            return MI_FALSE; 
        }
    }
    else
    {
        printf("crc16 error and right crc16 = 0x%04x\r\n",crc16);  
        //_Error_Handler(__FUNCTION__,__LINE__);
        return MI_FALSE; 
    }
}

flash写函数:

/* FLASH大小:STM32L412 :128K */
#define STM32_FLASH_SIZE         0x00020000UL
/* FLASH起始地址 */
#define STM32_FLASH_BASE         0x08000000UL
/* FLASH结束地址 */
#define STM32_FLASH_END          (STM32_FLASH_BASE | STM32_FLASH_SIZE)
/* FLASH页大小:2K */
#define STM32_FLASH_PAGE_SIZE    FLASH_PAGE_SIZE
/* FLASH总页数  64 页*/
#define STM32_FLASH_PAGE_NUM     (STM32_FLASH_SIZE / STM32_FLASH_PAGE_SIZE)


//针对STM32L412单片机,通过地址获取所在页的函数如下
MI_U32 get_flash_page(MI_U32 address)											//获取地址所在的
{
    MI_U32 page = 0;
    if (address < (STM32_FLASH_BASE + FLASH_BANK_SIZE))
        page = (address - STM32_FLASH_BASE) / FLASH_PAGE_SIZE;
    else
        page = (address - (STM32_FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE;
    return page;
}

MI_U8 stm32_erase_flash(MI_U32 start_addr,MI_U32 end_addr)		//擦除flash
{
    FLASH_EraseInitTypeDef EraseInitStruct;
    MI_U32 FirstPages = 0,LastPages = 0, NbPages = 0;
    uint32_t pageError = 0;
    HAL_FLASH_Unlock();                                         //首先解锁flash
    FirstPages = get_flash_page(start_addr);		            //获取要擦除的第一个页
    LastPages = get_flash_page(end_addr);
    NbPages = LastPages - FirstPages;	                      //获取擦除的页数量

    /* Fill EraseInit structure*/
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;	//页擦除
    EraseInitStruct.Banks = FLASH_BANK_1;
    EraseInitStruct.Page = FirstPages;
    EraseInitStruct.NbPages = NbPages;
    if(HAL_FLASHEx_Erase(&EraseInitStruct, &pageError) != HAL_OK)
    { 
          HAL_FLASH_Lock();                                       //上锁
      return 2;												//擦除有错误,返回2
    }
      //下面这些是清除标志位
    __HAL_FLASH_DATA_CACHE_DISABLE();
    __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
  
    __HAL_FLASH_DATA_CACHE_RESET();
    __HAL_FLASH_INSTRUCTION_CACHE_RESET();
  
    __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
    __HAL_FLASH_DATA_CACHE_ENABLE();
  
    HAL_FLASH_Lock();                                           //上锁
    return 0;
}


MI_BOOL stm32_flash_write(MI_U32 dest_addr, MI_U8 *src, MI_U32 Len)
{
    MI_U32 i = 0;

    HAL_FLASH_Unlock();
    /* Clear OPTVERR bit set on virgin samples */
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);

    for(i = 0; i < Len; i += 8)
    {
      /* Device voltage range supposed to be [2.7V to 3.6V], the operation will
        be done by byte */
      if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (MI_U32)(dest_addr+i), *(uint64_t*)(src+i)) == HAL_OK)
      {
      /* Check the written value */
        if(*(uint64_t *)(src + i) != *(uint64_t*)(dest_addr+i))
        {
          /* Flash content doesn't match SRAM content */
          HAL_FLASH_Lock();            
          return -1;
        }

      }
      else
      {
          HAL_FLASH_Lock();            

        /* Error occurred while writing data in Flash memory */
        return -1;
      }
    }
    HAL_FLASH_Lock();            

    return 0;
}

针对L412单片机,写入时是按FLASH_TYPEPROGRAM_DOUBLEWORD格式写入的,所以要8个地址++,这点要注意。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/422421.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

优思学院|质量改进必备技能:克罗斯比的14步骤全面解析

菲利普克罗斯比&#xff08;Philip Crosby&#xff09;是一位著名的质量管理专家&#xff0c;被誉为"零缺陷之父"、“现代质量运动之父”。他于1926年出生于美国俄亥俄州&#xff0c;曾在美国空军服役。后来他在ITT公司和马丁-马里埃塔公司等企业担任质量管理师和高级…

【pycharm】pycharm配置svn

目录 1、配置svn地址 2、配置svn按钮 3、配置svn地址 4、checkout项目到PycharmProjects 5、使用 6、打开项目或者checkout项目 7、配置虚拟环境 8、虚拟环境安装库 1、配置svn地址 Seting-version control-subversion 找不到svn.exe 点我博文 2、配置svn按钮 VCS--…

C语言课设项目-51单片机-独立按键与矩阵按键

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、独立按键 1.按键介绍 2.独立按键原理 3.编写独立按键控制程序 二、矩阵按键 1.矩阵按键的由来 2.…

《暮色心迹》开机大吉,在上虞上演一场蓄谋已久的爱情对弈

4月15日&#xff0c;由秀合影视&#xff08;浙江&#xff09; 有限公司、辰耀影业文化传媒&#xff08;北京&#xff09;有限公司、浙江中创华视文化发展有限公司出品&#xff0c;浙江诺然文化传媒有限公司、北京幻想纵横网络技术有限公司、浙江知马影视服务有限公司、浙江沐阳…

最落魄的时候,身上带着《毛选》

最落魄的时候&#xff0c;包里只带《毛选》的腕儿 史玉柱&#xff1a;营销大咖&#xff0c;早年创业起伏大 东山再起的腕儿 趣讲大白话&#xff1a;成功是用心血浇灌的 【趣讲信息科技133期】 **************************** 90%的企业是销售驱动型 所以要把品牌和营销的事搞透彻…

Buyflag

拿到题目可以获取到几条关键提示如果你想要买flag你必须是CUIT的学生你必须回答正确的密码查看源码也发现有php源码提示信息要求通过POST方式传参&#xff0c;并利用isset检测是否有password字段is_numeric是检测password字段是否为数字或者数字字符串&#xff0c;如果为数字则…

集合,Collection接口,Iterator(迭代器),List接口和方法,ArrayList底层结构和源码分析

数组的不足 长度开始必须指定&#xff0c;而且一旦指定&#xff0c;不能修改保存的必须为同一类型的元素使用数组进行增加/删除元素的示意代码麻烦SuppressWarnings({"all"})抑制警告集合 可以动态保存任意多个对象&#xff0c;使用比较方便提供了一系列方便的操作对…

【零基础学习】Javascript 快速入门(完整篇)简单、适合初学者

【零基础学习】Javascript 快速入门前言&#xff1a;如何解决错误提示&#xff08;Error&#xff09;Uncaught TypeError: Cannot set properties of null (setting innerHTML)Uncaught ReferenceError: displayDate is not defined at HTMLButtonElement.onclick安装Visual St…

剪枝与重参第八课:ACNet、DBB、RepVGG重参

目录ACNet、DBB、RepVGG重参前言1. 并行多分支结构1.1. 并行多分支结构 Demo2. ACNet2.1 ACNet简述2.2 init2.3 forward2.4 swtich to deploy2.5 get_equivalent_kernel_bias2.6 Conv2d与BN的融合(重参)2.7 Conv1x3Conv3x1Conv3x3的融合(重参)2.8 模型导出2.9 完整示例代码总结…

【电路理论】KCL、KVL、线性直流电路各大方法、定理详解

博主简介&#xff1a;努力学习的22级计科生一枚~博主主页&#xff1a; 是瑶瑶子啦所属专栏: 电路理论 目录一、KCL、KVL定律1.1&#xff1a;KCL1.2&#xff1a;KVL1.3&#xff1a;总结二、线性直流电路2.1&#xff1a;电阻网络等效变换2.1.1&#xff1a;电阻等效——三角&星…

【Flink】Flink基础

Flink 官网地址 &#xff08;官网介绍的非常详细&#xff0c;觉得看英文太慢的直接使用浏览器一键翻译&#xff0c;本文是阅读官方文档后进行的内容梳理笔记&#xff09; https://nightlies.apache.org/flink/flink-docs-release-1.17/docs/dev/python/overview/ 这 Flink API …

winForm初始

创建winForm应用程序步骤 创建项目界面设计&#xff0c;拖控件布局设置属性编写代码运行程序 设置属性 在forms框内右击属性 属性框内有 修改标题 在(属性)里的外观里的Text, 点击text后会出现相对应的提示 设置关联属性名称 查看代码 右击 设置label名称 设置textbox关联属…

38-Vue之cron表达式组件使用

cron表达式组件使用前言vue-cron-editor-buefy1. 安装vue-cron-editor-buefy包2. 使用3. 配置路由4. 运行并查看效果vcrontab1. 安装vcrontab包2. 使用3. 配置路由4. 运行并查看前言 本篇来学习下vue中如何生成cron表达式的两个包 vue-cron-editor-buefy 1. 安装vue-cron-ed…

先认识浏览器和 dom

先认识浏览器和 dom 认识浏览器使用控制台(console)初识 dom获取浏览器可见区域高度简单的操作一下 dom向页面添加一个元素innerHTML认识块模式认识坐标与定位小结认识浏览器 我们先创建一个文本文件,然后将其扩展名改成 html,或者直接创建一个 html 文档。嗯,空白的,里…

『pyqt5 从0基础开始项目实战』08. 本地数据配置文件的保存与读取之SMTP邮件报警(保姆级图文)

目录导包和框架代码简化说明绑定鼠标事件编写弹窗UI和读取配置保存配置功能读取本地配置文件编写UI界面保存设置main.py中启动弹窗UI完整代码main.pythreads.pydialog.py总结欢迎关注 『pyqt5 从0基础开始项目实战』 专栏&#xff0c;持续更新中 欢迎关注 『pyqt5 从0基础开始项…

什么是MVVM?

MVVM 是 Model-View-ViewModel 的缩写&#xff0c;是M-V-VM三部分组成。它本质上就是MVC的改进版。 M&#xff1a;Model 代表数据模型&#xff0c;也可以在Model中定义数据修改和操作的业务逻辑。 V&#xff1a;View 代表视图UI&#xff0c;它负责将数据模型转化成UI 展现出来。…

OpenResty+OpenWAF的WEB防护实战

OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台&#xff0c;其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。本文介绍通过OpenRestyOpenWAF来搭建软WAF的应用&#xff…

【Linux】多线程协同

目录 生产消费模型 BlockQueue阻塞队列模型 BlockQueue.hp Task.hpp mypc.cc RingQueue循环队列模型 POSIX信号量 RingQueue.hpp Task.hpp main.cc 生产消费模型 生产者与生产者之间关系&#xff1a;互斥&#xff08;竞争&#xff09; 消费者与消费者之间关系&…

偏向锁到轻量级锁的升级过程(耗资源)

目录 上原理&#xff1a; 细说原理&#xff1a; 什么是锁记录呢&#xff1f; 什么是Mark Word 呢&#xff1f; 上图解&#xff1a; 上原理&#xff1a; 偏向锁使⽤了⼀种等到竞争出现才释放锁的机制&#xff0c;所以当其他线程尝试竞争偏向锁时&#xff0c; 持有偏向锁的…

nssctf web 入门(3)

目录 [NISACTF 2022]easyssrf [SWPUCTF 2021 新生赛]ez_unserialize [SWPUCTF 2021 新生赛]no_wakeup 这里通过nssctf的题单web安全入门来写&#xff0c;会按照题单详细解释每题。题单在NSSCTF中。 想入门ctfweb的可以看这个系列&#xff0c;之后会一直出这个题单的解析&…