历史上的今天
今天是:2025年03月17日(星期一)
2020年03月17日 | Hi-Tech PICC 8bit MCU 的学习笔记 -- 环境篇
2020-03-17 来源:eefocus
/*---------------------------------------------------------*/
Hi-Tech PICC 8bit MCU 的学习笔记
---------- 环境篇
/*---------------------------------------------------------*/
第一节 环境
1.概况
PIC 单片机的 C 语言编译器众多,常见的有 Hitech、CCS、IAR、Bytecraft 等公司,其中最常用的是Hitech 公司的PICC 编译器。在此列出几个主要的针对 PIC 单片机的 C 编译器相关连接网址,供参考:
Hitech-PICC: www.htsoft.com
IAR:www.iar.com
CCS:www.ccsinfo.com/picc.shtml
ByteCraft:www.bytecraft.com/mpccaps.html
PICC和ANSI C根本的区别:
a. PICC的C语言按ANSI C来定义,并进行了C语言的扩展;
b. PICC不支持函数的递归调用。
这是因为PIC单片机的堆栈大小是由硬件决定的,资源有限,所以不支持递归调用。
2.MPLAB-IDE 内接 PICC
首先必须在你的计算机中安装PICC编译器,无论是完全版还是学习版都可以和MPLAB-IDE挂接。安装成功后可以进入IDE,选择菜单项Project Set Language Tool Locations…,打开语言工具挂接设置对话框,在对话框中选择“HI-TECH PICC Toolsuite”栏,展开可执行文件组“Executable”后,列出了将被 MPLAB-IDE 后台调用的编译器所用到的所有可执行文件,其中有汇编编译器“PICC Assembler”、C 原程序编译器“PICC Compiler”和连接定位程序“PICC Linker”。同时在此列表中还显示了对应的可执行程序名,请注意在这里都是“PICC.EXE”。用鼠标分别点击选中这三项可执行文件,观察对话框下面“Location”一栏中显示的文件路径,用“Browse…”按纽,从计算机中已经安装的 PICC 编译器文件夹中选择 PICC.EXE 文件。实际上 PICC.EXE 只是一个调度管理程序,它会按照所输入的文件扩展名自动调用对应的编译器和连接器,用户要注意的是 C 语言原程序扩展名用“.c”,汇编原程序用“.asm”即可。工具挂接完成后,在建立项目时可以选择语言工具为“HI-TECH PICC”。
3.PICC 源程序的框架
PICC 编译环境编程的方式和标准 C 程序类似。程序一般由以下几个主要部分组成:
3.1 #i nclude 预处理指令引用包含头文件
有个名称为pic.h文件,它为编译器提供的了单片机内特殊寄存器和其它特殊符号的声明
3.2 “__CONFIG”预处理指令定义芯片的配置位
声明本模块内被调用的所有函数的类型,PICC 将对所调用的函数进行严格的类型匹配检查;
3.3 定义全局变量或符号替换
实现函数(子程序),特别注意 main 函数必须是一个没有返回的死循环。
举例如下:
// 引用特殊寄存器和其它特殊符号的声明
#i nclude “pic.h”
// 定义芯片工作时的配置位
__CONFIG (HS & PROTECT & PWRTEN & BOREN & WDTDIS);
// 声明本模块中所调用的函数类型
void initial_program(void);
void clock(void);
// 定义变量
unsigned char second, minute, hour;
bit flag1,flag2;
// 函数和子程序
void main(void)
{
initial_program();
while(1)
{
CLRWDT(); //清看门狗
clock(); //更新时钟
}
for(;;);
}
4.函数的代码长度限制
PICC 决定了 C 原程序中的一个函数经编译后生成的机器码一定会放在同一个程序页面内。中档系列的 PIC 单片机其一个程序页面的长度是 2K 字,换句话说,用 C 语言编写的任何一个函数最后生成的代码不能超过 2K 字。一个良好的程序设计应该有一个清晰的组织结构,把不同的功能用不同的函数实现是最好的方法,因此一个函数 2K 字长的限制一般不会对程序代码的编写产生太多影响。如果为实现特定的功能确实要连续编写很长的程序,这时就必须把这些连续的代码拆分成若干函数,以保证每个函数最后编译出的代码不超过一个页面空间。
5.调用层次的控制
中档系列 PIC 单片机的硬件堆栈深度为 8 级,考虑中断响应需占用一级堆栈,所有函数调用嵌套的最大深度不要超过 7 级。编程员必须自己控制子程序调用时的嵌套深度以符合这一限制要求
6.*.map 文件
PICC 在最后编译连接成功后可以生成一个连接定位映射文件(*.map),在此文件中有详细的函数调用嵌套指示图“call graph”,建议留意一下。其信息大致如下
(取自于一示范程序的编译结果):
Call graph:
*_main->_initial_program size 0,0 offset 0
* _timer1_on
* _timer0_on
*_isr
7.interrupt - 中断函数的实现
7.1
PICC 可以实现 C 语言的中断服务程序。中断服务程序有一个特殊的定义方法:
void interrupt ISR(void);
其中的函数名“ISR”可以改成任意合法的字母或数字组合,但其入口参数和返回参数类型必须是“void”型,亦即没有入口参数和返回参数,且中间必须有一个关键词“interrupt”。
中断函数可以被放置在原程序的任意位置。因为已有关键词“interrupt”声明,PICC 在最后进行代码连接时会自动将其定位到 0x0004 中断入口处,实现中断服务响应。编译器也会实现中断函数的返回指令“retfie”。
举例说明,简单的中断服务示范函数如下:
void interrupt ISR(void) //中断服务程序
{
if (T0IE && T0IF)
{
T0IF = 0; //在此加入 TMR0 中断服务
}
if (TMR1IE && TMR1IF) //判 TMR1 中断
{
TMR1IF=0; //在此加入 TMR1 中断服务
}
} //中断结束并返回
7.2 中断函数处理过程
PICC 会自动加入代码实现中断现场的保护,并在中断结束时自动恢复现场,所以编程员无需象编写汇编程序那样加入中断现场保护和恢复的额外指令语句。如果在中断服务程序中需要修改某些全局变量时,是否需要保护这些变量的初值将由编程员自己决定和实施。
用 C 语言编写中断服务程序必须遵循高效的原则:
代码尽量简短,中断服务强调的是一个“快”字。
避免在中断内使用函数调用。虽然 PICC 允许在中断里调用其它函数,但为了解决递归调用的问题,此函数必须为中断服务独家专用。既如此,不妨把原本要写在其它函数内的代码直接写在中断服务程序中。
避免在中断内进行数学运算。数学运算将很有可能用到库函数和许多中间变量,就算不出现递归调用的问题,光在中断入口和出口处为了保护和恢复这些中间临时变量就需要大量的开销,严重影响中断服务的效率。
中档系列 PIC 单片机的中断入口只有一个,因此整个程序中只能有一个中断服务函数。
8.标准库函数
PICC 提供了较完整的 C 标准库函数支持,其中包括数学运算函数和字符串操作函数。在程序中使用这些现成的库函数时需要注意的是入口参数必须在 bank0 中。
C 语言中常用的格式化打印函数“printf/sprintf”用在单片机的程序中时要特别谨慎。 printf/sprintf 是一个非常大的函数,一旦使用,你的程序代码长度就会增加很多。除非是在编写试验性质的代码,可以考虑使用格式化打印函数以简化测试程序;一般的最终产品设计都是自己编写最精简的代码实现特定格式的数据显示和输出。本来,在单片机应用中输出的数据格式都相对简单而且固定,实现起来应该很容易。
对于标准 C 语言的控制台输入(scanf)/输出(printf)函数,PICC 需要用户自己编写其底层函数 getch()和 putch()。在单片机系统中实现 scanf/printf 本来就没什么太多意义,如果一定要实现,只要编写好特定的 getch()和 putch()函数,你就可以通过任何接口输入或输出格式化的数据。
9. PICC 定义特殊区域值
PICC 提供了相关的预处理指令以实现在原程序中定义单片机的配置字和标记单元。
9.1 定义工作配置字
在用 PICC 写程序时同样可以在 C 原程序中定义,具体方式如下:
__CONFIG (HS & UNPROTECT & PWRTEN & BORDIS & WDTEN);
上面的关键词__CONFIG(注意前面有两个下划线符)专门用于是芯片配置字的设定,后面括号中的各项配置位符号在特定型号单片机的头文件中已经定义(注意不是 pic.h头文件),相互之间用逻辑“与”操作符组合在一起。这样定义的配置字信息最后将和程序代码一起放入同一个 HEX 文件。
在这里列出了适用于 16F7x 系列单片机配置位符号预定义,其它型号或系列的单片机配置字定义方式类似,使用前查阅一下对应的头文件即可。
/*振荡器配置*/
#define RC
#define HS
0x3FFF // RC 振荡
0x3FFE // HS 模式
#define XT
#define LP
/*看门狗配置*/
0x3FFD // XT 模式
0x3FFC // LP 模式
#define WDTEN 0x3FFF // 看门狗打开
#define WDTDIS
/*上电延时定时器配置*/
#define PWRTEN
0x3FFB // 看门狗关闭
0x3FF7 // 上电延时定时器打开
#define PWRTDIS 0x3FFF // 上电延时定时器关闭
/*低电压复位配置*/
#define BOREN 0x3FFF // 低电压复位允许
#define BORDIS
/*代码保护配置*/
0x3FBF // 低电压复位禁止
#define UNPROTECT 0x3FFF // 没有代码保护
#define PROTECT 0x3FEF // 程序代码保护
9.2 头文件预定义的配置信息符号
定义芯片标记单元
PIC 单片机中的标记单元定义可以用下面的__IDLOC(注意前面有两个下划线符)预处理指令实现,方法如下:
__IDLOC (1234);
其特殊之处是括号内的值全部为 16 进制数,不需要用“0x”引导。这样上面的定义就设定了标记单元内容为 01020304。
10.MPLAB-IDE 中实现 PICC 的编译选项设置
PICC 和 MPLAB-IDE 开发平台的挂接,一旦项目建立成功、程序编写完成后即可以通过 MPLAB 环境下的项目管理工具实现程序的编译、连接和调试。它们的含义分别是:
-项目维护(Make):MPLAB 检查项目中的原程序文件,只编译那些在上次编译后又被修改过的原程序,最后进行连接;
-项目重建(Build All):项目中的所有原程序文件,不管是否有修改,都将被重新编译一次,最后进行连接。也可以通过 Project 菜单选择“Make”或“Build All”实现项目编译。不管采用何种方式,在启动编译过程前一般都要设定一些编译选项。
11.选择单片机型号
在选择 PICC 作为语言工具并建立了项目后,同样通过菜单项 Configure&O1616;Select Device在 MPLAB 环境中选择具体单片机型号。请回顾一下例 11-1 的代码,我们在原程序一开始使用了“#i nclude ”实现了相关单片机的一些预定义符号的直接引用,但没有具体指明是哪一个型号。实际上,“pic.h”头文件只是一个简单的管理工具(条件判别),它会按照MPLAB 所选择的特定型号的单片机,把真正对应的头文件包含进来。有兴趣者可以直接用文本编辑工具打开 pic.h 文件查看其是如何根据不同的单片机型号包含对应的头文件。这样对编程员而言,程序中只需加上一句“#i nclude ”即可。
12.PICC 普通编译选项(General)设定
启动编译选项设定对话框。在使用PICC 语言工具时对话框的内容和用 MPAMS 汇编工具相比完全不同。在此界面中用户唯一能改变的是编译器查找头文件时的指定路径(Include Path),实际上如果编译器安装没有问题,在此界面中这些普通选项的设定无需任何改动,编译器会自动到缺省认定的路径中(编译器安装后的相关路径)查找编译所需的各类文件。
13.PICC 全局选项设定(PICC Global)
全局选项将影响项目中所有 C 和汇编原程序的编译,其中,必须关注的有:Compile for MPLAB ICD:如果你准备用 ICD 调试 C 语言编译后的代码,那么此项就必须打钩选中。这样编译后的结果就能保证 ICD 本身使用的芯片资源(一小部分的程序和数据空间)不被应用程序所占用。
Treat ‘char’ as signed:为了提高编译后的代码效率,PICC 缺省认定‘char’型变量也是无符号数。如果在设计中需要使用带符号的‘char’型变量,此项就应该被选中。Floating point ‘double’ width:同样为了提高编译后的代码效率,PICC 缺省认定‘double’型的双精度浮点数变量的实现长度为 24 位(等同于普通 float 型浮点数)。在这里可以选择使其长度达 32 位。这样数值计算的精度将得到提高,但代码长度将增加,计算速度也会降低,所以请在权衡利弊后作出你自己的决定。
14.C 编译器选项设定(PICC Compiler)
项目中所有的 C 原程序都将通过 C 编译器编译成机器码,这些选项决定了 C 编译器是如何工作的。所有选项又分为两组:普通选项(General)和高级选项(Advanced)C 编译器的普通选项最重要的就是针对代码优化的设定。如果没有特殊原因,应该设定全局优化级别为 9 级(最高级别优化),同时使用汇编级优化,这样最终得到的代码效率最高(长度和执行速度两方面)。按笔者的使用经验,仅从代码长度去比较,使用最高级别优化后代码长度至少可以减少 20%(2K 字以上的程序)。而且 PICC 的优化器相当可靠,一般不会因为使用优化从而使生成的程序出现错误。碰到的一些问题也基本都是用户编写的原程序有漏洞所导致,例如一些变量应该是 volatile 型但编程员没有明确定义,在优化前程序可以正常运行,一旦使用优化,程序运行就出现异常。显然,把出现的这些问题归罪到编译器是毫无道理的。
使用优化后可能对原程序级的调试带来一些不便之处。因 PICC 可能会重组编译后的代码,例如多处重复的代码可能会改成同一个子程序调用以节约程序空间,这样在调试过程中跟踪原程序时可能会出现程序乱跳的现象,这基本是正常的。若为了强调更直观的代码调试过程,你可以将优化级别降低甚至关闭所有优化功能,这样调试时程序的运行就可以按部就班了。
C 编译器的高级选项设定基本都是针对诊断信息输出的,和生成的代码无关。用得相对较多的选项有:
Generate assembly list file:编译器生成 C 原程序的汇编列表文件(*.lst)。在此文件中列出了每一行 C 原代码对应的汇编指令,但这些都是优化前的代码。简单的一条 C 语句被翻译成汇编指令后可能有好几条。有时汇编列表文件可以作为解决问题的辅助手段。如果你怀疑编译器生成的代码有错误,不妨先产生对应的汇编列表文件,看看在优化前一条 C 语句被编译后的汇编码到底是什么。
Compile to assembly only:这一选项的作用是把&n, bsp; C 原程序编译成汇编指令文件(*.as),此时将不生成目标文件,也不进行最后的连接定位。这一选项在 C 和汇编混合编程时特别有用。通过解读 C 程序对应的汇编指令,可以掌握 C 程序中存取变量的具体方法,然后用在自己编写的汇编指令中。我们将在稍后专门做介绍。
15.连接器选项设定(PICC Linker)
连接器 PICC Linker 的选项基本不用作太多的改变,其中有两项有用的信息输出可以考虑加以利用:
Generate map file:生成连接定位映射文件。在此映射文件中详细列出了所有程序用到的变量的具体物理地址;所有函数的入口地址;函数相互之间调用的层次关系和深度等。这些信息对于程序的调试将非常有用。此文件将以扩展名“*.map”的形式存放在同一个项目路径下,需要时可以用任何文本编辑器打开观察。
Display memory-segment usage:显示详细的内存分配和使用情况报告。用户可以了解到程序空间和数据存储器空间资源分配的细节。
下面列举了在一个项目编译后实际的内存使用信息,为方便理解笔者用“//”添加了一些注释:
sect Usage Map: //程序段定位表
Psect | Contents | Memory Range
---------|------------------------------|--------------------
powerup | Power on reset code | $0000 - $0003
intentry | Interrupt service routine | $0004 - $0007
intcode | Interrupt service routine | $0008 - $0051
intret | Interrupt service routine | $0052 - $0056
init | Initialization code | $0057 - $005A
end_init | Initialization code | $005B - $005C
clrtext | Memory clearing code | $005D - $0063
text | Program and library code | $03B9 - $03BD
text5 | Program and library code | $03BE - $03C3
text0 | Program and library code | $03C4 - $03CF
text4 | Program and library code | $03D0 - $03DC
text2 | Program and library code | $03DD - $03FE
intsave | Registers saved on interrupt | $0020 - $0020
rbss_0 | Bank 0 RAM variables | $0021 - $0025
intsave | Registers saved on interrupt | $0026 - $0026
Memory Usage Map: //程序空间代码定位地址分布
//存储空间使用情况报告
Program ROM $0000 - $0063 $0064 ( 100) words
Program ROM $03B9 - $03FE $0046 ( 70) words
$00AA ( 170) words total Program ROM
//bank0 数据空间变量地址分布
Bank 0 RAM $0020 - $0026 $0007 ( 7) bytes total Bank 0 RAM
//配置字地址
Config Data $2007 - $2007 $0001 ( 1) words total Config Data
Program statistics: //程序总体资源消耗统计
Total ROM used 170 words (16.6%)
Total RAM used 7 bytes (10.9%)
上一篇:PICC C中的函数
史海拾趣
|
CORTEX-M3 移植 UCOS 由IAR5.1 转到 REALVIEW 3.20 1, UCOS在 IAR5.1下工作有点不正常了,所以想转到MDK 3.20下,可是编译文件os_cup.a.asm时出错,提示 RSEG CODE:CODE:NOROOT(2) ,没有这个伪指令。 2. 文件开始处定义了: EXTERN &n ...… 查看全部问答> |
|
请问IIC的应答信号是怎样产生的? SDA为0就应答,SDA为1就非应答,那SDA的值是怎样赋值的? 还有,是不是应答或非应答都可以传输下一帧数据?如果是的话那两者有什么意义?… 查看全部问答> |
|
单独编译EBOOT,听说好像要设置什么环境变量,怎么设? 我看HELP里有: Set WINCEREL=1 是这个么?还有人说:wince.bat/myproject1.bat 怎么设呀?谢谢。。… 查看全部问答> |
|
在下从事网络工作有一段时间了。不过就是做售后工作..平时无非也就是给客户调试下路由,3层交换,防火墙什么的。其中遇到许多软件上的问题,感觉自己无能为力.我想问下,需要掌握什么知识才能对路由的软件进行开发,或是需要什么样的开发环境.上学时有一 ...… 查看全部问答> |
|
请问注册表里面 HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\Root 下面的LEAGCY_XXX项是什么时候创建的?在安装驱动的时候? 这项的创建是不是仅仅和安装用的inf文件相关,和driver的源码相不相关? 我遇到的问题是这样的,在编译vi ...… 查看全部问答> |




