STM8实战(txt+pdf+epub+mobi电子书下载)


发布时间:2020-09-08 06:59:32

点击下载

作者:高显生,彭英杰

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

STM8实战

STM8实战试读:

前言

近年来,意法半导体有限公司(以下简称意法公司)的单片机产品在国内单片机市场上受到了广泛关注,其旗下STM32系列单片机凭借高性能、高性价比成为32位单片机的市场主流产品,而STM8系列由于采用了和STM32系列一样的外设以及高性能的beCAN模块也迅速得到用户的认可。甚至在如今的人才市场上,会不会使用STM8和STM32单片机往往是用人方选择硬件工程师的条件之一,其重要性和技术影响力可见一斑。

从8位单片机来讲,STM8系列单片机与PIC16系列、PIC18系列、mega AVR系列相比,无论在性能、价格上都不逊色,特别是STM8的超低价格系列单片机,使单片机的价格低于一元成为现实,这无疑对降低产品成本极为有利。

本书是介绍如何使用意法公司推出的集成开发环境STVD、配合使用意法公司的ST-LINK/V2在线仿真/编程器完成STM8系列单片机开发的入门书籍。全书以STM8主流系列大容量产品STM8S208RB单片机为例,对STM8S系列单片机的片内功能、开发环境、功能模块以及接口电路等方面做了详细介绍。本书也是一本零基础入门单片机C语言编程的实践指导书。

本书不拘泥于概念和原理的阐述,而是立足于实践,从系统板基础电路起步,一章一个例子、一章一个实验、一章一个总结、一个模块一套或多套代码,让读者低投入快速入门STM8单片机的开发。

本书对读者没有学历、基础知识的限制,只要快乐阅读、勤于动手,有无基础都可以在短时间内入门STM8单片机开发。更为欣喜的是,STM8单片机的外设模块与STM32系列是通用的,为日后学习基于ARM Cortex系列的STM32系列单片机打下了很好的基础。

本书使用了流行的C语言编写全部代码,而且所有代码均基于对单片机寄存器的直接操作,没有使用STM8的固件库。当前,对单片机的操作有两种观点,一方主张使用寄存器操作,理由是代码简洁且高效;另一方主张使用固件库,理由是代码易读,不用对硬件有更深入的了解。作为学习STM8单片机的入门教材,我更支持前者,原因有二:一是STM8的寄存器数量虽然多,但还在一个可接受的范围内;二是,作为初学者,将来肯定要过渡到STM32系列,掌握了STM8的外设详细功能,为日后学习用固件库开发STM32打下了好的基础。

本书的配套视频教程是《爱上STM8单片机》,读者可以登录优酷网观看。本书在视频教程的基础上进行了进一步的统筹、规纳和整理,是视频教程的凝练和升华。由于作者水平所限,加之写作时间仓促,书中错误在所难免,在此恳请读者和业内人士给予批评斧正。也欢迎大家通过互联网与我分享、交流STM8的开发心得。作者QQ:710878209,微信号:gpmza2000。本书配套DEMO系统板和开发板由睿芯美微淘宝网店同步推出,网址:http://shop59521455.taobao.com。

本书得以出版,要特别感谢机械工业出版社的编辑们。另外要感谢我的哥哥高显功,作为高级电气工程师的他,出于浓厚的亲情,在百忙的研发工作中,抽出了宝贵的时间,担任了本书的技术审校,并对编写方法提出了很多有价值的指导意见。最后要感谢我的家人,在我奋笔疾书的日日夜夜,是他们照顾了我的饮食起居,让我能更加专注于本书的创作。

尺有所短,寸有所长。如果你的头脑中时常对电子设备萌发出一些新奇的想法或创意,请一定将其捕捉,并且通过对本书的学习和实践,努力将其变为现实,这也许就是你走上研发之路的起点,你的人生也会因此变得更加精彩。高显生2015年9月于哈尔滨第一篇基础功能

我们正处在一个日新月异的信息时代,从工业控制到智能家居、智能手机,再到目前备受推崇的物联网,人们在生活中对控制和互联的关注和依赖程度从来没有像今天这样强烈。而作为工业和智能控制的核心部件单片机,也迎来了发展的又一个新时机。本书将以意法半导师体有限公司(简称意法公司)目前在市场上主推的8位单片机为例,带读者快速入门嵌入式智能产品和研发设计。

本书共3篇22章。本篇将从介绍STM8系列单片机的片内功能入手,循序渐进地讲解I/O口控制、软硬件开发环境,以及使用C语言为STM8编写程序、仿真调试等内容。第1章体验STM8

在单片机的行业中,一家独大的局面早已不复存在。全球知名的半导体公司大多推出了自己的单片机产品,作为全球最大的半导体公司之一的意法公司也不例外,其推出的8位、32位单片机凭借高速度、高可靠、高性价比等诸多优点,在嵌入式应用市场中有很高的占有率,在业界的影响力也是举足轻重的。1.1 意法公司的MCU产品

简单地说,单片机就是在一个芯片上集成了构成一个计算机系统最基本的单元,如CPU、程序存储器、数据存储器、各种类型的功能模块和输入/输出接口等,使其具有一台计算机的基本功能。由于单片机主要用于逻辑运算和系统控制,因此也称其为微型控制单元,即MCU(Micro Control Unit)。在工业和自动控制中,单片机总是与机电设备一道协同工作的,被作为控制器嵌入整个系统中,所以由单片机组成的控制系统也称为嵌入式系统。

1987年,两家历史悠久的半导体公司意大利SGS Micro electronics和法国汤姆逊半导体公司合并后,成立了意法公司,总部设在瑞士日内瓦。意法公司是半导体工业界最具创新能力的公司之一,拥有约16000项专利和9000多个专利家族,掌握着业界领先的半导体芯片制造工艺。意法公司凭借其多元化的技术、尖端的设计能力,推出了众多优秀的电子产品,特别是在嵌入式应用、传感器与功率芯片以及汽车芯片等应用中有着完整的产品线和解决方案。

在意法公司的官方网站上,有一幅图片清晰地展示了其在嵌入式应用中的产品线结构,如图1-1所示。图中展示了意法公司的微控制器产品线:8位控制器产品STM8系列和32位控制器产品STM32系列。1.1.1 STM8系列

STM8系列是意法公司的8位微控制器产品,具有高性能的8位内核和先进的外设集。该平台采用意法公司特有的130 nm嵌入式、非易失性存储器技术制造而成。目前STM8平台支持4个产品系列,即:主流的STM8S系列、超低功耗的STM8L系列、车用STM8AF系列和车用STM8AL系列。图1-1 意法公司的微控制器家族(图片源自意法官网)

1.STM8S主流系列

意法公司的STM8S系列是8位单片机的主打产品,适用于工业、消费类电子和计算机市场的多种应用,其高性价比非常适用于产品的批量化生产。该系列基于STM8专有内核,主频达到24 MHz,处理能力为20MIPS。片内配置有EEPROM、RC振荡器和全套标准外设,为设计者提供了稳定可靠的解决方案。STM8S系列包括4种类型的产品,其片内资源配置详见表1-1。本书将以STM8S208RBT6为例,重点介绍STM8S系列单片机的功能和开发方法。

·超值型(STM8S003/005/007):入门级产品,具有基本功能。

·基本型(STM8S103/105):提供更多的特性和封装选择。

·增强型(STM8S20x):配有全套外设,满足中、高端应用需求。

·专用型(STM8S专用):提供了更多模拟特性和专用固件解决方案。表1-1 STM8S系列单片机片内资源配置

2.STM8L系列

意法公司的STM8L系列是超低功耗产品,适用于便携式设备等对功耗极为敏感的应用。STM8L系列同样基于8位STM8内核,采用了超低漏电流工艺,实现了超低功耗(0.30μA)。STM8L系列包括4个不同的产品线,其片内资源配置详见表1-2。

·STM8L101系列:最低功耗模式0.30μA,动态运行模式150μA/MHz。

·STM8L151/152系列:最低功耗模式0.35μA,动态运行模式180μA/MHz。

·STM8L162系列:最低功耗模式0.35μA,动态运行模式180μA/MHz。

·STM8L051/052系列:最低功耗模式0.35μA,动态运行模式180μA/MHz。表1-2 STM8L系列单片机片内资源配置

3.STM8AL系列

STM8AL系列是面向汽车应用的超低功耗系列,主要特点是将绿色能源、应用安全性和功效放在首位,特别适于电池供电的应用(如远程无钥进入和轮胎压力监测)以及功耗至关重要的应用(如配套微控制器、驻车制动器和传感器)等。

STM8AL系列基于STM8A嵌入式的特性,进一步降低了系统成本,提高了可靠性,支持LIN通信,支持LCD驱动器、RTC、DMA、比较器、12位ADC和DAC等模块,进一步提升了计算性能,降低了整体功耗,节省存储空间,为汽车应用提供了低成本解决方案。STM8AL系列片内资源配置详见表1-3。表1-3 STM8AL系列单片机片内资源配置

4.STM8AF系列

STM8AF系列同样是面向汽车的应用系列,该系列属于模块化产品,具有更高的性能和更大的灵活性,具有真正的数据EEPROM以及能承受高达150℃环境温度的能力,使其更加适用于汽车应用。STM8AF系列片内资源配置详见表1-4。表1-4 STM8AF系列单片机片内资源配置1.1.2 STM32系列

STM32系列单片机产品基于ARM Cortex-M标准内核,并在此基础上进行了全新的定义,增加了更多的外设,扩展了应用范围。STM32系列产品具有32位的处理能力、高实时性能、强大的数字信号处理能力,以及低功耗、低电压的操作特性。STM32系列具有基于行业标准的内核、完整的产品线和开发环境,有无与伦比的高性能和强大的软硬件开发平台,使其成为工业控制、家庭智能产品和小型项目的理想选择。按照片内集成的ARM Cortex-M内核不同,STM32系列单片机又可以细分成若干个子系列,以适合不同的应用。1.2 性能优异的STM8S1.2.1 片内功能概述

STM8S系列单片机是基于8位框架结构的微控制器,全系列配备了高性能的STM8内核,外设采用模块化的设计方式,具有丰富的片内资源配置,其内部结构如图1-2所示。

从图1-2可以看出,STM8S系列单片机的核心部分是STM8内核,与其他模块一同连接于片内的地址和数据总线上。同样连接在地2址和数据总线上的模块还包括单线调试(Debug/SWIM)模块、IC模块、SPI模块、UART1模块、UART3模块、beCAN模块、模数转换(ADC2)模块、蜂鸣器(Beeper)模块、看门狗模块、多功能的定时器模块等。另外,FLASH、EEPROM及RAM存储器也通过这条总线进行通信。

STM8S单片机的时钟配置也比较灵活,可以使用晶体振荡器或外部时钟信号作为系统时钟源,也可以使用片内的两个RC振荡器(16MHz和128kHz)作为系统时钟。时钟信号在时钟控制单元的控制下,为外设和CPU提供时钟信号。此外,STM8S片内有复位模块,支持外部复位、欠压复位和上电复位。图1-2 STM8S单片机内部结构

1.STM8内核

STM8S全系列标配统一的STM8内核,CPU基于哈佛结构,配有3级指令流水线,采用32位宽程序存储器总线,对于大多数指令可进行单周期取指;CPU内部集成有6个内部寄存器,可以高效地进行数据运算和处理;2个16位寻址寄存器:X寄存器和Y寄存器,允许带有偏移的和不带偏移的变址寻址模式和基于读—修改—写方式的数据操作;8位累加器,使用24位程序指针,具备16M字节的线性寻址能力;16位堆栈指针,可以访问64K字节深度堆栈;8位状态寄存器,可根据上条指令的结果产生7个状态标志位;CPU在24MHz全速运行时,可以达到20MIPS的处理速度。

在这里有必要对STM8内核的哈佛(Harvard)结构做一些说明。51型单片机是基于冯·诺依曼(Von-Neumann)结构设计的,这种内核结构的单片机典型特点是程序存储器和数据存储器都挂接在同一条8位的数据总线上,CPU也通过该总线对程序和数据存储器进行访问,在同一时间里,CPU要么从程序存储器中存取指令,要么从数据存储器中存取数据,在复杂的数据处理过程中,难免出现总线竞争的情况,影响单片机的工作效率。基于冯·诺依曼结构的单片机内部结构如图1-3所示。图1-3 冯·诺依曼结构

而STM8S系列单片机采用的是哈佛结构。在这种结构的特点是采用分开的指令和数据总线,CPU对数据存储器的访问是通过8位的数据总线来完成的,而对程序存储器的访问则是通过独立的32位指令总线来完成的。哈佛结构的优点是指令和数据空间完全分开,可以实现对程序和数据的同时访问,提高了CPU的执行速度和数据的吞吐率。采用哈佛结构的单片机内部结构如图1-4所示。图1-4 哈佛结构

STM8S单片机在处理指令时需要经过3个步骤,即:取指、译码和执行。如果每一个步骤都消耗一个时钟周期,一条指令的执行过程将使用3个时钟周期来完成。STM8S单片机采用的是3级指令流水线操作,即在一条指令执行的同时,对下一条指令进行译码,并且对第三条指令进行预读,这样使一条指令的执行可以在一个时钟周期内完成,大大提高了数据的处理速度。3级指令流水线的原理如图1-5所示。图1-5 3级指令流水线

2.存储器

STM8S208RB单片机内部集成了多种不同类型的存储器:128K字节的FLASH程序存储器,经过10K次擦写后在55℃环境下数据可保存20年;2K字节真正的数据EEPROM,可耐30万次擦写;6K字节数据RAM。

3.时钟源

STM8S208RB单片机具有灵活的时钟控制,可以使用4个不同类型的时钟作为主时钟源,即:低功率晶体振荡器、外部时钟、内部集成16MHz RC振荡器、内部低功耗128kHz RC振荡器。且片内配有监控时钟的时钟安全保障系统,当主时钟源失效时,可以自动切换到备用时钟源上。

4.复位和电源管理

STM8S208RB单片机在2.95~5.5V工作电压下均能正常工作,具有等待、活跃停机、停机3种低功耗模式,每一个外设的时钟可单独关闭,具有低功耗上电和掉电复位电路。

5.中断管理

STM8S系列单片机具有灵活的优先级控制,支持4个软件可编程的嵌套等级,最多有32个中断向量,其入口地址由硬件固定。

6.定时器

STM8S208RB单片机片内有两个16位通用定时器,具有2+3个CAPCOM通道,能实现比较、捕捉和PWM控制功能(IC、OC或PWM);1个16位高级控制定时器,具有4个CAPCOM通道,3个互补输出,死区控制和灵活的同步控制功能;1个带有8位预分频器的8位基本定时器。此外,STM8S208RBT6单片机还配备了片内自动唤醒定时器、窗口看门狗定时器、独立看门狗定时器。灵活运用这些定时器,可以实现强大的控制功能。

7.通信接口

STM8S208RB单片机片内集成了丰富的通信接口:1Mbps CAN 2.0B高速接口、带有同步时钟输出的UART-LIN主模式通信接口、兼容LIN2.1协议的UART通信接口(主/从模式和自动重新同步)、最高2通信速率为10Mbps的SPI接口和最高到400Kbps的IC接口。

8.模数转换器

STM8S208RB单片机片内集成有10位精度的ADC模块,具有多达16路输入通道,可实现高精度的模数转换。

9.I/O端口

STM8S208RB单片机采用LQFP64封装,具有64个引脚,有52个I/O接口,其中包括16个高吸收电流输出的I/O接口。I/O驱动能力非常强大,标准输出模式下灌电流和拉电流均可达20mA。

10.开发支持

STM8S208RB系列单片机配备有单线接口模块(SWIM)和调试模块(DM),可以方便地与开发环境相连,进行在线编程和非侵入式调试。

STM8S208RB单片机片内资源配置详见表1-5。表1-5 STM8S208RBT6单片机片内资源配置1.2.2 引脚功能

STM8S208RB单片机采用LQFP64封装,具有64个物理引脚,其排列顺序如图1-6所示。STM8S208RBT6单片机共有A、B、C、D、E、F、G、I共8组I/O口,其中大部分是功能复用接口;NRST引脚是外部复位引脚,在该引脚上施加一个足够时间的低电平会使单片机复位;V、V分别是I/O口的电源和地,V和V分别是数字电源和DDIOSSIODDSS地,V和V分别是模拟电源和地;VCAP是内部稳压器滤波电容DDASSA端;V和V是A/D转换器参考电压正负输入端。STM8S208RBREF+REF-单片机引脚功能详见表1-6,更多的引脚功能描述可参考本书附录。图1-6 STM8S208RB单片机引脚表1-6 STM8S208RB单片机引脚功能1.3 通用I/O口

STM8S单片机的I/O口又称通用输入/输出口(General purpose I/O port,GPIO),用于芯片和外部进行数据传输。STM8S208RB单片机有8组I/O端口,完整的一组I/O端口由8个引脚构成,每个引脚可以被独立编程,作为数字输入或者数字输出使用,部分接口还可以编程为模拟输入、外部中断、片上外设的输入/输出等复用功能。这里需要说明的是,一个引脚虽然具有多个功能,但在同一时刻只能使用其中的一个功能。1.3.1 GPIO的特点

STM8S208RB单片机的每组端口都分配有专用的输出数据寄存器、输入引脚寄存器、数据方向寄存器、选择寄存器和配置寄存器。STM8S系列单片机I/O口的输入、输出状态是由数据方向寄存器决定的,在使用时需要进行单独设定,这一点和51单片机是不同的。STM8S208RB单片机GPIO的主要特点如下:

·当引脚被配置成输入模式时,有浮动输入或上拉输入两种选择。

·当引脚被配置成输出模式时,可选择推挽输出或开漏输出模式。

·数据输入和输出采用独立的寄存器。

·端口的外部中断可以单独使能或关闭。

·可编程输出摆率控制,可以减少EMC噪声。

·作为模拟输入时,可以关闭输入施密特触发器以降低功耗。

STM8S208RB单片机引脚的内部结构如图1-7所示。每一组端口都有一个输出数据寄存器(ODR)、一个引脚输入寄存器(IDR)、一个数据方向寄存器(DDR)与之对应。另外还有控制寄存器1(CR1)和控制寄存器2(CR2)用于对端口的输入/输出模式进行配置。ODR、DDR、CR1和CR2寄存器的每一位都与相应的I/O口相对应,任何一个I/O引脚可以通过对这些寄存器的相应位进行编程来配置。

我们来简单分析一下I/O口的工作原理。当I/O口被设置成输出状态时,数据总线上的输出数据被放置于ODR寄存器中。当ODR寄存器的对应位为0时,I/O口的输出端P沟道场效应管截止,而N沟道场效应管导通,引脚输出低电平。当ODR寄存器的对应位为1时,I/O口的输出端P沟道场效应管导通,而N沟道场效应管截止,引脚输出高电平。引脚内部的两个保护二极管用于保护内部电路不会因过压而损坏。

当引脚被设置成输入状态时,分两种情况:一种情况是该引脚与模拟输入复用,当工作于模拟状态时,引脚上的模拟电压会被直接送入片内A/D转换通道,用于对模拟电压进行采样。另一种情况是引脚被设置为数字输入,引脚上的数字电压信号被送入施密特触发器,用于判定输入数据的状态(0或1),之后数据被放置于IDR寄存器中等待读取。ADC_TDR寄存器用于在A/D转换器工作时关闭施密特触发器以降低功耗。图1-7 引脚内部结构1.3.2 GPIO的配置

GPIO的配置是通过DDR、CR1和CR2三个寄存器来完成的,其配置方式详见表1-7。

1.输入模式

将DDR寄存器的相应位清零可以选择输入模式。在该模式下,读IDR寄存器的位将返回对应I/O引脚上的电平值。当I/O引脚被配置成输入时,可以通过软件配置CR1和CR2寄存器,得到4种不同的输入模式:悬浮不带中断输入、悬浮带中断输入、上拉不带中断输入和上拉带中断输入。这里需要特别说明的是:不是所有的端口都具有外部中断能力和上拉功能,这一点需要在应用时灵活掌握。

2.输出模式

将DDR寄存器的相应位置1可以选择输出模式。在该模式下,向ODR寄存器的位写入数据将会通过锁存器输出对应数字值到I/O口上。此时读IDR寄存器同样会将对应I/O引脚上的电平值读回。通过软件配置CR1、CR2寄存器可以得到两种不同的输出模式:推挽输出和开漏输出模式。在推挽输出模式时,P沟道和N沟道两个场效应管都参与工作;而在开漏输出时,P沟道场效应管是关闭的,这种状态通常用于与外部器件形成线与的关系。

当单片机复位时,为了避免I/O口内部输入施密特触发器持续工作从而消耗更多的电流,所有I/O口都会被配置成悬浮输入模式,对于没有使用的引脚,应当连接到一个固定的电平值上,上拉或者是下拉。

3.摆率控制

输出摆率是GPIO口的驱动电路响应速度,可以通过软件配置CR2寄存器的相应位控制。清0 CR2位时,相应I/O口输出频率为2MHz,置位CR2相应位时输出频率为10MHz。表1-7 GPIO的配置方式1.3.3 GPIO的寄存器

GPIO的寄存器都是与端口相对应的,每一个端口寄存器位驱动相应的端口引脚。

1.端口x输出数据寄存器(Px_ODR)

Px_ODR寄存器:端口x输出数据寄存器

bit 7:0 ODR7:ODR0:端口输出数据寄存器位。

在输出模式下,写入寄存器的数值通过锁存器施加到相应的引脚上。读ODR寄存器,返回之前锁存的寄存器值。在输入模式下,写入ODR的值将被锁存到寄存器中,但不会改变引脚状态。ODR寄存器在复位后总是为0。

2.端口x输入寄存器(Px_IDR)

Px_IDR寄存器:端口x输入寄存器

bit 7:0 IDR7:IDR0:端口输入数据寄存器位。

无论引脚是输入还是输出模式,都可以通过该寄存器读入引脚状态值,该寄存器为只读寄存器。

0:引脚上为逻辑低电平。

1:引脚上为逻辑高电平。

3.端口x数据方向寄存器(Px_DDR)

Px_DDR寄存器:端口x数据方向寄存器

bit 7:0 DDR7:DDR0:数据方向寄存器位

这些位可通过软件置1或清0,用于设定引脚为输入或输出。

0:输入模式。

1:输出模式。

4.端口x控制寄存器1(Px_CR1)

Px_CR1寄存器:端口x控制寄存器1

bit 7:0 C17:C10:控制寄存器位

这些位可通过软件置1或置0,用来在输入或输出模式下选择不同的功能,具体配置方式详见表1-7。

在输入模式时(DDR=0)如下。

0:浮空输入。

1:带上拉电阻输入。

在输出模式时(DDR=1)如下。

0:模拟开漏输出(不是真正的开漏输出)。

1:推挽输出(由CR2相应位输出摆率控制)。

5.端口x控制寄存器2(Px_CR2)

Px_CR2寄存器:端口x控制寄存器2

bit 7:0 C27:C20:控制寄存器位

相应的位通过软件置1或置0,用来在输入或输出模式下选择不同的功能。在输入模式下,由CR2相应的位使能中断,如果该引脚无中断功能,则对该引脚无影响。在输出模式下,置位将提高I/O速度。

在输入模式时(DDR=0)如下。

0:禁止外部中断

1:使能外部中断

在输出模式时(DDR=1)如下。

0:输出速度最大为2MHz。

1:输出速度最大为10MHz。本章回顾

在这一章中我们了解了意法公司的MCU产品线,对STM8S系列单片机的功能、引脚、I/O口的设定有了一些基本的认识,同时也学习了多个与I/O口相关的控制寄存器。这些都是将来操控STM8S单片机的基础。第2章入门C语言

C语言是国际上流行的计算机语言,具有描述能力强、可移植性好、逻辑缜密的特点。其模块化的结构非常适合大型程序的开发,特别是近年来在嵌入式系统的程序开发中,使用C语言编写单片机程序已经是一个流行的趋势。C语言博大精深,学精不易,但入门却十分简单。本章将带读者快速学习和掌握C语言的基础知识,使读者能在短时间内编写出C的应用程序来。2.1 数据和运算2.1.1 数的进制

我们在日常生活中大多使用十进制,即逢10进1,这主要是由人们的使用习惯决定的。其实在生活中还有许多其他不同的进位制度,如时间的表示方法是六十进制,即1小时等于60分,1分钟等于60秒等。还有常用的表示数量的单位“一打”是十二进制等。在计算机中,常用的进位制度有二进制、十进制、八进制和十六进制。

1.二进制(Binary)

二进制数由0和1两个符号来表示,基数为2,按逢2进1、借1算2的规则计数。例如:

10110011 1010 1111000010101101

2.十进制(Decimal)

十进制数由0、1、2、3、4、5、6、7、8、9、0这10个数字符号表示,基数为10,按逢10进1、借1算10的规则计数。例如:

128 23 47

3.八进制(Octal)

八进制数由0、1、2、3、4、5、6、7这8个数字符号表示,基数为8,按逢8进1、借1算8的规则计数。例如:

70 360 777

4.十六进制(Hexadecimal)

十六进制数由0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F这16个数字符号表示,基数为16,按逢16进1、借1算16的规则计数。在C语言中表示十六进制数时,大小写字母的含义相同。例如:

EF08 5ac 7BF

对于不同进制的数字间的转换在这里不做太多的叙述,对于初学者来讲,使用PC中的计算器来进行不同进制数字间的转换是一个方便快捷的办法,如图2-1所示。打开计算器软件,如要将十进制数“254”转换成二进制数,首先在计算器中选择十进制,输入数字“254”,再用鼠标单击选择二进制,这时计算器中即可显示经转换后的二进制数“11111110”。图2-1 使用计算器进行进制转换2.1.2 码制

在计算机内部,所有的信息都要使用二进制的方法来表示,因为二进制的0和1两个数字恰好与存储单元的“有”和“无”相对应。不仅如此,数的正负符号“+”或“-”也需要用二进制数来表示,在通常情况下,用0表示正数的符号“+”,用1表示负数的符号“-”。当数的符号和数值表示方法使用二进制时,这样的数被称为“机器码”。机器码有不同的码制,对应不同的表示方法,常用的码制有原码、反码和补码3种。

·原码:原码用最高位表示数的符号位,数值部分用二进制的绝对值表示。

·反码:正数的反码与其原码相同,负数的反码是将符号位除外,其他各位按位取反。

·补码:正数的补码与其原码相同,负数的补码是将其取反码后加1。

数的原码、反码和补码的表示方法详见表2-1。表2-1 数的原码、反码和补码2.1.3 数据类型

程序运行的目的是对数据进行处理,在C语言中数据是有类型区分的,具体分类如下:

在以上的数据类型分类中,基本类型是不可以再次拆分为其他数据类型的;构造类型则是在基本类型的基础上,按照一定方式组合而成的数据类型;指针类型是一种特殊的数据类型,其值通常用来表示某一个量在内存中的存放地址;空类型是指在对函数进行定义时,如函数没有返回值,就在函数的名称前面加上void,以此表明该函数是“空类型”。2.1.4 常量

常量是指在程序的运行过程中,其值不能被改变的量。常量的种类有整型、实型、字符型和字符串4种。

1)整型常量:十进制的整数表示方法非常简单,如:29、–18、156等。十六进制的整数通常以0x(或0X)开头,如:0xFE、0xD7A9、0x7D等。八进制的整数则以0开头,如:057(其值相当于十进制的47)。

2)实型常量:实数有两种表示方法,一种是十进制的小数形式,如:0.625、–16.5等;另一种是采用指数形式,即用e(或E)后面跟一个整数,表示以10为底的幂指数,如:256.5的表示方法是2.565e2。

3)字符型常量:字符型常量的表示方法是用单引号引出,如‘a’‘B’等。

4)字符串常量:字符串常量用双引号引出,如“GOOD”“thank you”等。2.1.5 变量

变量是指在程序运行过程中其值可以改变的量。在C语言中使用变量时,要先给变量命名,还要给变量定义数据类型,有时还需指定变量的存储地点。

为什么要给变量定义数据类型呢?在数学上,一个数可以是+∞也可以是–∞,但是在计算机中,存储单元是有限的,因此必须根据数据的大小为其分配合适的存储空间。定义数据类型实际上就是为变量在内存中分配特定的存储空间,以便于用这个空间来存储相关的数据。如果把变量比喻成用于储存数据的盒子,指定数据类型就是指定盒子的大小,既能装得下要装的东西,又不会造成空间的浪费。C语言中变量的数据类型详见表2-2。表2-2 C语言中的基本数据类型

注:方括号表示其中的内容可以省略。

变量在程序中需要先定义后使用。定义变量的方法是先给变量指定名称和数据类型,这样编译器才能为变量分配相应的存储空间。定义变量的格式如下:数据类型变量名表;多个变量名称之间要用逗号分隔 //

在C程序中定义变量的方法可以参考如下语句:,,;定义、、为三个符号字符型变量char abc //abc;定义为无符号整型变量unsigned int num //num2.1.6 运算符

C语言的运算符非常丰富,在程序中使用这些运算符来处理各种基础操作,从而完成特定的功能。C语言的运算符主要有以下几种。

1.算术运算符

·+:加法运算符。或为取正值运算符。例如:3+5 A+B+23

·–:减法运算符。或为取负值运算符。例如:18–17 TIME1–TIME2–78

·*:乘法运算符。例如:5*8 AD*AF

·/:除法运算符。在这里除法运算符和一般的算术运算规则有所不同,如果是两个浮点数相除,结果也是浮点数。如果两个整数相除,结果也是整数。例如:10.0/20.0结果为0.5,7/2结果为3,而不是3.5。

·%:求余运算符。%两侧均应是整数。例如:10%3结果为1。

在上述的运算符中,我们同样可以用()来改变运算的优先级,这和我们在小学时学的是一样的,如:(A+B)*C,就需要先计算A与B的和,再计算与C的积。

2.赋值运算符

=:赋值运算符。在C语言中用于给变量赋值,其方法可以参考以下语句:;给变量赋值num=25 //num25;将变量的值赋给变量D=C //CD

3.自增、自减运算符

·++:自增运算符。作用是使变量的值自增1。例如:I++,表示让变量I的值自增1。

·––:自减运算符。作用是使变量的值自减1。例如:A––,表示让变量A的值自减1。

4.关系运算符

关系运算符通常是用来判别两个变量是否符合某个条件的,所以使用关系运算符的运算结果只有“真”或“假”,即“1”或“0”两种。

·>:大于。例如:A>B

·<:小于。例如:NUM1

·>=:大于等于。例如:U>=5

·<=:小于等于。例如:P<=7

·==:等于。例如:TEAM1==TEAM2,在这里要区别于赋值运算符“=”,它表示的意思不是将TEAM2的值赋给TEAM1,而是用来判定TEAM1是不是和TEAM2的值相等。

·!=:不等于。例如:A!=B

5.位运算符

位运算是C语言的一大特色。所谓位运算,形象地说就是指把数值以二进制位的方式进行相关的运算,参与位运算的数必须是整型或字符型的数据,实型(浮点型)数不能参与位运算。

1)&:按位与运算符。是实现“必须都有,否则就没有”的运算。它的规则是:

0&0=0 0&1=0 1&0=0 1&1=1

在实际应用中,按位与运算常用来对某些位清0或保留某些位。

例如A的值为:A=1001 0010

只想保留A的高4位,则用:A&1111 0000

与运算后A的值为:1001 0000

2)|:按位或运算符。是实现“只要其中之一有,就有”的运算。它的规则是:

0|0=0 1|0=1 0|1=1 1|1=1

在实际应用中,或运算常用来将一个数值的某些位定值为“1”。

例如A的值为:A=1001 0010

想将A的低4位定值为1,则用:A|0000 1111

或运算后A的值为:1001 1111

3)^:按位异或运算符。是实现“两个不同就有,相同就没有”的运算。它的规则是:

0^0=0 1^0=1 0^1=1 1^1=0

在实际应用中,异或运算常用来使数值的特定位翻转。

例如A的值为:A=1001 1010

想将A的低4位翻转,即0变1,1变0,则用:A^0000 1111

异或运算后A的值为:1001 0101

4)~:按位取反运算符。是实现“是非颠倒”的运算。它的运算规则是:

~0=1 ~1=0

例如A的值为:1001 1010

按位取反运算后,其值为:0110 0101

5)<<:左移运算符。是实现将一个二进制数的每一位都左移若干位的运算。左移运算的方法如图2-2所示。图2-2 左移运算

6)>>:右移运算符。是实现将一个二进制数的每一位都右移若干位的运算。右移运算的方法如图2-3所示。图2-3 右移运算2.1.7 复合赋值运算符

在赋值运算符“=”之前加上其他二目运算符,就可以构成复合赋值运算符。复合赋值运算符有+=、–=、*=、/=、%=、<<=、>>=、&=、^=和|=。

构成复合赋值表达式的方式为:变量双目运算符表达式 =

它相当于:变量变量运算符表达式 =

例如:

num+=15相当于:num=num+15

a*=b+23相当于:a=a*(b+23)

复合赋值运算符的这种书写方法,对于初学者来说也许不太习惯,但它有利于编译器的编译和处理,可以产生高质量的目标代码。2.2 语句

C语言用语句来向计算机发出操作指令。一个C语句经编译后,可以生成若干条机器指令,它是构成函数的基础。C语言的语句可以分为控制语句、函数调用语句、复合语句、表达式语句以及空语句等。以下我们主要介绍的是C语言的控制语句,这种语句具有相对固定的格式,用来实现某种特定的功能。2.2.1 控制语句

C语言有9种控制语句,可分成以下3类。

·循环执行语句:while语句、do while语句、for语句。

·条件判断语句:if语句、switch语句。

·转向语句:break语句、continue语句、return语句、goto语句。

1.while语句

while语句是一个循环语句,用来控制程序段(即循环体)的重复执行,构成“当型”循环结构。while语句的常用形式为:(表达式)while{语句; 1语句; 2…… 语句; n}

while语句的执行过程是:首先判断“表达式”的值,当“表达式”的值为非0时,即开始顺序执行一次while语句中循环体中的语句,之后再次判断“表达式”的值,再进行下一次的循环,直至“表达式”的值为0时结束while循环。

while语句的特点是先判断表达式,后执行语句。在循环体中应该有使循环趋于结束的语句,否则循环会永不停止,形成死循环。while语句的用法如以下代码所示:()whilei--{;循环体语句 num=num+i //1……循环体语句 //n}

2.do…while语句

do…while语句的特点是先执行循环体一次,再判断表达式的值,当“表达式”的值为非0时,则执行一次循环体中的语句,之后再判断“表达式”的值,并进行下一次的循环,直至“表达式”的值为0时结束。do…while语句的常用形式为:do{语句; 1语句; 2…… 语句; n}(表达式);while

do…while语句的具体用法如以下代码所示:do{;循环体语句num=num+i //1……循环体语句 //n}();whilei--

3.for语句

for语句是C语言中功能强大的循环语句,它的优点在于不仅适用于循环次数已经确定的情况,而且也可以用于未给出循环结束条件的情况。For语句的典型形式为:(表达式;表达式;表达式)for 123{语句序列; }

for语句的运行过程如下:

1)求解“表达式1”。“表达式1”一般用于给循环初始变量赋值。

2)求解“表达式2”。如果其值为非0,就执行一次for语句中指定的语句序列;如果其值为0,则退出for循环。

3)求解“表达式3”。“表达式3”一般用于改变控制循环次数的量,使循环趋于结束。

4)返回第2步,执行下一次循环。

for语句的执行过程如图2-4所示。图2-4 for语句的执行过程

for语句的用法可以参考以下代码:()定义名为的子函数,无返回值,入口参数void delayunsigned int t//delayt{;定义变量 unsigned int x //x(;;)循环,循环内容为空,用作延时 forx=tx>0x-- //for {……语句序列 }}

上面的代码是由for循环语句构成的延时函数,当for循环执行后,先执行的是x=t,将参数t的值赋予变量x,然后判断一下x>0是否为真,为真就执行花括号内的语句序列。执行完语句序列后,转而执行一次x––,让x的值自减1。完成上述任务后,回头去重新判断x>0是否为真,从而开始下一次循环。

4.if语句

if语句是一个条件语句,表达的意思是:如果……,就……,否则……。if语句的典型写法是:(表达式)if {语句序列; 1}else{语句序列; 2}

if语句在执行时,首先对条件表达式进行求解,当条件表达式的结果为真时,就执行“语句序列1”的内容,否则就执行“语句序列2”的内容。需要注意的是,不要误认为if语句和else语句是两个部分,其实它们同属于一个if语句。else子句不能单独使用,它必须和if语句一起使用,但实际使用时可以省略else及后面的语句,这时if语句就可以简单地写成:(表达式)if {语句序列; }

这种形式的if语句执行过程是先求解表达式,当其为真时就执行“语句序列”,当其为假时,就跳过该if语句,执行后面的其他语句。在if语句中,表达式通常都是用来判断二者关系的,表达式中使用的都是关系运算符,如>、<、>=、==、<=和!=,其运算结果只有“真”和“假”两种状态。

5.switch语句

C语言中提供了一个专门用于处理多分支结构的条件选择语句,称为switch语句,又称为开关语句。其语句的一般形式为:(表达式)switch{常量表达式:语句;; case 1 1 break常量表达式:语句;; case 2 2 break常量表达式:语句;; case 3 3 break…… 常量表达式:语句;; case n n break:语句; default n+1}

switch语句的执行过程是:首先计算switch后面圆括号中表达式的值,然后用此值依次与各个case后面的常量表达式相比较,若与某个常量表达式的值相等,就执行该case后面的语句,执行语句时遇到break后就退出switch语句;若圆括号中表达式的值与所有case后面的常量表达式都不相等,则执行default后面的“语句n+1”,然后退出switch语句。

使用switch语句时还应注意以下几点:

1)default语句总是放在最后,当要求没有符合的条件不做任何处理时,则可以去掉default语句。这时,当圆括号中表达式的值与所有case后面的常量表达式的值都不相等时,则直接退出switch语句。

2)如果在每一个case后面包含多条执行语句时,语句之间用“;”号隔开;进入某个case后,会自动顺序执行本case后面的所有执行语句,直到遇到break语句后才停止执行。

3)每一个case后面的break语句是可以省略的,如果break语句被省略,程序会自动进入下一个case中继续执行语句,而不判断是否匹配,直到遇到break语句后才停止执行。这是因为case后面的常量表达式实际上只是一个开始执行处的入口标号,而不起条件判断作用。

switch语句的实际用法可以参考以下代码:()switchkeysc2{:;;();; case 0xed PORTD=table[4] PORTC=0x01 beep break:;;();; case 0xdd PORTD=table[5] PORTC=0x01 beep break:;;();; case 0xbd PORTD=table[6] PORTC=0x01 beep break:;;();; case 0x7d PORTD=table[7] PORTC=0x01 beep break}

6.break语句

break语句可以用在循环语句和switch语句中,在循环语句中用来结束内部循环,在switch语句中用来跳出switch语句,break语句不能用在循环语句和switch语句之外的其他语句中。break语句的一般形式为:;break

break语句的用法可参考以下代码:(;;)定义次的循环forx=10x>0x-- //10for{…… ();如果的值小于,终止循环…… ifnum<0 break //num0for}

7.continue语句

continue语句的作用是结束本次循环,忽略continue语句后面的语句,重新开始下一次的循环判定。其一般形式为:;continue

continue语句的用法可参考以下代码:(;;)forx=10x>0x--{…… ();如果的值小于,重新开始下一次循环 ifnum<0 continue //num0for…… }

这里需要注意的是,break语句是不再判断循环的条件是否成立,而结束整个循环结构,跳出循环体,开始执行循环语句后面的语句;continue语句只结束本次循环,转向下一次循环条件的判断,如果判断结果为真,则继续下一次循环,判断结果为假,则结束循环。

8.return语句

return语句用于把函数的值返回给主调函数。所谓函数的值,是指函数被调用后,执行函数体中的程序段所取得的并需要返回给主调函数的值。return语句的一般形式为:表达式;return

或者为:(表达式);return

该语句的功能是计算表达式的值,并返回给主调函数。return语句的实际用法可以参考以下代码:()unsigned char read_1bytevoid{…… (()) ifPIND&0x10==0x10 {; TEMP=TEMP|0x01 }…… ;将的值返回 return TEMP //TEMP}

9.goto语句

goto语句是一个无条件分支语句,用于将程序转移到指定的位置继续执行。goto语句的一般形式为:语句标号;goto

goto语句的具体用法可以参考以下代码:()while1……{:词句标号,指定程序跳转地点……restart //(()());条件成立,返回处……iftemp<200||temp>800 goto restart //restart}

这里需要注意的是,过多地使用goto语句会造成程序结构上的混乱。2.2.2 其他语句

1.函数调用语句

函数调用语句由一个函数调用加一个分号构成。具体可参考以下代码:

();调用延时函数delay5 //

2.复合语句

将多个语句组合起来,用“{}”括起来,即可构成复合语句。其用法可参考以下代码:{; PORTD=0x01(); delay100; PORTD=0x02(); delay100}

3.表达式语句

将一个表达式和一个分号组合在一起即构成表达式语句。具体可参考以下代码:;X+5

4.空语句

空语句是只有一个分号的语句,它不执行任何的操作,一般作为循环语句中的循环体。空语句的使用可参考以下代码:();While1

以上代码中的分号即表示该循环中循环体为空语句。2.3 函数

把解决某一问题的算法汇集起来,组成一个相对独立的函数,在需要时就可以调用这个函数来处理相应的问题。可以说,C程序的全部工作都是由多个不同的函数来完成的。函数可以根据需要自行定义,这一类函数称为自定义函数。另外,为了简化代码编写的难度,通常C编译器中还会将一些相对固定的功能事先编写成函数,以库的形式存储起来,这一类函数称为库函数。2.3.1 自定义函数

这一类函数是用户根据需要自行定义的函数,需先定义后使用。自定义函数的形式如下:类型标识符函数名(形式参数表列) {声明部分; 语句部分; }

类型标识符用于指定函数的类型。函数的类型就是函数返回值的类型,即:函数被调用后,执行函数体中的程序段所取得的并需要返回给主调函数的值。很多情况下函数没有返回值,此时类型标识符由void取代;函数名通常由1~8个字符组成,建议给函数起名与函数的功能相联系,以便于阅读和记忆。形式参数表列用于指定函数的输入参数及类型,各参数间用“,”分隔。函数被调用时,主调函数将赋予这些形式参数以实际的值。花括号“{}”括起的部分是函数体,由声明部分和语句部分构成。声明部分是函数体内部所用到变量的类型说明或要调用的函数声明。以下,我们用一个函数的实例来说明自定义函数的方法:(,)int maxint aint b{;int temp();ifa>b temp=a;else temp=b;return temp}

上面的函数是一个比较a、b两数大小的函数,函数的类型是实型,即函数执行后,会反馈一个实型的数据给主调函数。函数的输入参数(形参)有两个,一个是a,另一个是b,它们也是实型数据。函数调用时,主调函数会给a和b两个形式参数赋予具体的值(如3和8),以比较这两个数的大小,这一过程也称为将实际参数赋于形式参数;在函数体部分,第一行是声明部分,声明了一个实型的变量temp,之后是语句部分,使用了if语句来比较两个数的大小,并将比较结果用return语句返回。

这里需要说明的是,无返回值的函数,可以将函数的类型定义为“空类型”,类型说明符为void。同样,如果函数没有入口参数,形参表列可以空白或用void表示,例如某一即无返回值又无入口参数的函数书写方法如下:()void t0_initvoid{…… }

除了自定义函数以外,每一个C编译器都会提供一些库函数,这些库函数无需用户定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。比如在本书后面章节里使用的延时函数,就是由GCC编译器提供的库函数。随着学习的不断深入,相信会对库函数有更深入的理解。2.3.2 函数的声明和调用

在一个C程序中,当自定义函数位于主调函数后面时,就需要在程序的开始位置对自定义函数进行声明,以便把函数的名称、函数参数的个数和类型等信息通知编译器,从而在调用此函数时,编译器能正确识别该函数,并检查调用是否合法。

1.函数的声明类型标识符函数名(参数类型形参名,参数类型形参名,……参数类型形参名); 1 12 2 n n

如前面介绍比较a、b两个数大小的函数声明方法为:(,);int maxint aint b

2.函数的调用

定义函数的目的就是为了使用它来完成某些功能,函数调用的一般形式为:函数名(实际参数表列);

调用前面介绍的比较a、b两个数大小的函数方法可以参考以下代码:(,);比较、两个数大小,并将比较的结果赋值给z=max38 //38z

此函数在调用时,用于比较3和8两个数的大小,并将比较的结果赋值给z。调用无返回值的函数的方法可以参考以下代码:

();延时delay5 //5ms2.4 程序2.4.1 程序的构成

C程序是由一个main函数和若干个其他函数构成的,以下用一个实际的例子来说明C程序的构成。#include ()int mainvoid{;将端口低位设为输出 DDRC=0x0F //PORTC4 0000 1111;使端口最低位输出高电平 PORTC=0x01 //PORTC 0000 0001() while1 { }}

在上面的程序中,第一行#是预处理命令行起始符号,include是预处理命令,表示程序在这里引用了来自另外一个地点的文件,include用于将该文件中编写的程序行包含到本程序中使用。io.h是C编译器(GCC)提供的头文件,用于对AVR单片机的寄存器作规范化的定义,avr/指定了该文件存放的文件夹。

程序的第二行是一个函数。我们知道,C语言是一个模块化的语言,它的主要部分是由多个具有特定功能的函数构成的。main函数和C语言中其他函数在结构上是一致的,但它的名称是固定的main,即主函数的意思。在一个C源程序中,有且仅有一个主函数,而且无论主函数位于源程序的什么位置,程序执行时都必须从它开始。

主函数的函数类型为int,函数名称后面圆括号里面的void表示该函数是没有输入参数的。在main函数的函数体中,第一个语句是一个赋值语句,意思是给寄存器DDRC赋值为十六进制的0F;第二个语句同样是一个赋值语句,意思是给寄存器PORTC赋值为十六进制的01。每一个程序行都以分号“;”结束。

接下来的程序行是一个while语句。它是一个循环语句,用来控制程序段(即循环体)的重复执行。这里需要明确的是,单片机的程序都是一个趋于无限的死循环,程序中使用了while(1)的写法,使程序在此进入持续的循环状态。

程序的运行过程如下:主函数是程序运行的开始。程序从主函数的函数体第一行开始执行,直至while循环之前,这一部分在每次系统复位后会顺序执行一次,程序中变量的声明、系统的初始化等可以放在这一部分运行;之后,程序进入由while语句构成的主循环中,这部分语句在程序运行时会无限地循环执行,适用于软件查询标志位、扫描按键和数码管等需要不间断访问的部分。主函数的运行过程如图2-5所示。图2-5 主函数的运行过程2.4.2 程序的注释

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载