单片机定时发送数据_51单片机定时发送数据 - CSDN
  • 一、使用proteus绘制简单的电路图,用于后续仿真 二、编写程序 .../******************************************************************************************************************** ...

    proteus虚拟串口的实现:https://mp.csdn.net/console/editor/html/107251649

    一、使用proteus绘制简单的电路图,用于后续仿真

     

    二、编写程序

    /********************************************************************************************************************
    ----	@Project:	USART
    ----	@File:	main.c
    ----	@Edit:	ZHQ
    ----	@Version:	V1.0
    ----	@CreationTime:	20200713
    ----	@ModifiedTime:	20200713
    ----	@Description:	
    ----	波特率是:9600 。
    ----	按一次按键S1,单片机就往上位机发送以下一串数据:eb 00 55 01 00 00 00 00 41
    ----	单片机:AT89C52
    ********************************************************************************************************************/
    #include "reg52.h"
    /*——————宏定义——————*/
    #define FOSC 11059200L
    #define BAUD 9600
    #define T1MS (65536-FOSC/12/500)   /*0.5ms timer calculation method in 12Tmode*/
    
    #define const_send_size 20	/*串口发送数据的缓冲区数组大小*/
    
    #define const_voice_short 40	/*蜂鸣器短叫的持续时间*/
    #define const_key_time1 20	/*按键去抖动延时的时间*/
    
    /*——————变量函数定义及声明——————*/
    /*蜂鸣器的驱动IO口*/
    sbit BEEP = P2^7;
    /*LED*/
    sbit LED = P3^5;
    
    /*按键*/
    sbit Key_S1 = P0^0;
    sbit Key_Gnd = P0^4;
    
    unsigned char ucSendregBuf[const_send_size];	/*接收串口中断数据的缓冲区数组*/
    
    /*为串口计时器多增加一个原子锁,作为中断与主函数共享数据的保护*/
    unsigned char ucVoiceLock = 0;	/*蜂鸣器鸣叫的原子锁*/
    
    unsigned int uiVoiceCnt = 0;	/*蜂鸣器鸣叫的持续时间计数器*/
    
    unsigned char ucKeySec = 0;	/*被触发的按键编号*/
    unsigned int uiKeyTimeCnt1 = 0;	/*按键去抖动延时计数器*/
    unsigned char ucKeyLock1 = 0;	/*按键触发后自锁的变量标志*/
    /**
    * @brief  定时器0初始化函数
    * @param  无
    * @retval 初始化T0
    **/
    void Init_T0(void)
    {
    	TMOD = 0x01;                    /*set timer0 as mode1 (16-bit)*/
    	TL0 = T1MS;                     /*initial timer0 low byte*/
    	TH0 = T1MS >> 8;                /*initial timer0 high byte*/
    }
    
    /**
    * @brief  串口初始化函数
    * @param  无
    * @retval 初始化T0
    **/
    void Init_USART(void)
    {
    	SCON = 0x50;
    	TMOD = 0x21;                    
    	TH1=TL1=-(FOSC/12/32/BAUD);
    }
    
    /**
    * @brief  外围初始化函数
    * @param  无
    * @retval 初始化外围
    * 让数码管显示的内容转移到以下几个变量接口上,方便以后编写更上一层的窗口程序。
    * 只要更改以下对应变量的内容,就可以显示你想显示的数字。
    **/
    void Init_Peripheral(void)
    {
    	ET0 = 1;/*允许定时中断*/
    	TR0 = 1;/*启动定时中断*/
    	TR1 = 1;
    	ES = 1;	/*允许串口中断*/
    	EA = 1;/*开总中断*/  
    }
    
    /**
    * @brief  初始化函数
    * @param  无
    * @retval 初始化单片机
    **/
    void Init(void)
    {
    	LED  = 0;
    	BEEP = 1;
    	Key_Gnd = 0;
    	Init_T0();
    	Init_USART();
    }
    /**
    * @brief  延时函数
    * @param  无
    * @retval 无
    **/
    void Delay_Long(unsigned int uiDelayLong)
    {
       unsigned int i;
       unsigned int j;
       for(i=0;i<uiDelayLong;i++)
       {
          for(j=0;j<500;j++)  /*内嵌循环的空指令数量*/
              {
                 ; /*一个分号相当于执行一条空语句*/
              }
       }
    }
    /**
    * @brief  延时函数
    * @param  无
    * @retval 无
    **/
    void Delay_Short(unsigned int uiDelayShort)
    {
      unsigned int i;
      for(i=0;i<uiDelayShort;i++)
      {
    		 ; /*一个分号相当于执行一条空语句*/
      }
    }
    /**
    
    * @brief  串口发送函数
    * @param  ucSendData
    * @retval 在发送一串数据中,每个字节之间必须添加一个延时,用来等待串口发送完成。
    * 不增加延时,单单靠发送完成标志位来判断还是容易出错,在51,PIC单片机中都是这么做。
    * 在stm32单片机中,可以不增加延时,直接靠单片机自带的标志位来判断就很可靠。
    **/
    void eusart_send(unsigned char ucSendData)
    {
    	ES = 0;	/*关串口中断*/	
    	TI = 0;	/*清零串口发送完成中断请求标志*/
    	SBUF = ucSendData;	/*发送一个字节*/
    
    	Delay_Short(400);	/*每个字节之间的延时。延时的大小请根据实际项目来调整*/
    
    	TI = 0;	/*清零串口发送完成中断请求标志*/	
    	ES = 1;	/*允许串口中断*/	
    }
    
    /**
    * @brief  按键扫描函数
    * @param  无
    * @retval 放在定时中断里
    **/
    void key_scan(void)
    {
    	if(Key_S1 == 1)	/*IO是高电平,说明按键没有被按下,这时要及时清零一些标志位*/
    	{
    		ucKeyLock1 = 0;	/*按键自锁标志清零*/
    		uiKeyTimeCnt1 = 0;	/*按键去抖动延时计数器清零*/
    	}
    	else if(ucKeyLock1 == 0)	/*有按键按下,且是第一次被按下*/
    	{
    		uiKeyTimeCnt1 ++;	/*累加定时中断次数*/
    		if(uiKeyTimeCnt1 > const_key_time1)
    		{
    			uiKeyTimeCnt1 = 0;
    			ucKeyLock1 = 1;	/*自锁按键置位,避免一直触发*/
    			ucKeySec = 1;
    		}
    	}
    }
    
    /**
    * @brief  按键服务的应用程序
    * @param  无
    * @retval 无
    **/
    void key_service(void)
    {
    	unsigned int i;
    	switch(ucKeySec)	/*按键服务状态切换*/
    	{
    		case 1:
    			ucSendregBuf[0] = 0xeb;	/*把准备发送的数据放入发送缓冲区*/
    			ucSendregBuf[1] = 0x00;
    			ucSendregBuf[2] = 0x55;
    			ucSendregBuf[3] = 0x01;
    			ucSendregBuf[4] = 0x00;
    			ucSendregBuf[5] = 0x00;
    			ucSendregBuf[6] = 0x00;
    			ucSendregBuf[7] = 0x00;
    			ucSendregBuf[8] = 0x41;
    			for(i = 0; i <= 8; i ++)
    			{
    				eusart_send(ucSendregBuf[i]);	/*发送一串数据给上位机*/
    			}
    
    			ucVoiceLock = 1;	/*原子锁加锁,保护中断与主函数的共享数据*/
    			uiVoiceCnt = const_voice_short;	/*按键声音触发,滴一声就停。*/
    			ucVoiceLock = 0;	/*原子锁解锁*/
    			ucKeySec = 0;	/*响应按键服务处理程序后,按键编号清零,避免一致触发*/
    			break;
    	}
    }
    
    /**
    * @brief  定时器0中断函数
    * @param  无
    * @retval 无
    **/
    void ISR_T0(void)	interrupt 1
    {
    	TF0 = 0;  /*清除中断标志*/
    	TR0 = 0; /*关中断*/
    	/* 
    	* 此处多增加一个原子锁,作为中断与主函数共享数据的保护
    	*/
    	if(ucVoiceLock == 0)	/*原子锁判断*/
    	{
    		if(uiVoiceCnt != 0)
    		{
    			uiVoiceCnt --;
    			BEEP = 0;
    		}
    		else
    		{
    			;
    			BEEP = 1;
    		}		
    	}
    
    	key_scan();
    
    	TL0 = T1MS;                     /*initial timer0 low byte*/
    	TH0 = T1MS >> 8;                /*initial timer0 high byte*/
      	TR0 = 1; /*开中断*/	
    }
    
    /**
    * @brief  串口接收数据中断
    * @param  无
    * @retval 无
    **/
    void usart_receive(void)	interrupt 4
    {
    	if(RI == 1)
    	{
    		RI = 0;
    	}
    	else
    	{
    		TI = 0;
    	}
    }
    
    /*————————————主函数————————————*/
    /**
    * @brief  主函数
    * @param  无
    * @retval 实现LED灯闪烁
    **/
    void main()
    {
    	/*单片机初始化*/
    	Init();
    	/*延时,延时时间一般是0.3秒到2秒之间,等待外围芯片和模块上电稳定*/
    	Delay_Long(100);
    	/*单片机外围初始化*/	
    	Init_Peripheral();
    	while(1)
    	{
    		key_service();	/*按键服务的应用程序*/
    	}
    }
    
    

    三、仿真实现

    51单片机实现通过串口用delay延时方式发送一串数据

     

    展开全文
  • 单片机自动向PC机发送数据的方法

    千次阅读 2009-08-06 22:20:00
    单片机通过串口向PC机发送数据一般是通过串口中断服务程序来实现,也就是说一般写在中断函数里。例如: #include /*主程序*/void main (void){ SCON = 0x50; /* SCON: 模式 1, 8-bit

    单片机通过串口向PC机发送数据一般是通过串口中断服务程序来实现,也就是说一般写在中断函数里。例如:

     

     

    以上是一个单片机与PC机互发的程序,是通过PC机向缓冲区SBUF发送数据来实现打开串口中断,但要单片机自主的向PC机发送数据而不需PC机操作,这样就不能实现,因此可以将串口中断另外写成一个函数,在main()函数中调用:
     
    展开全文
  • 一、定时器/计数器概述 1.相关寄存器 (1)TMOD——方式控制寄存器  GATE C/T M1 M0(T1 T0)  GATE=0,定时器/计数器的运行由内部TR0/TR1置1选通; ... GATE=1,定时器/计数器的运行由外部中断(I引脚P3.2/P3.3...

    一、定时器/计数器概述

    1.相关寄存器

    (1)TMOD——方式控制寄存器

        GATE C/T M1 M0(T1 T0)

      GATE=0,定时器/计数器的运行由内部TR0/TR1置1选通;

      GATE=1,定时器/计数器的运行由外部中断(I引脚P3.2/P3.3)和内部TR0/TR1 置1共同选通;

      C/T=1,外部脉冲计数(P3.4/P3.5),=0,定时器内部脉冲计时(晶振的倒数乘以12,所得值与定时器初值的乘积为总时间);

      M1/M0,00,模式0——TH高8位和TL低5位作用,2^13;

        01,模式1——TH高8位和TL低8位作用,2^16(常用);

        02,模式2——TH/TL中都放入初值,当TL中的值溢出后TH中的初值重新装入TL中,持续计数,2^8(常用与串行口波特率发生器);

        03,模式3——TL使用T0的状态位,同模式0/1,2^8;TH使用T1的状态位和中断(只适用于T0)

    (2)TCON——中断控制寄存器

        TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0

      TF1/TF0 溢出时置1;

      TR1/TR0置1开始计数

    2.初始化程序

    TMOD=0x06;    //初始化T0作为方式2,外部计数,0000 0110;
    TL0=0X056;    //设置初值
    TH0=0X0FF;    //设置初值
    TR0=1;    //开始计数

    3.初值计算

    定时:X=MAX-定时值/T

    计数:X=MAX-计数值

    4.实例

     

    转载于:https://www.cnblogs.com/Sonny-xby/p/9983499.html

    展开全文
  • 项目工程:keil2 测试单片机:IAP12c5a60s2(跟普通的stc89c51大概一样) 晶振:22.1184MHZ 本程序在stc12c5a60s2下测试通过,虽然5分,但是让你一目了然的理解串口...定时器0每秒钟通过串口发送一次字符串数据到PC。
  • 51单片机串口通信的发送与接收(转)

    万次阅读 多人点赞 2013-02-20 15:01:56
    51单片机的串口,是个全双工的串口,发送数据的同时,还可以接收数据。 当串行发送完毕后,将在标志位 TI 置 1,同样,当收到了数据后,也会在 RI 置 1。 无论 RI 或 TI 出现了 1,只要串口中断处于开放状态,...

    http://club.topsage.com/thread-2765635-1-1.html


    51单片机的串口,是个全双工的串口,发送数据的同时,还可以接收数据。
    当串行发送完毕后,将在标志位 TI 置 1,同样,当收到了数据后,也会在 RI 置 1。
    无论 RI 或 TI 出现了 1,只要串口中断处于开放状态,单片机都会进入串口中断处理程序。
    在中断程序中,要区分出来究竟是发送引起的中断,还是接收引起的中断,然后分别进行处理。
    看到过一些书籍和文章,在串口收、发数据的处理方法上,很多人都有不妥之处。
    接收数据时,基本上都是使用“中断方式”,这是正确合理的。
    即:每当收到一个新数据,就在中断函数中,把 RI 清零,并用一个变量,通知主函数,收到了新数据。
    发送数据时,很多的程序都是使用的“查询方式”,就是执行 while(TI ==0); 这样的语句来等待发送完毕。
    这时,处理不好的话,就可能带来问题。
    看了一些网友编写的程序,发现有如下几条容易出错:
    1.有人在发送数据之前,先关闭了串口中断!等待发送完毕后,再打开串口中断。
    这样,在发送数据的等待期间内,如果收到了数据,将不能进入中断函数,也就不会保存的这个新收到的数据。
    这种处理方法,就会遗漏收到的数据。
    2.有人在发送数据之前,并没有关闭串口中断,当 TI = 1 时,是可以进入中断程序的。
    但是,却在中断函数中,将 TI 清零!
    这样,在主函数中的while(TI ==0);,将永远等不到发送结束的标志。
    3.还有人在中断程序中,并没有区分中断的来源,反而让发送引起的中断,执行了接收中断的程序。
    对此,做而论道发表自己常用的方法:
    接收数据时,使用“中断方式”,清除 RI 后,用一个变量通知主函数,收到新数据。
    发送数据时,也用“中断方式”,清除 TI 后,用另一个变量通知主函数,数据发送完毕。
    这样一来,收、发两者基本一致,编写程序也很规范、易懂。
    更重要的是,主函数中,不用在那儿死等发送完毕,可以有更多的时间查看其它的标志。


    实例:
    求一个PC与单片机串口通信的程序,要求如下:
    1、如果在电脑上发送以$开始的字符串,则将整个字符串原样返回(字符串长度不是固定的)。
    2、如果接收到1,则将P10置高电平,接收到0,P10置低电平。(用来控制一个LED)
    单片机是STC89C52RC/晶振11.0592/波特率要求是9600或4800。谢谢!
    问题补充:可能会将【$ABCD,123456,987654ccc,aasdasd,aaaa,sssd,4D】这样的字符串(字符串长度约为50-150个字符)传送给单片机,只能能原样返回。

    最佳答案:
    下列程序,已经调试成功。
    #include <REG52.H>
    sbit LED = P1^0;
    unsigned char UART_buff;
    bit New_rec = 0, Send_ed = 1, Money = 0;
    //----------------------------------------------
    void main (void)
    {
        SCON = 0x50;   //串口方式1, 8-n-1, 允许接收.
        TMOD = 0x20;   //T1方式2
        TH1 = 0xFD;    [url=]//9600bps@11.0592MHz[/url]
        TL1 = 0xFD;
        TR1 = 1;                        
        ES  = 1;       //开中断.
        EA  = 1;
        while(Money == 0);    //等着交费,呵呵,等着接收$.
        while(1)  { 
          if ((New_rec == 1) && (Send_ed == 1))  {  //如果收到新数据及发送完毕
            SBUF = UART_buff; //那就发送.
            New_rec = 0;
            Send_ed = 0;
        } }
    }
    //----------------------------------------------
    void ser_int (void) interrupt 4 
    {
        if(RI == 1) {  //如果收到.
          RI = 0;      //清除标志.
          New_rec = 1;
          UART_buff = SBUF;  //接收.
          if(UART_buff == '1')  LED = 1;
          if(UART_buff == '0')  LED = 0;
          if(UART_buff == '$')  Money = 1;
        }
        else  {        //如果送毕.
          TI = 0;      //清除标志.
          Send_ed = 1;
        }
    } 
    //----------------------------------------------
    

    http://bbs.ednchina.com/BLOG_ARTICLE_3007162.HTM

    串口接收程序是基于串口中断的,单片机的串口每次接收到一字节数据产生一次中断,然后再读取某个寄存器就可以得到串口接收的数据了。然而在实际应用当中,基本上不会有单字节接收的情况。一般都是基于一定串口通信协议的多字节通信。在422或者485通信中,还可能是一个主机(一般是计算机)带多个从机(相应的有单片机的板卡)。这就要求我们的单片机能够在连续接收到的串口数据序列中识别出符合自己板卡对应的通信协议,来进行控制操作,不符合则不进行任何操作。简而言之就是,单片机要在一串数据中找到符合一定规律的几个字节的数据。

     

            先来说下怎样定串口协议吧。这个协议指的不是串口底层的协议,而是前面提到的数据帧协议。一般都是有帧头(2~3个字节吧),数据(长度根据需要),结束位(1位,有时候设计成校验字节,最简单的校验也就是前面所有数据求和)。

            比如0xaa 0x55 +(数据部分省略)+校验和(除了aa 55 之外数据的和),如果要是多板卡的话有时候还要在帧头后面加一个板选字节(相当于3字节帧头了)。

     

           第一次写串口接收程序的时候,我首先想到的就是定义一个全局变量(实际上最好是定义局部静态变量),初始值设置为0,然后每进一次中断+1,然后加到串口通信协议的长度的时候再清零。然后判断帧头、校验。写完了之后我自己都觉得不对,一旦数据错开了一位,后面就永远都接收不到数了。无奈看了一下前辈们的代码,跟我的思路差不多,只不过那个计数值跟接收到的数据时同时判断的,而且每次中断都要判断,一旦不对计数的那个变量就清零。

     

           废话少说,直接上一段代码让大家看看就明白了。(通信协议姑且按照简单的aa 55 一个字节数据 一个字节校验,代码是基于51单片机的)。接收成功则在中断程序中把串口接收成功标志位置1。

    然后串口中断部分
    void ser()interrupt 4
    {
    static unsigned char count;//串口接收计数的变量
      RI=0;//手动清某个寄存器,大家都懂的
      receive[count]=SBUF;
      if(count==0&&receive[count]==0xaa)//同时判断count跟收到的数据
      {
           count=1;
      }
      else if(count==1&&receive[count]==0x55)
      {
         count=2;
      }
      else if(count==2)
      {
           count++;
      }
      else if(count==3&&receive[count]== receive [2])//判断校验和,数据多的话是求//和,或者其他的校验方法,也可能是固定的帧尾
      {
        count=0;
         uart_flag =1;//串口接收成功标志,为1时在主程序中回复,然后清零
       ES=0;      //关中断,回复完了再ES=1;
      }
      else
      {
         count=0;//判断不满足条件就将计数值清零
      }
    }

    第一次做的串口大概就按照这个方法写完了(我后来看过其他的代码,有人用switch语句写的,逻辑跟这个也差不多,不过我还是感觉用if else来写清晰一些),

            不过在测试的时候发现了bug,如果数据帧发送一半,然后突然停止,再来重新发,就会丢失一帧的数据。比如先接受到aa 55,然后断了,再进来aa 55 01 01,就不受控制了。后来我也想到一个bug,如果在多设备通信中,属于其他设备的的帧数据最后一位是aa(或者最后两位为aa 55 ,或者最后3位为aa 55 板选),下一次通信的数据就接收不到了。

     

            当时对于数据突然中断的bug,没有想到很好的解决办法,不过这种情况几率极小,所以一直用这个方法写也没有问题。多设备通信最后一位恰好是aa的几率也很小,出问题的可能也很小。当时项目里面的控制数据跟校验恰好不可能出现aa,于是我把if(count==0&&receive[count]==0xaa)改成了if(receive[count]==0xaa)其他都没变,解决了,没有bug了。

     

            后来我又写了几次单片机程序,才想到了一些解决问题的方法——不过改天再接着写吧,太累了,明天还要上班呢。

     

            在后来的项目中,真的遇到了数据位跟校验位都可能出现aa的情况。我考虑到每次数据都是连续发送的(至少我们用labwindows做的上位机程序是这样的),成功接收到了一帧数据是要有一定时间回复的,也就是说如果接收到一半,但是很长时间没接收到数据,把计数值count清零就ok啦。涉及时间的问题自然要用定时器来实现啦。

    这次的通信协议如下,串口波特率19200,2个帧头aa 55 ,一个板选,6字节数据,一个校验字节(除帧头外其他数据的和)。


    全局变量定义
    unsigned char boardAddr;//板选地址,通过检测几个io引脚,具体怎么得到的就不写了,很简单的
    unsigned char g_DatRev [10]={0};//接收缓存
    bit retFlag=0;//为1代表串口接收到了一帧数据
     
     
    串口初始化函数,晶振22.1184
     
    void init_uart()
    {
           SCON = 0x50;                 //串口方式1允许接收
           TMOD = 0x21;                //定时器1,方式2,8位自动重载,同时配置定时器0,工作方式1
           PCON = 0x80;                // 波特率加倍
           TH1 = 0xfa;
           TL1 = 0xfa;               //写入串口定时器初值
           TH0=(65536-2000)/256;    //写入定时器0初值,串口传输一个字节时间为(1/19200)*10,计算得0.52ms
           TL0=(65536-2000)%256;   //定时器0定时大约1ms多
        EA=1;
        ET0=1;                  //波特率:19200    22.1184M  初值:250(0xfa)
           IE |= 0x90;           
        TR1 = 1;                   
    }
     
    串口中断函数
     
    void UART_INT(void) interrupt 4
    { 
           static unsigned char count;//串口接收计数的变量
     
                  RI = 0;
                  g_DatRev[count] = SBUF;
                  if(g_DatRev[count]==0xaa&&count==0)             //帧头
               {
                       count=1;                                                 
                }
                   else if(count==1&&g_DatRev[count]==0x55) 
                {  
                             count=2;          
                }
     
                    else if (count==2&&g_DatRev[2] == boardAddr)
                   { 
                      CK = g_DatRev[count];
                       count=3;
                     
                   }
         
                   else if(count>=3&&count<9)
                  {     
                    
                         CK += g_DatRev[count];
                        count ++;
                }
                 
               else if(count == 9&&CK==g_DatRev[9])
                         {     
                             ES = 0; 
                            retFlag = 1;
                             count=0;            
                         }            
                  else
                   {
                        count=0;
                   } 
                 resettimer();
     
    }
     
    //判断count不为0的话就启动定时器
    void resettimer()
    {
           TR0=0;
           TH0=(65536-2000)/256;
           TL0=(65536-2000)%256;
           if(count!=0)
           {
                  TR0=1;
           }
    }
     
    定时器中断函数
    void T0_time()interrupt 1
    {     
        TR0=0;
           TH0=(65536-2000)/256;
           TL0=(65536-2000)%256;
           count=0;
     
    }

    这种方法的确是本人自己想出来的,别人可能也这样做过,但我这个绝对不是抄袭或者模仿来的。这样写的确可以避免前面提到过的bug,不过代价是多用了一个定时器的资源,而且中断函数里的内容更多了,占用了更多的时间。

     

            要是能把第一种方法改进一下就好了,主要是那个校验不能为aa的那个bug,因为毕竟传输到一半突然断了的可能性是非常小的。后来我想第一个判断if(count==0&&receive[count]==0xaa)好像有点太严格了,考虑到第二字节的帧头,跟板选地址不可能为aa,于是把这个改写为if(count>=0&&count<=2&& receive[count]==0xaa),这样就把bug出现的几率降到了非常小,也只是在前一帧结尾数据恰好为 aa 55 板选 的时候才出现,几率是多少大家自己算一下吧,呵呵。这样我自己觉得,昨天写的那种方法改进到这个程度,应该算可以啦,反正我是很满意了。

     

            实际上我还想过其他的方法,比如缓存的数组采用移位寄存的方式。拿前面的4个字节的协议为例。

    void ser()interrupt 4
    {
     unsigned char i;
      RI=0;
     
      for(i=0;i<3;i++)
      {
         receive[i]=receive[i+1];
      }
      receive[3]=SBUF;
      if(reveive[0]==0xaa&&receive[1]==0x55&&receive[2]==receive[3])
      {
         ret_flag=1;
           ES = 0;  
      }
     
    }

    这段代码看上去可是简单明了,这样判断可是不错啊,同时判断帧头跟校验不会产生前面提到的bug。说实话当时我刚想出这种方法并写出来的时候,马上就被我给否了。那个for循环可真是很占时间的啊,延时函数都是这样写的。每次都循环一下,这延时太长,通信速度太快的话就不能接收到下一字节数据了。最要命的是这个时间的长度是随着通信协议帧的字节数增加而增加的,如果一次要接收几十个字节,肯定就玩完了。这种方法我一次都没用过。

     

            不过我居然又想出来了这种方法的改良措施,是前两天刚想出来的,呵呵,还没有实践过呢。

    下面代码的协议就按第二段程序(定时器清零的那个协议,一共10字节)

    全局变量

     

    bit ret_flag;
    unsigned char receive[256]={0};
    unsigned char boardaddress;
     
    中断函数
     
    void ser()interrupt 4
    {
     
     
      static unsigned char i=0;
      static unsigned char total=0;
      RI=0;
      receive[i]=SBUF;
      total=total-receive[i-7]+receive[i-1];
     
      if(receive[i-9]==0xaa&&receive[i-8]==0x55
      &&receive[i-7]==boardaddress&&receive[i]==total
      )
      {
         ret_flag=1;
           ES = 0;  
      }
      i++;
     
    }


            之所以要定义256个长度的数组,就是为了能够让数组“首尾相接”。因为0 -1 = 255 , 255+1 = 0。而且我在计算校验的时候也改进了算法,不会因为数据长度的增加而增加计算校验值的时间。这种方法也是我不久前才想出来的,所以还没有经过实际的验证。上面的代码可能会有逻辑上的错误,如果真有错误,有网友看出来的话,请在下面留言告诉我。这个方法也是我原创的哦,别人也肯能会想到,不过我这个绝对不是抄袭别人的。

     

            上面的代码最大的缺点就是变量定义的太多了,太占ram资源了,编译的时候可能会出现错误,毕竟51单片机才128字节的ram(有的资源也很丰富的,比如c8051系列的),这一下子就是256字节的变量。不过对于资源多一些的单片机,这样写还是可以的。要是能有4bit在一起的数据类型就好了,呵呵,verilog代码里面是可以的,C语言里貌似不行啊。

     

            要想能在例如51单片机上运行,只能按照下面的折中方式了,也就是把i相关的量都与一个0x0f

     

    全局变量
     
    bit ret_flag;
    unsigned char receive[16]={0};// 可以考虑在定义时加上idata,毕竟还可能是32
    //或者64长度的数组呢unsigned char idata receive[16]={0};
     
    unsigned char boardaddress;
     
    中断函数
     
    void ser()interrupt 4
    {
     
     
      static unsigned char i=0;
      static unsigned char total=0;
      RI=0;
      receive[i&0x0f]=SBUF;
      total=total-receive[(i-7)&0x0f]+receive[(i-1)&0x0f];
     
      if(receive[(i-9)&0x0f]==0xaa&&receive[(i-8)&0x0f]==0x55
      &&receive[(i-7)&0x0f]==boardaddress&&receive[i&0x0f]==total
      )
      {
         ret_flag=1;
           ES = 0;  
      }
      i++;
     
    }



    展开全文
  • #include <stc12c5a60s2.h> #define uint unsigned int #define uchar unsigned char #define ulong unsigned long sbit k1=P2^6; sbit k2=P2^7; ... //s是字符串UartBuffer[5]={0}变量 unsigned char code a[...
  • 服务器那块我打算用tomcat来做,但是单片机采集数据之后如何上传到服务器?还有,如果我用来做数据采集的服务器和用来响应http请求的服务器用的是同一台服务器这样会不会有什么影响?
  • 振荡周期:为单片机提供定时信号的振荡源周期(如晶振周期或外加周期)。 状态周期:为两个振荡周期。用S表示(又称S周期或时钟周期)。 机器周期:为六个状态周期。 指令周期:完成一条指令所需要的全部时间,以...
  • 51单片机串口通信的发送与接收 字符串

    万次阅读 多人点赞 2019-03-10 13:21:12
    51单片机的串口,是个全双工的串口,发送数据的同时,还可以接收数据。 当串行发送完毕后,将在标志位 TI 置 1,同样,当收到了数据后,也会在 RI 置 1。 无论 RI 或 TI 出现了 1,只要串口中断处于开放状态,单片机...
  • 52单片机定时/计数器2的研究

    千次阅读 2011-05-17 19:23:00
     毕业设计要用到外部中断和定时器,定时器和外部中断都要三个,当时晕,定时器可以弄三个,但是外部...  网上关于讲52单片机定时/计数器2的资料不是很多,因为它被用得较少,后来我只能根据网上的资料和我
  • 程序硬件平台:11.0592M晶振,STC单片机(兼容51)。 1、发送数据 /***************************************************...* 把单片机中存放的数据通过P2.1作为串口TXD发送出去 ******************************...
  • 以51单片机为例,直接上干货——代码: #define BUFFER_SIZE 128 typedef struct { unsigned char ri, wi, ct, run; unsigned char buff[BUFFER_SIZE]; } UARTFIFO; static volatile UARTFIFO Uart1TxFifo; ...
  • 当接收数据不定长时 ,需要判断出何时接收完成,之前我写的一个文章是在QT中处理如何判断接收完成的,在单片机中的思路其实是一样的,只不过在QT中实例化了一个定时器,在未超时接收到数据时再重新定时,到接收完成...
  • 52单片机定时/计数器2的研究 .

    千次阅读 2013-12-27 14:53:54
    毕业设计要用到外部中断和定时器,定时器和外部中断都要三个,当时晕,定时器可以弄三个,但是外部中断不好弄。...因为我做的东西刚好是中断后开始定时的那种,因此定时和中断也不冲突。  网上关于讲52单片
  • 51单片机的中断和定时(全面)

    千次阅读 2018-04-18 15:48:25
    定时器/计数器51的定时器/计数器有2个分别是T1和T0,52系列的单片机有3个定时器/计数器,T0和T1是通用定时器/计数器,定时器/计数器2(简称T2)是集定时、计数和捕获三种功能于一体,功能更强。首先看一下这个简单点...
  • 单片机中计数器是怎么调用的序数?单片机是按照计数器的顺序发送的采集数据的么?为什么上位机的接收序数是不按顺序得到的?
  • STC单片机串口收发学习总结

    千次阅读 2018-11-19 17:16:23
    STC单片机串口收发学习总结1字符串输出到串口2LED闪烁3LED流水灯4定时器5中断6定时与中断应用示例7数码管8串口9RS232接口 1字符串输出到串口 //////////////////////////////////////////////////////// //单片机...
  • 用Keil C51开发定时器/计数器 基本的51单片机内部有两个...它们各自具有4种工作状态,其控制字和状态均在相应的特殊功能寄存器中,可以通过软件对控制寄存器编程设置,使其工作在不同的定时状态或计数状态。 现在,...
  • 一、使用proteus绘制简单的电路图,用于后续仿真 二、编写程序 .../******************************************************************************************************************** ...
1 2 3 4 5 ... 20
收藏数 4,229
精华内容 1,691
关键字:

单片机定时发送数据