版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
王健带你征服Pic单片
机PROTUES仿真C语言
部作者多年工作经验的著作
讲解最全的单片机C语言程序
王健著
337884703@
前言
这个教程最大的特色就是用PROTUES软件,为读者省去
一大笔开支,真正零开支,所用软件是MPLABIDE8.63和
PICC9.8
这本书分为两部分,虽然是C语言,但是也要讲一下单片机
的一些基础知识,第一部分讲单片机的基础知识,第二部分
讲C程序
PIC单片机(PeripheralInterfaceController)是一
种用来可开发的去控制外围设备的可编程集成电路(IC)。
由美国Microchip(微星)公司推出的PIC单片机系列产品,
首先采用了RISC结构的嵌入式微控制器,其高速度、低电
压、低功耗、大电流LCD驱动能力和低价位0TP技术等都体
现出单片机产业的新趋势。现在PIC系列单片机在世界单片
机市场的份额排名中已逐年升位,尤其在8位单片机市场,
已从1990年的第20位上升到目前的第二位。PIC单片机从
覆盖市场出发,已有三种(又称三层次)系列多种型号的产品
问世,所以在全球都可以看到PIC单片机从电脑的外设、家
电控制、电讯通信、智能仪器、汽车电子到金融电子各个领
域的广泛应用。现今的PIC单片机已经是世界上最有影响力
的嵌入式微控制器之一。
第一部分认识pic单片机
PIC单片机(PeripheralInterfaceController)是一
种用来开发去控制外围设
备的集成电路(IC)o一种具有分散作用(多任务)功能
的CPU。与人类相比,大脑就是CPU,PIC共享的部分相当
于人的神经系统。
2.PIC单片机是一个小的计算机
3.PIC单片机有计算功能和记忆内存像CPU并由软件
控制运行。然而,处理能力一般,存储器容量也很有限,
这取决于PIC的类型。但是它们的最高操作频生大约都在
20MHz左右,存储器容量用做写程序的大约IK—4K字节。
时钟频率与扫描程序的时间和执行程序指令的时间
有关系。但不能仅以时林频茎来判断程序处理能力,它还
随处理装置的体系结构改变(1*)。如果是同样的体系结
构,时钟频率较高的处理能力会较强。
这里用字来解释程序容量。用一个指令(2*)表示一
个字。通常用字苴(3*)来表示存储器(4*)容量。一个
字节有8位,每位由1或0组成。PIC16F84A单片机的指
令由14位构成。当把1K个字转换成位为:1xl,024x14
=14,336位。再转换为字节为:14,336/(8x1,024)二
1.75KO在计算存储器的容量时,我们规定1G宝芭=
1,02圳字节,1M字节=l,024K字节,1K字节二1,024
位.它们不是以1000为倍数,因为这是用二进制计算的
缘故。
1*计算机的物理结构,包括组织结构、容量、该计算
机的CPU、存储器以及输入输出设备间的互连。经常特指
CPU的组织结构,包括它的寄存器、标志、总线、算术逻辑
部件、指令译码与执行机制以及定时和控制部件。
2*指出某种操作并标识其操徒数.(如果有操作数的话)
的一种语言构造
3*作为一个单位来操作(运算)的一个二进制字符串,
通常比计算机的一个字短。
4*处理机内的所有可寻址存储空间以及用于执行指
令的其它内存储器。
在计算存储器的容量时,我们规定1G字节二
1,024M字节,1M字节=l,024K字节,1K字节:1,024
字节.它们不是以1000为倍数,因为这是用二进制计算
的缘故。
用PIC单片机使电路做的很小巧变得可能。
因为PIC单片机可以把计算部分、内存、输入和输出
等都做在一个芯片内。所以她工作起来效率很高、功能也
自由定义还可以灵活的适应不同的控制要求,而不必去更
换不同的IC。这样电路才有可能做的很小巧。
PIC单片机型号命名规则
PICXXXXXXXX(X)-XXX/XX
12345678
1.前缀:PICMICROCHIP公司产品代号,特别地:dsPIC
为集成DSP功能的新型PIC单片机
2.系列号:10、12、16、18、24、30、33、32,其中
PIC10、PIC12.PIC16,PIC18为8位单片机
PIC24、dsPIC30,dsPIC33为16位单片机
PIC32为32位单片机
3.器件型号(类型):
CCMOS电路
CRCMOSROM
LC小功率CMOS电路
LCS小功率保护
AA1.8V
LCR小功率CMOSROM
LV低电压
F快闪可编程存储器
HC高速CMOS
FRFLEXROM
4.改进类型或选择
54A、58A、61、62、620、621
622、63、64、65、71、73、74
42、43、44等
5.晶体标示:
LP小功率晶体,
RC电阻电容,
XT标准晶体/振荡器
HS高速晶体
6.频率标示:
-022MHZ,
-044MHZ,
-1010MHZ,
-1616MHZ
-2020MHZ,
-2525MHZ,
-3333MHZ
7.温度范围:
空白0℃至70℃,
I-45℃至85℃,
E-40°。至125℃
8.封装形式:
LPLCC封装
JW陶瓷熔封双列直插,有窗口
P塑料双列直插
PQ塑料四面引线扁平封装
W大圆片
SL14腿微型封装-150mil
JN陶瓷熔封双列直插,无窗口
SM8腿微型封装-207mil
SN8腿微型封装T50mil
VS超微型封装8nlmX13.4mm
SO微型封装-300mil
ST薄型缩小的微型封装-4.4频
SP横向缩小型塑料双列直插
CL68腿陶瓷四面引线,带窗口
SS缩小型微型封装
PT薄型四面引线扁平封装
TS薄型微型封装8nlmX20mm
TQ薄型四面引线扁平封装
当今单片机厂商琳琅满目,产品性能各异。针对具体情况,
我们应选何种型号呢?首先,我们来弄清两个概念:集中指
令集(CISC)和精简指令集(RISC)。采用CISC结构的单片
机数据线和指令线分时复用,即所谓冯.诺伊曼结构。它的
指令丰富,功能较强,但取指令和取数据不能同时进行,速
度受限,价格亦高。采用RISC结构的单片机数据线和指令
线分离,即所谓哈佛结构。这使得取指令和取数据可同时进
行,且由于一般指令线宽于数据线,使其指令较同类CISC
单片机指令包含更多的处理信息,执行效率更高,速度亦更
快。同时,这种单片机指令多为单字节,程序存储器的空间
利用率大大提高,有利于实现超小型化。属于CISC结构的
单片机有Intel8051系列、Motorola和M68HC系列、Atmel
的AT89系列、台湾Winbond(华邦)W78系列、荷兰Pilips
的PCF80C51系列等;属于RISC结构的有Microchip公司的
PIC系列、Zilog的Z86系列、Atmel的AT90S系列、韩国三
星公司的KS57C系列4位单片机、台湾义隆的EM-78系列等。
一般来说,控制关系较简单的小家电,可以采用RISC型单
片机;控制关系较复杂的场合,如通讯产品、工业控制系统
应采用CISC单片机。不过,RISC单片机的迅速完善,使其
佼佼者在控制关系复杂的场合也毫不逊色。
根据程序存储方式的不同,单片机可分为EPROM、OTP(一次
可编程)、QTP(掩膜)三种。我国一开始都采用ROMless型
单片机(片内无ROM,需片外配EPROM),对单片机的普及起
了很大作用,但这种强调接口的单片机无法广泛应用,甚至
走入了误区。如单片机的应用一味强调接口,外接I/O及存
储器,便失去了单片机的特色。目前单片机大都将程序存储
体置于其内,给应用带来了极大的方便。值得一提的是,以
往OTP型单片机的价格是QTP的3倍,而现在已降至1.5-
1.2倍,选用OTP型以免订货周期、批量的麻烦是可取的。
PIC系列单片机的优势
自从我2006年接触PIC单片机以来,便一直热衷于这种单
片机的开发与应用
PIC最大的特点是不搞单纯的功能堆积,而是从实际出
发,重视产品的性能与价格比,靠发展多种型号来满足不同
层次的应用要求。就实际而言,不同的应用对单片机功能和
资源的需求也是不同的。比如,一个摩托车的点火器需要一
个I/O较少、RAM及程序存储空间不大、可靠性较高的小型
单片机,若采用40脚且功能强大的单片机,投资大不说,
使用起来也不方便。PIC系列从低到高有几十个型号,可以
满足各种需要。其中,PIC12c508单片机仅有8个引脚,是
世界上最小的单片机。该型号有512字节ROM、25字节RAM、
一个8位定时器、一根输入线、5根I/O线,市面售价在3
-6元人人民币。这样一款单片机在象摩托车点火器这样的
应用无疑是非常适合。PIC的高档型号,如PIC16c74(尚不
是最高档型号)有40个引脚,其内部资源为ROM共4K、192
字节RAM8路A/D、3个8位定时器、2个CCP模块、三个
串行口、1个并行口、11个中断源、33个I/O脚。这样一个
型号可以和其它品牌的高档型号媲美。
精简指令使其执行效率大为提高。PIC系列8位CMOS单
片机具有独特的RISC结构,数据总线和指令总线分离的哈
佛总线(Harvard)结构,使指令具有单字长的特性,且允
许指令码的位数可多于8位的数据位数,这与传统的采用
CISC结构的8位单片机相比,可以达到2:1的代码压缩,速
度提高4倍。
产品上市零等待(Zerotimetomarket)o采用PIC的
低价OTP型芯片,可使单片机在其应用程序开发完成后立刻
使该产品上市。
Pic有优越开发环境。OTP单片机开发系统的实时性是
一个重要的指标,象普通51单片机的开发系统大都采用高
档型号仿真低档型号,其实时性不尽理想。PIC在推出一款
新型号的同时推出相应的仿真芯片,所有的开发系统由专用
的仿真芯片支持,实时性非常好。就我个人的经验看,还没
有出现过仿真结果与实际运行结果不同的情况。
其引脚具有防瞬态能力,通过限流电阻可以接至220V交
流电源,可直接与继电器控制电路相连,无须光电耦合器隔
离,给应用带来极大方便。
彻底的保密性。PIC以保密熔丝来保护代码,用户在烧
入代码后熔断熔丝,别人再也无法读出,除非恢复熔丝。目
前,PIC采用熔丝深埋工艺,恢复熔丝的可能性极小。
自带看门狗定时器,可以用来提高程序运行的可靠性。
睡眠和低功耗模式。虽然PIC在这方面已不能与新型的
TI-MSP430相比,但在大多数应用场合还是能满足需要的。
Microchip基于精简指令集计算(RISC)的PlCmicro系列单
片机是为要求高性能而低价格的用户设计的。PlCmicro系列
单片机家族由130多种产品组成,集多种存储器配置,低电
压低功率,小引脚及易于使用等多种特点于一身。在我们8
位MCU引脚的微小空间里封装有大量的信息。随着您设计的
复杂化,您所写的程序代码以后可以轻易地转换到具有更多
外围资源的较大的MicrochipMCU中去。PlCmicroMCU家族
中的所有产品都是无间隙过渡的,在嵌入式设计中可完全向
上兼容。如果您已经知道设计应用产品所需的单片机规格,
您就可通过Microchip的参数搜索引擎按照相应的条件进行
搜索并选用相应的产品。在您的选择过程中所需用到的条件
为:编程存储器大小,数据RAM,存储器类型以及封装类型。
如果您对Microchip的PlCmicroMCU还不熟悉的话,以下
介绍将帮助您了解。PlCmicroMCU将高性能,低价格,封装
尺寸小,提供最高性价比等特点集于一身,提供5个家族的
8位单片机产品,以最好地满足您的需要。
PIC12CXXX家族:8引脚12/14位程序词
PIC12CXXX家族产品为Microchip功能强大的基于RISC的
PlCmicro系列8引脚DIP及S0IC封装。PIC12CXXX产品具
有12或14位宽指令集,2.5V低运行电压,小封装引脚,中
断处理,深硬件堆栈,多通道以及EEPR0M数据存储器。这
些智能级特性由于价格与尺寸的原因在以前的产品中是不
具备的。
PIC16c5X家族:12位程序词
PIC16C5X家族具有全套基础产品,为您提供最有效的价格解
决方案。PIC16c5X产品具备12位宽指令集,目前提供14,
18,20及28引脚封装。在选用S0IC与SS0P封装时,产品
的引脚所占据的空间是最小的。低至2.0V运行的低压0TP
MCU使产品成为电池驱动应用产品的理想选择。另外,
PIC16HV5XX在直接应用电池时最高可运行至15Vo
PIC16CXXX家族:14位程序词
在推出了新的PIC16CXXX家族成员后,Microchip现已在该
产业中具备向单片机提供最高性能的12位模拟数字转换器
能力的供应商。PIC16CXXX家族可提供从18引脚至68引脚
封装,以及由低级至高级的外围集成的一系列产品。该家族
具备14位宽指令集,中断处理能力及8级硬件深堆栈。
PIC16XXX家族提供了高性能及多功能的中档应用产品,以适
应当今市场的价格竞争。
PIC17CXXX家族:16位程序词
PIC17CXXX家族将PlCmicroMCU的高性能RISC结构扩展为
16位指令词,增强型指令集与强大的矢量中断处理能力。强
大的队列式精确在片外设特性提供了能适应更多应用场合
的各种性能。
PIC18CXXX家族:增强型16位程序词
PIC18CXXX家族产品具有高性能,CMOS,集成了模拟数
字(A/D)转换器的全静态MCU等特点。所有PIC18CXXX的
MCU均应用了先进的RISCoPIC18CXXX具备增强型中心特性,
32位深堆栈,以及内外多中断源。Harvard系列中的分离指
令与数据总线允许16位宽指令词与独立8位宽数据。两级
指令通道允许所有指令在一个周期内执行,除非子程序中设
定必须在两个周期内完成。精简指令集共有77条指令。另
外,一个大寄存器组使采用了结构性创新的MCU达到10MIPS
的极高的性能。PIC18CXXX家族具备的特殊特性能减少外部
组件以降低成本,增强系统可靠性并降低功耗。这些特性包
括可编程低压检测(LVD)及可编程Brown-Out检测(BOD)
PIC单片机C语言编程简介
用C语言来开发单片机系统软件最大的好处是编写代码
效率高、软件调试直观、维护升级方便、
代码的重复利用率高、便于跨平台的代码移植等等,因此C
语言编程在单片机系统设计中已得到越
来越广泛的运用。针对PIC单片机的软件开发,同样可以
用c语言实现。
但在单片机上用c语言写程序和在PC机上写程序绝对不
能简单等同。现在的PC机资
源十分丰富,运算能力强大,因此程序员在写PC机的应用
程序时几乎不用关心编译后的可
执行代码在运行过程中需要占用多少系统资源,也基本不用
担心运行效率有多高。写单片机
的C程序最关键的一点是单片机内的资源非常有限,控制
的实时性要求又很高,因此,如
果没有对单片机体系结构和硬件资源作详尽的了解,以笔者
的愚见认为是无法写出高质量实
用的C语言程序。这就是为什么前面所有章节中的的示范
代码全部用基础的汇编指令实现
的原因,希望籍此能使读者对PIC单片机的指令体系和
硬件资源有深入了解,在这基础之
上再来讨论C语言编程,就有水到渠成的感觉。
本书围绕中档系列PIC单片机来展开讨论,Microchip公
司自己没有针对中低档系列PIC
单片机的C语言编译器,但很多专业的第三方公司有众多
支持PIC单片机的C语言编译器
提供,常见的有Hitech、CCS、IAR、Bytecraft等公司。
其中笔者最常用的是Hitech公司的
PICC编译器,它稳定可靠,编译生成的代码效率高,在用
PIC单片机进行系统设计和开发
的工程师群体中得到广泛认可。其正式完全版软件需要购
置,但在其网站上有限时的试用版
供用户评估。另外,Hitech公司针对广大PIC的业余爱好
者和初学者还提供了完全免费的学
习版PICC-Lite编译器套件,它的使用方式和完全版相同,
只是支持的PIC单片机型号限制
在PIC16F84、PIC16F877和PIC16F628等几款。这几款
Flash型的单片机因其所具备的丰富
的片上资源而最适用于单片机学习入门,因此笔者建议感兴
趣的读者可从PICC-Lite入手掌
握PIC单片机的C语言编程。
在此列出几个主要的针对PIC单片机的C编译器相关连
接网址,供读者参考:
Hitech-PICC:
IAR:
CCS:/picc.shtml
ByteCraft:www.bytecraft,com/mpccaps.html
本章将介绍Hitech-PICC编译器的一些基本概念,由于篇
幅所限将不涉及C语言的标准
语法和基础知识介绍,因为在这些方面都有大量的书籍可以
参考。重点突出针对PIC单片
机的特点而所需要特别注意的地方。
11.2
Hitech-PICC编译器
PICC基本上符合ANSI标准,除了一点:它不支持函数的
递归调用。其主要原因是因
为Pic单片机特殊的堆栈结构。在前面介绍PIC单片机架
构时已经详细说明了PIC单片机
中的堆栈是硬件实现的,其深度已随芯片而固定,无法实现
需要大量堆栈操作的递归算法;
另外在PIC单片机中实现软件堆栈的效率也不是很高,为
此,PICC编译器采用一种叫做“静
态覆盖”的技术以实现对C语言函数中的局部变量分
配固定的地址空间。经这样处理后产
生出的机器代码效率很高,按笔者实际使用的体会,当代码
量超过4K字后,C语言编译出
的代码长度和全部用汇编代码实现时的差别已经不是很大
(<10%),当然前提是在整个C
代码编写过程中须时时处处注意所编写语句的效率,而如果
没有对PIC单片机的内核结构、
各功能模块及其汇编指令深入了解,要做到这点是很难的。
11.3
MPLAB-IDE内挂接PICC
PICC编译器可以直接挂接在MPLAB-IDE集成开发平台下,
实现一体化的编译连接和
原代码调试。使用MPLAB-IDE内的调试工具ICE2000.ICD2
和软件模拟器都可以实现原
代码级的程序调试,非常方便。
首先必须在你的计算机中安装PICC编译器,无论是完全版还
是学习版都可以和
MPLABTDE挂接。安装成功后可以进入IDE,选择菜单项
ProjectSetLanguageTool
Locations---,打开语言工具挂接设置对话框,如图11-1所
示:
图11-1MPLAB-IDE语言工具设置对话框
在对话框中选择wHI-TECHPICCToolsuite”栏,展开可执
行文件组“Executable”后,
列出了将被MPLAB-IDE后台调用的编译器所用到的所
有可执行文件,其中有汇编编译器
“PICCAssembler"、C原程序编译器“PICCCompiler”
和连接定位程序"PICCLinker”。同
时在此列表中还显示了对应的可执行程序名,请注意在这里
都是“PICC.EXE"。用鼠标分别
点击选中这三项可执行文件,观察对话框下面“Location”
一栏中显示的文件路径,用
“Browse…”按纽,从计算机中已经安装的PICC编译器文
件夹中选择PICC.EXE文件。实
际上PICC.EXE只是一个调度管理程序,它会按照所输入的
文件扩展名自动调用对应的编译
器和连接器,用户要注意的是C语言原程序扩展名用
“.c”,汇编原程序用“.as”即可。
工具挂接完成后,在建立项目时可以选择语言工具为
uHI-TECHPICC”,具体步骤可以
参阅第三章3.1.3节,此处不再重复。项目建立完成后可
以加入C或汇编原程序,也可以加
入已有的库文件或已经编译的目标文件。最常见的是只加入
C原程序。用C语言编程的好
处是可以实现模块化编程。程序编写者应尽量把相互独立的
控制任务用多个独立的C原程序文件实
现,如果程序量较大,一般不要把所有的代码写在一个文件
内。
图11-2列出的是笔者建立的一个项目中所有C原程序模
块,其中主控、数值计算、12c总线操
作、命令按键处理和液晶显示驱动等不同的功能分别在不同
的独立的原程序模块中实现。
图H-2c语言多模块编程
11.4PIC单片机的C语言原程序基本框架
基于PICC编译环境编写PIC单片机程序的基本方式和标
准C程序类似,程序一般由以
下几个主要部分组成:
&01540;在程序的最前面用#include预处理指令引用
包含头文件,其中必须包含一个编译器
提供的“pic.h”文件,实现单片机内特殊寄存器和其它特
殊符号的声明;
&01540;用“—CONFIG”预处理指令定义芯片的配置
位;
&01540;声明本模块内被调用的所有函数的类型,PICC
将对所调用的函数进行严格的类型
匹配检查;
&01540;定义全局变量或符号替换;
&01540;实现函数(子程序),特别注意main函数必
须是一个没有返回的死循环。
下面的例11T为一个C原程序的范例,供大家参考。
ttinclude〃包含单片机内部资源预定义
ttinclude“pc68.h”〃包含自定义头文件
〃定义芯片工作时的配置位
CONFIG(HS&PROTECT&PWRTEN&BOREN&WDTDIS);
〃声明本模块中所调用的函数类型
voidSetSFR(void);
voidClock(void);
voidKeyScan(void);
voidMeasure(void);
voidLCDTest(void);
voidLCDDisp(unsignedchar);
〃定义变量
unsignedcharsecond,minute,hour;
bitflagl,flag2;
〃函数和子程序
voidmain(void)
(
SetSFRO;
PORTC=0x00;
TMR1H+=TMR1H_CONST;
LED1=LED_OFF;
LCD_Test();
〃程序工作主循环
while(1){
asm("clrwdt”);
Clock();
KeyScan();
Measure();
SetSFRO;
)
〃清看门狗
〃更新时钟
〃扫描键盘
〃数据测量
〃刷新特殊功能寄存器
11.5
PICC中的变量定义
例n-ic语言原程序框架举例
11.5.1PICC中的基本变量类型
PICC遵循Little-endian标准,多字节变量的低字节放
在存储空间的低地址,高字节放
在「司地址。
11.5.2PICC中的高级变量
基于表11-1的基本变量,除了bit型位变量外,PICC完
全支持数组、结构和联合等复
合型高级变量,这和标准的C语言所支持的高级变量类型
没有什么区别。例如:
数组:unsignedintdata[10];
结构:structcommlnData{
unsignedcharinBuff[8];
unsignedchargetPtr,putPtr;
);
联合:unionint_Byte{
unsignedcharc[2];
unsignedinti;
};
例11~2C语言高级变量举例
11.5.3PICC对数据寄存器bank的管理
为了使编译器产生最高效的机器码,PICC把单片机中数据
寄存器的bank问题交由编程
员自己管理,因此在定义用户变量时你必须自己决定这些变
量具体放在哪一个bank中。如
果没有特别指明,所定义的变量将被定位在bankO,例如下
面所定义的这些变量:
unsignedcharbuffer[32];
bitflagl,flag2;
floatval[8];
除了bankO内的变量声明时不需特殊处理外,定义在其它
bank内的变量前面必须加上
相应的bank序号,例如:
banklunsignedcharbuffer[32];〃变量定位在
bankl中
bank2bitflagl,flag2;
bank3floatval[8];
〃变量定位在bank2中
〃变量定位在bank3中
中档系列PIC单片机数据寄存器的一个bank大小为128
字节,刨去前面若干字节的特
殊功能寄存器区域,在C语言中某一bank内定义的变量
字节总数不能超过可用RAM字节
数。如果超过bank容量,在最后连接时会报错,大致信息
如下:
Error[000]:Can'tfind0xl2Cwordsforpsectrbssl
insegmentBANK1
连接器告诉你总共有0xl2C(300)个字节准备放到bankl
中但bankl容量不够。显然,只
有把一部分原本定位在bankl中的变量改放到其它bank
中才能解决此问题。
虽然变量所在的bank定位必须由编程员自己决定,但在编
写原程序时进行变量存取操
作前无需再特意编写设定bank的指令。C编译器会根据所
操作的对象自动生成对应bank设
定的汇编指令。为避免频繁的bank切换以提高代码效率,
尽量把实现同一任务的变量定位
在同一个bank内;对不同bank内的变量进行读写操作时
也尽量把位于相同bank内的变量
归并在一起进行连续操作。
11.5.4PICC中的局部变量
PICC把所有函数内部定义的auto型局部变量放在
bankOo为节约宝贵的存储空间,它
采用了一种被叫做“静态覆盖”的技术来实现局部变量的
地址分配。其大致的原理是在编译
器编译原代码时扫描整个程序中函数调用的嵌套关系和层
次,算出每个函数中的局部变量字
节数,然后为每个局部变量分配一个固定的地址,且按调用
嵌套的层次关系各变量的地址可
以相互重叠。利用这一技术后所有的动态局部变量都可以按
已知的固定地址地进行直接寻
址,用PIC汇编指令实现的效率最高,但这时不能出现函
数递归调用。PICC在编译时会严
格检查递归调用的问题并认为这是一个严重错误而立即终
止编译过程。
既然所有的局部变量将占用banko的存储空间,因此用户
自己定位在bankO内的变量字
节数将受到一定的限制,在实际使用时需注意。
11.5.5PICC中的位变量
bit型位变量只能是全局的或静态的。PICC将把定位在同
一bank内的8个位变量合并
成一个字节存放于一个固定地址。因此所有针对位变量的操
作将直接使用PIC单片机的位
操作汇编指令高效实现。基于此,位变量不能是局部自动型
变量,也无法将其组合成复合型
高级变量。
PICC对整个数据存储空间实行位编址,0x000单元的第0
位是位地址0x0000,以此后
推,每个字节有8个位地址。编制位地址的意义纯粹是为
了编译器最后产生汇编级位操作指
令而用,对编程人员来说基本可以不管。但若能了解位变量
的位地址编址方式就可以在最后
程序调试时方便地查找自己所定义的位变量,如果一个位变
量flagl被编址为0x123,那么
实际的存储空间位于:
字节地址=0x123/8=0x24
位偏移=0xl23%8=3
即flagl位变量位于地址为0x24字节的第3位。在程序
调试时如果要观察flagl的变化,必
须观察地址为0x24的字节而不是0xl23o
PIC单片机的位操作指令是非常高效的。因此,PICC在编
译原代码时只要有可能,对
普通变量的操作也将以最简单的位操作指令来实现。假设一
个字节变量tmp最后被定位在
地址0x20,那么
tmp|=0x80
tmp&二0xf7
=>bsf
=>bcf
0x20,7
0x20,3
if(tmp&Oxfe)
=>btfsc0x20,0
即所有只对变量中某一位操作的C语句代码将被直接编译
成汇编的位操作指令。虽然编程
时可以不用太关心,但如果能了解编译器是如何工作的,那
将有助于引导我们写出高效简介
的C语言原程序。
在有些应用中需要将一组位变量放在同一个字节中以便需
要时一次性地进行读写,这一
功能可以通过定义一个位域结构和一个字节变量的联合来
实现,例如:
union{
struct{
unsignedbO:1;
unsignedbl:1;
unsignedb2:1;
unsignedb3:1;
unsignedb4:1;
unsignedb5:1;
unsigned:2;〃最高两位保留
}oneBit;
unsignedcharallBits;
}myFlag;
例n-3定义位变量于同一字节
需要存取其中某一位时可以
myFlag.oneBit.b3=l;//b3位置1
一次性将全部位清零时可以
myFlag.allBits=0;〃全部位变量清0
当程序中把非位变量进行强制类型转换成位变量时,要注意
编译器只对普通变量的最低
位做判别:如果最低位是0,则转换成位变量0;如果最低
位是1,则转换成位变量lo而标
准的ANSI-C做法是判整个变量值是否为0o另外,函数可
以返回一个位变量,实际上此返
回的位变量将存放于单片机的进位位中带出返回。
11.5.6PICC中的浮点数
PICC中描述浮点数是以IEEE-754标准格式实现的。此标
准下定义的浮点数为32位长,
在单片机中要用4个字节存储。为了节约单片机的数据空
间和程序空间,PICC专门提供了
一种长度为24位的截短型浮点数,它损失了浮点数的一点
精度,但浮点运算的效率得以提
高。在程序中定义的float型标准浮点数的长度固定为24
位,双精度double型浮点数一般
也是24位长,但可以在程序编译选项中选择double型浮
点数为32位,以提高计算的精度。
一般控制系统中关心的是单片机的运行效率,因此在精度能
够满足的前提下尽量选择
24位的浮点数运算。
11.5.7PICC中变量的绝对定位
首先必须强调,在用C语言写程序时变量一般由编译器
和连接器最后定位,在写程序
之时无需知道所定义的变量具体被放在哪个地址(除了
bank必须声明)。
真正需要绝对定位的只是单片机中的那些特殊功能寄存器,
而这些寄存器的地址定位在
PICC编译环境所提供的头文件中已经实现,无需用户操心。
编程员所要了解的也就是PICC
是如何定义这些特殊功能寄存器和其中的相关控制位的名
称。好在PICC的定义标准基本上
按照芯片的数据手册中的名称描述进行,这样就秉承了变量
命名的一贯性。一个变量绝对定
位的例子如下:
unsignedchartmpData@0x20;//tmpData定位在地址
0x20
千万注意,PICC对绝对定位的变量不保留地址空间。换句
话说,上面变量tmpData的
地址是0x20,但最后0x20处完全有可能又被分配给了其
它变量使用,这样就发生了地址冲
突。因此针对变量的绝对定位要特别小心。从笔者的应用经
验看,在一般的程序设计中用户
自定义的变量实在是没有绝对定位的必要。
如果需要,位变量也可以绝对定位。但必须遵循上面介绍的
位变量编址的方式。如果一
个普通变量已经被绝对定位,那么此变量中的每个数据位就
可以用下面的计算方式实现位变
量指派:
unsignedchartmpData@0x20;//tmpData定位在地址
0x20
bittmpBitO@tmpData*8+0;//tmpBitO对应于
tmpData第0位
bittmpBitl@tmpData*8+l;//tmpBitO对应于
tmpData第1位
bittmpBit2@tmpData*8+2;//tmpBitO对应于
tmpData第2位
如果tmpData事先没有被绝对定位,那就不能用上面的位
变量定位方式。
11.5.8PICC的其它变量修饰关键词
&01540;extern一外部变量声明
如果在一个C程序文件中要使用一些变量但其原型定义
写在另外的文件中,那么在本
文件中必须将这些变量声明成"extern”外部类型。例如程
序文件codel.c中有如下定义:
banklunsignedcharvarl,var2;
〃定义了bankl中的两个变量
在另外一个程序文件code2.c中要对上面定义的变量进行
操作,则必须在程序的开头定义:
externbanklunsignedcharvarl,var2;〃声明
位于bankl的外部变量
&01540;volatile一易变型变量声明
PICC中还有一个变量修饰词在普通的C语言介绍中一
般是看不到的,这就是关键词
“volatile”。顾名思义,它说明了一个变量的值是会随机
变化的,即使程序没有刻意对它进
行任何赋值操作。在单片机中,作为输入的10端口其内容
将是随意变化的;在中断内被修
改的变量相对主程序流程来讲也是随意变化的;很多特殊功
能寄存器的值也将随着指令的运
行而动态改变。所有这种类型的变量必须将它们明确定义成
“volatile”类型,例如:
volatileunsignedcharSTATUS@0x03;
volatilebitcommFlag;
"volatile"类型定义在单片机的C语言编程中是
如此的重要,是因为它可以告诉编译
器的优化处理器这些变量是实实在在存在的,在优化过程中
不能无故消除。假定你的程序定
义了一个变量并对其作了一次赋值,但随后就再也没有对其
进行任何读写操作,如果是非
volatile型变量,优化后的结果是这个变量将有可能被彻
底删除以节约存储空间。另外一种
情形是在使用某一个变量进行连续的运算操作时,这个变量
的值将在第一次操作时被复制到
中间临时变量中,如果它是非volatile型变量,则紧接其
后的其它操作将有可能直接从临时
变量中取数以提高运行效率,显然这样做后对于那些随机变
化的参数就会出问题。只要将其
定义成volatile类型后,编译后的代码就可以保证每次操
作时直接从变量地址处取数。
&01540;const—常数型变量声明
如果变量定义前冠以“const”类型修饰,那么所有这些变
量就成为常数,程序运行过
程中不能对其修改。除了位变量,其它所有基本类型的变量
或高级组合变量都将被存放在程
序空间(ROM区)以节约数据存储空间。显然,被定义在ROM
区的变量是不能再在程序
中对其进行赋值修改的,这也是“const”的本来意义。实
际上这些数据最终都将以“retlw”
的指令形式存放在程序空间,但PICC会自动编译生成相关
的附加代码从程序空间读取这些
常数,编程员无需太多操心。例如:
constunsignedcharname口=“Thisisademo";//定
义一个常量字符串
如果定义了“const”类型的位变量,那么这些位变量还
是被放置在RAM中,但程序
不能对其赋值修改。本来,不能修改的位变量没有什么太多
的实际意义,相信大家在实际编
程时不会大量用到。
&01540;persistent一非初始化变量声明
按照标准C语言的做法,程序在开始运行前首先要把所
有定义的但没有预置初值的变
量全部清零。PICC会在最后生成的机器码中加入一小段初
始化代码来实现这一变量清零操
作,且这一操作将在main函数被调用之前执行。问题是作
为一个单片机的控制系统有很多
变量是不允许在程序复位后被清零的。为了达到这一目的,
PICC提供了“persistent”修饰
词以声明此类变量无需在复位时自动清零,编程员应该自己
决定程序中的那些变量是必须声
明成“persisted类型,而且须自己判断什么时候需要对
其进行初始化赋值。例如:
persistentunsignedcharhour,minute,second;〃定义
时分秒变量
经常用到的是如果程序经上电复位后开始运行,那么需要将
persistent型的变量初始化,
如果是其它形式的复位,例如看门狗引发的复位,则无需对
persistent型变量作任何修改。
PIC单片机内提供了各种复位的判别标志,用户程序可依
具体设计灵活处理不同的复位情
形。
11.5.9PICC中的指针
PICC中指针的基本概念和标准C语法没有太多的差别。但
是在PIC单片机这一特定的
架构上,指针的定义方式还是有儿点需要特别注意。
&01540;指向RAM的指针
如果是汇编语言编程,实现指针寻址的方法肯定就是用FSR
寄存器,PICC也不例外。
为了生成高效的代码,PICC在编译C原程序时将指向RAM
的指针操作最终用FSR来实现
间接寻址。这样就势必产生一个问题:FSR能够直接连续寻
址的范围是256字节(bankO/1
或bank2/3),要覆盖最大512字节的内部数据存储空间,
又该如何让定义指针?PICC还是
将这一问题留给编程员自己解决:在定义指针时必须明确指
定该指针所适用的寻址区域,例
如:
unsignedchar*ptrO;〃①定义覆盖bankO/1的指针
bank2unsignedchar*ptrl;〃②定义覆盖bank2/3的
指针
bank3unsignedchar*ptr2;〃③定义覆盖bank2/3的
指针
上面定义了三个指针变量,其中①指针没有任何bank限
定,缺省就是指向bankO和bankl;
②和③一个指明了bank2,另一个指明了bank3,但实际上
两者是一样的,因为一个指针可
以同时覆盖两个bank的存储区域。另外,上面三个指针变
量自身都存放在bankO中。我们
将在稍后介绍如何在其它bank中存放指针变量。
既然定义的指针有明确的bank适用区域,在对指针变量赋
值时就必须实现类型匹配,
F面的指针赋值将产生一个致命错误:
unsignedchar*ptrO;
bank2unsignedcharbuff[8];
程序语句:
〃定义指向bankO/1的指针
〃定义bank2中的一个缓冲区
ptrO二buff;〃错误!试图将bank2内的变量地址赋给
指向bankO/1的指针
若出现此类错误的指针操作,PICC在最后连接时会告知类
似于下面的信息:
Fixupoverflowinexpression(...)
同样的道理,若函数调用时用了指针作为传递参数,也必须
注意bank作用域的匹配,
而这点往往容易被忽视。假定有下面的函数实现发送一个字
符串的功能:
voidSendMessage(unsignedchar*);
那么被发送的字符串必须位于bankO或bankl中。如果你
还要发送位于bank2或bank3内的
字符串,必须再另外单独写一个函数:
voidSendMessage_2(bank2unsignedchar*);
这两个函数从内部代码的实现来看可以一模一样,但传递的
参数类型不同。
按笔者的应用经验体会,如果你看到了“Fixupoverflow"
的错误指示,几乎可以肯定
是指针类型不匹配的赋值所至。请重点检查程序中有关指针
的操作。
&01540;指向ROM常数的指针
如果一组变量是已经被定义在ROM区的常数,那么指向它
的指针可以这样定义:
constunsignedcharcompanyMicrochipv;
constunsignedchar*romPtr;
程序中可以对上面的指针变量赋值和实现取数操作:
romPtr=company;〃指针赋初值
data=*romPtr++;〃取指针指向的一个数,然后指针加1
〃定义ROM中的常数
〃定义指向ROM的指针
反过来,下面的操作将是一个错误,因为该指针指向的是常
数型变量,不能赋值。
*romPtr=data;〃往指针指向的地址写一个数
&01540;指向函数的指针
单片机编程时函数指针的应用相对较少,但作为标准c语
法的一部分,PICC同样支持
函数指针调用。如果你对编译原理有一定的了解,就应该明
白在PIC单片机这一特定的架
构上实现函数指针调用的效率是不高的:PICC将在RAM中
建立一个调用返回表,真正的
调用和返回过程是靠直接修改PC指针来实现的。因此,除
非特殊算法的需要,建议大家尽
量不要使用函数指针。
&01540;指针的类型修饰
前面介绍的指针定义都是最基本的形式。和普通变量一样,
指针定义也可以在前面加上
特殊类型的修饰关键词,例如“persistent”、
“volatile”等。考虑指针本身还要限定其作用域,
因此PICC中的指针定义初看起来显得有点复杂,但只要了
解各部分的具体含义,理解一个
指针的实际用图就变得很直接。
㈠bank修饰词的位置含义
前面介绍的一些指针有的作用于bankO/1,有的作用于
bank2/3,但它们本身的存放位置
全部在bankOo显然,在一个程序设计中指针变量将有可能
被定位在任何可用的地址空间,
这时,bank修饰词出现的位置就是一个关键,看下面的例
子:
〃定义指向bankO/1的指针,指针变量为于bankO中
unsignedchar*ptrO;
〃定义指向bank2/3的指针,指针变量为于bankO中
bank2unsignedchar*ptrO;
〃定义指向bank2/3的指针,指针变量为于bankl中
bank2unsignedchar*banklptrO;
从中可以看出规律:前面的bank修饰词指明了此指针的作
用域;后面的bank修饰词定义了
此指针变量自身的存放位置。只要掌握了这一法则,你就可
以定义任何作用域的指针且可以
将指针变量放于任何bank中。
㈡volatile、persistent和const修饰词的位置含义
如果能理解上面介绍的bank修饰词的位置含义,实际上
volatile、persistent和const这
些关键词出现在前后不同位置上的含义规律是和bank一
词相一致的。例如:
〃定义指向bankO/1易变型字符变量的指针,指针变量位
于bankO中且自身为非易变型
volatileunsignedchar*ptrO;
〃定义指向bank2/3非易变型字符变量的指针,指针变量
位于bankl中且自身为易变型
bank2unsignedchar*volatilebanklptrO;
〃定义指向ROM区的指针,指针变量本身也是存放于ROM
区的常数
constunsignedchar*constptrO;
亦即出现在前面的修饰词其作用对象是指针所指处的变量;
出现在后面的修饰词其作用对象
就是指针变量自己。
11.6
PICC中的子程序和函数
中档系列的PIC单片机程序空间有分页的概念,但用C语
言编程时基本不用太多关心
代码的分页问题。因为所有函数或子程序调用时的页面设定
(如果代码超过一个页面)都由
编译器自动生成的指令实现。
11.6.1函数的代码长度限制
PICC决定了C原程序中的一个函数经编译后生成的机器
码一定会放在同一个程序页面
内。中档系列的PIC单片机其一个程序页面的长度是2K
字,换句话说,用C语言编写的任
何一个函数最后生成的代码不能超过2K字。一个良好的程
序设计应该有一个清晰的组织结
构,把不同的功能用不同的函数实现是最好的方法,因此一
个函数2K字长的限制一般不会
对程序代码的编写产生太多影响。如果为实现特定的功能确
实要连续编写很长的程序,这时
就必须把这些连续的代码拆分成若干函数,以保证每个函数
最后编译出的代码不超过一个页
面空间。
11.6.2调用层次的控制
中档系列PIC单片机的硬件堆栈深度为8级,考虑中断响
应需占用一级堆栈,所
有函数调用嵌套的最大深度不要超过7级。编程员必须自
己控制子程序调用时的嵌套深
度以符合这一限制要求。
PICC在最后编译连接成功后可以生成一个连接定位映射文
件(*.map),在此文件
中有详细的函数调用嵌套指示图“callgraph”,建议大家
要留意一下。其信息大致如下
(取自于一示范程序的编译结果):
Callgraph:
*_mainsize0,0offset0
RightShiftC
*_Tasksize0,1offset0
Iwtoft
ftmulsize0,0offset0
ftunpack1
ftunpack2
ftaddsize0,0offset0
ftunpack1
ftunpack2
ftdenorm
例n-4c函数调用层次图
上面所举的信息表明整个程序在正常调用子程序时嵌套最
多为两级(没有考虑中断)。因为
main函数不可能返回,故其不用计算在嵌套级数中。其中
有些函数调用是编译代码时自动
加入的库函数,这些函数调用从c原程序中无法直接看出,
但在此嵌套指示图上则一目了
然。
11.6.3函数类型声明
PICC在编译时将严格进行函数调用时的类型检查。一个良
好的习惯是在编写程序代码
前先声明所有用到的函数类型。例如:
voidTask(void);
unsignedcharTemperature(void);
voidBIN2BCD(unsignedchar);
voidTimeDisplay(unsignedchar,unsignedchar);
这些类型声明确定了函数的入口参数和返回值类型,这样编
译器在编译代码时就能保证生成
正确的机器码。笔者在实际工作中有时碰到一些用户声称发
现C编译器生成了错误的代码,
最后究其原因就是因为没有事先声明函数类型所致。
建议大家在编写一个函数的原代码时,立即将此函数的类型
声明复制到原文件的起始
处,见例11T;或是复制到专门的包含头文件中,再在每
个原程序模块中引用。
11.6.4中断函数的实现
PICC可以实现C语言的中断服务程序。中断服务程序有一
个特殊的定义方法:
voidinterruptISR(void);
其中的函数名“ISR”可以改成任意合法的字母或数字组
合,但其入口参数和返回参数类型
必须是“void”型,亦即没有入口参数和返回参数,且中间
必须有一个关键词“interrupt”。
中断函数可以被放置在原程序的任意位置。因为已有关键词
“interrupt”声明,PICC在
最后进行代码连接时会自动将其定位到0x0004中断入口
处,实现中断服务响应。编译器也
会实现中断函数的返回指令“retfie"。一个简单的中断服
务示范函数如下:
voidinterruptISR(void)〃中断服务程序
if(TOIE&&TOIF)
TOIF=0;
〃在此加入TMR0中断服务
〃判TMRO中断
〃清除TMRO中断标志
if(TMR1IE&&TMR1IF)〃判TMR1中断
TMR1IFO;
〃在此加入TMR1中断服务
〃清除TMR1中断标志
〃中断结束并返回
例11-5C语言中断函数举例
PICC会自动加入代码实现中断现场的保护,并在中断结束
时自动恢复现场,所以编程
员无需象编写汇编程序那样加入中断现场保护和恢复的额
外指令语句。但如果在中断服务程
序中需要修改某些全局变量时,是否需要保护这些变量的初
值将由编程员自己决定和实施。
用C语言编写中断服务程序必须遵循高效的原则:
&01540;代码尽量简短,中断服务强调的是一个“快”
字。
&01540;避免在中断内使用函数调用。虽然PICC允许
在中断里调用其它函数,但为了解决
递归调用的问题,此函数必须为中断服务独家专用。既如此,
不妨把原本要写在其
它函数内的代码直接写在中断服务程序中。
&01540;避免在中断内进行数学运算。数学运算将很有
可能用到库函数和许多中间变量,就
算不出现递归调用的问题,光在中断入口和出口处为了保护
和恢复这些中间临时变
量就需要大量的开销,严重影响中断服务的效率。
中档系列PIC单片机的中断入口只有一个,因此整个程序
中只能有一个中断服务函数。
11.6.5标准库函数
Pice提供了较完整的C标准库函数支持,其中包括数学运
算函数和字符串操作函数。
在程序中使用这些现成的库函数时需要注意的是入口参数
必须在bankO中。
如果需要用到数学函数,则应在程序前“include”
包含头文件;如果要使
用字符串操作函数,就需要包含“include”头文件。在
这些头文件中提供了函数
类型的声明。通过直接查看这些头文件就可以知道PICC提
供了哪些标准库函数。
C语言中常用的格式化打印函数“printf/sprintf”用在
单片机的程序中时要特别谨慎。
printf/sprintf是一个非常大的函数,一旦使用,你的程
序代码长度就会增加很多。除非是在
编写试验性质的代码,可以考虑使用格式化打印函数以简化
测试程序;一般的最终产品设计
都是自己编写最精简的代码实现特定格式的数据显示和输
出。本来,在单片机应用中输出的
数据格式都相对简单而且固定,实现起来应该很容易。
对于标准C语言的控制台输入(scanf)/输出(printf)
函数,PICC需要用户自己编写
其底层函数getchO和putchOo在单片机系统中实现
scanf/printf本来就没什么太多意义,如
果一定要实现,只要编写好特定的getchO和putchO函
数,你就可以通过任何接口输入或输
出格式化的数据。
11.7
PICC定义特殊区域值
PICC提供了相关的预处理指令以实现在原程序中定义单片
机的配置字和标记单元。
11.7.1定义工作配置字
在原程序中定义PIC单片机工作配置字的重要性在前面章
节中已经阐述。在用PICC写
程序时同样可以在c原程序中定义,具体方式如下:
_CONFIG(HS&UNPROTECT&PWRTEN&BORDIS&WDTEN);
上面的关键词“—CONFIG”(注意前面有两个下划线符)专
门用于是芯片配置字的设
定,后面括号中的各项配置位符号在特定型号单片机的头文
件中已经定义(注意不是pic.h
头文件),相互之间用逻辑“与”操作符组合在一起。这样
定义的配置字信息最后将和程序
代码一起放入同一个HEX文件。
在这里列出了适用于16F7x系列单片机配置位符号预定
义,其它型号或系列的单片机
配置字定义方式类似,使用前查阅一下对应的头文件即可。
/*振荡器配置*/
#defineRC
^defineHS
0x3FFF//RC振荡
0x3FFE//HS模式
^defineXT
ttdefineLP
/*看门狗配置*/
Ox3FFD//XT模式
Ox3FFC//LP模式
ttdefineWDTEN0x3FFF//看门狗打开
#defineWDTDIS
/*上电延时定时器配置*/
ftdefinePWRTEN
0x3FFB//看门狗关闭
0x3FF7//上电延时定时
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 体检科继续教育培训制度
- 审计局病媒防治管理制度
- 中铁物资集团绩效考核制度
- 单位上财务支出规章制度
- 万科物业内部审计制度
- 审计加班调休制度规定
- 关于企业财务规章制度
- 县直部门选人用人制度
- 国企高管绩效考核制度
- 公司内部审计处罚制度
- 2026年陕西铁路工程职业技术学院单招职业适应性测试题库完整参考答案详解
- 2026年安徽国防科技职业学院单招职业技能考试题库及完整答案详解一套
- 2026年湖北生态工程职业技术学院单招综合素质考试题库带答案详解
- 《特大型突发地质灾害隐患点认定与核销管理办法(试行)》
- XX街道中学初中部2026年春季家长会中期筹备工作方案:筹备家长会搭建沟通平台
- 2025年时事政治必考试题库(附含答案)
- 2026年汽车制造机器人自动化率提升:趋势、技术与实践
- 作业条件危险性评价方法LEC及案例分析
- 初中英语中考短文填空题型考点精析与知识清单
- 城市公共交通运营与服务规范
- 2026年1月浙江省高考首考英语试卷真题完整版(含答案+听力)
评论
0/150
提交评论