近日完成了一个泥浆压力信号数据采集的系统方案,采集频率设置为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.h
void 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();
}