欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

51单片机学习

程序员文章站 2022-06-08 20:50:45
...

51单片机学习

一直想给女儿做一个平衡小车玩具,想用PLC做,感觉难度,用单片机吧,都快20年没用了。
最近考试考完了,时间和资源都有,正好可以捣鼓一下。看了郭天翔的视频,讲的很不错,
边学边写。

定时器使用

控制任务:

P1.0 控制一个LED灯,亮0.5s,灭0.5s。

设计思路:这里我们只用定时器,不用软延时。51的定时器最多定时60ms,所以我们设置定时器每
50ms中断一次,通过在中断程序设置一个变量来统计中断次数,从而实现较长时间的定时。这里我们
是每500ms执行一次灯亮灯灭的动作,所以每10个中断等于500ms(50ms x 10)。第6行,全局变量
timer50msCount 就是中断次数。第19-23行,当timer50msCount 为10时,代表500ms时间到,把
P1.0 取反,动作一次。

这里有个繁琐的地方,定时器的初值需要手工计算。不过前人开发了一下小程序,直接拿过来用就可以了。
见下图。把自动生成代码中的第一行删掉就可以了。

51单片机学习

程序如下:

#include <reg52.h>
typedef unsigned int uint;
typedef unsigned char uchar;

sbit P10 = P1 ^ 0;
// 第 6 行
uchar timer50msCount = 0;   

void Timer0Init(void);

void main()
{
    EA = 1;         // 开总中断
    ET0 = 1;        // 开定时器 0 中断
    Timer0Init();

    while (1)
    {
        // 每500ms允许if语句块中的程序
        // 19 - 23 行
        if (timer50msCount == 10)
        {
            timer50msCount = 0;
            P10 = ~P10;
        }
    }
}

void Timer0Init(void) //aaa@qq.com
{
    TMOD &= 0xF0; // 设置定时器模式,这里为T0
    TMOD |= 0x01; // 设置定时器工作方式1,为16为定时器
    TL0 = 0x00;   // 设置定时器低位初值
    TH0 = 0x4C;   // 设置定时器高位初值
    TF0 = 0;      // 清楚TF0溢出标志位
    TR0 = 1;      // 启动定时器0开始计时
}


// 定时器0中断子程序
void timer0Interrupt() interrupt 1
{
    timer50msCount++;
    // 每次中断时,定时器初值为0,需重新设置定时器初值,保持50ms
    // 时间不变
    TL0 = 0x00;
    TH0 = 0x4C;
}

独立键盘

控制任务:

按下 s2 键,led 灯亮,松手灯灭。

见面的程序用到一个10ms的延时函数,可以用软件自动生成,见下图。
图片选了1ms,和10ms的延时函数是不一致。1ms的程序里包含了_nop()_指令,
使用这条指令,需要在在代码中使用头文件intrins.h。

#include <intrins.h>

51单片机学习

程序如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;
sbit s2 = P3 ^ 4;       // s2 按键
sbit d1 = P1 ^ 0;      //  led 灯

uchar code table[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71};

uchar code welaTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf};

void Delay10ms();

void main()
{
    uchar num = 0;
    wela = 1;
    P0 = 0xfe;
    wela = 0;

    // 把P3口置高电平
    P3 = 0xff;
    while (1)
    {
        // s2 == 0, 表示 s2 变为低电平,此时按键按下
        if (s2 == 0)
        {
            Delay10ms();  // 去按下时的抖动
            if (s2 == 0)
            {
                d1 = 0;  // 点亮led
                num++;   // 按键次数计数
                if (num > 9)  // led 数码管计数最大为9, 超出置为0
                {
                    num = 0;
                }
            }
            
            // 按键从按下到释放,时间有100-200ms,单片机的
            // 扫描时间很快,此处防止键盘按的过程中,num值反复加
            while (!s2)
                ;
            // 键盘松手是去毛刺延时
            Delay10ms();
        }
        else
        {
            d1 = 1;
        }

        dula = 1;
        P0 = table[num];
        dula = 0;
    }
}

void Delay10ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 18;
    j = 235;
    do
    {
        while (--j)
            ;
    } while (--i);
}

矩阵键盘

控制任务:

4 x 4 的矩阵键盘,按下第0个键,6个数码管都显示0,按下第一个键,6个数码管显示1,一次类推。

程序如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;

// 数字码表,数字最后个0x00为不显示.
uchar code numberTable[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71,
    0x00};

// 行扫描编码表, 每个元素代表一行
uchar code keyTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7};

void delay10ms();
uchar keyScan();
void show(uchar num);

// 51的c编译器比较原始,函数中返回的值只能用
// 全局变量
uchar num = 16;

void main()
{
    // 此处传入16,数字码表选择0x00,
    // 上电时关闭数码管,防止显示乱码, 
    show(16);

    wela = 1;
    P0 = 0xc0;
    wela = 0;

    while (1)
    {
        // keyScan函数返回 0 - 15 数字
        uchar keyNum = keyScan();
        show(keyNum);
    }
}

uchar keyScan()
{

    // 键盘为4X4的键盘矩阵,每个for循环扫描一行,while循环中扫描列,
    // num变量返回这个键盘矩阵的键位,
    // 比如第0个键按下,返回0,第一个返回1,以此类推
    uchar i = 0, temp;
    for (; i < 4; i++)
    {
        P3 = keyTable[i];
        temp = P3 & 0xf0;
        while (temp != 0xf0)
        {
            delay10ms();
            temp = P3 & 0xf0;
            switch (temp)
            {
            case 0xe0:
                num = 4 * i + 0;
                break;
            case 0xd0:
                num = 4 * i + 1;
                break;
            case 0xb0:
                num = 4 * i + 2;
                break;
            case 0x70:
                num = 4 * i + 3;
                break;
            }
        }
    }
    return num;
}

void show(uchar num2)
{
    dula = 1;
    P0 = numberTable[num2];
    dula = 0;
}

void delay10ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 18;
    j = 235;
    do
    {
        while (--j)
            ;
    } while (--i);
}

动态显示

控制任务:

在六个数码管上分别显示1,2,3,4,5,6

程序如下:

#include <reg52.h>

typedef unsigned int uint;
typedef unsigned char uchar;

sbit wela = P2 ^ 7;
sbit dula = P2 ^ 6;

// 段码表
uchar code table[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71};

// 位码表
uchar code welaTable[] = {
    0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf};

void Delay1ms();

void main()
{
    uchar i;

    while (1)
    {
        // 依次只显示一个数码管,但因速度很快,
        // 从而造成眼睛的错觉
        for (i = 0; i < 6; i++)
        {
            P0 = 0xFF;  // 消影
            wela = 1;
            P0 = welaTable[i];
            wela = 0;
            dula = 1;
            P0 = table[i + 1];
            dula = 0;
            Delay1ms();  // 务必要加延时
        }
    }
}

void Delay1ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 2;
    j = 199;
    do
    {
        while (--j)
            ;
    } while (--i);
}

ADC0804 模数转换

/************************
 *  通过AD芯片把模拟量电压值转换为数字量 *
 *  转换后的数字量显示在LED数码管上   *
 ************************/


#include <reg52.h>
#include <intrins.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit dula = P2 ^ 6;     // 段选信号
sbit wela = P2 ^ 7;     // 位选信号
sbit adWr = P3 ^ 6;     // AD 开始转换信号,低电平有效
sbit adRd = P3 ^ 7;     // AD 开始读取信号,低电平有效
sbit adCs = P0 ^ 7;     // AD 片选信号,低电平有效

// LED 数码管段选码表
uchar code numberTable[] = {
    0x3f, 0x06, 0x5b, 0x4f,
    0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c,
    0x39, 0x5e, 0x79, 0x71,
    0x00};

// LED 数码管位选码表
uchar code welaTable[] = {
    0x5f, 0x6f, 0x77, 0x7b, 0x7d, 0x7e};

void delay5ms();

void showNumOnSelectedLED(uchar number, uchar welaNum);

void main()
{
    uchar i, onesPlace, tensPlace, hundredsPlace, adValue;

    // AD 芯片片选使能
    adCs = 0;

    while (1)
    {
        // 转换开始
        _nop_();
        adWr = 0;
        _nop_();
        adWr = 1;

        // 显示数值,放在adWr后,是为了增加AD芯片的转换时间
        for (i = 0; i < 10; i++)
        {
            showNumOnSelectedLED(onesPlace, 0);
            showNumOnSelectedLED(tensPlace, 1);
            showNumOnSelectedLED(hundredsPlace, 2);
        }

        // adRd读取转换后的数字量
        _nop_();
        adRd = 0;
        _nop_();
        adValue = P1;
        adRd = 1;

        // adValue 是读取到的数字量,然后把个、十、百分离开来
        hundredsPlace = adValue / 100;
        tensPlace = adValue % 100 / 10;
        onesPlace = adValue % 10;
    }
}

/** 
 * @brief  在指定的LED数码上显示指定的数字
 * @note   例如,number = 3, placeOfNum = 0, 那么为最右边的第0个数码管显示数字3 
 * @param  number: 显示的数字 0 - F 
 * @param  placeOfNum:  显示的位置,从右到左,分别是 0 - 6 位
 * @retval None
 */
void showNumOnSelectedLED(uchar number, uchar placeOfNum)
{
    // 送位选数据前关闭所有显示,防止打开位选锁存时,
    // 原来段选数据通过位选锁存器造成混乱
    P0 = 0xff;


    wela = 1;
    P0 = welaTable[placeOfNum];
    wela = 0;
    dula = 1;
    P0 = numberTable[number];
    dula = 0;
    delay5ms();
}

/** 
 * @brief  延时函数
 * @note   
 * @retval None
 */
void delay5ms() //@11.0592MHz
{
    unsigned char i, j;

    i = 9;
    j = 244;
    do
    {
        while (--j)
            ;
    } while (--i);
}

DAC0832 数模转换


/*
单片机控制DAC0832芯片输出电流,让发光二极管D12由灭均匀变到
最亮,再有最亮均匀熄灭。最亮和最暗时蜂鸣器发声
*/

#include <intrins.h>
#include <reg52.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
sbit daWr = P3 ^ 6; 
sbit daCs = P3 ^ 2; 
sbit beep = P2 ^ 3; // 蜂鸣器

void delay100ms();
void delay50ms();

void main() {
  uchar flag, value;

  // 关闭数码管
  dula = 0;
  wela = 0;

  // daCs 和 daWr 低电平时,开始DA开始转换
  daCs = 0;
  daWr = 0;

  // P0 口清零
  P0 = 0x00;

  while (1) {
    if (flag == 0) {
      value += 5;
      P0 = value;
      if (value == 255) {
        flag = 1;
        beep = 0;
        delay100ms();
        beep = 1;
      }
      delay50ms();
    } else {
      value -= 5;
      P0 = value;
      if (value == 0) {
        flag = 0;
        beep = 0;
        delay50ms();
        beep = 1;
      }
      delay50ms();
    }
  }
}

void delay100ms()  //@11.0592MHz
{
  unsigned char i, j;

  i = 180;
  j = 73;
  do {
    while (--j)
      ;
  } while (--i);
}

void delay50ms()  //@11.0592MHz
{
  unsigned char i, j;

  i = 90;
  j = 163;
  do {
    while (--j)
      ;
  } while (--i);
}