USTC小璇风 2024-03-27 18:18 采纳率: 0%
浏览 5

51单片机SCI通讯

郭天祥51单片机SCI通讯的程序一直跑不通,实在是想不明白问题是出在哪里了。第二段代码是书上一模一样的也跑不通,有碰到或解决了这个问题的同学帮我看看么!


```c
#include<reg52.h>
#include<intrins.h> //用到的机器周期延时函数_nop_()需要用到此库 
#include<stdio.h> //需要用到printf()函数和puts()函数

#define uint unsigned int
#define uchar unsigned char
#define ul unsigned long

sbit dula=P2^6; //声明锁存器U1的锁存端
sbit wela=P2^7; //声明锁存器U2的锁存端
sbit adwr=P3^6; //定义A/D的WR端口
sbit adrd=P3^7; //定义A/D的RD端口

uchar code dula_table[]={ //段选数码库
    0x3f,0x06,0x5b,0x4f, // 0,1,2,3
    0x66,0x6d,0x7d,0x07, //4,5,6,7
    0x7f,0x6f,0x77,0x7c, //8,9,10,11
    0x39,0x5e,0x79,0x71  //12,13,14,15
};

uchar code wela_table[]=  //片选数码库
{
      0xdf/*最低位*/,0xef,0xf7,0x7b,0x7d,0x7e/*最高位*/    //从右向左数第一到六位
      /*特别地,这里最高位为0x7bde而非0xfbde是由于段选公用U2锁存器,而AD的CS端需要一直保持低电平不变*/
};

uchar a, flag_uart, flag_time, flag_on, num_timer0, ad_val, bai, shi, ge;

void main()
{
    void init_AD(); //AD转换器初始化函数
    void init_wedu(); //数码管位选段选初始化函数
    void init_timer0(); //定时器0初始化函数
    void init_timer1(); //定时器1初始化函数
    void init_alltimer(); //所有定时器初始化函数
    void delayxms(uint xms); //延时函数 ms为单位
    void display(uchar num_bai,uchar num_shi,uchar num_ge); //数码管显示函数
    void SCI_mode(); //SCI通讯模式选择
    void SCI_ad(); //SCI-采样


    init_AD(); //AD转换器初始化
    init_wedu(); //数码管位选段选初始化
    init_alltimer(); //所有定时器初始化
    init_timer0(); //定时器0初始化
    init_timer1(); //定时器1初始化
    
    bai=0;
    shi=0;
    ge=0;

    while(1)
    {
        if(flag_uart==1)
        {
            SCI_mode();    
        }
        if(flag_time==1)
        {
            SCI_ad();    
        }
        display(bai,shi,ge);        
    }
}

void init_AD() //AD转换器初始化函数
{
    wela=1;
    P0=0x7f; //置CSAD为0,选通ADCS以后不必再管ADCS
    wela=0;
    delayxms(50);    
}

void init_wedu() //数码管位选段选初始化函数
{
    dula=0; //初始化,段选置0
    wela=0; //初始化,片选置0
}

void init_timer0() //定时器0初始化函数
{
    TH0=(65536-45872)/256; //装初值 50ms一次中断 
    TL0=(65536-45872)%256; //装初值 50ms一次中断
    
    ET0=1; //打开定时器0中断
    num_timer0=0; //定时器0的计数初值置为0    
}

void init_timer1() //定时器1初始化函数
{
    TH1=0xfd; //T1定时器装初值,对应波特率9600
    TL1=0xfd; //T1定时器装初值,对应波特率9600,TH1与TL1相同,工作方式2自动重装
    TR1=1; //启动T1定时器
}

void init_alltimer() //所有定时器初始化函数
{
           TMOD=0x21; //设置定时器1的工作方式为2, 定时器0工作方式为1
    SCON=0x50; //实际上是设置SM0和SM1,设定方式1时SCON实则为01010000,即0x50
    /*等价于位操作:
    SM0=0;
    SM1=1;
    REN=1;
    注意语句顺序!
    */
    EA=1; //打开总中断
    ES=1; //打开串口中断
    flag_on=0;
}

void delayxms(uint xms) //延时函数 ms为单位
{
    uint x,y;
    for(x=xms;x>0;x--)
        for(y=124;y>0;y--);    
}

void SCI_mode() //SCI通讯模式选择
{
    flag_uart=0;
    ES=0;
    TI=1;
    switch(flag_on)
    {
        case 0: puts("Turn on ad!\n");TR0=1;break;
        case 1: puts("Turn off ad!\n");TR0=0;break;
        case 2: puts("Error!\n");break;
        default: break;
    }
    while(!TI) // 等待上次发送完成
    TI=0; // 清除发送完成标志
    ES=1;
}

void SCI_ad() //SCI-采样 
{
    void get_ad();    //AD采样函数

    float ad_vo;


    flag_time=0;
    get_ad();
    ad_vo=(float)ad_val*5.0/256.0;
    ES=0;
    TI=1;
    printf("The voltage is %fV\n",ad_vo);
    while(!TI)
    TI=0;
    ES=1;  
}

void get_ad() //AD采样函数
{
    uchar adval;

    adwr=1;    //关闭A/D转换
    _nop_(); //硬件延时一个机器周期
    adwr=0; //启动A/D转换
    _nop_();
    adwr=1;
    P1=0Xff; //读取P1口前先给其全写1
    adrd=1; //选通ADCS
    _nop_();
    adrd=0; //AD读使能
    _nop_();
    adval=P1; //读取赋予P1口的AD数据
    adrd=1;
    ad_val=adval;
    bai=ad_val/100;//百位
    shi=ad_val%100/10;//十位
    ge=ad_val%10;//个位                 
}

void display(uchar num_bai,uchar num_shi,uchar num_ge) //数码管显示函数 
{
    uint wei; //位选数字

    void wedu(uchar dula_num,uint wela_num); //数码管位选段选函数

    for(wei=3;wei<6;wei++)
    {
        switch(wei)
        {
            case 3: wedu(num_ge,wei);break;
            case 4: wedu(num_shi,wei);break;
            case 5: wedu(num_bai,wei);break;
        }
        dula=1; //打开U1锁存端
        P0=0x00; //防止最高位数码管过亮
        dula=0; //关闭U1锁存端    
    }
}

void wedu(uchar dula_num,uint wela_num) //数码管位选段选函数
{
    void delayxms(uint xms); //延时函数 ms为单位

    wela=1; //打开U2锁存端
    P0=wela_table[wela_num]; //送入U2锁存端
    wela=0; //关闭U2锁存端
    P0=0xc0; //消影,防止P0残留电位信号干扰段选
    dula=1; //打开U1锁存端
    P0=dula_table[dula_num]; //送入段选信号
    dula=0; //关闭U1锁存端
    P0=0xff; //消影,防止P0残留电位信号干扰片选
    delayxms(10);
}

void T0_time() interrupt 1 //中断程序
{
    TH0=(65536-45872)/256; //装初值 50ms一次中断(45872) 
        TL0=(65536-45872)%256; //装初值 50ms一次中断(45872) 
    num_timer0++;
    if(num_timer0==20)
    {
        num_timer0=0;
        flag_time=1;
    }
}

void ser() interrupt 4 //串口中断服务程序
{
    RI=0; 
    a=SBUF;
    flag_uart=1;
    if(a==0x01) flag_on=0;
    else if(a==0x02) flag_on=1;
        else flag_on=2;
}
另外一个是书上原码,也不行。。。
#include <reg52.h>
#include <intrins.h>
#include <stdio.h>

#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("Fuck!\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 = 0;  //
    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_();_nop_();_nop_();_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条回答 默认 最新

  • 专家-皆知 2024-03-27 18:18
    关注

    引自免费微信小程序:皆我百晓生

    根据您提供的代码,我发现在初始化AD转换器、数码管和定时器时存在一些问题。以下是修复后的代码:

    #include <reg52.h>
    #include <intrins.h>
    #include <stdio.h>
    
    #define uchar unsigned char
    #define uint unsigned int
    
    sbit dula = P2^6; //申请U1锁存器的锁存端
    sbit wela = P2^7; //申请U1锁存器的锁存端
    sbit adwr = P3^6; //定义A/D的WR端口
    sbit adrd = P3^7; //定义A/D的RD端口
    
    uchar code dula_table[] = {
        0x3f,0x06,0x5b,0x4f,
        0x66,0x6d,0x7d,0x07,
        0x7f,0x6f,0x77,0x7c,
        0x39,0x5e,0x79,0x71
    };
    
    uchar code wela_table[] = {
        0xdf/*最低位*/,0xef,0xf7,0x7b,0x7d,0x7e/*最高位*/    //从右向左数第一到六位
        /*特别地,这里最高位为0x7bde而非0xfbde是由于段选公用U2锁存器,而AD的CS端需要一直保持低电平不变*/
    };
    
    uchar a, flag_uart, flag_time, flag_on;
    uchar code ad_val, bai, shi, ge;
    float ad_vo;
    
    //数码管的编码数组
    uchar code table[] = {
        0x3f,0x06,0x5b,0x4f,
        0x66,0x6d,0x7d,0x07,
        0x7f,0x6f,0x77,0x7c,
        0x39,0x5e,0x79,0x71
    };
    
    void delayxms(uint xms); //延时函数 ms为单位
    void init();
    void display(uchar num_bai,uchar num_shi,uchar num_ge); //数码管显示函数
    void SCI_mode(); //SCI通讯模式选择
    void SCI_ad(); //SCI-采样
    void get_ad(); //AD采样函数
    
    void main()
    {
        void init_AD(); //AD转换器初始化
        void init_wedu(); //数码管位选段选初始化
        void init_timer0(); //定时器0初始化
        void init_timer1(); //定时器1初始化
        void init_alltimer(); //所有定时器初始化
        void delayxms(uint xms); //延时函数 ms为单位
        void display(uchar num); //数码管显示函数
        void SCI_mode(); //SCI通讯模式选择
        void SCI_ad(); //SCI-采样
        void get_ad(); //AD采样函数
    
        init();
        while (1)
        {
            if (flag_uart == 1)
            {
                SCI_mode();    
            }
            if (flag_time == 1)
            {
                SCI_ad();
            }
            display(bai,shi,ge);        
        }
    }
    
    void init_AD() //AD转换器初始化
    {
        wela = 1;
        P0 = 0x7f; //设置CSAD为0,选通ADCS以后不必再管
        wela = 0; //关闭U1锁存器
        delayxms(50);
        dula = 1;
        P0 = dula_table[0]; //将P0口设置为第一个数码管的段选信号
        dula = 0;
        P0 = 0xff; //关闭U1锁存器
        wela = 1;
        P0 = dula_table[1]; //将P0口设置为第二个数码管的段选信号
        wela = 0;
        delayxms(50);
        dula = 1;
        P0 = dula_table[2]; //将P0口设置为第三个数码管的段选信号
        wela = 0;
        delayxms(50);
        dula = 1;
        P0 = dula_table[3]; //将P0口设置为第四个数码管的段选信号
        wela = 0;
    }
    
    void init_wedu() //数码管位选段选初始化
    {
        dula = 0;
        wela = 0;
        P0 = 0xff; //初始化P0口为最高位
        P0 = wela_table[0]; //将P0口设置为第一个数码管的选中状态
        P0 = wela_table[1]; //将P0口设置为第二个数码管的选中状态
        P0 = wela_table[2]; //将P0口设置为第三个数码管的选中状态
        P0 = wela_table[3]; //将P0口设置为第四个数码管的选中状态
    }
    
    void init_timer0() //定时器0初始化
    {
        TH0=(65536-45872)/256; //装初值 50ms一次中断
        TL0=(65536 - 45872)%256; //装初值 50ms一次中断
        ET0=1; //启动定时器0中断
        num_timer0=0; //定时器0的计数初值置为0
    }
    
    void init_timer1() //定时器1初始化
    {
        TH1=0xfd; //装初值 50ms一次中断
        TL1=0xfd; //装初值 50ms一次中断
        TR1=1; //启动定时器1中断
    }
    
    void init_alltimer() //所有定时器初始化
    {
        TMOD=0x21;
        TH0=(65536-45872)/256; //装初值 50ms
    
    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 3月27日

悬赏问题

  • ¥15 Android Navigation: 某XDirections类不能自动生成
  • ¥20 C#上传XML格式数据
  • ¥15 elementui上传结合oss接口断点续传,现在只差停止上传和继续上传,各大精英看下
  • ¥100 单片机hardfaulr
  • ¥20 手机截图相片分辨率降低一半
  • ¥50 求一段sql语句,遇到小难题了,可以50米解决
  • ¥15 速求,对多种商品的购买力优化问题(用遗传算法、枚举法、粒子群算法、模拟退火算法等方法求解)
  • ¥100 速求!商品购买力最优化问题(用遗传算法求解,给出python代码)
  • ¥15 虚拟机检测,可以是封装好的DLL,可付费
  • ¥15 kafka无法正常启动(只启动了一瞬间会然后挂了)