51单片机

发布时间 2023-12-21 08:54:44作者: 持枢丶

51单片机

1、51单片机初始知识

在51单片机里,int为16位。

给单片机写程序的意义就是让输入/输出的高低电平可以动起来。(不写代码的高电平就一直是高电平了,除非拿开关等期间让它改变。)

51有自己的编译器,有些语法和C语言并不相通。

51单片机有256位寻址。即256Byte空间可用。但高128Byte不建议使用,一般用来存放运行时数据栈。低128Byte可供我们用来存储代码和操作代码中的数据。

另外有一些可供操作的寄存器,它们的起始地址为0x80,和高128Byte寻址重合了,所以使用起来语法有些怪异。高128Byte可以用指针访问,但不建议访问它们。寄存器的0x80开始的我们可以如下方法使用:

sfr P0 = 0x80;	// 第一次使用这个P0赋值等同于指针指定地址,只不过这样是指定的寄存器,是告诉编译器,我们以后再使用这个P0时,就是往这个寄存器的值作修改。一个寄存器8bit
sbit g_led = P0^0; 	// 声明寄存器第0位的地址,以后再使用g_led即是操作第0位
sbit g_led1 = P0^1; 	// 声明寄存器第1位的地址,以后再使用g_led即是操作第1位,不可 直接 P1=0x81这样声明,不是整8
2、关于<STC89C5xRC.H>头文件
// 摘取一丢丢代码,这是说明咱以后要操作的哪个空间,以后再使用这些P就是给里面赋值了
sfr P0   = 0x80;	// 这个有8个bit空间
sbit P00 = P0^0;	// 这个有1个bit空间
sbit P01 = P0^1;
sbit P02 = P0^2;
sbit P03 = P0^3;
sbit P04 = P0^4;
sbit P05 = P0^5;
sbit P06 = P0^6;
sbit P07 = P0^7;

注意:这个sfr 和 sbit 第一次是准备好地址,第二次是操作,有点怪

使用char右移时遇到了一个问题,这个有符号的char右移时前面补1,有时间搞清以下

51编译器不能赋值二进制

3、51控制数码管

这个模块由两个共阴极的数码管一个138译码器一个74HC245驱动器八个100Ω电阻构成。

  • 八个100Ω电阻不作解释;
  • XD74HC245驱动器的作用是增强电流。单片机会输出一个信号,只不过是弱电流,大概也就只能点亮个LED灯,所以我们需要一个驱动器来整合电流。
  • 74HC138N译码器,即138译码器(也有其他种类的38译码器)。138译码器的输出为Y0-Y7的0为表示的位,即当输入的A2、A1、A0为0、0、0时,Y0-Y7的输出为01111111。 001为10111111。
  • 共阴极数码管。用了两个,每个数码管有四个能表示的数,每个表示的数(能表示0-9的那一个)所用到的二极管共阴极(共负极,尾巴相连)。每个能表示的数用到八个二极管(七个表示数,一个表示小数点)。

我们的这个51的CPU关于寄存器的引脚分别有:P00-P07、P10-P17、P20-P27、P30-P37、P40-P43。

我们将51的P15、P14、P13引脚连接上138译码器的A2、A1、A0,这样可以通过代码控制138译码器的输出,138译码器输出的Y0-Y7这八位通过DIG1-DIG8这八个网络名和数码管连接起来,以达到通过P15、P14、P13引脚控制两个数码管八个数字的哪个数字的使用状态注意:我们同时只能操作同一个数字,因为我们3位同时操作只能让输出的8位的一个为0,如果想让两个及以上数字同时显示,需要不断短时间间隔刷新两个数,以达到看起来同时显示的目的。当我们把延时调低时,数码管会更亮,否则更暗,不加延时会出现不可预料的错误,但我们写的代码中一般都有消耗的时间,可代替一些延时。

我们将51的P00-P07和245驱动器的输入引脚连接起来,输出的八个引脚分别控制每一个数字的八个二极管,一般从头顶那个二极管开始表示A,也即是输出的第一个引脚,然后依次顺时针引脚递加,最后一个引脚是小数点。

通过51的引脚操控译码器选择操作哪个数字,这个叫做片选;通过51的引脚控制驱动器可选择操作显示出为那个数 。

下面是代码。作循环操作时,一定注意 无符号数 不要减到-1,不然它就成最大值了。

todo 无符号数右移时,前面可能补的是1,有点不确定。

Com通用代码如下(可用Util命名,这里不作.h和.c的区分)

/**
* 下面的代码为 数码管上的表示 和 真实数字 的对应
*/
char digital_code[10] = {
  0x3F, // 0
    0x06, // 1
    0x5B, // 2
    0x4F, // 3
    0x66, // 4
    0x6D, // 5
    0x7D, // 6
    0x07, // 7
    0x7F, // 8
    0x6F  // 9
};
/**
* 显存,即八个数字要显示的数
*/
static unsigned char s_digital_buffer[8];
/**
 * @brief 一段延时的代码,偏差也许很大,因为我把代码简化了
 * 
 * @param n 想要延时的毫秒数
 */
void Delay1ms(unsigned int n){
    unsigned int c = 400;
    while(c--);
}

Int驱动代码如下(针对此例可用DigitalTube命名,这里不作.h和.c的区分)

/**
 * @brief 调用此函数给显存赋值,即给数码管的八位数字放上数
 *
 * @param num 16bit无符号数,这里这个数是我们要显示的数,放到显存里,如1234,我们在这个函数里给它作取余操作
 */
void DigitalTube_DisplayNum(unsigned int num){
    unsigned char i;
    // 给显存赋值前先清空显存
    for(i=0; i<8; i++) s_digital_buffer[i]=0;
    // todo
    // 给显存赋值
    i = 7;
    while(num>0){
        s_ditgital_buffer[i--] = digital_codes[num % 10]; // 将真实数字对应的映射放到显存中。
        num /= 10;  
    }
    // 单独处理num传过来0的情况
    if(num==0){
        s_digital_buffer[7] = digital_codes[0];
    }
}
/**
 * @brief 让数码管的某一位显示特定的组合
 *
 * @param dig 传来的片选,取值范围[0-7],即直观地把操作第几个数字传进来。从0开始。
 * @param dat 传来的段选信号
 * 我们在代码中要操作的 片选为P1, 段选为P0
 */
void DigitalTube_DisplaySingle(unsigned char dig, unsigned char dat){
    // 关掉段选信号,否则当下面的片选赋值时,会直接显示出旧值来
    P0 = 0;
    // 初始化片选,我们只需要P1的3-5位,即P15、P14、P13。所以我们要让这三位置零(方便后续的操作,这个不操作的话确实是控制第0个数字,但我们要后面操作一下),其他的原来是啥还是啥,我们可以和0b11000111作与操作,但51编译器不支持二进制赋值,所以我们可以用十六进制0xc7
    P1 &= 0xc7;
    // 让传过来的dig转化为二进制,放到P1中的P15、P14、P13位置。
    dig <<= 3;	// 先和P15-P13对齐
    P1 |= dig;	// 原P15-P13为000,其他位为未知。或操作后,P15-P13变成dig,其他位是和0作的或操作,所以保持不变。
    // 段选,传的是数字对应好的那个数,比如要显示0,它的对应为0x3f。这个对应在别的地方给它对应上,这里不作操作。
    P0 = dat;
}
/**
 * @brief 将显存的内容显示到数码管
 */
void DigitalTube_Refresh()
{
    u8 i;
    for (i = 0; i < 8; i++)
    {
        DigitalTube_DisplaySingle(i, s_digital_buffer[i]);
        Delay1ms(1);
    }
}

Main函数略,上面的代码提供好了写显存和从显存中读取的功能,想使用什么功能可以自己实现但需要注意一下,如果想实现一个比如从100到1的递减显示操作,往显存写之后打印,然后写下一个数再打印...这样会出现只有个位数能完整显示的问题,这是因为我们执行完第一个数如100时,片选停留在选择了最后一个位,段选选择了0,下面的代码消耗时间,消耗的这段时间中,数码管一直在显示片选和段选组合出的结果。我们可以让100多循环显示一段时间,刷了后面位再刷前面的位,这样一小段时间后再执行下面的99。

todo,命名规则 和 分层