文档库 最新最全的文档下载
当前位置:文档库 › 编写高质量的单片机C程序word精品文档15页

编写高质量的单片机C程序word精品文档15页

编写高质量的单片机C程序word精品文档15页
编写高质量的单片机C程序word精品文档15页

§5 编写高质量的单片机C程序

收集于网络

高质量的单片机C程序代码运行效率高、可读性和可维护性强。在编写小型的单片机程

序时,代码质量的重要性可能不是很明显。但如果要编写较大规模的程序,特别是多人合作编写程序时,这一点就变得十分重要了。本章内容以林锐的《高质量C++/C编程指南》为蓝本,针对单片机编程的实际各部进行了大量删节和改写。

5.1 文件结构

每个单片机C程序通常分为两种文件。一个文件用于保存程序的声明(declaration),

称为头文件。另一个文件用于保存程序的实现(implementation),称为定义(definition)文件。程序的头文件以“.h”为后缀,定义文件以“.c”为后缀。

5.1.1 版权和版本的声明

版权和版本的声明位于头文件和定义文件的开头(参见示例5.1),主要内容有:

(1)版权信息。

(2)文件名称,标识符,摘要。

(3)当前版本号,作者/修改者,完成日期。

(4)版本历史信息。

* Copyright (c) 2019,上海贝尔有限公司网络应用事业部

* All rights reserved.

* 文件名称:filename.h

* 文件标识:见配置管理计划书

* 摘要:简要描述本文件的内容

* 当前版本:1.1

* 作者:输入作者(或修改者)名字

* 完成日期:2001年7月20日

* 取代版本:1.0

* 原作者:输入原作者(或修改者)名字

* 完成日期:2001年5月10日

示例5.1 版权和版本的声明

5.1.2 头文件的结构

头文件由三部分内容组成:

(1)头文件开头处的版权和版本声明(参见示例5.1)。

(2)预处理块。

(3)函数声明等。

假设头文件名称为 graphics.h,头文件的结构参见示例5.2。

【规则5-1-2-1】为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块。

【规则5-1-2-2】用 #include 格式来引用标准库的头文件(编译器将从标准中国科学技术大学业余无线电协会64

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

库目录开始搜索)。

【规则5-1-2-3】用 #include “filename.h”格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。

〖建议5-1-2-1〗头文件中只存放“声明”而不存放“定义”。

〖建议5-1-2-2〗不提倡使用全局变量,尽量少在头文件中出现象extern int value这类声明。但是,全局变量是中断处理函数与外界程序进行数据交换的唯一途径,因此,几乎每一个单片机程序都会定义全局变量。在单片机C程序中,全局变量使用的频率要比普通的C程序高,但仍应遵守能不用全局变量的地方就不用全局变量的原则。

// 版权和版本声明见示例1-1,此处省略。

#ifndef GRAPHICS_H // 防止graphics.h被重复引用

#define GRAPHICS_H

#include // 引用标准库的头文件

#include “myheader.h” // 引用非标准库的头文件

void Function1(…); // 全局函数声明

#endif

示例5.2 C头文件的结构

5.1.3 定义文件的结构

定义文件有三部分内容:

(1)定义文件开头处的版权和版本声明(参见示例5.1)。

(2)对一些头文件的引用。

(3)程序的实现体(包括数据和代码)。

假设定义文件的名称为 graphics.c,定义文件的结构参见示例5.3。

// 版权和版本声明见示例5.1,此处省略。

#include “graphics.h” // 引用头文件

// 全局函数的实现体

void Function1(…)

示例5.3 C定义文件的结构

5.1.4 目录结构

如果一个程序的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别保

存于不同的目录,以便于维护。

例如可以将头文件保存于include目录,将定义文件保存于source目录(可以是多级目录)。

中国科学技术大学业余无线电协会65

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

5.2 程序的版式

版式虽然不会影响程序的功能,但会影响可读性。程序的版式追求清晰、美观,是程序

风格的重要构成因素。

程序的版式好比为“书法”。好的“书法”可让人对程序一目了然,看得兴致勃勃。差

的程序“书法”如螃蟹爬行,让人看得索然无味,更令维护者烦恼有加。

5.2.1 空行

空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不得用空行。

【规则5-2-1-1】在每个函数定义结束之后都要加空行。参见示例5.4(a)。

【规则5-2-1-2】在一个函数体内,逻辑上密切相关的语句之间不加空行,其它地方应加空行分隔。参见示例5.4(b)。

// 空行 // 空行

void Function1(…) while (condition)

… statement1;

} // 空行

// 空行 if (condition)

void Function2(…) {

{ statement2;

} else

// 空行 {

void Function3(…) statement3;

…// 空行

} statement4;

示例5.4(a) 函数之间的空行示例5.4(b) 函数内部的空行

5.2.2 代码行

【规则5-2-2-1】一行代码只做一件事情,如只定义一个或一组相关的变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。

【规则5-2-2-2】if、for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{}。这样可以防止书写失误。

中国科学技术大学业余无线电协会66

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

x = a + b; X = a + b; y = c + d; z = e + f;

y = c + d;

z = e + f;

if (width < height) if (width < height) dosomething();

dosomething();

for for (initialization; condition; update) (initialization; condition; update) { dosomething();

dosomething(); other();

// 空行

other();

示例5.5(a) 风格良好的代码行示例5.5(b) 风格不良的代码行

〖建议5-2-2-1〗尽可能在定义变量的同时初始化该变量(就近原则)。如果变量的引用处和其定义处相隔比较远,变量的初始化很容易被忘记。如果引用了未被初始化的变量,可能会导致程序错误。本建议可以减少隐患。例如

int width = 10; // 定义并初始化width

int height = 10; // 定义并初始化height

int depth = 10; // 定义并初始化depth

5.2.3 代码行内的空格

【规则5-2-3-1】关键字之后要留空格。像const、virtual、inline、case等关键字之后至少要留一个空格,否则无法辨析关键字。像if、for、while等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

【规则5-2-3-2】函数名之后不要留空格,紧跟左括号‘(’,以与关键字区别。

【规则5-2-3-3】‘(’向后紧跟,‘)’、‘,’、‘;’向前紧跟,紧跟处不留空格。【规则5-2-3-4】‘,’之后要留空格,如Function(x, y, z)。如果‘;’不是一行的结束符

号,其后要留空格,如

for (initialization; condition; update)。

【规则5-2-3-5】赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=”、“+=”“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。

【规则5-2-3-6】一元操作符如“!”、“~”、“++”、“--”、“&”(地址运算符)等前后不加空格。

【规则5-2-3-7】像“[]”、“.”、“->”这类操作符前后不加空格。

〖建议5-2-3-1〗对于表达式比较长的for语句和if语句,为了紧凑起见可以适当地去掉一些空格,如

for (i=0; i<10; i++)和if ((a<=b) && (c<=d))

中国科学技术大学业余无线电协会67

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

void Func1(int x, int y, int z); // 良好的风格

void Func1 (int x,int y,int z); // 不良的风格

if (year >= 2000) // 良好的风格

if(year>=2000) // 不良的风格

if ((a>=b) && (c<=d)) // 良好的风格

if(a>=b&&c<=d) // 不良的风格

for (i=0; i<10; i++) // 良好的风格

for(i=0;i<10;i++) // 不良的风格

for (i = 0; i < 10; i ++) // 过多的空格

x = a < b ? a : b; // 良好的风格

x=a

array[5] = 0; // 不要写成array [ 5 ] = 0;

a.member; // 不要写成a . member;

b-> member; // 不要写成b -> member;

示例 5.6 代码行内的空格

5.2.4 对齐

【规则5-2-4-1】程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐。

【规则5-2-4-2】{ }之内的代码块在‘{’右边数格处左对齐。

中国科学技术大学业余无线电协会68

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

void Function(int x) void Function(int x){

{ … // program code

… // program code }

if (condition) if (condition){

{ … // program code

… // program code }

} else {

else … // program code

… // program code

for (initialization; condition; update) for (initialization; condition; update){

{ … // program code

… // program code }

While (condition) while (condition){

{ … // program code

… // program code }

如果出现嵌套的{},则使用缩进对齐,如:

示例5.7(a) 风格良好的对齐示例5.7(b) 风格不良的对齐

5.2.5 长行拆分

【规则5-2-5-1】代码行最大长度宜控制在70至80个字符以内。代码行不要过长,否则眼睛看不过来,也不便于打印。

【规则5-2-5-2】长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。如

if ((very_longer_variable1 >= very_longer_variable12)

&& (very_longer_variable3 <= very_longer_variable14)

&& (very_longer_variable5 <= very_longer_variable16))

dosomething();

5.2.6 修饰符的位置

修饰符“*”应该靠近数据类型还是该靠近变量名,是个有争议的活题。若将修饰符“*”

中国科学技术大学业余无线电协会69

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

靠近数据类型,例如:int* x; 从语义上讲此写法比较直观,即x是int类型的指针。上述写法的弊端是容易引起误解,例如:int* x,y; 此处y容易被误解为指针变量。虽然将x和y分行定义可以避免误解,但并不是人人都愿意这样做。

【规则2-6-1】应当将修饰符“*”紧靠变量名。例如:

char *name;

int *x, y; //此处y不会被误解为指针

5.2.7 注释

程序块的注释常采用“/*…*/”,行注释一般采用“//…”。注释通常用于:

(1)版本、版权声明;

(2)函数接口说明;

(3)重要的代码行或段落提示。

虽然注释有助于理解代码,但注意不可过多地使用注释。参见示例5.8。

【规则5-2-7-1】注释是对代码的“提示”,而不是文档。程序中的注释不可喧宾夺主,注释太多了会让人眼花缭乱。注释的花样要少。

【规则5-2-7-2】如果代码本来就是清楚的,则不必加注释。否则多此一举,令人厌烦。例如:i++; // i 加1,多余的注释

【规则5-2-7-3】边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。

【规则5-2-7-4】注释应当准确、易懂,防止注释有二义性。错误的注释不但无益反而有害。【规则5-2-7-5】尽量避免在注释中使用缩写,特别是不常用缩写。

【规则5-2-7-6】注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。

【规则5-2-7-7】当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。

/* if (…)

* 函数介绍: {

* 输入参数:…

* 输出参数: while (…)

* 返回值: {

void Function(float x, float y, float z) } // end of while

… } // end of if

示例5.8 程序的注释

5.3 单片机程序命名规则与变量选择

5.3.1 简单单片机程序命名规则

比较著名的命名规则当推Microsoft公司的“匈牙利”法,该命名规则的主要思想是“在

变量和函数名中加入前缀以增进人们对程序的理解”。例如所有的字符变量均以ch为前缀,若是指针变量则追加前缀p。如果一个变量由ppch开头,则表明它是指向字符指针的指针。“匈牙利”法最大的缺点是烦琐,例如

中国科学技术大学业余无线电协会70

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

中国科学技术大学业余无线电协会71

int i, j, k;

float x, y, z;

倘若采用“匈牙利”命名规则,则应当写成

int iI, iJ, ik; // 前缀i表示int类型

float fX, fY, fZ; // 前缀f表示float类型

如此烦琐的程序会让绝大多数程序员无法忍受。

据考察,没有一种命名规则可以让所有的程序员赞同,程序设计教科书一般都不指定命

名规则。命名规则对软件产品而言并不是“成败悠关”的事,我们不要化太多精力试图发明世界上最好的命名规则,而应当制定一种令大多数项目成员满意的命名规则,并在项目中贯彻实施。

单片机C程序用到的变量和常量类型较少,因此命名规则并不需要十分复杂。

【规则5-3-1-1】标识符应当直观且可以拼读,可望文知意,不必进行“解码”。标识符最好采用英文单词或其组合,便于记忆和阅读。切忌使用汉语拼音来命名。程序中的英文单词一般不会太复杂,用词应当准确。例如不要把CurrentValue写成NowValue。

【规则5-3-1-2】标识符的长度应当符合“min-length && max-information”原则。单字符的名字也是有用的,常见的如i,j,k,m,n,x,y,z等,它们通常可用作函数内的局部变量。【规则5-3-1-3】程序中不要出现仅靠大小写区分的相似的标识符。例如:

int x, X; // 变量x与X容易混淆

void foo(int x); // 函数foo与FOO容易混淆

void FOO(float x);

【规则5-3-1-4】程序中不要出现标识符完全相同的局部变量和全局变量,尽管两者的作用域不同而不会发生语法错误,但会使人误解。

【规则5-3-1-5】变量的名字应当使用“名词”或者“形容词+名词”。例如:

float value;

float oldValue;

float newValue;

【规则5-3-1-6】用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。例如:int minValue;

int maxValue;

int SetValue(…);

int GetValue(…);

〖建议5-3-1-1〗尽量避免名字中出现数字编号,如Value1,Value2等,除非逻辑上的确需要编号。这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名字(因为用数字编号最省事)。

【规则5-3-1-7】函数名用大写字母开头的单词组合而成,变量用小写字母开头的单词组合而成。例如:

void Draw(void); // 函数名

void SetValue(int currentValue); // 函数名与变量名

【规则5-3-1-8】常量全用大写的字母,用下划线分割单词。例如:

const int MAX = 100;

const int MAX_LENGTH = 100;

【规则5-3-1-9】静态变量加前缀s_(表示static)。例如:

void Init(…)

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

中国科学技术大学业余无线电协会72

static int s_initValue; // 静态变量

【规则5-3-1-10】全局变量加前缀g_(表示global)。例如:

int g_HowManyPeople; // 全局变量

【规则5-3-1-11】bit型变量加前缀b,整型变量加前缀i,浮点型变量加前缀f,有符号数加前缀sn_,unsigned char型变量不加前缀。

由于8位的单片机程序所用到的数据类型大多数为unsigned char,上述数据类型除bit

型外在单片机程序中甚少出现(如果你的单片机程序中有较多的其它类型的变量,你应该检讨一下你是否应该优化一下你的代码了)。因此上述命名规则并不会增加程序编写时的麻烦。例如:

unsigned char Count;

bit blocked;

unsigned int iCount;

float fPoint;

int sn_iCount;

5.3.2 单片机程序变量与常量的类型选择

一个单片机的内存资源是十分有限的。而变量存在于内存中,同时,变量的使用效率还

要受到单片机体系结构的影响。因此,单片机的变量选择受到了很大的限制。

【规则5-3-2-1】采用短变量。

一个提高代码效率的最基本的方式就是减小变量的长度。使用C编程时我们都习惯于对

循环控制变量使用int类型,这对8位的单片机来说是一种极大的浪费。你应该仔细考虑你所声明的变量值可能的范围,然后选择合适的变量类型。很明显,经常使用的变量应该是unsigned char,它只占用一个字节。

【规则5-3-2-2】使用无符号类型的变量。

为什么要使用无符号类型呢?原因是MCS-51不支持符号运算。程序中也不要使用含有带

符号变量的外部代码。除了根据变量长度来选择变量类型外,你还要考虑变量是否会用于负

数的场合。如果你的程序中可以不需要负数,那么把变量都定义成无符号类型的。

【规则5-3-2-3】避免使用浮点数。

在8位操作系统上使用32位浮点数是得不偿失的。你可以这样做但会浪费大量的时间。

所以当你要在系统中使用浮点数的时候,你要问问自己这是否一定需要。

【规则5-3-2-4】使用位变量

对于某些标志位,应使用位变量而不是unsigned char,这将节省你的内存。你不用多

浪费7位存储区。而且位变量在RAM中访问,它们只需要一个处理周期。

常量的使用在程序中起到举足轻重的作用。常量的合理使用可以提高程序的可读性、可

维护性。

【规则5-3-2-5】尽量用宏定义常量。不便用宏定义的常量应分析程序需要后再决定定义在单片机的内存还是程序存储器中。通常定义在程序存储器中是比较好的选择。

通过宏定义的常量并不占用单片机的任何存储空间,而只是告诉编译器在编译时把标识

符替换一下,这在资源受限的单片机程序中显得非常有用。用code关键字定义的常量放在单片机的程序存储器中;用const关键字定义的常量放在单片机的RAM中,要占用单片机的变量存储空间。而单片机的程序存储器毕竟要比RAM大得多,所以当要定义比较大的常量数组时,51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

用code关键字定义常量要比用const关键字定义合理一些。

【规则5-3-2-6】需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。

【规则5-3-2-7】如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值。例如:

#define RADIUS 100

#define DIAMETER RADIUS * 2

5.4 表达式和基本语句

5.4.1 运算符的优先级

【规则5-4-1-1】如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级。

由于把C语言运算的优先级熟记是比较困难的,为了防止产生歧义并提高可读性,应当

用括号确定表达式的操作顺序。例如:

word = (high << 8) | low

if ((a | b) && (a & c))

5.4.2 复合表达式

如 a = b = c = 0 这样的表达式称为复合表达式。允许复合表达式存在的理由是:

(1)书写简洁;

(2)可以提高编译效率。但要防止滥用复合表达式。

【规则5-4-2-1】不要编写太复杂的复合表达式。例如:

i = a >= b && c < d && c + f <= g + h ; // 复合表达式过于复杂

【规则5-4-2-2】不要有多用途的复合表达式。例如:

d = (a = b + c) + r ;

该表达式既求a 值又求d 值。应该拆分为两个独立的语句:

a =

b + c;

d = a + r;

【规则5-4-2-3】不要把程序中的复合表达式与“真正的数学表达式”混淆。例如:

if (a < b < c) // a < b < c 是数学表达式而不是程序表达式

并不表示

if ((a

而是成了令人费解的

if ( (a

5.4.3 if语句

if语句是C++/C语言中最简单、最常用的语句,然而很多程序员用隐含错误的方式写if

语句。本节以“与零值比较”为例,展开讨论。

1、位变量与零值比较

【规则5-4-3-1】位变量应与语义明确的宏定义常量比较,尽量不直接与1、0进行比较。

在程序中,1和0所表示的含义是不明确的,直接与1、0进行比较很容易发生错误。例如:#define TRUE 1

#define FALSE 0

if(bArrived == TRUE)//含义明确,

中国科学技术大学业余无线电协会73

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

中国科学技术大学业余无线电协会74

//如果写成if(bArrived == 1)或if(bArrived)就不明确了

2、整型、字符型变量与零值比较

【规则5-4-3-2】可将整型变量用“==”或“!=”直接与0比较,也可写成更简便的形式。假设整型变量的名字为value,它与零值比较的标准if语句如下:

if (value == 0)

if (value != 0)

也可写成

if (value)

if (!value)

3、浮点变量与零值比较

【规则5-4-3-3】不可将浮点变量用“==”或“!=”与任何数字比较。

千万要留意,无论是float还是double类型的变量,都有精度限制。所以一定要避免将

浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。假设浮点变量的名字为x,应当将

if (x == 0.0)

隐含错误的比较转化为if ((x>=-EPSINON) && (x<=EPSINON))

其中EPSINON是允许的误差(即精度)。当然,正如前面所说,能够不用浮点型变量就不同浮点型变量。

4、指针变量与零值比较

【规则5-4-3-4】应当将指针变量用“==”或“!=”与NULL比较。指针变量的零值是“空”(记为NULL)。尽管NULL的值与0相同,但是两者意义不同。假设指针变量的名字为p,它与零值比较的标准if语句如下:

if (p == NULL) // p与NULL显式比较,强调p是指针变量

if (p != NULL)

不要写成

if (p == 0) // 容易让人误解p是整型变量

if (p != 0)

或者

if (p)

if (!p)

5、对if 语句的补充说明

程序中有时会遇到if/else/return的组合,应该将如下不良风格的程序

if (condition)

return x;

return y;

改写为

if (condition)

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

return x;

else

return y;

或者改写成更加简练的

return (condition ? x : y);

5.4.4 循环语句的效率

C循环语句中,for语句使用频率最高,while语句其次,do语句很少用。本节重点论述

循环体的效率。提高循环体效率的基本办法是降低循环体的复杂性。

〖建议5-4-4-1〗在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。例如示例5.9(b)的效率比示例5.9(a)的高。

for (row=0; row<100; row++) for (col=0; col<5; col++ )

for ( col=0; col<5; col++ ) for (row=0; row<100; row++)

sum = sum + a[row][col]; sum = sum + a[row][col];

示例5.9(a) 低效率:长循环在最外层示例5.9(b) 高效率:长循环在最内层

〖建议5-4-4-2〗如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。示例5.9(c)的程序比示例5.9(d)多执行了N-1次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果N 非常大,最好采用示例5.9(d)的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例5.9(c)的写法比较好,因为程序更加简洁。

for (i=0; i

if (condition) for (i=0; i

DoSomething(); DoSomething();

else }

DoOtherthing(); else

for (i=0; i

DoOtherthing();

示例5.9(c) 效率低但程序简洁示例5.9(d) 效率高但程序不简洁

5.4.5 for语句的循环控制变量

【规则5-4-5-1】不可在for循环体内修改循环变量,防止for循环失去控制。

〖建议5-4-5-1〗建议for语句的循环控制变量的取值采用“半开半闭区间”写法。示例5.10(a) 中国科学技术大学业余无线电协会75

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

中国科学技术大学业余无线电协会76

中的x 值属于半开半闭区间“0=

for (x=0; x

for (x=0; x<=N-1; x++)

5.4.6 switch语句

switch是多分支选择语句,而if语句只有两个分支可供选择。虽然可以用嵌套的if语句

来实现多分支选择,但那样的程序冗长难读。这是switch语句存在的理由。

〖建议5-4-6-1〗分支很多的选择语句用switch语句;分支不是很多的可以考虑用if语句代替。在单片机程序中,由于编译器对switch语句的编译效率可能不是很好,所以对于分支不

是很多的多分支语句,也可以考虑用if语句代替。

switch 语句的基本格式是:

switch (variable)

case value1 : …

break;

case value2 : …

break;

default : …

break;

【规则5-4-6-1】每个case语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠)。

【规则5-4-6-2】不要忘记最后那个default分支。即使程序真的不需要default处理,也应该保留语句default : break; 这样做并非多此一举,而是为了防止别人误以为你忘了default 处理。

5.4.7 goto语句

自从提倡结构化设计以来,goto就成了有争议的语句。首先,由于goto语句可以灵活跳转,如果不加限制,它的确会破坏结构化设计风格。其次,goto语句经常带来错误或隐患。很多人建议废除C语言的goto语句,以绝后患。但实事求是地说,错误是程序员自己造成的,不是goto的过错。goto语句至少有一处可显神通,它能从多重循环体中咻地一下子跳到外面,用不着写很多次的break语句; 例如

goto error;

示例5.10(a) 循环变量属于半开半闭区间示例5.10(b) 循环变量属于闭区间

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

error:

就象楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑。所以我们主张少用、慎用goto语句,而不是禁用。

5.5 函数设计

函数是C程序的基本功能单元,其重要性不言而喻。函数设计的细微缺点很容易导致该

函数被错用,所以光使函数的功能正确是不够的。本节重点论述函数的接口设计和内部实现的一些规则。

函数接口的两个要素是参数和返回值。C语言中,函数的参数和返回值的传递方式有两

种:值传递(pass by value)和指针传递(pass by pointer)。

5.5.1 参数的规则

【规则5-5-1-1】参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字。如果函数没有参数,则用void 填充。例如:

void SetValue(int width, int height); // 良好的风格

void SetValue(int, int); // 不良的风格

float GetValue(void); // 良好的风格

float GetValue(); // 不良的风格

【规则5-5-1-2】参数命名要恰当,顺序要合理。

例如编写字符串拷贝函数StringCopy,它有两个参数。如果把参数名字起为str1和str2,

例如

void StringCopy(char *str1, char *str2);

那么我们很难搞清楚究竟是把str1拷贝到str2中,还是刚好倒过来。可以把参数名字起

得更有意义,如叫strSource 和strDestination。这样从名字上就可以看出应该把strSource 拷贝到strDestination。

还有一个问题,这两个参数那一个该在前那一个该在后?参数的顺序要遵循程序员的习惯。一般地,应将目的参数放在前面,源参数放在后面。

如果将函数声明为:

void StringCopy(char *strSource, char *strDestination);

别人在使用时可能会不假思索地写成如下形式:

char str[20];

StringCopy(str, “Hello World”); // 参数顺序颠倒

【规则5-5-1-3】如果参数是指针,且仅作输入用,则应在类型前加const,以防止该指针在函数体内被意外修改。例如:

void StringCopy(char *strDestination, const char *strSource);

〖建议5-5-1-1〗避免函数有太多的参数,参数个数尽量控制在5个以内。如果参数太多,在使用时容易将参数类型或顺序搞错。

5.5.2 返回值的规则

【规则5-5-2-1】不要省略返回值的类型。如果函数没有返回值,那么应声明为void类型。【规则5-5-2-2】函数名字与返回值类型在语义上不可冲突。

违反这条规则的典型代表是C标准库函数getchar。例如:

中国科学技术大学业余无线电协会77

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

中国科学技术大学业余无线电协会78

char c;

c = getchar();

if (c == EOF)

按照getchar名字的意思,将变量c声明为char类型是很自然的事情。但不幸的是getchar 的确不是char类型,而是int类型,其原型如下:

int getchar(void);

由于c是char类型,取值范围是[-128,127],如果宏EOF的值在char的取值范围之外,

那么if语句将总是失败,这种“危险”人们一般哪里料得到!导致本例错误的责任并不在用户,是函数getchar误导了使用者。

【规则5-5-2-3】不要将正常值和错误标志混在一起返回。正常值用输出参数获得,而错误标志用return语句返回。

回顾上例,C标准库函数的设计者为什么要将getchar声明为令人迷糊的int类型呢?他

会那么傻吗?

在正常情况下,getchar的确返回单个字符。但如果getchar碰到文件结束标志或发生读

错误,它必须返回一个标志EOF。为了区别于正常的字符,只好将EOF定义为负数(通常为-1)。因此函数getchar就成了int类型。

我们在实际工作中,经常会碰到上述令人为难的问题。为了避免出现误解,我们应该将

正常值和错误标志分开。即:正常值用输出参数获得,而错误标志用return语句返回。

函数getchar可以改写成BOOL GetChar(char *c);

虽然gechar比GetChar灵活,例如 putchar(getchar()); 但是如果getchar用错了,它

的灵活性又有什么用呢(注意上面所举的例子是Windows C的程序而不是单片机C程序,这里仅以这个例子说明道理。单片机C程序中是没有BOOL类型的)?

5.5.3 函数内部实现的规则

不同功能的函数其内部实现各不相同,看起来似乎无法就“内部实现”达成一致的观点。但根据经验,我们可以在函数体的“入口处”和“出口处”从严把关,从而提高函数的质量。【规则5-5-3-1】在函数体的“入口处”,对参数的有效性进行检查。

【规则5-5-3-2】在函数体的“出口处”,对return语句的正确性和效率进行检查。

5.5.4 其它建议

〖建议5-5-4-1〗函数的功能要单一,不要设计多用途的函数。

〖建议5-5-4-2〗函数体的规模要小,尽量控制在50行代码之内。

〖建议5-5-4-3〗合理使用“记忆”功能。

一般来说,相同的输入应当产生相同的输出。带有“记忆”功能的函数,其行为可能是

不可预测的,因为它的行为可能取决于某种“记忆状态”。这样的函数既不易理解又不利于测试和维护。在C语言中,函数的static局部变量是函数的“记忆”存储器。建议尽量少用static局部变量,除非必需。

在单片机的中断处理函数中,函数运行得出的与外界函数有关的参数通过全局变量来交换;而函数本身需要保留的数据则可以用static型变量放起来,而不用全局变量,避免也被外界程序改变这个数据的危险。在前面的程序例子中,就有过必需使用static型变量的例子。〖建议5-5-4-4〗不仅要检查输入参数的有效性,还要检查通过其它途径进入函数体内的变量的有效性,例如全局变量等。

〖建议5-5-4-5〗用于出错处理的返回值一定要清楚,让使用者不容易忽视或误解错误情况。

51 单片机C 语言编程入门——Easy 51 Kit Pro 配套学习资料

中国科学技术大学业余无线电协会79

5.6 单片机程序框架

通过前面的程序示例,相信读者可以清楚地看到单片机C程序的大体框架结构。单片机C

程序结构大体如下:

Initial(…)

Function1(…)

Function_n(…)

InterruptFunction1() interrupt n1

InterruptFunction_n() interrupt n

void main()

Initial();

…; //Some few codes outside initial() and while(1) loop

while(1)

如果代码较长,可按功能把不同的函数分组放在不同的C文件中。例如通常可以把

Initial()函数单独放在initial.c中。一个C文件的代码尽量不要太长,否则会造成查找他维护上的麻烦。

一个单片机小程序编写

一个单片机小程序编写 单片机在家用电器和工业系统中应用广泛,下面给大家介绍一个单片机小程序的编写。 1、设计任务: 如果开关合上,L1亮,开关打开,L1熄灭,如图1所示。监视开关K1(接在P3.0端口上),用发光二极管L1(接在单片机P1.0端口上)显示开关状态。 2、电路原理图: 图1 3、系统板上硬件连线:如图1所示,图中VCC = +5V。 4、程序设计内容: (1)开关状态的检测过程: 开关状态是从单片机的P3.0端口输入信号,当拨开开关K1拨上去(开关断开),即输入高电平;当拨动开关K1拨下去(开关闭合),即输入低电平。可以采用JB BIT,REL 指令来完成对开关状态的检测即可。 (2)输出控制: 如图1所示,当P1.0端口输出高电平,即P1.0=1时,根据发光二极管的单向导电性可知,这时发光二极管L1熄灭;当P1.0端口输出低电平,即P1.0=0时,发光二极管L1亮。我们可以使用SETB P1.0指令使P1.0端口输出高电平,使用CLR P1.0指令使P1.0端口输出低电平。 5、程序框图:如图2所示。

图2 6、汇编源程序的编写: ORG 00H START: JB P3.0,D1 CLR P1.0 SJMP START D1: SETB P1.0 SJMP START END 7、用“keil软件编”写好汇编程序,然后转换成HEX文件并保存。 8、用“增强型A51编程器”把刚才写好的HEX文件烧写入单片机中。 9、把已写入程序的单片机,装入图1的电路,然后通电。当拨动开关K1拨下去(开关闭合),发光二极管L1亮;拨开开关K1拨上去(开关断开),发光二极管L1灭。说明刚才编写的程序达到了我们的设计要求。

如何使用BSL方式给MSP 单片机烧录程序

如何使用BSL方式给MSP430单片机烧录程序 一,使用两个软件:IAR(EW430)和MSP430BSL.exe 二,IAR软件用于编程和编译;BSL软件负责载入烧录。三,在IAR中编程操作: IAR Systems是全球领先的嵌入式系统开发工具和服务的供应商。公司成立于1983年,迄今已有27年,提供的产品和服务涉及到嵌入式系统的设计、开发和测试的每一个阶段,包括:带有C/C++编译器和调试器的集成开发环境(IDE)、实时操作系统和中间件、开发套件、硬件仿真器以及状态机建模工具。 国内普及的MSP430开发软件种内不多,主要有IAR公司的Embedded Workbench for MSP430(简称为EW430)和AQ430。 目前IAR的用户居多。IAR EW430软件提供了工程管理,程序编辑,代码下载,调试等所有功能。并且软件界面和操作方法与IAR EW for ARM等开发软件一致。因此,学会了IAR EW430,就可以很顺利地过渡到另一种新处理器的开发工作。 IAR新建工程步骤 Step1:选择主菜单的File >New>Workspace命令,然后开启一个空白工作区窗口 Step2:选择主菜单Project>Create New Project>选择Empty project。点击OK。最好新建一个文件夹,用于放置所有的生成文件。选择保存路径后,点击保存,新工程建立完毕 Step3:file>new>file>编辑代码>save>文件名可以自己起,但后面一定要加 ”.c” ,保存为C文件 Step4: 右击工程名,将写好的程序添加进去Add>Add Files,也可以用Add>Add“main.c”添加入工程。 Step5: 右击工程名,点击Option>General Options>Device>选择MSP430F149 Step6: 继续设置Linker>Output>文件名.txt(这步很关键)>fomat>

单片机考试小程序

编程题 1,清零程序 将片外数据存储器中5000h~500ffh单元全部清零 ORG OOOOH MOV DPTR, #5000H MOV R0,#00H CLR A LOOP: MOVX @DPTR,A INC DPTR DJNZ RO,LOOP HERE: SJMP HERH 2.试着编写程序,查找在内部 RAM的20h~40h单元中出现00h这一数据的次数将查到的结果存入41h单元 ORG 0000H MOV R0,#20H MOV R1,#21H MOV 41H,#00H LOOP: CJNE @RO,#00H,NEXT INC 41H NEXT: INC R0 DJNZ RI,LOOP HERE: SJMP HERE 3查找在内部RAM的30h~50单元中是否有0AAH这一数据,若有则将51H单元置为01H;若未找到;则将51H单元置为00H. ORG 0000H MOV R0,#30H MOV R1,#21H LOOP: CJNE @R0,0AAH,NEXT MOV 51H,#01H SJMP HERE NEXT: INC R0 DJNZ R1,LOOP MOV 51H,#00H HERE: SJMP HERE 4编写程序功能为把1000H开始的外部RAM单元中的数据送到内部RAM50H开始的单元中,数据的个数存放在了内部RAM60H单元。 ORG 0000H MOV DPTR #1000H MOV R0,#50H MOV R1,60H LOOP: MOVX A,@DPTR MOV 50H,A INC DPTR INC R0

DJNZ RI,LOOP HERE: SJMP HERE 5.编写请将ROM3000H单元内容送R7. ORG 0000H MOV DPTR, #3000H CLR A MOVC A ,@A+DPTR MOV R7,A END 6.片外RAM2000H单元内容送到片外RAM1000H的单元中。 ORG 0000H MOV DPTR,#2000H MOVX A,@DPTR MOV DPTR,,#1000H\ MOVX @DPTR,A 7.锯齿形波: ORG 2000H START: MOV R0,#0FEH MOV A,#00H LOOP: MOVX @R0,A INC A SJMP LOOP 8三角形波 ORG 2000H START MOV R0,#0FEH MOV A,#00H UP: MOVX @R0,A INC A JNZ UP DOWN: DEC A MOVX @DPTR,A JNZ DOWN SJMP UP

51单片机实验程序

3 3 3 用查表方式编写y=x1 +x2 +x3 。(x 为0~9 的整数) #include void main() { int code a[10]={0,1,8,27,64,125,216,343,512,729}; //将0~9 对应的每位数字的三次方的值存入code中,code为程序存储器,当所存的值在0~255 或-128~+127 之间的话就用char ,而现在的值明显超过这个范围,用int 较合适。int 的范围是0~65535 或-32768~32767 。 int y,x1,x2,x3; //此处定义根据习惯,也可写成char x1,x2,x3 但是变量y 一定要用int 来定义。 x1=2; x2=4; x3=9; //x1,x2,x3 三个的值是自定的,只要是0~9 当中的数值皆可,也可重复。 y=a[x1]+a[x2]+a[x3]; while(1); //单片机的程序不能停,这步就相当于无限循环的指令,循环的内容为空白。 } //结果的查询在Keilvision 软件内部,在仿真界面点击右下角(一般初始位置是右下角)的watch 的框架内双击“double-click or F2 to add”文字输入y 后按回车,右侧会显示其16 进制数值如0x34,鼠标右键该十六进制,选择第一行的decimal,可查看对应的10 进制数。 1、有10 个8 位二进制数据,要求对这些数据进行奇偶校验,凡是满足偶校验的 数据(1 的个数为偶数)都要存到内RAM50H 开始的数据区中。试编写有关程序。 #include void main() { int a[10]={0,1,5,20,24,54,64,88,101,105}; // 将所要处理的值存入RAM 中,这些可以根据个人随意设定,但建议不要超过0~255 的范围。 char i; // 定义一个变量 char *q=0x50; // 定义一个指针*q 指向内部0x50 这个地址。 for(i=9;i>=0;i--) //9~0 循环,共十次,也可以用for(i=0;i<10;i++) { ACC=a[i]; //将a[i] 的值赋给累加器ACC if (P==0) //PSW0 位上的奇偶校验位,如果累加器ACC 内数值1 的个数为偶数那么P 为0,若为奇数,P 为1。这里的P 是大写的。 { *q=a[i]; q++; // 每赋一个值,指针挪一个位置指向下一个。 } } while(1); //同实验一,程序不能停。 }

单片机C语言编程实例

单片机C语言编程实例 前言 INTEL公司的MCS-51单片机是目前在我国应用得最广泛的单片机之一.随着 单片机应用技术的不断发展,许多公司纷纷以51单片机为内核,开发出与其兼容的 多种芯片,从而扩充和扩展了其品种和应用领域。 C语言已成为当前举世公认的高效简洁而又贴近硬件的编程语言之—。将C语言向单片机上的移植,始于20世纪80年代的中后期。经过十几年的努力,C语言终于成为专业化单片机上的实用高级语言。用C语言编写的8051单片机的软件,可以大大缩短开发周期,且明显地增加软件的可读性,便于改进和扩充,从而研制出规模更大、性能更完善的系统。因此,不管是对于新进入这一领域的开发者来说,还是对于有多年单片机开发经验的人来说,学习单片机的C语言编程技术都是十分必要的。. C语言是具有结构化.模块化编译的通用计算机语言,是国际上应用最广.最多的计算语言之一。C51是在通用C语言的基础上开发出的专门用于51系列单片机编程的C语言.与汇编语言相比,C51在功能上.结构上以及可读性.可移植性.可维护性等方面都有非常明显的优势。目前 最先进、功能最强大、国内用户最多的C51编译器是Keil Soft ware公司推出的KeilC51。第 一章单片机C语言入门 1.1建立您的第一个C项目 使用C语言肯定要使用到C编译器,以便把写好的C程序编译为机器码, 这样单片机才能执行编写好的程序。KEIL uVISION2是众多单片机应用开发软 件中优秀的软件之一,它支持众多不同公司的MCS51架构的芯片,它集编辑, 编译,仿真等于一体,同时还支持PLM、汇编和C语言的程序设计,它的界面 和常用的微软VC++的界面相似,界面友好,易学易用,在调试程序,软件仿真 方面也有很强大的功能。因此很多开发51应用的工程师或普通的单片机爱好者,都对它十分喜欢。 以上简单介绍了KEIL51软件,要使用KEIL51软件,必需先要安装它。KEIL51是一个商业的软件,对于我们这些普通爱好者可以到KEIL中国代理周 立功公司的网站上下载一份能编译2K的DEMO版软件,基本可以满足一般的个

51单片机考试常见试题简答 题

简答题部分 1、什么叫堆栈? 答:堆栈是在片内RAM中专门开辟出来的一个区域,数据的存取是以"后进先出"的结构方式处理的。实质上,堆栈就是一个按照"后进先出"原则组织的一段内存区域。 2、进位和溢出? 答:两数运算的结果若没有超出字长的表示范围,则由此产生的进位是自然进位;若两数的运算结果超出了字长的表示范围(即结果不合理),则称为溢出。 3、在单片机中,片内ROM的配置有几种形式?各有什么特点? 答:单片机片内程序存储器的配置形式主要有以下几种形式:(1)掩膜(Msak)ROM型单片机:内部具有工厂掩膜编程的ROM,ROM中的程序只能由单片机制造厂家用掩膜工艺固 化,用户不能修改ROM中的程序。掩膜ROM单片机适合于 大批量生产的产品。用户可委托芯片生产厂家采用掩膜方法 将程序制作在芯片的ROM。 (2)EPROM型单片机:内部具有紫外线可擦除电可编程的只读存储器,用户可以自行将程序写入到芯片内部的EPROM 中,也可以将EPROM中的信息全部擦除。擦去信息的芯片 还可以再次写入新的程序,允许反复改写。 (3)无ROM型单片机:内部没有程序存储器,它必须连接程序存储器才能组成完整的应用系统。 无ROM型单片机价格低廉,用户可根据程序的大小来选择外接 程序存储器的容量。这种单片机扩展灵活,但系统结构较复 杂。 (4)E2ROM型单片机:内部具有电可擦除叫可编程的程序存储器,使用更为方便。该类型目前比较常用 (5) OTP(One Time Programmable)ROM单片机:内部具有一次可编程的程序存储器,用户可以在编程器上将程序写入片 内程序存储器中,程序写入后不能再改写。这种芯片的价 格也较低。 4、什么是单片机的机器周期、状态周期、振荡周期和指令周期?它们之间是什么关系? 答:某条指令的执行周期由若干个机器周期(简称M周期)构成,一个机器周期包含6个状态周期(又称时钟周期,简称S周期),而一个状态周期又包含两个振荡周期(P1和P2,简称P周期)。也就是说,指令执行周期有长有短,但一个机器周期恒等于6个状态周期或12个振荡周

单片机如何运行程序

单片机如何运行程序 知道了单片机通过I/O口与外设打交道,也知道了单片机的程序与数据如何保存,到底单片机是如何运行程序的?原来单片机和其他微机一样,也拥有一个中央处理器(CPU),它是整个单片机的核心部件,是8位数据宽度的处理器,能处理8位二进制数据或代码,CPU 负责控制、指挥和调度整个单元系统协调的工作,完成运算和控制输入输出功能等操作。它在单片机中的核心地位见图2.10所示。它通过单片机的内部总线,将单片机内部的各个部分:程序存储器(ROM)、数据存储器(RAM)、定时/计数器、并行接口、串行接口和中断系统等联系在一起,内部总线有三种:数据总线,专门用来传送数据信息,地址总线专门用来传送地址信息,选中各操作单元,控制总线专门用来传送CPU各种控制命令,以便CPU统一指挥协调工作。完成程序所要执行的各种功能。CPU执行程序一般包括两个主要过程:第一,就是从程序存储器中取出指令,指令的地址由PC指针提供,在前面我们已经知道,PC指针在CPU取指后会自动加一,所以PC指针总是指向下一个将要取出的指令代码或操作数。这样,就能保证程序源源不断往下执行。第二,就是执指过程,取出的指令代码首先被送到CPU中控制器中的指令寄存器,再通过指令译码器译码变成各种电信号,从而实现指令的各种功能。 4.怎样保证CPU工作? 现在我们知道了单片机怎样取指、执指,即怎样运行程序了。那么怎样才能保证CPU有序的工作?这就必须提到单片机的两个非常重要的外围电路:单片机的时钟电路和复位电路。在单片机上面有两个引脚,分别是它的第18、19脚,其功能如下。

Pin19:时钟XTAL1脚,片内振荡电路的输入端。 Pin18:时钟XTAL2脚,片内振荡电路的输出端。 89S51的时钟有两种方式,一种是片内时钟振荡方式,但需在18和19脚外接石英晶体和振荡电容,振荡电容的值一般取10p~30p。另外一种是外部时钟方式,即将XTAL1接地,外部时钟信号从XTAL2脚输入。如图2.11 当时钟电路起振后,产生一定频率的时钟信号,单片机的CPU在时钟信号的控制下,就能一步一步完成自己的工作。通常我们必须了解以下几种周期。 【振荡周期】:单片机外接石英晶体振荡器的周期。如外接石英晶体的频率若为12MHz,这其振荡周期就是1/12微秒。 【状态周期】:单片机完成一个最基本的动作所需的时间周期。如扫描一次定时器T0引脚状态所需要的时间。一个状态周期=2个振荡周期。 【机器周期】:单片机完成一次完整的具有一定功能的动作所需的时间周期。如一次完整的读操作或写操作对应的时间。一个机器周期=6个状态周期。 【指令周期】:执行完某条指令所需要的时间周期,一般需要1~4个机器周期,如MUL AB指令是四机器周期指令。一个指令周期=1~4个机器周期。 单片机工作时,除了需要时钟支持外,还必须有一个初始状态,即单片机的复位状态。在单片机外部引脚第9脚,就是专门给单片机提供复位脉冲的。 Pin9:RESET/Vpd复位信号复用脚,当89S51通电,时钟电路开始工作,在RESET 引脚上出现24个时钟周期以上的高电平,系统即初始复位。

单片机C语言小程序

单片机C语言小程序 #include #include #define V AR XBYTE[0x00] /*V AR为外部位址0000*/ #define read 0 /*93c46读取的识别码READ=0*/ #define write 2 /*93c46写入的识别码WRITE=2*/ #define ewen 4 /*93C46写致能的识别码EWEN=4*/ #define ewds 6 /*93C46写除能的识别码EWDS=6*/ #define cs INT0 /*93C46 CS接脚=8051 RD P3.2*/ #define clk INT1 /*93C46 CLK接脚=8051 WR P3.3*/ #define di T0 /*93C46 DI接脚=8051 T1 P3.4*/ #define d0 T1 /*93C46 DO接脚=8051 T0 P3.5*/ #define LOW 0x49 /*存放测试温度的下限值*/ #define HIGH 0x51 /*存放测试温度的上限值*/ bit FLAG0=0; /*宣告TIMER0响应旗号*/ //外接工业专用温度传感器时,目前设置测量温度为0-99度: static const char tab[13]={0x3a,0x53,0x6f,0x8a,0xa3, /*0度,10度,20度,30度,40度*/ 0xB8,0xC8,0xD5,0xDE,0xE5, /*50度,60度,70度,80度,90度*/ 0xEA,0xEE}; /*100度,110度*/ //使用板上AD590温度传感器时,目前设置测量温度为0-99度: //static const char tab[13]={0x88,0x8d,0x92,0x97,0x9c, /*0度,10度,20度,30度,40度,*/ // 0xa1,0xa6,0xab,0xb0,0xb5, /*50度,60度,70度,80度,90度*/ // 0xba,0xc0}; /*100度,110度*/ char data1[2]; char C,S,k=0; char MEP[7]; /*显示器值存放阵列*/ //MEP[0]=数码管最低位显示值,温度指示小数点后位 //MEP[1]=数码管次低位显示值,温度指示个位数 //MEP[2]=数码管高位显示值,温度指示十位数 //MEP[3]=数码管最低位显示值,功能显示目前定为1,2,3,4,5 //MEP[4]= //MEP[5]=暂放置温度显示值,高4位为温度指示十位数值,低4位为温度指示个位数值//MEP[6]=在温度显示与电压调整副程式中,将测量值C暂存MEP[6]中 unsigned char combuf[10]; unsigned char ADR46,CH,CL,m,C1,C2; /*ADR46,93C46位址,CH高位元组,CL低*/ int sec,sec1; char ptr=0,ptr1=0x10,psr=0; /*ptr显示器值存放阵MEP[]指标,ptr1显示器扫描指标*/ char count=100,sb=0; void delay (unsigned int value) /*延时副程式*/ { while (value!=0) value--; /*10us延时*/ } void COMP(); /*宣告比较现在温度与设定温度副程式*/ void SET();

如何将程序代码烧录进STC单片机

不能用keil作下载,它应该只是一个程序编辑和调试用的吧,用keil生成hex 文件。下载时用专门的下载软件找到生成的那个hex文件就可以下载了。,有专门的单片机烧写软件的。那个软件的名字叫STC-ISP V391(你可以下载个更高版本的)的,你的开发板里面应该自带下载软件的啊! 如果你用的下载下是USB转串口的线的话,你需要安装一个USB转串口驱动才能下载程序。网上搜一下就可以了。如果有光盘的话就在光盘里面找,里面肯定有的。 如何将程序代码烧录进STC单片机 先安装<STC单片机编程工具>软件到计算机中,然后进行下面的操作。 以下是烧录程序的主要界面。烧录过程非常简单,操作也非常简单。图中红色的五个大数字就表示了整个过程。简简单单的五步就可以了(实际上只需4步)。 启动本烧录程序后,第一步就是选择烧录器件。本烧录软件支持STC全系列的51单片机芯片,因此,第一步必须选择相对应的型号。由于本实验板选用的单片机芯片就是本烧录

软件首次启动默认的型号,所以,本项一般都不需要选择。 另外,“AP Memory”是指该芯片的内存大小和起止地址,根据器件型号自动更改,不必理会。 选择了器件型号,第二步就应该选择将要被烧录的HEX机器码文件。HEX文件由单片机开发环境输入、编辑代码,最后编绎产生。

至于如何产生HEX文件,很多资料也都有介绍,本板光盘中有详细的说明,并且提供了多套开发软件(每套都能用)。通过对照自学完全可以很快掌握操作。并且,本实验板光盘也提供了多种单片机开发环境供大家学习研究。 本光盘中提供了一些例子程序,大家可以点“open file”按钮,弹出以下窗口(源程序也有,但这里不显示),先选择一些例子程序烧录实验。 选好了文件后,大家可以发现“文件校验和”中的数据发生了变化,大家可以通过留意这个数据是否变化来确定打开文件是否成功,或者文件刷新是否有更改。 当然,文件打开后,会显示在右边的数据区,大家也可以观查右边数据区是否有更改。不过,当数据太多时,更改的地方又很少时,观查“文件校验和”会更快更准确。 选好了器件,选好了文件,第三步就可以设置串口和串口通信速度了。串口是一个九针的插座,老式的鼠标口就是串口。为了让通信可靠,我们可以适当的选低一些的速度,这个串口线较长时非常重要。 烧录过程中,如果出现失败,可以考虑将串口通信速度降低再试,这是由于机器配置以及当地环境因素决定的,当环境干扰过大时,必需选低一点的波特率(即通信速率)。烧录成功与失败,可以从信息区的提示看出。 选择并设置好串口后(一般不需更改),进入第四步,而这一步基本上不用更改。设置时钟倍频主要是为了提高工作速度,设置时钟增益是为了降低电磁幅射。这些,对于高级工程人员和最终产品会很有用,对于初学者来说,只当没有看见就行了。 第五步是最后一步,点击“Download/下载”,就可以进入烧录状态。 特别说明:点击“Download/下载”之前实验板的电源开关必须关闭,使单片机彻底断电,点击“Download/下载”之后才可以使单片机上电,完成程序的烧录。 注意:点击“Re-Download/重复下载”也可以,这常用于大批量的编程,不必每次都去点“Download/下载”。出现以下图状态时,给实验板通电就可以完成编程过程(如果实验板已经通电,则必须关掉电源1秒钟再次通电)。

单片机c程序编写

单片机C语言编程基础 时间:2011-05-01 22:47:26来源:作者: 单片机的外部结构: 1、DIP40双列直插; 2、P0,P1,P2,P3四个8位准双向I/O引脚;(作为I/O输入时,要先输出高电平) 3、电源VCC(PIN40)和地线GND(PIN20); 4、高电平复位RESET(PIN9);(10uF电容接VCC与RESET,即可实现上电复位) 5、内置振荡电路,外部只要接晶体至X1(PIN18)和X0(PIN19);(频率为主频的12倍) 6、程序配置EA(PIN31)接高电平VCC;(运行单片机内部ROM中的程序) 7、P3支持第二功能:RXD、TXD、INT0、INT1、T0、T1 单片机内部I/O部件:(所为学习单片机,实际上就是编程控制以下I/O部件,完成指定任务) 1、四个8位通用I/O端口,对应引脚P0、P1、P2和P3; 2、两个16位定时计数器;(TMOD,TCON,TL0,TH0,TL1,TH1) 3、一个串行通信接口;(SCON,SBUF) 4、一个中断控制器;(IE,IP) 针对AT89C52单片机,头文件AT89x52.h给出了SFR特殊功能寄存器所有端口的定义。教科书的160页给出了针对MCS51系列单片机的C语言扩展变量类型。 单片机C语言编程基础 1、十六进制表示字节0x5a:二进制为01011010B;0x6E为01101110。 2、如果将一个16位二进数赋给一个8位的字节变量,则自动截断为低8位,而丢掉高8位。 3、++var表示对变量var先增一;var—表示对变量后减一。 4、x |= 0x0f;表示为x = x | 0x0f; 5、TMOD = ( TMOD & 0xf0 ) | 0x05;表示给变量TMOD的低四位赋值0x5,而不改变TMOD的高四位。 6、While( 1 ); 表示无限执行该语句,即死循环。语句后的分号表示空循环体,也就是{;} 在某引脚输出高电平的编程方法:(比如P1.3(PIN4)引脚) #include //该头文档中有单片机内部资源的符号化定义,其中包含P1.3 void main( void ) //void 表示没有输入参数,也没有函数返值,这入单片机运行的复位入口 { P1_3 = 1; //给P1_3赋值1,引脚P1.3就能输出高电平VCC While( 1 ); //死循环,相当LOOP: goto LOOP; } 注意:P0的每个引脚要输出高电平时,必须外接上拉电阻(如4K7)至VCC电源。 在某引脚输出低电平的编程方法:(比如P2.7引脚) #include //该头文档中有单片机内部资源的符号化定义,其中包含P2.7 void main( void ) //void 表示没有输入参数,也没有函数返值,这入单片机运行的复位入口 { P2_7 = 0; //给P2_7赋值0,引脚P2.7就能输出低电平GND While( 1 ); //死循环,相当LOOP: goto LOOP; } 在某引脚输出方波编程方法:(比如P3.1引脚) #include //该头文档中有单片机内部资源的符号化定义,其中包含P3.1 void main( void ) //void 表示没有输入参数,也没有函数返值,这入单片机运行的复位入口 {

STC向单片机发送数据小程序(C语言)

#include"stc12c5a60s2.h" #define uchar unsigned char; void initiate(void); void check_zero(void); void time0_on(void); void send_char(void); uchar shu,t,n=1,i=0; uchar code value[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; //************************************************************** void main() { initiate(); for(;;) check_zero(); } //**************************************************************** void initiate(void) { TMOD=0X21; //定时器T1溢出提供波特率,定时器T0定时。 TL1=0XFD; //fosc=11.0592mHz,波特率9600bps对应初值 TH1=0XFD; PCON=0X00; //波特率不加倍。 SCON=0X50; //串口工作方式一,允许接受。 ET1=0; EA=1; TR1=1; } //***************************************************************** void check_zero(void) { if(RI==1) { if(SBUF==0) { RI=0; SCON=0X40;

教你如何用C++写单片机程序

教你如何用C++写单片机程序 关键词:C++ 面向对象单片机 从大一就开始学习单片机,学51,A VR编程都使用C语言的风格,即面向过程,只要能画出程序流程图,程序基本就born了。我热衷于编程,尤其是C++,当时想有没有一天,C++的类和对象也能出现在单片机中? 历经世事沧桑,事到如今,我终于有机会,和大家一起学习使用真正面向对象的C++来控制单片机。目前单片机编译器大部分只支持C语言,C++还不够普及,但我们有理由相信,有着更先进的面向对象的理念,有更加平易近人的类和继承,C++必将取代C,成为单片机程序的主流。试看将来环球单片机,必是C++的世界! 下面大家跟着我来一起学习怎么用C++给单片机编程序!本文要求大家玩过A VR单片机,有过C语言编程经验,而且要对VC6.0开发环境有一定了解。 必备软件:VC6.0(用于编辑源程序),WinA VR(用于生成Makefile,支持A VR系列单片机),Proteus(用于仿真调试)。 首先,你的电脑上要装有VC6.0,进入后选菜单【file】---【new】新建工程,如图1选择Makefile工程,输入工程名称,路径,点确定。 图1_新建工程 一路OK建好工程,界面如图2。这个工程是专门写makefile脚本的,你如果学A VR单片机使用avr-gcc那应该对makefile有一定了解,如果想多了解一点详见https://www.wendangku.net/doc/193364970.html,/view/974566.html?wtp=tt(呵呵,百度百科)。 你还需要安装WINA VR,这是个免费软件,网上很多资源,这个软件很容易安装,一路Next 就可以啦!为了使用方便,我的WINAVR安装到了C盘根目录下的WINA VR文件夹。安装好后,可以直接用它来编辑源代码,今天我就不讲它的使用方法了,只讲怎么生成makefile。自我感觉用熟悉的VC6.0环境编写程序心情很愉快,大家还是跟我一起来,打造

51单片机多任务运行

51单片机多任务运行 最近发现有的幺弟在对系统的内核感兴趣,加上我也是部分内核的初学者,突然来兴,便用了两天写了一个简单的内核。这个内核简单得不能再简单了,加上空格行、大括号和详细的注解只有246行,还带了4个点亮LED的任务。至今为止我所见最简单的内核~~~ 就跟这个内核取个“多任务分时处理内核”吧!这个内核和ucos系统思想有很大的差异,但是能够帮助我们学习理解ucos系统,能够帮我们了解51的内部结构,以及大多数的单片机运行处理数据的原理~~~ 好废话就不说啦!希望我们能互相学习共同进步 1、先来讲讲原理: 首先,我们看书时会知道51单片机在执行中断的时候,会有以下几个步骤和几种情况。 根据KEIL的编译惯例(这个编译惯例你可以在编完程序后点仿真,里面有个后缀为.src 的文件,这个文件里面是一句C对应一句汇编,你就可以知道你编译的C代码它是怎么处理的,能帮助你学习汇编哦~~~),通常把进入中断后的所使用的通用寄存器组根据情况选择压栈。也就是说,中断前后使用的寄存器组可能不一样,中断前可能使用0,中断中可能使用1。如果使用的同一组寄存器,为了保存现场,KEIL就PUSH现场数据,然后POP 就行啦。但是keil很多时候不是你想象中那样,你叫它怎样他就怎样编译。所以在程序中嵌入了少量的汇编。 其实,嵌入汇编是很简单的事情。 只要在C代码中加入#pragma asm 和#pragma endasm并在他俩的中间加入汇编就行。别忘了还要在工程文件中添加C51S.LIB,这个文件在KEIL/C51/LIB中,这个文件也很重要,不然编译会出现警告,记得把文件类型选择为全部文件,不然看不见它。 接下来说说KEIL的中断汇编。在C51中,中断到来时,CPU响应中断保存当前PC 指针地址压栈SP所指地址。然后将PC指针指向中断向量地址,在中断向量地址中只有一句汇编程序:LJMP XX 意思是跳转到某地址。因为中断后只有8个寄存器,但是你的代码量远远不只有8个寄存器能装下的。这也就是说,响应中断后,先跳转到硬件规定的地址,再由那个地址跳转到中断程序入口。 然后,PC指针跳转到中断程序地址,开始从SP所指地址压栈ACC,B,DPH,DPL,PSW,按理说还需要压栈R0~R7,但KEIL一般是通过换通用寄存器来实现的(也就是改变RS1和RS0来实现的)。也就说KEIL根本不压栈R0~R7。 这个怎么能行,当然不行!不保存我们就不能完全的返回先前压栈的任务啦!好吧,那我们就只有手动保存压栈,这样不就行了,简单吧! 所以我们来帮它。已经通过前面知道它在进入中断的时候已经把中断前的PC指针压栈到中断前SP所指的地址了,所以进入中断后,实际在SP中断前所指地址中已经按顺序压栈了PC低8位,PC高8位,ACC,B,DPH,DPL,PSW总共7个数据,SP是向上增长的,也就是说每压一次堆栈SP+1。然后再把我们的R0~R7寄存器压入堆栈,这不就行啦,就保护现场所需的全部数据,就算有时R0~R7寄存器用不上我们也得加进去,为了为了保证正确的返回现场。 因此我们保存一次数据就需要7+8=15字节的堆栈,每个任务的起始地址保存一次,中间临时要保存一次,共需要15+15=30字节的堆栈。所以定义程序空间为现场保存空间为 0~29。名字叫:unsigned char TASK_STACK[TASK_MAX][30];//程序现场保存数组。TASK_MAX是程序个数,因为每一个程序都需要保存两次,每次15个变量来保存现场,并且51是8位的单片机所以用unsigned char。 然后就是程序现场保存数组的初始化使每个数据都是0。 首先,根据响应中断后的压栈顺序,知道了数组0位和1位保存的是中断前程序的地

STC系列单片机程序烧录方法

S T C系列单片机程序烧 录方法 -CAL-FENGHAI-(2020YEAR-YICAI)_JINGBIAN

STC系列单片机程序烧录方法 来源:互联网作者: 关键字:STC单片机 ??单片机烧录 ??单片机烧录方法 ?? STC单片机具有通过串口编程功能,简单到通过串口3三根线就能将程序烧录到单片机内,这大大的方便了开发人员,省去了昂贵复杂的编程器,在调试程序时也可将内部数据直接通过串口发送到PC上观看,一些不太复杂的程序甚至可以省掉仿真器。 目前大部分的计算机都不带串口,这里还得介绍一下一个小转换工具,可将PC上的USB口转换成单片机的TTL电平。插入硬件后提示安装驱动,完成后查看PC上设备管理器,端口中会多出一个串口,这里是COM3,记住这个串口号,下面给单片机烧录是要用。

单片机板和转换板连线对应连接好,如发现无法通讯,可调整2、3的连线。 1,地线----地线 2,TXD-----RXD 3,RXD-----TXD 转换板由于PC供电,指对外提供很小的供电能力,建议单片机板用单独的电源供电,切记不能外接电源和转换板同时对单片机板供电,否则会烧设备或计算机的危险。 硬件连接正常后就是通过STC专门的烧录软件进行烧录了。

1、现在对应的单片机型号 2、打开编译过的需要写入单片机内的程序,类型都是以.bin和.Hex 结尾的文件。 3、选择连接的串口号,就是上边在设备管理器里看到的COM3。 4、选择通讯波特率,单片机目标板上有晶振的,这一项基本可以不用理会,系统会自动适应合适的波特率。如果目标板使用的是内部振荡,由于内部制造误差,自动波特率可能会连接不成功,这时就要手工

单片机课程学习总结

《单片机》课程学习总结 《单片机》这门课程我已经学了一个学期了,在这一个学期的学习过程中,我一开始不怎么懂得编程,但慢慢的我现在已经不仅会读程序还会写程序了。真为自己一个学期来努力学到的单片机知识只是而感到高兴。 怎么学单片机?也常看到有人说学了好几个月可就是没有什么进展。当然,受限于每个人受到的教育水平不同和个人理解能力的差异,学习起来会有快慢之分,但我感觉最重的就是学习方法。一个好的学习方法,能让你事半功倍,这里说说我学习单片机的经历和方法。 我觉得学习单片机首先要懂得C语言,因为单片机大多说都是靠程序来实现的,如果看不懂程序或则不懂的编程是很难学会单片机的。学习单片机首先要明白一个程序是怎么走的,要完全懂得程序每一个步骤的意思。其次要懂得每一条指令的意思,不能盲目地去靠背指令,这是记得不牢靠的,最主要的还是靠了解。学习单片机最主要的对89C51芯片内部结构有全方面的,只要了解了89C51才能知道单片机实现什么样的功能和作用,才能对单片机有更深一步的了解。 通过一个学期《单片机》这门课程的学习,我也从中有了不少心得和体会想和大家分享一下。 万事开头难、要勇敢迈出第一步。开始的时候,不要老是给自己找借口,不要说单片机的程序全是英文,自己看不懂。遇到困难要一件件攻克,不懂指令就要勤奋看书,不懂程序就先学它,这方面网上教程很多,随便找找看一下,做几次就懂了。然后可以参考别的人程序,抄过来也无所谓,写一个最简单的,让它运行起来,先培养一下自己的感觉,知道写程序是怎么一回事,无论写大程序还是小程序,要做的工序不会差多少。然后建个程序,加入项目中,再写代码、编译、运行。必须熟悉这一套工序。个人认为,一块学习板还是必要的,写好程序在上面运行一下看结果,学习效果会好很多,仿真器就看个人需要了。单片机是注重理论和实践的,光看书不动手,是学不会的。 知识点用到才学,不用的暂时丢一边。厚厚的一本书,看着人头都晕了,学了后面的,前面的估计也快忘光了,所以,最好结合实际程序,用到的时候才去看,不必说非要把书从第一页看起,看完它才来写程序。比如你写流水灯,完全就没必要看中断的知识,专心把流水灯学好就是了,这是把整本书化整为零,一小点一小点的啃。 程序不要光看不写,一定要自己写一次。最开始的时候,什么都

松翰单片机的OTP可重复烧写的技巧!

松翰单片机的OTP可重复烧写的技巧! 1.问题:笔者在前期工作中,常常遇到在做测试时,只要修改一些简短的指令或数据时,就要再换一个全新的芯片重新烧录一下,再测试。这样即浪费时间,又消费芯片,还消费金钱呢。 2.解决:其实OTP不是你想象的那么“顽固”,只要你对它好一些,还是有些回报的。在烧录前,芯片的内部全是由2进制的1组成,烧录后,是对里面的1进行切断成0,如此,烧录过的不能再烧录,是对已经把1烧录成0的不可再烧,而是没有把1烧成0的,还是可以再烧的。也就是说,1可以变成0,而0不可再变为1,就象保险丝一样,烧断了就不可再烧,而没烧断的,你还可以把它烧断。要想实现重烧的过程,还是要有些技巧的。哎,废话这么多,来些实例的讲吧。 你可以在你想要的地方预留些空间,等你想要在这修改时,再从里面提取出来。 ;-----------------------------------------------------------------------------; 3.实例1:在已经烧过程序的IC上修改数据: incms t_enter_io ;500ms 进入一次 nop ; mov a,#0xFF ;1 预留数据修改(二进制为:b) mov a,#0xFF ;2 预留数据修改 mov a,#0xFF ;3 预留数据修改 mov a,#0xFF ;4 预留数据修改

mov a,#0xFF ;5 预留数据修改 mov a,#0xF3 ;等待被修改的数据 cjb t_enter_io,a,e_tele_io ;249次进入一下 clr t_enter_io ; ;-----------------------------------------------------------------------------; 3.1.1 (直接修改)比如我想在已经烧有上面程序的IC的基础上,修改a=0xF3(2二进制:B)为a=0xF1,此时,你就不要再浪费IC了,直接在上面修改就可以。你可以看到,0xF3与0xF1的区别只在于3和1,二进制为:0011B和0001B,所以你可以把0011B(十进制3)中第2位的1修改为0,即从0011B成为0001B,所以可以直接修改,当然你也要以把它修改成为你想要的数据,但前提是:你只能从二进制中的1烧成0,不可从0变为1. 比如我可以把0xF1再烧成0xA1或0xA0或0x01或0x00等等。 ;-----------------------------------------------------------------------------; (覆盖修改)是否有一种可以在上面的程序中任意修改数据呢这是有的!读者可以看到,我上面的程序为了防止修改不同的数据而所预留的5条 :mov a,0xFF ,这是为了修改各种数据而预留的。你可以把 0xFF(二进制:B)修改成0~255范围的任意一个数据。修改过程为:先把在最下面的不想要的数据(直接送数指令:mov a,0xF3 ;此指令生成的机器指令为:2DF3),用 NOP(机器指令为:0000 )给填充掉。然后第5条的0xFF修改为你想要的数据),比如修改为:0x45,修改后的程序如下: incms t_enter_io ;500ms 进入一次 nop ; mov a,#0xFF

单片机入门小程序

51单片机的学习是一个动手实践的过程,很多同学在学习单片机的初期总是觉得很难,无法入门。本人根据自己的学习经历,结合自己刚学习单片机的体验,写下几个小的程序供大家参考,如能掌握如下几个小程序的思想与精髓,那么就已经探踏入了单片机的大门。学习单片机的核心就是动手实践,当我们通过自己的努力把第一个LED点亮时,我们就会信心加倍,之后的学习就会变得轻松!希望对大家有所帮助。 一个LED闪烁 #include sbit led=P0^3; unsigned int a; void main() { P0=0x00; while(1) {a=100000; led=1; while(a--); a=10000; led=0; while(a--); } } 两个led闪烁 #include #define uchar unsigned char #define uint unsigned int void delay(); void main() { while(1) { P0=0xaa; delay(); P0=0x00; delay(); } } void delay() { uchar x,y; for(x=110;x>0;x--) for(y=120;y>0;y--); } 流水灯 #include #include void delay(); void main() { unsigned char temp;

P0=0xef; temp=P0; delay(); while(1) { temp=_crol_(temp,1); delay(); } } void delay() { unsigned char x,y; for(x=40;x>0;x--) for(y=200;y>0;y--); } 键控移位信号灯 #include #include #define uchar unsigned char #define uint unsigd neint sbit k1=P1^0; sbit k2=P1^1; void delay(uchar z); void main() { P0=0X18; while(1) { if(k1) P0=_crol_(P0,1); if(k2) P0=_cror_(P0,1); } } void delay(uchar z) { uchar x,y; for(x=z;x>0;x--) for(y=120;y>0;y--) ;} 中断的简单应用 1. 利用定时器中断实现LED闪烁#include #define uchar unsigned char #define uint unsigned int sbit led1=P1^0; uchar num; void main() { P1=0x00;

相关文档
相关文档 最新文档