一、Keil MDK ARM和Keil C51安装
1、软件的下载
Keil C51与ARM共存(含JLINK驱动),C51与ARM编译软件,二者可共存,使用方便;可支持到2032年;含有JLINK驱动。
2、Keil MDK ARM安装
1)以5.12版说明
图1.1.1
2)注册
图1.2.1
注:Win7/64位Keil必须用管理员运行
图1.2.3
3)Help >> About uVision中查看自己安装的工具链
图1.2.4
4)如何让 keil MDK v5 支持 ARM7/9 设备
For maintaining existing MDK Version 4 projects, or using devices that are not yet supported by a Software Pack, please install Legacy Support for ARM 7/9 or Cortex-M. Refer to the Device Database for Legacy Support device coverage. 原来,keil-5 的安装包突然变那么小是因为裁掉了很多老版的设备,如一些 Cortex-M 系列的和 ARM7、9系列的。 MDK v4 Legacy Support
图1.2.5
3、Keil C51安装 1)以c51v952说明
图2.2.1
2)注册
图2.2.2
余下的操作同上。
3)删除原来的C51,用此C51
图2.2.3
4)STC单片机的头文件导入
图2.2.4
5)查看自己安装的工具链 在Help >> About uVision中,如下图。
图2.2.5
二、Keil5.12警告的处理
1、一般介绍
打开keil的帮助文档,help->uVision help ,本处以搜索#550-D为例说明:
图2.1.1
打开倒数第二项,suppress这行,由文档可知,这个语句的作用是禁止某一类warning; 若禁止“已设置但未用”警告, 在相关的文件里添加代码:#pragma diag_suppress 550; 如有很多文件,打开“options for target”,在C/C++选项中,可以看到有个Misc Controls选项,如图2.2.2处理。
2、文件的末尾增加空白行警告
图2.2.1
打开工程配置菜单,点击C/C++选项卡,在Misc Controls里面输入:--diag_suppress=1
图2.2.2
图2.2.2的2处绿圆中的“1”对应图2.2.1编译处的“#1-D”的“1”。 3、出现“invalid multibyte character sequence” 1)某个文件的代码中加入#pragma diag_suppress 1,550,870 2)或在“Misc Controls”中输入:--diag_suppress=1,550,870,......
三、Keil5.12错误的处理(STM32使用J-Link仿真相关汇总)
3.1.0 软件版本
1、去除“STM32F10X_MD”
编译后报错如下:
添加图片注释,不超过 140 字(可选)
原来的设置如下,后来突然就没有此选项(原因不明)。
图3.1.1
这样就只能安装知识包,将下图中的STM32F10X_MD去掉即可解决。
图3.1.2
在编译环境中预先定义USE_STDPERIPH_DRIVER的意义: 打开stm32f10x.h头文件,可以找到如下语句
图3.1.3
也就是说只有预先定义了USE_STDPERIPH_DRIVER后,才会包含stm32f10x_conf.h头文件。在stm32f10x_conf.h中,包含了各个驱动脚本的头文件。
图3.1.4
这也是为什么有些头文件并没有手动添加却自动包含了进来的原因。
2、文件名中有C的关键字,报“Malformed via file”
报错startup_stm32f10xx.s: error: A3906U: Malformed via file'.\debug\startup_stm32f10xx._ia'. you might be using special character like '#" for directory name under which you put your project.
就是在放工程的目录名不能使用象“#”那样的特殊符号。
The easy solution is to make sure, the directory name under which you have your project files should not have any special characters. e.g. your project path :C:\Users\acer\Desktop\myProject#2\App Code\Application\s310\arm Here the directory "myProject#2" is invalid path for keil, if you remove the special character from directory name then it will compile without any error.
或将工程放置在全英文路径下,最简单方法,可以直接将文件放在桌面
3、__use_no_semihosting was requested, but _ttywrch was referenced
如果加载了库文件(该库包含有打印输出到串口、memcpy()等函数),则要点选“Use MicroLIB”。
图3.3.1
图3.3.2
MicroLIB是缺省 C 库的备选库。 它用于必须在极少量内存环境下运行的深层嵌入式应用程序。 这些应用程序不在操作系统中运行。MicroLIB不会尝试成为符合标准的 ISO C 库。 MicroLIB进行了高度优化以使代码变得很小。 它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。某些库函数的运行速度也比较慢,例如,memcpy()。
4、应用程序发生异常未知的软件异常(0xc0000409),位置为0x0a36a6f7
应用程序发生异常未知的软件异常(0xc0000409),位置为0x0a36a6f7 原因:当前使用J-LINK进行仿真,但驱动使用了ST-Link的。
图3.4.1
图3.4.2
5、出现identifier "bool" is undefined
编译IEC104规约时出现“..\..\HARDWARE\INC\IEC104.h(159): error: #20: identifier "bool" is undefined” 在stm32f103.h中添加 typedef enum {FALSE = 0, TRUE = !FALSE} bool; 就没有错误了。
图3.5.1
6、出现“No space in execution regions with .ANY selector match Section”
图3.6.1
1)分析
这个出现的原因是因为芯片RAM空间不足,无法执行程序。通常RAM的空间会比较小,ROM空间相对较大。
2)解决办法
(1)扩大RAM,更换芯片 比如这里将STM32F103VCT6(0xC000)更改为STM32F103VDT6或STM32F103VET6(0x10000)。
图3.6.2
(2)减少需要存在RAM里的内容 尝试修改”Target Options“中右侧RAM的Size,但这个要先查看芯片文档,找到文档中描述的RAM大小,把Size调成最大值。但是我的这个工程还是不行,看来是预存的数据量太大了。 如果第一种方法还是不行,那就只能尝试把一些数据类的变量定义为静态,这样就可以利用ROM的存储空间了。
找到工程中存储数据的部分,发现是大量的float数组。这种情况会占用大量的空间,所以要在float声明的前面加一个”const“,转换为静态变量。这样我的就可以通过了,成功生成了hex文件。 如果第二种方法还是不行,那就只能修改代码的数据结构了。
7、隐藏的文件处理
图3.7.1
Keil直接编译掩藏的文件会报错,按下图设置。
图3.7.2
8、图标错乱
图3.8.1
界面的菜单栏有个"Windows"菜单,单击”Windows“菜单下的"Reset View to Default"。如果运行还是不行,这时就将keil软件关闭,打开重试。一般会恢复正常。如果您的图标还是错误,那您就在关闭软件,打开在点”Reset View to Default“,这样反复重复几遍。
9、地址空间溢出
图3.9.1
图3.9.2
small模式未指定存储类型的变量默认为data,data有128字节 可以直接寻址,但是并不是全部分配给变量,其中通用寄存器组0 要占8个字节,用于主程序,因此超过120字节的变量就要溢出 。 可以用 idata修饰 idata为间接寻址 速度稍慢,可以把速度要求不严格的变量放在idada 或者放在xdata片外RAM,xdata的访问速度是最慢的。
51单片机的内部RAM是128字节,128~255字节是SFR(特殊功能寄存器),超出的都会存放到扩展RAM中,最大可以扩展64K外部RAM。直接DATA访问0~127,128~255用idata访问,再多的用xdata。 或者直接就在程序之前声明然后数组的话,类型为code,直接编译时候写到ROM 其他的话就用xdata,STC会根据内存空间自动分配是否在RAM里。慎用large模式。
10、Registered ARM Compiler ignored,Version needs to be 5 or higher
PC机时间不对或按下图设置到最晚的版本。
11、explicit type is missing ("int" assumed)
图3.11.1
图3.11.2
12、Keil5.14.0.0版本编译通过的程序,在Keil5.12.0.0版本运行出错
Keil5.14.0.0版本编译通过的程序,在Keil5.12.0.0版本运行时会造成程序的时钟变慢,如果有闪烁指示灯会非常明显的感觉到闪烁变长。
13、STM32单片机模拟仿真晶振处出不来
图3.13.1
Dialog DLL默认是DARMSTM.DLL;Parameter默认是-pSTM32F103VC。 若未做相应设置,调试时程序一直停在SystemInit()等待晶振出不来。
14、MDK无法更新Package(pack软件包镜像下载)
15、array index 10 is past the end of the arry
数组下标越界。
16、SUPPORT EXPIRED, NEW PRODUCT SERIAL NUMBER REQUIRED
17、ARM-Compiler version 'Unspecified: use latest compiler version 5' is not available
四、RAM和ROM
1、查看内存
1)“字母:数字”,D、 I、 X、 C分别代表着直接寻址的片内RAM、间接寻址的片内RAM、扩展的外部RAM和ROM。 切记:查看内存仅限于模拟仿真时。
图4.1.1
2)保存memory中的数据,注意保存下来的文件是HEX386格式的,可以通过其他工具转换成BIN格式。 “Command”中用“SAVE”命令: SAVE path filename addr1, addr2 SAVE E:\ 0x0000,0x0100
3)keil编译后会产生.M51或者.map文件,在这里面也可看到内存的使用情况。
4)C程序中查看某一个变量在内存中的地址,比如下面的ChannelKindFault变量 通过串口输出:printf("\r\nSRAM Address:%x",&ChannelKindFault);
2、ARM单片机RAM和ROM
ARM程序(指在ARM系统中正在执行的程序,而非保存在ROM中的bin文件)的组成一个ARM程序包含3部分:RO段,RW段和ZI段。
1)ARM编译结果(可看代码量)
图4.2.1 程序修改后编译结果
(1) Code(inc.Data) : 包含两部分,即代码和数据 - code,即程序代码部分 - inline data. For example, literal pools(文字常量池), and short strings(短字符串)等. 这个一般被忽略,请大家注意!!! - 代码段,存放程序的代码部分。
(2)RO Data: read-only data,只读的数据 Shows how many bytes are occupied by read-only data. This is in addition to the inline data included in the Code (inc. data) column. 除inline data 之外的所有只读数据。 只读数据段,存放程序中定义的常量。
(3)RW Data: read write data,可读写的数据 Shows how many bytes are occupied by read-write data. 读写数据段,存放初始化为非0值的全局变量。
(4)ZI Data: zero initialized data,零初始化的可读写变量 Shows how many bytes are occupied by zero-initialized data. 0数据段,存放未初始化的全局变量及初始化为0的变量。 存储Size: RO size: Code + RO_data,表示程序占用Flash空间的大小。 RW size: RW_data + ZI_data,表示运行时占用RAM的大小。
ROM (minimum)size = Code + RO_data + RW_data (即烧写/下载程序到FLASH/ROM时,所占用的最小空间)
Total ROM Size (Code + RO Data + RW Data)这样所写的程序占用的ROM的字节总数,也就是说程序所下载到ROM Flash 中的大小。为什么ROM中还要存RW,因为掉电后RAM中所有数据都丢失了,每次上电RAM中的数据是被重新赋值的,每次这些固定的值就是存储在ROM中的,为什么不包含ZI段呢,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。RAM相当于内存,Flash相当于硬盘。
RAM size: RW Data + ZI Data (即程序运行的时,RAM使用的空间)
另可参看:单片机C语言程序与数据存储之二、C语言程序的存储区域。
2)ARM映像文件的组成 所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。 Image文件包含了RO和RW数据。之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。
包含进去反而浪费存储空间。 ARM程序的执行过程: 从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。 实际上,RO中的指令至少应该有这样的功能:
(1)将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。启动过程另可见main函数之前都发生了什么。
(2)将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中。 在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。
------------------------
更为详细的讲解搜索百度云盘“嵌入式网络那些事LwIP协议深度剖析与实战演练”的第三章3.1.4 ARM连接器。 一个映像文件(image)一般由一个或多个域组成,而往往在image中的位置同域在实际存储器的的位置俱有对应关系。
图4.2.2
3)ARM映像文件的存储地址映射
图4.2.3
五、ARM单片机Keil5.12正确配置Flash截图
图5.0.1
1、Devie设置
图5.1.1
2、Target设置
图5.2.1
3、Output设置
图5.3.1
4、Listing设置
图5.4.1
5、User设置
图5.5.1
6、C/C++设置
图5.6.1
7、Asm设置
图5.7.1
8、Linker设置
图5.8.1
9、Debug设置
图5.9.1
10、Utilities设置
图5.10.1
六、Keil MDK ARM输出汇编与fromelf.exe生成.bin文件
图6.0.1
1、设置按下图
图6.1.1
2、文件存放
在“Project\LIST目录下方”。
3、使用fromelf.exe生成.bin文件
命令:fromelf.exe --bin -o "$L@L.bin" "#L"
图6.3.1
编译后的输出结果:
图6.3.2
在使用KEIL的时候,我们要使生成的hex文件转化成bin文件,方便下载或fireware更新,有时候看到的是一个目录,遇到这种情况是什么问题呢! 比如说这个命令:fromelf.exe --bin -o "$L@L.bin" "#L", 这个也许就生成了一个bin目录文件,这个是为什么呢?
首先看一下链接脚本,发现里面有几个段的定义,其实我们需要的只是代码段,那么我们只需要修改这条命令为: fromelf.exe --bincombined -o "$L@L.bin" "#L",把--bin 改成 --bincombined, 这样子我们就达到了我们所需要的bin文件,它可以把多个负载段生成一个输出文件。
七、MDK LIB库文件的制作与C文件生成静态库
另可参考:STM32标准库编译成lib库文件、如何将C文件生成静态库。
1、打开一个测试通过的工程
如果本来要打包的库文件里面的代码有错的话,打包成库后也是不能用的,这步是关键。
图7.1.1
测试没有问题后将不需要的部分删除
图7.1.2
2、打开KEIL MDK->Project->Option for target...->Output,选 中Create Executable:....选项
图7.2.1
3、切记:需要重新编译,即可在原本生成Hex文件的目录下找到*.lib文件
4、打开原工程,只需将原来的dsp_g2.c文件移除,添加进该.lib文件即可使用
5、使用指令生成.lib文件(Keil C51测试通过)
1)在编译通过的工程目录里找到delay1s.obj和delay5ms.obj文件,复制到Keil安装目录下的BIN文件夹内。
图7.5.1
2)打开上图中突显的LIB51.EXE。在上面输入以下代码:
图7.5.2
3)这时在BIN中就出现了mylib.lib文件,它已经被添加了之前的两个延时程序。
图7.5.3
4)复制这个库到LIB中,并添加进该.lib文件即可使用 使用指令生成.lib文件在Keil MDK中测试没有通过。
6、C文件生成静态库(如何将C文件生成静态库)
八、程序编辑
1、从任意位置选择程序的多行
按住ALT再作选择即可。
图8.1.1
2、Keil5.12中文注释代码或粘贴后乱码
1)中文注释代码乱码 在新版本的KEIL中,总有人反映中文注释会出现乱码。
出现这种情况,对于中文注释程序的人来说,无疑是一件十分不爽的事情。但实际解决这个问题其实很简单,在Edit/Configuration里如下图:
图8.2.1
这个是view菜单的最后一下configuration的界面,在蓝色选中的部分选择GB2312,如图:
图8.2.2
这时候,编辑文档里的删除文字和退格操作都是按照中文编码了,不再会出现乱码的情况。
2)复制粘贴后乱码(复制粘贴代码变乱码)
3、Keil5.12代码补全功能
前提是不能有中文路径。
图8.3.1
图8.3.2
设置一下代码补全功能。
4、支持C99模式
程序中加入“#include "complex.h"”报需要“C99 mode or gnu mode”
图8.4.1
5、Keil使用AStyle格式化代码
九、程序调试
1、查看硬件的状态
图9.1.1
2、周期性Watch窗口更新(变量随着程序更新)
图9.2.1
3、Watch窗口以十进制显示
图9.3.1
4、使用串口调试程序
图9.4.1
使用printf打印输出到串口,可以查看程序的执行状态。在没有仿真器或单片机不支持仿真时,此法比较实用。 printf指令详见“C语言使用相关汇总1之10、printf格式化字符串”,具体查看数据方法详见“51单片机-宏晶STC之四、程序调试之1、通过串口查看内部变量”。
5、程序中事先进行中断的“埋点”处理
图9.5.1
Keil菜单栏点击“View/Call Stack Window”弹出“Call Stack + Locals”对话框。然后在对话框中右键选择“Show Caller Code”,就会跳转到出错之前的函数处,查看这部分函数被调用或者数组内存使用情况等。Keil调试时设置断点的高级用法。
6、硬仿真查看代码运行时间
点到Trace”标签下,若选择“SW”,则勾选“Enable”选项,在“Core”框中输入MCU实际工作时钟频率(就是单片机以什么频率来执行指令,MDK会用它来计算时间),再勾选“Autodetect max SW0 Clock”。
图9.6.1
图9.6.2
若选择“JTAG”,先勾选“Enable”,在“Core”中设好时钟频率,最后去掉刚才勾选的“Enable”
图9.6.3
进入仿真,界面右下角就会有时间窗口:
图9.6.4
鼠标放在上面右键点击,就会有:
图9.6.5
上面两个是复位“t1”和“t2”的,下面3个是选择在状态栏上显示哪个时间。 “t0”表示程序开始运行到现在的时间,是不能复位的。另外两个可以随便复位,就可以用来测具体某一个函数或某一行程序的运行时间。
具体方法:在要测试的代码前加一个断点,当程序运行到目标行时会停下,然后复位“t1”或“t2”,并在下一行代码前加断点,然后继续运行程序,程序会停在下一行代码前,这个时候“t1”的值就是目标行程序的运行时间。
图9.6.6
运行两次程序,如图9.6.2晶振处设置0.072MHz,故时间单位mS,1010.27mS。
7、watch中添加的变量不能实时更新
图9.7.1
8、调试串口的软件仿真方法
1)Keil 里面先对串口编程,以下假设你已经设定串口的参数为 9600,n,8,1
2)下载一个虚拟串口软件 vspd,安装后config一下,增加一对串口,比如下图的 COM2<-->COM3
3)Keil 里面给两条指令(调试状态下) MODE COM2 9600,0,8,1 assign COM2 S1OUT (如果嫌麻烦,可以先保存为一个 ini文件,并在调试选项里面选中)
图9.8.1
4)然后打开一个串口调试助手,选用另外一个对接的虚拟串口 就可以看到单片机串口输出的内容了。
图9.8.2
十、时钟周期与指令周期
1、时钟周期
时钟周期也称为震荡周期,定义为时钟脉冲的倒数,是计算机中最基本, 最小的时间单位。
2、机器周期
在计算机中,为了便于管理,通常把一条指令执行划分为若干个阶段,每一个阶段完成一项任务。如: 取指令、存储器读,、存储器写等,这每一项工作称为一个基本操作。 完成一个基本操作所需要的时间为机器周期,一个机器周期由若干个S(Status)周期(状态周期)组成,如图10.1所示。 机器周期和时钟周期是传统51留下的一个概念,传统51是把晶振12分频后作为系统时钟使用,因此有一个12倍的关系,后续的很多单片机延续使用此概念,但很多这两个周期都相等了。
3、指令周期
执行1条指令所需要的时间, 一般由若干个机器周期组成。指令不同,所需要的机器周期也不同。 对于一些简单的单字节指令, 在取指令周期中, 指令取出到指令寄存器后, 立即译码执行,不再需要其他的机器周期。 对一些比较复杂的指令, 例如:转移指令、 乘法指令,则需要两个或两个以上的机器周期。 通常含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令。
图10.1
4、总结
厂商
机器周期
晶振频率
指令周期(执行1条单字节指令时间)
8051
1个机器周期=12个时钟周期
12MHz
1uS
STC*
12T/6T/1T
12MHz
12T同上
PIC
1个机器周期=4个时钟周期
4MHz
1uS
AVR
1个机器周期=4个时钟周期
4MHz
0.25uS
MSP430
STM32**
平均执行速度1.25MIPS/Mhz
8MHz*9倍=72M
20nS
*传统51单片机12个时钟周期执行完一条简单指令(如INC A),称为12T模式,而现在增强型51单片机(比如宏晶STC12C5608)执行这样的指令只需一个晶振周期,即1T模式。可见晶振频率相同的情况下,1T单片机速度是传统51单片机的12倍。
**STM32有三级流水线,指令周期不定,ARM给出1.25MIPS/Mhz的1个平均执行速度。1Mhz频率每秒可执行1.25M指令。72M就是72*1.25。而这个最大的应用是通过单周期指令去测试系统时钟是否配置的正确。 下面做一下实际测试: 若软仿真时一直在晶振处出不来,按如下设置。
图10.2
图10.3
图10.4
16.88221mS-16.88219mS = 20nS
如若喜欢这篇文章,不妨留下您宝贵的点赞,这将是对我莫大的鼓励。也可以前往公众号获取更多资料,全网同号。
钶龙战记第四季
迈高奶粉怎么样,迈高奶粉到底好不好