VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)

VB爱好者乐园(VBGood)

 找回密码
 立即注册
搜索
查看: 40423|回复: 59

[原创] 边学边写之——51单片机软硬件全实践学习笔记(新手教程系列)

[复制链接]
 楼主| 发表于 2008-12-5 00:47:04 | 显示全部楼层 |阅读模式
[前言:]
    本文是作者利用每天晚上下班后的业余时间学习51单片机C语言编程过程的学习笔记,从自制实验电路开始,从硬件和软件(C51语言)两方面边实践边学习。在没有任何单片机硬件、C语言、数字电路、二进制和位运算基础知识的前提下,记述每天的学习过程和进展。

    大家应该能注意到每贴的发表时间,作者每天都是下班后学习到半夜,然后写笔记写到凌晨一点多才发表,负出的辛苦可想而知,所以请初学者要认真仔细地耐心读下去,并且强烈建议结合实践仔细体会文中的内容。

    在本文的学习过程中,会先后接触到以下一些重要的基础知识:
    ?Keil Uv2开发环境的基本操作;
    ?二进制与十六进制的原理和基础运算知识;
    ?单片机 C 语言编程要素(主函数、头文件、数据类型、运算符、语法、基本语句、位运算、数组、指针等等);
    ?了解 C51 语言开发单片机的常用概念(主循环、中断、定时/计数器、串行通信、端口操作、寻址方式简介等等);
    ?单片机与各种常用的外围器件的接口及操作方式(数码管、键盘、存储器、模数/数模转换、串并转换、液晶屏、步进电机等);

    学习过程以实践为主,学习中所涉及到的必要的理论基础知识都有一定程度的论述,虽然不够全面,但确保能够理解,足够满足单片机基础运用的要求,同时也为以后深入学习打下基础。

    学习本文需要一些基本的开发工具软件,以下是下载链接:
    Keil C51 UV2 开发环境:
        http://www.vbgood.com/viewthread ... page%3D2&page=2
    51单片机ISP烧写程序 Easy51 Pro 2.0:
        http://www.vbgood.com/attachment.php?aid=21601

    本文中的学习方法需要自制几个实验电路,所需元器件不超过30元。包括51最小系统核心板,逻辑笔,ISP下载线。此外还需要找一个二手的摩托罗拉手机充电器作为单片机的5V电源。自制实验电路的目的是:
    ?通过自制单片机最小系统实验电路了解单片机最小系统电路结构;
    ?通过自制ISP下载线了解单片机编程器;
    ?通过自制逻辑笔了解逻辑电平原理和测试方法;

    以下链接里有上述实验电路的具体制作的方法。
    《逻辑笔》
    《ISP下载线》
    《51万能实验板》
    《单片机-232串口连接器》
    《可调式直流电源》
    《自制输入输出实验板》
    一些适合初学者自制的实用电路(图纸+照片+配套软件)

    提倡自己动手,不建议购买成品开发板!
    文中所提到的ISP下载线,51最小系统板,逻辑笔都很容易自制,元器件成本只需要30元左右,
强烈建议想学单片机的朋友实践一下!
    本文将持续更新,直到作者完成51单片机入门初期的学习。

                                                       DreamonII 于 2008年12月6日

[声明]
    本文版权归作者 xuhuaping 所有,如需转载请征得作者同意,并注明本文出处:www.vbgood.com,多谢合作。

=================================================================================

C51 新手编程(第一课)
用了3个晚上的时间,做好了3个基本的实验电路:ISP下载线、逻辑测试笔和89S51单片机最小系统实验板。

今天做了C51的第一个实验。。

1、打开Keil UV2
2、工程->新建
3、给工程取个名字,选个文件夹
4、选一种单片机型号(Atmel->89C51)
5、新建一个文件,另存为 XXX.C
6、右键点Source Group 1,把 XXX.C添加进去
7、打开XXX.C

8、写第一个程序:

#include <reg51.h>

void main()
{
   P1 = 0x0F;
   P2 = 0xAA;
}

这就完了。。。

9、然后设置两个属性:文件001.C的属性和工程Target1的属性:

   工程->文件 001.c 属性,弹出对话框,点确定,
   工程->目标"Target1"属性;
   弹出的对话框点“输出”页面,勾选 “生成HEX文件

(Create HEX File)”,确定。
   这样等会编译的时候就能生成“HEX”文件了,这个文件是我们要烧写到单片机里运行的东西。

10、按“F7”,开始编译。如果程序没写错,就能看到:
   “构造目标Target1,正在产生HEX文件...0错误 0警告”这样的提示。
    那么恭喜,你已经生成了第一个可以在单片机上跑的程序了!

现在我们把这个文件写到单片机里去!

11、打开Easy51 Pro 2.0,选好单片机型号“89S51”
12、点“(自动)打开文件”,文件类型选“HEX”,找到你的工程路径,找到刚刚生成的 001.HEX文件;
13、把ISP线插到实验板,接收实验板电源;
14、点“自动完成”,成功后再点“写锁定位”。大功告成了!

现在我们看看刚刚的成果吧!

15、拔掉ISP线,接通逻辑笔电源。
16、按一下实验板的复位键,然后逐一量P1.0~P1.7(1脚到8脚),看看什么现象?
    是不是1、2、3、4脚输出“1”,5、6、7、8脚显示“0”?

17、再逐一量P2.0~P2.7口(21脚~28脚),看什么现象?
    是不是一低一高一低一高?

为什么会这样呢?

18、我们来分析一下!

十六进制的 0 表示成二进制是 0000, F 表示成二进制是 1111,
所以:P1 = 0x0F; 这一句是把 00001111送到P1口,所以P1口就成了4低4高了。
同样道理,十六进制的 A 表示二进制的 1010,
所以 P2 = 0xAA; 这句是把 10101010 送到P2口,于是。。。。

好,第一课结束!

本课要点
1、写程序的时候会发现,如果把 P1 写成 p1,按F7编译会提示出错,
   这是因为 C 语言是区分大小写的。这一点要注意!

2、如果你这样写: P1 = 0x0F 还是会出错,为什么呢?
    因为句末少了个分号 “;”。C语言规定每个语句后面要跟一个分号,表示语句结束。

3、#include <reg51.h>这个干什么用的呢?
   这个是系统提供的头文件,干什么用的?
   我们的程序里用到了 P1 和 P2,很直观地表示 P1口和P2口,
   但实际上P1和P2在单片机里是两个地址,比如0x90,0xA0之类的,即难看又难记。
   所以Keil C51 把它们都放在reg51.h文件里定义成了P1和P2,如下:

     sfr P1   = 0x90;   // sfr 是C51语言独有的一种数据类型,表示“特殊功能的寄存器”
     sfr P2   = 0xA0;

    于是我们就不再需要记地址了。

4、void main()这个有什么用呢?
   C 语言规定程序是从主函数main()这里开始执行的!
   要是缺少了这个单片机就找不到执行的开头了。
   要是你把main改掉也就成了个普通函数那么单片机就没有“入口”了,
   哈哈!那么你就“幸福”了。这个单片机永远也不会执行你的程序。

    main就是领导,当然它可以分配工作给其他的函数。而其他函数就象是员工。。
    在这里main是协调其他函数去干不同的事情,所以函数都不会去调用主函数,
    但是他们之间可以相互调用,主函数也可以调用其他函数。

   函数就把他们做好的事情打包起来,留出人口(参数表)来接受数据,通过return语句返回处理结果。
   
   在单片机里,如果主函数退出(返回)了,程序就结束了,单片机就死机了。
   因此是主函数不需要返回值的,所以其返回值类型就用了 void ,即空类型,相当于“没有”。


举个函数的例子吧!

例(1):

int add(int a,int b)    // 定义一个函数 add,它有两个整数型的参数 a和b
{
    return a + b;    // 它的执行结果就是 返回 a+b 的值
}


void main()  // 这是主函数 main(),是程序的唯一入口,程序从这里开始执行。
{
   int x,y,z;  // 定义3个整数型变量 x,y,z
   
   x = 3;      // 令 x 为 3
   y = 4;      // 令 y 为 4

   z = add(x,y);   // 调用 add()函数,计算 add(x,y)的值,并返回给 z
}

说明:
    程序从void main()这里开始然往下执行,到执行到 z=add(x,y),就调用函数 int add(int a,int b) 。
    于是 x=a y=b ,return a+b 也就是return x+y

     return 把结果计算结果通过函数名 add 返回给 z = add(...),
    因为前面你定义了x=3 y=4
    所以 z = add(x,y) = return x+y = 3+4 = 7

5、P1 = 0x0F;  P2 = 0xAA; 这个0x0F和0xAA是什么东西呢?
   0F和AA是两个16进制的数字,跟10进制差不多,只不过10进制用0~9这十个数字符号表示,
   而16进制用0~9和ABCDEF这16个符号表示,分别对应于10进制的0~15。
   也就是说,不大于15的数都可以用1位16进制数表示。

   为什么呢?
   因为计算机内部是用二进制表示数据的,10进制转化成二进制即难算又难看,
   而每位16进制数刚好能表示4位二进制数,

   0 = 0000     1 = 0001     2 = 0010     3 = 0011
   4 = 0100     5 = 0101     6 = 0110     7 = 0111
   8 = 1000     9 = 1001     A = 1010     B = 1011
   C = 1100     D = 1101     E = 1110     F = 1111

这样的话,我们想让P2口一亮一灭,对应二进制  1010 1010,就可以用十六进制的AA来表示,即

   P2 = 0xAA;

编后语

    单片机就象是一个没有思想的人。而且你和它语言还不通。
    编译器(比如Keil uv2提供的C51编译器)就是一个翻译。
    你所编写的程序(源程序)就是要交给单片机去执行的的工作安排表,
    编译器把你的工作表(源程序)翻译成单片机能理解的语言(目标程序)。
    然后你通过编程器和烧写程序把翻译好的工作表(目标程序)交待(烧写)给单片机。
    于是单片机就会按照你所编辑的程序执行。

比如单片机是个你的一个新来的员工。它还什么都不会干,那么你编程就是给他安排工作任务,比如:
   1,倒茶
   2,要是茶打翻了,就重新去倒。(打翻了就重倒然后执行下条指令,不打翻就直接执行下面条指令)
   3,扫地

    在的编辑程序的时候你要把所有可能发生的事情要考虑到。要是你没考虑到的话。要么它就不执行,要么他就出错。

好了,今天就到这了,明天继续!

[未完待续]

[ 本帖最后由 xuhuaping 于 2009-1-4 23:15 编辑 ]

评分

参与人数 7威望 +40 金钱 +10 人气 +10 收起 理由
bakurise + 8 + 1 很好很强大
灯雪 + 8 + 1 写得真好。
liangdezhi + 5 + 1 虽然不知道是什么,但是我很激动.
hovidelphic + 1 + 1 精品文章
xingjing + 2 + 1 令人热血沸腾
a5566255 + 6 + 2 支持下~~
DreamonII + 10 + 10 + 3 精品文章

查看全部评分

发表于 2008-12-5 15:31:54 | 显示全部楼层
,不错!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-12-6 02:11:07 | 显示全部楼层
C51新手编程(第二课)
1、打开Keil UV2
2、工程->新建
3、给工程取个名字,选个文件夹
4、选一种单片机型号(Atmel->89C51)
5、新建一个文件,另存为 XXX.C
6、右键点Source Group 1,把 XXX.C添加进去
7、打开XXX.C

8、写程序:

#include <reg51.h>

void main()
{
  int i;
  unsigned char j;  //定义类型
  j=0;  //给J设置一个初值
  while(1)  //条件为1,死循环
  {
     for(i=0;i<10000;i++) // 循环10000次
     {
       i=i;  //执行一条语句用来拖延时间
     }

     j=j+1;  //让j增加1
     P1=j;   //把j送到P1上去
  }
}



这就完了。。。

9、然后设置两个属性:文件002.C的属性和工程Target1的属性:

   工程->文件 002.c 属性,弹出对话框,点确定,
   工程->目标"Target1"属性;
   弹出的对话框点“输出”页面,勾选 “生成HEX文件(Create HEX File)”,确定。
   这样等会编译的时候就能生成“HEX”文件了,这个文件是我们要烧写到单片机里运行的东西。

10、按“F7”,开始编译。如果程序没写错,就能看到:
   “构造目标Target1,正在产生HEX文件...0错误 0警告”这样的提示。

现在我们把这个文件写到单片机里去!

11、打开Easy51 Pro 2.0,选好单片机型号“89S51”
12、点“(自动)打开文件”,文件类型选“HEX”,找到你的工程路径,找到刚刚生成的 002.HEX文件;
13、把ISP线插到实验板,接收实验板电源;
14、点“自动完成”,成功后再点“写锁定位”。大功告成了!

现在我们看看刚刚的成果吧!

15、拔掉ISP线,接通逻辑笔电源。
16、按一下实验板的复位键,然后逐一量P1.0~P1.7(1脚到8脚),看看什么现象?
    是不是1、2、3、4、5、6、7、8脚显示“0”“1”的闪烁,而且一个脚比一个脚慢?

为什么会这样呢?

17、我们来分析一下!

int 表示16位有符号整数

char 表示8位有符号整数

在它们前面加上个 unsigned  就表示无符号数了

unsigned char 表示无符号8位整数

unsigned int 表示无符号16位整数

还有 long 表示32位长整数

unsigned long 表示32位无符号长整数

float 表示单精度浮点数

double 表示双精度浮点数

short 表示短整数

bool 是逻辑类型

还有几种,如单片机 C51 语言专用的 bit 表示位类型,和 sbit 特殊位。sbit 表示的是单片机的某个引脚


在这段程序里,我们用到了两种类型的变量: int i和 unsigned

j
分别为16位有符号(-32768~32768)和8位无符号数(0~255)
用到了两种循环: while() 和 for()

while()循环的格式是:

while(条件)
{
     语句1 。。。
     语句2 。。。
     。。。
}

例如:

while( i < 200)
{
    i = i+1;
}




for()循环的格式是:


for(循环变量初值; 循环变量范围; 循环变量计算方法)
{
     语句1 。。。
     语句2 。。。
     。。。   
}

例如:

for(i = 0; i < 100; i=i+1)
{
    P1 = i;
}



我们回到程序!
int i;
unsigned char j;  //定义类型

int i;表示i是16位有符号整数
unsigned char j; 表示j是无符号8位整数
那么i的二进制范围是从多少到多少呢?
-32768~+32767这就是i的范围。
那么j的二进制范围又是从多少到多少呢?
0~255这就是j的范围。

好我们看后面的程序!!!
j=0;  //给J设置一个初始为0的值
while(1)  //条件为1,死循环 这里要是不放这个循环的话那么程序执行一般就结束了。。。所以。。这里我们做个死循环。。
for(i=0;i<10000;i++)
     {
       i=i;  //执行一条语句用来拖延时间
     }

     j=j+1;  //让j增加1
     P1=j;   //把j送到P1上去
for(i=0;i<10000;i++)这句的意思是i从0循环到10000每次加1
i=i;  //执行一条语句用来拖延时间
这里的i++表示i=i+1是C语言里的简略写法
i--表示i=i-1
i+=10表示i=i+10
i*=2表示i=i*2
回到主题
什么意思呢..我们要合起来看这句语句....当i从0循环到10000后再执行后面的语句.
i=i 相当于什么也没干,就是用来拖延时间(10000次循环里这句会被执行10000次)
     j=j+1;  //让j增加1
     P1=j;   //把j送到P1上去
当程序执行到这里时j就增加1然后输出给P1口...
好我们把整个语句结合起来就是...当while为1时一直循环下面的语句..当for的i循环10000遍后j就增加1然后在把j输出P1口.当第一次送出来时j的二进制码为0000 0000循环加到1111 1111的时候已经是到了8位二进制数能表示的最大值了,下次循环的时候j=j+1=11111111+1=1 0000 0000向高位进位了但进位的第9位在8位里表示不了所以就剩下8个0了实际上是进位丢了。所以我们这里可以看出最低位为闪烁频率为最快,最高位的闪烁频率为最慢.所以P1.0闪烁为最快P1.7为最慢.

其实你已经给while设定值为1了..那么单片机只好给你买苦力啦.
本课要点:
这里我们学习到了int 16位有符号整数
unsigned char  无符号8位整数
两个循环
while循环和for循环
while循环就是“当。。。。的时候”
就是说当条件满足的时候就执行语句
这里我们程序是设定的为1.这里说说的1但是不一定为1
当的条件是 “非0” 时就不停的执行下面的语句
而for循环for 循环是根据计数变量的值决定循环次数

今天就到这里,因明天有事!!后天继续!!

[ 本帖最后由 xuhuaping 于 2008-12-9 19:46 编辑 ]

评分

参与人数 1威望 +10 金钱 +10 人气 +3 收起 理由
DreamonII + 10 + 10 + 3 精品文章

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-12-9 19:43:25 | 显示全部楼层
C51新手编程(第三课)
今天学习一下分支语句,C语言的分支语句主要有 if()和

switch()两种。

分别利用这两种语句来做这样一个实验:
    检测P0口的状态,根据哪条引脚为低电平(即有键按下) 来决

定P1口和P2的输出内容。
    比如,如果P0.0为低,就在P2口显示0x01,如果P0.1为低,就

设P2口为0x02

先来看看if() 语句的语法:

if(条件1)
{
    语句
}
elseif(条件2)
{
    语句
}
...
else
{
    语句
}



写程序:
#include <reg51.h>

void main()
{
       
    P0 = 0xff;   // 拉高P0,准备接收输入
    P2 = 0x00;   // P2默认为全0

    while(1)
    {
        if(P0^0 == 0)   // 在C51语言里,P0^0 表示P0口的第

0位,即P0.0口
        {
            P2=0x01;
        }
        else if(P0^1 == 0)
        {
            P2=0x02;
        }
        else if(P0^2 == 0)
        {
            P2=0x03;
        }
        else if(P0^3 == 0)
        {
            P2= 0x04;
        }
        else if(P0^4 == 0)
        {
            P2= 0x05;
        }
        else if(P0^5 == 0)
        {
            P2=0x06;
        }
        else if(P0^6 == 0)
        {
            P2=0x07;
        }
        else if(P0^7 == 0)
        {
            P2=0x08;
        }
        else
        {
            P2=0x00;
        }
    }
}

    编译,写入单片机后,按一下实验板的复位键,然后

P0.0~P0.7逐一对地短接,在测量P2.0~P2.7(1脚到8 脚),看

看什么现象?
是不是。
P0.0对地短接P2.0为高电平P2.1~P2.7为低电平
P0.1对地短接P2.1为高电平P2.0,P2.2~P2.7为低电平
P0.2对地短接P2.2为高电平P2.0~P2.1,P2.3~P2.7为低电平
P0.3对地短接P2.3为高电平P2.0~P2.2,P2.4~P2.7为低电平
P0.4对地短接P2.4为高电平P2.0~P2.3,P2.5~P2.7为低电平
P0.5对地短接P2.5为高电平P2.0~P2.4,P2.6~P2.7为低电平
P0.6对地短接P2.6为高电平P2.0~P2.5,P2.7为低电平
P0.7对地短接P2.7为高电平P2.0~P2.6为低电平
为什么会这样呢?

我们来分析一下!
if(条件1)
{
    语句
}
elseif(条件2)
{
    语句
}
...
else
{
    语句
}
当if条件满足后就执行后面的程序,否则的话就执行else if也就是第二个条件
                if(P0^0 == 0)
                {
                    P2=0x01;
                }
    我们看写的这段程序。当P0.0口得到一个低电平时,那么P2 口就输出0x01=0000 0001,
那么我们用短接的 方法使P0.0得到一个低电平,P2口就相应的输出你设定的值。
    要使用一个IO口作为接受信号的话那么我们在接受之前必须要把接受端口为高电平。

        P0 = 0xff;   //  拉高P0,准备接收输入

    给P2设置一个初值:P2 = 0x00;   // P2默认为全0

    这里为什么P2口为全部低电平呢。。
因为在单片机刚运行时我们也不知道这个接口的是不是都是低电平或高电平,
所以在这里我们把端口P2都设置为低电平。。这样做就是为了端口初始化。

    我们在程序的最后一段看到这样的一段程序
                else
                {
                P2=0x00;
                }
具体有什么用呢?
这段程序是用来你松开按键后失P2口还原到低电平位置。。。
好这段程序我们学习到了if我想大家在VB里也看到过这样的语句。
还有一个语句也能达到这个功能就是case语句

Select case a
   case 值1:
      语句1
   case 值2:
      语句2
   ...
   default: '否则
      语句n
   end select

程序
#include <reg51.h>

void main()
{
        P0 = 0xff;   // 拉高P0,准备接收输入
        P1 = 0x00;   // P1默认为全0
        P2 = 0x00;   // P2默认为全0

    while(1)
        {
          switch(P0)
          {
            case 0xfe:
            {
              P1=0x01;
                break;
            }
            case 0xfd:
            {
              P1=0x02;
                break;
            }
            case 0xfb:
            {
              P1=0x04;
                break;
            }
            case 0xf7:
            {
              P1=0x08;
                break;
            }
            case 0xef:
            {
              P1=0x10;
                break;
            }
            case 0xdf:
            {
              P1=0x20;
                break;
            }
            case 0xbf:
            {
              P1=0x40;
                break;
            }
            case 0x7f:
            {
              P1=0x80;
                break;
            }
            default:
            {
              P1=0x00;
            }
          }
        }
}

我们分析下这段程序。

    当P0口的值等于case的值时就运行case下的程序,
也就是说当PO口的值为0xfe=1111 1110时那么P1口就为 0x01=0000 0001
也就是说当P0.0为低电平时那么P1口就输出0000 0001即P1.0为高电平

在这里几点要小心case  0xfe:是冒号不是分号哦...
还有在每个语句下面加个break这个有什么用呢?
    这个 break 就是说执行了case 下面的语句后就退出select语句,
如果不加这个break,那么在执行完这当前的case分支后的语句后,还将继续执行后面的语句,
一旦条件再次符合,则其他的case分支也会被执行。

    如:
    select(a)
    {
    case 1:
       语句1;
       break;
    case 2:
        语句2;    // <=== 这条后面没有break,
                  //      那么当语句2执行后,还会认为条件满足而继续执行后面分支下的语句3,
                  //      直到遇到一条break语句或执行完后面的所有分支。
    case 3:
        语句3;
        break;
    case 4:       // <=== 则前面的分析可知,case4后没有语句,相当于case4一旦满足,
                  //      将继续执行后面分支直到遇到break
                  //      这种方式通常被用于多个条件都执行相同语句的情况。
    case 5:      
        语句4;
        break;
    case 6:
        语句5;   
    }

本课要点:
1.今天我们学习了两个语句分别是if和case语句.这两个语句都是分支语句.
2.我们在用并口的时候要加个初始化..作为输入口的端口的话初始化必须为高电平..
  要是低电平的话,使输入的 数据不准确.
3.今天我们还是用到了while(1)主循环,
  假如不加主循环的话我们的程序跑一遍就结束了.不再理你了。
  那么等你对地短接时我们的程序已经运行完了,没事干了,就是死机了。
  所以 必须要加个while(1)

今天就到这里!!!待续!!!

[ 本帖最后由 DreamonII 于 2008-12-12 18:18 编辑 ]

评分

参与人数 1威望 +10 人气 +3 收起 理由
DreamonII + 10 + 3 精品文章

查看全部评分

回复 支持 反对

使用道具 举报

发表于 2008-12-12 17:18:19 | 显示全部楼层
等待后文
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-12-14 19:51:11 | 显示全部楼层
本帖最后由 DreamonII 于 2013-6-25 01:23 编辑

C51新手编程(第1-3课小结)
今天我把前面三课的内容回顾下
在前面三课我们讲到
(1):KEIL UV2和Easy51 Pro 2.0的使用方法
(2):#include <reg51.h>的作用
(3)void main()的作用
(4):十六进制和二进制的对应关系
(5):数据类型
(6):while语句
(7):for语句
(8):if语句
(9):switch语句

一:Keil UV2和Easy51 Pro 2.0
1、打开Keil UV2
2、工程->新建
3、给工程取个名字,选个文件夹
4、选一种单片机型号(Atmel->89C51)
5、新建一个文件,另存为 XXX.C
6、右键点Source Group 1,把 XXX.C添加进去
7、打开XXX.C
8、写程序
9、然后设置两个属性:文件001.C的属性和工程Target1的属性



   工程->文件 001.c 属性,弹出对话框,点确定,
   工程->目标"Target1"属性;
   弹出的对话框点“输出”页面,勾选 “生成HEX文件

(Create HEX File)”,确定。
   这样等会编译的时候就能生成“HEX”文件了,这个文件是我

们要烧写到单片机里运行的东西。

10、按“F7”,开始编译。如果程序没写错,就能看到:
   “构造目标Target1,正在产生HEX文件...0错误 0警告”这

样的提示。
现在我们把这个文件写到单片机里去!

11、打开Easy51 Pro 2.0,选好单片机型号“89S51”
12、点“(自动)打开文件”,文件类型选“HEX”,找到你的

工程路径,找到刚刚生成的 001.HEX文件;
13、把ISP线插到实验板,接收实验板电源;
14、点“自动完成”,成功后再点“写锁定位”。大功告成了!

二:#include <reg51.h>
这个是系统提供的头文件,干什么用的?
   我们的程序里用到了 P1 和 P2,很直观地表示 P1口和P2口


   但实际上P1和P2在单片机里是两个地址,比如0x86,0x90之

类的,即难看又难记。
   所以Keil C51 把它们都放在reg51.h文件里定义成了P1和P2

,于是我们就不再需要记地址了。

三:void main()
   程序是从这里执行的!要是缺少了这个单片机就找不到执行

的开头了。要是你把main改掉也就成了个普通函数那么单片机就

没有“领导”了,哈哈!那么你就“幸福”了。这个单片机永远

也不会执行你的程序。
main就是领导,当然它可以分配工作给其他的函数,而其他函数

就是员工。。在这里main是协调其他函数去干不同的事情,所以

函数都不会去调用主函数,但是他们之间可以相互协调用一般函

数,主函数也可以调用其他函数。函数就把他们做好的事情打包

起来,留出人口(参数表)来接受数据,返回处理结果
其实这个也是个函数就是一个是没有返回值的主函数。

四:十六进制和二进制的对应关系
   因为计算机内部是用二进制表示数据的,10进制转化成二进

制即难算又难看,而每位16进制数刚好能表示4位二进制数,

   0 = 0000     1 = 0001     2 = 0010     3 = 0011
   4 = 0100     5 = 0101     6 = 0110     7 = 0111
   8 = 1000     9 = 1001     A = 1010     B = 1011
   C = 1100     D = 1101     E = 1110     F = 1111
这样的话,我们想让P2口一亮一灭,对应二进制  1010 1010,

就可以用十六进制的AA来表示,即P2 = 0xAA;

五:数据类型
int 表示16位有符号整数

char 表示8位有符号整数

在它们前面加上个 unsigned  就表示无符号数了

unsigned char 表示无符号8位整数

long 表示32位长整数

unsigned long 表示32位有/无符号整数

float 表示单精度浮点数

double 表示双精度浮点数

short 表示短整数

还有一种 bool是逻辑类型
51单片机的C语言专用的 bit位   bit 表示1和0, 和 sbit 特殊

的位   sbit 表示的是某个专门的引脚

六:while语句
while()循环的格式是:

while(条件)
{
     语句1 。。。
     语句2 。。。
     。。。
}

例1:

while( i < 200)
{
    i = i+1;
}

列2:
while(1)
{
    i = i+1;
}
while循环就是“当。。。。的时候”
就是说当条件满足的时候就执行语句
这里我们程序是设定的为1.这里说说的1但是不一定为1
当的条件是 “非0” 时就不停的执行下面的语句

七:for语句
for(循环变量初值; 循环变量范围; 循环变量计算方法)
{
     语句1 。。。
     语句2 。。。
     。。。   
}

例如:

for(i = 0; i < 100; i=i+1)
{
    P1 = i;
}
而for循环for 循环是根据计数变量的值决定循环次数

八:if语句
if(条件1)
{
    语句
}
elseif(条件2)
{
    语句
}
...
else
{
    语句
}
也就是说当if条件满足时就执行下面的语句.

九:switch语句
相当于VB的Select语句:
[VB]:
Select case a
   case 值1:
      语句1
   case 值2:
      语句2
   ...
   default: '否则
      语句n
   end select

例3:
[C]:

    switch(a)
    {
    case 1:
    {
       语句1;
       break;
    }
       case 2:
    {
        语句2;    // <=== 这条后面没有break,
                  //      那么当语句2执行后,还会认为条

件满足而继续执行后面分支下的语句3,
                  //      直到遇到一条break语句或执行完

后面的所有分支。
    }
        case 3:
    {
        语句3;
        break;
    }
        case 4:       // <=== 则前面的分析可知,case4后

没有语句,相当于case4一旦满足,
                      //      将继续执行后面分支直到遇到

        break
                      //      这种方式通常被用于多个条件

都执行相同语句的情况。
        case 5:      
        语句4;
        break;
    case 6:
        语句5;   
    }
还有在每个语句下面加个break这个有什么用呢?
    这个 break 就是说执行了case 下面的语句后就退出select

语句,
如果不加这个break,那么在执行完这当前的case分支后的语句

后,还将继续执行后面的语句,
一旦条件再次符合,则其他的case分支也会被执行。

[ 本帖最后由 DreamonII 于 2008-12-14 20:03 编辑 ]

评分

参与人数 1威望 +6 金钱 +8 人气 +3 收起 理由
DreamonII + 6 + 8 + 3 原创内容

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-12-14 19:52:29 | 显示全部楼层
C51新手编程第四课(1)
这一讲我们了解定时/计数器的作用和原理
我先讲一下什么是定时器
单片机就是"放在一个芯片里的计算机" ,所以光有CPU还不算单片机,还需要有内存,外存,输入输出接口和外部设备,这个芯片里就有一台完整的小电脑了.
所以叫"单个芯片的计算机" 简称单片机
内存,外存,输入输出我们都好理解,外部设备有哪些呢?
主要就是串行通信控制器(串口)和定时/计数器
今天这课就是讲定时/计数器
定时/计数器是即能定时,又能计数的器件

单片机不能完全靠人来控制
比如你按什么键它就执行什么事,那么你不按呢?它就傻等着,这可不行,那么我们给单片机制定了工作日程表,总不能一直用人盯着提醒它做什么吧。这样我们给它提供了一个闹钟,就是这个定时器,我们把要做的时安排好时间,然后定时器到了时间就提醒CPU做该做的事,这样就自动化了
再说计数器
如果用单片机来计数,一般可以通过用CPU来计算,可是这样一来,CPU就不能集中精力做事了
比如它想知道生产线上一共传送了多少个产品,那么有一个办法就是让它一直等,有一个产品它就计数加1,可是它也不知道下一个产品什么时候来,所以只好一直等,那它就没办法专心做别的事了,开发人员想了,干脆给它派个助手吧,专门在那等着计数,然后CPU也不管它计了多少,什么时候想知道了就到计数器那里去问一下。。
定时器和计数器其实是一回事 !!!只不过定时器是对系统的时钟信号进行计数。
我们通常用用电是200V 50HZ 我想前面那个我不用解释了吧。。
后面那个的意思就是一秒钟有50次的频率。。也就是50HZ。那么6M也就是6MHZ也就是600万次的频率。也就是说一秒钟600万次。
比如我们用6M的晶振,那么12个时钟周期执行一条指令。就是一个指令周期。我们用计数器对指令周期计数。
6M=600万
600万/12=500K  (k是指千)
就是一秒钟有500K个指令周期。
一个指令周期就是1秒/500K=2微秒
那么我们想定时1毫秒
500*2微秒=1000微秒=1毫秒
就设定计数器记录500个时钟周期就行了
那么要得到1秒呢?
就是1000个1毫秒,无非就是改变计数的值
现在我们来总结一下
这个定时/计数器,其实就是个计数器。只不过用来对内部的指令周期计数的时候就相当于定时器。对外部输入信号计数的时候就是计数器了
那么这个定时器是不是可以无限的计数呢?
不能。为什么呢????因为8位单片机只能表示0~255。
这当然远远不够。开发人员把两个8位的空间连了起来成了一个16位的,这样一来就能计数0~65535了。再多点行不?也行,只是没必要。因为可以用程序做个变量,当作软计数。
而在计数65535这么多个指令周期的过程中,CPU可以执行65535条指令。这么多指令足够完成任何复杂的操作了!!!
比如我们要计数1秒,那么我们用定时器产生1毫秒的定时周期。然后每个周期让i 加1,那么当i=1000的时候,就是1秒了。
如果我们做个j,当i =1000的时候j+1,那么j就是一秒变一次,再做个k,当j每次到60的时候k加1那么j归0
再做个L,当k到60的时候L +1就一小时了,再做个M,L到24的时候M+1,再做N当M到365,N+1,再做个O,M到100,O+1。
好回忆一下,定时器是怎么工作的。
定时器是靠晶振周期工作的。
比如,我们用12MHz的晶振,它一秒钟有多少个周期呢???
12M=12个100万 就是1千200万(M就是100万)
12M表示每秒1200万个周期,
12M/12=100万
也就是说12MHZ的晶振它一秒钟有100万个指令周期。
为什么呢。。因为一个晶振周期是12,当1200万除以12(一个指令周期)时那么就是100万,那么它一秒钟就有100万个指令周期。定时器每个机器周期加1那么一个机器周期是多少秒?
1M个指令周期/秒 表示每个指令周期是 1/1M 秒。
1/1M=1/100万就是100万分之一秒 就是1微秒
说明每1微秒定时器加1那么从0 加到65535一共需要 65535 *1微秒=65535微秒 = 65.535毫秒
所以我们要设计程序的时候,要算好这个时间,也就是说算出定时器的最大定时间隔,不然容易出错。12M晶振的最大定时间隔是65.535毫秒,用定时器定时的时候,不要超过这个数,要不就不准了。
如果换成6M的,最大定时时间也会加倍。如果想得到1秒。
那么600W/12=50万,那么也就是1秒钟得到50万个指令周期。
那么1/0.5M=0.000002秒,0.000002秒=0.002毫秒=2微秒
也就是说我们一个指令周期是2微秒
1秒=1000毫秒=1000000微秒
那么我们的当一个指令周期时定时/计时器就加1当计时器到65535是就65535*2微妙=131070微秒=131.070毫秒=0.131070秒
但是我们发觉这里的换算不好算。。那么我们可以变个方式来算
我们可以用100毫秒来算。。100毫秒=100000微妙
100000微妙/2=50000次
那么当定时/计数器到50000时候也就是100毫秒。那么1秒就等于10*100毫秒。也就是说当在计10次时就是1秒。
我们在这里会碰到一个最大时间间隔用6M的最大时间间隔是131.070毫秒只要小于131.070毫秒就行了。。
但是要小的合理。。别当50毫秒的时候计数。。那样就浪费了。
明明可以分一次操作能完成的事情。。你硬是分了两次。就浪费了,我们要合理的利用里面的资源。。这样既要保证不出错,也要保证不浪费。。

[ 本帖最后由 xuhuaping 于 2008-12-31 00:58 编辑 ]

评分

参与人数 1威望 +8 金钱 +8 人气 +3 收起 理由
DreamonII + 8 + 8 + 3 精品文章

查看全部评分

回复 支持 反对

使用道具 举报

发表于 2008-12-14 20:05:22 | 显示全部楼层
LZ辛苦了。。。
整理得很用心。。。
希望能帮助更多的初学者!!

同意加精!
回复 支持 反对

使用道具 举报

发表于 2008-12-15 23:28:38 | 显示全部楼层
精彩还在后面,谢谢楼主
回复 支持 反对

使用道具 举报

发表于 2008-12-22 14:30:07 | 显示全部楼层
MARK
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

文字版|手机版|小黑屋|VBGood  

GMT+8, 2022-1-20 19:25

VB爱好者乐园(VBGood)
快速回复 返回顶部 返回列表