找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 838|回复: 4
收起左侧

单片机+74HC595驱动8位数码管显示异常

[复制链接]
ID:140489 发表于 2024-4-19 09:55 | 显示全部楼层 |阅读模式
74HC595驱动8位数码管显示异常问题:
1. 用定时器0计时模拟时钟显示正常;
2. 读取DS3231时钟芯片的数据就显示异常,前面7位亮度低,第8位亮度高,数字变化看不清楚,具体见异常显示图片。
压缩包里面有所有的工程资料
请论坛大师看看怎么解决这个问题,谢谢。
原理图.png 异常显示.jpg 正常显示.jpg
单片机源程序如下:
  1. //74HC595驱动8位数码管
  2. //#include <reg52.h>
  3. #include <STC8G.h>

  4. #include <intrins.h>
  5. #define uchar unsigned char
  6. #define uint unsigned int


  7. uchar code Table[] =
  8. { // 0         1          2           3        4         5          6           7        8         9          A           b        C    d          E    不显    -
  9.         0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0xFF,0xbf
  10. };        //共阳段码表
  11. //-----------------------------------------------------------------------------

  12. sbit SER = P3^7;        //串行数据输入端
  13. sbit STCP = P3^6;        //时钟脉冲信号——上升沿有效 存储寄存器时钟输入端,并行输出
  14. sbit SHCP = P3^5;        //输入信号————上升沿有效 移位寄存器时钟输入端

  15. sbit key1 = P3^0;        //选择键
  16. sbit key2 = P3^1;        //加键
  17. sbit key3 = P3^2;        //减键

  18. char shi=12,fen=59,miao=30;
  19. //char shi,fen,miao;

  20. uchar shan,shan1,A,K,C,D,E,F;
  21. uchar knum;                //选择键计数变量
  22. uint keycont1,keycont2,keycont3;//消抖计时变量
  23. bit lock1,lock2,lock3;//按键自锁标志

  24. bit key3_shotr_flag;
  25. bit        ack;                //应答标志位
  26. uchar key3_cnt;//按键3按下次数记录
  27. uint key3_sj_time;//按键3双击时间变量

  28. uchar key_num;//按键触发编号变量
  29. uchar hour,minute,second,year,month,day,date,week;
  30. #define const_key_time  20    //按键去抖动延时的时间
  31. //#define const_key_time2  20    //按键去抖动延时的时间
  32. //#define const_key_time3  20    //按键去抖动延时的时间

  33. #define const_key_long_time  500    //按键去抖动延时的时间
  34. #define const_key_lj_time  400    //按键去抖动延时的时间
  35. #define key3_sj_cnt  250    //按键去抖动延时的时间


  36. void TimerInit();
  37. void KeyScan();        //按键扫描
  38. void key_service();//按键服务程序
  39. void Display (uchar shi10,uchar shi,uchar fen10,uchar fen,uchar miao10,uchar miao);        // 数码管显示
  40. void SMG_Int(uchar Dat);                // 数码管单字节串行移位函数
  41. ///////////////////////////////////

  42. // ------------------------------------------------------------
  43. // IO口模拟I2C通信

  44. // ------------------------------------------------------------
  45. sbit SCL=P3^4; //串行时钟
  46. sbit SDA=P3^3; //串行数据

  47. /********************************************************************************************************
  48. **         DS3231常数定义
  49. ********************************************************************************************************/

  50. #define DS3231_WriteAddress 0xD0    //器件写地址
  51. #define DS3231_ReadAddress  0xD1    //器件读地址
  52. #define DS3231_SECOND       0x00    //秒
  53. #define DS3231_MINUTE       0x01    //分
  54. #define DS3231_HOUR         0x02    //时
  55. #define DS3231_WEEK         0x03    //星期
  56. #define DS3231_DAY          0x04    //日
  57. #define DS3231_MONTH        0x05    //月
  58. #define DS3231_YEAR         0x06    //年

  59. #define DS3231_TEMPERATUREH 0x11    //温度寄存器高字节(8位)        整数部分
  60. #define DS3231_TEMPERATUREL 0x12    //温度寄存器低字节(低2位) 小数部分



  61. #define NACK    1
  62. #define ACK     0         
  63. //////////////////

  64. void Delay5US()                //@11.0592MHz         STC Y6指令
  65. {
  66.         unsigned char i;

  67.         _nop_();
  68.         i = 16;
  69.         while (--i);
  70. }

  71. void delay(uint x)
  72. {
  73.         uint y,z;
  74.         for(y=0; y<x; y--)
  75.                 for(z=0; z<930; z--);

  76. }

  77. /**********************************************
  78. //IIC Start
  79. **********************************************/

  80. void IIC_Start()
  81. {
  82.    SCL = 1;               
  83.    SDA = 1;
  84.    SDA = 0;
  85.    SCL = 0;
  86. }

  87. /**********************************************
  88. //IIC Stop
  89. **********************************************/

  90. void IIC_Stop()
  91. {
  92.    SCL = 0;
  93.    SDA = 0;
  94.    SCL = 1;
  95.    SDA = 1;
  96. }

  97. /********************************************************************************************************
  98. **         3231
  99. ********************************************************************************************************/


  100. uchar        BCD2HEX(uchar val)//转10进制
  101. {
  102.         return        ((val>>4)*10)+(val&0x0f);
  103. }

  104. uchar        HEX2BCD(uchar val) //转16进制
  105. {
  106.         return        (((val%100)/10)<<4)|(val%10);
  107. }


  108. void SendByte(uchar c)
  109. {
  110.     uchar BitCnt;
  111.    
  112.     for(BitCnt=0;BitCnt<8;BitCnt++)         //要传送的数据长度为8位
  113.     {
  114.         if((c<<BitCnt)&0x80)
  115.             SDA=1;                          //判断发送位
  116.         else
  117.             SDA=0;     
  118.         SCL=1;                            //置时钟线为高,通知被控器开始接收数据位
  119.         Delay5US();                       //保证时钟高电平周期大于4μs   
  120.         SCL=0;
  121.     }
  122.     SDA=1;                                  //8位发送完后释放数据线,准备接收应答位
  123.     SCL=1;
  124.     Delay5US();
  125.     if(SDA==1)
  126.         ack=0;   
  127.     else
  128.         ack=1;                              //判断是否接收到应答信号
  129.     SCL=0;
  130.     Delay5US();
  131. }           

  132. uchar RcvByte()
  133. {
  134.    uchar retc;
  135.    uchar BitCnt;

  136.    retc=0;
  137.    SDA=1;                           //置数据线为输入方式
  138.    for(BitCnt=0;BitCnt<8;BitCnt++)
  139.    {
  140.         SCL=0;                      //置时钟线为低,准备接收数据位      
  141.         Delay5US();                 //时钟低电平周期大于4.7μs                       
  142.         SCL=1;                      //置时钟线为高使数据线上数据有效
  143.         Delay5US();
  144.         retc=retc<<1;
  145.         if(SDA==1)
  146.             retc=retc+1;            //读数据位,接收的数据位放入retc中
  147.         Delay5US();
  148.    }
  149.    SCL=0;
  150.    return(retc);
  151. }                           

  152. void Ack_I2C(bit a)
  153. {
  154.         SDA        =        a;  
  155.     SCL=1;                     
  156.     Delay5US();             //时钟低电平周期大于4us   
  157.     SCL=0;                  //清时钟线,钳住I2C总线以便继续接收
  158.     Delay5US();   
  159. }                                            

  160. uchar write_byte(uchar addr, uchar write_data)
  161. {
  162.     IIC_Start();
  163.     SendByte(DS3231_WriteAddress);
  164.     if (ack == 0)
  165.         return 0;
  166.    
  167.     SendByte(addr);   
  168.     if (ack == 0)
  169.         return 0;
  170.    
  171.     SendByte(write_data);
  172.     if (ack == 0)
  173.         return 0;
  174.    
  175.     IIC_Stop();
  176.     Delay5US();      
  177.     Delay5US();      
  178.     return 1;
  179. }                                          

  180. uchar read_current()
  181. {
  182.     uchar read_data;
  183.     IIC_Start();
  184.     SendByte(DS3231_ReadAddress);
  185.     if(ack==0)
  186.         return(0);              
  187.     read_data = RcvByte();
  188.     Ack_I2C(1);
  189.     IIC_Stop();
  190.     return read_data;
  191. }                                                

  192. uchar read_random(uchar random_addr)
  193. {
  194.     uchar Tmp;
  195.         IIC_Start();
  196.     SendByte(DS3231_WriteAddress);
  197.     if(ack==0)
  198.         return(0);            
  199.     SendByte(random_addr);
  200.     if(ack==0)
  201.         return(0);
  202.         Tmp=read_current();
  203.         if(random_addr==DS3231_HOUR)
  204.                 Tmp&=0x3f;
  205.                                             
  206.     return(BCD2HEX(Tmp));//都转10进制输出
  207. }


  208. void KeyScan()           //按键程序放在2ms定时器中断里面扫描
  209. {
  210.         if(key1)
  211.         {
  212.                 keycont1=0;
  213.                 lock1=0;        
  214.         }
  215.         else if(lock1==0)//按键按下
  216.         {
  217.                 keycont1++;
  218.                 if(keycont1>const_key_time)
  219.                 {
  220.                         keycont1=0;
  221.                         lock1=1;  //按键锁置1,避免一直触发
  222.                         key_num=1; //触发1号键
  223.                 }        
  224.         }
  225. /////////////////////////
  226.         if(key2)
  227.         {
  228.                 keycont2=0;
  229.                 lock2=0;        
  230.         }
  231.         else if(lock2==0)
  232.         {
  233.                 keycont2++;
  234.                 if(keycont2>const_key_time)
  235.                 {
  236.                         keycont2=0;
  237.                         lock2=1;
  238.                         key_num=2; //触发2号键
  239.                 }        
  240.         }
  241.         else if(keycont2<const_key_long_time)//长按 连减
  242.         {
  243.                 keycont2++;
  244.                 if(keycont2==const_key_long_time)
  245.                 {
  246.                         keycont2=const_key_lj_time;//连加速度设置
  247.                         key_num=2;         //触发2号键
  248.                 }        
  249.          }
  250. /////////////////////////
  251.         if(key3)        //按键没有按下
  252.         {
  253.                 keycont3=0;
  254.                 lock3=0;
  255.                 if(key3_cnt>0) //按键按下次数key3_cnt>0说明按键按下过
  256.                 {
  257.                         key3_sj_time++;// 计时变量自加
  258.                         if(key3_sj_time>key3_sj_cnt)//计时大于500ms            2ms中断
  259.                         {
  260.                                 key3_sj_time=0;//双击计时变量清零
  261.                                 key3_cnt=0;//按键按下次数清零
  262.                         }                                       
  263.                 }
  264.                
  265.                 if(key3_shotr_flag)        //松手后触发3号按键的单击短按
  266.                 {
  267.                         key_num=3; //触发3号键
  268.                         key3_shotr_flag=0;               
  269.                 }        
  270.         }
  271.         else if(lock3==0) //按键按下
  272.         {
  273.                 keycont3++;
  274.                 if(keycont3>const_key_time)
  275.                 {
  276.                         keycont3=0;
  277.                         key3_cnt++;//k3按键次数
  278.                         lock3=1;
  279.                         if(key3_cnt==1)//单击
  280.                         {
  281.                         //        key_num=3; //触发3号键
  282.                                 key3_shotr_flag=1;
  283.                         }

  284.                         if(key3_cnt==2 && knum==0) //双击
  285.                         {
  286.                                 miao = 30;//把秒写30                        
  287.                         }
  288.                         if(key3_cnt==3 && knum==0) //三击
  289.                         {
  290.                                 fen = 30;//把分写30                        
  291.                         }

  292.                 }
  293.         }
  294.         else if(keycont3<const_key_long_time)//长按 连减
  295.         {
  296.                 keycont3++;
  297.                 if(keycont3==const_key_long_time)
  298.                 {
  299.                         keycont3=const_key_lj_time;//连减速度设置
  300.                         key_num=3; //触发3号键
  301.                 }               
  302.         }
  303. }

  304. void key_service() // 按键服务的应用程序
  305. {
  306.         switch(key_num)
  307.         {
  308.                 case 1:        knum++;
  309.                                 if(knum==4)
  310.                                         knum=0;
  311.                             key_num=0; //响应按键服务处理程序后,按键编号清零,避免一直触发!!!
  312.                                 break;

  313.                 case 2: if(knum==1){shi++; if(shi>23)shi=0;}
  314.                             else if (knum==2){fen++; if(fen>59)fen=0;}
  315.                             else if (knum==3){miao++; if(miao>59)miao=0;}
  316.                             key_num=0; //响应按键服务处理程序后,按键编号清零,避免一直触发!!!
  317.                                 break;
  318.                         
  319.                 case 3: if(knum==1){shi--; if(shi<0)shi=23;}
  320.                                 else if (knum==2){fen--; if(fen<0)fen=59;}
  321.                                 else if (knum==3){miao--; if(miao<0)miao=59;}
  322.                                 key_num=0; //响应按键服务处理程序后,按键编号清零,避免一直触发!!!
  323.                                 break;

  324.                 default: break;
  325.         }
  326. }

  327. void out595()
  328. {
  329.         STCP = 0;         //数据并行输出,(借助上升沿)
  330.         _nop_();
  331.         _nop_();
  332.         STCP = 1;
  333. }


  334. void Display (uchar shi10,uchar shi,uchar fen10,uchar fen,uchar miao10,uchar miao)
  335. {
  336.         uchar i;

  337.         //显示第1位        小时10位
  338.         if(knum==1)                //
  339.         {
  340.                 if(shan>125)         //        调时数码管闪烁
  341.                 i = Table[15];
  342.                 else
  343.                 i = Table[shi10];
  344.         
  345.                 SMG_Int(i);        //段码               
  346.                 SMG_Int(0x01);//位码               
  347.         
  348.                 out595();        
  349.                 //显示第2位        小时个位
  350.                 if(shan>125)         //
  351.                 i = Table[15];          //黑屏
  352.                 else
  353.                 i = Table[shi];
  354.         
  355.                 SMG_Int(i);               
  356.                 SMG_Int(0x02);               
  357.         
  358.                 out595();        
  359.         }
  360.         else
  361.         {
  362.                 i = Table[shi10];
  363.         
  364.                 SMG_Int(i);                        
  365.                 SMG_Int(0x01);               
  366.         
  367.                 out595();        
  368.         
  369.                 //显示第2位        小时个位
  370.                 i = Table[shi];
  371.         
  372.                 SMG_Int(i);               
  373.                 SMG_Int(0x02);               
  374.         
  375.                 out595();        
  376.         }
  377.         //显示第3位        -
  378. //        if(shan<125)         
  379. //        i = Table[15];
  380. //        else
  381.         i = Table[16];         //"-" 闪烁
  382.         SMG_Int(i);                        
  383.         SMG_Int(0x04);        

  384.         out595();        

  385.         //显示第4位        分钟10位
  386.         if(knum==2)
  387.         {
  388.                 if(shan>125)         //
  389.                 i = Table[15];
  390.                 else
  391.                 i = Table[fen10];        
  392.                 SMG_Int(i);                        
  393.                 SMG_Int(0x08);               
  394.         
  395.                 out595();        
  396.         
  397.                 //显示第5位        分钟个位
  398.                 if(shan>125)         //
  399.                 i = Table[15];
  400.                 else
  401.                 i = Table[fen];
  402.         
  403.                 SMG_Int(i);                        
  404.                 SMG_Int(0x10);        
  405.         
  406.                 out595();        
  407.         }
  408.         else
  409.         {
  410.                 i = Table[fen10];
  411.         
  412.                 SMG_Int(i);                        
  413.                 SMG_Int(0x08);               
  414.         
  415.                 out595();        
  416.         
  417.                 //显示第5位        分钟个位
  418.                 i = Table[fen];
  419.         
  420.                 SMG_Int(i);                        
  421.                 SMG_Int(0x10);        
  422.         
  423.                 out595();        
  424.         }
  425.         //显示第6位        -
  426. //        if(shan<125)           //"-" 闪烁
  427. //        i = Table[15];
  428. //        else
  429.         i = Table[16];         //"-"

  430.         SMG_Int(i);                        
  431.         SMG_Int(0x20);        

  432.         out595();        

  433.         if(knum==3)
  434.         {
  435.                 //显示第7位        秒10位
  436.                 if(shan>125)         //
  437.                 i = Table[15];
  438.                 else
  439.                 i = Table[miao10];
  440.         
  441.                 SMG_Int(i);                        
  442.                 SMG_Int(0x40);        
  443.         
  444.                 out595();        
  445.         
  446.                 //显示第8位        秒个位
  447.                 if(shan>125)         //
  448.                 i = Table[15];
  449.                 else
  450.                 i = Table[miao];
  451.         
  452.                 SMG_Int(i);               
  453.                 SMG_Int(0x80);        
  454.         
  455.                 out595();        
  456.         }
  457.         else
  458.         {
  459.                 //显示第7位        秒10位
  460.                 i = Table[miao10];
  461.         
  462.                 SMG_Int(i);                        
  463.                 SMG_Int(0x40);        
  464.         
  465.                 out595();        
  466.         
  467.                 //显示第8位        秒个位
  468.                 i = Table[miao];
  469.         
  470.                 SMG_Int(i);               
  471.                 SMG_Int(0x80);        
  472.         
  473.                 out595();        
  474.         }
  475. }

  476. void SMG_Int(uchar Dat)         //通过8次循环将8位数据移入74HC595
  477. {
  478.         uchar i;
  479.         for(i=8;i>=1;i--)
  480.         {
  481.                 if (Dat & 0x80) //如果数据的最高位是1
  482.                 {
  483.                         SER = 1;        //串行数据输入端置1
  484.                 }
  485.                
  486.                 else
  487.                 {
  488.                         SER = 0;  //否则串行数据输入端清零
  489.                 }
  490.                 Dat <<= 1;          //数据左移1位,分8次把8位数据移入74HC595
  491.                 SHCP = 0;  //SH引脚的上升沿把数据送入寄存器
  492.                 _nop_();
  493.                 _nop_();
  494.                 SHCP = 1;
  495.         }
  496. }

  497. void TimerInit()         //定时器0初始化
  498. {
  499.         TMOD = 0x01;
  500.         TH0 = (65536-2000)/256;        //2ms初值
  501.         TL0 = (65536-2000)%256;
  502.         EA = 1;
  503.         ET0 = 1;
  504.         TR0 = 1;

  505. }

  506. void main ()
  507. {
  508.         TimerInit();
  509. //        PWM_Init();
  510.         P0M0 = 0x00;
  511.     P0M1 = 0x00;
  512.     P1M0 = 0x00;
  513.     P1M1 = 0x00;//
  514.     P2M0 = 0x00;
  515.     P2M1 = 0x00;
  516.     P3M0 = 0x00;
  517.     P3M1 = 0x00;
  518.     P4M0 = 0x00;
  519.     P4M1 = 0x00;
  520.     P5M0 = 0x00;
  521.     P5M1 = 0x00;
  522.         while(1)
  523.         {
  524. //                shi = read_random(DS3231_HOUR);
  525. //                fen = read_random(DS3231_MINUTE) ;
  526. //                miao = read_random(DS3231_SECOND);
  527. //                A=shi/10;
  528. //                K=shi%10;
  529. //                C=fen/10;
  530. //                D=fen%10;
  531. //                E=miao/10;
  532. //                F=miao%10;

  533.                 Display (A,K,C,D,E,F);
  534.                 key_service();// 按键服务的应用程序
  535.         }
  536. }

  537. void Timer0() interrupt 1
  538. {
  539.         static uint cnt;
  540.         
  541.         TH0 = (65536-2000)/256;
  542.         TL0 = (65536-2000)%256;
  543. //        shan1++;
  544. //        if(shan1==250)
  545. //                shan1=0;
  546.         

  547.         shan++;
  548.         if(shan==250)
  549.                 shan=0;

  550.         if(knum==0)
  551.         {
  552.                 cnt++;
  553.                 if(cnt==500)
  554.                 {
  555.                         cnt = 0;
  556.                         miao++;
  557.                         if(miao == 60)
  558.                         {
  559.                                 miao = 0;
  560.                                 fen++;
  561.                                 if(fen==60)
  562.                                 {
  563.                                         fen = 0;
  564.                                         shi++;
  565.                                         if(shi==24)
  566.                                         {
  567.                                                 shi=0;
  568.                                         }
  569.                                 }
  570.                         }
  571.                 }
  572.         }
  573.         A=shi/10;
  574.         K=shi%10;
  575.         C=fen/10;
  576.         D=fen%10;
  577.         E=miao/10;
  578.         F=miao%10;
  579.         
  580.         KeyScan();
  581. }
复制代码

595数码管时钟资料.zip

250.9 KB, 下载次数: 6

回复

使用道具 举报

ID:69038 发表于 2024-4-19 11:46 | 显示全部楼层

很怀疑你中断程序会忙的过来不?定时器中断里,要完成的工作是不是有点多,又是计时又是分配缓存又是调用万里长城般的键扫的代码?中断不要放太多代码,最完美的就是进中断后处理一些标志位就退出来,那些冗长的代码让主程序去完成。
595的显示代码再简单不过了,咋就写成这样乱了?不如开个2ms的中断,每进中断,位选+1,8位数码管16ms一轮显,视觉上完全不会闪的。用595驱动LED出现亮度不均,无非两种情况,其一是电流不足,其二是显示时长不相等。。
显示代要改改,值未改,就没必要重新加载一次到缓存。
.
.
.
个人意见,仅供参考。。。
回复

使用道具 举报

ID:1109793 发表于 2024-4-19 12:50 | 显示全部楼层
立创EDA的这些零件都是自带的还是你画 的?
回复

使用道具 举报

ID:140489 发表于 2024-4-19 16:23 | 显示全部楼层
xiaobendan001 发表于 2024-4-19 12:50
立创EDA的这些零件都是自带的还是你画 的?

自己画的有一些,系统里面有的就不要自己画
回复

使用道具 举报

ID:1099129 发表于 2024-4-19 17:08 | 显示全部楼层
第一检查电流
第二显示延时 驱动595弹数据时可以弹两次或者三次,或者加延时2~5us加强显示时间
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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