×
单片机 > 单片机程序设计 > 详情

串行口通信 单片机在数码管显示AD采集值

发布时间:2020-08-31 发布时间:
|
一. 程序功能
单片机上电后等待从上位机串口发送来的命令(波特率为9600bps),同时在数码管的前三位以十进制显示 AD采集来的数值。 
1)当收到以十六进制发送来的01后,向上位机发送字符串“Turn on ad!”,同时间隔 一秒读取AD的值,然后把AD转换过来的数表示成实际电压值(浮点数),并且从串口发 送给上位机,形式如“The voltage is 3.398438V”发送周期也是一秒一次。 
2)当收到02时,向上位机发送“Turn off ad!”并停止发送电压值 
3)收到其他数时显示“Error!”
 
二. 程序源码
1)法1
#include
#include
#include
 
#define uchar unsigned char
#define uint unsigned int
 
sbit dula = P2 ^ 6; //申请U1锁存器的锁存端
sbit wela = P2 ^ 7; //申请U1锁存器的锁存端
sbit adwr = P3 ^ 6;    //定义AD的WR端口
sbit adrd = P3 ^ 7; //定义AD的RD端口
 
uchar flag, flag_uart, flag_time, flag_on;
uchar a, i, t0_num, ad_val;
 
float ad_vo;
 
//数码管的编码数组
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
 
void delayms(uint xms);
void init();
void display(uchar value);
uchar get_ad();
 
void main()
{
    //初始化
    init();
    //为了节省IO端口,ADC0804的片选端CS连接U2锁存器的Q7输出端
    //即位选时,P0的最高位   
    //于是通过位选时对P0赋值置CSAD为0,选通后,以后不必在管
    wela = 1;
    P0 = 0x7f;
    wela = 0;
    while (1)
    {
        //若检测到flag_uart为1,说明串口已经执行过串口中断服务程序, 即收到了数据
        if (flag_uart == 1)
        {
            //手动将flag_uart清0,方便标志位检测
            flag_uart = 0;
            //检测到收到数据后,先将ES清0,原因是接下来要发送数据,若不关闭串口,
            //发送完数据后,单片机会重新申请串口中断flag_uart又为1,又再次发送,一直反复
            ES = 0;
            //由于下面switch中需要调用puts,printf,在单片机的puts,printf中需要对TI是否为1
            //进行检测,就是需要等待TI为1才会将字符发送出去,否则一直等待下去
            //所以这里需要先将TI置为1
            TI = 1;
            //根据执行串口中断后获取的flag_on的值执行相应的动作
            switch(flag_on)
            {
                case 0:
                    puts("Turn on ad!\n");
                    TR0 = 1; //开启计数器0
                    break;
                case 1:
                    printf("Turn off ad!\n");
                    TR0 = 0;   //关闭计数器0
                    break;
                case 2:
                    puts("Error!\n");
                    break;
            }
            //等待发送完毕,因为发送完毕后TI会由硬件清0
            while (!TI);
            //手动TI清0
            TI = 0;
            //重新开启串口中断
            ES = 1;
        }
 
        //通过flag_time判断时间是否到1s, 从而去获取AD转换的值
        if (flag_time == 1)
        {
            //手动将flag_time清0,方便下次检测
            flag_time = 0;
            //获取AD转换的值
            ad_val = get_ad();
            //获取浮点数表示的AD实际采集到的电压值
            ad_vo = (float)ad_val*5.0/256.0;
            //关闭串口中断
            ES = 0;
            //下面要调用printf,所以将TI置1, 原因见上面
            TI = 1;
            printf("The voltage is %fV\n", ad_vo);
            //检测发送是否完成
            while (!TI);
            //手动TI清0
            TI = 0;   
            //开启串口中断
            ES = 1;
        }
 
        //数码管上显示AD转化的值
        display(ad_val);
    }
}
 
void delayms(uint xms)
{
    uint i, j;
 
    for (i = xms; i > 0; i--)
        for (j = 110; j > 0; j--)
            ;
}
 
void init()
{
    //设定T1定时器工作方式2, T0定时器工作方式1
    TMOD = 0x21;
    //为T0定时器装入初值
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    //为T1定时器装入初值
    TH1 = 0xfd;
    TL1 = 0xfd;
    //ET1 = 1;  这里不需要开启定时器1中断,因为定时器1工作在方式2,为8位自动重装方式,进入中断也无事可做
    //启动T1定时器
    TR1 = 1;
    //开启定时器0中断
    ET0 = 1;
    //启动定时器0
    //TR0 = 1; TR0的初始化放在主函数的while中,方便检测到串口发送数据后的1s延时,即延时1s从串口发送完数据开始
    //设定串口工作方式
    //11位异步收发,含9位数据,波特率可变,且由定时器1溢出率控制
    SM0 = 1;
    SM1 = 1;
    //容许串口中断
    REN = 1;
    //开启总中断
    EA = 1;
    //开启串口中断
    ES = 1;
}
 
void display(uchar num)
{
    uchar bai, shi, ge;
 
    bai = num / 100;
    shi = num % 100 / 10;
    ge = num % 10;
 
    dula = 1;
    P0 = table[bai];
    dula = 0;
    P0 = 0xff;
    wela = 1;
    //为了节省IO端口,ADC0804的片选端CS连接U2锁存器的Q7输出端
    //即位选时,P0的最高位需要为0   
    P0 = 0x7e;      
    wela = 0;
    delayms(5);
 
    dula = 1;
    P0 = table[shi];
    dula = 0;
    P0 = 0xff;
    wela = 1;
    P0 = 0x7d;
    wela = 0;
    delayms(5);
 
    dula = 1;
    P0 = table[ge];
    dula = 0;
    P0 = 0xff;
    wela = 1;
    P0 = 0x7b;
    wela = 0;
    delayms(5);
}
 
uchar get_ad()
{
    uchar adval;
 
    adwr = 1;
    _nop_();
    adwr = 0;    //启动AD转换
    _nop_();
    adwr = 1;
    P1 = 0xff;
 
    adrd = 1;
    _nop_();
    adrd = 0;      //AD读使能
    _nop_();
    adval = P1;   //AD数据读赋给P1
    adrd = 1;
 
    return adval;
}
 
void timer0() interrupt 1
{
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    t0_num++;
    if (t0_num == 20)
    {
        t0_num = 0;
        flag_time = 1;  //flag_time置1,便于主程序检测是否到1s
    }
}
 
void ser() interrupt 4
{
    //RI为接收中断标志位, 在方式0时, 当串行接收第8位数据结束时, 或在其他方式, 串行接收停止位的
    //中间时, 由内部硬件使RI置1, 向CPU发出中断申请, 也必须在中断服务程序中, 用软件将其清0,取消
    //此中断申请, 以方便下一次中断申请检测, 即这样才能产生下一次中断.
    //这里RI清0, 因为程序既然产生了串口中断, 肯定是收到或发送了数据, 在开始时没有发送任何数据
    //那必然是收到了数据, 此时RI会被硬件置1, 所以进入串口中断服务程序后必须由软件清0, 这样才能
    //产生下一次中断.
    RI = 0;
    //将SBUF中的数据读走给a, 这是此中断服务程序最重要的目的
    a = SBUF;
    //将串口中断标志位设置为1,便于主程序检测
    flag_uart = 1;
    if (a == 1)
        flag_on = 0;
    else if (a == 2)
        flag_on = 1;
    else
        flag_on = 2;
}
 
2)法2
#include
#include
 
//中断允许寄存器IE,字节地址位0xA8
//单片机复位时, IE中所有位被清0
sfr IE    = 0xA8;        
 
//EA为全局中断允许位
//EA = 1时打开全局中断控制,在这样条件下,由各个中断控制位打开或关闭相应的中断
//EA = 0时关闭所有中断
sbit EA    = IE^7;       
 
//ET2为定时器/计数器2中断允许位
//ET2 = 1时打开T2中断, ET2 = 0时关闭T2中断
sbit ET2   = IE^5; //8052 only
 
//ES为串行口中断允许位
//ES = 1时打开串行口中断, ES = 0时关闭串行口中断
sbit ES    = IE^4;
 
//ET1为定时器/计数器1中断允许位
//ET1 = 1时打开T1中断, ET1 = 0时关闭T1中断
sbit ET1   = IE^3;
 
//EX1为外部中断1中断允许位
//EX1 = 1时打开外部中断1中断, EX1 = 0时关闭外部中断1中断
sbit EX1   = IE^2;
 
//ET0为定时器/计数器0中断允许位
//ET0 = 1时打开T0中断, ET0 = 1时关闭T0中断
sbit ET0   = IE^1;
 
//EX0为外部中断0中断允许位
//EX0 = 1时打开外部中断0中断, EX0 = 0时关闭外部中断0中断
sbit EX0   = IE^0;
 
//SBUF为串行数据缓冲寄存器
//51单片机中含有两个SBUF,其中一个为发送缓冲寄存器,另一个为接收缓冲寄存器
//这两个寄存器共有一个地址0x99, 但物理上是两个独立的寄存器,有指令操作决定访问哪个寄存器
//执行写指令时, 访问串行发送寄存器, 执行读指令时, 访问串行接收寄存器
//接收器具有双缓冲结构, 即在从接收寄存器中读出前一个已收到的字节之前, 便能接受第二个字节
//如果第二个字节已经接收完毕,第一个字节还没有读出,则丢失其中一个字节
//对于发送器,数据由CPU控制和发送,所以不需要考虑
sfr SBUF  = 0x99;
 
//SCON为串行口控制寄存器
//SCON可位寻址, 即可以访问它的具体某一位
//SCON用以设定串行口的工作方式, 接收/发送控制以及设置状态标志
//单片机复位时SCON全部被清0
sfr SCON  = 0x98;
 
//SM0,SM1为工作方式选择位, 串行口有4中工作方式, 由SM0,SM1设定
//SM0=0,SM1=0为方式0,即同步移位寄存器方式,用于扩展I/O口
//SM0=0,SM1=1为方式1,即10位异步收发,含8位数据,波特率可变,且由定时器1的溢出率控制
//SM0=1,SM1=0为方式2,即11位异步收发,含9位数据,波特率固定
//SM0=1,SM1=1为方式3,即11位异步收发,含9位数据,波特率可变,且由定时器1的溢出率控制
sbit SM0   = SCON^7;
sbit SM1   = SCON^6;
 
//SM2为多机通信控制位,主要用于方式2和方式3
//当接收机的SM2=1时,可以利用收到的RB8来控制是否激活RI,即RB8=0时不激活RI,收到的信息丢弃
//RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走
//当SM0=0时,不论收到的RB8是0还是1,均可以使收到的数据进入SBUF,并激活RI,即此时RB8不具有控制RI激活功能
//通过控制SM2,可以实现多机通信.
//在方式0时,SM2必须是0
//在方式1时,若SM2=1,则只有接收到有效停止位时,RI才置1
sbit SM2   = SCON^5;
 
//REN为允许串行接收位
//REN=1时允许串行口接收数据
//REN=0时禁止串行口接收数据
sbit REN   = SCON^4;
 
//TB8为方式2,3中发送数据的第9位
//方式2或方式3中,时发送数据的第9位,可以用软件规定其作用,可以用作数据的奇偶校验位
//或在多机通信中,作为地址帧/数据帧的标志位
//方式0和方式1时,该位未用
sbit TB8   = SCON^3;
 
//RB8为方式2,3中接收数据的第9位
//方式2或方式3中,是接收数据的第9位,可作为奇偶校验位或地址帧/数据帧的标志位
//方式1时,若SM2=0,则RB8是接收到的停止位
sbit RB8   = SCON^2;
 
//TI为发送中断标志位
//方式0时,当串行发送第8位数据结束时,或在其他方式,串行发送停止位的开始时,
//由内部硬件使TI置1,向CPU发出中断申请,在中断服务程序中,必须用软件将其清0,取消此中断申请
sbit TI    = SCON^1;
 
//RI为接收中断标志位
//方式0时,当串行接收第8位数据结束时,或在其他方式,串行接收停止位的中间时,
//由内部邮件使RI置1,向CPU发出中断申请,也必须在中断服务程序中,用软件将其清0,取消此中断申请
sbit RI    = SCON^0;
 
//TMOD为定时器/计数器工作方式寄存器
//字节地址位0x89,不能位寻址
//单片机复位时TMOD全部被清0
//TMOD的高4位用于设置定时器1,低4位用于设置定时器0,
//其中高低4位均由GATE,C/T,M1,M0构成
//GATE为门控制位
//GATE=0,定时器/计数器启动与停止仅受TCON寄存器中TRX(X=0,1)来控制
//GATE=1,定时器/计数器启动与停止由TCON寄存器中TRX(X=0,1)和外部中断引脚(INT0或INT1)的电平共同控制
//C/T为定时器模式和计数器模式选择位
//C/T=1为计数器模式,C/T=0为定时器模式
//M1M0为工作方式选择位
//M1=0,M0=0为方式0,为13位定时器/计数器
//M1=0,M0=1为方式1,为16位定时器/计数器
//M1=1,M0=0为方式2,8位出值自动重装的8位定时器/计数器
//M1=1,M1=0为方式3,仅适用于T0,分成两个8位计数器,T1停止计数
sfr TMOD  = 0x89;
 
//TCON为定时器/计数器控制寄存器
//TCON字节地址为88H,可位寻址
//TCON寄存器用来控制定时器的启,停,标志定时器溢出和中断
//单片机复位时TCON全部被清0
//TCON包含的TF1,TR1,TF0,TR0用于定时器/计数器
//TCON包含的IE1,IT1,IE0,IT0用于外部中断
sfr TCON  = 0x88;
 
//TF1为定时器1溢出标志位
//当计数器1计满溢出时,由硬件使TF1置1,并且申请中断,进入中断服务程序后,由硬件自动清0
//如果使用定时器的中断,那么该位完全不用人为去操作
//如果使用软件查询的方式,查询该位为1后,就需要用软件清0
sbit TF1   = TCON^7;
 
//TR1为定时器1运行控制位
//由软件清0关闭定时器1,当GATE=1,且INT1为高电平时,TR1置1启动定时器1
//当GATE=0时,TR1置1启动定时器1
sbit TR1   = TCON^6;
 
//TF0为定时器0溢出标志,功能及操作方法同TF1
sbit TF0   = TCON^5;
 
//TR0为定时器0运行控制位,其功能及操作方式同TR1
sbit TR0   = TCON^4;
 
//IE1为外部中断1请求标志
//IT1=0时,为电平触发方式,每个机器周期的S5P2采样INT1引脚,
//若INT1脚为低电平,则置1,否则IE1清0
//IT1=1时,INT1位跳变沿触发方式,当第一个机器周期采样到INT1为低电平时,则IE1置1
//IE1=1时,表示外部中断1正在向CPU申请中断,当CPU响应中断,转向中断服务程序,该位由硬件清0
sbit IE1   = TCON^3;
 
//IT1为外部中断1触发方式选择位
//IT1=0,电平触发方式,引脚INT1上低电平有效
//IT1=1,跳变沿触发方式,引脚INT1上的电平从高到低的负跳变有效
sbit IT1   = TCON^2;
 
//IE0为外部中断0请求标志,功能及操作同IE1
sbit IE0   = TCON^1;
 
//IT0为外部中断0触发方式选择位,功能及操作同IT1
sbit IT0   = TCON^0;
 
//定时器1初值高8位
sfr TH1   = 0x8D;
 
//定时器1初值低8位
sfr TL1   = 0x8B;
 
//定时器0初值高8位
sfr TH0   = 0x8C;
 
//定时器0初值低8位
sfr TL0   = 0x8A;
 
//声明单片机P0口的地址
sfr P0    = 0x80;
 
//声明单片机P1口的地址
sfr P1    = 0x90;
 
//声明单片机P2口的地址
sfr P2    = 0xA0;
 
//声明单片机P3口的地址
sfr P3    = 0xB0;
 
#define uchar unsigned char
#define uint unsigned int
 
sbit dula = P2 ^ 6; //申请U1锁存器的锁存端
sbit wela = P2 ^ 7; //申请U1锁存器的锁存端
sbit adwr = P3 ^ 6; //定义AD的WR端口
sbit adrd = P3 ^ 7; //定义AD的RD端口
 
uchar flag, flag_uart, flag_time, flag_on;
uchar a, i, t0_num, ad_val;
 
float ad_vo;
 
//数码管的编码数组
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
 
void delayms(uint xms);
void init();
void display(uchar value);
uchar get_ad();
 
void main()
{
    //初始化
    init();
    //为了节省IO端口,ADC0804的片选端CS连接U2锁存器的Q7输出端
    //即位选时,P0的最高位   
    //于是通过位选时对P0赋值置CSAD为0,选通后,以后不必在管
    wela = 1;
    P0 = 0x7f;
    wela = 0;
    while (1)
    {
        //若检测到flag_uart为1,说明串口已经执行过串口中断服务程序, 即收到了数据
        if (flag_uart == 1)
        {
            //手动将flag_uart清0,方便标志位检测
            flag_uart = 0;
            //检测到收到数据后,先将ES清0,原因是接下来要发送数据,若不关闭串口,
            //发送完数据后,单片机会重新申请串口中断flag_uart又为1,又再次发送,一直反复
            ES = 0;
            //由于下面switch中需要调用puts,printf,在单片机的puts,printf中需要对TI是否为1
            //进行检测,就是需要等待TI为1才会将字符发送出去,否则一直等待下去
            //所以这里需要先将TI置为1
            TI = 1;
            //根据执行串口中断后获取的flag_on的值执行相应的动作
            switch(flag_on)
            {
                case 0:
                    puts("Turn on ad!\n");
                    TR0 = 1; //开启计数器0
                    break;
                case 1:
                    printf("Turn off ad!\n");
                    TR0 = 0;   //关闭计数器0
                    break;
                case 2:
                    puts("Error!\n");
                    break;
            }
            //等待发送完毕,因为发送完毕后TI会由硬件清0
            while (!TI);
            //手动TI清0
            TI = 0;
            //重新开启串口中断
            ES = 1;
        }
 
        //通过flag_time判断时间是否到1s, 从而去获取AD转换的值
        if (flag_time == 1)
        {
            //手动将flag_time清0,方便下次检测
            flag_time = 0;
            //获取AD转换的值
            ad_val = get_ad();
            //获取浮点数表示的AD实际采集到的电压值
            ad_vo = (float)ad_val*5.0/256.0;
            //关闭串口中断
            ES = 0;
            //下面要调用printf,所以将TI置1, 原因见上面
            TI = 1;
            printf("The voltage is %fV\n", ad_vo);
            //检测发送是否完成
            while (!TI);
            //手动TI清0
            TI = 0;   
            //开启串口中断
            ES = 1;
        }
 
        //数码管上显示AD转化的值
        display(ad_val);
    }
}
 
void delayms(uint xms)
{
    uint i, j;
 
    for (i = xms; i > 0; i--)
        for (j = 110; j > 0; j--)
            ;
}
 
void init()
{
    //设定T1定时器工作方式2, T0定时器工作方式1
    TMOD = 0x21;
    //为T0定时器装入初值
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    //为T1定时器装入初值
    TH1 = 0xfd;
    TL1 = 0xfd;
    //ET1 = 1;  这里不需要开启定时器1中断,因为定时器1工作在方式2,为8位自动重装方式,进入中断也无事可做
    //启动T1定时器
    TR1 = 1;
    //开启定时器0中断
    ET0 = 1;
    //启动定时器0
    //TR0 = 1; TR0的初始化放在主函数的while中,方便检测到串口发送数据后的1s延时,即延时1s从串口发送完数据开始
    //设定串口工作方式
    //11位异步收发,含9位数据,波特率可变,且由定时器1溢出率控制
    SM0 = 1;
    SM1 = 1;
    //容许串口中断
    REN = 1;
    //开启总中断
    EA = 1;
    //开启串口中断
    ES = 1;
}
 
void display(uchar num)
{
    uchar bai, shi, ge;
 
    bai = num / 100;
    shi = num % 100 / 10;
    ge = num % 10;
 
    dula = 1;
    P0 = table[bai];
    dula = 0;
    P0 = 0xff;
    wela = 1;
    //为了节省IO端口,ADC0804的片选端CS连接U2锁存器的Q7输出端
    //即位选时,P0的最高位需要为0   
    P0 = 0x7e;      
    wela = 0;
    delayms(5);
 
    dula = 1;
    P0 = table[shi];
    dula = 0;
    P0 = 0xff;
    wela = 1;
    P0 = 0x7d;
    wela = 0;
    delayms(5);
 
    dula = 1;
    P0 = table[ge];
    dula = 0;
    P0 = 0xff;
    wela = 1;
    P0 = 0x7b;
    wela = 0;
    delayms(5);
}
 
uchar get_ad()
{
    uchar adval;
 
    //CS在主函数已经为低电平,/WR初始化为高电平,经过至少tw时间后, /WR拉高
    //随后A/D转换器被启动,并且经过1~8个A/D时钟周期和内部Tc时间后,模数转换完成转换
    //转换结果存入数据锁存器,同时INTR(其为AD转换结束信号)自动变成低电平,通知单片机本次转换结束
    adwr = 1;
    _nop_();
    adwr = 0;    //启动AD转换
    _nop_();
    adwr = 1;
 
    //读取P1口之前先给其写全1
    P1 = 0xff;
 
    //INTR为低电平后,将/CS置低,初始化/RD为1,再将/RD置低(主函数已经置低),
    //在/RD至少经过tACC时间后,数字输出口上的数据达到稳定状态,此时直接读取数字输出端口
    //数据便可得到转换后的数字信号,读走数据后,马上将/RD拉高,
    //RD置低tR1时间后,/INTR自动拉高,
    adrd = 1;
    _nop_();
    adrd = 0;      //AD读使能
    _nop_();
    adval = P1;   //AD数据读赋给P1
    adrd = 1;
 
    return adval;
}
 
void timer0() interrupt 1
{
    TH0 = (65536 - 50000) / 256;
    TL0 = (65536 - 50000) % 256;
    t0_num++;
    if (t0_num == 20)
    {
        t0_num = 0;
        flag_time = 1;  //flag_time置1,便于主程序检测是否到1s
    }
}
 
void ser() interrupt 4
{
    //RI为接收中断标志位, 在方式0时, 当串行接收第8位数据结束时, 或在其他方式, 串行接收停止位的
    //中间时, 由内部硬件使RI置1, 向CPU发出中断申请, 也必须在中断服务程序中, 用软件将其清0,取消
    //此中断申请, 以方便下一次中断申请检测, 即这样才能产生下一次中断.
    //这里RI清0, 因为程序既然产生了串口中断, 肯定是收到或发送了数据, 在开始时没有发送任何数据
    //那必然是收到了数据, 此时RI会被硬件置1, 所以进入串口中断服务程序后必须由软件清0, 这样才能
    //产生下一次中断.
    RI = 0;
    //将SBUF中的数据读走给a, 这是此中断服务程序最重要的目的
    a = SBUF;
    //将串口中断标志位设置为1,便于主程序检测
    flag_uart = 1;
    if (a == 1)
        flag_on = 0;
    else if (a == 2)
        flag_on = 1;
    else
        flag_on = 2;
}
 
三. 程序小结
1) 串口初始化中先设定串口模式, 再容许串口接收,即顺序是SM0 = 0;SM1 = 1;REN=1;(这三位都在串口控制寄存器SCON里)
这样做的原因是单片机刚上电时SCON被清0,因为串口方式为方式0,串行口为同步移位寄存器的输入输出方式,当执行完REN为1的
语句后,直接从RXD接收数据,不管连接的系统有无发送数据.把REN放在模式设置的后面可以避免串口模式还未设置下就读取数据.
2) 调用printf()或者puts()前需要手动将TI置1, 因为这两个函数需要对TI进行检测, 以代表是否发送完毕.
3) 调用printf()或者puts()前还需要手动关闭串口中断(ES = 0).如不关闭串口中断,每发送一个字节,程序就会申请加入串口中断,从而导致程序出错.
4) ad_val * 5.0/256.0可以得到浮点数表示的AD实际采集到的电压标准值.
5) 如果要程序接受十六进制的01或者字符格式的1, 那么需要将串口中断子程序中的if(a == 1)改成if(a == 1 || a == '1'),
   02, 03类似.

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
8051单片机的函数发生器的设计