kl800.com省心范文网

富士通学习


第 1 章 单片机基础 (本章介绍了单片机的特点,发展简史,发展趋势,分类以及应用,最近介绍了几种主流单片机) 第 1 节 什么是单片机及单片机的特点 单片机 特点 单片机是指在一块芯片上集成了 CPU,ROM,RAM,定时器/计时器和多种 I/O 接口电路等,具有一定规 模的微型计算机。单片机与通用微型计算机相比,在硬件结构,指令设置上均有独到之处。 单片机以其卓越的性能,得到了广泛的应用,已深入到各个领域。单片机应用在检测,控制,仪器仪表 等各个领域中,其主要特点如下: 1、小巧灵活,成本低,易于产品化,能够组装成各种智能式测控设备及智能仪器仪表。 2、可靠性好,应用范围广。单片机芯片本身是按工业控制测控环境要求设计的,抗干扰性强,能够适应 各种恶劣的环境,这是其它机种无法比拟的。 3、功能齐全,扩展性强,很容易构成各种规模的应用系统,控制功能强。单片机内部有掩模 ROM,内部 E2PROM 和外接 ROM 等形式,并可以很方便的扩展外部的 ROM,RAM 及 I/O 接口,与许多通用的微机接口芯 片兼容,给应用系统的设计和生产带来了极大的方便。 4、具有通信功能,可以很方便的实现多机和分布式控制,形成控制网络和远程控制。 5、单片机的功能是通用的,单片机主要作控制器使用,但功能上是通用的,可以像一般微处理器那样广 泛的应用在各个领域。 第 2 节 单片机的发展简史 单片机 发展简史 单片机作为微型计算机的一个重要分支,应用面很广,发展很快。自单片机诞生至今,已发展为上百种系 列的近千个机种。 如果将 8 位单片机的推出作为起点,那么单片机的发展历史大致可分为以下几个阶段 : (1) 第一阶段(1976-1978):单片机的控索阶段。以 Intel 公司的 MCS-48 为代表。MCS–48 的推出是在工控领域的控索,参与这一控索的公司还有 Motorola 、Zilog 等,都取得了满意的效果。这 就是 SCM 的诞生年代,“单机片”一词即由此而来。 (2) 第二阶段(1978-1982):单片机的完善阶段。Intel 公司在 MCS-48 的基础上推出了完善的,典型 的单片机系列 MCS –51。它在以下几个方面奠定了典型的通用总线型单片机体系结构。 ①完善的外部总线。MCS-51 设置了经典的 8 位单片机的总线结构,包括 8 位数据总线、16 位地址总线、 控制总线及具有很多机通信功能的串行通信接口。 ②CPU 外围功能单元的集中管理模式。 ③体现工控特性的位地址空间及位操作方式。 ④指令系统趋于丰富和完善,并且增加了许多突出控制功能的指令。 (3) 第三阶段(1982-1990):8 位单片机的巩固发展及 16 位单片机的推出阶段,也是单片机向微控制 器发展的阶段。Intel 公司推出的 MCS–96 系列单片机,将一些用于测控系统的模数转换器、程序运行监视器、脉宽调制器等纳

入片中,体现了单片机的微控制器特征。随着 MCS–51 系列的广应用,许多电气厂商竞相使用 80C51 为内核,将许多测控系统中使用的电路技术、接口技术、多 通道 A/D 转换部件、可靠性技术等应用到单片机中,增强了外围电路路功能,强化了智能控制的特征。 (4) 第四阶段(1990-):微控制器的全面发展阶段。随着单片机在各个领域全面深入地发展和应用,出 现了高速、大寻址范围、强运算能力的 8 位/16 位/32 位通用型单片机,以及小型廉价的专用型单片机。 第 3 节 单片机的发展趋势 单片机 发展趋势 目前,单片机正朝着高性能和多品种方向发展趋势将是进一步向着 CMOS 化、低功耗、小体积、大容量、高性能、低价格和外围电路内装化等几个方面发展。下面是单片机的主要 发展趋势。 (1)CMOS 技术。 近年,由于 CHMOS 技术的进步,大大地促进了单片机芯片采用 CMOS 技术进行设计和生 产。CMOS 芯片除了低功耗特性之外,还具有功耗的可控性,使单片机可以工作在功耗精细管理状态。因为 单片机芯片多数是采用 CMOS(金属栅氧化物)半导体工艺生产。CMOS 电路的特点是低功耗、高密度、低速 度、低价格。采用双极型半导体工艺的 TTL 电路速度快,但功耗和芯片面积较大。随着技术和工艺水平的 提高,又出现了 HMOS(高密度、高速度 MOS)和 CHMOS 工艺。CHMOS 和 HMOS 工艺的结合。目前生产的 CH MOS 电路已达到 LSTTL 的速度,传输延迟时间小于 2ns,它的综合优势已优于 TTL 电路。因而,在单片机 领域 CMOS 正在逐渐取代 TTL 电路。 (2)低功耗。单片机的功耗已下降许多,静态电流甚至降到 1uA 以下;使用电压在 3~6V 之间,完全能 够适应于电池工作。低功耗化的效应不仅是功耗低,而且带来了产品的高可靠性、高抗干扰能力以及产品 的便携化。 (3)低电压。几乎所有的单片机都有 WAIT、STOP 等省电运行方式。允许使用的电压范围越来越宽,一般 在 3~6V 范围内工作。低电压供电的单片机电源下限已可达 1~2V。目前 0.8V 供电的单片机已经问世。 (4)低噪声与高可靠性。为提高单片机的抗电磁干扰能力,使产品能适应恶劣的工作环境,满足电磁兼容 性方面更高标准的要求,各单片厂家在单片机内部电路中都采用了新的技术措施。

(5)大容量。以往单片机内的 ROM 为 1KB~4KB,RAM 为 64~128B。但在需要复杂控制的场合,该存储容量是不够的,必须进行外接扩充。为了适应这种领域的 要求,须运用新的工艺,使片内存储器大容量化。目前,单片机内 ROM 最大可达 64KB,RAM 最大为 2KB。 (6)高性能。主要是指进一步改变 CPU 的性能,加快指令运算的速度和提高系统控制的可靠性。采用精简 指令集(RISC)结构和流水线技术,可以大幅度提高运行速度。现指令速度最高者已达 100MIPS(Million Instruction Per Seconds,即兆指令每秒),并加强了位处理、中断和定时控制功能。这类单片机的运算

速度比标准的单片机高出 10 倍以上。由于这类单片机有极高的指令速度,可以使用软件模拟其 I/O 功能,由此引入了虚拟外设的新概 念。 (7)小容量、低价格。与上述相反,以 4 位、8 位机为中心的小容量、低价格化也是发展动向之一。这类单片机的用途是把以往用数字逻辑集成电 路组成的控制电路单片化,可广泛用于家电产品。 (8)外围电路内装。这也是单片机发展的主要方向。随着集成度的不断提高,有可能把众多的各种处围功 能器件集成在片内。除了一般必须具有的 CPU、ROM、RAM、定时器/计数器等以外,片内集成的部件还有模 /数转换器、DMA(直接存储器访问 )控制器、声音发生器、监视定时器、液晶显示驱动器、彩色电视机和录 像机用的锁相电路等。

(9)串行扩展技术。在很长一段时间里,通用型单片机通过三总线结构扩展外围器件成为单片机应用的主 流结构。随着低价位 OTPProgramble)及各种特殊类型片内程序存储器的发展,加之处围接口不断进入内, 推动了单片机“单片”应用结构的发展。特别是 I2C、SPI 等串行总线的引入,可以使单片机的引脚设计 得更少,单片机系统结构更加简化及规范化。 随着半导体集成工艺的不断发展,单片机的集成度将更高、体积将更小、功能将更强。在单片机家族中,8 0C51 系列是其中的佼佼者,加之 Intel 公司将其 MCS–51 系列中的 80C51 内核使用权以专利互换或出售 形式转让给全世界许多著名 IC 设计厂商,如 Philips、NEC、Atmel、AMD、华邦等,这些公司都在保持与 80C51 单片机兼容的基础上改善了 80C51 的许多特性。这样,80C51 就变成有众多制造厂商支持的、发展 出上百品种的大家族,现统称为 80C51 系列。80C51 单片机已成为单片机发展的主流。专家认为,虽然世 界上的 MCU 品种繁多,功能各异,开发装置也互不兼容,但是客观发展表明,80C51 可能最终形成事实上 的标准 MCU 芯片。 第 4 节 单片机的分类 单片机 分类 通用型 专用型 总线型 非总线型 工控型 家电型 单片机作为计算机发展的一个重要领域,根据目前的发展情况,从不同角度单片机大致可以分为通用型/ 专用型、总线型/非总线型及工控型/家电型。 (1) 通用型/专用型 这是按单片机适用范围来进行区分。通用型单片机不是为某种专门用途设计的;专用型单片机是针对一类 产品甚至某一个产品设计生产的,例如为了满足电子体温计的要求,在片内集成 ADC 接口等功能的温度测量控制电路。 (2) 总线型/非总线型 这是按单片机是否提供并行总线来进行区分。总线型单片机普遍设置有并行地址总线、数据总线、控制总 线,这些引脚用以扩展并行外围器件都可通过串行口与单片机连接,另外,许多单片机已把所需要的外围 器件及外设接口集成于一单片内,因此在许多情况下可以不需要并行扩展总线,大大减省封装成本和芯片 体积,这类单片机称为非总线型单片机。

(3)工控型/家电型 这是按照单片机大致应用的领域进行区分的。一般而言,工控型寻址范围大,运算能力 强;用于家电的单片机多为专用型,通常是小封装、低价格,外围器件和外设接口集成度高。显然,上述 分类并不是惟一的和严格的,还可以按照多种方法进行划分。 第 5 节 单片机的应用 单片机 应用 由于单片机具有显著的优点,它已成为科技领域的有力工具,人类生活的得力助手。它的应用遍及各个领 域,主要表现在以下几个方面: (1) 单片机在智能仪表中的应用 单片机广泛地用于各种仪器仪表,使仪器仪表智能化,并可以提高测量的自动化程度和精度,简化仪器仪 表的硬件结构,提高其性能价格比。 (2) 单片机在机电一体化中的应用 机电一体化是机械工业发展的方向,机电一体化产品是指集成机械技术、微电子技术、计算机技术于一体, 具有智能化特征的机电产品,例如微机控制的车床、钻床等。单片机作为产品中的控制器,能充分发挥它 的体积小、可靠性高、功能强等优点,可大大提高机器的自动化、智能化程度。 (3) 单片机在实时控制中的应用 单片机广泛地用于各种实时控制系统中。例如,在工业测控、航空航天、尖端武器、机器人等各种实时控 制系统中,都可以用单片机作为控制器。单片机的实时数据处理能力和控制功能,可使系统保持在最佳工 作状态,提高系统的工作效率和产品质量。

(4) 单片机在分布式多机系统中的应用 在比较复杂的系统中,常采用分布式多机系统。多机系统一般由若干台功能各异的单片机组成,各自完成 特定的任务,它们通过串行通信相互联系、协调工作。单片机在这种系统中往往作为一个终端机,安装在 系统的某些节点上,对现场信息进行实时的测量和控制。单片机的高可靠性和强抗干扰能力,使它可以置 于恶劣环境的前端工作。

(5) 单片机在日常生活中的应用 自从单片机诞生以后,它就步入了人类生活,如洗衣机、电冰箱、电子玩具、收录机等家用电器配上单片 机后,提高了智能化程度,增加了功能,倍受人们喜爱。单片机将使人类生活更加方便、舒适、丰富多彩。

综合所述,单片机已成为计算机发展和应用的一个重要方面。另一方面,单片机应用的重要意义还在于, 它从根本上改变了传统的控制系统设计思想和设计方法。从前必须由模拟电路或数字电路实现的大部分功 能,现在已能用单片机通过软件方法来实现了。这种软件代替硬件的控制技术也称为微控制技术,是传统 控制技术的一次革命。 第 6 节 主流单片机简介

MOTOROLA 单片机 NEC Atmel 富士通 目前已投放市场的主要单片机产品多达 70 多个系列,500 多个品种。这其中还不包括那些系统或整机厂商定制的专用单片机,及针对专门业务、专 门市场的单片机品种。这里仅对部分常见的和常用的单片机系列进行介绍。

(1)8051 单片机 最早由 Intel 公司推出的 8051/31 类单片机也是世界上用量最大的几种单片机之一。 由于 Intel 公司在嵌 入式应用方面将重点放在 186、386、奔腾等与 PC 类兼容的高档芯片的开发上,8051 类单片机主要由 Philips、三星、华邦等公司接手生产。这些公司都在 保持与 8051 单片机兼容的基础上改善了 8051 许多特点(如时序特性)。提高了速度、降低了时钟频率,放宽了电源电压的动态范围,降低了产品的价 格。 (2)MOTOROLA 单片机 MOTOROLA 是世界上最大的单片机厂商,品种全、选择余地大、新产品多是其特点。在 8 位机方面有 68HC 05 和升级产品 68HC08。68HC05 有 30 多个系列,200 多个品种,产量已超过 20 亿片。16 位机 68HC16 也有十多个品种。32 位单片机的 683XX 系列也有几十个品种。MOTOROLA 单片机特点之一是在同样速度下所用的时钟频率较 Intel 类单片机低得多,因而使得高频噪声低、抗干扰 能力强,更适合用于工业控制领域及恶劣的环境。 (3)Microchip 单片机 Microchip 单片机是市场份额增长最快的单片机。它的主要产品是 16C 系列 8 位单片机,CPU 采用 RISC 结构,仅 33 条指令,其高速度,低电压,低功耗,大电流 LCD 驱动能力和低价位 OTP 技术等都体现出单 片机产业的发展新趋势。且以低价位著称,一般单片机价格都在一美元以下。由美国 Microchip 公司推出 的 PIC 单片机系列产品,已有三种系列多种型号的产品问世,从电脑的外设,家电控制,电讯通信,智能 仪器,汽车电子到金融电子的各个领域都得到广泛的应用。Microchip 单片机没有掩膜产品,全都是 OTP 器件(近年已推出 FLASH 型单片机——编者注)。Microchip 强调节约成本的最优化设计、使用量大、档 次低、价格敏感的产品。 (4)Atmel 单片机 ATMEL 公司的 90 系列单片机是增强 RISC 内载 Flash 的单片机,通常简称为 AVR 单片机,90 系列单片 机是基于新的精简指令 RISC 结构的。 这种结构是在 90 年代开发出来的综合了半导体集成技术和软件性能 的新结构,这种结构使得在 8 位微处理器市场上 AVR 单片机具有最高 MIPS mw 能力 (5)NEC 单片机 NEC 单片机自成体系,以 8 位单片机 78K 系列产量最高,也有 16 位、32 位单片机。16 位以上单片机采 用内部倍频技术,以降低外时钟频率。有的单片机采用内置操作系统。NEC 的销售策略著重于服务大客户, 并投入相当大的技术力量帮助大客户开发产品。

(6)东芝单片机 东芝单片机从 4 位单片机到 64 位单片机,门类齐全。4 位机在家电领域仍有较大的市场。8 位机主要有 8 70 系列、90 系列等,该类单片机允许使用慢模式,采用 32K 时钟时功耗低至 10uA 数量级。CPU 内部多

组寄存器的使用,使得中断响应与处理更加快捷。东芝的 32 位单片机采用 MIPS3000A RISC 的 CPU 结构, 面向 VCD、数字相机、图像处理等市场。 (7)富士通单片机 富士通有 8 位、16 位和 32 位单片机,其中 8 位单片机主要有 3V 产品和 5V 产品,3V 产品应用于消费类 及便携设备,如空调、洗衣机、冰箱、电表、小家电等,5V 产品应用于工业及汽车电子。8 位单片机有 8L 和 8FX 两个系列,是市场上最常见的两个系列。16 位主流单片机有 MB90F387,MB90F462,MB90F548,MB 90F428 等,这些单片机主要是采用 64 脚或 100 脚 QFP 封装,1 路或多路 CAN 总线,并可外扩总线,适用 于电梯、汽车电子车身控制及工业控制等。32 位单片机采用 RISC 结构,主要产品有 MB91101A,它采用 10 0 脚 QFP 封装,超低成本,可外扩总线,适用于 POS 机、银行税控打印机等;MB91F362GA,208 脚 QFP 封装,CAN 总线,可外扩总线,适用于电力及工业控制等;MB91F364GA,120 脚 LQFP 封装,CAN 总线,I 2C 等丰富通讯接口,支持低成本的在线仿真技术(AccemiCMDE),广泛适用于要求高性能低成本的各种应 用。富士通公司注重于服务大公司、大客户,帮助大客户开发产品。

橡皮泥咖啡 2009-01-13 23:49:26 第 2 章 单片机结构与原理 (介绍了单片机典型结构——CISI,RISC,8FX 单片机的内部结构和原理) 第 1 节 单片机典型结构 CISC RISC cache 流水线 现有的单片机按照其采用的指令结构主要分为两大类:复杂指令集(CISC)和精简指令集(RISC)结构。C ISC 的英文全称“Complex Instruction Set Computer”,中文即(复杂指令集计算机);RISC 的英文全称 为“Reduced Instruction Set Computing” ,中文即(精简指令集计算机)。 两种指令结构的单片机其应 用不同之处,在于 CISC 结构的单片机数据线和指令线分时复用,即所谓冯.诺伊曼结构。它的指令丰富, 功能较强,但取指令和取数据不能同时进行,速度受限,价格亦高。采用 RISC 结构的单片机数据线和指 令线分离,即所谓哈佛结构。这使得取指令和取数据可同时进行,且由于一般指令线宽于数据线,使其指 令较同类 CISC 单片机指令包含更多的处理信息,执行效率更高,速度亦更快。同时,这种单片机指令多 为单字节,程序存储器的空间利用率大大提高,有利于实现超小型化。

2.1.1 CISC 长期以来,计算机性能的提高往往是通过增加硬件的复杂性来获得.随着集成电路技术.特别是 VLSI(超 大规模集成电路)技术的迅速发展,为了软件编程方便和提高程序的运行速度,硬件工程师采用的办法是 不断增加可实现复杂功能的指令和多种灵活的编址方式.甚至某些指令可支持高级语言语句归类后的复杂 操作.至使硬件越来越复杂,造价也相应提高.为实现复杂操作,微处理器除向程序员提供类似各种寄存 器和机器指令功能外.还通过存于只读存贮器(ROM)中的微程序来实现其极强的功能,处理在分析每一条指 令之后执行一系列初级指令运算来完成所需的功能,这种设计的型式被称为复杂指令集计算机(Complex Instruction Set Computer-CISC) 结构.一般 CISC 计算机所含的指令数目至少 300 条以上,有的甚至超 过 500 条.

2.1.2 RISC 80 年代开始,人们发现机器执行的指令中 85%左右的都是简单指令,复杂指令甚少,因此开始研制精简 指令系统计算机(RISC)。由于程序执行时间=程序中指令数 I*每条指令执行所用周期数 CPI*周期时间 T, 因此 RISC 技术主要从这三方面下手,对 I、CPI、T 进行优化改良,其措施如下: 1.采用多级指令流水线结构 采用流水线技术可使每一时刻都有多条指令重叠执行,以减小 CPI 的值,使 CPU 不浪费空周期。实例:P entium Ⅱ/Pro/Celeron 可同时发出执行五条指令,AMD-K6/K6-2 可同时发出六条指令。 2.选取机器中使用频率最高的简单指令及部分复杂指令 这样可减小时钟周期数量,提高 CPU 速度,其实质是减小 CPI 下的值实现。实例:选取运算指令、加载、 存储指令和转移指令作主指令集。 3.采用加载(Load)、存储(Store) 结构 只允许 Load 和 Store 指令执行存储器操作,其余指令均对寄存器操作。实例:Amd-K6/K6-2 、PⅡ/Cele ron/Pro 均支持对寄存器的直接操作和重新命名,并大大增加通用寄存器的数量。 4.延迟加载指令和转移指令 由于数据从存储器到寄存器存在二者速度差、转移指令要进行入口地址的计算,这使 CPU 执行速度大大受 限,因此,RISC 技术为保证流水线高速运行,在它们之间允许加一条不相关的可立即执行的指令,以提高 速度。实例:主要体现于预测执行、非顺序执行和数据传输等方面,除 Intel P54/55C 不支持,像 K6-2 、 PⅡ均支持。 5.采用高速缓存(cache)结构 为保证指令不间断地传送给 CPU 运算器,CPU 设置了一定大小的 Cache 以扩展存储器的带宽,满足 CPU 频繁取指需求,一般有两个独立 Cache ,分别存放“指令+数据”。实例:PⅡ/Celeron:16K+16K,AMDK6/K6-2 为 32K+32K,Cyrix MⅡ:64K( 实质上为 2 个 32K Cache,此作共享 Cache),PⅡ还加了 L2 Cach e,更是大幅提高了 CPU 速度。 两种结构的单片机各有自己的特点,目前市面上属于 CISC 结构的单片机有 Intel8051 系列、Motorola 和 M68HC 系列、Atmel 的 AT89 系列、台湾 Winbond( 华邦)W78 系列、荷兰 Pilips 的 PCF80C51 系列等; 属于 RISC 结构的有 Microchip 公司的 PIC 系列、Zilog 的 Z86 系列、Atmel 的 AT90S 系列、韩国三星 公司的 KS57C 系列 4 位单片机、台湾义隆的 EM-78 列等。一般来说,控制关系较简单的小家电,可以采 用 RISC 型单片机;控制关系较复杂的场合,如通讯产品、工业控制系统应采用 CISC 单片机。 第 2 节 8FX 单片机的内部结构和原理 8FX 单片机 整体结构 I2C CPU 核 UART-SIO LIN-UART 外围功能 静态模式 单片机的内部结构如图 2-1 所示。

图 2-1 MB95100 内部结构图 可以看出 8FX 系列单片机采用了 RISC 结构。地址和数据使用两个流水线操作,MCU 可以在一个时钟周期 内同时获取指令和访问数据。 8FX 系列单片机包括了 CPU 核,定时与控制逻辑部件,片内数据存储器(R AM),片内程序存储器(ROM),各种功能的寄存器,并行 I/O 端口,定时器/计时器,各种总线接口,中 断系统及复位系统等。所用部件可以总分为内核,硬件外设和特殊功能 3 部分。其中,内核是芯片的基本 部分,如 CPU,CPU 寄存器等;硬件外设为硬件资源配置部分,如定时/计时器等。特殊功能可以使系统提 高可靠性,降低功耗,如低电压扫描等。 2.2.1 单片机整体结构 8FX 系列单片机提供了多种规格不同引脚数量的系列芯片,并使用了 FPT 形式的封装。在各种系列芯片中 所有的引脚功能分配,复用对应都是相同的,只是针对具体型号芯片有所删减。这里以 64Pin 的 FPT 方形 扁平式封装单片机为例,他的对外引脚有 64 条,引脚分布如图 2-2 所示。

图 2-2 MB95100 系列引脚分布图 电源引脚 电源引脚包括了总电源引脚,A/D 转换电源引脚,A/D 转换参考电平引脚。 总电源引脚(Vcc):按说明接入 3.3 或 5V 电源。 A/D 转换电源引脚(AVcc):按说明接入 3.3 或 5V 电源。 A/D 转换参考电平(AVR):根据需要,设定 A/D 的最大转换电平。 时钟引脚 通用寄存器的时钟引脚的外部连接方式有两种:一种是外接 4MHz 晶体振荡器,产生内部时钟信号,称之 为内部时钟方式;另一种是直接连接外部时钟信号,为单片机提供时钟信号,称之为外部时钟方式。两种 时钟方式可以根据实际情况应用自由选择。由于 8FX 系列内部有主时钟和子时钟两种模式,使用晶体振荡 器的时钟连接电路如图 2-3 所示

(a)双时钟模式

(b)单时钟模式

图 2-3 内部时钟连接电路图 使用外部时钟的连接电路如图 2-4 所示

(a)双时钟模式 输入/输出引脚

(b)单时钟模式

图 2-4 外部时钟连接电路图 P0,P1,P2 到 P8 口的所有引脚构成了单片机的输入/输出(I/O)端口,其中 P0 到 P7 引脚为硬件资源 专用端口,同时也可以作为通用(I/O)端口;P8 只为通用(I/O)端口。所有端口均由端口数据寄存器(P DR)和端口方向寄存器(DDR )控制,根据端口的使用不同还由上拉使能寄存器(PUL),输入级别选择寄 根据端口的使用不同还由上拉使能寄存器 PUL), 寄存器( ),输入级别选择寄

存器(ILSR)以及高位/低位输入失效寄存器(AIDRH/AIDRL)进行特殊控制。 存器(ILSR)以及高位/低位输入失效寄存器(AIDRH/AIDRL)进行特殊控制。 P0 端口:8 个引脚,为外
部中断输入端口。 P1 端口:共 5 个引脚,主要为 UART 总线的数据输入输出以及时钟端口,此外也包括 了 16 位 PPG 定时器 0 的运行触发位和输出端口。 P3/P4 端口:共 12 个引脚,为 A/D 转换的模拟电平 输入端口。 P5 端口:共 4 个引脚,主要是 I2C 的数据和时钟线以及 16 位 PPG 定时器 1 的运行触发位 和输出端口。 P6 端口:共 8 个引脚,包括了 LIN-UART 通信的输入,输出,时钟口;8/16 位 PPG 的 10 通道和 11 通道输出口以及复合定时器/计时器的 10 与 11 计时器输出口。 P2/P7 端口为各种定时器/计 时器的输入输出端口。 需要注意的是有些硬件资源的专用寄存器,在设置了输出使能后,会将该端口的方 向寄存器(DDR )强制设置为输出;有些专用寄存器的输出使能位在设置后却不会对其方向寄存器(DDR )进行强制控制。故这里建议读者在专用寄存器设置了输出使能后,应同时对端口的方向寄存器也进行输 出设置以免在用示波器进行观测中出现错误。 控制引脚 8FX 的控制引脚十分简洁,只有 RST 和 MOD。 RST:复位引脚,当对该引脚进行低电平输入时会触发复位。 MOD:单片机工作模式控制引脚。可以控制单片机是在进行程序烧写模式还是正常运行模式。 综上所述,可以归纳 8FX 系列单片机的引脚特点如下: 1)单片机功能多,引脚多,但引脚复用现象并不少。使用时需要视情况设定各引脚的具体功能。 2)单片机引脚的(I/O)端口类型主要为 3 种:普通类型,上拉电阻类型,开漏类型。端口电路类型多可 广泛用于各种功能的硬件资源。

2.2.2 CPU 核

FFMC-8FX CPU 作为高性能 8 位 CPU 主要是用在机械,OA,机车系统。该 CPU 主要工作在低电压低功耗环 境下,同时由于支持 16 位数据的运行与传输,也可以用在要求 16 位控制数据的器件上面。

CPU 核优化的控制器指令系统 (1)乘法和除法指令 (2)16bit 操作 (3)位检测跳转指令 (4)位操作指令等 CPU 特点: 最短指令执行时间:400ns(在 10MHz 的晶振下) 内存:64Kbyte 可操作的数据类型:bit,byte,word 。 寻址模式:9 种 16 位数据运行:累加器和临时累加器运行。 8FX 系列 CPU 寄存器分为内部专用寄存器和内部 RAM 通用寄存器。专用寄存器专为 CPU 内部的硬件所用, 而且它们的使用受 CPU 的结构限制。通用寄存器和 RAM 共用 CPU 寻址空间。同专用寄存器一样,通用寄 存器不用寻址访问。同时和普通内存一样,用户也可以指定使用寄存器。 专用寄存器包括程序运算(PC)寄存器,两个算术寄存器(A 和 T),三个地址指针(IX, EP 和 SP)以 及程序状态(PS)寄存器。每个寄存器为 16 位长。由于专用寄存器中的大多数寄存器和一般 MCU 中的类 似,就不再叙述。 这里主要谈一下 PC 寄存器,8FX 的 PC 寄存器包括了寄存器区指针(RP),直接寄存器指针(DP)以及条 件码寄存器(CCR)。寄存器区指针(RP)如图 2-6 所示。

图 2-6 RP 寄存器区指针分布图 此指针指向 RAM 区域里作为通用寄存器的内存模块的首地址。通用寄存器区有 32 个区。RP 用 0~31 来指向各区。寄存器区指针(RP)使用来指向当前使用的通用寄存器区首地址的寄存器。使用通用寄存器寻 址时,RP 用来换算实际地址。 直接寄存器指针(DP)主要是用来划分连接的区域。具体的划分方法如表 2-1 表 2-1 直接连接指针和连接区域

PC 寄存器的最低位是条件码寄存器(CCR)。该寄存器包括了半接受标志位,中断使能标志位,中断级别 标志位,取否标志位,取 0 标志位,溢出标志位,全接受标志位。 通用寄存器作为 8 位寄存器是位于 RAM 中的内存块。它总共分成了 32 个区。每个寄存器可以用作通用 8 位寄存器(R0~R7)。通过寄存器指针(RP)可以直接指向这些寄存器使用的区,并进行操作。其中寄存器 指针(RP)的低 3 位用来区分这 8 位寄存器(R0~R7)。寄存器的大致地址排布入图 2-7:

图 2-7 寄存器区分布图 通用寄存器的优点在于可以通过一条段指令高速访问 RAM。此外,由于通用寄存器被分为寄存器区,故寄 存器内容保护和功能单元的划分变得很容易实现。 2.2.3 基本功能模块 1.复位 复位主要包括了外部复位,软件复位,看门狗复位和电源开启复位/低电压扫描复位。外部复位:当外部复 位针脚 RST 接收到低电平时,便会触发外部复位。当发生外部复

位时,所有的 I/O 端口或者功能端口也将被同步复位。此外,由于外部复位对触发脉冲宽度值有标准,故 在设计外围电路时一定要使脉冲宽度达到要求。 软件复位:对稳定控制寄存器中的复位标志位(STBC:SRST )写入高电平即可处罚软件复位。 看门狗复位:当看门狗计时器开始计数,如果在一定时间之内看门狗计数器的值没有被清除便会导致看门 狗复位。 电源开启复位/低电压扫描复位:在电源刚开始通电时会产生复位,同时一些 5V 的器件本身具有低电压扫 描功能。当提供的电压低于一特定标准时也会产生复位。 复位输出:5V 单片机的复位管脚在一般的复位状态下将会输出低电平信号。但如果复位信号由外部复位触 发,则不会进行输出。同时不具备复位输出功能的 3/5V 芯片的复位针脚在复位状态下也不会有输出功能。 2.时钟 主时钟:4096/Fch,当 Fch 为 10MHz 时,PLL 振荡稳定时间为 409.6us 子时钟:512/Fcl,当 Fcl 为 32.7 68kHz 时,PLL 振荡稳定时间为 15ms 时钟控制器可以控制时钟晶振是否使能,对内部电路提供的时钟是否使能还可以选 择 时钟模式以及控制 PLL 和分频电路。 2 种时钟源,即主时钟振荡器和 PLL 振荡器。 其中主时钟和 PLL 时钟又可以被分别分为单时钟模式和双时钟模式,双时钟模式中包 括了子时钟和副 PLL 时钟。

图 2-8 时钟控制图 3.静态模式(standby mode) 四种可用模式: (1)睡眠模式:使 CPU 和看门狗定时器的工作时钟停止其它外围功能继续工作 (2)停止模式:停止主时钟振荡,除了外部中断和低电压检测外所有功能停止 (3)时基定时器模式:主时钟只提供给时基定时器,除了时基定时器,外部中断和低电压检测外所有功能 停止 (4)监测模式(watch mode):CPU 和所有外围设备的工作时钟停止,除了 watch prescaler, watch co

unter, 外部中断和低电压检测外所有功能停止 当系统转变为时钟停止状态时(睡眠模式,停止模式,监测模式),运行开始前需要一段延时,以稳定时 钟振荡。从主时钟转换到 PLL 时钟时,PLL 震荡也需要震荡稳定等待时间。 陶瓷和石英振荡器开始振荡时,通常会经过几到 20ms 稳定到自然频率。因为这个原因,不允许振荡已开 始就允许 CPU 运行,只有当振荡完全稳定后才允许 CPU 运行。振荡稳定等待时间过后,时钟提供给 CPU。因为振荡稳定时间取决于振荡器的种类(石英,陶瓷等), 对于振荡器要选择适当的振荡稳定等待时间。振荡稳定等待时间是通过晶振稳态等待时间设置寄存器(WA TR )的设置来选择的。从主时钟转换到 PLL 时钟,在 PLL 振荡稳定等待时钟内,CPU 依然是以主时钟运行的。这段时间后,运行时钟转换为 PLL 时钟。 4.内存 60KB 的程序空间(ROM);3.75KB 的数据空间(RAM)。 8FX 系列芯片的内存通常为 64K Byte,主要划分为 I/O 区,外部 I/O 区,数据和程序区。 内存也包括了一些专用区如通用寄存器,中断向量表。不同的芯片可能有所区别,少数芯片的程序空间和 数据空间会有所削减。图 2-9 为 8FX 系列的几种常用芯片的存储映射表:

图 2-9 8FX 系列内存分布图 5.中断 外部中断作为专用的中断通道,可以通过专用寄存器对中断通道输入的高电平进行多 种检测来触发中断。外部中断控制寄存器:中断请求输出使能,触发中断边沿扫描,中断请求标志。外部 中断控制原理:

图 2-10 外部中断原理图 中断特点 中断级别设置寄存器(ILR0~ILR5)包含了最多可达到 24 个由中断源发出的中断请求。每一位可以设定对 应的中断级别。 最多可达 14 个外部中断通道(64Pin 封装或者 100Pin 封装)。 边沿检测外部中断可以选择上升沿,下降沿或两者作为中断 可用于备用模式的唤醒。 两个外部中断共享一个中断矢量。 6.I/O 端口 最多可超过 80 个通用 I/O(100Pin 封装) 可设置为上拉和悬浮输入口,或者数出口; 所有端口具备直接驱动 LED 的能力; P00~P07 的 I/O 端口具备 20mA 的驱动能力。 2.2.4 外围功能模块 1.定时器/计时器 8/16 位复合定时器/计时器 复合定时器/计时器主要是用来当其他专用计时器不够用时,保留的一个备用定时器/计时器。因为是备用 定时器/计时器,所以复合定时器/计时器的最大特点在于其广泛的适用性。复合定时器/计时器有多达 16 种工作模式,最主要的几种工作模式为:PWM 输出模式;PWC 捕捉模式;输入捕捉模式。 PWM 通过改变时钟和占空比可产生多种脉冲输出,适用于显示亮度的设定,如果连接到外部低通滤波器, 也可以用作 D/A 转换器,有可变周期模式和固定周期模式两种模式。 可变周期模式:通过设定 8bit 计数器寄存器确定周期和“L”脉冲宽度,来产生任意周期和占空比的 8bi t PWM 信号。 固定周期模式:改变“H”脉冲宽度产生 PWM 信号。 8bit 工作模式时周期设定为 FFH,16bit 工作模式时周期设定为 FFFFH。 PWC 测量脉冲宽度,适用于传感器的输入和远程控制器的接受。可用于测量“H”或“L”脉冲的宽度,也 可以测量一个周期的宽度。 输入捕捉:测量实时周期,适用于有实时控制需要的发动机驱动的调整。 PPG 可编程脉冲发生器。

PPG 可编程脉冲发生器是专用生成可变脉冲波的定时器。其最大特定就是利用自身设定可以生成几乎各种 频率的脉冲波。 工作模式:双 8 位 PPG,16 位 PPG,8 位预分频 PPG。 16bit PPG 计时器根据需要可以输出 PWM 波形或者是单步方波,且输出的方波可以通过软件对占空比和周 期进行设置。同时根据输出波形的边沿扫描产生中断。 特点: 外部触发(上升沿开始/下降沿停止/无效) 工作过程中可选择有效/无效重触发 占空比的设置可以从 0 到 100% 16bit 重载计时器 (1)工作模式 内部时钟模式 外部时钟模式 (2)特点 每种时钟模式均可选择重载或单触发模式 可选择软件或硬件触发 2.LIN-UART LIN(Local Interconnect Network)一种可与外部设备进行同步/异步串行通信的通用接口, 除了支持正常模式下双向通信功能和多处 理器模式下的主从通信功能外,还支持 LIN 总线的特殊功能。 一个主节点,多个从节点,速度最高可达 20kbps。 3.UART-SIO 全双工通信; 异步时钟或同步串行数据传输; 数据长度为 5~8bit,可选校验位; NRZ 非归零码,LSB 或 MSB; 错误检测(溢出,帧错误或校验位错误); 通过专用波特率发生器可设定准确的波特率; 传输或发送数据时可以获取数据寄存器的地址。 高速数据通信: 同步时钟:最大传输速率=机器时钟/8 异步时钟:最大传输速率=机器时钟/8 4.I2C I2C:Inter-Integrated Circuit 由 PHILIPS 公司开发。总线支持主从模式下数据的发送和接受,仲裁检测,从地址扫描以及全呼地址;同时还支持初 始和停止状态生成和检测,总线错误扫描,以及睡眠模式下 MCU 唤醒。 I2C 特点: 两线式串行总线,连接微控制器及外围设备; 每个器件根据分配的不同的地址既可以作为发 送器又可以作为接收器; 支持多主控,有冲突检测以及通信控制协议防止数据丢失; 通信控制协议保证即使在有多个主控试图同时

控制总线时,在任一时间点上也只能有一 个主控来控制总线; 能启动等待模式下的 MCU。 5.A/D 转换模块 A/D 转换模块将模拟输入引脚上的输入电压转换为 10 位或者 8 位数字值。A/D 转换器具有以下特点:当 A/D 转换完成时将产生对应的中断。可以使用 ADTG 针脚作为 A/D 转换的外部触发源。 12 通道带外部参考电压的 8/10 位 ADC。 使用电阻电容型的逐次比较的转换方法,并带有采样保持电路。 6.LCD 驱动模块 LCD 驱动模块主要是驱动 LCD 显示管的,因此这里和我们通常所说的 LCD 有很大的区别。平常所说的 LCD 已经自带有驱动电路和驱动芯片,在编程时需要编程人员按照其时序控制 LCD 显示。而 8FX 中所用的 LCD 驱动只是驱动不带驱动电路和芯片的 LCD 显示管, 其本质更接近于多个 LED 显示管的级连。 所以在使用时, 所写的代码也更类似于多个 LED 的控制。 LCD 控制/驱动器由显示控制器,显示 RAM,端驱动器和公共端驱动器组成。显示驱动器根据需要显示的数据产 生段和公共端去洞信号,段驱动器和公共端驱动器的输出可以直接驱动 LCD(liquid crystal display )显示面板。LCD 控制/驱动器有如下特点: 可直接驱动 LCD 面板; 具有 4 个公共端(COM0~COM3)和 16 个端(SEG0~SEG15); 具有 8 字节显示 RAM; LCDC 可以提供二等分或三等分的 LCD 运行电压; 具有两种可选择的驱动时钟源:主时钟和子时钟。 7.双操作 Flash 双操作 Flash 是 8FX 中的一个革新的模块。其原理是将一个 Flash 存储区划分为一大一小 upper bank(4 k×3)和 lower bank(16k×2+4k×4)两个独立的存储区,能够进行半独立的读写和擦出操作。为什么说是半独立了,因 为在对两个存储区进行操作时,两个区不能同时进行读写操作或者是擦出操作,而只能一个进行读写一个 擦出。 不需要 E2PROM 减小贴片面积 提高烧写速度 提高数据可靠性 可以在系统控制过程中对 Flash 烧写程序 不需要在 RAM 上执行程序 可设置小的扇区,容易使用

橡皮泥咖啡 2009-01-14 00:04:17

第 3 章 单片机开发语言 (单片机的编程语言主要有汇编和高级语言两种,每一种类型的单片机都有自己的汇编语言,软件可移植 性不是很好。而高级语言有 C 语言,PLM 语言,PASCAL 语言,BASIC 语言等几种。本章对这些语言进行了 具体概述。) 第 1 节 单片机开发语言概述 单片机 开发语言 目前,单片机的编程语言主要有汇编和高级语言两种,每一种类型的单片机都有自己的汇编语言,软件可 移植性不是很好。而高级语言有 C 语言,PLM 语言,PASCAL 语言,BASIC 语言等几种。BASIC 语言主要应用在 MCS51 系列单片机上,但效果不是很理想,现在已经基 本上不用。PLM 语言主要应用在 MCS51 和 MCS96 系列单片机中,对硬件的控制能力和代码效率都很好。PASCAL 语言在 MOTOROLA 单片机中有应用。C 语言是现代单片机开发中较常用的高级语言,其程序的可读性,可移植性都很好,对 硬件的控制能力也很强,唯一不足的是其代码效率较低,在程序量较大时需使用大容量的程序存储器。 第 2 节 汇编语言 汇编语言 汇编指令 寻址 位操作 3.2.1 汇编结构及特点 汇编语言是一种采用助记符来编写程序的语言,它由操作符和操作数两个部分组成,其中操作数又分为源 操作数和目的操作数。汇编语言比用机器语言的二进制代码编程要方便些,在一定程度上简化了编程过程。 汇编语言的特点是用符号代替了机器指令代码,而且助记符与指令代码一一对应,基本保留了机器语言的 灵活性。使用汇编语言能面向机器并较好地发挥机器的特性,得到质量较高的程序。 由于汇编语言中使用了助记符,用汇编语言编制的程序必须通过预先放入计算机的"汇编程序"的加工和翻 译,才可以变成能被计算机识别和处理的二进制代码程序。用汇编语言等非机器语言书写好的符号程序称 为源程序,运行时汇编程序要将源程序翻译成目标程序。目标程序是机器语言程序,它一经被安置在内存 的预定位置上,就能被计算机的 CPU 处理和执行。 汇编语言像机器指令一样,是硬件操作的控制信息,因而仍然是面向机器的语言,使用起来还是比较繁琐 费时,通用性也差。但是,汇编语言用来编制系统软件和过程控制软件,其目标程序占用内存空间少,运 行速度快,有着高级语言不可替代的用途。 3.2.2 汇编寻址 在学习寻址方式之前,应首先了解符号指令的操作数中常用的符号,各个符号的意义如下表 3-1 所示: 表 3-1 指令中助记符的意义

F2MC-8FX 有如下十种类型的寻址方式: 1.直接寻址 当第一个操作数为直接地址时,就可以对“0000H”到“047FH”的地址空间直接访问。在这种寻址方式中, 当第一个操作数为“00H”到“7FH”时,访问“0000H”至“007FH”, 而当第一个操作数为“80H”到“FFH”时,通过设置直接组指针(DP),将其映射到“0080H”到“047FH” 的地址空间来访问,如下图所示:

由于第一个操作数 92H 在“80H”~ “FFH”范围内,故需要将其映射到“0080H”到“047FH”的地址空间。 当 DP=001B 时,其对应的访问地址为“0100H”~ “017FH”,因此第一个操作数的地址为(0100+92-80)H=0112H,如上图所示,即将“45H”送入“011 2H”中。 2.扩展寻址 这种寻址方式用于指令中的扩展地址(16 位)访问整个 64K 字节的空间,在这种寻址方式中,将第二个操作数(即扩展地址)中的两个字节的数据送入第一个操作数 中,如下图所示:

图 3-2 扩展寻址 将扩展地址“1234H”~“1235H”中的 16 位数据“5678H”送入到累加器中。 3.位直接寻址 这种寻址方式用于对应指令表中的“dir:b”时,访问“0000H”到“047FH” 地址中的第 b 位。当操作数为“00H”~“7FH”时,访问“0000H”至“007FH”,而当操作数为“80H”~ “FFH”时,通过设置直接组指针(DP),将其映射到“0080H”到“047FH”的地址空间来访问,如下图所 示:

上图中的 dir 为“34H”在“00H”~ “7FH”内,故直接将“0034H”地址中的 bit2 置“1”。 4.变址寻址 这种寻址方式对应指令表中的“@IX+off”,用于寻址整个 64K

字节的空间,在这种寻址方式中,将变址寄存器(IX)中的地址加上偏移量(off)得到所需要的地址,如 下图所示:

如上图所示,IX+off=27A5H+5AH=27FFH,即将 27FFH 和 2800H 地址中的值送到累加器中。 5.指针寻址 这种寻址方式对应指令表中的“@EP”, 用于寻址整个 64K 字节的空间,在这种寻址方式中,EP(附加指 针)的值为地址,如下图所示:

如上图所示,将 EP 地址 27A5H 和 27A6H 中的值 1234H 送到累加器中。 6.通用寄存器寻址 这种寻址方式对应指令表中的“Ri”,用于寻址通用寄存器区域中的寄存器组,在这种寻址方式中,寄存 器组的起始地址为“0100H”,通过 RP 的值确定是 32 个寄存器组中的哪一组,操作码的低三位确定访问寄存器组中的哪一个寄存器,如下图所示:

如上图所示,被访问寄存器的地址为:0100H+8*RP(01010B)+6=0156H,即将 0156H 寄存器中的值送入累 加器中。 7.立即数寻址 这种寻址方式对应指令表中的“#d8”,用于需要立即数时,在这种寻址方式中,操作数即为立即数,操作 数的长度可以为 8 位,也可以为 16 位,如下图所示:

8.向量寻址 这种寻址方式对应指令表中的“#vct”,用于跳转到子程序地址。在这种寻址方式中, “#vct”为操作码,对应跳转到向量表的地址如下表所示: 表 3-2 向量寻址

如上图所示,将#5 对应地址 FFCAH:FFCBH 中的值 FEDCH 送入 PC 中。 9.相对寻址 这种寻址方式对应指令表中的“rel”,用于跳转到当前 PC(程序计数器)128 字节范围内。在这种寻址 方式中,将操作数的值与 PC 的值相加,得到新的 PC 的值,如下图所示:

10.固有寻址

这种寻址方式在指令表中没有操作数,它由运行时的操作码决定。在这种寻址方式中,指令的执行取决于 每一条指令,如下图所示:

3.2.3 汇编指令 富士通的单片机汇编指令主要由以下几类组成: 1.专用指令 JMP @A 将累加器 A 中的值送入 PC(程序累加器)中,程序跳转到新的 PC 处执行,如下图所示:

MOVW A,PC 这条指令与 JMP @A 的功能相反,它把 PC 中的值存入累加器 A 中,当执行完这条指令后,累加器中的值与储存的下一条指令的地址值相同,而不是储存的这条指令的 操作码的地址,如下图所示:

MULU A 这条指令将 AL 和 TL 中的无符号数相乘, 并将 16bit 结果放在累加器 A 中, 临时累加器 T 中的值不变, 在指令执行前,不使用 AH 和 TH 中的值。如下图所示:

DIVU A

这条指令将 T 中的 16bit 数除 A 中的 16bit 无符号数,并把 16bit 商存在累加器 A 中,16bit 余数存 在临时累加器 T 中。如下图所示:

XCHW A,PC 这条指令交换累加器 A 和程序计数器 PC 中的值,指令执行前,程序跳转到累加器 A 中的地址,指令执 行后,A 中的值为这条指令的下一条指令的地址,如下图所示:

图 3-15 XCHW A,PC CALLV #vct 这条指令用于跳转到指令表中对应地址的子程序中,并把返回地址(PC 的值)保存在堆栈指针中,指令 执行完后,PC 保存在堆栈中的值为下一条指令的地址,如下图所示:

2.位操作指令(SETB,CLRB) ( CLRB) 0 ( SETB)或 1 位操作指令主要包括 SETB 和 CLRB 两个,在这个指令中,可以设置寄存器 或 RAM 中指定位为 ,但是,由于 CPU 是对 8bit 数进行操作的,实际的操作过程包含了一系列过程(读出-修改-写入):读出 8bit 数据,修改指定位,把数据写回到原来的地址。 3.F2MC-8FX 指令 F2MC-8FX 主要有以下几类指令: 数据转移类指令

算术运算指令 转移指令 其它指令 第 3 节 C 语言 嵌入式 编译器 中断函数 C 语言 汇编语句 C 语言是一种通用的计算机程序设计语言,在国际上十分流行,它既可用来编写计算机的系统程序,也可 用来编写一般的应用程序。以前计算机的系统软件主要使用汇编语言编写的,对于单片机应用系统来说更 是如此。由于汇编语言程序的可读性和可移植性都较差,采用汇编语言编写单片机应用系统程序的周期长, 而且调试和排错也比较困难。 而一般效率高的高级语言难以实现汇编语言对于计算机硬件直接进行操作 (如 对内存地址的操作移位操作等)的功能。而 C 语言既具有一般高级语言的特点,又能直接对计算机的硬件 进行操作,并且采用 C 语言编写的程序能够很容易地在不同类型的计算机之间进行移植,因此许多以前只 能采用汇编语言来解决的问题现在可以改用 C 语言来解决。

3.3.1 C 语言的特点 C 语言可以用来编写科学计算或其他应用程序,但它更适合于编写计算机的操作系统程序以及其他一些需 要对机器硬件进行操作的场合,有的大型应用软件也采用 C 语言进行编写,这主要是因为 C 语言具有很好 的可移植性和硬件控制能力,表达和运算能力也较强。 概括来说,C 语言具有以下一些特点: 1.语言简洁紧凑,使用方便灵活 C 语言一共只有 32 个关键字,9 个控制语句,主要用小写字母表示,压缩了一切不必要的成分;C 语言程 序书写形式自由,可以用简单的方法构造除复杂的数据类型和程序结构。 2.运算符丰富 C 语言把括号、赋值、强制类型转换等都作为运算符处理,从而使 C 的运算类型极其丰富,共有 34 种运算符。C 表达式类型多样化,灵活使用各种运算符可以实现其他高级语言难以实现的运算。 3.数据结构类型丰富 C 的数据结构类型丰富,根据需要可以采用:整型、实型、字符型、数组类型、指针类型、结构体类型、共 用体类型等多种数据类型来实现复杂数据结构的运算,尤其是指针类型数据使用起来非常灵活多样。

4.可进行结构化程序设计 C 语言是以函数作为程序设计的基本单位的,用函数作为程序模块以实现程序的模块化,是结构化的理想 语言。 5.语法限制不严格,程序设计自由度大 C 语言的语法规则不太严格,程序设计的自由度比较大,限制和灵活是一对矛盾。C 语言放宽了语法检查, 所以程序员应当仔细检查程序,而不要过分依赖 C 编译程序去查错。

6.C 语言允许直接访问物理地址 C 语言允许直接访问物理地址,能进行位 bit 操作,能实现汇编语言的大部分功能,可以直接对硬件进行操作,这样它可以对单片机的内部寄存器和 I/ O 口进行操作,可以直接访问片内或片外存储器。 7.生成目标代码质量高 众所周知,汇编语言程序目标代码的效率是最高的,但统计表明,C 语言编 写的程序生成代码的效率仅比 汇编语言低 10~20%。 8.程序可移植性好 汇编语言完全依赖于机器硬件,因而不具有可移植性,C 语言是通过编译来得到可执行代码的。C 语言的编 译程序便于移植,基本上不作修改就能用于各种机器和操作系统。 尽管 C 语言具有许多的优点,但和其他任何一种程序设计语言一样,也有其自身的缺点。但总的来说,C 语言的优点远远超过了它的缺点。 3.3.2 C 语言的程序结构 C 语言程序是由若干个函数单元组成的,每个函数都是完成某个特殊任务的子程序段。组成一个程序的若 干个函数可以保存在一个源程序文件中,也可以保存在几个源程序文件中,最后再将它们连接在一起,C 语言源程序文件的扩展名为“.c”。 一个 C 语言程序必须有而且只能有一个名为 main()的函数,它是一个特殊的函数,也称为该程序的主函 数。程序的执行都是从 main()函数开始的,下面我们先来看一个简单的程序例子。 [例] 求最大值 int max( int x,int y) main { int a, b, c ; a=10; b=20; c=max (a,b); } int max( x ,y) int x, y ; { int z ; if( x>y) z=x; else z=y; return (z); } 本例中除了 main 函数之外,还用到了功能函数调用,函数 max 是一个被调用的功能函数其作用是将变量 x 和 y 中较大者的值赋给变量 z, 变量 x 和 y 在函数 max 中是一种形式变量,它的实际值是通过 main 函数中的调用语句传送过来的。变量 z 是函数 max 要返回的值,return 语句将 z 的值返回给 main 函数 的调用处。 Main 函数中第三行调用 max 函数,在调用时,将实际参数 a 和 b 的值分别传送给 max 函数 中的形式参数 x 和 y 。经过执行 max 函数得到一个返回值(即 max 函数中变量 z 的值),把这个值赋给 /*将计算的最大值 z 返回通过 max 带回调用处 */ /* max 函数中用到的变量 z 也要加以定义 */ /* 计算最大值 */ /* 定义函数 max 函数值为整型 x y 为形式参数 */ /* 对形参 x y 作类型定义 */ /* 变量赋值 */ /* 调用 max 函数将得到的值赋给 c */ /* 主函数名 */ /* 主函数体开始 */ /* 主函数的内部变量类型说明 */

变量 c。 通过以上的例子可以看到一般 C 语言程序具有如下结构: 预处理命令 函数说明 功能函数 1 函数体 主函数 主函数体 功能函数 2 #include< > int fun1; char fun2; fun1 { … } main { … } fun2 { 函数体 … } …. C 语言的开头部分通常是预处理命令,#include 命令。它是通知编译器在对程序进行编译时,将所需要的 头文件读入后再一起进行编译。 C 语言程序是由函数所组成的,一个程序至少应包含一个主函数 main()。函数之间可以相互调用,但 mai n()函数只能调用其他的功能函数,而不能被其他函数所调用,不管 main 函数处于程序中的什么位置,程序总是从 main()函数开始执行。 3.3.3 C 语言的基本语法 C 语言是用的最为广泛的高级语言,在单片机中也是很重要的,相较于汇编语言,C 语言简单易懂,移植性 好,少许修改后可以应用于不同的单片机。其基本语法主要包括以下几类: 1.数据类型 在 C 语言中,每个变量在使用之前必须定义其数据类型,C 语言有以下几种数据类型:整型(int),浮点型 (float),字符型(char),指针型(*),无值型(void),数组类型(array)以及结构(struct)和联合(union)。 整型(int) 加上不同的修饰符, 整型数有以下几种类型: signed short int 有符号短整型数,简写为 short 或 int,字长为 2 字节,共 16 位二进制数, 数的范 围是-32768~32767 signed long int 有符号长整型数,简写为 long, 字长为 4 字节共 32 位二进制数, 数的范围是-214748 3648~2147483647 unsigned short int 无符号短整型数,简写为 unsigned int,字长为 2 字节共 16 位二进制数, 数的 范围是 0~65535 unsigned long int 无符号长整型数,简写为 unsigned long,字长为 4 字节共 32 位二进制数, 数的范 围是 0~4294967295

浮点型(float) C 中有以下两种类型的浮点数: Float: 单浮点数,字长为 4 个字节,数的范围是 3.4x10-38E~3.4x10+38E Double:双浮点数,字长为 8 个字节,数的范围是 1.7x10-308E~1.7x10+308E 说明: 浮点数均为有符号浮点数, 没有无符号浮点数 字符型(char) 字符型分为有符号和无符号两种类型,例如: char a; /*a 被定义为有符号字符变量*/ unsigned char l; /*l 被定义为无符号字符变量*/ 在计算机中以其 ASCII 码方式表示, 其长度为 1 个字节, 有符号字符型数取值范围为-128~127, 无符 号字符型数到值范围是 0~255 因此在 C 语言中, 字符型数据在操作时将按整型数处理, 如果某个变量定 义成 char, 则表明该变量是有符号的, 即它将转换成有符号的整型数。C 中规定对 ASCII 码值大于 0x80 的字符将被认为是负数,例如 ASCII 值为 0x8c 的字符, 定义成 char 时, 被转换成十六进制的整数 0xff8 c 这是因当 ASCII 码值大于 0x80 时, 该字节的最高位为 1, 计算机会认为该数为负数, 对于 0x8c 表示的 数实际上是-74(8c 的各位取反再加 1), 而-74 转换成两字节整型数,并在计算机中表示时就是 0xff8c( 对 0074 各位取反再加 1),因此只有定义为 unsigned char 0x8c 转换成整型数时才是 8c。 这一点在处理 大于 0x80 的 ASCII 码字符时(例如汉字码)要特别注意,一般汉字均定义为 unsigned char。另外, 也可 以定义一个字符型数组, 此时该数组表示一个字符串。 例如: char str[10];计算机在编译时, 将留出连续 10 个字符的空间, 即 str[0]到 str[9]共 10 个变 量, 但只有前 9 个供用户使用,第 10 个 str[9]用来存放字符串终止符 NULL,即"\0", 但终止符是编编译程序自动加上 的, 这一点应特别注意。 指针型(*) 指针是一种特殊的数据类型, 在其它计算机语言中一般都没有定义这种数据类型。指针是指向变量的地 址, 实质上指针就是存贮单元的地址,根据所指的变量类型不同, 可以是整型指针(int *),浮点型指针(float *),字符型指针(char *),结构指针(struct *)和联合指 针(union *)等。 无值型(void) 无值型字节长度为 0, 主要有两个用途: 一是明确地表示一个函数不返回任何值;一是产生一个同一类 型指针(可根据需要动态分配给其内存)。 例如: void *buffer; /*buffer 被定义为无值型指针*/ 2.常量与变量 C 语言的数据有常量和变量之分: 常量:在程序运行过程中其值不能改变的量称为常量,常量可以有不同 的数据类型; 变量:在程序运行中其值可以改变的量称为变量,变量是由变量名和变量值组成的。 每个变量都有一个变 量名,在内存中占据一定的存储单元地址,并在该内存单元存放 该变量的值。 对于单片机来说,变量类型或数据类型的选择是值得一提的,对于 C 这样的高级语言,不管使用何种数据类型,从字面上看其操作都非常简单,然而,实际上编译器需要一系 列机器指令对其进行复杂的变量类型、数据类型处理,特别是浮点运算,明显增加运算时间和程序的长度。 所以应该慎重进行变量和数据类型的选择。不要使用大量不必要的变量类型。

3.运算符和表达式 C 语言中运算符和表达式数量之多在高级语言中是少见的,运算符按其在表达式中所起的作用可分为赋值运 算符、算术运算符、关系运算符、逻辑运算符、位运算符、逗号运算符、条件运算符、指针和地址运算符、 强制类型转换运算符和 sizeof 运算符等。根据运算符在表达式中与运算对象的关系又可分为单目运算符、双目运算符和三目运算符。单 目运算符是只需要一个运算对象,双目运算符则要求有两个运算对象。 4.表达式语句 表达式语句有如下几种: 基本表达式语句 就构成了表达式语句。表达式语句也可以仅有一个分号组成这种语句称为空语句空语句是表达式语句的 一个特例它在程序设计中有时是很有用的 ; 表达式语句是最基本的一种语句,在表达式的后边加一个分 号 复合语句 将若干条语句组合在一起而形成的一种功能块复合语句,它不需要以分号结束,但内部的各条单语句仍 需以分号结束。 {} 复合语句是由若干条语句组合而成的一种语句,它是用一个大括号 复合语句的一般形式为 { 局部变量定义; 语句 1; 语句 2; 语句 n; } 复合语句在执行时,其中的各条单语句依次顺序执行,整个复合语句在语法上等价于一条单语句,因此 在 C 语言程序中可以将复合语句视为一条单语句,复合语句允许嵌套。 5.判断选取控制语句 If 语句的简化形式 if (条件表达式) 语句 其含义为:如果条件表达式的结果为真(1),就执行后面的语句,反之若条件表达式的结果为假(0), 就不执行后面的语句,这里的语句也可以是复合语句。 if 语句的基本形式 if 语句的基本形式为: if(条件表达式) 语句 1 else 语句 2 上述结构表示:如果条件表达式的值为 1(即真),则执行语句 1,反之如果条件表达式的值为 0(即假), 则执行语句 2,这里语句 1 和语句 2 都可以是复合语句。 if 语句的多分支形式 if 语句的多分支形式为: if (条件表达式 1) 语句 1 else if(条件表达式 2) 语句 2 ...... else if(条件表达式 m) 语句 m

else 语句 n 这种条件语句常用来实现多方向条件分支 开关语句————switch 语句 switch 语句是多分支选择语句,相比较 if 语句的多分支形式,switch 语句可以直接处理多分支选择, 使程序结构清晰,使用方便。 switch 语句的一般形式如下: switch 表达式 { case 常量表达式 1: 语句 1 break; case 常量表达式 2; 语句 2 break; ...... case 常量表达式 n; 语句 n break default: 语句 d } 开关语句的执行过程是:将 switch 后面表达式的值与 case 后面各个常量表达式的值逐个进行比较,若 遇到相等时,就执行相应的 case 后面的语句,再执行 break 语句,跳出 switch 语句,若无相等的情况,则只执行语句 d。break 语句又称间断语句,它的功能是中止当前语句的执行,使程序跳出 switch 语句。如果在 case 语句中遗 忘了 break,则程序在执行了本行 case 选择之后,将执行后续的 case 语句,而不会按规定的跳出 switch 语句。 6.循环语句 在许多实际问题中,需要进行有规律的重复操作,如累加求和,数据块的搬移等等。循环结构是结构化程 序的三种基本结构之一,C 语言中用来构成循环控制的语句分述如下: while 语句 while 语句的一般形式为: while(条件表达式) 语句 while 循环表示:当条件表达式的结果为真(1)时,程序便执行后面的语句,直到条件表达式的结果变化为假 (0)时,才结束循环,并继续执行循环程序外的后续语句。这种循环结构是先检查后执行,如果条件表达 式的结果一开始就为假,则后面的语句一次也不会被执行。 do-while 语句 do-while 构成循环结构的一般形式如下: do 语句 while (条件表达式); do-while 语句先执行循环体中的语句,然后再判断条件表达式的值是否为真,如果为真,则重复执行循环体语句, 直到条件表达式的值变为假时为止。这种循环结构的特点是先执行再检查,因此 do-while 语句即使条件表 达式的值 一开始就为假,循环体语句也会被执行一次。 for 语句 for 语句的一般形式如下: for (<初始化表达式>;<循环条件表达式>;<更新表达式> ) 语句

隔开。 ; 初始化表达式一般是一个赋值语句,它用来给循环控制变量赋初值;循环条件表达式决定什 么时候退出循环;更新表达式定义循环控制变量每循环一次后按什么方式变化。这三个部分之间用分 for 语句的执行过程是:先计算出初始化表达式的值作为循环控制变量的初值,再检查循环条件表达式的结 果,当满足循环条件时,就执行循环体语句,并计算更新表达式,然后再根据更新表达式的计算结果来判 断循环条件是否满足,一直进行到循环条件表达式的结果为假时退出循环体。 goto 语句 goto 语句是一个无条件转向语句,它的一般形式为: goto 语句标号; goto 语句中的语句标号是一个带冒号的标识符,将 goto 语句和 if 语句一起使用,可以构成一个循环 结构。下面的程序可以用来累加 1~100 求和 main { int a,sum=0; a=1; loop: if (a<=100) { sum=sum+a; a++; goto loop; } } 除此之外,goto 语句还可以在多重循环中从内层循环跳到外层循环,但是结构化程序设计方法主张限制 使用 goto 语句,因为滥用 goto 语句会使程序流程无规律,可读性差,因此在程序设计中应尽量少用 goto 语句。 continue 语句 continue 语句是一种中断语句,它一般用在循环结构中,其作用是结束本次循环,即跳过循环体中下面尚未执行 的语句,把程序流程转移到当前循环语句的下一个循环周期,接着进行下一次循环控制条件的判断。cont inue 语句的一般形式为: continue; continue 语句只用在 for、 while 、do-while 等循环体中,它也是一种具有特殊功能的无条件转移语 句,但与 break 语句不同,continue 语句并不跳出循环体,而只是结束本次循环。 返回语句 返回语句用于终止函数的执行,并控制程序返回到调用该函数时所处的位置。返回语句有两种形式: return (表达式); return; 如果是第一种 return 语句,后边带有表达式,则要计算表达式的值,并将表达式的值作为该函数的返回值。若使用不带表达 式的第 2 种形式。则被调用函数返回主调用函数时,函数值不确定。 一个函数的内部可以含有多个 return 语句,但程序仅执行其中的一个 return 语句而返回主调用函数。

一个函数的内部也可以没有 return 处时就自动返回主调用函数。 } 3.3.4 C 编译器 C 预处理器是 Softune V3 C 编译器的一个组成部分,在 C 语言中,通过预处理指令可以为 C 语言本身提供很多功能和符号等方面的扩充,可增强其灵活性和方便性。预处理指令只在程序编译时起作 用,且通常是按行进行处理的,因此常又称为编译控制行。编译器在对整个程序进行编译之前,先对程序 中的编译控制行进行预处理,然后再将预处理的结果与整个 C 语言源程序一起进行编译,产生汇编文件。常用的预处理指令有:宏定义、文件包含和条件编译。预处理 命令以符号“#”开头。 预处理与编译的结构如下图 3-17 所示: 语句,在这种情况下当程序执行到最后一个界限符

1.宏定义 宏定义――不带参数的宏定义 不带参数的宏定义的一般格式为: #define 标识符待替换的字符串 其中,“标识符”是所定义的宏符号名(或称宏名)。宏定义的作用是,在程序中用指定的宏名来替代指 定的字符串。 宏定义又称宏替换,预处理器每次在程序行中扫描到宏名就把宏名替换为指定的字串,但如果宏名出现 在注释中和字符串中则是例外,如果宏名为预处理器的保留字也不能被替换。 按习惯,通常将宏符号名用大写字母表示,以区别于其它的变量名。宏定义不是 C 语言的语句,因此在 宏定义行的末尾不要加分号,否则在编译时将连同分号一起进行替换,可能导致语法错误。如果在一个宏 定义中包含另一个宏符号名,那么就形成宏定义嵌套,宏定义的嵌套深度最大可达 255 级。一般宏定义指

令#define 的行放在文件的开头,其作用范围是从被定义的地方开始,至本源文件结束。 宏定义――带参数的宏定义 带参数的宏定义的一般格式为: #define 宏符号名(参数列表) 表达式 宏符号名和左括号必须紧紧相连,其间不能有空格注释及诸如此类的字符串。括号中参数表里的参数被 称为形式参数,在以后的程序中它们将被实际参数所替代,实际参数的数目必须与形式参数的数目一样。 如果未指定任何表达式,则宏符号名将替换空字符。带参数的宏定义也允许宏定义嵌套,宏定义的嵌套深 度最多为 255 级。 【例】 #define eq(a, b) a==b #define ne(a, b) a!=b ... int x,y,z; x=y=1; if( eq(x,y) ) z=10; /* 这一行得到执行 */ else z=20;; /* 这一行未得到执行 */ 宏定义指令#define 要求在一行内写完,如果一行内写不下时,可在行末加反斜杠“\” 进行续行。 2.文件包含(#include 指令) 文件包含是指一个程序文件将另一个指定文件的全部内容包含进来。比如经常使用的文件包含命令 #incl ude <stdio.h>,就是将 C 编译器提供的输入输出库函数的说明文 stdio.h 包含到自己的程序中去。文件 包含命令的一般格式为: #include <文件名> 或 #include “文件名” 文件包含命令#include 的功能是用指定文件的全部内容替换该预处理行。在进行较大规模程序设计时,文 件包含命令是十分有用的。为了适应模块化编程的需要,可以将组成 C 语言程序的各个功能函数分散到多 个程序文件中,分别由若干人员完成编程,最后再用#include 命令将它们嵌入到一个总的程序文件中去。 需要注意的是一个#include 命令只能指定一个被包含文件, 如果程序中需要包含多个文件则需要使用多个 包含命令,还可以将一些常用的符号常量、带参数的宏以及构造类型的变量等定义在一个独立的文件中, 当某个程序需要时再将其包含进来,这样将可以减少重复劳动,提高程序的编制效率。 文件包含命令#include 通常放在 C 语言程序的开头,被包含的文件一般是一些公用的宏定义和外部变量 说明,当它们出错或是由于某种原因需要修改其内容时,只需对相应的包含文件进行修改,而不必对使用 它们的各个程序文件都作修改,这样有利于程序的维护和更新。当程序中需要调用 C 编译器提供的各种库 函数的时候,必须在程序的开头使用#include 命令将相应函数的说明文件包含进来,经常在程序开头使用 的命令#include <stdio.h>就是为了这个目的。 3.条件编译 一般情况下对 C 语言程序进行编译时所有的程序行都参加编译,但有时希望对其中一部分内容只在满足一 定条件时才进行编译,这就是所谓的条件编译。条件编译可以选择不同的编译范围,从而产生不同的代码。 Softune V3 C 编译器的预处理器提供以下编译命令#if、#elif、#else、#endif、#ifdef、#ifndef 这些命令有三 种使用格式,如下所述: 条件编译命令格式一 #ifdef 标识符 程序段 1

#else 程序段 2 #endif 该命令格式的功能是:如果指定的标识符已被定义,则程序段 1 参加编译并产生有效代码,而忽略掉程 序段 2,否则程序段 2 参加编译并产生有效代码而忽略程序段 1。其中#else 和程序段 2 可以没有,这里的 程序段可以是单行或多行的 C 语言语句,这种条件编译对于提高 C 语言源程序的通用性是很有好处的。 【例】 #define DEBUG ... #ifdef DEBUG printf(“We are debugging”); #endif 条件编译命令格式二 #ifndef 标识符 程序段 1 #else 程序段 2 #endif 该命令格式与第一种命令格式只在第一行上不同,它的作用与第一种刚好相反,即如果指定的标识符未 被定义,则程序段 1 参加编译并产生有效代码而忽略程序段 2,否则程序段 2 参加编译,并产生有效代码而忽略程序段 1。 以上两种格式的用法也很相似,可根据实际情况视需要而定。 条件编译命令格式三 #if 常量表达式 1 程序段 1 #elif 常量表达式 2 程序段 2 ... ... #elif 常量表达式 n-1 程序段 n-1 #else 程序段 n #endif 这种格式条件编译的功能是:如果常量表达式 1 的值为真(非 0),则程序段 1 参加编译,然后将控制传 递给匹配的#endif 命令,结束本次条件编译,继续下面的编译处理。否则如果常量表达式 1 的值为假(0),则忽略掉程序 段 1(不参加编译)而将控制传递给下面的一个#elif 命令,对常量表达式 2 的值进行判断。如果常量表达式 2 的值为假(0),则将控制再传递给下一个#eli f 命令。如此进行,直到遇到#else 或#endif 命令为止,使用这种条件编译格式可以事先给定某一个条件,使程序在不同的条件下完成不同的功能。 3.3.5 C 编译器 C 编译器进行编译的结构如下图 3-18 所示:

1.C 编译器的数据调用协议 内存模式(编译模式) 内存模式是指如何在存储器中放置程序代码和数据,它们允许占用的存储 器大小,及如何存取它们。与 PC 机上的编译器相似, Softune V3 编译器允许用户使用 4 种内存模式来进行 C 语言编程,4 种可用的 内存模式及其含义如下面的表格所示:

2.与汇编语言程序的接口 为了使汇编语言的源程序能够调用 C 语言源程序,编译器必须按照某种规则来对 C 语言源程序进行编译, 生成的汇编输出文件中的变量名标号和函数名标号才能被其它汇编源程序引用,以达到汇编程序调用 C 源 程序的目的。如下表 3-7 所示:

表 3-7 C 源程序与汇编语言程序的接口 注:“no”是 C 编译器内部产生的数字 3.中断函数调用接口 通过给函数加上“__interrupt” 类型限定符就成为中断函数,如果用普通的方法直接调用中断函数,而不是由于发生中断导致中断函数被 调用,则无法保证这种直接调用(中断函数)的正确性和安全性。在中断函数里进行(普通)函数调用遵 循标准链接规则。 中断函数调用接口有如下几方面内容: 堆栈结构 中断发生后,堆栈被切换为中断堆栈。 函数参数 中断函数不带任何参数。 调用过程 中断发生后,通过查中断向量表,而触发中断程序运行。 寄存器的使用 编译器保证,在中断函数里所有(使用到的)寄存器都被保护起来的;除非在中断函数里使用嵌入汇编 语句而改变了寄存器的值。 函数返回值 中断函数没有返回值 ? 中断调用的堆栈结构 中断发生后,堆栈指针被切换为中断堆栈指针(中断函数以系统堆栈指针 作为堆栈指针),下表 3-8 说 明中断发生后,中断调用的堆栈结构: SSP 3-8 中断调用的堆栈结构 (低位地址)

(高位地址) ? 中断调用过程 中断发生后,通过查中断向量表,而触发中断程序运行,如果用普通的方法直接调用中断函数,而不是 由于发生中断导致中断函数被调用,则无法保证这种直接调用(中断函数)的正确性和安全性。 3.3.6 嵌入式 C 语言的特殊之处 富士通 8FX 的集成开发环境 Softune V3 在启动文件的编译时,必须调用到启动文件 Start8FX.asm,在这个启动文件内,主要完成了内存,堆栈 的定义,分配和初始化,以及对机器时钟进行了配置,目标代码的执行从它开始,并调用主程序文件(ma in.c)。详细介绍见第六章。 另外,C 语言与汇编语言可以互相调用以及嵌入汇编,在 C 语言中嵌入汇编的格式如下: 使用 asm 表达式 asm(字符串序列); asm 表达式可以在函数内和函数外使用,当在函数内使用 asm 表达式时就在表达式的位置上直接展开为 汇编语句,当在函数外使用 asm 表达式时它被扩展成独立的 SECTION, 因此如果在函数外使用 asm 表达式,一定要用 SECTION 的预处理指令来为其单独定义一个 SECTION,否则难以保证相应操作的正确性。 在函数内使用 asm 表达式,必须由用户自己来保存和恢复寄存器,对于累加器是个例外,因为编译器会对累加器进行保存 和恢复所以可以随意地使用累加器。 如下所示: void wait(unsigned long j) { while(j--) asm(" \tNOP "); } ,即没有操作。 NOP 在 wait()函数内使用 asm 表达式,直接将其展开成 使用 pragma 预处理指令 使用 pragma 预处理指令的一般格式为: #pragma asm 汇编语句 #pragma endasm pragma 预处理指令可以在函数内和函数外使用,当在函数内使用时,在#pragmaasm 和#pragma endasm

预处理指令之间的汇编语句直接被转换成同样的汇编代码。当在函数外使用时它被扩展成独立的 SECTIO N,因此如果在函数外使用 pragma 预处理指令,一定要用 SECTION 的预处理指令来为其单独定义一个 SECTION,否则难以保证相应操作的 正确性,在函数内使用 pragma 预处理指令必须由用户自己来保存和恢复寄存器,对于累加器是个例外,因为编译器会对累加器进行保 存和恢复,所以可以随意地使用累加器。 如下所示: void wait(unsigned int j) { volatile unsigned int i; for (i = 0; i < j; i++) { #pragma asm NOP NOP NOP NOP #pragma endasm } } 在#pragma asm 和#pragma endasm 之间的汇编语句直接转换为四个连续的 NOP。

橡皮泥咖啡 2009-01-14 00:26:29

第4章 单片机软件开发工具 (目前市面上有各种的单片机,同时相应的产生了各种的软件开发工具。本章主要介绍最主要的软件开发 工具包括了 51 汇编集成开发环境,ARM 公司的集成开发环境软件 ADS,PIC 公司的开发软件 mikroBasic For PIC ,凌阳的集成开发环境 Fortis IDE 以及富士通的 Softune V3 ) 第 1 节 常用单片机软件开发工具简介 单片机 软件开发 Fortis IDE Softune V3 目前市面上有各种的单片机,同时相应的产生了各种的软件开发工具。最主要的软件开发工具包括了 51 汇编集成开发环境,ARM 公司的集成开发环境软件 ADS,PIC 公司的开发软件 mikroBasic For PIC ,凌阳 的集成开发环境 Fortis IDE 以及富士通的 Softune V3 。 从总体上开发环境的特点如下 : (1)开发环境主要以仿真器为主,优点:方便,可以设置断点,可以观察存贮器及寄存器的内容。缺点:

价格昂贵,且仿真器终究不是单片机,有时代码在仿真器上能通过,但在单片机中不能正常工作,反而增 加了调试的难度。

(2)开发环境主要以编程器为主,优点:价格相对便宜,通常一款编程器可编多种器件。缺点:操作相当 不便,每次要将芯片在目标板与编程器之间转移,并且还要在编译操作界面与编程器操作界面之间切换, 大部份时间在做简单的重复的工作。 当然随着单片机技术日趋成熟,开发环境集成的功能也日趋强大。 第 2 节 Softune V3 集成开发工具包 Softune V3 工具包 C Analyzer C Checker 富士通公司提供了一种支持 8FX 系列单片机的集成开发环境 Softune V3 。Softune V3 是基于 Windows95 /98/NT/2000 的集成开发工具包,它同时支持 C 语言和汇编语言,这使得用户可以在编写程序时更加灵活。

从图 4-1 可知 Softune V3 集成开发工具包(ProPack)包括了 Softune V3 Workbench, C Compiler, Assembler, Linker, C Analyzer 和 C Cheker。在 Softune V3 集成开发环境中,应用程序的调试过程如 图 4-2 所示。

4.2.1 Softune V3 概述 Softune V3 具有全软件仿真功能,如果只是进行代码级调试,则一台安装了 Softune V3 的计算机就可以 了;但是如果要在逻辑时序级调试,则 BGM 在线仿真器和用户开发系统都是必需的。 要安装该工具包,只要找到软件包中的 Setup.exe 安装文件运行即可。当软件安装完成后,打开 Softune V3,同时在其操作界面中运行工程文件可以看到如图 4-3 的开发环境界面。

(1)菜单条:菜单条如图 4-4 所示,和常用软件的菜单条类似,包括了(File/Edit )文件的新建,打开,保存,编辑等操作,(Project)工程的各种配置,(Debug )包括对 debug 各项 操作以及断点的设置,此外还有 Setup, Window 和 help, 即环境设置,界面设置和帮助文本。 (2)工具条:实现菜单的一些快捷键操作,如图 4-4 所示

(3)源文件编辑区:汇编源文件,c 源文件以及编译调试中的一些辅助窗口如观测(watch) 窗口,寄存 器值(Register)窗口等。 (4)文件工程列表:以树形的结构来显示工程项目中的源文件,头文件和编译文件。

(5)状态条:显示当前命令的提示信息,工程项目使用的 CPU 类型以及与调试有关的一些信息,如图 47 所示

(6)输出信息区:显示编译项目时的输出信息和其他一些输出信息,如图 4-8 所示

4.2.2 C Checker

C Checker 用于对 C 语言源程序进行检查,发现下列可能存在问题的代码,并针对各种情况,提出改正、改进的建议:有错误 的代码;通过改进可以提高整个程序质量的代码;通过改进可以提高运行性能的代码;通过改进可提高程 序可移植性的代码。

该代码可以由非富士通的 MCU 向富士通的 MCU 进行相互移植。不过随着单片机的普及与发展,代码也逐渐 变得越来越成熟和复杂。移植的难度也在不断的加高,这使得 C Checker 在应用和开发方面也出现了一定的问题。现在已经基本不再推荐使用 C Checker。

4.2.3 C Analyzer C Analyzer 用于对 C 语言源程序进行分析,程序开发过程中,通过 C Analyzer 可显示、打印程序流程, 统计数据和堆栈的使用情况等。C Analyzer 分析程序, 并根据分析的结果优化代码, 提高程序的效率和性能。 对于由多个人合作编写的程序, 通过 C Analyzer 可以发现合并后代码中的错误,分析其性能。如果程序编写者没有给出程序的说明文档,你也可以用 C An alyzer 来分析程序的结构和处理过程。

综上所述,Softune V3 的总体特点是具有高效的开发环境。

管理器和调试器集成于一体的开发环境提高了“编辑-编译-调试”循环的效率。 程序设计效率高 ----支持 C 编译器、C Checker、C Analyzer 和结构化汇编语言。 高效率开发 ----提供 C 函数库和 C 语言/汇编源程序调试。 多窗口易操作,Softune V3 支持多窗口操作,但在仿真中建议读者还是只打开单窗口,以防止窗口过多所 导致的 Softune V3 在编译中产生错误。 符合标准:C 语言和库函数符合 ANSI 标准。 第 3 节 软件开发工具应用 编译 项目配置 软件 开发工具 通用的单片机应用程序一般包括的内容如图 4-11 所示。 这些内容按照功能可分为“定义段代码”,“初始化段代码”,“主程序段代码”三部分,跟单片机以及 编译器等的不同,它们之间的顺序,内容可能有些差异。这三部分可能在一个文件里,也可能在多个文件 内,但这些内容都是必须的。

在用 Softune Workbench 编写应用程序时,每一个应用程序被称为一个工程(Project)。工程是头文件,参数初始化文件,主程序 文件等的集合,如图 4-12 所示。

这可以分别和前面提到的“定义段代码”,“初始化段代码”,“主程序段代码”三个部分对应起来,它 们的作用也是相同的。三种类型文件的个数是不确定的,根据功能的不同,可能有的被分成几个文件,如 主程序可以将程序主体,子程序,中断服务程序等部分分别放在不同的文件中。 对于每一个要完成工程的程序员,他所要做的事情就是建立一个工程,同时在工程里面添加入需要的代码 段,然后通过编译和仿真,最终生成机器代码烧入进单片机核中。 在本章中,将通过建立一个工程的例子来介绍有关内容。该例子以 FFMC-8FX 中的 MB95F100 为目标单片机,若读者要使用其他目标单片机,请参照说明文档在相关硬件属性(如单片机种类,RAM,R OM)设置的地方作相应的变动。 4.3.1 创建新的项目 创建一个新的应用程序,第一步是创建一个新工程。创建新工程的步骤如下:在 Softune Workbench 中, 选择 File->New,弹出如图 4-13 所示的新建(New)对话框。

选择 Project File,点击“OK”,出现如图 4-13 的新建工程(Create)对话框。在新建工程中首先选择选择工程一栏,即 Project,对新工程要使用的芯 片型号,工程名称,建立的工程代码的类型等进行设置。 在 Project Type 中可以看到三种选择:ABS,REL 和 LIB,ABS 工程产生绝对地址代码,它可以直接载入 单片机中执行;REL 工程产生相对地址代码,不能直接载入单片机中执行;LIB 是库文件,用于建立库。在 Debug 时只有 ABS 的文件可以装入调试。 从上说明可以知道,这里 Project Type ( 工程类型)选择 Loadmodule (ABS) ,在 Project Name ( 工程名)中输入工程的名字,这里的工程名字取为“”。同时在 Target Filename ( 目标文件)的默认值也 会变为*\ABS 。*与 Project Name 中的值相同,此文件可装载入仿真器或软仿真模块中,此文件的默认路径为*\ABS 。在 Chip Classificat

ion 中可以选择芯片类型和芯片型号,也可以在不改变芯片类型的前提下,直接在 Target MCU 中选择要编程 的目标芯片。 这里选择的事 FMC8FX 下的 MB95FV100 。点击确定。 工程路径列表区中列出了工程*.abs 将用到的子目录(source, include, dependencies),但此时所有的子目录都是空的,现在只建立了一个空的工程。如前所叙述的,需要将该 工程中添加参数初始化文件(Start8FX.asm,mb95100.asm),头文件(mb95100.h )这里如果是 8FX 其它系列的芯片则添加对应的后缀名为.h 的头文件以及主程序文件(main.c)。通常 由于习惯将中断向量表(vectors.c )和主程序文件(main.c )区别开,便于程序阅读和修改,所以主程序文件实际包括了 main.c 和 vecto rs.c 两个部分。 接下来将初始化文件和主程序文件放入 source 目录下,include 目录下放入头文件即可。Dependencies 目录下的文件会根据需要有系统装入“#include ”中的文件,通常也就是在 include 中的头文件。

然后设置工程代码地址,工程代码地址与工作空间的地址有很密切的关系。如果对工作空间进行了设置的 话,系统会自动对工程代码地址进行分配,所以直接点确定就可以。 4.3.2 建立工作空间 工作空间(Workspace )一栏设置工作空间 Workspace 的名称和地址,该地址直接关系到工程代码的地址。 工作空间(Workspace )的设置非常间洁,只有名称和地址两栏,同时地址设置好后,对应的工程代码会由软件自动的分配到工 作空间的相应地址中去。

一个工作空间包含了该工程项目的全部内容:工程代码,编译代码,软件仿真代码,硬件仿真代码以及最 后可以烧写到实际芯片上的机器代码。一个完整地通过软硬件仿真工作空间包含的文件通常有 ABS 子文件夹,LST 子文件夹,OBJ 子文件夹,OPT 子文件夹,SRC 子文件夹,以及一些软件仿真代码和硬件 仿真代码。其中 ABS 子文件夹,LST 子文件夹,OBJ 子文件夹,OPT 子文件夹,SRC 子文件夹在新建时就会有,我们所添加的各种工程代码文 件,如 main.c,start8FX.asm 等都在 SRC 子文件夹中,因此添加新文件时,也可以将写好的代码文件直接复制其中,即可进行编译。 除此之外, ABS 子文件夹是另一个非常关键的文件夹, 它里面包含了两个非常重要的文件即*.abs 文件和*. mhx 文件。*.abs 文件是通过仿真后的所有的工程代码(main.c, start8FX.asm,vectors.c )等的融合。*.mhx 则是前面所说的工程代码生成的机器代码,只要将该代码烧入进单片机里便可以让单片机跑起来了。因此, 建立一个工程的最终的目标就是要生成*.abs 文件和*.mhx 文件。 4.3.3 项目配置 按照建立新的工程的说明,读者实际建立起来只是一个空的工程空间和一个空的工程代码文件夹,接下来 要做的就是在刚建立的文件夹中添加工程代码。 添加之前,先来看一个已方针完成的工程包含了那些部分。图 4-16 是一个工程的工程目录列表。 从图 4-16 可以看到,一个完整的工程项目列表通常包括了 main.C,mb95100.asm,readme.txt,start8F X.asm,vectors.c 以及 mb95100.h 这 6 个文件组成。其中 readme.txt 主要用来对整个项目程序进行说明可以不要,除此外 其余 5 个全是必须的。现在分别对这几个文件进行说明。

main.C 文件包含了你所编写的主要的 C 语言程序,同时也作为你的工程项目的入口。你的程序代码将从 v oid main(void) 开始。同时 main.C 文件可以包含带有的子程序和中断处理程序,当然也可以让这两部分分别放在专门的文件里。 mb95100.asm 将各种寄存器的每一位地址以及 I/O 端口地址进行了声明。 需要注意的是 mb95100.asm 定义了 95100 系列所有可能要用到的寄存器和 I/O 端口,而有些寄存器或者端口是读者拥有的芯片所不带的,读者只需要对你自己的芯片内实际拥有的寄存 器进行操作即可。此外,如果使用的非 95100 系列的芯片如 95110 系列等则必须对 mb95100.asm 文件进行适当的修改。 Start8FX.asm 这个汇编程序主要完成了内存,堆栈的定义,分配和初始化。同时对机器时钟进行配置,目标代码的执行 从它开始,它调用主程序文件(main.c)。 参考中的 startup.asm 给出了对机器时钟的配置,当然也可以不在 startup.asm 中对时钟进行设置而在主程序对相应的寄存器进行配置来达到设置的目的。具体方法详见第六章的 start8 FX.asm 的配置。 readme.txt 文件主要用来说明该项目的一些具体用途以及需要留意的注意事项, 主要由程序员自己根据个 人习惯来定。如无必要,也可以不用编写。 vectors.c 包含了中断向量表以及对对应的中断级别的设置,以及要用到的中断的声明。需要用到中断的读者必须要 在该文件下,就对应的中断进行修改。修改的具体方法将在第六章详细的说明。 mb95100.h, 该文件对 mb95100.asm 文件下的所有的源寄存器的位进行了声明和定义。需要注意的是对应 于不同的 mb95100.h 文件,main.c 中的源寄存器和 I/O 端口的位的编写也不一样。因此,必须对 mb95100.h 进行了解后,才可以进行 main. c 的编写,否则会浪费大量的时间。 4.3.4 添加各种代码文件 定义完新工程后,在工程窗口将看到如图 4-17 所示的工程模块,若要加入新的文件,右键点击对应的模 块文件夹,然后选择“Add Member to folder->file…”,便会弹出添加文件框,选中要添加文件最后点确定就可以了。提醒一点的是,在 Sour ce Files 里加入的文件的后缀名为.c 或者.asm,即添加文件框中的默认文件类型。而在 Include Files 里加入的文件后缀名为.h,故在添加文 件框中的文件类型要改为任何类型。

第二点说明是无论.h,.asm 还是.c 文件都可以先用 txt 文件编写入代码后再修改后缀名,添加入工程即 可。要修改代码,也可以不通过 Softune V3 直接由 txt 文件打开.h,.asm 和.c 文件进行修改,当然修改后的文件在 txt 文件状态下无法进行编译和 调试,最终的编译和调试还是要由 Softune V3 来完成。 4.3.5 编译 当源文件编写完成后,就需要对该文件进行编译/汇编。执行 Project/Compile xxx.c 或者 Project/Asse mble xxx.asm ,或者点击工具栏按钮 。

根据源文件的相互依赖关系,检察源文件的日期,支队更新过的文件进行编译/汇编,然后连接生成目标文 件。执行 Project/Make 选项,或按 Shift+F8 快捷键,或点击工具栏按钮 。

对所有源文件进行编译/汇编,链接操作。然后链接生成目标文件。执行 Project/Build 选项,或者按 Ct rl+F8 快捷键,或点击工具栏按钮 。

停止正在进行的编译/汇编,链接操作。执行 Project/Stop 选项,或点击工具栏按钮



如果编译或者连接过程中出现错误,输出窗口将给处警告或错误信息(对应于工具选项设置中的警告级别, 警告信息在 Softune V3 种通常都不影响硬件仿真,故若只有警告信息 warnning 可以直接进行软硬件调试)。按 F1 键可以得到该错误信息的帮助信息,直接双击输出窗口的错误和警告星系,就会打开发生错误的源文件, 并且光标自动移动到错误行。改正错误,重新编译,链接程序,直到没有错误,可以进行软硬件调试。到 此为止,新工程的基本框架就建好了,接下来就是如何调试程序。

橡皮泥咖啡 2009-01-14 00:26:56 第 5 章 单片机硬件开发平台

(前面讲述了单片机的软件开发平台,要想很好的完成设计,必须要有硬件开发平台与之配合使用,本章 主要讲述单片机的硬件开发平台——8FX 开发套件简介,8FX 开发套件的应用 ) 第 1 节 单片机的硬件开发平台概述 单片机 硬件开发平台 在第四章中讲述了单片机的软件开发平台,要想很好的完成设计,必须要有硬件开发平台与之配合使用, 本章主要讲述单片机的硬件开发平台。 第 2 节 8FX 开发套件简介 8FX 转接板 开发套件 MCU Board BGM Adapter 富士通所提供的 8FX 开发套件简介主要包括 BGM Adapter,MCU board,以及转接板三个部分组成,通过它 们实现与软件开发平台的配套使用。 5.2.1 BGM Adapter BGM Adapter 的全称是 BackGround Monitor Adapter,即后台监视适配器,它通过内部的 FLASH 实现电脑与 MCU 的连接,并将代码下载到内部的 FLASH 上,其外观如下图 5-1 所示:

其中 USB Connector 与电脑的 USB 接口相连,并支持 USB 2.0 规范,数据传输速率可达 480Mbps,并由 US B 总线供电。Power LED 指示当前 MCU 的连接状态,当 BGM Adapter 与电脑连接上后,LED 为绿色,加上 5V 直流电压后,LED 变为橘黄色。Interface Connector 为适配器端的接口。

5.2.2 MCU Board MCU Board 是硬件开发平台的核心部分,其组成如下所示:

MCU Board 由如下几个部分组成: (1) 低电压检测跳线插头 (2) 芯片选择开关(Ch.5-Ch.8) (3) 时钟选择开关(Ch.4:CLK) (4) 时钟管理选择开关(Ch.3:CLK S.V.) (5) APB8 总线输出选择开关(Ch.2:APB8)(用于扩展) (6) C-pin 选择开关(Ch.1:C) (7) 低电压选择开关(Ch.5:LVD1,Ch.6:LVD2) (8) 时钟输入选择开关(Ch.1: X0, Ch.2: X1, Ch.3:X0A, Ch.4:X1A) (9) 5V 或 3V 电源开关 (10)主时钟晶振插座 (11)子时钟晶振插座 (12)子时钟选择跳线插头 (13) BGM Adapter 接口 (14) 错误插入防止键 (15) Header board I/F connector A (16)Header board I/F connector B (17)仿真芯片(MB95FV100-103)

5.2.3 转接板 转接板是用来连接带有仿真芯片的 8FX 系列 8 位单片机,它将仿真芯片的 64 个引脚通过转接板引出来,从 而使用户对仿真芯片有更好的控制。对于使用不同型号的单片机,仿真芯片在转接板上所对应的引脚是不 一样的,因此,在使用转接板前,一定要明确所使用的是哪种类型的芯片。转接板是由左右各 120 个插孔 组成的,每个插孔的间距为 0.5mm,用户可以在上面焊接排针,来监测仿真芯片各个引脚的状态并对其进 行控制。 第 3 节 8FX 开发套件的应用 8FX 开发套件 BGM Adapter MCU board 转接板 8FX 开发套件主要由以下三个部分构成:BGM Adapter,MCU board 以及转接板,如下所示:

5.3.1 时钟的配置 MCU 板上提供两种不同的时钟,一种是主时钟,另一种是子时钟。主时钟主要为主时钟模式和主时钟 PLL 模式提供时钟,而子时钟则为子时钟模式和子时钟 PLL 模式提供时钟。在外接时钟时,还需要连接两个容 值 30pF 的电容,当使用陶瓷振荡器时,应将电容与插孔的 1 和 3 引脚连接,而使用晶体振荡器时,应将电 容与插孔的 1 和 2 引脚连接。其连线如下图 5-4 所示:

MCU 板上的 SW-1 开关用来选择是否开启子时钟,当开关关断时,启用子时钟,而开启时则不使用子时钟。 SW3-1 和 SW3-2 用于使能主时钟的, 当使用系统自带的时钟, 则关断 SW3-1 和 SW3-2, 而当使用外部的时钟, 则开启 SW3-1 和 SW3-2。对于具体使用哪种时钟,可以通过在 start8FX.asm 文件内选择,如下所示: 4.2 Clock Mode Selection ;================================================================ # set SUB 0 ; Sub clock mode # set SUB_PLL 1 ; Sub PLL clock mode # set MAIN 2 ; Main clock mode # set MAIN_PLL 3 ; Main PLL mode # set NOCLOCK FF ; Do not touch the clock and PLL register ; #set CLOCKMODE MAIN_PLL ; <<< Clock seletion ; ; ;================================================================ ; 4.3 Main-PLL Clock Selection ;================================================================ # set MAINPLL_OFF 0 ; No Main-clock setting # set MAINPLLx1 1 ; Original oscillator clock x1 # set MAINPLLx2 2 ; Original oscillator clock x2 # set MAINPLLx2p5 3 ; Original oscillator clock x2.5 ; #set CLOCKSPEED MAINPLLx2p5 ; <<< set PLL speed ; ; ;================================================================ ; 4.5 Sub-PLL Clock Selection ;================================================================ # set SUBPLL_OFF 0 ; No Sub-clock setting # set SUBPLLx2 1 ; Original oscillator clock x2 # set SUBPLLx3 2 ; Original oscillator clock x3 # set SUBPLLx4 3 ; Original oscillator clock x4 ; #set SUBCLOCKSPEED SUBPLL_OFF ; <<< set Sub-clock PLL speed ; ; ;================================================================ ; 4.5 DIV Clock Selection (Machine clock division ratio) ;================================================================ # set CLK_0 0 ; Original oscillator div 1 # set CLK_4 1 ; Original oscillator div 4 # set CLK_8 2 ; Original oscillator div 8 # set CLK_16 3 ; Original oscillator div 16

; #set CLOCKDIV CLK_0 ; <<< set DIV clock ratio 通过选择不同的时钟模式以及时钟的速度,可以对时钟进行灵活的配置,在一般情况下,都是选择主时钟 PLL 模式,且时钟速度选择 MAINPLLx2p5,如果外接晶振为 4MHz,则所使用的时钟为 10 MHz。 5.3.2 电源的管理 电源管理就是让系统进入低电源消耗的待机模式(Standby Mode),它主要包括:睡眠模式,停止模式,时 基定时器模式(Timebase Timer Mode),监测模式(Watch Mode)等,目的就是控制 MCU 的电源消耗。通过设置待机控制寄存器(Standby Control Register),可以使系统进入待机模式。当产生中断或复位时,系统跳出待机模式,如果是系统复位,则 系统进入主时钟模式;当产生中断时,则系统进入待机前的时钟模式。 在设置完待机控制寄存器后,系统进入待机模式需要四个机器时钟周期,因此,在设置待机模式的指令后, 至少应该有三条“NOP”的指令。 5.3.3 芯片的选择 MB95FV100-103 是一个仿真芯片,它可以对所有的 8FX 系列芯片进行仿真,因此,在使用开发板前,必须 对所使用的芯片进行选择,这主要是通过对 SW-5, SW-6, SW-7, SW-8 四个开关来实现的,如下表 5-1 所示:

5.3.4 MCU 的其它设置 关闭时钟管理选择开关 SW1-3 关闭 APB8 总线 SW1-2 关闭 C-pin 选择开关 SW1-1 关闭 LVD 选择开关 SW1-5,SW1-6 设置电压为 5V(SW2) 设置低电压检测为 LVD0 第 6 章 单片机开发调试 (对于每一个不同的 MCU 芯片,其功能,引脚,寄存器,中断都是所不同。同时由于程序员自身的需要也 会对使用的 MCU 芯片,在时钟,中断等方面做出适当的调整。本节主要以 mb95f100 为例,就其初始化配 置,时钟调整,中断定义等进行说明) 第 1 节 系统文件的配置 系统文件 Start8FX 中断配置 对于每一个不同的 MCU 芯片,其功能,引脚,寄存器,中断都是所不同。同时由于程序员自身的需要也会 对使用的 MCU 芯片,在时钟,中断等方面做出适当的调整。本节主要以 mb95f100 为例,就其初始化配置,时钟调整, 中断定义等进行说明。 6.1.1 Start8FX.asm 文件配置 Start8FX 如前面所介绍的主要是对 MCU 进行一个初始化包括了对堆栈的定义,分配。同时对机器时钟进行配置,目标代码的执行从它开始,它的 最后将调用主程序文件(main.c)。其中,对堆栈的定义,分配和初始化是 Start8FX 的必要部分。在 8FX 系列中,对时钟控制设定了专门的寄存器,可以在主程序中进行配置。 下面是 mb95f100 的 Start8FX 初始化文件代码 ;================================================================= ;4 Settings 前面的 1,2,3 位该代码内容的一些简单声明故省略 ;================================================================= ;CHECK ALL OPTIONS WHETHER THEY FIT TO THE APPLICATION; ; ;Configure this startup file in the "Settings" section. Search for ; comments with leading "; <<<". This points to the items to be set. ; #set OFF 0 #set ON 1 ; ;================================================================= ; 4.1 Modbyte ;================================================================= # set SINGLE_CHIP 0 ;single chip mode, no external bus interface # set EXTERNAL_BUS 1 ;external bus interface

;# set MODEBYTE SINGLE_CHIP ;<<< 设置 modebyte ,定义为单片模式 ; ;The Mode Byte defines whether an external bus interface is available or not. ;it is read after the reset vector. Please have a look at the Hardware manual ;of the used microcontroller for further information on that topic ; ;================================================================= ;4.2 Clock Mode Selection ;================================================================= # set SUB 0 ; Sub clock mode # set SUB_PLL 1 ; Sub PLL clock mode # set MAIN 2 ; Main clock mode # set MAIN_PLL 3 ; Main PLL mode # set NOCLOCK FF ; Do not touch the clock and PLL register ; #set CLOCKMODE MAIN_PLL ;<<< 内部时钟设置,这里设置为主 PLL ; ;================================================================= ; 4.3 Main-PLL Clock Selection ;================================================================= # set MAINPLL_OFF 0 ; No Main-clock setting # set MAINPLLx1 1 ; Original oscillator clock x1 # set MAINPLLx2 2 ; Original oscillator clock x2 # set MAINPLLx2p5 3 ; Original oscillator clock x2.5 ; #set CLOCKSPEED MAINPLLx2p5 ; <<< 设置 PLL 时钟参数,为 2.5 倍频 ; ;================================================================= ; 4.5 Sub-PLL Clock Selection ;================================================================= # set SUBPLL_OFF 0 ; No Sub-clock setting # set SUBPLLx2 1 ; Original oscillator clock x2 # set SUBPLLx3 2 ; Original oscillator clock x3 # set SUBPLLx4 3 ; Original oscillator clock x4 ; #set SUBCLOCKSPEED SUBPLL_OFF ; <<< 设置子时钟 PLL 速度 ; ;================================================================= ; 4.5 DIV Clock Selection (Machine clock division ratio) ;================================================================= # set CLK_0 0 ; Original oscillator div 1 # set CLK_4 1 ; Original oscillator div 4 # set CLK_8 2 ; Original oscillator div 8 # set CLK_16 3 ; Original oscillator div 16

; #set CLOCKDIV CLK_0 ; <<< 设置内部时钟分频器为无分频 ; ;================================================================= ; 4.6 Stack Size ;================================================================= ; # set STACKSIZE 64 ; <<< 堆栈大小,默认为 64byte ; ;================================================================= ; 4.7 Flash Security ;================================================================= ; # set FLASHSECURITY OFF ; <<< Flash 安全设置(ON / OFF),默认为关闭 ; ;<<< END OF SETTINGS >>> 上面的代码主要为时钟模式,主/子时钟速度的设置。不包括堆栈初始化,RAM,ROM 的设置。以及主程序 接口。前面已经介绍过,在 8FX 系列单片机中,有时钟控制的专用寄存器模块(CPU Controller)。 在(CPU Controller )时钟控制寄存器中包括了系统时钟控制寄存器(SYCC),PLL 控制寄存器(PLLC),晶振稳态等待时间设置寄存器(WATR),静态控制寄存器(STBC)。下面分别谈下 各寄存器的作用: 系统时钟控制寄存器(SYCC )可以进行机器时钟分频设置;可以对源时钟进行不分频,4 分频,8 分频, 16 分频;可以控制子时钟晶振运行/停止及静态设置;时钟模式选择。 PLL 控制寄存器(PLLC)可以对子 时钟 PLL 静态进行设置;子时钟倍频设置;副 PLL 时钟晶振使能设置;主时钟倍频设置;主 PLL 时钟晶 振使能设置晶振稳态等待时间设置寄存器(WATR )分别对主时钟和子时钟晶振稳态等待时间进行设置。 静态控制寄存器(STBC)主要对机器的监测模式,停止模式,睡眠模式,软件复位设置,针脚状态设置。 从 start8FX.asm 程序中可以看出只是对时钟进行了 2.5 倍频设置,同时设置子时钟关闭,机械时钟 1 分 频。 /* PLL */ PLLC = 0x90; while(PLLC_MPRDY==0){;} SYCC = 0xF4; while(SYCC_SCM1 == 0){;} while(SYCC_SCM0 == 0){;} /* Main PLL clock mode shift waiting */ 该段程序必须在主程序的最开始处进行编写,因为 CPU 时钟总是在程序执行之前,就要预先设定的。在本 书后面的程序段中,所有的时钟频率均为 4MHz * 2.5 = 10MHz ,机械时钟 1 分频,时钟模式为主 PLL 时钟模式。同时时钟的设置全部在 start8FX 中进行,就不 再在程序中列出。该时钟频率也是目前 8FX 系列的最快时钟频率,在新的富士通的低端单片机中,其时钟频率已经可以达到 4MHz * 4 = 16MHz 。 /*设置时钟频率为:4MHz * 2.5 = 10MHz */ /* PLL oscillation stability waiting */ /* 机械时钟 1 分频, 时钟模式为主 PLL 时钟模式*/

6.1.2 中断配置

前面介绍过 FFMC-8FX 定义了 24 种中断,每一种中断对应于外部中断源,可独立的设置中断级别。那么中 断究竟与那些函数有关,下面将具体讨论与中断有关的函数: 禁止中断函数: 一般格式为:void__DI(void) 用以清零中断标志位,从而禁止中断。由于 8FX 支持扩充的 C 源代码,所以通常在主程序中也可以写成“__DI();”。 允许中断函数: 一般格式为:void__EI(void) 置位中断允许标志位,从而允许中断,也可以写成“__EI()”。这是最常用的中断函数,如果工程要使 用中断,那么在主程序 main.c 中都会用到。 中断级别设置标志为: 一般格式:void__set_il(int level) 把中断级别设置为“level”。该函数主要在主程序中使用,如果不想在主程序中进行中断级别设置,也 可以在中断向量表中进行设置。扩充的 C 源代码:__set_il(int level)。 中断函数的定义和声明: 一般格式:__interrupt void Interrupt function(void) {……}和 extern__ interrupt void Interrupt function(void) 。中断函数的声明和定义是通过在函数前面加类型限定符“__interrupt” 来实现。 由于中断函数的运行是有中断来触发,一般中断不带参数的,也不返回任何值。如果用普通的方法直接 调用中断函数,而不是由于发生中断导致中断函数被调用,则无法保证这种直接调用(中断函数)方法的 正确性和安全性。 中断向量表生成伪指令 一般格式:#pragma intevct Interrupt function name Vector number[Mode value] 和#pragma defevct Interrupt function name 说明#pragma intevct 伪指令为中断函数生成对应的中 断向量表 #pragma defevct 伪指令为,没有使用#pragma intevct 伪指令的其他中断向量,生成缺省的中断向量 表。 中断向量表被放置在一个单独的名为“INTVECT”的 SECTION 里。 当使用#pragma defevct 伪指令,就产生所有的中断向量,而无一遗漏。因此,如果使用了#pragma de fevct 伪指令,所有的中断向量都必须在这一源程序里定义,否则中断向量会产生冲突,编译里会有提示。反 之,如果没有使用#pragma defevct 伪指令,就可以在多个源程 序里面使用 pragma defevct 伪指令。 不能使用该函数在同一中断向量上,定义不同的中断函数/中断向量。但是,在同一源函数,同一中断向 量号上, 介绍完有关中断的各种函数,现在来谈下中断流程。8FX 系列单片机的中断流程:外部源产生中断源,执行主程序,设置中断请求标志位,检测中断请求使能位, 检测中断级别(ILR0 到 ILR5),检测所有中断请求级别同时检测中断使能标志位。 当外部源的中断请求位被接受时,中断请求位不会自动清零,所以必须要通过软件在中断执行程序后进 行清零

若中断程序在执行过程中产生了更高优先级的中断请求,CPU 会中止当前的中断进程并接受更高级别的 中断请求。其具体的执行流程如图 6-1 所示

中断向量表声明程序如下: #include "mb95100.h" /*--------------------------------------------------------------------------InitIrqLevels() 初始中断级别设置。这里将所有中断的级别进行了设置。这里有 0xFF 对中断的级别进行设置,每个 0xFF 对应了 4 个中断。故每个中断的级别有两个 bit 构成,最高优先级为 0,最低为 3。需要注意的是这里定义了 8FX 芯片全部可能用到的中断,但实际的芯 片并不一定拥有 下面全部的中断。读者在使用时需要根据自己的需要,自行调整中断的级别。在这里可以看到对 16 位重 载定时器的中断级别设置为 0,即最高级别。在今后的样例 中,由于在主程序中设置了只有中断级别高于 3 的中断才可以发生。故通常都会对要用到的中断级别进行 对应的修改。 /*-----------------------------------------------------------------------------*/ void InitIrqLevels(void) { /* ILRx IRQs defined by ILRx */ ILR0 = 0xFF; // IRQ0: external interrupt ch0 | ch4 // IRQ1: external interrupt ch1 | ch5 // IRQ2: external interrupt ch2 | ch6 // IRQ3: external interrupt ch3 | ch7 ILR1 = 0xFF; // IRQ4: UART/SIO ch0 // IRQ5: 8/16-bit timer ch0 (lower) // IRQ6: 8/16-bit timer ch0 (upper) // IRQ7: LIN-UART (reception) ILR2 = 0x3F; // IRQ8: LIN-UART (transmission) // IRQ9: 8/16-bit PPG ch1 (lower) | UART/SIO ch1 // IRQ10: 8/16-bit PPG ch1 (upper) | I2C ch1 // IRQ11: 16-bit

reload timer ch0 <<<--- Level 00 ILR3 = 0xFF; // IRQ12: 8/16-bit PPG ch0 (upper) // IRQ13: 8/16-bit PPG ch0 (lower) // IRQ14: 8/16-bit timer ch1 (upper) // IRQ15: 16-bit PPG ch0 + ch2 ILR4 = 0xFF; // IRQ16: 16-bit reload timer ch1 | I2C ch0 // IRQ17: 16-bit PPG ch1 // IRQ18: 10-biat A/D-converter // IRQ19: Timebase timer ILR5 = 0xFF; // IRQ20: Watch timer / counter // IRQ21: external interrupt ch 8-11 // IRQ22: 8/16-bit timer ch1 (lower) | external interrupt ch 12-15 // IRQ23: Flash | Custom ch1 } /*--------------------------------------------------------------------------中断处理声明,可以看到这里有两个中断的处理声明。一个是 DefaultIRQHandler 。另一个是 IRQ_Reloa dTimer0 。DefaultIRQHandler 的处理声明主要是当发生误中断时,程序会自动寻找子程序名为 DefaultIRQHandle r 的中断处理子程序,在下面中会谈到。而 IRQ_ReloadTimer0 则是主程序中的中断子程序在这里的声明。必须要在这里先声明,然后中断程序才能在主程序中正常运行。 -----------------------------------------------------------------------------*/ __interrupt void DefaultIRQHandler (void); __interrupt void IRQ_ReloadTimer0 (void); /*------------------------------------------------------------------------------------------------------------这里定义了全部的中断,其中 23 中断很少用到。可以看到由于要用重载定时器的中断, 因而中断名也被改为和上面的声明以及 main.c 中的子程序的名称一致。相应的当读者要用到其他的中断时,同样也要对对应的中断名进行修改。没有用 到的中断可以不用进行预定义。当然如果不进行预定义的话,则如果在未定义的地方发生了中断将会导致 程序跑飞,增加编译时检查错误的难度。 -------------------------------------------------------------------------------------------------------------*/ #pragma intvect DefaultIRQHandler 0 // IRQ0: external interrupt ch0 | ch4 #pragma intvect DefaultIRQHandler 1 // IRQ1: external interrupt ch1 | ch5 #pragma intvect DefaultIRQHandler 2 // IRQ2: external interrupt ch2 | ch6 #pragma intvect DefaultIRQHandler 3 // IRQ3: external interrupt ch3 | ch7

#pragma intvect DefaultIRQHandler 4 // IRQ4: UART/SIO ch0 #pragma intvect DefaultIRQHandler 5 // IRQ5: 8/16-bit timer ch0 (lower) #pragma intvect DefaultIRQHandler 6 // IRQ6: 8/16-bit timer ch0 (upper) #pragma intvect DefaultIRQHandler 7 // IRQ7: LIN-UART (reception) #pragma intvect DefaultIRQHandler 8 // IRQ8: LIN-UART (transmission) #pragma intvect DefaultIRQHandler 9 // IRQ9: 8/16-bit PPG ch1 (lower) | UART/SIO ch1 #pragma intvect DefaultIRQHandler 10 // IRQ10: 8/16-bit PPG ch1 (upper) | I2C ch1 #pragma intvect IRQ_ReloadTimer0 11 // IRQ11: 16-bit reload timer ch0 #pragma intvect DefaultIRQHandler 12 // IRQ12: 8/16-bit PPG ch0 (upper) #pragma intvect DefaultIRQHandler 13 // IRQ13: 8/16-bit PPG ch0 (lower) #pragma intvect DefaultIRQHandler 14 // IRQ14: 8/16-bit timer ch1 (upper) #pragma intvect DefaultIRQHandler 15 // IRQ15: 16-bit PPG ch0 + ch2 #pragma intvect DefaultIRQHandler 16 // IRQ16: 16-bit reload timer ch1 | I2C ch0 #pragma intvect DefaultIRQHandler 17 // IRQ17: 16-bit PPG ch1 #pragma intvect DefaultIRQHandler 18 // IRQ18: 10-biat A/D-converter #pragma intvect DefaultIRQHandler 19 // IRQ19: Timebase timer #pragma intvect DefaultIRQHandler 20 // IRQ20: Watch timer / counter #pragma intvect DefaultIRQHandler 21 // IRQ21: external interrupt ch 8-11 #pragma intvect DefaultIRQHandler 22 // IRQ22: 8/16bit timer ch1 | ext. interrupt ch 12-15 #pragma intvect DefaultIRQHandler 23 // IRQ23: Flash | Custom ch1 //#pragma asm // .SECTION INTVECT23,CONST,LOCATE=0FFCCH // .DATA.W _DefaultIRQHandler // IRQ22: 8/16bit timer ch1 | ext. interrupt ch 12-15 // .SECTION INTVECT22,CONST,LOCATE=0FFCEH // .DATA.W _DefaultIRQHandler // IRQ23: Flash | Custom ch1 //#pragma endasm /*--------------------------------------------------------------------------DefaultIRQHandler() 这个模块主要是用来处理除你的程序定义外的所有的其他中断。它的主要作用在于防止因为一些对寄存器 的错误设置所导致的不应有的中断,进而使得程序跑飞。同时,它也能够间接的帮助编程者检查自己的程 序。当在调试时,结束整个程序的连续运行后,发现程序最终跑到这里,则很有可能是程序中错误的是用 了一些中断。真正需要用到的中断处理程序应该在 main.c 的文件里编写。 -----------------------------------------------------------------------------*/ __interrupt void DefaultIRQHandler (void) { __DI(); // disable interrupts while(1)

__wait_nop(); // halt system } 上面的中断程序,包含了读者在编程时可能要用到的几乎所有的中断和对应的声明。读者需要根据自己的 实际情况来合理的编写程序,才能将中断用到灵活自如。 第 2 节 开发环境设置 开发环境 MCU C compiler 在 project 菜单中选择 setup project 选项,选择之后会出现如图 6-2 所示

可以看到 setup project 主要由 general, MCU, C compiler, Assembler, Linker, Librarian, Converter, Debug 这几个选项框组成,而其中读者主要要注意的选项框有 3 个,分别是 MCU, C compile r, Linker 。

6.2.1 MCU 配置 MCU 选项框主要用来选择读者实际使用的 MCU 芯片型号,使用者只需要在对应的 Target MCU 下面的下拉菜单中选择所用的芯片型号即可。由于使用的软件 softune V3 是一款向下兼容的软件,因此 拥有 8L 系列富士通公司的芯片也可以用此软件进行编程。在图 6-3 中,可以选择实际要使用的芯片型号即可

6.2.2 C compiler 配置 C compiler 选项卡主要是用来对用户接下来进行 C 编译所作的一个初始的规定。其中,Category 选项框主要是设置在编译时的一些语言习惯,编译优先级,包含的路径,宏定义以及是否现实 Debug 信息 等。

其中,编译优先级和编译的语言习惯是我们需要注意的。在选择框中选择编译优先级,可以看到如图 6-5 的界面:

要注意的是由于用户使用 softune V3 上时,在默认编程优先级别可能会自动设置成速度优先,即:Speed Priority

。从而导致一些系统认定速度级别较低的代码,如延时程序等无法被编译。此外,Size Priority 则可能在一些大的堆栈的设置下起作用进而导致部分代码无法被编译。因此,这里我们建议读者将这里的 编译优先级统一设定为 None ,这样就不会对你的编程造成一些意想不到的影响。 这里我们用一段程序来说明 Speed Priority 对实际应用的影响。

上面的这段程序中,用黑色所表示的是笔者编写的源代码,而蓝色字体表示的是软件对所写的进行一个汇 编的翻译,前面的箭头和圆圈表示设置断点的位置。对于那些编程熟练的读者可以比较容易的发现,在速 度优先的情况,delay 子程序居然没有进行任何编译, 或者准确的说是 for 循环语句并没有进行任何编译, 当然也无法设置断点。 而当速度优先级设置为 none 时,有下面可以看到 delay 的子程序进行了编译,当然相应的断点也可以设置。在普通的编译报错里面,是完全不会提示读者的。因 此,读者在初始设置时务必要小心这些隐藏的陷阱。

在设定完编译优先级之后, 我们再来看下编译语言分析的设置: Language 。 这里我们首先看一段报错提示:

当你看到这段报错提示,不要以为就一定是你的代码出现了语法错误。其实还有可能是因为在语言分析这 一栏中你没有将最下一行的方框打上勾,更需要提醒的是如果为默认设置的话,软件将不会对下面一行勾 上。那么语言分析的最下面一栏究竟是什么,其实仔细一看就可以明白,如图 6-6。它是问使用者是否将’ //’之后的语句看成是注释语句。勾上表示是,不勾则依然当作普通的主程序语句处理。所以如果使用类 似的注释语句而又没有在编译语言习惯中设定的,那么 Softune V3 会把有//之后的语句也当作程序进行编译,当然就会报错了。

6.2.3 Linker 的配置 如果你前面都已经设置正确,那么要留意的最后一个问题就是对选项框 Linker 的设置,Linker 中的 Cat egory 有很多选项,我们要注意的主要由两个。第一个是 Disposition/Connection 选项,第二个是 Register Bank。选择 Disposition/Connection 选项,可以进入图 6-7 的界面,默认模式为 mode 2

这里需要在区域名中分别添加如图 6-7 中的@INIT 和@DIRINIT 并在内容类型中分别选择 Const 和 Dircon st ,或者全部选择 Data 类型,然后依次点 Set 添加入下面区域名表即可。这主要是由于在常用的 start8FX.asm 文件中,当初始 化时,有部分代码被放在了 ROM 区的 INIT 和 DIRINIT 里面。如果没有对区域进行设置,而又使用了 start8FX.asm 文件,在编译时会报错提示,说 在初始化中没有对应的区域。 由于编写的任何代码都要有存放的空间,因而还必须要在 Register Bank 选项中为其设定一个堆栈来装载这些代码。在通常的默认情况下,软件会设定寄存器堆栈表设定堆栈 0 来装载这些代码。这样的设定对于一般的程序而言已经足够,当然如果有特殊要求或者要座大型的工程也 可以按读者的需要自行设定更多的堆栈。 第 3 节 调试与仿真 调试 硬件仿真 软件仿真 仿真 Monitor Debugger 主要的仿真调试软件帮手有如下几个:watch 窗口;memory 窗口,register 窗口。读者在程序编译暂停时可以对程序中的各种变量和寄存器进行观测。具体的方法是选中要观测的变 量,然后右键点击,在弹出的菜单中选择 watch 便会出现图 6-8 所示的 watch 框。

Watch 选择框由 3 部分中第一部分为变量名,第二部分为变量类型,第三部分为观测变量的取样个数。这里是第二部分需要 注意,但读者不选择观测变量的类型会默认为自动,即 C language 。该类型的含义是指由读者在编程中所定义的各种变量,但不包括硬件中的寄存器。如果读者要观测的是 一个寄存器的值,而使用的类型时自动或者 C 语言,则此时观测的实际为该寄存器在硬件中的存储地址。然后读者要根据地址在 memory 窗口中去查找该地址下实际的值,才能真正找到正确的存储器值。当然更简单的方法是当观测值为寄存器 值时,将类型选择为第三个 Assembler 。这时在 watch 窗口下观测到的便是该寄存器的实际值了。 watch 窗口只能在程序仿真运行的中断期间,才能显示,故无法对要观测的值进行连续的测试。

memory 窗口,memory 窗口主要用于对连续地址下的存储值进行有效的观测。它在总线传输中,判断传输是否正确能起到非常重 要的作用。具体的应用将在异步传输中结合实例作进一步说明。 在调试程序时,为了对程序的部分段代码的逻辑进行细致的观测,我们通常会设置断点。设置断点的方法

很简单,只要在 Debugger 的暂停模式下,点击要设置断点的代码前的小圆圈即可。设置好断点的区域如图 6-10 所示。

当要解除掉这些断点时,可以在设置断点的红叉处在点下,即可取消。也可以在主菜单中,选择 Debug 。 在 Debug 的下拉菜单中选择 Breakpoints ,便会看到如图 6-11 的弹出菜单。

以清楚地看到已有断点所在的地址和代码段,并可以根据自己的需要设置新的断点或删除已有断点。上面 的 Debugger 的工具均可以在软件仿真和硬件仿真中,合理的使用这些工具可以有效的提高检查各种语法和逻辑错误的 效率,节约时间,更快更好的编写出代码。 Mix Display 在 Debug 中选择 Mix Display 时,C 语言源程序与对应反汇编代码将会同时在代码中显示,同时程序执行时也将按照汇编代码段一步一步地进 行运行。具体操作方法是在 Debug 下右键点击,然后在弹出框中选择 Mix Display 即可得到下面编译结果:

可以看到当运行 Mix Display 时,断点设置也将按照反汇编代码的每一步进行设置。同时由于 Softune V 3 支持在 C 中嵌入汇编代码,因此当有些地方必须要用到汇编,而编写人员又想不起汇编代码该如何编写时,便可以 使用 Mix Display 。先将要用到汇编用 C 写出,再由 Mix Display 自动编译为汇编,再带入到程序中即可。 执行和中止程序 要连续执行程序可以点击如图 6-12 ,点击程序将一直运行,除非遇到中断或断点,否则不会停下。

有些时候为了进行对代码段进行局部的检测,除了设置断点外,我们还会是用到单步运行和一些其他运行 方式。这里如图 6-13 所示

在连续运行的右边第一个,便是单步运行(Step in),点击该图标时,程序将完全按照代码一步一步的执 行。在单步运行右边是 Step over,其作用是直接跳过循环运行下一句代码。运行它主要是在确定部分程序正确性的条件下,节约时间。 再右边的是 Step out ,即跳出循环。它的作用和 Step over 类似都是为了节约调试时间,提升效率。最右边的是 Reset,顾名思义就是重置 MCU,这在进行完一 次调试后,到需要带 MCU 重置下,以防对接下来的调试产生干扰。 中止运行,当然就是为了停止目前正在执行的程序。图标为: 。

当工程建立完成,Softune V3 配置完成,工程代码通过编译,那么最后就是要进行仿真了。在工程窗口可 以看到最下面有一个 Debug 文件夹,Debug 文件夹要装的就是软件仿真和硬件仿真文件,而在设置之前装的是一个*.sup 的文件。双击该文件,就可 以弹出仿真对话框,然后按照对话框的指示设定软件和硬件仿真。

6.3.1 软件仿真(Simulator Debugger) Softune V3 的软件仿真(Simulator Debugger )设定如下,要进行 8FX 芯片的仿真右键点击 Debug 文件 夹,选择 Add Setup ,再选择 New。这时便会弹出 Create New Setup 窗口,输入要创建的新的 Debug 文件名。创建完 Debug 文件名后,按照指示便可以进入 Setup Wizard 对话框了,如图 6-14 所示。

Simulator Debugger 点击下一步,然后一直选择默认的设置即可。这里结合下面的程序介绍如何软件仿真 调试 #include "mb95100.h" const unsigned char seg_display[12] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0xff, 0x00 }; /*************************************************************************/ /* Main Routine */ /*************************************************************************/ unsigned long delay; char counter; void main(void) {// initialize I/O-ports PDR0 = 0xff; // Port 0: DDR0 = 0xff; // 7-Segment display (all segments off) PDR1 = 0x00; // Port 1: DDR1 = 0xfe; // P10 = UI0 = Input; other pins are set to output 'L' // 初始计数设为 0 counter = 0;

// 进入主循环,LED 将在循环中不停的显示各种值 while(1) { PDR0 = 0xFF; PDR0 = seg_display[counter]; for(delay = 0; delay < 10000; delay++) asm("\tNOP"); counter = counter + 1; if (counter == 10) counter = 0; } } 进入 Simulator 后,选中 counter 后点击右键,在弹出的选项框中通过 watch 窗口观测 counter 的值。 前面介绍过入 watch 窗口观测的值为变量,则设为 C 语言;为寄存器则设为 Assembler ;这里设置 C 语言。按照类似的规则, 再在 watch 窗口中观测 PDR0 端口寄存器。可以得到如图 6-15。

在该窗口下可以看到 counter 和 PDR0 的值,初始值为 0。然后可以在程序中设置断点,让其进行周期性 的运行,就可以看到 counter 和 PDR0 的值发生变化。可以看到当 counter = H’01 时 PDR0 = H’06。因为 PDR0 输出的是 counter 的 LED 显 示码。如果在 P0 口接入了 7 段 LED,同时执行的是硬件仿真那么 LED 就应该会显示 1。同时当程序执行了 10 次后,就 应该让 counter 回到 0。 由上可知,通过 watch 窗口和寄存器窗口,Simulator Debugger 可以对程序的一些基本语法和逻辑进行检测,但因为是脱离了硬件进行的测试。所以对于实际中各种硬件 在时序以及其他的一些要求是否满足上,并不能找到答案,帮助有限。例如如果要涉及到必需的硬件支持, 如 ADC ,各种总线调试。由于在调试时,必须使用硬件功能,那么软件将几乎无法对最终的硬件调试起到任何的 帮助作用。 此外软仿真使用的工具和可以实现的功能如 watch 窗口和寄存器窗口,硬件仿真都可以完成,而且部分工具如寄存器窗口在进行一些特定的硬件调试中,可 以对程序的验证起非常好的帮助作用。故现在不十分推荐使用软仿真功能。

6.3.2 硬件仿真(Emulator Debugger)

由于现在用户所使用的 8FX 系列的晶振统一为 4MHz 所以在设定主时钟频率时,也要设定为 4MHz ,否则在编译时序时会出现问题。其他方面用户可以直接采用默认设置便可。 使用 Emulator 时,必须连接用户开发的目标系统,通过借助于 Emulator 来排除目标系统样机中的程序错 误和硬件故障。 6.3.3 Monitor Debugger 在线仿真 Monitor Debugger 是目前富士通公司正对目前单片机程序硬件调试困难,而专门编写的新的软件。通常的硬件调试及程序编 写,需要使用评估板先对编写程序进行调试,在烧写到实际的单片机 MCU 中,这样既麻烦又费时费力费钱。Monitor Debugger 出现后,使得程序直接在实际使用的单片机 MCU 中 进行调试变为可能。 在使用 Monitor Debugger 在线仿真之前,首先必须要注意的是使用 Monitor Debugger 在线仿真的工程代码与之前的用软硬件仿真代码有很大的不同。使用 Monitor Debugger 在线仿真的工程 代码如图 6-17 所示

在图 6-17 中,可以发现相比普通的工程代码,在线仿真代码多了很多新的子文件。其中,与在线仿真极 为相关的有 ASM 文件夹里的 fgm_cfg.asm,C 文件里面的 FGM.rel, autoboot.c 以及 Include Files 里面的 fgm.h 和 fgm_cfg.h 。这几个文件直接涉及在线仿真,不能进行随意修改。笔者建议如果要进行在线仿真,则最好不要修改上 述的任何文件。这些文件可以在富士通公司的网站以及本书的附录里面找到。 在这几个文件中,fgm_cfg.h 头文件会对读者的在线仿真设置产生影响,期代码如下: /* Start address */ .IMPORT __start #define USER_STARTADR /* Stack top */ .IMPORT STACK_TOP #define USER_STACKTOP STACK_TOP /* Auto-boot address */ .IMPORT _AutoBootCheck #define USER_ABOOTADR _AutoBootCheck __start

/* password (4byte) */ #define USER_PASSWORD 0xFFFFFFFF

这里需要注意的是最后一句:#define USER_PASSWORD 0xFFFFFFFF。在进行在线仿真设置时,会要求程序员输入在线仿真密码,就是这里的 0xFFFFFFFF。 要进行在线仿真的流程如下,必须首先将调试好工程后生成的.mhx 工程对应机器代码烧入进单片机中。即 使用 Monitor Debugger 的 USB 编程器烧入进实际的单片机,可以进入如图 6-18 的烧录界面,根据用户实际拥有的富士通单片机选择该

单片机的家族和对应的型号,同时选择晶振频率,一般均为 4MHz 。然后点击 Full Operation(D+E+B+P) 就可以将整个编写的程序烧录进取。当然也可以由下面的 Downloa d 按键一步一步依次完成,结果也是一样的。

烧入完成后,回到 Softune V3 界面中。和软硬件仿真类似,在 Debug 文件夹里面选中 Monitor Debugger ,或者单击右键,依照提示执行后会出现如图 6-17 的界面。

选择 Monitor Debugger 点击下一步。系统会询问你进行 Debug 的开始密码,这里按照刚才所介绍的输入进密码,点击下一步。然后会要你设置接口,选项其实只有一个 就是默认项 USB 接口,以后会要求你设置时钟晶振,和普通设置一样通常为 4MHz 。以后按照默认设置即可。设置完成后 S oftune V3 界面会进入和普通软硬件仿真类似的调试界面,既说明你已经进入到在线仿真的调试中去。 Monitor Debugger 在线仿真作为富士通最新设计的调试工具, 有其出色的一面也有其不足之处。 总结如下: 使用 Monitor Debugger 在线仿真的优点在于可以直接在单片机中烧写和调试,保证调试的准确性,调试成功则立即可以在单片机

中实现功能,避免了因为仿真器与实际芯片差异所导致的在仿真器中调试通过的程序可能无法在芯片中完 全实现功能的尴尬。同时出现此类情况时,要进行矫正是非常麻烦和困难的。 缺点在于因为仿真程序代码都必须直接拷入对应单片机中进行调试,而实际单片机在调试时不如测试芯片 和仿真硬件那样进行过专门的优化,所以调试速度很慢。在进行单步调试时,这种缺陷表现得尤为明显。 此外,使用 Monitor Debugger 在线仿真在设置断点时,最多只能设置两个断点。而如果是普通的硬件仿 真,可以设置大量的断点,以方便监测。 第 7 章 定时/计时器应用设计 (8FX 系列单片机的一大特点就是拥有丰富的定时器/计时器以及对应的大量专用输出端口。定时器/计时 器包括了 8/16 位复合定时/计时器,8/16 位 PPG,16 位 PPG 定时器和 16 位重载定时器。每个定时/计时 器都分别具备 PWM 输出功能。同时每个定时/计时器都有自己的特色,以实现一些特殊功能) 第 1 节 定时/计时器概述 定时/计时器 8FX 8FX 系列单片机的一大特点就是拥有丰富的定时器/计时器以及对应的大量专用输出端口。定时器/计时器 包括了 8/16 位复合定时/计时器,8/16 位 PPG,16 位 PPG 定时器和 16 位重载定时器。每个定时/计时器 都分别具备 PWM 输出功能。同时每个定时/计时器都有自己的特色,以实现一些特殊功能。 第 2 节 8/16 位复合定时/计时器 复合 定时/计时器 寄存器配置 8/16 位复合定时/计时器作为 8FX 系列芯片中一种专门的备用定时/计时器。其最大的特点与优点在于广 泛的适用性。8/16 位复合定时器/计时器可选择 16 种不同的工作模式,用来模拟 PPG 定时器,重载定时 器,输入捕捉定时器的功能。 复合计时器中断说明如表 7-1:

复合计数器端口引脚如表 7-2:

7.2.1 复合定时/计时器原理及应用 复合计时器功能说明: 间隔定时器功能(单步模式):当使用此功能时,计数器从 0 开始计数,当达到寄存器中的设置值时,计 数器输出取反,同时产生中断请求信号,计数停止。 间隔定时器功能(连续模式):当使用此功能时,计数器从 0 开始计数,当达到寄存器中的设置值时,计 数器输出取反,同时产生中断请求信号,计数重新由 0 开始计数。计数器也因此输出一个方波。 间隔定时器功能(自由模式):当使用此功能时,计数器从 0 开始计数,当达到寄存器中的设置值时,计 数器输出取反,同时产生中断请求信号,计数仍然持续计数,直到 0xFF 为止,又从 0 开始计数,计数器 用样输出一个方波。 PWM 波定时器模式(固定周期):当使用此功能时,在 8 位定时器模式下,周期被固定为 0xFF,在 16 位定时器模式下,周期被固定为 0xFFFF。高脉冲宽度有对应寄存器设置。 PWM 波定时器模式(可变周期):当使用此功能时,在 8 位定时器模式和 16 位定时器模式下,周期均为可变周期,通过对应的寄存器进行设置。同时高脉冲宽度也要由对应寄存器设 置。 PWC 定时器功能:使用此功能时,可以对外部输入脉冲的宽度和周期进行测量。当扫描到外部输入脉冲边 沿时,计数器开始从 00H 计数,同时结合不同的情况产生对应的终端请求。(后面会有详细地介绍) 输入捕捉功能:使用此功能时,当捕捉到外部的输入信号时,计数器的计数值将会被存储到寄存器中。该 功能有两种模式自由运行模式和清除模式。 清除模式下,计数器从 0 开始计数,当扫描到输入信号,则将计数器此时的值存入寄存器中,同时计数器 重新从 0 开始计数。 在自由运行模式下,计数器从 0 开始计数,当扫描到输入信号,计数器此时的值同样存入寄存器中,当计 数器依然继续技术并不会被清除。 7.2.2 复合定时/计时器寄存器配置 8/16 位复合定时器/ 计时器的寄存器包括了 T00CR0/T01CR0 控制寄存器,T00CR1/T01CR1 控制状态寄存 器,TMCR0 定时器/计时器模式控制寄存器,T00DR/T01DR 专用的数据寄存器。 T00CR0/T01CR0 控制寄存器用来控制定时器的 16 种运行模式,计数时钟的选择以及中断信号是否使能。 T00CR1/T01CR1 控制状态寄存器用来控制定时器的运行,停止,暂停,各种中断是否使能,各种中断信号 以及寄存器是否输出和输出的初始值。 TMCR0 定时器/计时器模式控制定时器 timer00 和 timer01 的噪声抑制(高电平噪声抑制,低电平噪声抑 制,高低电平噪声抑制或不进行抑制),定时器按 8

或者 16 位运行,计时器输入信号为外部输入信号还是内部输入信号,定时器 timer00 和 timer01 是否输 出。 T00DR/T01DR 专用的数据寄存器用来设定或者记录定时器的数据值。 7.2.3 复合定时/计时器应用设计范例 前面介绍了复合计数器有多种功能,其中作为 PWM 波输出又有两种主要的工作模式即固定周期模式和可变 周期模式,下面将结合可变周期 PWM 波输出模式来说明 8/16 位复合定时器的应用。 由于可变周期模式,复合定时器/计时器需要使用 T00DR/T01DR 分别进行占空比和周期的设置,因此周期 最多只能是 0xFF,且由于是周期循环所以不需要使用 IR 中断来表示计时结束,BF 中断来显示寄存器存储的数值。而只需要使用 IF 来表示一个周期的 PWM 方波结 束。可变周期 PWM 波输出模式的原理和操作原理如图 7-1 所示

PWM 可变周期模式下要用到的寄存器位:

可用位视读者的需要情况来定,如 IE,IFE,使用中断就必须要将 IE,IFE 位设定为 1。不使用位可以不 用设定使其为默认设置, 也可以任意设定值均不会影响定时器的工作。 本文中所有工程程序中的 Start8FX. asm 初始化程序和中断向量表 vectors.c 均省略,请读者根据前面的说明自行进行编写。样例说明:将 8

/16 位复合定时器的输出设定为按一定规律变化的可变周期及可变占空比的 PWM 波, 并用 LED 显示周期及 占空比变化的不同时期。主程序代码如下: #include "mb95100.h" //LED 的每一位晶体管设置以及 0~9 预定义 #define SEG_A 0xFE #define SEG_B 0xFD #define SEG_C 0xFB #define SEG_D 0xF7 #define SEG_E 0xEF #define SEG_F 0xDF #define SEG_G 0xBF #define SEG_DP 0x7F #define SEG_0 SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F #define SEG_1 SEG_B & SEG_C #define SEG_2 SEG_A & SEG_B & SEG_D & SEG_E & SEG_G #define SEG_3 SEG_A & SEG_B & SEG_C & SEG_D & SEG_G #define SEG_4 SEG_B & SEG_C & SEG_F & SEG_G #define SEG_5 SEG_A & SEG_C & SEG_D & SEG_F & SEG_G #define SEG_6 SEG_A & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G #define SEG_7 SEG_A & SEG_B & SEG_C #define SEG_8 SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G #define SEG_9 SEG_A & SEG_B & SEG_C & SEG_D & SEG_F & SEG_G const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, 0xff, 0x00 }; /*************************************************************************/ /*主程序部分*/ /*************************************************************************/ void main(void) { unsigned int pwm_value; // 可变低电平宽度设置 unsigned int pwm_period; // 可变周期设置 unsigned int delay; // 延时设置 unsigned int direction = 0; const int DELAY = 500; // 各 I/O 端口初始化 PDR0 = 0xff; // Port 0: DDR0 = 0xff; // 7 段显示 PDR1 = 0x00; // Port 1: DDR1 = 0xff; PDR2 = 0x00; // Port 2: DDR2 = 0xff; PDR3 = 0x00; // Port 3:

AIDRL = 0xff; // 作为普通 I/O 端口无模拟输入信号 DDR3 = 0xff; PDR4 = 0x00; // Port 4: AIDRH = 0xff; //作为普通 I/O 端口无模拟输入信号 DDR4 = 0xff; PDR5 = 0x00; // Port 5: DDR5 = 0xff; PDR6 = 0xff; // Port 6: DDR6 = 0xff; PDR7 = 0x00; // Port 7: DDR7 = 0xff; PDR8 = 0x00; // Port 8: DDR8 = 0xff; PDRE = 0x00; // Port E: DDRE = 0xfF; InitIrqLevels(); // 初始化中断寄存器和 IRQ 中断向量表 __EI(); // 全局中断使能 __set_il(3); // 设置全局中断级别为 3,最低级别 pwm_value = 0x10; pwm_period = 0xFF; T00CR0 = 0x54; //设定该计数器主时钟周期为 3.2us ,禁止该计数器中断 T00CR1_STA = 0; // 关闭计数器,在寄存器设定完成之前,建议先单独设置开始位为 0,以防止计数器在 设置完成前误触发 T00CR1_HO = 0; // 计数器开启模式 T00CR1_OE = 1; // 开输出 T00CR1_IE = 0; // 中断位不使能 T00CR1_IF = 0; // 中断标志位为 0 TMCR0_FE1 = 0; TMCR0_FE0 = 0; // 两个寄存器均没有用到电平噪声抑制功能 T00DR = pwm_value; T01DR = pwm_period; T00CR1_STA = 1; T01CR1_STA = 1; // 由于两个数据寄存器均用到,故两个计数器均开启 while(1) // " 主循环" {

for (delay = 0; delay < DELAY; delay++) { asm("\tnop"); }//for() if (direction == 0) // 第零阶段,低电平宽度增加 { if ( pwm_value < 0xFF) { pwm_value++;} else {direction = 1;} } if (direction == 1) // 第一阶段,低电平宽度减少 { if ( pwm_value > 10) { pwm_value--;} else {direction = 2;} } if (direction == 2) // 第二阶段,周期减小 { if ( pwm_period > 30) { pwm_period--;} else {direction = 3;} } if (direction == 3) // 第三阶段,周期增加 { if ( pwm_period < 0xFF) { pwm_period++;} else {direction = 0;} } T00DR = pwm_value; T01DR = pwm_period; PDR0 = seg_display[direction]; //LED 显示各个阶段 }//while(1) }//main 这里的周期与低电平宽度变化设置是在主程序的循环中进行,也可以将此程序改为在中断中设置,具体的 方法是在比较匹配中断发生后, 设置中断子程序并在其中根据所处的阶段相应的改变周期或者低电平宽度。

除开 PWM 波输出外复合计时器/定时器的另一个比较常用的功能是 PWC 模式即端口高电平捕捉模式。 PWC 模式用来捕捉端口处高电平的持续时间。在各种 PWC 模式下,当扫描到对应的电平边沿时,计数器开 始计数。扫描结束则计数器停止工作。寄存器通过 IR 结束中断位来表示扫描结束,同时存在 BF 位表示结 束中断时,计数器保存有数值,可以对其进行读取。此外当高电平持续时间过长,导致计数器的寄存器溢

出时,寄存器可以由 IF 位来进行提示。注意的是,8FX 系列的芯片对于中断都没有设置通常自动清零的 功能,故这里所有的中断都要求程序员手动清零。 PWC 模式下的工作原理如图 7-3 所示

使用 Composit timer 在 PWC 模式下具体要使用到的寄存器如图 7-4 所示 这里,PWC 模式下设置了多种 形式的高电平捕捉,故 T00CR0 寄存器中的 F0~F3 需要读者结合自己的需要并参考 PDF 说明文档设定相应 的捕捉模式。 PWC 模式在扫描设置中包括了内部扫描和外部扫描两种模式。内部扫描指的是通过 LIN-UAR T 总线传入的信号,请详见 LIN-UART 总线一章。外部模式则主要依靠 EC0/1 端口来扫描高电平。

样例说明:设置复合计数器为外部 PWM 波监测模式,通过 LED 显示发生计数器溢出的次数,同时当扫描结 束时 LED 给出一个特殊的显示。程序代码如下: #include "mb95100.h" #define SEG_A 0x01 #define SEG_B 0x02 #define SEG_C 0x04 #define SEG_D 0x08 #define SEG_E 0x10 #define SEG_F 0x20 #define SEG_G 0x40 #define SEG_DP 0x80

#define SEG_0 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F #define SEG_1 SEG_B | SEG_C #define SEG_2 SEG_A | SEG_B | SEG_D | SEG_E | SEG_G #define SEG_3 SEG_A | SEG_B | SEG_C | SEG_D | SEG_G #define SEG_4 SEG_B | SEG_C | SEG_F | SEG_G #define SEG_5 SEG_A | SEG_C | SEG_D | SEG_F | SEG_G #define SEG_6 SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G #define SEG_7 SEG_A | SEG_B | SEG_C #define SEG_8 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G #define SEG_9 SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8,SEG_9, 0xff, 0x00 }; //LED 初始化 /*************************************************************************/ /*主程序部分*/ /*************************************************************************/ int a; void PWC_init(void) // 初始化 PWC { T00DR = 0x00; T00CR0 = 0x85; //开中断,机器时钟周期 01us,PWC 工作模式 T00CR1_STA = 0; T00CR1_SO = 0; T00CR1_HO = 0; T00CR1_IE = 1; T00CR1_IF = 0; T00CR1_IR = 0; T00CR1_BF = 0; TMCR0_FE1 = 0; TMCR0_FE0 = 0; TMCR0_MOD = 0; TMCR0_IIS = 1; T00CR1_STA = 1; } void main(void) { a = 0; //端口设置 PDR0 = 0x00; DDR0 = 0xff; PDR1 = 0x00; DDR1_D10=0; PUL1 = 0xff; PDR2 = 0x00; //DDR2 = 0xff; DDR2_D24=0;

PDR3 = 0x00; // 运行模式 // 中断位使能 // 对 3 个中断标志位赋初值 // 无噪声抑制作用 //8 位 // // Port 0: // 7 段显示 // Port 1: // P10 = UI0 设置输入,用来进行输入捕捉 // Port 2: // // Port 3: AIDRL = 0xfe; DDR3 = 0xfe; PDR4 = 0x00; // Port 4: AIDRH = 0xff; DDR4 = 0xff; PDR5 = 0x00; // Port 5: DDR5 = 0xff; PDR6 = 0xff; // Port 6: DDR6 = 0xff; PDR7 = 0x00; // Port 7: DDR7 = 0xff; PDR8 = 0x00; // Port 8: DDR8 = 0xff; PDRE = 0x00; // Port E: DDRE = 0xff; InitIrqLevels(); //中断级别设置,全局中断使能 __EI(); __set_il(3); PWC_init(); PDR0 = seg_display[0]; while(1) {} } __interrupt void PWC (void) { a++;

if(T00CR1_IF == 1) PDR0 = seg_display[a]; //出现数据溢出时,LED 开始显示 if(a == 9)a = 0; T00CR1_IF = 0; T00CR1_BF = 0; T00CR1_IR = 0; if(T00CR1_BF == 1 && T00CR1_IR == 1) {PDR0 = 0x80; a = 0; } // 出现扫描结束时,LED 开始显示特殊标志 DP } 第 3 节 8/16bit PPG 寄存器设置 I/O 端口选择 PPG 8/16 位 PPG 是一个 8 位重载定时器模块,它通过对脉冲输出的控制来形成 PPG 输出。8/16 位 PPG 也可 以用两个 8 位模块来构建 16 位定时器。 7.3.1 8/16bit PPG 原理及应用 8/16 位 PPG 的工作模式包括了:双 8 位 PPG,8 位预分频 PPG,16 位 PPG。 8/16 位 PPG 中断说明

7.3.2 8/16bit PPG 寄存器设置及 I/O 端口选择 8/16 位 PPG 端口引脚:PPG00 和 PPG01。两者在前面介绍引脚时提到既可以作为 8-16 位 PPG 专用输出口 也可以当作普通得 I/O 口使用,主要由专用寄存器位 PC0:POEN0 和 PC1:POEN1 来控制。 要控制 8/16 位 PPG 的工作模式,则首先要对其寄存器进行了解。8/16 位 PPG 的控制寄存器包括:PPG 控制寄存器 0/1(PC0/1),PPG 周期设置寄存器 0/1(PPS0/1),PPG 占空比设置寄存器 0/1(PDS0/1),PPG 初始设置寄存器(PPGS), PPG 输出取反寄存器(REVC) PPG 控制寄存器 0/1(PC0/1),两个寄存器均能控制定时器时钟周期,输出使能,终端使能,中断标志位

读写。此外,PC0 还可以设置前面介绍的 PPG 的 3 种工作模式。 PPG 周期设置寄存器 0/1(PPS0/1)设置 PPG0/1 的周期,在 16 位 PPG 工作模式下,PPS0 为低 8 位周期 设置。 PPG 占空比设置寄存器 0/1(PDS0/1)设置 PPG0/1 的占空比,在 16 位 PPG 工作模式下,PDS0 为低 8 位 占空比的设置。 PPG 初始设置寄存器(PPGS)设置 PPG0~3 的各个管道的运行使能。 PPG 输出取反寄存器(REVC)设置 PPG 各个通道的输出值是否取反。 7.3.3 8/16bit PPG 应用设计范例 这里通过一个小程序来说明:双 8 位独立 PPG 和 8 位预分频 PPG 的具体区别。程序说明让 8/16 位 PPG 以 8 位预分频模式工作同时输出双通道,代码如下: #include "mb95100.h" /*****************************************************************************/ /*主程序部分*/ /*****************************************************************************/ void main(void) { PDR2 = 0x00; // Port 2: DDR2 = 0xff; // PPS00 = 0x0A; // 设置 0 管道的周期(10 edges of prescaler) PDS00 = 0x03; // 设置 0 管道的占空比(3 edges of prescaler) PC00 = 0x68; // 8 位预分频模式,中断不使能,使能 PPG 输出 // 初始化 8 位通道 1 作为通道 0 的预分频 PPS01 = 0x80; // 设置 1 管道的周期 set period value for channel 1 PDS01 = 0x40; //设置 1 管道的占空比(always 50 % needed in prescaler mode) PC01 = 0x28; // 中断不使能,使能 PPG 输出 PPGS = 0x03; // PPG 0 和 1 管道输出运行 } 当上面的代码中 PC00 = 0x28 ,其他保持不变时,则程序变为以双 8 位独立模式工作同时输出双通道,然后通过示波器对比两次的输出波形及频率。读者可以发现两次的输出 波形完全一样。这是因为无论是 8+8 预分频还是双 8 位独立 PPG 工作模式下,由于设置的占空比完全一样,所以输出波形也完全一样。而测试频率就可以发现,两次模式 下 PPG1 的频率是相同的,而 PPG0 不同。这是因为两种模式均不影响 PPG1 的频率输出,而在程序设置中由于周期和占空比均设置一致,故 频率一样。而在 8+8 预分频模式下 PPG0 的频率主要由 PPG1 作为时钟预分频来影响,双 8 位独立 PPG 工作模式下两个定时器的频率是完全独立的,所以两种模式下 P PG0 的频率完全不同。由于可以利用 PPG1 作为时钟预分频因此在对 PPG0 的频率设置中,几乎可以设置任何频率,这是其他定时器/计时器所无法完 成的。 代码说明:通过中断控制多种 PPG 模式之间的切换的。 #include "mb95100.h" #define SEG_A 0x01

#define SEG_B 0x02 #define SEG_C 0x04 #define SEG_D 0x08 #define SEG_E 0x10 #define SEG_F 0x20 #define SEG_G 0x40 #define SEG_DP 0x80 #define SEG_0 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F #define SEG_1 SEG_B | SEG_C #define SEG_2 SEG_A | SEG_B | SEG_D | SEG_E | SEG_G #define SEG_3 SEG_A | SEG_B | SEG_C | SEG_D | SEG_G #define SEG_4 SEG_B | SEG_C | SEG_F | SEG_G #define SEG_5 SEG_A | SEG_C | SEG_D | SEG_F | SEG_G #define SEG_6 SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G #define SEG_7 SEG_A | SEG_B | SEG_C #define SEG_8 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G #define SEG_9 SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, 0xff, 0x00 }; //LED 初始化 unsigned char flag_16PPG; unsigned char flag_8PPG; unsigned char flag_16PPG_mode; unsigned char flag_8PPG_mode; /*****************************************************************************/ /* Main Routine */ /*****************************************************************************/ void main(void) { PDR0 = 0xff; // Port 0: DDR0 = 0xff; // 7 段显示 PDR1 = 0x00; // Port 1: DDR1 = 0xff ; PUL1 = 0xff; PDR2 = 0x00; // Port 2: DDR2 = 0x03; /*8-16 位 PPG 通道 ch0 输出 :8bit PPG 输出*/ PDR3 = 0x00; // Port 3: AIDRL = 0xff; DDR3 = 0xff; PDR4 = 0x00; // Port 4: AIDRH = 0xff; DDR4 = 0xff;

PDR5 = 0x00; // Port 5: DDR5 = 0xff; PDR6 = 0xff; // Port 6: DDR6 = 0x01; /*8-16bit PPG 通道 ch1 输出:16bit PPG 输出*/ PDR7 = 0x00; // Port 7: DDR7 = 0xff; PDR8 = 0x00; // Port 8: DDR8 = 0xff; PDRE = 0x00; // Port E: DDRE = 0xfc; // PE0=INT10, PE1=INT11 键位输入外部中断; //端口设置 flag_16PPG = 0x00; /*计数标志位*/ flag_8PPG = 0x00; /*计数标志位*/ flag_16PPG_mode = 0x00; /* 模式标志位*/ flag_8PPG_mode = 0x00; /*模式标志位*/ // __EI(); // 功能开始,中断设置 InitIrqLevels(); __EI(); __set_il(3); // allow all levels // globaly enable interrupts // set global interrupt mask to allow all IRQ levels

/*8-16bit PPG 初始化*/ /*PPG ch0: 双 8 位 PPG 独立模式 */ PC01 = 0x28; /* 中断使能, 1/MCLK count */ PC00 = 0x28; /* 中断使能, 1/MCLK count*/ PPS01 = 0xFF; /*PPG period = 0xFF*/ PPS00 = 0x7F; /*PPG period = 0x7F*/ PDS01 = 0x7F; /* Duty 50% (0xFF/2=0x7F) */ PDS00 = 0x3F; /* Duty 50% (0x7F/2=0x3F) */ REVC = 0x02; /* ch0-Upper unit output reverce level*/ /*PPG ch1:16bit PPG mode <->8bit PPG + 8bit pre-scaler mode */ /*初始化 16 位 PPG 模式*/ PC11 = 0x00; /* 中断,输出全关断 */ PC10 = 0xA8; /* 中断使能, 1/MCLK count*/ PPS11 = 0x01; /*PPG period = 0x01FF*/ PPS10 = 0xFF;

PDS11 = 0x01; /* Duty 75% (0x01FF/4*3=0x017F) */ PDS10 = 0x7F; /* PPG start */ PPGS = 0x07; while(1) { __wait_nop(); __wait_nop(); __wait_nop(); __wait_nop(); __wait_nop(); } } /*****************************************************************************/ /*中断 */ /*****************************************************************************/ /* PPG 中断处理*/ __interrupt void PPG_ch1_L_int(void) { __DI(); flag_16PPG ++; if (flag_16PPG_mode == 0){ if (flag_16PPG > 0x7F) { /* duty change */ PPGS_PEN10 = 0; /*16bit PPG stop*/ PDS11 = 0x00; /* Duty 25% (0x01FF/4=0x7F) */ PDS10 = 0x7F; flag_16PPG = 0; flag_16PPG_mode = 1; } } else { if (flag_16PPG > 0x7F) { /* duty change */ PPGS_PEN10 = 0; /*16bit PPG stop*/ PDS11 = 0x01; /* Duty 75% (0x01FF/4*=0x7F) */ PDS10 = 0x7F; flag_16PPG = 0; flag_16PPG_mode = 0; } } PC10_P UF0 = 0; /*count borrow bit clear*/ PPGS_PEN10 = 1; /*16bit PPG start*/ // 当条件触发,16 位 PPG 的占空比设置为 25%

__EI(); } // 当条件触发,16 位 PPG 的占空比设置为 75% __interrupt void PPG_ch1_U_int(void) { /*PPG_ch1 is "16bit PPG mode", so this handler is not used.*/ } __interrupt void PPG_ch0_U_int(void) { __DI(); flag_8PPG += 1; if (flag_8PPG_mode == 0){ if (flag_8PPG > 0x7F) { /* mode change */ PPGS_PEN00 = 0; /*8 位 PPG 停止*/ PPGS_PEN01 = 0; /*8 位 PPG 停止*/ PC01 = 0x28; /* 中断使能, 1/MCLK 计数*/ PC00 = 0x68; /*8 位预分频计数模式 */ PPS01 = 0x0A; /*PPG period = 0x0A*/ PPS00 = 0x0A; /*PPG period = 0x0A*/ PDS01 = 0x05; /* Duty 50% (0x0A/2=0x05) */ PDS00 = 0x05; /* Duty 50% (0x0A/2=0x05) */ flag_8PPG = 0; flag_8PPG_mode = 1; PC01_PUF1 = 0; /* 计数器借位标志位清零*/ PC00_PUF0 = 0; /* 计数器借位标志位清零*/ PPGS_PEN00 = 1; /*8 位 PPG 开始*/ PPGS_PEN01 = 1; /*8 位 PPG 开始*/ } // 触发条件产生,双 8 位 PPG 转变为预分频工作模式 else { PC01_PUF1 = 0; /* 计数器借位标志位清零*/ PC00_PUF0 = 0; /* 计数器借位标志位清零*/ } } else { if (flag_8PPG > 0x7F) { /* mode change */ PPGS_PEN00 = 0; /*8bit PPG stop*/ PPGS_PEN01 = 0; /*8bit PPG stop*/ PC01 = 0x28; /*interrput enable, 1/MCLK count*/ PC00 = 0x28; /*8bit PPG x2ch mode*/ PPS01 = 0xFF; /*PPG period = 0xFF*/ PPS00 = 0x7F; /*PPG period = 0x7F*/

PDS01 = 0x7F; /* Duty 50% (0xFF/2=0x7F) */ PDS00 = 0x3F; /* Duty 50% (0x7F/2=0x3F) */ flag_8PPG = 0; flag_8PPG_mode = 0; PC01_PUF1 = 0; /*count borrow bit clear*/ PC00_PUF0 = 0; /*count borrow bit clear*/ PPGS_PEN00 = 1; /*8bit PPG start*/ PPGS_PEN01 = 1; /*8bit PPG start*/ } //触发条件产生,预分频 PPG 转变为双 8 位工作模式,具体代码和前面的功能一样 else { PC01_PUF1 = 0; /*count borrow bit clear*/ PC00_PUF0 = 0; /*count borrow bit clear*/ } } __EI(); } __interrupt void PPG_ch0_L_int(void) { __DI(); PC00_PUF0 = 0; /*count borrow bit clear*/ __EI(); } 第 4 节 16bit PPG 定时器 PPG 定时器 16 位 PPG 定时器可以进行单步或者持续 PWM 方波的输出,周期和占空比同样可以分别进行 16 位设置。 故周期和占空比最大值为 65534。 PPG 的占空比和周期可以在运行自行调整。 7.4.1 16bit PPG 定时器原理及应用 16 位 PPG 定时器主要通过向下计数器进行减计数,当计数器的值与设置的占空比之乡同时,即改变方波 的高低电平,从而达到输出 PWM 的目的。具体原理如图 7-5 所示

16 位 PPG 定时器可以有多种中断触发条件:外部触发或减计数借位;正极性中上升沿被扫描到,负极性 下降沿被扫描到。具体的中断如表 7-4 所示

7.4.2 16bit PPG 定时器寄存器配置及 I/O 端口选择 16 位 PPG 定时器端口引脚:

16 位 PPG 定时器的对应寄存器:16 位 PPG 向下计数器寄存器(PDCRH0/PDCRL0), 16 位 PPG 周期设置缓冲寄存器(PCSRH0/PCSRL0),16 位 PPG 占空比设置缓冲寄存器(PDUTH0/PDUTL0),16 位 P PG 状态控制寄存器(PCNTH0/PCNTL0)。 16 位 PPG 向下计数器寄存器(PDCRH0/PDCRL0):存储当前 16 位 PPG 向下计数器值。 16 位 PPG 周期设置缓冲寄存器(PCSRH0/PCSRL0):设置 16 位 PPG 向下计数器周期。16 位 PPG 占空比设置缓冲寄存器(PDUTH0/PDUTL0):设置 16 位 PPG 向下计数器占空比值。16 位 PPG 状态控制寄 存器 PCNTH0 包括了计数器使能位,软件触发使能位,软件触发标志位,工作模式选择位,工作时钟选择位,输出使能 位。16 位 PPG 状态控制寄存器 PCNTL0 包括了输出取反位,PPG 专用端口输出使能位,终端模式选择位,中断标志位,中断请求使能位,硬件触 发使能位 0/1。 7.4.3 16bit PPG 定时器应用设计范例 前面介绍过 16 位 PPG 定时器可以在运行中改变周期和占空比值并运行,该节通过下面的样例详细说明其 使用。 #include "mb95100.h" #define SEG_A 0x01 #define SEG_B 0x02 #define SEG_C 0x04 #define SEG_D 0x08 #define SEG_E 0x10 #define SEG_F 0x20 #define SEG_G 0x40 #define SEG_DP 0x80 #define SEG_0 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F #define SEG_1 SEG_B | SEG_C #define SEG_2 SEG_A | SEG_B | SEG_D | SEG_E | SEG_G #define SEG_3 SEG_A | SEG_B | SEG_C | SEG_D | SEG_G #define SEG_4 SEG_B | SEG_C | SEG_F | SEG_G #define SEG_5 SEG_A | SEG_C | SEG_D | SEG_F | SEG_G #define SEG_6 SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G #define SEG_7 SEG_A | SEG_B | SEG_C #define SEG_8 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G #define SEG_9 SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4,

SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, 0xff, 0x00 }; /*****************************************************************************/ /*主程序部分*/ /*****************************************************************************/ void main(void) { const int DELAY = 100; // 延迟时间 // variables unsigned int pwm_value; // 设置的可变 PWM 占空比值 int delay; // 计数延时,以方便在示波器上观测波形变化 int direction; // 表示占空比输出的各阶段的信号 unsigned int pwm_see; // 初始化 I/O 端口 PDR0 = 0xff; // Port 0: DDR0 = 0xff; // 7 段显示 PDR1 = 0x00; // Port 1: DDR1 = 0xff; PDR2 = 0x00; // Port 2: DDR2 = 0xff; PDR3 = 0x00; // Port 3: AIDRL = 0xff; DDR3 = 0xff; PDR4 = 0x00; // Port 4: AIDRH = 0xff; DDR4 = 0xff; PDR5 = 0x00; // Port 5: DDR5 = 0xff; PDR6 = 0xff; // Port 6: DDR6 = 0xff; PDR7 = 0x00; // Port 7: DDR7 = 0xff; PDR8 = 0x00; // Port 8: DDR8 = 0xff; PDRE = 0x00; // Port E: DDRE = 0xfc; // PE0=INT10, PE1=INT11 键位输入; // 设置中断 InitIrqLevels(); // 初始化中断寄存器和 IRQ 向量表 __EI(); // 全局中断使能 __set_il(3); // 初始化 PPG 定时器 PCSR0 = 0xFFFF; // 设置周期(duration of high and low phase!) pwm_value = 10; // 设定 PPG 占空比初始值 PDUT0 = 10; PCNTH0_CNTE = 0; // 停止 PPG timer PCNTH0_MDSE = 0; // 选择 PWM PCNTH0_RTRG = 0; // 禁止触发 PCNTH0_CKS = 0; // 选择时钟模式(0: MCLK/1)

// PWM 周期= 65536 x 0.1us = 152.58Hz @ 10MHz 主时钟状态下 PCNTH0_PGMS = 0; // 禁止 PPG 输出

PCNTL0_EGS1 = 0; // 禁止触发扫描 TRG PCNTL0_EGS0 = 0; PCNTL0_IREN = 0; // 禁止终端请求 PCNTL0_POEN = 1; // 使能 PPG 输出 PCNTL0_OSEL = 0; // 正常输出模式 PCNTH0_CNTE = 1; // 使能 PPG timer, 等待触发开始 PCNTH0_STGR = 1; // 开始 PPG timer direction = 1; while(1) // " 主循环" { for (delay = 0; delay < DELAY; delay++) { asm("\tnop"); }//for() if (direction == 1) // 增加 PWM 占空比值阶段 { if (pwm_value <= 30000) { pwm_value = pwm_value + 10; // 增加 PWM 占空比值,但实际看见的输出波形不变 pwm_see = 30000; PDR0 = seg_display[0]; } else if ( pwm_value < 65530) { pwm_value = pwm_value + 10; // 增加 PWM 占空值,波形随占空值增加而改变 PDR0 = seg_display[1]; pwm_see = pwm_see + 10; } else { direction = 0; } //if(direction = 1) } if (direction == 0) // 减少 PWM 占空值比阶段 { if (pwm_value > 30000) { PDR0 = seg_display[2]; pwm_value = pwm_value - 10; // 减少 PWM 占空值,波形不变 pwm_see = 65530; } else if ( pwm_value > 0)

{ PDR0 = seg_display[3]; pwm_value = pwm_value -10; // 减少 PWM 占空值,波形随变化而改变 pwm_see = pwm_value - 10; } else { direction = 1; // set to upcounting direction (upcounting) } }//if(direction = 0) PDUT0 = pwm_see; // 将各阶段 PWM 占空比的值导入进寄存器中 }//while(1) }//main 第 5 节 16 位重载定时器 16 位 重载 定时器 16 位重载定时器在两种时钟模式下,分别有两种工作模式。16 位重载定时器可以作为间隔定时器当在定时器中发生向下溢出时,会产生一个中断信号。该定时器在各工 作条件下的触发模式如表 7-6 所示 表 7-6 16 位重载定时器工作模式

7.5.1 16 位重载定时器原理及应用 内部时钟模式下触发: 软件触发模式:计数使能位设为 1,软件触发位(TRG)设为 1。 外部边沿触发输入模式:计数使能位设为 1,且 MOD0~MOD2 下设置的有效边沿在 TI 口被扫描到。 外部电平输入模式:计数使能位设为 1,且 MOD0~MOD2 下设置的有效高地电平在 TI 口被扫描到。 外部时钟模式下触发:计数时钟设置位为 111,且 MOD0~MOD2 下设置的有效边沿在 TI 口被扫描到。 定时器的状态控制及状态间的转换如图 7-6 所示

7.5.2 16 位重载定时器寄存器配置及 I/O 端口选择 16 位重载定时器的对应引脚较为简单:TI0 和 TO0 。TI0 主要用来作为外部脉冲输入以触发定时器开始 工作;TO0 则作为重载定时器信号的外部输出。 16 位重载定时器的寄存器有:重载定时器状态控制寄存器(TMCSRH0)/(TMCSRL0), 重载定时器计数寄存器(TMRH0)(TMRL0)定时器重载寄存器/。/,(TMRLRH0)(TMRLRL0) 重载定时器状态控制寄存器(TMCSRH0)控制寄存器时钟模式和触发模式。 重载定时器状态控制寄存器(TMCSRL0)包括了软件触发位,计数使能位,向下溢出中断请求标志位,中断 使能位,重载工作模式选择位,针脚电平输出选择位,定时器输出使能位。 重载定时器计数寄存器(TMRH0)/(TMRL0)设定了 16 位的向下计数值。 定时器重载寄存器(TMRLRH0)/(TMRLRL0)设定了 16 位的重载计数值。 计数器的状态由定时器控制状态寄存器的 CNTE 和内部等待信号决定,通过设置,可将计数器设置位停止 态(Stop Status),等待态(Wait Status )和计数(运行)态(Run Status)。 7.5.3 16 位重载定时器应用设计范例 本节通过一个 16 位重载定时器的实例来具体说明其应用。代码说明:通过重载定时器实现 LED 从 0 到 9 循环亮,每 1.5 秒改变一次。 #include "mb95100.h" #define SEG_A 0xFE #define SEG_B 0xFD #define SEG_C 0xFB #define SEG_D 0xF7 #define SEG_E 0xEF #define SEG_F 0xDF #define SEG_G 0xBF #define SEG_DP 0x7F

#define SEG_0 SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F #define SEG_1 SEG_B & SEG_C #define SEG_2 SEG_A & SEG_B & SEG_D & SEG_E & SEG_G #define SEG_3 SEG_A & SEG_B & SEG_C & SEG_D & SEG_G #define SEG_4 SEG_B & SEG_C & SEG_F & SEG_G #define SEG_5 SEG_A & SEG_C & SEG_D & SEG_F & SEG_G #define SEG_6 SEG_A & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G #define SEG_7 SEG_A & SEG_B & SEG_C #define SEG_8 SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G #define SEG_9 SEG_A & SEG_B & SEG_C & SEG_D & SEG_F & SEG_G const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, 0xff, 0x00 }; // 初始化设置 LED /*****************************************************************************/ /*主程序部分*/ /*****************************************************************************/ char counter; void main(void) { PDR0 = 0xff; // Port 0: DDR0 = 0xff; // 7-Segment display (all segments off) 7 段 LED 显示输出口 // Initialise Reload-Timer TMRLR0 = 46874; // 设置重载值: (46874+1) * 3.2us = 150ms TMCSR0 = 0x285B; // 在 10 MHz 状态工作时钟为 3.2us, 重载模式, IRQ 使能,输出使能 // Application Start 中断设置,应用功能开始 counter = 0; InitIrqLevels(); // allow all levels __EI(); // globaly enable interrupts while(1) { asm("\tNOP"); // do nothing, 7-Segment counter is controlled by the Reload-Timer } } /*****************************************************************************/ /*Interrupts 中断子程序,使 LED 循环显示 */ /*****************************************************************************/ __interrupt void IRQ_ReloadTimer0 (void) { PDR0 = seg_display[counter++]; if (counter == 10) counter = 0; // 循环显示 TMCSRL0_UF = 0; // 清中断位 }

橡皮泥咖啡 2009-01-14 22:55:51 各位如果有比较好的单片机教材,欢迎上传。 jxph1123 2009-01-15 08:14:06

学习了

yw_1210 2009-01-15 08:18:16 学习了,顶起!!!!!!!!!!! scyblb 2009-01-15 08:23:40 不错哈!


赞助商链接