Steins;Lab

某团的自留研究所

【学习手记】51单片机基础实验(C语言)←2016.6.30~7.06

搞了一块51实验板。考试周刚刚结束,修完了c艹,学了一点汇编和逻辑电路基础,这个东西变得很容易上手。找一片不大不小、不深不浅的泳池,每天做一些练习吧~


实验环境:

主控:STC 89C52

适合新手学习的单片机系统实验

集成usb电源和下载

编程环境:keil C51

下载器:stc-isp-15xx-v6.82

进行实验前需要知道的一些信息

  • 振荡周期:时钟周期
  • 机器周期:一个机器周期包含12个时钟周期,CPU完成一个独立操作
  • 使用TTL电平
  • 编译器选择Atmel AT89C51或者AT89C52,可以兼容

 1.流水灯实验 2016.6.30

流水灯貌似是单片机学习的Hello World。

实验板连接信息:

20160630230822

  • 正极接Vcc,负极接芯片引脚。RL2.RL3为排阻。
  • 发光二极管工作电压1.6V~2.1V。每条支路可以使用电阻150Ω~3000Ω,使二极管正常发光。不接电阻烧毁二极管(我试过,一下就便当了)
  • 在头文件reg52.h中已经申明了P接口地址
  • 关键字sbit为位申明作用,使用方法如
[code]
#include<reg52.h>
sbit LED1 = P1^0;
[/code]

 

  • 由电路原理图,输出接口为低电平(0)时,LED点亮

实验目标:使LED0~8依次点亮,最后刷新全灭重复操作。

代码段:

#include <reg51.h>
void Delay_ms(unsigned int xms)
{
    unsigned int i , j ;
    for(i = xms ;i>0 ;i--)
        for(j = 118; j>0 ;j--);
}
void main()
{
    P1 = 0;
    Delay_ms(100);
    P1 = 0xff;
    Delay_ms(100);
    while(1)
        {
            unsigned int n ;
            for(n=8 ; n>0 ; n--)
            {
                P1 = P1 << 1 ;
                Delay_ms(1000); 
            }   
            P1 = 0xff ;
            Delay_ms(1000);
        }
}

 

在此程序段中,main函数使用了while循环函数。若是不使用while循环,keil程序会自动将main函数重复执行。可以在末尾使用while(1);停留在函数末尾。


2.数码管静态显示2016.7.01

 

8段式数码管,连接信息如下

20160701120525

需要了解的信息:

  • 显示动态显示和静态显示。静态显示选入一直显示。而动态显示使用动态扫描。所有段选线并联。类似选择器。
  • 共阳数码管,阳极共接Vcc,所以,字形库定义如下

 

unsigned char leddata[]={



                0xC0,  //"0"

                0xF9,  //"1"

                0xA4,  //"2"

                0xB0,  //"3"

                0x99,  //"4"

                0x92,  //"5"

                0x82,  //"6"

                0xF8,  //"7"

                0x80,  //"8"

                0x90,  //"9"

                0x88,  //"A"

                0x83,  //"B"

                0xC6,  //"C"

                0xA1,  //"D"

                0x86,  //"E"

                0x8E,  //"F"

                0x89,  //"H"

                0xC7,  //"L"

                0xC8,  //"n"

                0xC1,  //"u"

                0x8C,  //"P"

                0xA3,  //"o"

                0xBF,  //"-"

                0xFF,  //熄灭

                0xA4  //自定义



                         };

推荐小工具:

20160701120655

  • 本接线方法中,P20~P21用于选择,P00~P07用于指示字形。
  • 本实验不使用锁存器

实验要求:使用四位数码管,使其从“0000”每次每位加1变成“9999”

 

代码段:

#include <reg51.h>

sbit P2_0 = P2^0;
sbit P2_1 = P2^1;
sbit P2_2 = P2^2;
sbit P2_3 = P2^3;
int count ;

/*类型为code时,存储在程序段,节省内存空间。但是后期不可改变。若定义char类型,则存储在内存中。*/
unsigned code leddata[]={ 
 
                0xC0,  //"0"
                0xF9,  //"1"
                0xA4,  //"2"
                0xB0,  //"3"
                0x99,  //"4"
                0x92,  //"5"
                0x82,  //"6"
                0xF8,  //"7"
                0x80,  //"8"
                0x90,  //"9"
                0x88,  //"A"
                0x83,  //"B"
                0xC6,  //"C"
                0xA1,  //"D"
                0x86,  //"E"
                0x8E,  //"F"
                0x89,  //"H"
                0xC7,  //"L"
                0xC8,  //"n"
                0xC1,  //"u"
                0x8C,  //"P"
                0xA3,  //"o"
                0xBF,  //"-"
                0xFF,  //熄灭
                0xA4  //自定义
 
                         };

/*延时函数*/
void Delay_ms(unsigned int xms)
{
    unsigned int i , j ;
    for(i = xms ;i>0 ;i--)
        for(j = 118; j>0 ;j--);
}

void LED_ON()
{
    P2_0 = 0 ;
    P2_1 = 0 ;
    P2_2 = 0 ;
    P2_3 = 0 ;
}

void LED_OFF()
{
    P2_0 = 1 ;
    P2_1 = 1 ;
    P2_2 = 1 ;
    P2_3 = 1 ;
}

void main()
{


    do{
        P0 = 0xff;
        LED_ON();
        for(count=0;count<10;count++)
        {
        //  LED_OFF();
            P0=leddata[count];
        //  Delay_ms(5);  个人做了一些实验验证其特性的冗余代码
        //  LED_ON();
            Delay_ms(1000);
        }
    }while(1)   ;
}

备注:

  • 其中计数变量conut写在主函数中就会迷之报错,最后只好写成全局变量。
  • P2输出低电位时,使该段数码管点亮
  • 静态显示不必强制刷新显示。如果想节省io口,可使用锁存器和选择器

 

 

 

 


3.定时计数器和动态数显 2016.7.3

需要知道的信息:

  • 51有2个16位定时计数器,T0T1,之前使用的软件计数器占用CPU资源,而且不准确。
  • 定时器每经过一个*机器周期*刷新一次
  • 简单地,计数器计数范围为0~65535,实际上,即达到65536溢出。
  • 快速公式:65536*1.085us
  • 定时器的控制寄存器TCON地址0x88
  • 7:TF1 6:TR1 5:TF0 4:TR0
  • TF:定时器溢出标志。溢出时为1.中断执行时硬件清零或者软件清零。
  • TR:定时器控制位:1开始计时
  • 定时器工作模式寄存TMOD 0x89 。默认初值为0

 

使用定时器的步骤:

1.设置定时器工作模式寄存器TMOD

2.设置计数寄存器的初值

3.设置定时寄存器TCON

 

TCON能被8整除,可以进行位操作。而不能被8整除,不能进行位操作

 

实验要求:做一个秒表,0到60循环,使用1个计数器即可,使用软件复位。

代码段

#include <reg51.h>

sbit P2_0 = P2^0;
sbit P2_1 = P2^1;
sbit P2_2 = P2^2;
sbit P2_3 = P2^3;
int count ;
int time_counter ;


unsigned code leddata[]={ 
 
                0xC0,  //"0"
                0xF9,  //"1"
                0xA4,  //"2"
                0xB0,  //"3"
                0x99,  //"4"
                0x92,  //"5"
                0x82,  //"6"
                0xF8,  //"7"
                0x80,  //"8"
                0x90,  //"9"
                0x88,  //"A"
                0x83,  //"B"
                0xC6,  //"C"
                0xA1,  //"D"
                0x86,  //"E"
                0x8E,  //"F"
                0x89,  //"H"
                0xC7,  //"L"
                0xC8,  //"n"
                0xC1,  //"u"
                0x8C,  //"P"
                0xA3,  //"o"
                0xBF,  //"-"
                0xFF,  //熄灭
                0xA4  //自定义
 
                         };



void Delay_ms(unsigned int xms)
{
    unsigned int i , j ;
    for(i = xms ;i>0 ;i--)
        for(j = 118; j>0 ;j--);
}



void boot()
{
    TMOD = 0x10; //定时器1工作模式1 16位计时器
}



void display(unsigned char t)
{
 unsigned char t0,t1 ;
 t0= t%10;
 t1= t/10;

 P2_3=0;
 P0=leddata[t0];
 Delay_ms(1);
 P2_3=1;



 P2_2=0;
 P0=leddata[t1];
 Delay_ms(1);
 P2_2=1;
}


void main()
{

    boot();
    TH1 = 0x4b ;
    TL1 = 0xfe ;//送50ms
    TR1 = 1;//启动定时器
    do
    {
        if(TF1 == 1)
        {
            TF1 = 0;//软件复位
            TH1 =0x4b;
            TL1 = 0xfe;//重新送入50ms;
            count++;            
        }

        if(count == 20)
        {
            count = 0;
            time_counter++;
        }
        if(time_counter == 60)
        {
            time_counter = 0;
        }
        display(time_counter);
    
    }while(1)   ;
}

  • 注意数码管显示的位数位置

 


2016.7.4

头文件:

intrins.h

C51单片机编程中,头文件INTRINS.H的函数使用起来,就会让你像在用汇编时一样简便.
_crol_ 字符循环左移
_cror_ 字符循环右移
_irol_ 整数循环左移
_iror_ 整数循环右移
_lrol_ 长整数循环左移
_lror_ 长整数循环右移
_nop_ 空操作 (相当于8051 NOP 指令)
_testbit_ 测试并清零位 (相当于8051 JBC 指令)
函数名: _crol_,_irol_,_lrol_
原 型:
unsigned char _crol_(unsigned char val,unsigned char n);
unsigned int _irol_(unsigned int val,unsigned char n);
unsigned int _lrol_(unsigned int val,unsigned char n);
举例:
_crol_,_cror_:将char型变量循环向左(右)移动指定位数后返回
_testbit_: 相当于JBC bitvar测试该位变量并跳转同时清除。
_chkfloat_: 测试并返回源点数状态。
就是汇编中的子函数。
_crol_,_cror_:如果二进制数为01010101 那么_crol_(1) 左移1位后将高位补低位。
结果10101010。
功 能:_crol_,_irol_,_lrol_以位形式将val 左移n 位,该函数与8051“RLA”指令相关,上面几个函数不同于参数类型。

4.独立键盘 2014.7.5

硬件独立键盘接法图:
S1接P35,S2接P34,S3接P33,S4接P32,另一极共地
 20160706001945
实验前需要知道的信息:
  • 有硬件进行编码的键盘称为编码键盘。
  • 由软件编程识别按键的称为非编码键盘。
  • 所有IO口上电后默认是高电平
  • 另一端接地,按键接通后为0,可以检测io口输入为0

暂时先不使用中断。使用软件循环检测

实验目标:计数器,s3+1,s4-1,注意别溢出

#include <reg51.h>

sbit S1 = P3^5;
sbit S2 = P3^4;
sbit S3 = P3^3;
sbit S4 = P3^2;
sbit P2_3 = P2^3 ;
sbit P2_2 = P2^2 ;

int count ;
int time_counter ;


unsigned code leddata[]={ 
 
                0xC0,  //"0"
                0xF9,  //"1"
                0xA4,  //"2"
                0xB0,  //"3"
                0x99,  //"4"
                0x92,  //"5"
                0x82,  //"6"
                0xF8,  //"7"
                0x80,  //"8"
                0x90,  //"9"
                0x88,  //"A"
                0x83,  //"B"
                0xC6,  //"C"
                0xA1,  //"D"
                0x86,  //"E"
                0x8E,  //"F"
                0x89,  //"H"
                0xC7,  //"L"
                0xC8,  //"n"
                0xC1,  //"u"
                0x8C,  //"P"
                0xA3,  //"o"
                0xBF,  //"-"
                0xFF,  //熄灭
                0xA4  //自定义
 
                         };



void Delay_ms(unsigned int xms)
{
    unsigned int i , j ;
    for(i = xms ;i>0 ;i--)
        for(j = 118; j>0 ;j--);
}






void display(unsigned char t)
{
    unsigned char t0,t1 ;
    t0= t%10;
    t1= t/10;

    P2_3=0;
    P0=leddata[t0];
    Delay_ms(1);
    P2_3=1;



    P2_2=0;
    P0=leddata[t1];
    Delay_ms(1);
    P2_2=1;



    

}


void main()
{

    do //大循环
    {
        display(time_counter);  //数码管显示函数
        if(S3 == 0) //检测s3被按下
        {
            Delay_ms(30);//延时消抖
            if(S3 == 0)
            {
                time_counter++;
                while(!S3); //若不弹起,则暂停 
            }
        }
        if(S4 == 0&& time_counter!=0)   //检测s4被按下,并防止负向溢出
        {
            Delay_ms(30);//延时消抖
            if(S4 == 0)
            {
                time_counter--;
                while(!S4); //若不弹起,则暂停 
            }
        }
    
    }while(1)   ;
}

 


5.矩阵键盘 2016.7.6

连接:

板子无内置,使用了某套件盒里的矩阵键盘

20160706210535

P30第一行P31第二行P32第三行P33第四行

P34第一列P35第二列P36第三列P37第四列

逻辑上跟这个一样

20160706211207

 实验前需要知道的信息:
  • IO 之间如果连接,最终的信号可以用并运算判断
  • 矩阵键盘没有引用外部地线或vcc,用软件编程判断。

 

实验目标:P3接外接矩阵键盘。用数码管显示其16进制键位码,同时用LED指示其按键位置

#include <reg51.h>

sbit S1 = P3^5;
sbit S2 = P3^4;
sbit S3 = P3^3;
sbit S4 = P3^2;
sbit P2_3 = P2^3 ;
sbit P2_2 = P2^2 ;

int count ;
int time_counter ;
unsigned char temp;


unsigned code leddata[]={ 
 
                0xC0,  //"0"
                0xF9,  //"1"
                0xA4,  //"2"
                0xB0,  //"3"
                0x99,  //"4"
                0x92,  //"5"
                0x82,  //"6"
                0xF8,  //"7"
                0x80,  //"8"
                0x90,  //"9"
                0x88,  //"A"
                0x83,  //"B"
                0xC6,  //"C"
                0xA1,  //"D"
                0x86,  //"E"
                0x8E,  //"F"
                0x89,  //"H"
                0xC7,  //"L"
                0xC8,  //"n"
                0xC1,  //"u"
                0x8C,  //"P"
                0xA3,  //"o"
                0xBF,  //"-"
                0xFF,  //熄灭
                0xA4  //自定义
 
};


void Delay_ms(unsigned int xms)
{
    unsigned int i , j ;
    for(i = xms ;i>0 ;i--)
        for(j = 118; j>0 ;j--);
}


void display(unsigned char t)
{
    unsigned char t0,t1 ;
    t0= t%0xf;
    t1= t/0xf;

    P2_3=0;
    P0=leddata[t0];
    Delay_ms(1);
    P2_3=1;

    P2_2=0;
    P0=leddata[t1];
    Delay_ms(1);
    P2_2=1;

}

unsigned char keyscan()
{
    unsigned char key_x, key_y;//坐标变量
    P3 =0xf0;//初始化,等待行线全为1,列线全为0。1111 0000
    if((P3 & 0xf0) != 0xf0) //若发生变化
    {
        Delay_ms(5); //软件消抖
        if((P3 & 0xf0) != 0xf0) //仍然按下
        {
    
            key_y = (P3 & 0xf0); //保存列坐标(y坐标)
            P3 = 0x0f;
            key_x = (P3 & 0x0f); //保存行坐标(x坐标)
            while((P3 & 0x0f) != 0x0f);//松手检测

            return (key_x+key_y); //返回键值
        }
    }
    return temp;
}


void main()
{

    do //大循环
    {
        temp=keyscan();
        P1=temp;
        display(temp);

    
    }while(1)   ;
}


1.不知为啥 我接上了P3的外接键盘后,开发板开始迷之一股青烟,然后某个器件以肉眼可见的速度变黑

2.但是功能似乎没有受到影响

3.冒青烟后,一段时间内编译器无法正常工作。后来洗了脸,竟然又能正常工作了

4.希望这个板子能撑过我的实验折腾……

存活图:

20160706223722

 

阿门!世界真是艰难

(出青烟那瞬间我以为我还得买个新的)

2016.7.6


六 中断系统 2016.7.9

对之前定时器计数器的补充

  • 计数器用于计数, P3.4对应T0,P3.5对应T1
  • 工作方式寄存器TMOD用于设置定时器的工作模式和方式
  • 控制寄存器 TCON 用于控制开始停止
  • TH0,8位,TH1,8位
  • 溢出可触发中断
  • 快速计算: 初值=T0溢出值-需求的计数

 

另外TMOD和TCON的一些注意:

  • TMOD地址89H,只有能被8整除才能位选(^)。
  • 20160709215939
  • C/~T,0计时器,1计数器
  • 00:方式0:13位定时计数器,TH0用低5位,TL0用8位:
  • 方式0溢出后,TH0TL0都被置0.需要软件重新装载
  • 01方式1:16位,TH0TL0都用8位
  • 10方式2:8位自动重装
  • TCON控制方法
  • 20160709220709

 

 

0 0 vote
Article Rating
Subscribe
提醒
guest
5 评论
最新
最旧 得票最多
Inline Feedbacks
View all comments
brotherbiao
brotherbiao
3 年 之前

太狠了!!

fandy
fandy
4 年 之前

必须支持一下!我也想假期学学单片机看时间能不能安排开吧

SPtuan
Reply to  fandy
4 年 之前

7月1日0点01的评论!另我觉得你学起来更快啊…这些c语言方式写的代码看看就懂了~

SPtuan
Reply to  fandy
3 年 之前

你的承诺呢,你的担架呢

fandy2333
fandy2333
Reply to  SPtuan
3 年 之前

我现在课设考试还感冒都要狗带了(手动再见)

5
0
Would love your thoughts, please comment.x
()
x