第88节:单片机靠关键字快速截取有效数据串

发布者:MysticalWhisper最新更新时间:2016-03-11 来源: eefocus关键字:单片机  关键字  有效数据串 手机看文章 扫描二维码
随时随地手机看文章
开场白:
我前面串口程序大部分都是通过靠时间来识别每一串数据是否接收完毕,有一些串口项目的协议是固定不变的,而且也不需要从机反馈任何应答信号,这类项目只需根据特定关键字来快速识别数据串是否接收完毕即可。比如现在有一种电子称,它的测量范围是0.00克到500.00克,他是靠串口不断对外发送当前重量数据的,每串数据固定长度26个字节,最后两个字节是回车换行符0x0d 0x0a,倒数第9,10,11,12,13,14为有效的ASCII码数字,其中倒数第11位为固定的小数点,其它的数据可以忽略不计。这类串口框架的思路是:根据数据尾是否有0x0d 0x0a来判断数据串是否有效的,一旦发现有此关键字,再判断总的数据长度是否等于或者大于一串数据的固定长度,如果满足,则把相关标志位置位,通知主函数中的串口服务程序进行处理。同时也及时关闭串口中断,避免在处理串口数据期间受到串口数据的中断干扰,等串口服务程序处理完毕再打开。
 
具体内容,请看源代码讲解。
 
(1)     硬件平台:
基于朱兆祺51单片机学习板。
 
(2)     实现功能:
波特率是:9600。把当前电子称的重量数据显示在数码管上,在电脑上用串口助手软件来模拟电子称发送以下格式协议的3串数据,它的协议很简单,每串数据固定长度26个字节,最后两个字节是回车换行符0x0d 0x0a,倒数第9,10,11,12,13,14为有效的ASCII码数字,其中倒数第11位为固定的小数点,其它的数据可以忽略不计。
(a)字符是:
ST,GS,+      0.77    g
转换成16进制是:
20 53 54 2C 47 53 2C 2B 20 20 20 20 20 20 30 2E 37 37 20 2020 20 20 67 0D 0A
数码管显示:0.77
(b)
字符是:
ST,GS,+    136.39    g
转换成16进制是:
20 53 54 2C 47 53 2C 2B 20 20 20 20 31 33 36 2E 33 39 20 2020 20 20 67 0D 0A
数码管显示:136.39
(c)
字符是:
ST,GS,+      0.00    g
转换成16进制是:
20 53 54 2C 47 53 2C 2B 20 20 20 20 20 20 30 2E 30 30 20 2020 20 20 67 0D 0A
数码管显示:0.00

(3)源代码讲解如下:
  1. #include "REG52.H"
  2.  
  3.  
  4. #define const_rc_size  36  //接收串口中断数据的缓冲区数组大小
  5.  
  6. #define const_least_size 26   //一串标准数据的大小
  7.  
  8. void initial_myself();    
  9. void initial_peripheral();
  10. void delay_short(unsigned int uiDelayShort); 
  11. void delay_long(unsigned int uiDelaylong);
  12. //驱动数码管的74HC595
  13. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);  
  14. void display_drive(); //显示数码管字模的驱动函数
  15. void display_service(); //显示的窗口菜单服务程序
  16.  
  17. //驱动LED的74HC595
  18. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
  19.  
  20. void usart_service(void);  //串口接收服务程序,在main函数里
  21. void usart_receive(void); //串口接收中断函数
  22.  
  23. void T0_time();  //定时中断函数
  24.  
  25.  
  26. sbit dig_hc595_sh_dr=P2^0;     //数码管的74HC595程序
  27. sbit dig_hc595_st_dr=P2^1;  
  28. sbit dig_hc595_ds_dr=P2^2;  
  29.  
  30.  
  31. sbit hc595_sh_dr=P2^3;    //LED灯的74HC595程序
  32. sbit hc595_st_dr=P2^4;  
  33. sbit hc595_ds_dr=P2^5; 
  34.  
  35. sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
  36.  
  37. sbit led_dr=P3^5;  //独立LED灯
  38.  
  39.  
  40. //根据原理图得出的共阴数码管字模表
  41. code unsigned char dig_table[]=
  42. {
  43. 0x3f,  //0       序号0
  44. 0x06,  //1       序号1
  45. 0x5b,  //2       序号2
  46. 0x4f,  //3       序号3
  47. 0x66,  //4       序号4
  48. 0x6d,  //5       序号5
  49. 0x7d,  //6       序号6
  50. 0x07,  //7       序号7
  51. 0x7f,  //8       序号8
  52. 0x6f,  //9       序号9
  53. 0x00,  //无      序号10
  54. 0x40,  //-       序号11
  55. 0x73,  //P       序号12
  56. };
  57.  
  58.  
  59. unsigned int  uiRcregTotal=0;  //代表当前缓冲区已经接收了多少个数据
  60. unsigned int  uiRcregTotalTemp=0;  //代表当前缓冲区已经接收了多少个数据的中间变量
  61. unsigned char ucRcregBuf[const_rc_size]; //接收串口中断数据的缓冲区数组
  62. unsigned char ucReceiveFlag=0; //接收成功标志
  63.  
  64.  
  65. unsigned char ucDigShow8;  //第8位数码管要显示的内容
  66. unsigned char ucDigShow7;  //第7位数码管要显示的内容
  67. unsigned char ucDigShow6;  //第6位数码管要显示的内容
  68. unsigned char ucDigShow5;  //第5位数码管要显示的内容
  69. unsigned char ucDigShow4;  //第4位数码管要显示的内容
  70. unsigned char ucDigShow3;  //第3位数码管要显示的内容
  71. unsigned char ucDigShow2;  //第2位数码管要显示的内容
  72. unsigned char ucDigShow1;  //第1位数码管要显示的内容
  73.  
  74. unsigned char ucDigDot8;  //数码管8的小数点是否显示的标志
  75. unsigned char ucDigDot7;  //数码管7的小数点是否显示的标志
  76. unsigned char ucDigDot6;  //数码管6的小数点是否显示的标志
  77. unsigned char ucDigDot5;  //数码管5的小数点是否显示的标志
  78. unsigned char ucDigDot4;  //数码管4的小数点是否显示的标志
  79. unsigned char ucDigDot3;  //数码管3的小数点是否显示的标志
  80. unsigned char ucDigDot2;  //数码管2的小数点是否显示的标志
  81. unsigned char ucDigDot1;  //数码管1的小数点是否显示的标志
  82. unsigned char ucDigShowTemp=0; //临时中间变量
  83. unsigned char ucDisplayDriveStep=1;  //动态扫描数码管的步骤变量
  84.  
  85. unsigned char ucWd1Part1Update=1; //8位数码管更新显示标志
  86.  
  87. unsigned long ulWeightCurrent=12345; //显示当前实际的重量
  88.  
  89. void main() 
  90.   {
  91.    initial_myself();  
  92.    delay_long(100);   
  93.    initial_peripheral(); 
  94.    while(1)  
  95.    { 
  96.       usart_service();  //串口接收服务程序
  97.       display_service(); //显示的窗口菜单服务程序
  98.    }
  99. }
  100.  
  101. /* 注释一:
  102. * 本节内容处理串口数据是根据数据尾是否有0x0d 0x0a来判断数据串是否有效的,一旦发现有此关键字,
  103. * 再判断总的数据长度是否等于或者大于一串数据的固定长度,如果满足,则把相关标志位置位,通知主函数中
  104. * 的串口服务程序进行处理。同时也及时关闭串口中断,避免在处理串口数据期间受到串口数据的中断干扰,
  105. * 等串口服务程序处理完毕再打开。
  106. */
  107. void usart_receive(void) interrupt 4   //串口接收数据中断函数      
  108. {        
  109.  
  110.    if(RI==1)  
  111.    {
  112.         RI = 0;
  113.  
  114.         ++uiRcregTotal;
  115.         ucRcregBuf[uiRcregTotal-1]=SBUF;   //将串口接收到的数据缓存到接收缓冲区里
  116.         if(uiRcregTotal>=2&&ucRcregBuf[uiRcregTotal-2]==0x0d&&ucRcregBuf[uiRcregTotal-1]==0x0a)  //一旦发现后缀是0x0d 0x0a关键字的就进去处理判断
  117.         {
  118.            if(uiRcregTotal
  119.            {
  120.                uiRcregTotal=0;
  121.            }
  122.            else
  123.            {
  124.                uiRcregTotalTemp=uiRcregTotal; //把接收到的总数据传递给一个中间变量,在主函数那边处理这个中间变量
  125.                ucReceiveFlag=1;            //通知主程序接收成功
  126.                            ES=0;      // 禁止接收中断,等主函数处理完接收的数据后再打开串口中断,避免在处理串口数据期间受到串口数据的中断干扰。 
  127.            }
  128.         }
  129.         else if(uiRcregTotal>=const_rc_size)  //超过缓冲区
  130.         {
  131.            uiRcregTotal=0;   
  132.         }  
  133.  
  134.  
  135.      
  136.     
  137.    }
  138.    else    //如果不是串口接收中断,那么必然是串口发送中断,及时清除发送中断的标志,否则一直发送中断
  139.    {
  140.         TI = 0; 
  141.    }
  142.                                                          
  143. }  
  144.  
  145. void usart_service(void)  //串口接收服务程序,在main函数里
  146. {
  147.   //加了static关键字后,此局部变量不会每次进来函数都初始化一次,这样有可能减少了一点指令消耗的时间。
  148.     static unsigned long ulReceiveData10000; //定义成long类型,是为了方便后面换算的乘法运算,让它不会溢出而出错。
  149.     static unsigned long ulReceiveData1000;
  150.     static unsigned long ulReceiveData100;
  151.     static unsigned long ulReceiveData10;
  152.     static unsigned long ulReceiveData1;
  153.  
  154.  
  155.     if(ucReceiveFlag==1)  //说明有数据接收成功,进入数据处理分析
  156.     {
  157.        ulReceiveData10000=0;
  158.        ulReceiveData1000=0;
  159.        ulReceiveData100=0;
  160.        ulReceiveData10=0;
  161.        ulReceiveData1=0;
  162.  
  163. /* 注释二:
  164. * 根据协议,倒数第9,10,11,12,13,14为有效的ASCII码数字,其中倒数第11位为固定的小数点,因此省略不写。
  165. */
  166.  
  167.        if(ucRcregBuf[uiRcregTotalTemp-9]>=0x30)  
  168.        {
  169.           ulReceiveData1=ucRcregBuf[uiRcregTotalTemp-9]-0x30; //接收到的ASCII码数字减去0x30变成实际数值.
  170.        }
  171.  
  172.        if(ucRcregBuf[uiRcregTotalTemp-10]>=0x30)  
  173.        {
  174.           ulReceiveData10=ucRcregBuf[uiRcregTotalTemp-10]-0x30;
  175.           ulReceiveData10=ulReceiveData10*10;
  176.        }
  177.  
  178.        if(ucRcregBuf[uiRcregTotalTemp-12]>=0x30)  
  179.        {
  180.           ulReceiveData100=ucRcregBuf[uiRcregTotalTemp-12]-0x30;
  181.           ulReceiveData100=ulReceiveData100*100;
  182.        }
  183.  
  184.        if(ucRcregBuf[uiRcregTotalTemp-13]>=0x30)  
  185.        {
  186.           ulReceiveData1000=ucRcregBuf[uiRcregTotalTemp-13]-0x30;
  187.           ulReceiveData1000=ulReceiveData1000*1000;
  188.        }
  189.  
  190.        if(ucRcregBuf[uiRcregTotalTemp-14]>=0x30)  
  191.        {
  192.           ulReceiveData10000=ucRcregBuf[uiRcregTotalTemp-14]-0x30;
  193.           ulReceiveData10000=ulReceiveData10000*10000;
  194.        }
  195.  
  196.  
  197.        ulWeightCurrent=ulReceiveData10000+ulReceiveData1000+ulReceiveData100+ulReceiveData10+ulReceiveData1;
  198.        ucWd1Part1Update=1; //更新显示
  199.  
  200.        uiRcregTotalTemp=0;  //清零实际接收到的字节数的中间变量
  201.        uiRcregTotal=0;  //清零实际接收到的字节数
  202.        ucReceiveFlag=0;  //清零完成标志
  203.  
  204.            ES = 1;            // 允许接收中断
  205.     }            
  206. }
  207.  
  208.  
  209.  
  210. void display_service() //显示的窗口菜单服务程序
  211. {
  212.   //加了static关键字后,此局部变量不会每次进来函数都初始化一次,这样有可能减少了一点指令消耗的时间。
  213.             static unsigned char ucTemp5;   //中间过渡变量
  214.             static unsigned char ucTemp4;   //中间过渡变量
  215.             static unsigned char ucTemp3;   //中间过渡变量
  216.             static unsigned char ucTemp2;   //中间过渡变量
  217.             static unsigned char ucTemp1;   //中间过渡变量
  218.  
  219.  
  220.             if(ucWd1Part1Update==1)  //更新显示
  221.             {
  222.                ucWd1Part1Update=0;  //及时清零标志,避免一直进来扫描
  223.  
  224.               //先分解数据用来显示每一位
  225.                ucTemp5=ulWeightCurrent%100000/10000;  
  226.                ucTemp4=ulWeightCurrent%10000/1000;     
  227.                ucTemp3=ulWeightCurrent%1000/100;
  228.                ucTemp2=ulWeightCurrent%100/10;
  229.                ucTemp1=ulWeightCurrent%10;
  230.  
  231.                ucDigDot3=1;  //显示第3位数码管的小数点,实际数据带2位小数点。
  232.  
  233.                ucDigShow8=10;  //没有用到第8位数码管,因此显示无。10代表显示空。
  234.                ucDigShow7=10;  //没有用到第7位数码管,因此显示无。10代表显示空。
  235.                ucDigShow6=10;  //没有用到第6位数码管,因此显示无。10代表显示空。
  236.  
  237.                if(ulWeightCurrent<10000)   
  238.                {
  239.                   ucDigShow5=10;  //如果小于1000,千位显示无
  240.                }
  241.                else
  242.                {
  243.                   ucDigShow5=ucTemp5;  //第5位数码管要显示的内容
  244.                }
  245.   
  246.  
  247.                if(ulWeightCurrent<1000)   
  248.                {
  249.                   ucDigShow4=10;  //如果小于1000,千位显示无
  250.                }
  251.                else
  252.                {
  253.                   ucDigShow4=ucTemp4;  //第4位数码管要显示的内容
  254.                }
  255.  
  256.                 //因为带2位小数点,因此最前面3位数据都是有效数,必然要显示,不要判断去0的空显示处理。
  257.                 ucDigShow3=ucTemp3;  //第3位数码管要显示的内容
  258.                 ucDigShow2=ucTemp2;  //第2位数码管要显示的内容
  259.                 ucDigShow1=ucTemp1;  //第1位数码管要显示的内容
  260.             }
  261.  
  262.      
  263. }
  264.  
  265.  
  266.  
  267.  
  268. void display_drive()  
  269. {
  270.    //以下程序,如果加一些数组和移位的元素,还可以压缩容量。但是鸿哥追求的不是容量,而是清晰的讲解思路
  271.    switch(ucDisplayDriveStep)
  272.    { 
  273.       case 1:  //显示第1位
  274.            ucDigShowTemp=dig_table[ucDigShow1];
  275.                    if(ucDigDot1==1)
  276.                    {
  277.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  278.                    }
  279.            dig_hc595_drive(ucDigShowTemp,0xfe);
  280.                break;
  281.       case 2:  //显示第2位
  282.            ucDigShowTemp=dig_table[ucDigShow2];
  283.                    if(ucDigDot2==1)
  284.                    {
  285.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  286.                    }
  287.            dig_hc595_drive(ucDigShowTemp,0xfd);
  288.                break;
  289.       case 3:  //显示第3位
  290.            ucDigShowTemp=dig_table[ucDigShow3];
  291.                    if(ucDigDot3==1)
  292.                    {
  293.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  294.                    }
  295.            dig_hc595_drive(ucDigShowTemp,0xfb);
  296.                break;
  297.       case 4:  //显示第4位
  298.            ucDigShowTemp=dig_table[ucDigShow4];
  299.                    if(ucDigDot4==1)
  300.                    {
  301.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  302.                    }
  303.            dig_hc595_drive(ucDigShowTemp,0xf7);
  304.                break;
  305.       case 5:  //显示第5位
  306.            ucDigShowTemp=dig_table[ucDigShow5];
  307.                    if(ucDigDot5==1)
  308.                    {
  309.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  310.                    }
  311.            dig_hc595_drive(ucDigShowTemp,0xef);
  312.                break;
  313.       case 6:  //显示第6位
  314.            ucDigShowTemp=dig_table[ucDigShow6];
  315.                    if(ucDigDot6==1)
  316.                    {
  317.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  318.                    }
  319.            dig_hc595_drive(ucDigShowTemp,0xdf);
  320.                break;
  321.       case 7:  //显示第7位
  322.            ucDigShowTemp=dig_table[ucDigShow7];
  323.                    if(ucDigDot7==1)
  324.                    {
  325.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  326.            }
  327.            dig_hc595_drive(ucDigShowTemp,0xbf);
  328.                break;
  329.       case 8:  //显示第8位
  330.            ucDigShowTemp=dig_table[ucDigShow8];
  331.                    if(ucDigDot8==1)
  332.                    {
  333.                       ucDigShowTemp=ucDigShowTemp|0x80;  //显示小数点
  334.                    }
  335.            dig_hc595_drive(ucDigShowTemp,0x7f);
  336.                break;
  337.    }
  338.    ucDisplayDriveStep++;
  339.    if(ucDisplayDriveStep>8)  //扫描完8个数码管后,重新从第一个开始扫描
  340.    {
  341.      ucDisplayDriveStep=1;
  342.    }
  343.  
  344. }
  345.  
  346. //数码管的74HC595驱动函数
  347. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
  348. {
  349.    unsigned char i;
  350.    unsigned char ucTempData;
  351.    dig_hc595_sh_dr=0;
  352.    dig_hc595_st_dr=0;
  353.    ucTempData=ucDigStatusTemp16_09;  //先送高8位
  354.    for(i=0;i<8;i++)
  355.    { 
  356.          if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  357.          else dig_hc595_ds_dr=0;
  358.          dig_hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器
  359.          delay_short(1); 
  360.          dig_hc595_sh_dr=1;
  361.          delay_short(1);
  362.          ucTempData=ucTempData<<1;
  363.    }
  364.    ucTempData=ucDigStatusTemp08_01;  //再先送低8位
  365.    for(i=0;i<8;i++)
  366.    { 
  367.          if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  368.          else dig_hc595_ds_dr=0;
  369.          dig_hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器
  370.          delay_short(1); 
  371.          dig_hc595_sh_dr=1;
  372.          delay_short(1);
  373.          ucTempData=ucTempData<<1;
  374.    }
  375.    dig_hc595_st_dr=0;  //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
  376.    delay_short(1); 
  377.    dig_hc595_st_dr=1;
  378.    delay_short(1);
  379.    dig_hc595_sh_dr=0;    //拉低,抗干扰就增强
  380.    dig_hc595_st_dr=0;
  381.    dig_hc595_ds_dr=0;
  382. }
  383.  
  384. //LED灯的74HC595驱动函数
  385. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
  386. {
  387.    unsigned char i;
  388.    unsigned char ucTempData;
  389.    hc595_sh_dr=0;
  390.    hc595_st_dr=0;
  391.    ucTempData=ucLedStatusTemp16_09;  //先送高8位
  392.    for(i=0;i<8;i++)
  393.    { 
  394.          if(ucTempData>=0x80)hc595_ds_dr=1;
  395.          else hc595_ds_dr=0;
  396.          hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器
  397.          delay_short(1); 
  398.          hc595_sh_dr=1;
  399.          delay_short(1);
  400.          ucTempData=ucTempData<<1;
  401.    }
  402.    ucTempData=ucLedStatusTemp08_01;  //再先送低8位
  403.    for(i=0;i<8;i++)
  404.    { 
  405.          if(ucTempData>=0x80)hc595_ds_dr=1;
  406.          else hc595_ds_dr=0;
  407.          hc595_sh_dr=0;     //SH引脚的上升沿把数据送入寄存器
  408.          delay_short(1); 
  409.          hc595_sh_dr=1;
  410.          delay_short(1);
  411.          ucTempData=ucTempData<<1;
  412.    }
  413.    hc595_st_dr=0;  //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
  414.    delay_short(1); 
  415.    hc595_st_dr=1;
  416.    delay_short(1);
  417.    hc595_sh_dr=0;    //拉低,抗干扰就增强
  418.    hc595_st_dr=0;
  419.    hc595_ds_dr=0;
  420. }
  421.  
  422.  
  423.  
  424. void T0_time() interrupt 1
  425. {
  426.   TF0=0;  //清除中断标志
  427.   TR0=0; //关中断
  428.  
  429.  
  430.   display_drive();  //数码管字模的驱动函数
  431.  
  432.   TH0=0xfe;   //重装初始值(65535-500)=65035=0xfe0b
  433.   TL0=0x0b;
  434.   TR0=1;  //开中断
  435. }
  436.  
  437.  
  438. void delay_short(unsigned int uiDelayShort) 
  439. {
  440.    unsigned int i;  
  441.    for(i=0;i
  442.    {
  443.      ;   //一个分号相当于执行一条空语句
  444.    }
  445. }
  446.  
  447. void delay_long(unsigned int uiDelayLong)
  448. {
  449.    unsigned int i;
  450.    unsigned int j;
  451.    for(i=0;i
  452.    {
  453.       for(j=0;j<500;j++)  //内嵌循环的空指令数量
  454.           {
  455.              ; //一个分号相当于执行一条空语句
  456.           }
  457.    }
  458. }
  459.  
  460. void initial_myself()  //第一区 初始化单片机
  461. {
  462.  
  463.   beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
  464.   led_dr=0;  //关闭独立LED灯 
  465.   hc595_drive(0x00,0x00);  //关闭所有经过另外两个74HC595驱动的LED灯
  466.  
  467.   TMOD=0x01;  //设置定时器0为工作方式1
  468.   TH0=0xfe;   //重装初始值(65535-500)=65035=0xfe0b
  469.   TL0=0x0b;
  470.  
  471.   //配置串口
  472.   SCON=0x50;
  473.   TMOD=0X21;
  474.  
  475. /* 注释三:
  476. * 为了保证串口中断接收的数据不丢失,必须设置IP = 0x10,相当于把串口中断设置为最高优先级,
  477. * 这个时候,串口中断可以打断任何其他的中断服务函数实现嵌套,
  478. */
  479.   IP =0x10;  //把串口中断设置为最高优先级,必须的。
  480.  
  481.   TH1=TL1=-(11059200L/12/32/9600);  //串口波特率为9600。
  482.   TR1=1;
  483. }
  484.  
  485. void initial_peripheral() //第二区 初始化外围
  486. {
  487.  
  488.    ucDigDot8=0;   //初始化小数点全部不显示
  489.    ucDigDot7=0;  
  490.    ucDigDot6=0; 
  491.    ucDigDot5=0;  
  492.    ucDigDot4=0; 
  493.    ucDigDot3=0;  
  494.    ucDigDot2=0;
  495.    ucDigDot1=0;
  496.  
  497.    EA=1;     //开总中断
  498.    ES=1;     //允许串口中断
  499.    ET0=1;    //允许定时中断
  500.    TR0=1;    //启动定时中断
  501. }
  502.  
 
总结陈词:
    前面我在第48节里讲过用ds1302做的时钟程序,但是后来很多网友建议,为了方便初学者学习编程思路,我应该用单片机定时器做一个时钟程序。因此,我决定下一节讲这方面的内容。欲知详情,请听下回分解----用单片机内部定时器做一个时钟。
关键字:单片机  关键字  有效数据串 引用地址:第88节:单片机靠关键字快速截取有效数据串

上一篇:第89节:用单片机内部定时器做一个时钟
下一篇:第八十七节:郑文显捐赠的工控项目源代码

推荐阅读最新更新时间:2024-03-16 14:47

PIC单片机实现LCD1602滚动显示字符
初始学习单片机,LCD1602的滚动只能实现2行同时移动,就改了哈,写了个滚动的程序。在测试的时候发现了一个问题,在学习板上能正常运行的代码放到Proteus上问题出错,不能正常显示,最后在网上搜索原因时发现可能是Proteus仿真时对端口有要求,最后将商品修改到PORTB和PORTD就正常了。 这是显示效果https://v.youku.com/v_show/id_XNDI5MDEwOTg4NA==.html?spm=a2h3j.8428770.3416059.1 LCD1602.c //--------------------------------------------------------------- /
[单片机]
PIC<font color='red'>单片机</font>实现LCD1602滚动显示字符<font color='red'>串</font>
基于AT89C51单片机直流电机PWM调速程序分享
这是一款AT89C51单片机直流电机PWM调速程序,程序可以直接用于AT89C52、AT89S51、AT89S51,STC89C51、STC89C52单片机中,单片机晶振采用11.0592M,直流电机由L298集成电路控制,产生的PWM的频率约为91Hz。L298各引脚已在程序中标明,原理图大家可以自己画一下,这里就不具给出。下面是源程序。 #include[reg52.h》//注意请把‘ [ ’换成 “《”,下同。否则编译时会出错。 #include [intrins.h》// #define uchar unsigned char #define uint unsigned int sbit en1=P2^
[单片机]
基于AT89C51<font color='red'>单片机</font>直流电机PWM调速程序分享
单片机I/O口推挽输出与开漏输出的区别
推挽输出:可以输出高,低电平,连接数字器件; 开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内). 推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止. 我们先来说说集电极开路输出的结构。集电极开路输出的结构如图1所示,右边的那个三极管集电极什么都不接,所以叫做集电极开路(左边的三极管为反相之用,使输入为“0”时,输出也为“0”)。对于图1,当左端的输入为“0”时,前面的三极管截止(即集电极C跟发射极E之间相当于断开),所以5V电源通过1K电阻加到右边的三极管上,右边的三极管导通(即相当于一个开关闭合
[单片机]
<font color='red'>单片机</font>I/O口推挽输出与开漏输出的区别
PIC单片机与触摸屏串行通信的MODBUS协议实现
摘要:介绍一种在PIC单片机与触摸屏之间采用Modbus协议实现异步串行通信的方法。简单介绍了Modbus通信协议,给出了硬件电路连接图、程序流程图以及用PIC单片机C语言编写的部分通信程序。实际使用证明该方法数据传输稳定可靠,并提供了良好的人机交互环境。 关键词:触摸屏 PIC单片机 Modbus协议 通信 工控中经常需要观察系统的运行状态或者修改运行参数。触摸屏能够直观、生动地显示运行参数和运行状态,而且通过触摸屏画面可以直接修改系统运行参数,人机交互性好。单片机广泛应用于工控领域中,与触摸屏配合,可组成良好的人机交互环境。触摸屏和单片机通信,需要根据触摸屏采用的通信协议为单片机编写相应的通信程序。Modbus协议是美国M
[单片机]
单片机的位数(转载)
指CPU处理的数据的宽度,参与运算的寄存器的数据长度? 是指单片机一次处理数据量位数的多少。 一般8位单片机是低档的单片机,16位的是中档的单片机,32位的是高档单片机。 如果总线宽度与CPU一次处理的数据宽度相同,则这个宽度就是所说的单片机位数。 如果总线宽度与CPU一次处理的数据宽度不同: 1)总线宽度小于CPU一次处理的数据宽度,则以CPU的数据宽度定义单片机的位数,但称为准多少位。比如著名的Intel 8088,CPU是16位但总线是8位,所以它是准16位。 2)总线宽度小于CPU一次处理的数据宽度,则以CPU的数据宽度定义单片机的位数。 位宽不是指总线宽度,也不是存储器的宽度,像51单片机的地址总线是
[单片机]
单片机C语言编程定时器的几种表达方式
单片机C语言编程中,定时器的初值对于初学者真的是比较不好计算,因此我总结了以下几种方法。 第1种方法: #define FOSC 11059200L //晶振的频率 #define TIMS (65536-FOSC/12/1000) //12T mode 对于8051系列单片机通用 //#define TIMS (65536-FOSC/1000) //1T mode STC单片机可以用这个 unsigned int timer0_tick; int timer0_count; void Timer0(void) interrupt 1 using 1 //定时器0中断外理 { TL0=TIMS; TH0=TIM
[单片机]
一种基于低功耗单片机的抗干扰电源
  引 言   近年来,各种低功耗单片机在各类仪表中得到了广泛应用,特别是89C51/2单片机以其优良的性能、低廉的价格和标准的降低功耗特性以及片内存储器的快速可擦写性等赢得了广大用户。但各种测试仪表常常要求能方便地携带使用,因此仪表电源常采用专用电瓶。一般专用电瓶电压为12V(或12V 串联组成) ,而以89C51/2单片机组成的应用系统其电源电压Vcc要求在5×(1±0.1)V 范围内,有些仪表使用环境常常较恶劣,干扰因素较多。因此,要使单片机系统可靠工作,一套抗干扰能力强的供电电路显得十分重要。   1  抗干扰电源电路设计与分析    抗干扰电源电路由两部分组成,如图1 所示。以MAX638 为中心组成直流降压电路,
[单片机]
盛群推出8位精简A/D型MCU,面向智能控制产品
盛群半导体日前新推出8位精简型MCU,内建9位ADC,型号分别是HT46R48及HT46R48E,适用于家电、车用周边及其它智能控制的产品。 HT46R48采用盛群半导体的8位微控制器核心,工作频率最高可达8MHz;具有2K Word的程序内存(Program Memory)及实用的周边电路,例如内置4信道的9位模拟/数字转换器可用以获取外界环境的信号如温湿度等;具有脉冲宽度调制功能(PWM)及19个输入/输出接脚,用于控制马达转速及各种开关;具有PFD(Programmable Frequency Divider)功能可产生音频信号等,可以较容易地构成一个完整的微控制系统。 HT46R48E在一个封装内置入二颗集成电路:一颗
[新品]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved