一个Flash编程错误标志的探析

1、问题描述

客户项目中使用的 MCU 型号是 STM32G0B1, 他们反馈在代码中尝试擦除并编程 FLASH时, 发现 FLASH 的状态寄存器显示编程错误(如图 1 所示). 问题是当前代码还没有开始擦除和编程, 怎么就有了编程错误标志了呢 ? 如果不将此错误标志清除, 后续的编程操作无法继续.客户对于每次想要操作 FLASH 之前这个清除动作既感觉多余也感觉别扭, 且还不得不做, 且做了也不知对整个产品的稳定性会有什么样的影响 ?
Figure 1 Flash 编程错误标志
访问客户时, 客户也曾私下里反馈, 经常在网络论坛上获取类似这种问题, 客户怀疑是不是STM32 本身就存在某些未曾公开的问题 ? 其实, STM32 的所有问题都已公开在勘误手册中, 如果客户的问题在勘误手册中没找到, 那么极有可能是自己代码哪里出了问题。

2、问题分析及测试

查看客户的工程, 由于客户的工程相当庞大, 各个模块和任务相互交叉, 一时半刻是很难从如此庞大的工程中找出问题, 更麻烦地是, 客户的电脑是有加密系统的, 导致在工程内查找任何字符和函数都相当痛苦. 好在是, 问题能够稳定地复现。

于是尽量精简客户的代码, 将所有不相关的任务,模块统统移除掉, 并且保持问题能够重现. 并使其能够在 ST 官方的 NUCLEO 板上重现. 这样一来, 就完全可以脱离客户原来的硬件环境进行测试. 由于客户的环境非常不利于查找问题, 效率事倍功半. 于是, 将客户的最小化工程提取出来(与软件泄密无关), 并拿到办公室进行测试. 很快就找到了问题所在。

原来客户的工程中有用到两个串口, 串口 2 和串口 3, 都是使用的 DMA 模式。客户不同的软件人员负责不同的模块, 最终在整合代码时, 串口 2 并没有使用, 所以串口 2 对应的初始化代码是删除掉的, 但由于串口 2 和串口 3 的 DMA 中断是共用一条中断线, 是相同的中断入口, 在中断处理时,串口 2 的 DMA 处理函数和串口 3 的处理函数都会一起处理. 问题就出在串口 2 的 DMA 中断处理并没有移除 。
如 stm32g0xx_it.c 文件 :

/*** @brief This function handles DMA1 Ch4 to Ch7, DMA2 Ch1 to Ch5 and 
DMAMUX1 Overrun Interrupts.*/
void DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQHandler(void)
{/* USER CODE BEGIN 
DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQn 0 *//* USER CODE END 
DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQn 0 */
// HAL_DMA_IRQHandler(&hdma_spi2_tx);
// HAL_DMA_IRQHandler(&hdma_adc1);HAL_DMA_IRQHandler(&hdma_usart2_rx);HAL_DMA_IRQHandler(&hdma_usart2_tx);HAL_DMA_IRQHandler(&hdma_usart3_rx);HAL_DMA_IRQHandler(&hdma_usart3_tx);/* USER CODE BEGIN 
DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQn 1 *//* USER CODE END 
DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQn 1 */
}

如上图,DMA 的通道 4~7 以及 DAM2 的通道 1~5 都是共用一个中断入口的。在这个中断处理函数内, 串口 2 并没有使用到, 但其对应处理代码由于疏忽仍然保留了下来。句柄hdma_usart2_rx, 和 hdma_usart2_tx 内的数据成员很多都是不定内容或为 0. 当代码运行到函数内部, 如下图所示出问题的代码行:
在这里插入图片描述
如上面代码所示, 代码运行到上图 866 行代码 hdma->DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << (hdma->ChannelIndex & 0x1CU));时, 实际上是给错误地址 0x0800 4109 赋值了, 此地址是内部 FLASH 地址, 这样相当于直接写 FLASH, 肯定会出错, 这也是为什么FLASH->SR.PGSERR 置位的原因. 我们都知道, 写内部 FLASH, 必须先擦除, 才可以写入, 而且写也是调用对应的 HAL API 函数, 且还需要先写 key 解锁 FLASH 等操作, 有一套写操作流程. 并不是直接用赋值语句, 这样操作出现问题一点也不奇怪。

当在中断中将串口 2 的 DMA 对应处理函数移除掉后功能就恢复正常, 这也佐证了结论的准确性。

另外, 客户反映, 这个最小化工程, 相同的代码, 使用 IAR 时测试会出错, 但使用 KEIL 时并没有出错. 这个很奇怪. 这就引出的另外一个问题. 相同代码, 不同编译器运行结果不一致的问题。

于是继续找原因, 对比 IAR 和 KEIL 的调试情况, 发现当代码运行到图 2 中 857 行代码 if 语句时其判断结果不相同. IAR 调试环境会进入到 if 语句内容, 从而导致错误的给内部 FLASH 地址赋值, 进行导致问题. 而 KEIL 调试环境并没有进入到 if 语句内部, 因此并没有触发问题. 那么为什么if 语句的判断结果不一样呢?
为了方便并避免不同编译器对长语句的执行顺序的差异, 将这个 if 长语句拆开:

/* Transfer Error Interrupt management **************************************/
// else if (((flag_it & (DMA_FLAG_TE1 << (hdma->ChannelIndex & 0x1CU))) != 0U) && ((source_it & DMA_IT_TE) != 0U))else{/*---ADD------*/uint32_t tmp1, tmp2,tmp3;tmp1 =DMA_FLAG_TE1 << (hdma->ChannelIndex & 0x1CU);tmp2 = flag_it&tmp1;tmp3 =source_it & DMA_IT_TE;if(tmp2 && tmp3!=0)
/*---END-----*/{/* When a DMA transfer error occurs *//* A hardware clear of its EN bits is performed *//* Disable ALL DMA IT */__HAL_DMA_DISABLE_IT(hdma, (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE));/* Clear all flags */#if defined(DMA2)hdma->DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << (hdma->ChannelIndex & 0x1CU));#else__HAL_DMA_CLEAR_FLAG(hdma, (DMA_FLAG_GI1 << (hdma->ChannelIndex & 0x1CU)));#endif /* DMA2 *//* Update error code */hdma->ErrorCode = HAL_DMA_ERROR_TE;/* Change the DMA state */hdma->State = HAL_DMA_STATE_READY;}

如上标注的代码, 用它替换原来的 if 判断语句. 结果发现 tmp1 在 IAR 和 KEIL 两个编译器环境中的值是一样的, 但是 tmp2 的值却不一样, 正是由于 tmp2 值的不一样, 导致 if 语句的最终判断结果不同。
进一步发现, tmp2 的值主要是由于 flag_it 的值在两种编译器环境不一样所致。
在这里插入图片描述
如上 IAR 编译器环境, flag_it 的值为 0x2000 10f8。
在这里插入图片描述
如上 KEIL 编译器环境, flag_it 的值却是 0x2000 14F0。
那么 flag_it 的值又是如何来的呢? 从如下代码:

void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
{#if defined(DMA2)uint32_t flag_it = hdma->DmaBaseAddress->ISR;#elseuint32_t flag_it = DMA1->ISR;#endif /* DMA2 */uint32_t source_it = hdma->Instance->CCR;
}

如上所示, flag_it 的值来自 hdma->DmaBaseAddress->ISR, 原来是 DMA 相关 ISR 寄存器的值, 但实际调试如下:
在这里插入图片描述
如上 IAR 调试环境下, 出错时, hdma->DmaBaseAddress 实际指向的是地址 0, 其成员 ISR为其第一个成员, 实际也就是地址 0 上的数据. 我们都知道, 在默认情况下, MCU 的地址 0 默认是映射到内部 FLASH 的首地址 0x0800 0000 上的, 而此地址一般保存的是栈顶.。也就是说, IAR 编译环境下, 地址 0 指向栈顶地址 0x2000 10f8。
对应地, 在 KEIL 调试环境下:
在这里插入图片描述
如上 KEIL 调试环境, hdma->DmaBaseAddress 同样地实际指向的是地址 0, 而地址 0 的上对应的数据为栈顶地址: 0x2000 14F0。

也就是说, 在不同的 编译器 IAR 和 KEIL 环境下, 地址 0 指向栈顶地址是未必相同的, 进而导致两种编译环境下运行相同的代码结果不一样。

我们知道, 通常栈地址是由编译器来指定的, 在默认情况下, IAR 和 KEIL 都会将栈放在内存的所有静态变量之后来分配. 其具体的分配地址这两个编译器都会默认按自动填充地方式来. 实际分配的地址具有不确定性, 当然, 我们也可以通过链接配置文件(IAR 的.icf 文件, KEIL 的.sct 文件)来将栈地址指定某一固定地址, 但我们通常不会这么做, 且完全没有必要.

3、问题小结

至此,将问题稍作小结。给变量 flag_it 实际赋值栈顶地址, 不同的编译器环境下, 此栈顶地址的不一致导致变量 flag_it 的值不一致, 进而导致 if 语句的判断结果不同, 最终导致 IAR 和 KEIL 这两个编译器环境下运行相同代码而结果不一样的情形。

4、后记

有时会听到某某客户反馈说, 在网络上看到 STM32 某款 MCU 存在某某问题, 然后问是不是 ST 故意隐瞒 ?
不存在故意隐瞒的说法,芯片终究是要经过终端验证的。

正常来讲, 任何芯片存在应用局限是正常的。对于 ST,一方面会正式地将所有已知 bug或应用局限放入到勘误手册中公示, 大家需要注意使用最新版勘误手册;另一方面,对于 ST 量产芯片,因本身缺陷导致的问题的概率非常低。事实上,绝大多数问题都来自我们自身的应用,遇到问题若简单的基于芯片品质来回猜疑非常不利于开发者静下心来查找问题原因。 其实,面对问题时,我们很多人欠缺的并不是多么高深的水平,而是一颗冷静、自信并富有条理的心。

本文档参考ST官方的《【应用笔记】LAT1210+一个Flash编程错误标志的探析》文档。
参考下载地址:https://download.csdn.net/download/u014319604/88971311

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

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

相关文章

vue2 table 页面 + 功能 展示

首页代码 <!-- 首页展示页面 弹框展示 --> <template><div style><el-button type"text" size"small" click"dailys()">测试跳转</el-button><!-- <div class"dingwei"><a href"#…

LeetCode 面试经典150题 134.加油站

题目&#xff1a; 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发&#xff0c;开始时油箱为空。 给定两个整数…

How to install mongodb on redhat 7.7

下载rpm: mongodb-enterprise-server-6.0.3-1.el7.x86_64.rpmmongodb-org-server-6.0.4-1.el7.x86_64.rpmmongodb-mms-6.0.9.100.20230201T2148Z.x86_64.rpm rpm -ivh mongodb-org-server-6.0.4-1.el7.x86_64.rpm rpm -ivh mongodb-mms-6.0.9.100.20230201T2148Z.x86_64.rpm …

【 Redux 】 Redux中间件的理解?常用的中间件有哪些?实现原理?

1. 是什么 中间件(Middleware)是介于应用系统和系统软件之间的一类软件&#xff0c;它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用&#xff0c;能够达到资源共享、功能共享的目的 那么如果需要支持异步操作&#xff0c;或者支持错误处理、日…

【办公类-16-07-07】“2023下学期 大班户外游戏2(有场地和无场地版,每天不同场地)”(python 排班表系列)

作品展示 背景需求&#xff1a; 2024年2月教务组发放的是“每周五天内容相同&#xff0c;两周10天内容相同”的户外游戏安排 【办公类-16-07-05】合并版“2023下学期 大班户外游戏&#xff08;有场地和无场地版&#xff0c;两周一次&#xff09;”&#xff08;python 排班表系…

css预处理器scss的使用如何全局引入

目录 scss 基本功能 1、嵌套 2、变量 $ 3、mixin 和 include 4、extend 5、import scss 在项目中的使用 1、存放 scss 文件 2、引入 variables 和 mixins 2-1、局部引入 2-2、全局引入 3、入口文件中引入其他文件 项目中使用 css 预处理器&#xff0c;可以提高 cs…

Uibot6.0 (RPA财务机器人师资培训第1天 )RPA+AI、RPA基础语法

训练网站&#xff1a;泓江科技 (lessonplan.cn)https://laiye.lessonplan.cn/list/ec0f5080-e1de-11ee-a1d8-3f479df4d981(本博客中会有部分课程ppt截屏,如有侵权请及请及时与小北我取得联系~&#xff09; 紧接着小北之前的几篇博客&#xff0c;友友们我们即将开展新课的学习~…

InsCode是一个集成了在线IDE、在线AI编程、在线算力租赁、在线项目部署以及在线SD 模型使用的综合代码开发平台。

一、 Stable Diffusion 模型在线使用   InsCode是一个集成了在线IDE、在线AI编程、在线算力租赁、在线项目部署以及在线SD 模型使用的综合代码开发平台。 Stable Diffusion是目前最火的AI绘画工具之一&#xff0c;它是一个免费开源的项目。通过Stable Diffusion&#xff0c;…

Python爬取网站数据

Python爬取网站数据 前言 什么是爬虫&#xff1f; 通过编写程序&#xff0c;模拟浏览器上网&#xff0c;然后让其去互联网上抓取数据的过程 爬虫合法还是违法&#xff1f; 在法律上是不被禁止的但是也有违法风险 爬虫带来的风险可以体现在如下2方面 爬虫干扰了被访问网站…

Centos上批量压缩子目录为ZIP文件的代码

需求&#xff1a; 写一个Centos7.9的脚本完成下列需求&#xff1a; 把目录/home/backup/test的第一级子目录依次进行压缩处理&#xff0c;压缩文件的格式为ZIP格式&#xff0c;压缩文件的名字就是子目录的名字&#xff0c;压缩文件存放于目录/home/backup/vgroup000001中。 注意…

Vulnhub - Raven2

希望和各位大佬一起学习&#xff0c;如果文章内容有错请多多指正&#xff0c;谢谢&#xff01; 个人博客链接&#xff1a;CH4SER的个人BLOG – Welcome To Ch4sers Blog Raven2 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/raven-2,269/ 0x01 信息收集 Nmap扫描…

spring-boot-devtools debug SilentExitException

spring-boot-devtools debug SilentExitException&#xff1a;springboot热部署debug模式进入SilentExitException /** Copyright 2012-2019 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use …

软考网工学习笔记(6) 广域通信网

公共交换电话网&#xff08;pstn&#xff09; 在pstn是为了语音通信而建立的网络。从20世纪60你年代开始用于数据传输 电话网有三个部分组成&#xff1a; 本地回路 &#xff0c;干线 和 交换机 。 干线 和 交换机 一般采用数字传输和交换技术 &#xff0c;而 本地回路基本采…

Ubutun部署docker,并使用docker部署springboot项目,关闭软件可继续访问

工具为xftp和xshell。 我这里使用的Ubuntu的版本是20.04的&#xff0c;话不多说&#xff0c;我们来直接上代码。 首先我们最好使用管理员权限进行操作&#xff0c;预防操作时遇到权限问题。 部署docker 登入管理员 不登入管理员也没关系。 su根据提示输入密码&#xff0c;进…

通过Docker安装MySQL数据库

1. 安装Docker 首先&#xff0c;确保你的系统上已经安装了Docker。如果还没有安装&#xff0c;可以访问Docker官网查看安装指南。 对于大多数Linux发行版&#xff0c;可以使用以下命令安装Docker&#xff1a; sudo apt-get update sudo apt-get install docker.io 安装完成…

JVM常见启动参数

目录 内存参数设置 垃圾收集 内存溢出 其他杂项 总结 java虚拟机&#xff08;JVM&#xff09;的启动参数可以用来调整其行为、性能和资源分配。以下是一些常见的JVM启动参数&#xff1a; 内存参数设置 初始化堆内存以满足应用程序需求是最常见的与性能相关的实践之一。 …

Linux实战笔记(三) 文件压缩

大家好&#xff0c;我是半虹&#xff0c;这篇文章来讲 Linux 系统中常用的文件压缩方式 0、序言 在 Linux 系统中&#xff0c;存在许多打包或压缩文件的工具 这篇文章会对一些常用的工具进行分类整理和介绍 如果只是需要知道怎么对不同格式的文件做解压缩&#xff0c;可以直…

Linux中的流刷新和定位

一、流的刷新 int fflush(FILE *fp); 1.成功时返回0&#xff1b;出错时返回EOF 2.将流缓冲区中的数据写入实际的文件 3.Linux下只能刷新输出缓冲区,输入缓冲区丢弃 4.如果输出到屏幕使用fflush(stdout) 二、流的定位 long ftell(FILE *stream);long fseek(FILE *stre…

《底层逻辑》--透过表象看系统

上一个视频&#xff0c;讲了微软公司通过改变与供应商的“连接关系”从而提高了工作餐的质量&#xff1b; 今天就讲讲拥有这种洞察力背后的理论依据&#xff0c;即“透过表象看系统”&#xff1b; 系统 要素 连接关系&#xff1b;再细化一下&#xff0c;构成…

如何在MATLAB中处理图像和视频?

在MATLAB中处理图像和视频是一项复杂而强大的任务&#xff0c;涉及多个步骤和工具。MATLAB提供了丰富的函数和工具箱&#xff0c;使用户能够轻松地加载、处理、分析和显示图像和视频数据。 一、图像处理基础 加载图像&#xff1a;在MATLAB中&#xff0c;可以使用imread函数加…