乐高 发表于 2025-1-28 21:01:11

第十五集 定时器做计数器


void INT1_Count_Init(void)
{
        IT1 =1;
EX1 = 1;       
}
void INT1_Isr() interrupt 2
{
        Count1 = Count_T1;
        if(Count1>5)
        {
                Count = Count1/10;
          Count_INT1++;
        }       
       Count_T1 = 0;       
}       
void Timer1_Isr(void) interrupt 3
{
        Count_T1++;
}

void Timer1_Init(void)                //10毫秒@24.000MHz
{
        AUXR &= 0xBF;                        //定时器时钟12T模式
        TMOD &= 0x0F;                        //设置定时器模式
        TL1 = 0xE0;                                //设置定时初始值
        TH1 = 0xB1;                                //设置定时初始值
        TF1 = 0;                                  //清除TF1标志
        TR1 = 1;                                  //定时器1开始计时
        ET1 = 1;                                  //使能定时器1中断
}579

乐高 发表于 2025-1-30 13:22:15

第十六集 DS18B20测温课后小任务
把温度传感器的数值显示改成两位数,加一小数点,然后小数点后再显示一位。

先在程序中加入转换精度可预设的代码:
void DS18B20_Init(void)
{
                        DS18B20_Reset();                  //1.发送复位命令
                        DS18B20_WriteByte(0xcc);      //2.跳过ROM命令
                        DS18B20_WriteByte(0x4e);       //3.写暂存器指令4E
                        DS18B20_WriteByte(0x4b);      //4.写TH最高限值温度
                        DS18B20_WriteByte(0x00);      //5.写TL最低限值温度
                        DS18B20_WriteByte(0x7f);      //写配置寄存器4
                                                //0x1f : 0.5000°C转换时间93.75ms
                                                //0x3f : 0.2500°C转换时间187.5ms
                                                //0x5f : 0.1250°C转换时间375ms
                                                //0x7f : 0.0625°C转换时间750ms
}      Temp_18b20 = Temp_18b20/1000;      //除1000,再在十位数之后加上小数点,则显示出需要的数值。
u8 SEG_NUM1[]=                              //带小数点
{
    0xBF,       /*'0', 0*/
    0x86,       /*'1', 1*/
    0xDB,       /*'2', 2*/
    0xCF,       /*'3', 3*/
    0xE6,       /*'4', 4*/
    0xED,       /*'5', 5*/
    0xFD,       /*'6', 6*/
    0x87,       /*'7', 7*/
    0xFF,       /*'8', 8*/
    0xEF,       /*'9', 9*/
    0xF7,       /*'A', 10*/
    0xFC,       /*'B', 11*/
    0xB9,       /*'C', 12*/
    0xDE,       /*'D', 13*/
    0xF9,       /*'E', 14*/
    0xF1,       /*'F', 15*/
    0x40,       /*'-', 16*/
    0x00,       /*' ', 17*/
    0x80,       /*'.', 18*/
};只显示三位数字
void SEG_Show_U32(u32 num)
{
      u8 i;
      for(i=0;i<3;i++)                      //只显示三位数字
      {
                passward = num%10;
                num /= 10;
      }
}



乐高 发表于 2025-1-30 18:34:54

第十六集 DS18B20测温课后小练


鉴于实验箱没有真正的电源开关机按键,所以采用右侧的某个键盘象征性的作为开关机键使用,但不能真正切断电源。
由于四个独立按键P32\P33\P34\P35都已经被占用,所以考虑用矩阵键盘来实现此课后小练的功能。
此处采用按键1(P01)作为开关机键,按键3(P03)作为摄氏华氏温度显示方式切换键。
下面是main主函数

while(1)
        {
//               
//    if (bUsbOutReady)                                                                                                        //如果接收到了数据
//      {
//            //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
//                       
//            usb_OUT_done();                                                        //
//      }
                Task_Pro_Handler_Callback();                                //执行功能函数

                if(C_F == 0)
                                SEG_Show_U32(Temp_18b20);                                //如果C_F=0,则显示摄氏温度
                else
                          SEG_Show_U32(Temp_F);                                                //如果C_F=1,则显示华氏温度
   if(P01==0)                                                                                                                //按键1用作开关机键
       {
                        Delay20ms();
                  if(P01==0)
                        {
                                run = !run;
                                Key_num=0;
                        }
                        while(P01==0);
       }
        }
}
void Key_Task(void)      
{      
      Key_num++;
      if(P03==0)                                                                              //按键3用作摄氏/华氏显示切换键
                {      
                        Key_Vol++;
                        Key_num = 0;
                        if(Key_Vol==5)               
                        {
                              C_F = !C_F;
                        }
                }
      else
                Key_Vol = 0;
      if(Key_num==3000)                                                 //30秒无操作,关机
                run = 0;
}void SEG_Task(void)
{
      u8 num = 0;
      
      if(run==0)                     //未按P01键,熄屏,代表未开机
      {
                        tim =0;
      SEG_Show0();                                                               //未开机,熄屏
}
      else
      {
                tim++;
                if(tim>2000)                                                //2秒钟后
                {
                        if(C_F)
                              SEG_Show4();                                                      //如果C_F=1,则显示华氏温度
                        else
                              SEG_Show3();                                                      //如果C_F=0,则显示摄氏温度
                        tim = 2000;
   }
                else                                                                                                      //2秒内
                {
                        if(C_F)
                              SEG_Show2();                                                      //如果C_F=1,显示”---F"
                        else
                              SEG_Show1();                                                      //如果C_F=0,显示”---C“
          }                        
      }
      Seg_no ++;
      if( Seg_no>7 )
                Seg_no=0;
}583



乐高 发表于 2025-1-31 19:06:21

第十六集 DS18B20测温数码管闪烁问题


数码管闪烁是由于18B20的读写中采用了阻塞延时程序(软件延时)影响了主程序的运行,要采用非阻塞程序才能使数码管不闪烁。
非阻塞写法有多种,我现在找到了一种:定时器中断方式。用定时器中断来实现精确延时。
本程序中使用定时器1,设置定时长度为60us, 480us就是8个定时周期,420us正好是7个定时周期。
18B20全部的读写过程都在定时中断中实现
void Timer1_Isr(void) interrupt 3         //定时时间60us,就是60us走一步
{
      switch(t1_num)
                {
                case 0 :
                case 33 :
                bz = 1; DQ = 0; break;
                case 8 :                                                                                                                        //0-8步时间间隔480us
                case 41 :
                DQ = 1; break;
                case 9 :                                                                                                                        //8-9步时间间隔60us
                case 42 :
                bz = DQ; break;
                case 16 :                            //9-16步时间间隔420us
                case 49 :
                if(bz)                                                                                                                         //如果bz为1,则初始化失败,关闭定时器1,等待下次采集。
                {
                        TR1 = 0;
                        break;}
                else                                                                                                                                 //如果bz为0,则开始写入命令cmd
                {
                  dat = cmd;
                        DQ = 0;
                        Delay1us();
                  dat >>= 1;
                        DQ = CY;
                        break;
                }
                case 17 : //cmd
                case 18 :
                case 19 :
                case 20 :
                case 21 :
                case 22 :
                case 23 :

                case 25 : //cmd
                case 26 :
                case 27 :
                case 28 :
                case 29 :
                case 30 :
                case 31 :

                case 50 : //cmd
                case 51 :
                case 52 :
                case 53 :
                case 54 :
                case 55 :
                case 56 :

                case 58 : //cmd
                case 59 :
                case 60 :
                case 61 :
                case 62 :
                case 63 :
                case 64 :
               DQ = 1;
               Delay1us();
               DQ = 0;
               Delay1us();                                          //开始下一位
               dat >>= 1;
               DQ = CY;
                break;
                case 24 :
                case 57 :
                DQ = 1;
                Delay1us();                                           //写第一个字节结束
                dat = cmd;      
                DQ = 0;
                Delay1us();                                           //写第二个字节
                dat >>= 1;
                DQ = CY;
                break;
                case 32 :
                DQ = 1;
                Delay1us();                                           //写第二个字节结束 0x44
                case 65 :
                DQ = 1;
                Delay1us();                                    //0xbe 命令发完
                dat = 0;                        
                dat >>= 1;
                DQ = 0;
                Delay1us();
                DQ = 1;
                Delay1us();
                if(DQ) dat |= 0x80;
                break;
                case 66 :
                case 67 :
                case 68 :
                case 69 :
                case 70 :
                case 71 :
                case 72 :

                case 74 :
                case 75 :
                case 76 :
                case 77 :
                case 78 :
                case 79 :
                case 80 :
                dat >>= 1;
                DQ = 0;
                Delay1us();
                DQ = 1;
                Delay1us();
                if(DQ) dat |= 0x80;
                break;
                case 73 :
                TempL = dat;
                dat = 0;         
                dat >>= 1;
                DQ = 0;
                Delay1us();
                DQ = 1;
                Delay1us();
                if(DQ) dat |= 0x80;
                break;
                case 81 :
                TempH = dat;
                TR1 = 0;
                break;
                default : break;
                }      
      t1_num++;
}void Timer1_Init(void)                //60微秒@24.000MHz
{
      AUXR &= 0xBF;                        //定时器时钟12T模式
      TMOD &= 0x0F;                        //设置定时器模式
      TL1 = 0x88;                              //设置定时初始值
      TH1 = 0xFF;                              //设置定时初始值
      TF1 = 0;                              //清除TF1标志
      TR1 = 1;                              //定时器1开始计时
      ET1 = 1;                              //使能定时器1中断
}温度值计算放在main函数内,因为怕60us时间不够用。
while(1)
      {
//               
//    if (bUsbOutReady)                                                                                                      //如果接收到了数据
//      {
//            //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
//                        
//            usb_OUT_done();                                                      //
//      }
                Task_Pro_Handler_Callback();                              //执行功能函数
      if( TempH & 0x80 )                        //如果最高位是1,这个就是负数
      {
                MinusFlag = 1;
                Temp = (((u16)TempH << 8) | ((u16)TempL << 0));
                Temp = (~Temp) +1;
                Temp_18b20 = (u32)Temp*625;
      }
      else
      {
                MinusFlag = 0;
                Temp = (((u16)TempH << 8) | ((u16)TempL << 0));
                Temp_18b20 = (u32)Temp*625;
                Temp_18b20 = Temp_18b20/1000;      //除1000,再在十位数之后加上小数点,则显示出需要的数值。
                Temp_F = Temp_18b20*9/5 + 320;                         //转换成华氏温度值
      }      
                if(C_F == 0)
                              SEG_Show_U32(Temp_18b20);                              //如果C_F=0,则显示摄氏温度
                else
                        SEG_Show_U32(Temp_F);                                                //如果C_F=1,则显示华氏温度
   if(P01==0)                                                                                                                //按键1用作开机键
         {
                        Delay20ms();
                  if(P01==0)
                        {
                              run = !run;
                              Key_num=0;
                        }
                        while(P01==0);
         }
      }
}u8 t1_num = 0;
u8 cmd_n = 0;
void Key_Task(void)       
{       
        Key_num++;

                if(Key_num%50==0)         //本处设置温度采集周期,500ms采集一次温度
                {
                        //num = 0;
                        t1_num = 0; cmd_n = 0; //nodevice = 0;
                        TR1 = 1;
                }
        if(P03==0)                                                                                //按键3用作摄氏/华氏显示切换键
                {       
                        Key_Vol++;
                        Key_num = 0;
                        if(Key_Vol==5)               
                        {
                                C_F = !C_F;
                        }
                }
        else
                Key_Vol = 0;
        if(Key_num==3000)                                               //30秒无操作,关机
                run = 0;
}
其他的程序跟前面课后小练中的一样,DS18B20_ReadTemp函数不用了。
585

silas3082 发表于 2025-2-1 16:00:49

<p>楼主没考虑用掉电模式PD=1来实现关机吗?</p>

乐高 发表于 2025-2-2 13:56:19

silas3082 发表于 2025-2-1 16:00
楼主没考虑用掉电模式PD=1来实现关机吗?

谢谢同学的提醒,我还没有用过MCU的掉电功能呢,这次尝试一下。

乐高 发表于 2025-2-2 15:06:29

第十六集 DS18B20测温    掉电及掉电唤醒
主程序main函数while(1)循环前加入掉电及掉电唤醒功能
P0IM0 = 0x00;                                    //下降沿中断
      P0IM1 = 0x00;
//PINIPH=0x01;                  //设置P0口GPIO中断为最高优先级
//PINIPL=0x01;
      P0INTE = 0x02;                                                                //使能P01中断功能
      P0WKUE = 0x02;                                                                //使能P01中断掉电唤醒功能
EA = 1;                                                                                                //IE |= 0X80;      
while (DeviceState != DEVSTATE_CONFIGURED);   //等待USB完成配置
//P40 = 0;
      P06 = 0;                      //给P01,P03提供低电平
      _nop_();
      _nop_();
      
      PD = 1 ;                                                                                        //MCU进入掉电模式      ,      PCON = 0x02;                              
      _nop_();                      //掉电模式被唤醒后,MCU首先会执行此语句
      _nop_();                      //然后再进入中断服务程序
      _nop_();
      _nop_();
   
      while(1)void common_irs() interrupt 37                                        //使用GPIO,P01口中断
{
      u8 intf;                        
      intf = P0INTF;
      P0INTF = 0x00;                                                               //清空中断标志位,以正常保证中断请求
      if(intf & 0x02)                           
      {
                run = !run;
      }
}void SEG_Task(void)
{      
      if(run==0)                     //未按P01键,熄屏,代表未开机
      {
                        tim =0;
      SEG_Show0();                                                               //未开机,熄屏
                        PD = 1;                                                                                       //熄屏以后再掉电,不然数码管还有残余显示
                        Key_num = 0;
}
      else
      {
                PD = 0;                     
                tim++;
                if(tim>2000)                                                //2秒钟后
                {
                        if(C_F)
                              SEG_Show4();                                                      //如果C_F=1,则显示华氏温度
                        else
                              SEG_Show3();                                                      //如果C_F=0,则显示摄氏温度
                        tim = 2000;
   }
                else                                                                                                      //2秒内
                {
                        if(C_F)
                              SEG_Show2();                                                      //如果C_F=1,显示”---F"
                        else
                              SEG_Show1();                                                      //如果C_F=0,显示”---C“
          }                        
      }
      Seg_no ++;
      if( Seg_no>7 )
                Seg_no=0;
}


silas3082 发表于 2025-2-4 12:38:56

<p>https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=12763&amp;extra=&amp;page=4</p>
<p>感谢提供的思路,我也完成作业了</p>

香河英茂工作室 发表于 2025-2-26 09:37:18

感谢分享,期待15、16集作业后续。

香河英茂工作室 发表于 2025-2-26 09:37:26

感谢分享,期待15、16集作业后续。
页: 1 2 [3] 4 5 6
查看完整版本: AI8051U教学视频课后小练 | 高质量打卡