【Keil5】

编程入门 行业动态 更新时间:2024-10-26 18:15:29

【Keil5】

【Keil5】

Keil5

  • 1 Keil 下载安装
  • 2 GCC编译4个阶段
    • 2.1 预处理->编译->汇编->链接
    • 2.2 GNU工具链开发流程图
    • 2.3 armcc/armasm(编译C和汇编)
    • 2.4 armlink (链接)
    • 2.5 armar (打包)
    • 2.6 fromelf (格式转换器)
    • 2.7 地址段
  • 3 文件介绍
    • 3.1 output目录下的文件后缀
      • 3.1.1 uvprojx
      • 3.1.2 uvoptx
      • 3.1.3 uvguix
    • 3.2 源文件
    • 3.3 lib库文件
    • 3.4 dep、 d依赖文件
    • 3.5 .crf交叉引用文件
    • 3.6 o、 axf及elf文件
    • 3.7 hex文件及bin文件
  • 4 SCT分散文件
    • 4.1. 适用范围
    • 4.2. 分散加载文件概述
    • 4.3. 一个普通的分散加载配置
    • 4.4. 多块RAM的分散加载文件配置
    • 4.5. 多块Flash的分散加载文件配置
    • 4.6. 基础知识
  • 5 _main()函数组成,
  • 6 Keil5编辑器
    • 6.1 pack包
    • 6.2 使用微库
    • 6.3 RTX-Kernel
    • 6.4. PC-Lint 工具
    • 6.5. 定制化工具菜单
    • 6.6. SVCS 版本控制工具
    • 6.7 .map文件
    • 6.8. Debug中的USB/TCP/IP下载
    • 6.9. assert(断言)
  • 7 魔术棒
    • 7.1 Device(设备)
    • 7.2 Target(目标)
    • 7.3 output(输出)
    • 7.4 Listing
    • 7.5 User(用户)
    • 7.6 C/C++(代码)
    • 7.7 Asm(汇编)
    • 7.8 Linker(链接器)
    • 7.9 Debug(调试)
    • 7.10 Utilitier(应用)
  • 8 理论介绍
  • 9 keil5-Debug和发布版本配置
    • 9.1 .sct文件 sct文件配置
    • 9.2 .发布版本不需要在 C/C++,Asm,Linker,中添加 CMDDEBUG 宏定义。
    • 9.3 .Debug版本需要在Linker添加 --pd="-DCMDDEBUG" 才能 生效sct配置文件
    • 9.4 .app、和debug、版本必须分开工程文件
    • 9.5 .Debug版本。才有跳转函数, boot 跳转到app 代码中boot 会重新配置向量表偏移地址并将app地址压栈,这样就完美调到App中
    • 9.6 keil5-将App应用程序烧录到指定Flash地址
  • 10 keil5编译报错
    • 10.1 No Space in execution regions with .ANY selector matching startup ...
    • 10.2 Execution regon ICOD with Execuyion range ...
    • 10.3 报错三:调试烧录出现问题
    • 10.4 报错四:Error: L6218E: Undefined symbol STLED316_Init
    • 10.5 下载 Error Flash Download failed Cortex-M4
  • 11 芯片相关数据查找
    • 11.1 芯片Flash 起始地址
    • 11.2. FLASH_SECTOR_SIZE 大小
    • 11.3. FLASH_EraseSector(u32 startAddr,u32 codeLength)
    • 11.4 STM32F205RBT6Flash大小
    • 11.5 IROM1 地址 就是Flash 地址
    • 11.6 IRAM1 IRAM2地址;
    • 11.7 IRAM起始地址为啥为0x20000000
    • 11.8. RAM分段使用
    • 11.9. Stack_Size Heap_Size
    • 11.10. 芯片手册分:数据手册(硬件比较关注的),参考手册(软件比较关注的)。
    • 11.11. sct中(.bootcode) (.RamCode) 意思解析
    • 11.12. Use memory layout from target dialog 选项配合使用的,
    • 11.13. 烧录程序
    • 11.14. 编译报错 .\Objects\stm32h7_tms.axf: Error: L6218E: Undefined symbol __heap_base (referred from alloc.o).
    • 11.15. Keil5 load 出错
  • 12 芯片加密

1 Keil 下载安装

keil5 MDK下载地址:/
pack下载地址:/
st中文社区网:

学习方法:
参考官网的armlink手册。

2 GCC编译4个阶段

2.1 预处理->编译->汇编->链接

2.2 GNU工具链开发流程图



编译:编译器是armcc和armasm ,每个c/c++和汇编源文件编译成对应的以“.o”为后缀名的对象文件(Object Code,也称目标文件),
链接: 链接器armlink把各个.o文件及库文件链接成一个映像文件“.axf”或“.elf”;
格式转换:一般来说Windows或Linux系统使用链接器直接生成可执行映像文件elf后,内核根据该文件的信息加载后,就可以运行程序了,但在单片机平台上,需要把该文件的内容加载到芯片上,所以还需要对链接器生成的elf映像文件利用格式转换器fromelf转换成“.bin”或“.hex”文件,交给下载器下载到芯片的FLASH或ROM中。

2.3 armcc/armasm(编译C和汇编)

它调用了-c、 -cpu –D –g –O1等编译选项, 查看MDK的帮助手册,

2.4 armlink (链接)

armlink是链接器,它把各个O文件链接组合在一起生成ELF格式的AXF文件, AXF文件是可执行的,下载器把该文件中的指令代码下载到芯片后,该芯片就能运行程序了;利用armlink还可以控制程序存储到指定的ROM或RAM地址。
链接器默认是根据芯片类型的存储器分布来生成程序的,该存储器分布被记录在工程里的sct后缀的文件中,有特殊需要的话可自行编辑该文件,改变链接器的链接方式。

2.5 armar (打包)

armar工具用于把工程打包成库文件

2.6 fromelf (格式转换器)

fromelf可根据axf文件生成hex、bin文件, hex和bin文件是大多数下载器支持的下载文件格式。
例如如果想利用fromelf生成bin文件,可以在MDK的“Option for Target->User”页中添加调用fromelf的指令:如下

User配置页面中,提供了三种类型的用户指令输入框分别是
编译前(Before Compile c/c++ file)、
构建前(Before Build/Rebuild)
构建后(After Build/Rebuild)执行。
这些指令并没有限制必须是arm的编译工具链,例如如果您自己编写了python脚本,也可以在这里输入用户指令执行该脚本。





2.7 地址段

Code: 即代码域,它指的是编译器生成的机器指令,这些内容被存储到ROM区。
RO-data: Read Only data,即只读数据域它指程序中用到的只读数据,这些数据被存储在ROM区,因而程序不能修改其内容。例如C语言中const关键字定义的变量就是典型的RO-data。
RW-data:Read Write data,即可读写数据域例如C语言中使用定义的全局变量,且定义时赋予“非0值”给该变量进行初始化。
ZI-data:Zero Initialie data,即0初始化数据,RW-data的区别是程序刚运行时这些数据初始值全都为0,例如C语言中使用定义的全局变量,且定义时赋予“0值
ZI-data的栈空间(Stack)及堆空间(Heap): 在C语言中,函数内部定义的局部变量属于栈空间,进入函数的时候从向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。而使用malloc动态分配的变量属于堆空间。在程序中的栈空间和堆空间都是属于ZI-data区域的,这些空间都会被初始值化为0值。编译器给出的ZI-data占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用malloc动态申请堆空间,编译器会优化,不把堆空间计算在内)。

3 文件介绍

3.1 output目录下的文件后缀

3.1.1 uvprojx

uvprojx 文件是 工程文件,它记录了整个工程的结构,如芯片类型、工程包含了哪些源文件等内容:

3.1.2 uvoptx

uvoptx 文件记录了工程的配置选项,如下载器的类型、变量跟踪配置、断点位置以及当前已打开的文件等等:

3.1.3 uvguix

uvguix 文件记录了MDK软件的GUI布局,如代码编辑区窗口的大小、编译输出提示窗口的位置等等。

uvprojx、 uvoptx及 uvguix都是使用XML格式记录的文件,若使用记事本打开可以看到XML代码。
uvprojx文件是最重要的 而uvoptx及uvguix文件并不是必须的,可以删除.重新使用MDK打开uvprojx工程文件后,会以默认参数重新创建uvoptx及uvguix文件。 (所以当使用Git/SVN等代码管理的时候,往往只保留uvprojx文件)

3.2 源文件

MDK支持c、 cpp、 h、 s、 inc类型的源代码文件,
其中c、 cpp分别是c/c++语言的源代码, h是它们的头文件, s是汇编文件, inc是汇编文件的头文件,可使用“$include”语法包含。编译器根据工程中的源文件最终生成机器码。

3.3 lib库文件

在某些场合下可能不希望提供给第三方一个可用的代码库,但不希望对方看到源码,这个时候我们就可以把工程生成lib文件(Library file)提供给对方,可把它像C文件一样添加到其它工程中,并在该工程调用lib提供的函数接口,除了不能看到*.lib文件的源码,在应用方面它跟C源文件没有区别。

3.4 dep、 d依赖文件

*.dep是整个工程的依赖,它以工程名命名,
*.d是单个源文件的依赖,它们以对应的源文件名命名 这些记录使用文本格式存储

3.5 .crf交叉引用文件

*.crf是交叉引用文件(Cross-Reference file),它主要包含了浏览信息(browse information),即源代码中的宏定义、变量及函数的定义和声明的位置。

3.6 o、 axf及elf文件

*.o、 *.elf、 *.axf、 .bin及.hex文件都存储了编译器根据源代码生成的机器码,根据应用场合的不同,它们又有所区别。
*.o、 *.elf、 *.axf以及前面提到的lib文件都是属于目标文件,它们都是使用ELF格式来存储的,
ELF是Executable and Linking Format的缩写,告诉操作系统如何链接、加载及执行该应用程序。

  • 可重定位的文件(Relocatable File),包含基础代码和数据,但它的代码及数据都没有指定绝对地址,因此它适合于与其他目标文件链接来创建可执行文件或者共享目标文件。
    例如MDK的armcc和armasm生成的*.o文件就是这一类,另外还有Linux的*.o 文件, Windows的 *.obj文件。
  • 可执行文件(Executable File) ,
    它包含适合于执行的程序,它内部组织的代码数据都有固定的地址(或相对于基地址的偏移),系统可根据这些地址信息把程序加载到内存执行。这种文件一般由链接器根据可重定位文件链接而成,
    例如MDK的armlink生成的*.elf及*.axf文件,
  • 共享目标文件(Shared Object File),
    MDK生成的*.lib文件就属于共享目标文件,它可以继续参与链接,加入到可执行文件之中。另外, Linux的.so,如/lib/ glibc-2.5.so, Windows的DLL都属于这一类





3.7 hex文件及bin文件

若编译过程无误,即可把工程生成前面对应的*.axf文件,而在MDK中使用下载器(DAP/JLINK/ULINK等)下载程序或仿真的时候, MDK调用的就是*.axf文件,它解释该文件,然后控制下载器把*.axf中的代码内容下载到STM32芯片对应的存储空间,然后复位后芯片就开始执行代码了。
然而,脱离了MDK或IAR等工具,下载器就无法直接使用*.axf文件下载代码了,它们一般仅支持hex和bin格式的代码数据文件。默认情况下MDK都不会生成hex及bin文件,需要配置工程选项或使用fromelf命令。
hex是Intel公司制定的一种使用ASCII文本记录机器码或常量数据的文件格式,这种文件常常用来记录将要存储到ROM中的数据,绝大多数下载器支持该格式
hex格式:

生成hex文件
生成bin文件
fromelf需要根据工程的*.axf文件输入来转换得到bin文件,
“fromelf --bin --output …\Output\多彩流水灯.bin …\Output\多彩流水灯.axf”

4 SCT分散文件


4.1. 适用范围

有时候用户希望将不同的代码放在不同存储空间,也就是通过编译器生成的映像文件需要包含多个域,
每个域在加载和运行时可以有不同的地址。要生成这样的映像文件,必须通过某种方式告知编译器相关的地
址映射关系。
在Keil/ADS/IAR等编译工具中,可以通过分散加载机制实现。分散加载通过配置文件实现,这样的文件
称为分散加载文件。本文重点介绍Keil的分散加载文件配置。

4.2. 分散加载文件概述

分散加载(scatter)文件是一个文本文件,它可以用来描述连接器生成映像文件时需要的信息。通过编写一个分散加载文件
来指定ARM连接器在生成映像文件时如何分配Code、RO-Data、RW-Data、ZI-Data等数据的存放地址。如果不用分散加载文件指定,
那么ARM连接器会按照默认的方式来生成映像文件。一般情况下我们不需要使用分散加载文件。但对一些特殊的情况例如需要将
不同的程序代码存储到不同的地址区域时需要修改分散加载文件。

1、如何使用分散加载
连接器的命令行选项提供了一些对数据和代码位置的控制,但要对位置进行全面控制,则需要使用此命令行中的输入内容更
详细的指令。需要或最好使用分散加载描述的情况包括:
2、复杂内存映射
如果必须将代码和数据放在多个不同内存区域中,则需要使用详细指令指定将那些数据放在那个内存空间中。
3、不同类型的内存
驱动系统都包含多种不同的物理内存设备,如闪存、ROM、SDRAM和快速SRAM。分散加载描述可以将代码和数据与最适合的内存类型
相匹配。例如,可以将重点代码放在快速SRAM中以缩短中断等待时间,而将不经常使用的配置信息放在较慢的闪存中。
4、内存映射的I/O
分散加载描述可以将数据节准确放在内存映射中的某个地址,以便能够访问内存映射的外围设备。
5、位于固定位置的函数
可以将函数放在内存中的固定位置,即使已修改并重新编译周围的应用程序。
6、使用符号标识堆和堆栈
链接应用程序时,可以为堆和堆栈位置定义一些符合。

4.3. 一个普通的分散加载配置

假设,一个Cortex-M3内核的LPC17xx微控制器有Flash、RAM的资源如下:
Flash基址:0x00000000,大小:256KByte
RAM基址:0x10000000,大小:32Kbyte
那么一个分散加载文件应该怎样描述呢?可参考如下:

LR_IROM1 0x00000000     0x00040000{       ;定义一个加载时域,域基址:0x0000000,域大小0x00040000,对应实际Flash的大小ER_IROM 0x0000000   0x00040000{      ;定义一个运行时域,第一个运行时域必须和加载时域起始地址相同,否则库不能加载到该时域的错误,;其域大小一般也和加载时域大小相同*.o(RESET,+First)         ;将RESET段最先加载到本域的起始地址外,即RESET的起始地址为0,RESET存储的是向量表.ANY(+RO)            ;加载所有匹配目标文件的只读属性数据,包含:Code、RW-Code、RO-Data。}
}

4.4. 多块RAM的分散加载文件配置

还是上述的MCU,假设其增加了另一块RAM,其资源如下:
1、Flash基址:0x00000000,大小:256KByte
2、RAM1基址:0x10000000,大小:32Kbyte
3、RAM2基址:0x2007C000,大小:32Kbyte
如果我想将这两块连续的RAM都使用起来(可使用64Kb RAM)?分散加载文件应该怎样描述?LR_IROM1 0x00000000     0x00040000{        ;定义一个加载时域,域基址:0x0000000,域大小0x00040000,对应实际Flash的大小ER_IROM 0x0000000   0x00040000{        ;定义一个运行时域,第一个运行时域必须和加载时域起始地址相同,否则库不能加载到该时域的错误,;其域大小一般也和加载时域大小相同*.o(RESET,+First)                  ;将RESET段最先加载到本域的起始地址外,即RESET的起始地址为0,RESET存储的是向量表.ANY(+RO)                          ;加载所有匹配目标文件的只读属性数据,包含:Code、RW-Code、RO-Data。}RW_IRAM1 0x10000000        0x00008000{  ;定义RAM1的运行时域,使用.ANY进行随意分配变量,这里不能使用*号代替,*表示匹配所有的目标文件,.ANY(+RW +ZI)                       ;这样变量就无法分配到第二块RAM空间了}RW_IRAM2 0x2007C000        0x00008000{   ;定义RAM2的运行时域,使用.ANY进行随意分配变量,这里不能使用*号代替,*表示匹配所有的目标文件,.ANY(+RW +ZI)                        ;这样变量就无法分配到第二块RAM空间了};如果还有另外多的RAM块,在这里增加新的运行时域即可,格式和RAM2的定义相同}如上面所示,确实可以将两块RAM都使用起来,即有64KB的RAM可以使用,但其并不能完全等价于一个64KB的RAM,实际应用可能会碰到如下问题。如我在main.c文件中声明了1个40KB的数值,unsigned char GucTest0[40*1024];    /*定义一个40KB的数组*///unsigned char GucTest2[20*1024];    /*定义一个20KB的数组*///unsigned char GucTest3[20*1024];    /*定义一个20KB的数组*/如上所示的程序在编译的时候会出现错误,并提示没有足够的空间,为什么?因为数组是一个整体,其内部元素的地址是连续的,不能分割的,但是
在两个不连续的32KB空间中,是没办法配出一个连续的40KB的地址空间,所以编译会提示空间不足,分配40KB数组失败。还是上述程序,申请两个20KB的数组,编译结果会如何?编译结果还是会提示空间不足,这是为什么?这里出错的原因其实和上面的原因是相同的,关于大数组分配的解决方法,有两种,分别是:第一种:将数组分开在不同的C文件中定义,避免在同一个C文件定义的数据大小总量超过其中最大的分区。第二种:将一个C数组,使用段定义,使其从该C文件中独立除了,这样编译器就不会将它们作为一个整体来划分空间了,示例如下:#pragma arm section zidata = "SRAM"            //在C文件中定义新的段unsigned char GucTest1[20*1024];            //定义一个20KB的数组#pragma arm section                         //恢复原有的段unsigned char GucTest2[20*1024];            //定义第二个20KB数组,这20KB数组不会和GucTest1作为一个整体来划分空间

4.5. 多块Flash的分散加载文件配置

多块Flash的分散加载文件配置
再一下上述的MCU,假其增加多了一块Flash,不是RAM,其资源如下:
1、Flash1基址:0x00000000,大小:256KByte;
2、Flash2基址:0x20000000,大小:2048KByte;
3、RAM基址:0x10000000,大小:32Kbyte;
注意这里多增加的一块的不是RAM,而是Flash,其情况会如何呢?假设其相同,LR_IROM1 0x00000000     0x00040000{        ;定义一个加载时域,域基址:0x0000000,域大小0x00040000,对应实际Flash的大小
ER_IROM 0x0000000   0x00040000{        ;定义Flash1运行时域
*.o(RESET,+First)                    ;先加载向量表
.ANY(+RO)                            ;随意分配只读数据
}
ER_IROM1 0x2000000   0x00200000{        ;定义Flash2运行时域.ANY(+RO)                            ;随意分配只读数据
}
RW_IRAM2 0x10000000        0x00008000{        ;定义RAM1的运行时域,使用.ANY进行随意分配变量,这里不能使用*号代替,*表示匹配所有的目标文件,.ANY(+RW +ZI)                        ;这样变量就无法分配到第二块RAM空间了}
}

4.6. 基础知识

要了解分散加载文件前首先需要对以下各个概念进行了解,
Code:为程序代码部分;
RO-Data:表示程序定义的常量及const型数据;
RW-Data:表示已经初始化的静态变量,变量有初值;
ZI-Data:表示未初始化的静态变量,变量无初值;

{    #define         DATA        (0x10000000)        /*RO-Data        */char const      GcChar = 5;                     /*RO-Data        */char            GcStr[] = "string.";            /*RW-Data        */char            GcZero;                          /*ZI-Data       */    
}
Keil工程编译完后,查看其的map文件,可得结果如程序清单2.2类似
{Total Ro     Size(Code+RO-Data)                768(    0.75kB)Total RW     Size(RW-Data+ZI-Data)           2060(    2.01kB)Total ROM     Size(Code+RO-Data+RW-Data)        780(    0x76kB)      
}
由程序清单2.2所示的map文件可看出:
ROM (Flash) Size = Code+RO-Data+RW-Data = 0.76KB;
RAM Size = RW-Data+ZI-Data = 2.01KB为什么上述的RW-Data既占用Flash又占用RAM,变量不是放在RAM中的么,为什么会占用Flash?
因为RW数据不能像ZI那样"无中生有"的,ZI段数据只要求其所在的区域全部初始化为0,所以只需要程序根据编译器
给出的ZI基址及大小来将相应的RAM清0。但RW段数据却不这样做,所以编译器为了完成所有RW段数据赋值,其
先将RW段的所有初值,先保存到flash中,程序执行时,再flash中的数据搬运到RAM中,所以RW段既占用flash
用占用RAM,且占用的空间大小是相等的。_main()函数主要由以下两个部分功能组成,
第一部分:  _main():完成代码和数据的拷贝,并把ZI数据区清零。代码拷贝可将代码拷贝到另一个映射空间并执行,如
将代码拷贝到RAM执行;数据拷贝完成RW段数据赋值;数据区清零完成ZI段数据赋值。以上的代码和分散加载文件密切相关。
第二部分:  _rt_entry():进行STACK和HEAP等的初始化。最后_rt_entry跳进main()函数入口。_rt_entry又将控制权交还给调试器。 

5 _main()函数组成,

第一部分:
_main():完成代码和数据的拷贝,并把ZI数据区清零。代码拷贝可将代码拷贝到另一个映射空间并执行,如
将代码拷贝到RAM执行;数据拷贝完成RW段数据赋值;数据区清零完成ZI段数据赋值。以上的代码和分散加载文件密切相关。
第二部分:
_rt_entry():进行STACK和HEAP等的初始化。最后_rt_entry跳进main()函数入口。_rt_entry又将控制权交还给调试器。

6 Keil5编辑器

6.1 pack包

/

6.2 使用微库

Targert-> Use MicroLIB 使用微库
使用微库能够减少代码容量,但有时也会造成编译错误(我反正没遇到)。

6.3 RTX-Kernel

6.4. PC-Lint 工具

Tools->Set-up PC-Lint… 配置PC-Lint 工具
C/C++静态代码检查工具,其中Logiscope RuleChecker和PC-Lint 是应用比较广泛的两个工具。
PC-Lint 的检查分很多种类,有强类型检查、变量值跟踪、语义信息、赋值顺序检查、弱定义检查、格式检查、缩进检查、const 变量检查和volatile变量检查等等。对每一种检查类型,PC-Lint 都有很多详细的选项,用以控制PC-Lint的检查效果。
PC-Lint:这个工具好像很牛逼的样子,据说它声称可以通过不运行代码找出80%的BUG
PC-Lint 是GIMPEL SOFTWARE公司开发的C/C++软件代码静态分析工具,它的全称是PC-Lint/FlexeLint for C/C++,
PC-Lint 能够在Windows、MS-DOS和OS/2平台上使用,以二进制可执行文件的形式发布
PC-Lint 检查作为代码走查的第一道工序。
PC-Lint不仅能够对程序进行全局分析,识别没有被适当检验的数组下标,报告未被初始化的变量,警告使用空指针以及冗余的代码,还能够有效地帮你提出许多程序在空间利用、运行效率上的改进点。
PC-Lint 能够检查出很多语法错误和语法上正确的逻辑错误,
PC-Lint 为大部分错误消息都分配了一个错误号
PC-Lint/FelexLint 提供了和许多编译器类似的告警级别设置选项-wLevel,
PC-Lint/FelexLint还提供了用于处理函数库的头文件的告警级别设置选项-wlib(Level)
PC-Lint 集成到很多开发环境或常用的代码编辑软件中,比如集成到Source Insight/SLICKEDIT/MS VC6.0/KEIL C…等
PC-Lint 8.0对VC++6和VC++7.0的支持是最完善的 (Microsoft Visual C++)
PC-Lint 与source insight 集成
PC-Lint 与UltraEdit 集成
以Microsoft Visual C++ 6的开发环境为例

6.5. 定制化工具菜单

Tools->Customize Tools Menu… 定制化工具菜单

6.6. SVCS 版本控制工具

SVCS 版本控制工具。 我只会使用git工具,可以是gitlab,或者github。

6.7 .map文件

从map文件中,我们可以找到很多有用的信息,比如数据放在ROM中那个位置,函数属于哪个section。

6.8. Debug中的USB/TCP/IP下载

6.9. assert(断言)

在使用C语言编写工程代码时,我们总会对某种假设条件进行检查,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。
常常会出现一些隐藏得很深的BUG,或者是一些概率性发生的BUG,通常这些BUG在我们调试的过程中不会出现很明显的问题,但是如果我们将其发布,在用户的各种运行环境下,这些程序可能就会露出马脚了。那么,如何让我们的程序更明显的暴露出问题呢?这种情况下,我们一般都会使用 assert 断言函数,这是C语言标准库提供的一个函数,也就是说,它的使用与操作系统平台,调试器种类无关。我们只要学会了它的使用,便可一次使用,处处开花。

断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新起用断言。它可以快速发现并定位软件问题,同时对系统错误进行自动报警。
断言可以对在系统中隐藏很深,用其它手段极难发现的问题可以用断言来进行定位,从而缩短软件问题定位时间,提高系统的可测性。实际应用时,可根据具体情况灵活地设计断言。
#include <assert.h>
void assert( int expression );
1.assertEquals(expected,actual) 和 assertNotEquals(expected,actual);
比较实际值与预期值是否一致。如果一致,程序继续运行,否则抛出异常,会打印报错信息。常用断言方法,便于调试。
2.assertTrue(message,condition) 和 assertFalse(message,condition)
如果条件的真假与预期相同,程序继续运行,否则抛出异常,不会打印报错信息。
3.assertNull(message,object) 和 assertNotNull(message,object)
判断一个对象是否为空,如果结果与预期相同,程序继续运行,否则抛出异常。
4.assertSame(expected,actual) 和 assertNotSame(expected,actual)
判断预期的值和实际的值是否为同一个参数(即判断是否为相同的引用),如果结果与预期相同,程序继续运行,否则抛出异常。
assertSame(expected,actual) 和 assertEquals(expected,actual)的区别;
assertSame(A,B) ————————————> A==B
assertEquals(A,B)————————————>A.equals(B)
5.fail(message)
“fail”断言能使测试立即失败,这种断言通常用于标记某个不应该被到达的分支。例如测试中某个代码块要try catch,则在catch代码中加入fail(message)方法,否则代码直接进入catch块,无法判断测试结果。

#include <stdio.h>
#include <assert.h>
int main( void )
{int i;i=1;assert(i++);printf("%d\n",i);return 0;
}


看看运行结果,因为我们给定的i初始值为1,所以使用assert(i++);语句的时候不会出现错误,进而执行了i++,所以其后的打印语句输出值为2。如果我们把i的初始值改为0,那么就回出现如下错误。
Assertion failed: i++, file E:\fdsa\assert2.cpp, line 8
Press any key to continue
是不是发现根据提示很快就能定位出错点呢?

//…
assert( c1 /条件1/ && c2 /条件2/ );
//…
如果我们的程序在这里断言失败了,我们如何知道是 c1 断言失败还是 c2 断言失败呢,答案是:没有办法
//…
assert(c1 /条件1/);
assert(c2 /条件2/);
//…
这样,一旦出现问题,我们就可以通过行号知道是哪个条件断言失败了。

7 魔术棒

7.1 Device(设备)

7.2 Target(目标)

ROM与RAM配置名称介绍:
RAM 就是内存,ROM就是flash。
RAM是随机存取存储器(random access memory),是计算机内部存储器中的一种,也是其中最重要的,计算机和手机中一般把其叫做(运行)内存,它的速度要比硬盘快得多,所以用运行程序在RAM中
ROM是只读存储器(Read-Only Memory),也是计算机内部存储器中的一种,而硬盘是外部存储器 后来随着技术的发展,在ROM的基础上出现了新的半导体存储介质EPROM和EEPROM,这两种可擦写,这就不符合ROM的命名,但是由于是在ROM的技术上衍变出来的,所以延用了一部分原来的叫法,此时非易失的半导体存储介质开始得以广泛应用,被大量用于电脑主板的bios和嵌入式存储,
解释:
ROM 配置是用来自动生成scatter散列文件,如下图所说:
ROM与RAM配置,它帮助编译器定义下ROM,RAM的范围,使得编译时不会把数据放在范围外。当然你也可以将ROM或者RAM切成两部分使用,这里我是特殊使用所以和平常数据不一样。我把ROM分成了4部分,这里没看出来? 我通过scatter文件代码实现的。当使用手动分配的话,这里设置的IROM1和IROM2就失效了。具体在linker这里配置。
我们控制通过某些手段,将代码指定到ROM的任何位置,那么我们再次推,是不是数据也是一样。是的,我们可以设置精确到一个字符都能指定到ROM的某个位置(当然支持flash program,STM32基本都支持)。比如我实现了一个小功能将ROM指定某个1K位置空出来给用户平时存配置数据,掉电还能存在,代替EEROM。

7.3 output(输出)

若我们想要在AXF文件中加入debug信息的话就勾上Debug Information。
若你想输出lib你就可以点击下面这个create library

7.4 Listing

7.5 User(用户)

7.6 C/C++(代码)

说明了就是指定编译器生成规则。
实际核心是使用命令行,而界面的设置只是在编译执行命令行时添加参数而已

7.7 Asm(汇编)

7.8 Linker(链接器)

链接器,它的设置基本是默认的,所以我们基本不动除非有特殊需要,去掉勾use memory layout from Target Dialog。



只有添加 --pd=“-DCMDDEBUG” 链接选项后执行sct文件就会执行CMDDEBUG 条件编译地址是 0x08000000
会在map文件中查兰到的生成地址为 0x08000000
RESET 0x08000000 Section 192 startup_n32g031.o(RESET)

屏蔽 //–pd=“-DCMDDEBUG” 链接选项后执行sct文件就会执行CMDDEBUG 条件编译地址是 0x08008000
会在map文件中查兰到的生成地址为 0x08008000
RESET 0x08008000 Section 192 startup_n32g031.o(RESET)

7.9 Debug(调试)





用来追踪异常(中断),比如,我可以看到进入了多少次的SysTick,或者调度程序,又或是查看哪个中断有没有触发,触发多少次。例如:在自己写RTOS时,你可以通过这个来查看PendSV(关于调度的一个异常,查看CM3内核可知)有没有被设置。

全片擦除,就是整个ROM都擦掉。
扇区擦除,就是你代码到哪个扇区就擦掉哪个。

问题在于你的下载算法没有设置,设置一个算法即可。

7.10 Utilitier(应用)

8 理论介绍

axf 和 bin、 hex 同样也属于程序文件,差别在于 axf 具有更多的调试信息。
同样一段代码,编译生成的bin文件最小,axf最大。

BIN 文件本身只是数据,没有包含地址信息,所以在下载bin文件时需要选择内存的起始地址和终止地址,即要把bin文件下载到指定的内存空间。通常需要指定程序内存地址的芯片为ARM芯片和DSP芯片。
BIN 文件本身只是数据,因此,你下载 bin 程序文件的时候,必须要设置起始地址
BIN 文件,通过右键属性查看到的文件的大小就是数据的实际大小。而对HEX文件而言,你看到的文件大小并不是实际的数据的大小。
BIN 是 binary 的缩写,直白的翻译即为二进制文件

主要分4段:文件头,编码表,检索表,点阵信息。

文件头结构体
/*针对 height fixed 存储格式*/
typedef struct gui_font_head_height_fixed{ BYTE  magic[4];    //'S'('U'or ’M’), 'F', 'L', X---Unicode(or MBCS) Font Library, X: 表示版本号. 分高低4位。如 0x12表示 Ver 1.2  DWORD Size;        // 文件大小      BYTE  nSection;    // 共分几段数据,主要针对 UNICODE 编码有效。  BYTE  YSize;       // 字体高度               WORD  wCpFlag;     // codepageflag:  bit0~bit13 每个bit分别代表一个CodePage 标志,如果是1,则表示当前CodePage 被选定,否则为非选定。  WORD  nTotalChars; // 总的字符数  BYTE  ScanMode;    // 扫描模式  BYTE  bpp;         // 位深度   
} GUI_FONT_HEAD_HF, *PGUI_FONT_HEAD_HF;

HEX文件是用ASCII来表示数据,
1、hex文件包含地址信息而bin文件只包含数据本身,烧写或下载hex文件时,一般不需要用户指定地址,因为hex文件内部已经包含了地址信息。烧写bin文件时则需要用户指定烧录的地址信息。
2、hex文件是用ASCII码来表示二进制的数值。例如8-BIT的二进制数值0x4E,用ASCII来表示就需要分别表示字符‘4’和字符‘E’,每个字符均需要一个字节,因此hex文件至少需要2倍bin文件的空间。
3、hex可以直接转换为bin文件,但是bin文件要转化为hex文件必须要给定一个基地址。

hex 格式文件由 Intel 制定的一种十六进制标准文件格式,是由编译器转换而成的一种用于下载到处理器里面的ASCII文本文件。
HEX文件本身还包括别的附加信息。
HEX文件包含地址信息。在用ISP方式烧写程序时,我们都有这样的经验:1)选择单片机型号;2)选择串口号;3)设置波特率(或者默认);4)选择下载的文件;5)点击下载按钮下载。
HEX文件包含地址信息 hex 则不可修改(文件中包含地址信息):

其中:
: 代表行开始,固定为冒号:
BB代表Bytes,数据长度
AAAA代表Address,地址
TT代表Type,数据类型(标识)
D···D代表Date,数据
CC代表CheckSum,校验和
说明:
BB数据长度,也就是D···D这个字段的数据长度;
AAAA地址,起始地址、偏移地址,根据数据类型(TT)有关;
TT数据类型(标识):
00:数据标识
01:文件结束标识
02:扩展段地址
04:线性地址
05:线性开始地址
(地址代表高16位地址,也就是要向左移16bit)

CC校验和计算公式:
CheckSum = 0x100 - (Sum & 0xFF)

ELF​​(Executable and Linkable Format,可执行与可链接格式)也算是一种程序文件,这种文件包含信息更多、更复杂。

axf 格式文件是针对ARM编译器的一种格式文件,它是由 ARM 编译器产生。
axf 文件除了包含程序数据(bin)和地址(hex)等数据之外,还包含调试信息。
axf 文件内的调试信息附加在程序文件中,有助于分析和调试。
axf 文件的调试信息作用:
可将源代码包括注释夹在反汇编代码中,这样我们可随时切换到源代码中进行调试。
还可以对程序中的函数调用情况进行跟踪(通过Watch & Call Stack Window查看)。
对变量进行跟踪(利用Watch & Call Stack Window)。

ELF:Executable and Linkable Format,可执行与可链接格式。
elf是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的,也是Linux的主要可执行文件格式。
elf文件和bin、hex、axf文件同样属于可执行文件这一类,但是他们之间差异还是很大,elf文件包含的信息更多,也更复杂。


ELF header:描述整个文件的组织。
Program Header Table: 描述文件中的各种segments,用来告诉系统如何创建进程映像的。
Section:是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出,segments与sections是包含的关系,一个segment包含若干个section。
Section Header Table: 包含了文件各个segction的属性信息。

9 keil5-Debug和发布版本配置

在没有烧录Boot情况下: App Debug 版本烧录在0x08000000地址中的进行调试,
在烧录Boot情况下:有boot 的情况下boot 开机在没有接收到握手信号就会跳到app地址执行,所以这个时候在App在用keil调试也是没有问题的。

9.1 .sct文件 sct文件配置


注意 :#! armcc -E 必须占用第一行,或者编译不通过。

9.2 .发布版本不需要在 C/C++,Asm,Linker,中添加 CMDDEBUG 宏定义。

9.3 .Debug版本需要在Linker添加 --pd=“-DCMDDEBUG” 才能 生效sct配置文件

9.4 .app、和debug、版本必须分开工程文件

9.5 .Debug版本。才有跳转函数, boot 跳转到app 代码中boot 会重新配置向量表偏移地址并将app地址压栈,这样就完美调到App中

9.6 keil5-将App应用程序烧录到指定Flash地址

将它的程序分为Bootloader程序和用户程序。
一个Bootloader程序和一个用户程序,那么这就需要调整分散加载文件 .stm32f2_dock.sct文件,以达成在一个Flash里面同时摆放两个不同程序的目的。

下面修改App示例,boot也是这样修改为boot起始地址和大小:
例如 Flash128KByte 大小为 0x20000 == 0x8020000 - 0x8000000
128*1024 == 131,072‬ == 0x‭20000

假如我们要讲APP烧录在APP_START_ADDR 0x8008000
设置一:

设置二:


设置三:

设置四:向量表偏移地址

因为在boot中已经设置了向量表 SCB->VTOR = APP_START_ADDR; //设置向量表偏移地址。
发布版本要屏蔽修改向量表。所以条件个CMDDEBUG .不在初始化向量表。



设置五: 查看烧录结果


10 keil5编译报错

10.1 No Space in execution regions with .ANY selector matching startup …


空间并进行分析

FLASH 大小为:12596 字节(12234+362),所用的 SRAM 大小为:4632 个字节(1192+3440)。
编译结果里面的几个数据的意义:
Code:表示程序所占用 FLASH 的大小(FLASH)。
RO-data:即 Read Only-data,表示程序定义的常量,如 const 类型(FLASH)。
RW-data:即 Read Write-data,表示已被初始化的全局变量(SRAM)
ZI-data:即 Zero Init-data,表示未被初始化的全局变量(SRAM)
有了这个就可以知道你当前使用的 flash 和 sram 大小了,所以,一定要注意的是程序的大小不是.hex 文件的大小,而是编译后的 Code 和 RO-data 之和。

也可以通过查看map文件
我的路径是…\MDK-ARM\xxx_STM32F103ZET6_HAL\xxx_STM32F103ZET6_HAL.map
打开xxx_STM32F103ZET6_HAL.map文件,拉到底部就可以看到了。

查看FLASH和SRAM的方法
STM32F103ZET6和STM32G030C8T6的FLASH和SRAM分别是多少呢?
从ST选型手册看:
从对应的数据手册来看:
STM32F103ZET6:
STM32G030C8T6:
从keil软件中看FLASH的大小:
MCU:STM32F103ZET6,FLASH:512K,SRAM:64K;
MCU:STM32G030C8T6,FLASH:64K, SRAM:8K;

解决办法
1、换芯片(MCU)
为了节约成本,不换MCU。(开发前最好选合适的MCU,即选型)
2、修改代码
将占用2048个字节的数组注释后,不使用该数组,可以成功编译,结果如下所示。
代码占用 FLASH 大小为:16760字节 ≈ 16.37kB(16416+344),
所用的 SRAM 大小为:7696 个字节 ≈ 7.52kB(24+7672)。
7.52k小于8k,没超过STM32G030C8T6的SRAM的大小。

10.2 Execution regon ICOD with Execuyion range …


10.3 报错三:调试烧录出现问题

安装高版本的pack包。

10.4 报错四:Error: L6218E: Undefined symbol STLED316_Init

.\Objects\GD32F303RGT6_APP.axf: Error: L6218E: Undefined symbol STLED316_Init (referred from main.o).

10.5 下载 Error Flash Download failed Cortex-M4

STM32芯片flash被锁导致Error Flash Download failed Cortex-M4,

解决办法一: 全片擦出芯片如下:

解决办法二: J-Flash全片擦除
执行“Target-Manual Programming-Erase Chip”,即可擦除芯片。该操作是全片擦除,即擦除片内所有Flash和掉电保持的寄存器。

11 芯片相关数据查找

11.1 芯片Flash 起始地址

#define FLASH_BASE_ADDR 0x8000000 // 程序存储区基址
答: 0x8000000 是Flash 起始地址,查芯片手册。

11.2. FLASH_SECTOR_SIZE 大小

FLASH_SECTOR_SIZE 大小
答:芯片的每个扇区大小都一样,查芯片手册

11.3. FLASH_EraseSector(u32 startAddr,u32 codeLength)

attribute((section(“.InterruptCode”))) void FLASH_EraseSector(u32 startAddr,u32 codeLength)
答: 这个是芯片示例中有的,不是自己写的擦除芯片Flash代码。

11.4 STM32F205RBT6Flash大小

STM32F205RBT6Flash大小 R = 64 pins or 66 pins(1) , B = 128 Kbytes of Flash memory
答: 芯片Flash大小为128Kbytes;

11.5 IROM1 地址 就是Flash 地址

答: IROM1 地址 就是Flash 地址 查看芯片手册。STM32F205RBT6

IROM1地址:是Flash地址 128Kbype 128*1024 = ‭131072‬

11.6 IRAM1 IRAM2地址;

这个地址就是类似电脑DDR内存;
IRAM1 地址在芯片硬件手册里面定义


注意点: 有可能修改上图地址大小不一定起效果,还要设置一下sct 文件中的 RW_IRAM1 大小如下:
RW_IRAM1 0x20000000 0x00020000 {
; RW data
.ANY (+RW +ZI)
}

11.7 IRAM起始地址为啥为0x20000000

11.8. RAM分段使用

#define DTCM_ADDR        			 (0x20000000)
#define AXISRAM_ADDR            (0x24000000)
#define SRAM123_ADDR            (0x30000000)
#define SRAM4_ADDR       	     (0x38000000)#define DTCM_SIZE       		   (0x20000)    //128K
#define AXISRAM_SIZE            (0x80000)    //512K
#define SRAM123_SIZE           (0x48000)    //288K
#define SRAM4_SIZE       	      (0x10000)    //64K

11.9. Stack_Size Heap_Size

答:堆栈大小,根据程序大小自己设置大小。待补充:

11.10. 芯片手册分:数据手册(硬件比较关注的),参考手册(软件比较关注的)。

有关性能参数和使用方式的技术资料主要有两类,一类称为数据手册,另一类称为技术参考手册或简称参考手册。
数据手册是有关产品技术特征的基本描述,包含产品的基本配置(如内置Flash和RAM的容量、外设的数量等),
管脚的数量和分配,电气特性,封装信息,和定购代码等。
技术参考手册是有关如何使用该产品的具体信息,包含各个功能模块的内部结构、所有可能的功能描述、
各种工作模式的使用和寄存器配置等详细信息。

11.11. sct中(.bootcode) (.RamCode) 意思解析



11.12. Use memory layout from target dialog 选项配合使用的,


勾选Use memory layout from target dialog 选项配合使用的,就会根据下面的配置生成sct文件

11.13. 烧录程序


解决方法一:.取消校验,这个就纯属掩耳盗铃了,可以运行进去但是不能运行。

解决方法二:使用更高版本的pack包就能解决 (验证过)

之前的版本太低,安装高pack版本后就解决了问题。

11.14. 编译报错 .\Objects\stm32h7_tms.axf: Error: L6218E: Undefined symbol __heap_base (referred from alloc.o).



11.15. Keil5 load 出错

No Algorithm found for: 08000000H - 0800BFFFH
Partial Erase Done (areas with no algorithms skipped!)
No Algorithm found for: 08000000H - 0800BFFFH
Partial Programming Done (areas with no algorithms skipped!)
Contents mismatch at: 08060000H (Flash=4CH Required=DCH) !
Contents mismatch at: 08060001H (Flash=26H Required=E6H) !
Contents mismatch at: 08060002H (Flash=10H Required=F4H) !
Contents mismatch at: 08060003H (Flash=00H Required=D0H) !

解决办法是: 下载地址错误,没有写到0x8000000.
修改前 0x800C000H - 是发布的地址,所以要修改为 0x8000000下载地址

12 芯片加密

更多推荐

【Keil5】

本文发布于:2024-03-08 12:49:17,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1720920.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!