近日完成了一个泥浆压力信号数据采集的系统方案,采集频率设置为100Hz,每分钟采集一次,每次采集5s,需要设计上位机与下位机之间的通信协议(比较简单,勿喷)。上位机采用java语言,因为对java比较熟悉。开始从淘宝买来了采集板,自己编写上位机软件(使用jfreechart),采用轮询的方式(上位机发送命令读取一个当前数据),做不到100Hz的采集频率,可能是由于串口通信速率和稳定性的限制。于是变换方案,采用先快速采集缓存在单片机中,再发送到上位机的方式,比较完满的完成了。

此次任务。主要的协议就是:上位机发送采集的命令,单片机开始采集5秒,频率为100Hz,先缓存,上位机在串口上一直监听,有数据就收。通过一个状态变量workingModal标识目前处于什么状态,细节见程序。传感器输出4-20ma的信号,通过一个变送器转换成0-5V信号。下位机控制器及采集器为STC12C5A60S2。

         此次尝试还是涉及比较多的知识,主要涉及java串口通信,javaGUI编写,jfreechart绘图,上下位机的通信协议(这一点尤其重要,需要良好的设计才能又简单又实用),

单片机(主要是STC单片机)定时器、ADC、UART的使用,系统方案设计(根据要求自己定完成方案,采用什么实现方式,需要哪些硬件软件支持,各个方案的优缺点是

什么、适用条件是什么)。其中这个方案中的串口可以改成FT245BL的USB通信方案。

           在这个过程中,遇到的最主要的一个问题就是:串口中断接收不完全,下位机传输的是1000字节,但是很多次收不到1000个字节,并且没有规律,但是能知道剩下的有些字节在缓冲区中,没有引起串口中断, 所以需要判断有没有读取完全,没有的话就要把缓冲区的数据读完。这个问题在网上搜了一下,大体上是由于串口的线程和读取的线程之间的数据共享区的问题造成的,上面的重新读取是一种方案,网上提供了另外一种方案就是不要这个缓冲区,自己直接控制串口的线程,这种方案还没有试试,以后有机会再说。

 

主要的代码贴出,有意者读之,详细的见我的126.com那个云盘中的java中的”自己写的实例“。

public class SerialConnection implements SerialPortEventListener,
                     CommPortOwnershipListener {

     private static Logger logger = Logger.getLogger(SerialConnection.class);
     private DataAcquisitionFrame parent;

     private SerialParameters parameters;
     private OutputStream os;
     private InputStream is;

     private CommPortIdentifier portId;
     private SerialPort sPort;

     
     int datalen = 2;
     private int[] readBuffer = new int[datalen];//这个缓冲数组保存串口数据,以供线程使用
     int bytesNum = 0;

     private int sampleLen = 0 ;//目前采集了多少数据

     
     //工作模式
     private static final int MODAL_SAMPLING      = 0;                //0表示真正开始采集
     private static final int MODAL_BEGINING      = 1;                //1表示开始采集数据
     private static final int MODAL_SAMPLE_CONFIG = 2;                //2表示采样率设置
     private static final int MODAL_TIME_SETTING  = 3;                //3表示采样时间设置
     private static final int MODAL_BAUD_SETTING  = 4;                //4表示波特率设置
     private static final int MODAL_IDLE          = 10;               //10表示波特率设置
     
     private int workingModal = SerialConnection.MODAL_IDLE;
     private int data = 0;
  
     public SerialConnection(DataAcquisitionFrame parent,
                 SerialParameters parameters) {
         this.parent = parent;
         this.parameters = parameters;
    }
    public void openConnection() throws SerialConnectionException {

         // Obtain a CommPortIdentifier object for the port you want to open.
         try {
             portId =
              CommPortIdentifier.getPortIdentifier(parameters.getPortName());
         } catch (NoSuchPortException e) {
             throw new SerialConnectionException(e.getMessage());
         }
     
         // Open the port represented by the CommPortIdentifier object. Give
         // the open call a relatively long timeout of 30 seconds to allow
         // a different application to reliquish the port if the user
         // wants to.
         try {
             sPort = (SerialPort)portId.open("SerialDemo", 3000);
         } catch (PortInUseException e) {
             throw new SerialConnectionException(e.getMessage());
         }
     
         // Set the parameters of the connection. If they won't set, close the
         // port before throwing an exception.
         try {
             setConnectionParameters();
         } catch (SerialConnectionException e) {    
             sPort.close();
             throw e;
         }
     
         // Open the input and output streams for the connection. If they won't
         // open, close the port before throwing an exception.
         try {
             os = sPort.getOutputStream();
             is = sPort.getInputStream();
         } catch (IOException e) {
             sPort.close();
             throw new SerialConnectionException("Error opening i/o streams");
         }
     
     
         // Add this object as an event listener for the serial port.
         try {
             sPort.addEventListener(this);
         } catch (TooManyListenersException e) {
             sPort.close();
             throw new SerialConnectionException("too many listeners added");
         }
     
         // Set notifyOnDataAvailable to true to allow event driven input.
         sPort.notifyOnDataAvailable(true);
     
         // Set notifyOnBreakInterrup to allow event driven break handling.
         sPort.notifyOnBreakInterrupt(true);
     
         // Set receive timeout to allow breaking out of polling loop during
         // input handling.
         try {
             sPort.enableReceiveTimeout(30);
         } catch (UnsupportedCommOperationException e) {
         }
     
         // Add ownership listener to allow ownership event handling.
         portId.addPortOwnershipListener(this);
         
 //        System.out.println(sPort.getReceiveThreshold());
 //        try {
 //            if(!sPort.isReceiveThresholdEnabled()){
 //                sPort.enableReceiveThreshold(1);
 //            }
 //        } catch (UnsupportedCommOperationException e1) {
 //            e1.printStackTrace();
 //        }
     
         logger.info("串口打开成功");
         //在这个开启定时器进行定时发送命令采集
         timer = new Timer();
         timer.schedule(new TimerTask(){

             @Override
             public void run() {

                 /**现在有个问题就是:每次采集的数据收不到1000个字节,有些字节在缓冲区中,没有引起串口中断
                  * 那么再重新开始采集的时候就需要判断是否采集完成,没有的话就需要把读缓冲区的数据读完,否则
                  * 造成SerialConnection.MODAL_BEGINING这个模式的前两个字节不是OK,于是开始不了,一直是这个
                  * 模式读,造成java.lang.ArrayIndexOutOfBoundsException异常。
                  * Exception in thread "Win32SerialPort Notification thread" java.lang.ArrayIndexOutOfBoundsException: 2
                         at com.xsy.serialport.SerialConnection.serialEvent(SerialConnection.java:363)
                         at com.sun.comm.Win32SerialPort.sendDataAvailEvent(Win32SerialPort.java:649)
                         at com.sun.comm.NotificationThread.run(Win32SerialPort.java:878)
                  */
                 if(workingModal != SerialConnection.MODAL_IDLE){//SerialConnection.MODAL_SAMPLING
                     int lenMore;
                     try {
                         lenMore = is.available();
                         if(lenMore>0){
                             logger.info("***有  "+lenMore+"  个字节未读**");
                             for (int i = 0; i < lenMore; i++) {
                                 commDataProcess();
                             }
                         }
                         } catch (IOException e) {
                             e.printStackTrace();
                     }
                 }
                clearBuffer();
                sampleLen = 0;
                workingModal = SerialConnection.MODAL_BEGINING;
                sendBeginCommand();
                
                 
             }
             
         }, 0, 60000);//60秒采一次
     }
     private void sendBeginCommand(){
         //发出命令等待一会儿
         byte[] outbuf = new byte[]{0x01,0x00,0x00,0x00,0x2A};
         try {
 /************************************CRC校验先不做*************************************************/                
             os.write(outbuf);//串口写数据
             os.flush();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }

     public void setConnectionParameters() throws SerialConnectionException {

     // Save state of parameters before trying a set.
     int oldBaudRate = sPort.getBaudRate();
     int oldDatabits = sPort.getDataBits();
     int oldStopbits = sPort.getStopBits();
     int oldParity   = sPort.getParity();
     int oldFlowControl = sPort.getFlowControlMode();

     // Set connection parameters, if set fails return parameters object
     // to original state.
     try {
         sPort.setSerialPortParams(parameters.getBaudRate(),
                       parameters.getDatabits(),
                       parameters.getStopbits(),
                       parameters.getParity());
     } catch (UnsupportedCommOperationException e) {
         parameters.setBaudRate(oldBaudRate);
         parameters.setDatabits(oldDatabits);
         parameters.setStopbits(oldStopbits);
         parameters.setParity(oldParity);
         throw new SerialConnectionException("Unsupported parameter");
     }

     // Set flow control.
     try {
         sPort.setFlowControlMode(parameters.getFlowControlIn()
                        | parameters.getFlowControlOut());
     } catch (UnsupportedCommOperationException e) {
         throw new SerialConnectionException("Unsupported flow control");
     }
     }

     public void closeConnection() {
     // If port is alread closed just return.
     if (!isWorking) {
         return;
     }


     // Check to make sure sPort has reference to avoid a NPE.
     if (sPort != null) {
         try {
         // close the i/o streams.
             os.close();
             is.close();
         } catch (IOException e) {
         System.err.println(e);
         }

         // Close the port.
         sPort.close();

         // Remove the ownership listener.
         portId.removePortOwnershipListener(this);
     }
     isWorking = false;
     clearBuffer();
     sampleLen = 0;
     timer.cancel();
     logger.info("串口关闭");
     }
     public void sendBreak() {
     sPort.sendBreak(1000);
     }
     public void serialEvent(SerialPortEvent e) {//串口收数据
          // Create a StringBuffer and int to receive input data.
         switch (e.getEventType()) {
             case SerialPortEvent.DATA_AVAILABLE:
             {
                 try {
                     switch (workingModal) {
                         case SerialConnection.MODAL_BEGINING://采集数据
                             readBuffer[bytesNum++] = is.read();
                             if(readBuffer[0]=='O' && readBuffer[1]=='K')
                             {
                                 logger.info("+++++++++++++++++++"+new Date()+"开始采集了+++++++++++++++++++++");
                                 sampleLen = 0;
                                 workingModal = SerialConnection.MODAL_SAMPLING;//真正开始采集
                                 clearBuffer();
                             }
                             break;
                         case SerialConnection.MODAL_SAMPLE_CONFIG://采样率设置
                             readBuffer[bytesNum++] = is.read();
                             if(readBuffer[0]=='S' && readBuffer[1]=='C')
                             {
                                 logger.info("+++++++++++++++++++"+new Date()+"采样率设置成功+++++++++++++++++++");
                                 workingModal = SerialConnection.MODAL_IDLE;
                                 clearBuffer();
                             }
                             
                             break;
                         case SerialConnection.MODAL_TIME_SETTING://采样时间设置
                             readBuffer[bytesNum++] = is.read();
                             if(readBuffer[0]=='T' && readBuffer[1]=='S')
                             {
                                 logger.info("+++++++++++++++++++"+new Date()+"采样时间设置成功+++++++++++++++++++");
                                 workingModal = SerialConnection.MODAL_IDLE;
                                 clearBuffer();
                             }
                             
                             break;
                         case SerialConnection.MODAL_BAUD_SETTING://波特率设置
                             readBuffer[bytesNum++] = is.read();
                             if(readBuffer[0]=='B' && readBuffer[1]=='S')
                             {
                                 logger.info("+++++++++++++++++++"+new Date()+"波特率设置成功+++++++++++++++++++");
                                 workingModal = SerialConnection.MODAL_IDLE;
                                 clearBuffer();
                             }
                             
                             break;
                         case SerialConnection.MODAL_SAMPLING://真正开始采集
                             commDataProcess();
                             break;
                         default:
                             logger.info("你传的什么玩意儿?");
                             break;
                     }
                 } catch (IOException e1) {
                     e1.printStackTrace();
                 }
             }
     
         }

     }
     /**
      * 每次读取一个字节
      * @throws IOException
      */
     private void commDataProcess() throws IOException {
         readBuffer[bytesNum++] = is.read();
         sampleLen++;
         logger.info("------第"+sampleLen+"个字节: "+readBuffer[bytesNum-1]);
         if(2==bytesNum)//传输完成一个数据,两个字节
         {
             bytesNum = 0;
             //int data = (readBuffer[0]<<8 | ((readBuffer[1] & 0x03))<<6)>>6;//高八位和低八位的低两位
             data  = (readBuffer[0]<<2) | (readBuffer[1] & 0x03);
             Millisecond now = millSecond.nextMilliSecond();   //获取一下时间
             double[] valid = parent.getValidateData();
             this.parent.getYali().add(now,(valid[1]-valid[0])*data/1024+valid[0]);
             logger.info("------第"+sampleLen/2+"个数:  "+Integer.toHexString(data));
         }

         this.parent.updateStatusPanelColor();
         
         if(sampleLen>=1000){//数据传输完成
             sampleLen = 0;
             logger.info("+++++++++++++++++++数据采集完成+++++++++++++++++++");
             workingModal = SerialConnection.MODAL_IDLE;
             clearBuffer();
         }
     }

     private void clearBuffer() {
         bytesNum = 0;
         for(int i = 0;i<datalen;i++){//清空缓冲区
             readBuffer[i] = 0;}
         
     }

     private boolean checkCRC(int len){
         MiscCRC16 m = new MiscCRC16();
         //m.calCRC16(readBuffer, len);//不同的命令的长度是不一样的
         //m.calCRC16(msg, msglen)
         int crc = m.getValue();
         //System.out.println(crc);
         return (crc==0);
     }
     private void parseYaliData() {
         //01 04 02 03 02 38 01
         //开始解析...
         if(checkCRC(7)){//压力信号的7位
             //************************两个字节合成一个int*********************************    
             int value=0;
             value = readBuffer[3] & 0xff;//高字节
             //System.out.println(Integer.toHexString(value));
             value = (value<<8)|(readBuffer[4] & 0xff);//低字节
             //System.out.println(Integer.toHexString(value));
         //*******************************************************************************
             //可能要使用这个函数来构建,动态生成millsecond,以备保存和恢复数据Millisecond(int millisecond, int second, int minute, int hour,int day, int month, int year)
             Millisecond now = millSecond.nextMilliSecond();   //获取一下时间
             double mA = 4+(double)value*(20-4)/1023;//获取mA值
             DecimalFormat df = new DecimalFormat("0.00");
             //mA = Double.parseDouble(df.format(mA));
             //this.parent.getmA().add(now,mA);//mA这个就不要了,由巴特沃斯滤波器产生
             
             //System.out.println(mA);
            
             //按照校验数据获取压力值
             double yali = (parent.getValidateData()[1]-parent.getValidateData()[0])*mA/16
             +(3*parent.getValidateData()[0]-parent.getValidateData()[1])/4;
             
             logger.info("得到新压力数据:"+yali);

             //this.parent.getSeries().add(now,Double.parseDouble(df.format(yali)));
             
             this.parent.getYali().add(now,yali+1);

             double filterData=this.dul.addOneMoreValueAndFilterDataOut(yali);
             this.parent.getFilterData().add(now,filterData);

             this.parent.updateStatisticsBackColor();//更新背景色
             this.parent.updateStatusPanelColor();
             
             this.statisticscurrent.setText(df.format(this.dul.getCurrentValue()));
             this.statisticsaverage.setText(df.format(this.dul.getAverageValue()));
             this.statisticsmax.setText(df.format(this.dul.getMaxBuffValue()));
             this.statisticsmin.setText(df.format(this.dul.getMinBuffValue()));
         }
         
     }

     private void writeData2File(String model, String message) {//有时间使用log4j
         // TODO Auto-generated method stub
         File f=null;
         OutputStream fos = null;
         if("out".equals(model)){
             f= new File(System.getProperty("user.dir")+"\\messageout.txt");
         }
         else
             f= new File(System.getProperty("user.dir")+"\\messagein.txt");
         try {
             fos = new FileOutputStream(f,true);
             fos.write(message.getBytes());
             fos.flush();
         } catch (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         } finally {     
             try {     
                 if(fos != null){  
                     fos.close();}
             } catch (IOException e) {     
                 e.printStackTrace();}     
         }     

     }

     public void ownershipChange(int type) {
     if (type == CommPortOwnershipListener.PORT_OWNERSHIP_REQUESTED) {
         PortRequestedDialog prd = new PortRequestedDialog(parent);
     }
     }



     public boolean getWorking() {
         // TODO Auto-generated method stub
         return isWorking;
     }

     public void setWorking(boolean b) {
         this.isWorking = b;
     }

 }

 

下位机程序

Main.c
#include "Commons.h"
 #include "UART.h"
 #include "Timer0.h"
 #include "ADC.h"

 void main()
 {
   int i=0;
   unsigned char sc = 100;       //采样率的设置值sc赫兹
   unsigned char ts = 5;            //采样时间设置ts秒
   unsigned char bs = 1;      //波特率设置 1--2400  2--4800  4--9600  8--19200 16--38400  32--76800  
  
   P3M1 &=  0xEF;
   P3M0 |=  0x10;                //把P3.4口设置为推挽输出

   LEDMCUFAST();                    //快闪表示正常工作
   P0=0xFF;                        //端口配置为可读
   Uart_init();                    //串口中断,可以读写
   Init_Timer0();                //定时器0初始化
   InitADC(0);                    //ADC初始化,P1.0

   while(1)                        //死循环等待串口送来数据处理
      {
 /收到上位机传过来的数据,目前只能是半双工工作模式,也就是只能单接收或者单发送//
 //接收到上位机来的信息,开始采样?配置参数(采样率设置,采集时间设置,传输波特率设置)?///
           if(IsReceived_UART()==1)            //判断接收到了串口数据结束符
           {
               ClrFlag_UART();              //清除标志位
             
             /*回发数据握手
             for(i=0;i<Return_Recv_Len();i++)   //得到发送来的数据
             {
                 SendOneByte(Return_Rxbuffer_UART()[i]); //一个字节一个字节地往串口发送命令
             }*/
             
             //目前的协议是COMMAND(1字节)+DATA(1字节)+CRC16(2字节,前面命令和*的CRC16校验)+*
             if(Return_Rxbuffer_UART()[4] == '*')
             // && ((Return_Rxbuffer_UART()[2]<<8 | Return_Rxbuffer_UART()[3])==CalCRC16(Return_Rxbuffer_UART(),2)))    //现在还没加入校验
             {
                 switch(Return_Rxbuffer_UART()[0])
                 {
                     case 0x01:                      //接收到命令就开启定时器0,开始采样    01 00 03 70 2A
                     {   
                         ClrBuffer_ADC();          //清空缓冲区          //这个还未验证,这句话是后加的
                         Start_Timer0();
                         StartADC();
                         SendOneByte('O');         //发送OK表示我要采集数据了            
                         SendOneByte('K');          //4F 4B
                         break;
                     }
                     case 0x02:                      //接收到02表示采样频率设置            02 64 01 3b 2A
                     {
                         sc =  Return_Rxbuffer_UART()[1];
                         SendOneByte('S');          //发送SC表示Sampling Config            
                         SendOneByte('C');          //53 43
                         break;
                     }                                      
                     case 0x03:                     //接收到03表示采样时间设置            03 05 c1 43 2A
                     {
                         ts =  Return_Rxbuffer_UART()[1];
                         SendOneByte('T');          //发送TS表示Timing Setting
                         SendOneByte('S');          //54 53
                         break;
                     }
                     case 0x04:                    //接收到04表示波特率设置              04 01 c2 b0 2A
                     {
                         bs =  Return_Rxbuffer_UART()[1];
                         SendOneByte('B');          //发送BS表示Baud Setting
                         SendOneByte('S');          //42 53
                         break;
                     }
                     default  :{}
                 }
                 
                 LEDMCUSLOW();                      //慢闪指示收到串口数据   //花2s
             }
             ClrRxbuffer_UART();                      //清缓冲区
            }
 如果ADC所有的采样完成了/
          else if(IsFull_ADC()==1)            //ADC所有的数据准备好      //总共花5s
          {
             ClrFlag_Timer0();                    //清标记
             Send2UART();                    //发送结果
             ClrBuffer_ADC();                //清空缓冲区
         //    LEDMCUFAST();                      //快闪指示发送完成ADC数据      //花2s
          }
      }
  }

Commons.h

#include <STC12C5A60S2.h>

 sbit led=P3^4;

 unsigned int CalCRC16(unsigned char* msg, unsigned int msglen);

 void LEDMCUFAST();
 void LEDMCUSLOW();

 void ChangeLight();

 void delay100us();
 void delay1ms();
 void delay(unsigned int xx);

 

Commons.c

#include "Commons.h"
 #include <intrins.h>

 //校验表,需要用8位的unsigned char来保存
 /*加入这个校验之后好像单片机的运转就混乱了,校验一位(命令)还行
 unsigned char code auchCRCHi[] = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
             0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80,
             0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
             0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
             0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
             0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
             0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
             0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
             0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 };
 unsigned char code auchCRCLo[] = { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02,
             0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D,
             0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08,
             0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF,
             0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16,
             0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31,
             0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34,
             0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B,
             0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A,
             0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25,
             0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20,
             0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7,
             0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E,
             0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9,
             0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC,
             0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3,
             0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52,
             0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D,
             0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58,
             0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F,
             0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46,
             0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 };
 unsigned int CalCRC16(unsigned char* msg, unsigned int msglen)  //返回16位无符号整数
 {           
     unsigned char crcHigh = 0xFF;
     unsigned char crcLow  = 0xFF;
     unsigned int i;
     unsigned char index;

     for (i = 0; i < msglen; i++) {
         index = (crcHigh & 0xFF) ^ (msg[i] & 0xFF);
         crcHigh = (crcLow & 0xFF) ^ (auchCRCHi[index] & 0xFF);
         crcLow = auchCRCLo[index] & 0xff;
     }

     return (crcHigh & 0xFF) << 8 | (crcLow & 0xFF);
 }
 //*/


 void LEDMCUFAST()
 {
    unsigned char i;
    led=0;
    for(i=0;i<40;i++)
    {
            led=~led;
         delay(50);
    }    
 }

 void LEDMCUSLOW()
 {
    unsigned char i;
    led=0;
    for(i=0;i<20;i++)
    {
            led=~led;
         delay(100);
    }    
 }
 void ChangeLight()
 {
     led = ~led;
 }

 void delay100us()
 {
     unsigned int i=50;
     while(i--)
     {
         _nop_();
         _nop_();
     }
 }

 void delay1ms()
 {
     unsigned char i=10;
     while(i--)
     {
         delay100us();
     }
 }


 void delay(unsigned int t)
 {

     unsigned int i;
     for(i=0;i<t;i++)
     {
         delay1ms();
     }
 }

ADC.h

//CONST FOR ADC
 #define ADC_POWER    0x80  //ADC POWER CONTROL BIT
 #define ADC_FLAG     0x10  //ADC COMPLETE FLAG
 #define ADC_START    0x08  //ADC START CONTROL BIT
 #define ADC_SPEEDLL  0x00  //ADC COMPLETE 540 CLOCKS
 #define ADC_SPEEDL   0x20  //ADC COMPLETE 360 CLOCKS
 #define ADC_SPEEDH   0x40  //ADC COMPLETE 180 CLOCKS
 #define ADC_SPEEDHH  0x60  //ADC COMPLETE 90 CLOCKS


 void  InitADC(unsigned char ch);
 void  StartADC();
 void  CloseADC();
 void  WaitforRes();
 unsigned int GetResADC();
 //unsigned char CH = 0;//通道

 /*
 void ADC_Power_On();
 void Set_P12_ASF();
 void Set_P12_Normal_IO();
 void Set_ADC_Channel_2();

 unsigned int Get_AD_Result();
 void AD_initial();
 */

ADC.c

#include "ADC.h"
 #include "Commons.h"



 // ADC INTERRUPUT SERVICE
 /*
 void adc_isr() interrupt 5 using 1
 {
     ADC_CONTR &= !ADC_FLAG;   //CLEAR ADC INTERRUPT FLAG

     //处理结果
     //发送到上位机或者存储起来
     //ADC_RES   ADC_RESL
     SendOneByte(ADC_RES); //发送高8位
     SendOneByte(ADC_RESL);//发送低2位
     //切换通道吗?

 } */

 /*ch = 0(P1.0),1(P1.1),2(P1.2),3(P1.3),4(P1.4),5(P1.5),6(P1.6),7(P1.7)*/
 void  InitADC(unsigned char ch)
 {
     P1ASF     = 2^ch;  //SET ch AS ANALOG INPUT PORT
 }
  
 void  StartADC() //由定时器0每次中断中调用开启
 {    
     ADC_RES      = 0;        //清空结果
     ADC_RESL  = 0;
     ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START;    //ADC开启转换
 }

 void  CloseADC() //关闭ADC
 {    
     ADC_RES      = 0;        //清空结果
     ADC_RESL  = 0;
     ADC_CONTR &= !ADC_START;    //ADC关闭
 }
 void WaitforRes()
 {
     while((ADC_CONTR & ADC_FLAG) != ADC_FLAG)
     {delay1ms();}   //一直等待ADC转换结束
     ADC_CONTR &= !ADC_FLAG;   //CLEAR ADC INTERRUPT FLAG
 }

 unsigned int GetResADC()
 {
     unsigned int res = 0;
     
     WaitforRes();               //这里有个重大发现,声明必须在函数开始
 //    res = (res | ADC_RES)<<8;  //高八位
 //    res = (res |  ADC_RESL);   //低八位,实际上只有低两位
     res = (res | 0xfd)<<8;       //做测试用
     res = (res | 0xde);
     return res;

 }






 /*
 void ADC_Power_On()
 {
   ADC_CONTR=ADC_CONTR||0x80; //开启电源
   delay(1);
 }

 void Set_P12_ASF()
 {
   P1ASF=P1ASF||0x04;//#00000100B    ,设置P1.2为模拟功能
 }

 void Set_P12_Normal_IO()
 {
   P1ASF=P1ASF||0xfb;//#11111011B,设置P1.2为普通IO口
 }

 void Set_ADC_Channel_2()
 {
   ADC_CONTR=0xe2;//#111000010B,设置P1.2为A/D转换通道
   delay(1);
 }

 unsigned int Get_AD_Result()
 {
   unsigned int mm,kk=0x10;    //#00010000 flag
   ADC_RES=0;
   ADC_CONTR=ADC_CONTR||0x08;//启动AD
   delay(4);
   mm=ADC_CONTR&&kk;
   while(!mm);//未完成等待
   ADC_CONTR=ADC_CONTR & 0xe7;//#11100111B,清ADC_FLAG,ADC_START位,停止AD
   return(ADC_RES);//返回结果

 }

 void AD_initial()
 {
     
     ADC_Power_On();
     Set_P12_ASF();
     Set_P12_Normal_IO();
     Set_ADC_Channel_2();
 }
 */

Timer0.h

void Init_Timer0();
 void Start_Timer0();
 void Close_Timer0();

 unsigned char IsFull_ADC();
 void ClrFlag_Timer0();
 unsigned int* Return_Buffer_ADC();
 void ClrBuffer_ADC();

 void Send2UART();

Timer0.c

//中断中开启ADC开始采集,使采集的采样率为100HZ
 #include "Timer0.h"
 #include "Commons.h"
 #include "ADC.h"
 #include "UART.h"

 #define LEN_Timer0ADC_BUFFER 1000         //缓冲器长度,最多1000个字节:100HZ采5秒,就有500个数,1000个字节

 unsigned char Buffer_Timer0ADC[LEN_Timer0ADC_BUFFER]; //ADC结果缓冲区
 unsigned int Bufferpos_Timer0ADC=0;         //接收缓冲区指针
 unsigned char flag_ADC=0;       //是否准备好所有的新数据,0没有,1有

 /*(2^16-x)*12/(12*10^6) = 1/100*/
 #define Reload_Value_T0H 0xD8         //定时器0H重装值
 #define Reload_Value_T0L 0xF0         //定时器0L重装值

 void Init_Timer0()
 {
     TMOD=0x21;                      //定时器1为8位自动重装计数器,用于产生波特率 ,定时器0为16位计数器
     
     TR0=0;                          //
     ET0 = 1;                      //允许中断
     
     ClrBuffer_ADC();
 }
 void Start_Timer0()
 {
     TH0=Reload_Value_T0H;          //设置初值
     TL0=Reload_Value_T0L;
     TR0=1;          //开启定时器
 }
 void Close_Timer0()
 {
     TR0=0;          //关闭定时器0
     TH0=0;          //清空初值
     TL0=0;
 }

 //定时器0的中断,这之中进行ADC开启转换,获取结果,重启定时器
 void timer0() interrupt 1                   //每0.01s来一次
 {
       //ChangeLight();//指示灯闪一下,太快了没显示
       if(Bufferpos_Timer0ADC < LEN_Timer0ADC_BUFFER)
       {      
           
           if(Bufferpos_Timer0ADC%5==0)       //间隔50ms灯闪一下
           {
                 ChangeLight();
           }
           
           Start_Timer0();                //开启定时器,后面的时间不能超过0.01s
           
           //unsigned int res = GetResADC();//获取结果
           WaitforRes();
           Buffer_Timer0ADC[Bufferpos_Timer0ADC++]    = ADC_RES;        //高八位
           Buffer_Timer0ADC[Bufferpos_Timer0ADC++]    = ADC_RESL;      //低八位,低两位
           
           
           StartADC();                    //开启ADC,等到定时器中断的时候就能获取ADC结果
       }
       else
       {
             Close_Timer0();
           CloseADC();

           flag_ADC=1;      //准备好新数据了
       }
 }

 unsigned char IsFull_ADC()
 {
     return flag_ADC;                      //返回标志供主函数调用
 }

 void ClrFlag_Timer0()
 {
     flag_ADC=0;                          //清除标志供主函数调用
 }

 void ClrBuffer_ADC()
 {    unsigned int i=0;
     for(i=0;i<LEN_Timer0ADC_BUFFER;i++)
       {
           Buffer_Timer0ADC[i]='\0';             //清空缓冲区
       }
     Bufferpos_Timer0ADC = 0;//指针归零
 }

 void Send2UART()
 {
     unsigned int i = 0;
     for(i=0;i<LEN_Timer0ADC_BUFFER;i++)
     {
          SendOneByte(Buffer_Timer0ADC[i]); //一个字节一个字节地往串口发送命令
          ChangeLight();//指示灯闪一下
          delay(50);     //是传输完成了再闪一会儿还是传输过程中闪一闪?
     }
 }

UART.hvoid Uart_init();
 void SendOneByte(unsigned char xx);
 void Is_Uart_Receive();
 unsigned char IsReceived_UART();
 void ClrFlag_UART();
 unsigned char* Return_Rxbuffer_UART();
 void ClrRxbuffer_UART();
 unsigned char Return_Recv_Len();



UART.c

#include"UART.h"
 #include <STC12C5A60S2.h>

 #define Reload_Value_T1 0xf3     //波特率重装值

 #define LEN_UART_BUFFER 10         //缓冲器长度,最多10个字符

 unsigned char Rxbuffer_UART[LEN_UART_BUFFER]; //串口接收缓冲区
 unsigned char Rxpos_UART=0;         //串口接收缓冲区指针
 unsigned char flag_UART=0;       //是否接受到新数据,0没有,1有

 //Reload_Value=256-2^SMOD*Fosc/32/12/BAUD ,波特率最好小一点,
 //因为晶振有误差,要让重装值尽可能大点
 //Fosc         BAUD       SMOD        RELOAD  
 //12           2400         0              f3
 //12           2400         1              e5
 //12           1200         0              e5
 //12           1200         1              cb
 //11.0592      9600         0              fd

 void Uart_init()                  //串口初始化
 {
     flag_UART=0;                  //未收到数据标识
 //    PCON = PCON | 0x80;              //波特率加倍
     SCON=0x50;                    //0101,0000  8位可变比特率,无奇偶校验
     TMOD=0x21;                      //定时器1为8位自动重装计数器 ,定时器0为16位计数器
     TH1=Reload_Value_T1;              //初值
     TL1=Reload_Value_T1;
     TR1=1;                          //启动
     ES=1;                          //允许串口中断
     EA=1;                          //允许总中断

     ClrRxbuffer_UART();
 }

 void SendOneByte(unsigned char xx)        //发送一个字节
 {
   ES=0;
   TI=0;
   SBUF=xx;
   while(!TI);                              //等待发送完成
   TI=0;
   ES=1;
 }

 void intrupt(void) interrupt 4              //串口中断处理函数
 {
     if(RI==1)
     {
      Is_Uart_Receive();
     }
     TI=0;
 }

 void Is_Uart_Receive()
 {
     unsigned char str;
     RI=0;
     str=SBUF;

     Rxbuffer_UART[Rxpos_UART++]=SBUF;       //接收数据放入缓冲区
 //    if(Rxpos_UART>=LEN_UART_BUFFER)
 //       Rxpos_UART=0;
     if(str=='*')                           //收到结束符
     {
         flag_UART=1;                       //标识
     }
     //SendOneByte(str);                    //回发

 }

 unsigned char IsReceived_UART()
 {
     return flag_UART;                       //返回标志供主函数调用
 }

 void ClrFlag_UART()
 {
     flag_UART=0;                           //清除标志供主函数调用
 }

 unsigned char* Return_Rxbuffer_UART()
 {
     return     Rxbuffer_UART;                   //返回接收缓冲区供主函数调用
 }

 void ClrRxbuffer_UART()
 {    unsigned char i=0;
     for(i=0;i<LEN_UART_BUFFER;i++)
       {
           Rxbuffer_UART[i]='\0';             //清空缓冲区
       }
     Rxpos_UART=0;                           //指针归零
 }

 unsigned char Return_Recv_Len()               //返回接收到的数量
 {
     return     Rxpos_UART;                       //返回接收缓冲区供主函数调用
 }

STC12C5A60S2.h

//--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机内核特殊功能寄存器 C51 Core SFRs
 //                                          7     6      5       4     3    2    1     0   Reset Value
 sfr ACC  = 0xE0; //Accumulator                                                              0000,0000
 sfr B    = 0xF0; //B Register                                                               0000,0000
 sfr PSW  = 0xD0; //Program Status Word      CY    AC    F0    RS1   RS0    OV    F1    P    0000,0000
 //-----------------------------------
 sbit CY  = PSW^7;
 sbit AC  = PSW^6;
 sbit F0  = PSW^5;
 sbit RS1 = PSW^4;
 sbit RS0 = PSW^3;
 sbit OV  = PSW^2;
 sbit P   = PSW^0;
 //-----------------------------------
 sfr SP   = 0x81; //Stack Pointer                                                            0000,0111
 sfr DPL  = 0x82; //Data Pointer Low Byte                                                    0000,0000
 sfr DPH  = 0x83; //Data Pointer High Byte                                                   0000,0000
 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机系统管理特殊功能寄存器
 //                                          7     6      5    4     3      2    1     0     Reset Value
 sfr PCON   = 0x87; //Power Control        SMOD  SMOD0  LVDF  POF   GF1    GF0   PD   IDL    0001,0000
 //                                        7     6       5      4     3      2      1      0   Reset Value
 sfr AUXR  = 0x8E; //Auxiliary Register  T0x12 T1x12 UART_M0x6 BRTR S2SMOD BRTx12 EXTRAM S1BRS  0000,0000
 //-----------------------------------
 sfr AUXR1 = 0xA2; //Auxiliary Register 1  -  PCA_P4  SPI_P4  S2_P4  GF2    ADRJ   -    DPS  0000,0000
 /*


PCA_P4:
    0, 缺省PCA 在P1 口
    1,PCA/PWM 从P1 口切换到P4 口: ECI 从P1.2 切换到P4.1 口,
                                   PCA0/PWM0 从P1.3 切换到P4.2 口
                                   PCA1/PWM1 从P1.4 切换到P4.3 口
SPI_P4:
    0, 缺省SPI 在P1 口
    1,SPI 从P1 口切换到P4 口: SPICLK 从P1.7 切换到P4.3 口
                               MISO 从P1.6 切换到P4.2 口
                               MOSI 从P1.5 切换到P4.1 口
                               SS 从P1.4 切换到P4.0 口
S2_P4:
    0, 缺省UART2 在P1 口
    1,UART2 从P1 口切换到P4 口: TxD2 从P1.3 切换到P4.3 口
                                 RxD2 从P1.2 切换到P4.2 口
GF2: 通用标志位

ADRJ:
    0, 10 位A/D 转换结果的高8 位放在ADC_RES 寄存器, 低2 位放在ADC_RESL 寄存器
    1,10 位A/D 转换结果的最高2 位放在ADC_RES 寄存器的低2 位, 低8 位放在ADC_RESL 寄存器

DPS: 0, 使用缺省数据指针DPTR0
     1,使用另一个数据指针DPTR1

*/
 //-----------------------------------
 sfr WAKE_CLKO = 0x8F; //附加的 SFR WAK1_CLKO
 /*
       7            6          5          4          3       2       1      0         Reset Value
    PCAWAKEUP  RXD_PIN_IE  T1_PIN_IE  T0_PIN_IE  LVD_WAKE    _    T1CLKO  T0CLKO      0000,0000B

 b7 - PCAWAKEUP : PCA 中断可唤醒 powerdown。
 b6 - RXD_PIN_IE : 当 P3.0(RXD) 下降沿置位 RI 时可唤醒 powerdown(必须打开相应中断)。
 b5 - T1_PIN_IE : 当 T1 脚下降沿置位 T1 中断标志时可唤醒 powerdown(必须打开相应中断)。
 b4 - T0_PIN_IE : 当 T0 脚下降沿置位 T0 中断标志时可唤醒 powerdown(必须打开相应中断)。
 b3 - LVD_WAKE : 当 CMPIN 脚低电平置位 LVD 中断标志时可唤醒 powerdown(必须打开相应中断)。
 b2 -
 b1 - T1CLKO : 允许 T1CKO(P3.5) 脚输出 T1 溢出脉冲,Fck1 = 1/2 T1 溢出率
 b0 - T0CLKO : 允许 T0CKO(P3.4) 脚输出 T0 溢出脉冲,Fck0 = 1/2 T1 溢出率
 */
 //-----------------------------------
 sfr CLK_DIV = 0x97; //Clock Divder          -     -      -       -     -  CLKS2 CLKS1 CLKS0 xxxx,x000
 //-----------------------------------
 sfr BUS_SPEED = 0xA1; //Stretch register      -     -    ALES1   ALES0   -   RWS2  RWS1  RWS0 xx10,x011
 /*
 ALES1 and ALES0:
 00 : The P0 address setup time and hold time to ALE negative edge is one clock cycle
 01 : The P0 address setup time and hold time to ALE negative edge is two clock cycles.
 10 : The P0 address setup time and hold time to ALE negative edge is three clock cycles. (default)
 11 : The P0 address setup time and hold time to ALE negative edge is four clock cycles.

 RWS2,RWS1,RWS0:
   000 : The MOVX read/write pulse is 1 clock cycle.
   001 : The MOVX read/write pulse is 2 clock cycles.
   010 : The MOVX read/write pulse is 3 clock cycles.
   011 : The MOVX read/write pulse is 4 clock cycles. (default)
   100 : The MOVX read/write pulse is 5 clock cycles.
   101 : The MOVX read/write pulse is 6 clock cycles.
   110 : The MOVX read/write pulse is 7 clock cycles.
   111 : The MOVX read/write pulse is 8 clock cycles.
 */
 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机中断特殊功能寄存器
 //有的中断控制、中断标志位散布在其它特殊功能寄存器中,这些位在位地址中定义
 //其中有的位无位寻址能力,请参阅 新一代 1T 8051系列 单片机中文指南
 //                                           7     6     5    4     3    2    1    0   Reset Value
 sfr IE      = 0xA8;  //中断控制寄存器        EA  ELVD  EADC   ES   ET1  EX1  ET0  EX0  0x00,0000
 //-----------------------
 sbit EA       = IE^7;
 sbit ELVD     = IE^6; //低压监测中断允许位
 sbit EADC     = IE^5; //ADC 中断允许位
 sbit ES       = IE^4;
 sbit ET1      = IE^3;
 sbit EX1      = IE^2;
 sbit ET0      = IE^1;
 sbit EX0      = IE^0;
 //-----------------------
 sfr IE2       = 0xAF;  //Auxiliary Interrupt   -     -     -    -     -    -  ESPI  ES2  0000,0000B
 //-----------------------
 //                                          7     6     5    4    3    2    1    0    Reset Value
 sfr IP      = 0xB8; //中断优先级低位      PPCA  PLVD  PADC  PS   PT1  PX1  PT0  PX0   0000,0000
 //--------
 sbit PPCA     = IP^7;  //PCA 模块中断优先级
 sbit PLVD     = IP^6;  //低压监测中断优先级
 sbit PADC     = IP^5;  //ADC 中断优先级
 sbit PS       = IP^4;
 sbit PT1      = IP^3;
 sbit PX1      = IP^2;
 sbit PT0      = IP^1;
 sbit PX0      = IP^0;
 //-----------------------
 //                                         7      6      5     4     3     2     1     0    Reset Value
 sfr IPH   = 0xB7; //中断优先级高位       PPCAH  PLVDH  PADCH  PSH  PT1H  PX1H  PT0H  PX0H   0000,0000
 sfr IP2   = 0xB5; //                       -      -      -     -     -     -   PSPI   PS2   xxxx,xx00
 sfr IPH2  = 0xB6; //                       -      -      -     -     -     -   PSPIH  PS2H  xxxx,xx00
 //-----------------------
 //新一代 1T 8051系列 单片机I/O 口特殊功能寄存器
 //                                      7     6     5     4     3     2     1     0         Reset Value
 sfr P0   = 0x80; //8 bitPort0          P0.7  P0.6  P0.5  P0.4  P0.3  P0.2  P0.1  P0.0       1111,1111
 sfr P0M0 = 0x94; //                                                                         0000,0000
 sfr P0M1 = 0x93; //                                                                         0000,0000
 sfr P1   = 0x90; //8 bitPort1          P1.7  P1.6  P1.5  P1.4  P1.3  P1.2  P1.1  P1.0       1111,1111
 sfr P1M0 = 0x92; //                                                                         0000,0000
 sfr P1M1 = 0x91; //                                                                         0000,0000
 sfr P1ASF = 0x9D; //P1 analog special function
 sfr P2   = 0xA0; //8 bitPort2          P2.7  P2.6  P2.5  P2.4  P2.3  P2.2  P2.1  P2.0       1111,1111
 sfr P2M0 = 0x96; //                                                                         0000,0000
 sfr P2M1 = 0x95; //                                                                         0000,0000
 sfr P3   = 0xB0; //8 bitPort3          P3.7  P3.6  P3.5  P3.4  P3.3  P3.2  P3.1  P3.0       1111,1111
 sfr P3M0 = 0xB2; //                                                                         0000,0000
 sfr P3M1 = 0xB1; //                                                                         0000,0000
 sfr P4   = 0xC0; //8 bitPort4          P4.7  P4.6  P4.5  P4.4  P4.3  P4.2  P4.1  P4.0       1111,1111
 sfr P4M0 = 0xB4; //                                                                         0000,0000
 sfr P4M1 = 0xB3; //                                                                         0000,0000
 //                                      7      6         5         4      3     2     1     0     Reset Value
 sfr P4SW = 0xBB; //Port-4 switch        -   LVD_P4.6  ALE_P4.5  NA_P4.4   -     -     -     -        x000,xxxx

 sfr P5   = 0xC8; //8 bitPort5           -     -       -      -    P5.3  P5.2  P5.1  P5.0    xxxx,1111
 sfr P5M0 = 0xCA; //                                                                         0000,0000
 sfr P5M1 = 0xC9; //                                                                         0000,0000
 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机定时器特殊功能寄存器
 //                                          7     6     5     4     3     2     1     0     Reset Value
 sfr TCON = 0x88; //T0/T1 Control           TF1   TR1   TF0   TR0   IE1   IT1   IE0   IT0    0000,0000
 //-----------------------------------
 sbit TF1 = TCON^7;
 sbit TR1 = TCON^6;
 sbit TF0 = TCON^5;
 sbit TR0 = TCON^4;
 sbit IE1 = TCON^3;
 sbit IT1 = TCON^2;
 sbit IE0 = TCON^1;
 sbit IT0 = TCON^0;
 //-----------------------------------
 sfr TMOD = 0x89; //T0/T1 Modes             GATE1 C/T1  M1_1  M1_0  GATE0 C/T0  M0_1  M0_0   0000,0000
 sfr TL0  = 0x8A; //T0 Low Byte                                                              0000,0000
 sfr TH0  = 0x8C; //T0 High Byte                                                             0000,0000
 sfr TL1  = 0x8B; //T1 Low Byte                                                              0000,0000
 sfr TH1  = 0x8D; //T1 High Byte                                                             0000,0000
 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机串行口特殊功能寄存器
 //                                          7     6     5     4     3     2     1     0     Reset Value
 sfr SCON = 0x98; //Serial Control         SM0/FE SM1   SM2   REN   TB8   RB8    TI    RI    0000,0000
 //-----------------------------------
 sbit SM0 = SCON^7;  //SM0/FE
 sbit SM1 = SCON^6;
 sbit SM2 = SCON^5;
 sbit REN = SCON^4;
 sbit TB8 = SCON^3;
 sbit RB8 = SCON^2;
 sbit TI  = SCON^1;
 sbit RI  = SCON^0;
 //-----------------------------------
 sfr SBUF = 0x99; //Serial Data Buffer                                                     xxxx,xxxx
 sfr SADEN = 0xB9; //Slave Address Mask                                                    0000,0000
 sfr SADDR = 0xA9; //Slave Address                                                         0000,0000
 //-----------------------------------
 //                                7      6      5      4      3      2     1     0        Reset Value
 sfr S2CON = 0x9A; //S2 Control  S2SM0  S2SM1  S2SM2  S2REN  S2TB8  S2RB8  S2TI  S2RI      00000000B
 sfr S2BUF = 0x9B; //S2 Serial Buffer                                                      xxxx,xxxx
 sfr BRT = 0x9C; //S2 Baud-Rate Timer                                                    0000,0000
 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机看门狗定时器特殊功能寄存器
 sfr WDT_CONTR = 0xC1; //Watch-Dog-Timer Control register
 //                                      7     6     5      4       3      2   1   0     Reset Value
 //                                  WDT_FLAG  -  EN_WDT CLR_WDT IDLE_WDT PS2 PS1 PS0    xx00,0000
 //-----------------------

 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机PCA/PWM 特殊功能寄存器
 //                                         7     6     5     4     3     2     1     0     Reset Value
 sfr CCON   = 0xD8;   //PCA 控制寄存器。    CF    CR    -     -     -     -    CCF1  CCF0   00xx,xx00
 //-----------------------
 sbit CF     = CCON^7; //PCA计数器溢出标志,由硬件或软件置位,必须由软件清0。
 sbit CR     = CCON^6; //1:允许 PCA 计数器计数, 必须由软件清0。
 //-
 //-
 sbit CCF1   = CCON^1; //PCA 模块1 中断标志, 由硬件置位, 必须由软件清0。
 sbit CCF0   = CCON^0; //PCA 模块0 中断标志, 由硬件置位, 必须由软件清0。
 //-----------------------
 sfr CMOD  = 0xD9; //PCA 工作模式寄存器。   CIDL   -     -     -   CPS2   CPS1  CPS0  ECF   0xxx,x000
 /*
 CIDL: idle 状态时 PCA 计数器是否继续计数, 0: 继续计数, 1: 停止计数。

 CPS2: PCA 计数器脉冲源选择位 2。
 CPS1: PCA 计数器脉冲源选择位 1。
 CPS0: PCA 计数器脉冲源选择位 0。
    CPS2   CPS1   CPS0
     0      0      0    系统时钟频率 fosc/12。
     0      0      1    系统时钟频率 fosc/2。
     0      1      0    Timer0 溢出。
     0      1      1    由 ECI/P3.4 脚输入的外部时钟,最大 fosc/2。
     1      0      0    系统时钟频率,  Fosc/1
     1      0      1    系统时钟频率/4,Fosc/4
     1      1      0    系统时钟频率/6,Fosc/6
     1      1      1    系统时钟频率/8,Fosc/8

 ECF: PCA计数器溢出中断允许位, 1--允许 CF(CCON.7) 产生中断。
 */
 //-----------------------
 sfr CL     = 0xE9; //PCA 计数器低位                                                        0000,0000
 sfr CH     = 0xF9; //PCA 计数器高位                                                        0000,0000
 //-----------------------
 //                                         7     6      5      4     3     2     1     0     Reset Value
 sfr CCAPM0 = 0xDA; //PCA 模块0 PWM 寄存器  -   ECOM0  CAPP0  CAPN0  MAT0  TOG0  PWM0  ECCF0   x000,0000
 sfr CCAPM1 = 0xDB; //PCA 模块1 PWM 寄存器  -   ECOM1  CAPP1  CAPN1  MAT1  TOG1  PWM1  ECCF1   x000,0000

 //ECOMn = 1:允许比较功能。
 //CAPPn = 1:允许上升沿触发捕捉功能。
 //CAPNn = 1:允许下降沿触发捕捉功能。
 //MATn  = 1:当匹配情况发生时, 允许 CCON 中的 CCFn 置位。
 //TOGn  = 1:当匹配情况发生时, CEXn 将翻转。
 //PWMn  = 1:将 CEXn 设置为 PWM 输出。
 //ECCFn = 1:允许 CCON 中的 CCFn 触发中断。

 //ECOMn  CAPPn  CAPNn  MATn  TOGn  PWMn  ECCFn
 //  0      0      0     0     0     0     0   0x00   未启用任何功能。
 //  x      1      0     0     0     0     x   0x21   16位CEXn上升沿触发捕捉功能。
 //  x      0      1     0     0     0     x   0x11   16位CEXn下降沿触发捕捉功能。
 //  x      1      1     0     0     0     x   0x31   16位CEXn边沿(上、下沿)触发捕捉功能。
 //  1      0      0     1     0     0     x   0x49   16位软件定时器。
 //  1      0      0     1     1     0     x   0x4d   16位高速脉冲输出。
 //  1      0      0     0     0     1     0   0x42   8位 PWM。

 //ECOMn  CAPPn  CAPNn  MATn  TOGn  PWMn  ECCFn
 //  0      0      0     0     0     0     0   0x00   无此操作
 //  1      0      0     0     0     1     0   0x42   普通8位PWM, 无中断
 //  1      1      0     0     0     1     1   0x63   PWM输出由低变高可产生中断
 //  1      0      1     0     0     1     1   0x53   PWM输出由高变低可产生中断
 //  1      1      1     0     0     1     1   0x73   PWM输出由低变高或由高变低都可产生中断

 //-----------------------
 sfr CCAP0L = 0xEA; //PCA 模块 0 的捕捉/比较寄存器低 8 位。                                    0000,0000
 sfr CCAP0H = 0xFA; //PCA 模块 0 的捕捉/比较寄存器高 8 位。                                    0000,0000
 sfr CCAP1L = 0xEB; //PCA 模块 1 的捕捉/比较寄存器低 8 位。                                    0000,0000
 sfr CCAP1H = 0xFB; //PCA 模块 1 的捕捉/比较寄存器高 8 位。                                    0000,0000
 //-----------------------
 //                                                       7   6   5   4   3   2    1     0    Reset Value
 sfr PCA_PWM0 = 0xF2; //PCA 模块0 PWM 寄存器。            -   -   -   -   -   -  EPC0H EPC0L   xxxx,xx00
 sfr PCA_PWM1 = 0xF3; //PCA 模块1 PWM 寄存器。            -   -   -   -   -   -  EPC1H EPC1L   xxxx,xx00
 //PCA_PWMn:    7      6      5      4      3      2      1      0
 //             -      -      -      -      -      -    EPCnH  EPCnL
 //B7-B2: 保留
 //B1(EPCnH): 在 PWM 模式下,与 CCAPnH 组成 9 位数。
 //B0(EPCnL): 在 PWM 模式下,与 CCAPnL 组成 9 位数。
 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机 ADC 特殊功能寄存器
 //                                            7        6      5       4         3      2    1    0   Reset Value
 sfr ADC_CONTR = 0xBC; //A/D 转换控制寄存器 ADC_POWER SPEED1 SPEED0 ADC_FLAG ADC_START CHS2 CHS1 CHS0 0000,0000
 sfr ADC_RES  = 0xBD;  //A/D 转换结果高8位 ADCV.9 ADCV.8 ADCV.7 ADCV.6 ADCV.5 ADCV.4 ADCV.3 ADCV.2     0000,0000
 sfr ADC_RESL = 0xBE;  //A/D 转换结果低2位                                           ADCV.1 ADCV.0     0000,0000
 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机 SPI 特殊功能寄存器
 //                                      7     6     5     4     3     2     1     0    Reset Value
 sfr SPCTL  = 0xCE; //SPI Control Register  SSIG  SPEN  DORD  MSTR  CPOL  CPHA  SPR1  SPR0  0000,0100
 sfr SPSTAT = 0xCD; //SPI Status Register   SPIF  WCOL   -     -     -     -     -     -    00xx,xxxx
 sfr SPDAT  = 0xCF; //SPI Data Register                                                     0000,0000
 //--------------------------------------------------------------------------------
 //新一代 1T 8051系列 单片机 IAP/ISP 特殊功能寄存器
 sfr IAP_DATA    = 0xC2;
 sfr IAP_ADDRH   = 0xC3;
 sfr IAP_ADDRL   = 0xC4;
 //                                                7    6    5      4    3    2    1     0    Reset Value
 sfr IAP_CMD     = 0xC5; //IAP Mode Table          0    -    -      -    -    -   MS1   MS0   0xxx,xx00
 sfr IAP_TRIG    = 0xC6;
 sfr IAP_CONTR   = 0xC7; //IAP Control Register  IAPEN SWBS SWRST CFAIL  -   WT2  WT1   WT0   0000,x000
 //--------------------------------------------------------------------------------



验证用的测试程序

采集测试程序_输出15Hz占空比50%

#include"STC12C5A60S2.H"
 sbit PulserOut=P3^2;
 sbit LED=P3^3;
 /*为了测试采集系统发出的20Hz的信号*/
 int NumTimer=0;     //当前计数秒数指示
 int NumNextOn=0;   //高电平时间
 int NumNextOff=0;  //低电平时间

 int index=0;
 char current=0;//记录当前是高还是低,用PulserOut有个隐患,当判断是高还是低的时候其实是读进                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
 void Timer_Init();
 void StartTimer();
 /*int code CodeTime[]={40,20,40,20,40,20,100,20,40,20,40,20,100,20,40,20,40,20}; */    /*低*//*高*/
 //正表示有脉冲时间20->1s,负表示无脉冲时间20->1s            

 /*
 //    TH0=(65536-46080)/256;    //定时50ms--11.0592
 //    TL0=(65536-46080)%256;
 //    TH0=(65536-4608)/256;    //定时5ms--11.0592
 //    TL0=(65536-4608)%256;

 //    TH0=(65536-3000)/256;    //定时3ms--12
 //    TL0=(65536-3000)%256;
     TH0=(65536-2765)/256;    //定时3ms--11.0592
     TL0=(65536-2765)%256;
 */
 //#define RELOAD_H0  (65536-3072)/256    //1/300S--11.0592
 //#define RELOAD_L0  (65536-3072)%256
 #define RELOAD_H0  (65536-3333)/256        //1/300S--12
 #define RELOAD_L0  (65536-3333)%256
 unsigned int code CodeTime[]={10,10/*高*/,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10};      //1/30s占空比50%
 //正表示有脉冲时间1->5ms,负表示无脉冲时间1->5ms     这个数组根据数据编码改变,以改变脉冲    
 void main()
 {  
     P3M1=0;
     P3M0=12;//.3和.2推挽输出提高负载能力
     index=0;
     NumNextOff=CodeTime[0];       
     StartTimer();
     while(1)
     {
      }
                     
 }
 void Timer_Init()
 {
     TMOD=0x01; //定时器0方式1
     TH0=RELOAD_H0;
     TL0=RELOAD_L0;
     EA=1;
     ET0=1;
     TR0=1;
 }
 Timer0()interrupt 1 using 1
 {
     NumTimer=NumTimer+1;
     if((current==0)/*当前是低!!!!*/ && (NumNextOff==NumTimer)) //低定时到了
     {
         
         NumTimer=0;
         PulserOut=1;
         current=1;
         LED=1;
         index++;
         if(18==index)//其实这个不会发生
         {
             index=0;
         }
         NumNextOn=CodeTime[index];//下一次高电平时间
     }
     else if((current==1)/*当前是高!!!!*/ && (NumNextOn==NumTimer)) //高定时到了
     {
         NumTimer=0;
         PulserOut=0;
         current=0;
         LED=0;
         index++;
         if(18==index)
         {
             index=0;
         }
         NumNextOff=CodeTime[index];//下一次低电平时间
     }
     TH0=RELOAD_H0;
     TL0=RELOAD_L0;
 }

 void StartTimer()
 {
     PulserOut=0; //开始无脉冲
     current=0;

     LED=0;
     NumTimer=0;
     Timer_Init();    
 }