《嵌入式C编程:PIC单片机和C编程技术与应用》一3.3 非标准编译指示

简介:

本节书摘来自华章出版社《嵌入式C编程:PIC单片机和C编程技术与应用》一书中的第3章,第3.3节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问云栖社区“华章计算机”公众号查看

3.3 非标准编译指示

3.3.1 #warning
它和#error相似,但只抛出警告信息,不会停止编译。
3.3.2 #use delay
该指令告诉编译器,硬件使用的是什么类型的晶振。编译器根据它为芯片设置正确的熔丝位,同时还会生成一些控制时间的内置函数。我们已经见过这类函数了,如delay_ms(time),其中的#use delay在文件e3.h中。这里给出了一些例子:


918bf73a9c9adaae7052818276f13f200cd94829

CCS C编译器的#use...指令驱使编译器在编译阶段根据程序员的指定选项生成新的内置函数。这些#use库包括RS-232、I2C、触摸板等,具体细节将会在本书后面部分介绍。
对于大多数非嵌入式的传统C编译器来说,通常都将像delay_ms这样的通用函数封装成函数库。但是由于PIC MCU的内存比较紧张,在编译时生成这些函数会更有效率,这样只有在程序员真正需要的时候才会被包含进来。
该指令对于整个程序都非常重要,因为有时编译器为了满足特定器件接口的需求,需要加入延时。例如,PIC的寄存器在设置和读取一个比特之间需要稍作延时。编译器从该指令中获取到时钟信息,从而知道如何实现延时。
有些芯片有双晶振以及其他的时钟特性。该指令能够处理所有器件的各种选项。
3.3.3 关于频率
频率的单位是赫兹(hertz),简写为Hz。1Hz表示每秒钟发生一次;2Hz表示每秒钟重复出现两次。下面给出了频率的常用单位:
Hz ???每秒1次
kHz ??每秒1000次
Mhz 每秒1?000?000次
gHz ??每秒1?000?000?000次
3.3.4 #use rs232(options)
该指令会创建出一组用于接收和发送串口异步数据的函数,有很多选项。下面给出个简单的例子:


e62373e182c2050ffe45334e6ba884b4cf77a1af

该指令会生成putc()和getc(),分别用来从C6、C7引脚以9600 Baud的波特率传输和接收数据。如果使用的是PIC的UART引脚,那么通信由硬件完成。否则,编译器会创建一个函数来实现bit-bang模式。第22章将会详细介绍异步通信和#use rs232指令。
3.3.5 #fuses options
options会根据硬件不同而变化,device.h文件中列出了相关硬件的选项。
PIC微控制器有一块非易失性内存用来存放配置信息。这块区域称为熔丝位或配置位,可以设置晶振类型,开关各种硬件特性,有的还可以设置某些引脚的功能。在下载程序到芯片上时,可以修改熔丝位。只有重新下载程序才能改变熔丝位的设置。不同芯片的设置内容不同。在IDE的VIEW>FUSES中,可以找到相应芯片支持哪些设置。使用#use delay指令,CCS C编译器会自动设置所需的晶振熔丝位。下面是所有芯片通用的两个常用熔丝位:


adddb7d8623338f139b4fba216c4c7a38777973f

在程序写入芯片之后,该熔丝位用来防止从芯片内存中读取程序。通常用于产品中,用来防止终端客户从芯片中复制程序。


c110bd72647fe9564d1f8580309ca4926e1de891

该熔丝位用于激活芯片的看门狗定时器(watchdog timer)。在该定时器没有达到预设值时,芯片正常工作。一旦定时器到期,芯片将会被复位。正常情况下,程序将会周期性地重置定时器(复位到起始状态)。只要定时器不停地被重置,程序就会一直运行下去。如果程序卡在某个地方,看门狗定时器得不到重置,就会在到期之后重启程序。函数setup_wdt()用来设置定时器时间,restart_wdt()用来重置定时器。
3.3.6 #locate id = address
id 一个有效的C标识符
address PIC微控制器数据内存中的地址
通常情况下,定义一个C变量之后,编译器在RAM中寻找一个空位置来存放该变量。该指令能够强制将变量放置到内存中的某个特殊位置。我们可以使用它将C变量指向PIC的特定功能寄存器。例如,E3板子上的PIC控制器的状态寄存器位于RAM中的4056位置。下面这段代码创建了一个变量指向该位置:


2aa05db82826a5498562f988cfe8d7ec8eb3cfbc

那么,后面的这行代码就可以将状态寄存器清零:


2cf6be5286c635dfa8c9ea96b2c17b7a72d0380b

CCS C编译器中的getenv()宏可以用于获取PIC寄存器的位置。下面这条指令和前面的#locate功能一样,并且可以用于所有的芯片:


59e3350de93e75281039c21d222b806a8a5688a5

虽然C语言的ANSI标准中不允许这么声明,但是C语言还有个嵌入式扩展。在该扩展标准中,对于嵌入式程序,我们将其称为named register(有名寄存器)。CCS C编译器支持该扩展,而且,很少有编译器支持它。下面是使用named register的另外一种方法:


c231e851e92d321f3f63c7b9a6bc37900261b14e

下划线前的名字(register)就是寄存器的地址。
3.3.7 #byte id=x和#word id=x
id 一个有效的C标识符
x  一个C变量或常量
该指令功能和#locate一样,但除了定位变量,它还声明了一个C变量。#byte用于声明8位的变量,#word则是16位。下面这行指令和前面的#locate功能一样:

3.3.8 #bit id=x.y
id 一个有效的C标识符
x 一个C变量或常量
y 可以是0~7中的任意一个数
该指令功能和#byte类似,但是它只能声明一位的变量。C变量的每个字节都可以对应到内存中的每位,Bit0就是一个字节的最低位。例如:


7b3bf4d53a8e43e13990377009c6db914a51c86f

3.3.9 #reserve address
该指令用于在RAM中预留空间,编译器不会使用这段空间。当同一个芯片中还存在其他程序(如bootloader)时,我们会用到该指令。
3.3.10 引导加载程序
引导加载程序(bootloader)是位于程序存储器中的一段独立的程序,通常用于向程序存储器中加载应用程序。引导加载程序使用串口、USB口,或者一些其他手段来加载程序。通常,在重启的时候会启动bootloader,用于检测是否需要加载新程序或者程序是否开始运行。有时,bootloader也提供一些基础功能给应用程序使用。以本书的E3板子为例,bootloader用于加载应用程序并提供了USB相关函数,以帮助应用程序和PC之间传输数据。
3.3.11 #rom address={data}
该指令用于在芯片内存的特定位置写入数据。最常见的用法是初始化芯片上的EEPROM。许多PIC微控制器中都有一个内部数据存储器,在掉电之后仍然能够保存数据。编译器为我们提供了函数来读写这些存储器;然而,有时在程序启动前需要在内存中写入某些数据。该存储器地址在每个芯片上都不一样,预编译宏可以帮助我们在预编译阶段获取到这些地址。getenv()可以返回很多信息,本例中用它来获取EEPROM的数据起始地址:


a870d25404acbd3bea5a11976d687dd2fe4c5940

对于PIC16F887,该指令相当于:


0c213706f21895c702d53dec6d7313b4638fa13f

rom还有个特性,能够计算整个程序寄存器的校验和并将其存储到某个位置。在应用程序中使用该指令周期性地计算程序寄存器的校验和并与最初的值相比,从而保证内存中的内容没有变动。如果想将校验和存储在内存的最后位置,可以这么做:


9cbb9699d671b2ba51464f4ddae96e0fc7be19a9

因为地址是从0开始的,所以这里需要-1。如果芯片上有1024字的内存,它的地址范围就是0~1023。
这个校验和计算的是除存放校验和的内存之外的整个内存。
rom的另一个应用是在指定地址中存放数据,因为这些数据可能会根据外部条件发生变化。例如,某些特殊客户可能需要在产品中使用加密代码。#rom还可以用于指定设备的配置数据,如产品中所用引脚的数量。在程序中可以通过函数read_program_memory()读取#rom写入的数据,该函数使用程序存储器的绝对地址来读取数据。
3.3.12 #id data
许多芯片上还有一块特殊内存(可能只有一个字长),程序员可以在其中添加某种类型的标识。即使程序存储器由于读保护而不能读的情况下,这块区域仍然可以读取。因此,它可以用来存储固件版本或者存放校验和:


cd490d5c3ed14eb4908796f974bb722074e085ce

3.3.13 其他编译指示
下面是其他一些常用的预编译指令。在编译器参考手册的预编译器小节中可以找到完整的指令列表。

c8c38d061283213655207b4521a85c5edf2f8cc0
相关文章
|
10月前
|
人工智能 自然语言处理 测试技术
嵌入式开发者的灵魂拷问:通义灵码2.0能否Hold住51单片机竞赛级开发?
通义灵码2.0嵌入式开发专项评测,基于蓝桥杯第十二届单片机赛题(NE555频率检测),验证多文件代码生成及单元测试智能体能力。评测结果显示,AI在基础场景中具备实用性,但存在硬件抽象层缺陷和图像识别局限。原始得分58.1/70,主要问题为LED状态异常。完整代码已开源。
336 3
|
传感器 存储 物联网
单片机的原理与应用
单片机是一种将计算机的CPU、存储器、输入输出接口等功能集成在一块芯片上的微型计算机,被广泛应用于各类控制系统和智能设备中。
722 5
|
物联网 Linux 芯片
学习嵌入式是选择单片机还是Linux?
单片机通常指的是一种集成了处理器、存储器和各种外设接口的微控制器芯片,常见的有STC、51系列、AVR、ARM Cortex-M等。单片机具有低成本、低功耗、实时性强等特点,适用于对资源要求较低、功耗要求较低、实时性要求较高的嵌入式系统。学习单片机开发可以让您深入了解嵌入式系统的底层原理和硬件编程,对于对嵌入式系统底层开发感兴趣的人来说,是一个很好的选择。
383 4
|
Web App开发 传感器 Linux
【嵌入式软件工程师面经】STM32单片机
【嵌入式软件工程师面经】STM32单片机
583 1
|
算法 定位技术 C语言
51单片机实现俄罗斯方块游戏编程
设计了一款基于AT89C51单片机的俄罗斯方块游戏机,使用LCD12864液晶显示,按键控制方块移动与变形。游戏中,7种不同形状的方块随机下落,填满一行得分,满屏则游戏结束。包含电源、单片机最小系统、LCD和按键模块的硬件电路通过Proteus进行了仿真,展示了游戏运行、得分和计时等。代码部分展示了检查碰撞和更新地图的函数。
383 1
|
传感器 存储 程序员
《单片机原理与应用及C51编程技术》期末复习笔记
《单片机原理与应用及C51编程技术》期末复习笔记
432 0
|
传感器 人工智能 物联网
单片机:编程实践与应用探索
单片机:编程实践与应用探索
258 4
|
存储 传感器 数据处理
单片机编程入门与实践
单片机编程入门与实践
247 2
|
存储 芯片
【期末不挂科-单片机考前速过系列P11】(第十一章:15题速过串行口的工作原理和应用)经典例题盘点(带图解析)
【期末不挂科-单片机考前速过系列P11】(第十一章:15题速过串行口的工作原理和应用)经典例题盘点(带图解析)
|
传感器 安全 物联网
单片机在智能家居系统中的应用研究
单片机在智能家居系统中的应用研究
700 1