找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3298|回复: 1
收起左侧

4位数码管时钟单片机程序+PCB文件可直接打样

[复制链接]
ID:608007 发表于 2020-5-11 10:21 | 显示全部楼层 |阅读模式
求高手改时钟显示闪烁程序 和DS3231低温度程序   程序是在论坛里下载的 还想在修改哈  望高手帮帮忙
Altium Designer画的原理图和PCB图如下:(51hei附件中可下载工程文件)
F4)1PLOAPT)DBM(0YA`TL8Y.png %7ZE08@NKIUK]YGKFP_D7.png VHIS[RJNZ@D%T6DLPV)VC}N.png

单片机源程序如下:
  1. #include <reg52.h>
  2. #include<intrins.h>

  3. #define uchar unsigned char
  4. #define uint  unsigned int


  5. sbit SDA=P2^7;     //数据传送位SDA        
  6. sbit SCL=P2^6;     //时钟控制位SCL
  7. sbit INT=P3^3;     //中断位
  8. sbit RESET=P3^2;   //复位
  9. sbit smg4=P1^1;    //位码1声明 分个位
  10. sbit smg3=P1^3;    //位码2声明 分十位
  11. sbit smg2=P1^5;    //位码3声明 时个位
  12. sbit smg1=P1^7;    //位码4声明 时十位
  13. sbit k1=P1^4;      //按键k1的声明
  14. sbit k2=P1^6;      //按键k2的声明
  15. sbit k3=P3^7;      //按键k3的声明
  16. sbit bell=P3^5;    //蜂鸣器声明
  17. sbit gm=P2^1;      //测光声明,黑暗时等于1

  18. bit gm_en=0;       //光敏使能信号;
  19. bit  ack;          //应答标志位

  20. char code dis_code[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0X88,0X83,0XC6,0XA1,0X86,0X8E};//数码管没有按a-h的顺序连接,自定义的显示字库,0-9,A-F
  21. //0X28,0XEE,0X32,0XA2,0XE4,0XA1,0X21,0XEA,0X20,0XA0,0X60,0X25,0X37,0X26,0X31,0X71
  22. uchar bell_en=0,x=1;//闹钟使能信号,任意键关闭铃声标志位
  23. uchar tabtime[]={0x00,0x00,0x07,0x02,0x23,0x10,0x18};                //首次上电时默认的时间2018年 星期二 10月23日 07:00:00
  24. uchar year,month,day,week,hour,minute,second,temperature;             //从1302读出的实时时间数据
  25. uchar gm_time=60,temp_en=0,n=0,a=0,b=0;       //gm_time光敏控制亮度值,n是按键标志位,a产生一个1秒的计时,b控制字符闪烁,temp_en温度显示标志

  26. uint  t=0;      //用来产生30秒的计时
  27. uint  TtempH,TtempL;    //定义温度高位,低位

  28. char shi,fen,miao,b_shi=7,b_fen=0; //必须使用符号型:数据暂存单元shi/fen/miao,闹铃时间b_shi,b_fen,

  29. void baojing();               //报警函数
  30. void delay_50us(uint t);      //延时50*T微妙函数的声明
  31. void Start_I2C();             //I2C总线产生起始信号函数
  32. void Stop_I2C();              //I2C总线产生停止信号函数
  33. void Ack_I2C(bit a);          //接收应答信号函数;
  34. void SendByte(uchar c);       //向I2C总线写入一个字节的数据函数
  35. uchar RcvByte();              //字节数据接收函数
  36. uchar write_byte(uchar addr, uchar write_data);//将一个字节写入DS3231指定地址
  37. uchar read_byte(uchar addr);  //从DS3231指定地址读一个字节
  38. void Set_Time();              //设置时间
  39. void get_time(void);          //读取时间
  40. void get_Temperature();       //读取温度
  41. void show_Temperature();      //显示温度
  42. void init();                  //初始化函数
  43. void timer0();                //定时器0中断服务程序
  44. void display(uchar s,f);      //显示子程序
  45. void key();                   //按键控制函数


  46. void baojing()//报警函数
  47. {
  48.    uint j=10,i=10;
  49.    bell=0;
  50.    while(i--)display(shi,fen);
  51.    bell=1;
  52.    while(j--)display(shi,fen);
  53. }



  54. //函数名称:void delay_50US(unsigned int t)
  55. //功能: 延时50*t(us)
  56. void delay_50us(uint t)
  57. {
  58.   unsigned char j;
  59.   for(;t>0;t--)
  60.   {
  61.     for(j=19;j>0;j--);
  62.   }
  63. }


  64. void Start_I2C()            //I2C总线产生起始信号函数
  65. {
  66.     SDA=1;                  //拉高数据线,发送起始条件的数据信号
  67.     SCL=1;                  //拉高时钟线
  68.     SDA=0;                  //在时钟线为高电平时,拉低数据线,产生起始信号
  69.     SCL=0;                  //钳住I2C总线,准备发送或接收数据
  70. }


  71. void Stop_I2C()             //I2C总线产生停止信号函数
  72. {
  73.     SDA=0;                  //拉低数据线,发送结束条件的数据信号
  74.     SCL=1;                  //拉高时钟线,发送结束条件的时钟信号
  75.     SDA=1;                  //时钟时线为高电平时,拉高数据线,发送I2C总线结束信号
  76. }


  77. /********************************************************************
  78.                       应答子函数
  79. 函数原型:   void I2CACK(bit a);
  80. 功能:       主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
  81. ********************************************************************/

  82. void Ack_I2C(bit a)         //接收应答信号函数;a是定义的一个位变量,来暂存应答状态。
  83. {

  84.     if(a==0)
  85.         SDA=0;              //在此发出应答或非应答信号
  86.     else
  87.         SDA=1;
  88.     SCL=1;
  89.     SCL=0;                  //清时钟线,钳住I2C总线以便继续接收
  90. }




  91. /*******************************************************************
  92.                   字节数据发送函数               
  93. 函数原型:     void   SendByte(uchar c);
  94. 功能:      将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
  95.            此状态位进行操作.(不应答或非应答都使ack=0)
  96.            ack=1        发送数据正常,
  97.            ack=0        被控器无应答或损坏。
  98. ********************************************************************/

  99. void SendByte(uchar c)                      //向I2C总线写入一个字节的数据函数
  100. {
  101.     uchar BitCnt;
  102.    
  103.     for(BitCnt=0;BitCnt<8;BitCnt++)         //要传送的数据长度为8位
  104.     {
  105.         if((c<<BitCnt)&0x80)
  106.             SDA=1;                          //判断发送位
  107.         else
  108.             SDA=0;               

  109.           SCL=1;                            //置时钟线为高,通知被控器开始接收数据位
  110.           SCL=0;
  111.     }
  112.    
  113.     SDA=1;                                  //8位发送完后释放数据线,准备接收应答位
  114.     SCL=1;
  115.     if(SDA==1)
  116.         ack=0;   
  117.     else
  118.         ack=1;                              //判断是否接收到应答信号
  119.     SCL=0;
  120. }




  121. /*******************************************************************
  122.                   字节数据接收函数               
  123. 函数原型:     uchar   RcvByte();
  124. 功能:      用来接收从器件传来的数据,并判断总线错误(不发应答信号),
  125.            发完后请用应答函数应答从机。  
  126. ********************************************************************/
  127. uchar RcvByte()    //字节数据接收函数
  128. {
  129.    uchar retc;
  130.    uchar BitCnt;

  131.    retc=0;
  132.    SDA=1;                           //置数据线为输入方式
  133.    for(BitCnt=0;BitCnt<8;BitCnt++)
  134.    {
  135.         SCL=0;                      //置时钟线为低,准备接收数据位
  136.         SCL=1;                      //置时钟线为高使数据线上数据有效
  137.         retc=retc<<1;
  138.         if(SDA==1)
  139.             retc=retc+1;            //读数据位,接收的数据位放入retc中
  140.    }
  141.    SCL=0;
  142.    return(retc);
  143. }







  144. /***********将一个字节写入DS3231指定地址***********/

  145. uchar write_byte(uchar addr, uchar write_data)
  146. {
  147.     Start_I2C();
  148.     SendByte(0xD0);
  149.     if (ack == 0)
  150.         return 0;
  151.    
  152.     SendByte(addr);   
  153.     if (ack == 0)
  154.         return 0;
  155.    
  156.     SendByte(write_data);
  157.     if (ack == 0)
  158.         return 0;
  159.    
  160.     Stop_I2C();
  161.     return 1;
  162. }

  163. /************从DS3231指定地址读一个字节************/
  164. uchar read_byte(uchar addr)
  165. {
  166.     uchar read_data;

  167.     Start_I2C();
  168.     SendByte(0xD0);
  169.     if(ack==0)
  170.         return(0);
  171.    
  172.     SendByte(addr);
  173.     if(ack==0)
  174.         return(0);
  175.        
  176.     Start_I2C();

  177.     SendByte(0xD1);
  178.     if(ack==0)
  179.         return(0);

  180.     read_data = RcvByte();
  181.     Ack_I2C(1);
  182.     Stop_I2C();
  183.         return read_data;
  184. }




  185. /**************************设置时间******************************************/


  186. void Set_Time()
  187. {
  188.     uchar i;
  189.     uchar ucAddr=0x00;      //秒的地址
  190.     for(i=0;i<7;i++)
  191.     {
  192.        write_byte(ucAddr,tabtime[i]);//从秒开始连续写入秒分时星期日月年
  193.        ucAddr+=1;
  194.         }
  195. }





  196. /*********************读取时间**********************/
  197. void get_time(void)
  198. {
  199.     hour=read_byte(0x02)/16*10+read_byte(0x02)%16;
  200.         minute=read_byte(0x01)/16*10+read_byte(0x01)%16;    //因只显示时、分,故可只读取时、分
  201.     year=read_byte(0x06)/16*10+read_byte(0x06)%16;
  202.        
  203.         /*
  204.     uchar i;
  205.         uchar ucCurtime[7];
  206.         uchar ucAddr = 0x00;     //秒地址

  207.         for(i=0;i<7;i++)
  208.         {ucCurtime[i]=0;}
  209.        
  210.         for(i=0; i<7; i++)
  211.         {
  212.                 ucCurtime[i] = read_byte(ucAddr);  //格式为: 从秒地址开始连续读取,秒分时星期日月年。               
  213.                 ucAddr += 1;
  214.         }

  215.         year=ucCurtime[6]/16*10+ucCurtime[6]%16;
  216.         month=ucCurtime[5]/16*10+ucCurtime[5]%16;
  217.         day=ucCurtime[4]/16*10+ucCurtime[4]%16;
  218.         week=ucCurtime[3]/16*10+ucCurtime[3]%16;
  219.         hour=ucCurtime[2]/16*10+ucCurtime[2]%16;
  220.         minute=ucCurtime[1]/16*10+ucCurtime[1]%16;
  221.         second=ucCurtime[0]/16*10+ucCurtime[0]%16;
  222.         */
  223. }




  224. /*********************读取温度**********************/
  225. void get_Temperature()
  226. {
  227.     TtempH=read_byte(0X11);   //读温度高字节
  228.         //因数码管位数只有四位,故温度的小数部分不显示,可以不用读温度低字节
  229.         TtempL=read_byte(0x12);  //原为注释不用ox12   //读温度低字节
  230.         TtempL=(TtempL>>6)*25;   //原为注释不用    //将BIT7,BIT6的数据移入BIT1,BIT0位;分辨率数值扩大100倍便于整数运算
  231.    
  232. }
  233. void show_Temperature()   //显示温度
  234. {
  235.     if((TtempH&0X80)==0x80)   //判断首字节为1即为零下温度
  236.     {
  237.            P0=0xBF;           //0xbf第一位数码管显示“-”
  238.            smg1=0;
  239.            delay_50us(gm_time);
  240.        smg1=1;
  241.           //////
  242.          P0=dis_code[TtempL/10];//送显示断码  十位
  243.                 smg2=0;
  244.         delay_50us(gm_time);
  245.                 smg2=1;   
  246.          P0=dis_code[TtempL%10];//送显示断码  个位
  247.         smg3=0;        
  248.     delay_50us(gm_time);
  249.     smg3=1;

  250.     P0=0xC6;               //送显示断码  ℃
  251.     smg4=0;
  252.     delay_50us(gm_time);
  253.     smg4=1;  
  254.              
  255.    }


  256.         else
  257.            smg1=1;              //第一位数码管不显示


  258.     if((TtempH/10)==0)     //原为0 温度低于10度时,即为个位时,第二位数码管位不显示
  259.        smg2=1;           //原为=1
  260.     else
  261.     {
  262.         P0=dis_code[TtempH/10];//送显示断码  十位
  263.                 smg2=0;
  264.         delay_50us(gm_time);
  265.                 smg2=1;
  266.     }
  267.                        
  268.     P0=dis_code[TtempH%10];//送显示断码  个位
  269.         smg3=0;        
  270.     delay_50us(gm_time);
  271.     smg3=1;

  272.     P0=0xC6;               //送显示断码  ℃
  273.     smg4=0;
  274.     delay_50us(gm_time);
  275.     smg4=1;
  276.        
  277.        

  278. }


  279. void init()        //初始化函数
  280. {
  281.    write_byte(0x0e,0x00);    //设定DS3231的控制寄存器
  282.    IE=0X82;    //设定单片机的中断允许控制寄存器,(1000 0010)
  283.    TMOD=0X01;    //0000 0001选择定时器0的工作方式1 选择定时器1的工作方式0
  284.    TH0=(65536-50000)/256;//初值15536定时50mS
  285.    TL0=(65536-50000)%256;
  286.    TR0=1; //设定TCON的TR0位,开启定时器0
  287. }

  288. void timer0() interrupt 1  //定时器0中断服务程序,用来设置冒号闪烁频率
  289. {
  290.    TH0=(65536-50000)/256;//进入中断重新赋初值15536
  291.    TL0=(65536-50000)%256;//计数5万次(50毫秒)
  292.    a++;                  //标志位b,用来确定光标闪烁频率
  293.    t++;
  294.    if(t==600)t=0;         //50ms*600次=30秒,用于温度显示,30秒一次
  295.    if(t>520)temp_en=1;    //温度显示持续3秒
  296.    else temp_en=0;
  297.    if(a==20)a=0;//a=20 时被清零  产生一个1秒的计时。
  298. }




  299. //四位数码管显示程序
  300. void  display(uchar s,f)
  301. {
  302.    if((s/10)<1) //小于10,第一位不显示
  303.       smg1=1;
  304.    else
  305.    {
  306.       P0=dis_code[s/10];//送显示断码  时的十位
  307.       if((n==1||n==3)&&a<5)
  308.         smg1=1;              //锁存数据
  309.       else
  310.             smg1=0;
  311.       delay_50us(gm_time);
  312.       smg1=1;
  313.    }


  314.       P0=dis_code[s%10];      //送显示断码         时的个位
  315.    if((n==1||n==3)&&a<5)         
  316.       smg2=1;
  317.    else
  318.       smg2=0;                   //锁存数据
  319.    delay_50us(gm_time);
  320.    smg2=1;         
  321.   
  322.    
  323.    
  324.    if((n==2||n==4)&&a<5)        
  325.       P0=0x7F;            //字符闪烁,小点不闪
  326.    else if(a>=10)                        
  327.       P0=dis_code[f/10]-0x80; //小点闪烁,字符不闪
  328.    else                                   
  329.       P0=dis_code[f/10];      //送显示断码  分的十位
  330.    smg3=0;
  331.    delay_50us(gm_time);
  332.    smg3=1;



  333.    if((n==2||n==4)&&a<5)        
  334.       P0=0x7F;            //字符闪烁,小点不闪
  335.    else if(a>=10)                        
  336.       P0=dis_code[f%10]-0x80; //小点闪烁,字符不闪
  337.    else                                   
  338.       P0=dis_code[f%10];      //送显示断码  分的个位
  339.    smg4=0;
  340.    delay_50us(gm_time);
  341.    smg4=1;

  342. }




  343. void key()                        //按键控制函数
  344. {
  345.    
  346.    if(k1==0&&k2==0)          //k1k2同时按下,切换闹铃开关 并且在按下之后查看闹钟时间松手后恢复时间显示
  347.    {
  348.       delay_50us(40);
  349.       if(k1==0&&k2==0)
  350.       {        
  351.          if(bell_en==1&&b_shi==hour&&b_fen==minute&&x==1)
  352.                x=0;                      //如果处于响零状态,就任意键关闭响铃   
  353.              shi=b_shi;
  354.          fen=b_fen;
  355.          bell_en=!bell_en;
  356.          if(bell_en==1)       //使能的话报警两声
  357.          {
  358.             
  359.                     baojing();
  360.                         baojing();
  361.          }
  362.                  else
  363.                  {
  364.             
  365.                     baojing();    //无效的话报警一声
  366.                  }
  367.          while(k1==0&&k2==0)
  368.                  display(shi,fen);//等待松手
  369.          shi=hour;
  370.          fen=minute;
  371.       }
  372.    }
  373.    if(k1==0&&k3==0) //k1k3同时按下,切换光控功能
  374.    {
  375.       delay_50us(40);
  376.       if(k1==0&&k3==0)
  377.       {        
  378.          gm_en=!gm_en;
  379.                  if(bell_en==1&&b_shi==hour&&b_fen==minute&&x==1)
  380.                x=0;                      //如果处于响零状态,就任意键关闭响铃   
  381.          if(gm_en==1)       //使能的话报警两声
  382.          {
  383.                     baojing();
  384.                         baojing();
  385.          }
  386.          else baojing();    //无效的话报警一声
  387.          while(k1==0&&k3==0)
  388.                  display(88,88);//等待松手
  389.       }
  390.    }
  391.    
  392.   
  393.    if(k2==0&&k3==0)                         //K2,K3同时按下,进入 调时间 和  闹铃状态
  394.    {                        //功能键k1被按下
  395.       delay_50us(40);      //消抖延时
  396.       if(k2==0&&k3==0)
  397.           {
  398.              if(bell_en==1&&b_shi==hour&&b_fen==minute&&x==1)
  399.                x=0;                      //如果处于响零状态,就任意键关闭响铃      
  400.              while(k2==0&&k3==0)
  401.                   n=1;
  402.           
  403.       }

  404.                                //要加入数字闪烁代码
  405.    }        

  406.    
  407.    
  408.    
  409.    
  410.    if(n==0)//状态0,正常显示时间
  411.    {
  412.       b=a; //b是控制小数点闪烁的
  413.       shi=hour;
  414.       fen=minute;
  415.    }


  416.    
  417.    if(n==1)    //状态1:调节时间的"时"
  418.    {
  419.       b=10;
  420.       
  421.           if(k1==0)
  422.           {
  423.              delay_50us(40);      //消抖延时
  424.              if(k1==0)   
  425.          {
  426.                     while(k1==0)
  427.                       n=2;
  428.                  }
  429.       }
  430.           
  431.           if(k2==0)
  432.           {
  433.              delay_50us(40);      //消抖延时
  434.              if(k2==0)//k2被按下
  435.                    shi=shi+1;   //小时加一
  436.          if(shi>=24)
  437.            shi=0;   //达到24清零
  438.          while(k2==0)
  439.          display(shi,fen);
  440.       }
  441.       
  442.           if(k3==0)
  443.           {
  444.              delay_50us(40);      //消抖延时
  445.              if(k3==0) //k3被按下
  446.            shi--; //小时减一
  447.          if(shi<0)
  448.            shi=23;//小于0,重新赋值
  449.                  while(k3==0)
  450.                  display(shi,fen);
  451.       }
  452.    }
  453.   
  454.    if(n==2)//状态2:调节时间的"分"
  455.    {        
  456.       b=10;
  457.       
  458.           if(k1==0)
  459.           {
  460.              delay_50us(40);      //消抖延时
  461.              if(k1==0)   
  462.                  {
  463.                     while(k1==0)
  464.              n=3;
  465.                  }
  466.          tabtime[2]=shi/10*16+shi%10;
  467.          tabtime[1]=fen/10*16+fen%10;
  468.       
  469.              hour=shi;
  470.          minute=fen;

  471.          Set_Time();//设置DS1302的初始时间              
  472.                  
  473.       }          
  474.           
  475.           
  476.           if(k2==0)//k2被按下
  477.       {
  478.          delay_50us(40);      //消抖延时
  479.          if(k2==0)
  480.             fen++;//分加一
  481.          if(fen>=60)
  482.             fen=0;//分到达60 分清零   
  483.                  while(k2==0)
  484.                     display(shi,fen);
  485.       }
  486.       
  487.           if(k3==0)//k3被按下
  488.       {            
  489.          delay_50us(40);      //消抖延时
  490.          if(k3==0)
  491.             fen--;//分减一
  492.          if(fen<0)
  493.             fen=59;//分小于0,分重新赋值59   
  494.                  while(k3==0)
  495.                  display(shi,fen);                                       
  496.       }

  497.   
  498.    }
  499.    
  500.    
  501.    if(n==3)//状态三:调节闹铃"时"
  502.    {        

  503.       b=10;
  504.       shi=b_shi;
  505.       fen=b_fen;   
  506.    
  507.           if(k1==0)
  508.           {
  509.              delay_50us(40);      //消抖延时
  510.              if(k1==0)   
  511.                  {
  512.                     while(k1==0)
  513.                          n=4;
  514.                  }
  515.       }      
  516.           
  517.           if(k2==0)//k2被按下
  518.       {        
  519.          delay_50us(40);      //消抖延时
  520.          if(k2==0)
  521.             b_shi++;//闹铃时间加一
  522.          if(b_shi>=24)
  523.             b_shi=0;//闹铃时间清零     
  524.                  while(k2==0)
  525.                  display(shi,fen);
  526.       }
  527.       
  528.           if(k3==0)//k3被按下
  529.       {           
  530.          delay_50us(40);      //消抖延时
  531.          if(k3==0)
  532.             b_shi--;//闹铃时间减一
  533.          if(b_shi<0)
  534.             b_shi=23;//闹铃时间
  535.                  while(k3==0)
  536.                  display(shi,fen);
  537.       }

  538.    }
  539.    
  540.    
  541.    if(n==4)//状态四 :调节闹铃的"分"
  542.    {      
  543.       b=10;
  544.       shi=b_shi;
  545.       fen=b_fen;
  546.       
  547.           if(k1==0)
  548.           {
  549.              delay_50us(40);      //消抖延时
  550. ……………………

  551. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
51hei.png

所有资料51hei提供下载:
0.8寸数码管 PCB 电路图 程序.rar (3.56 MB, 下载次数: 80)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:53978 发表于 2021-10-7 15:04 | 显示全部楼层
本帖最后由 smartphone 于 2021-10-7 17:54 编辑

按照这个代码在开发板上测试了可以正常显示,但数码管闪烁,不知道什么原因,有大神能指点一下吗?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表