novelcrab 发表于 2014-12-16 23:21:34

arduino uno 第一次用于商业项目


       深入学习arduino基本上是从极客工坊这个论坛开始的,看了精华区的很多教程,非常感谢有这个论坛,也非常感谢那些愿意跟大家分享学习心得的朋友。

       为了回馈论坛,特地奉献上本人最近利用arduino uno开发的一个商业项目源代码,PCB以及其它器件资料因为商业因素,不便透露,敬请谅解。

      本项目用了18B20进行温度测量,用到PID对温度进行恒温控制,用到定时器TIMER1进行了步进电机的速度控制,另外对串口、EEPROM的读写操作也比较多,有兴趣的朋友可以作为参考。

         本人之前从事过一些C51项目的开发,对arduino其实不算精通,因此基本上是拿来主义,不求甚解,因此很多细节的问题我无法做深入的解答,但设计思路方面,可以与我讨论(其实最重要是解决问题的思路)。


#include <EEPROM.h>
unsigned char recordMaxID = 0;                                                                                  //组号
unsigned inttargetTemp = 0;                                                                                 //设定温度
unsigned char targetSpeed = 0;                                                                                  //设定速度
//========================================================================================================================================
#include <TimerOne.h>
#define PIN_ENCODER 2                                                                                                //光电位移计数开关(外部中断源0)
#define PIN_LIMIT 3                                                                                                //极限位置开关(外部中断源1)
#define PIN_ZERO 4                                                                                              //零位开关
#define PIN_PULSE 5                                                                                             //脉冲
#define PIN_DIR 6                                                                                             //方向
boolean pulseLevel = LOW;                                                                                       //脉冲
boolean pulseDir = HIGH;                                                                                        //方向(LOW-正转,HIGH-反转)
unsigned int Distence = 0;                                                                                        //位移计数值
boolean FlagRun = false;                                                                                        //运行状态
//========================================================================================================================================
#include <OneWire.h>
#include <DallasTemperature.h>
#define TEMPERATURE_PRECISION 12                                                                                //温度采集精度12位
#define PIN_TEMP8                                                                                                  //温度传感器                                                                                                                
DeviceAddress tempDeviceAddress1;
OneWire oneWire1(PIN_TEMP);                                 
DallasTemperature sensors1(&oneWire1);
double CurrentTemp = 0;
//========================================================================================================================================
#include <PID_v1.h>
#define PIN_BUMP 11                                                                                                //水泵
#define PIN_HOT12                                                                                                //加热管继电器
#define PIN_COOL 13                                                                                                //压缩机继电器
double Ki = 0,Kp = 0,Kd = 0;
double Setpoint, Input, Output;                                                                                        //目标温度值,输入值,输出值
PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);
int WindowSize = 5000;                                                                                                //温度控制周期5s
unsigned long windowStartTime = 0;

boolean FlagTempCtrl = false;
boolean BumpState = HIGH;
//========================================================================================================================================
unsigned char SampleID = 0;
unsigned int Ductility;
unsigned char readRecordID = 1;
//========================================================================================================================================
void initGPIO()
{
pinMode(PIN_ENCODER,INPUT);
pinMode(PIN_LIMIT,INPUT);
pinMode(PIN_ZERO,INPUT);

pinMode(PIN_BUMP,OUTPUT);
pinMode(PIN_HOT,OUTPUT);
pinMode(PIN_COOL,OUTPUT);

pinMode(PIN_PULSE,OUTPUT);
pinMode(PIN_DIR,OUTPUT);

digitalWrite(PIN_BUMP,HIGH);
digitalWrite(PIN_HOT,HIGH);
digitalWrite(PIN_COOL,HIGH);

digitalWrite(PIN_PULSE,LOW);
digitalWrite(PIN_DIR,LOW);
}
//========================================================================================================================================
void init18B20()
{
sensors1.begin();
sensors1.getAddress(tempDeviceAddress1,0);
sensors1.setResolution(tempDeviceAddress1,TEMPERATURE_PRECISION);
}

void initPID()
{
Setpoint = float(targetTemp / 10.0);
myPID.SetTunings(Kp,Ki,Kd);
myPID.SetOutputLimits(0, WindowSize);
myPID.SetControllerDirection(DIRECT);
myPID.SetMode(AUTOMATIC);
}

void tempCtrlStart()
{
//1.打开压缩机
initPID();
digitalWrite(PIN_COOL,LOW);       

//2.采集温度,温控输出
sensors1.requestTemperatures();
CurrentTemp = sensors1.getTempC(tempDeviceAddress1);
unsigned int Temp=CurrentTemp*100;
unsigned char tempChar = {
    0x5A,0xA5,0x05,0x82,0x00,0x0F, char(Temp>>8), char(Temp & 0x00FF)};
Serial.write(tempChar,8);

//3.温度控制
Input = CurrentTemp;
myPID.Compute();

unsigned long now = millis();
if(now - windowStartTime > WindowSize)
    windowStartTime += WindowSize;

if(Output < now - windowStartTime)
    digitalWrite(PIN_HOT,HIGH);//OFF
else
    digitalWrite(PIN_HOT,LOW);//ON
}

void tempCtrlStop()
{
FlagTempCtrl = false;
digitalWrite(PIN_COOL,HIGH);
digitalWrite(PIN_HOT,HIGH);
}

//========================================================================================================================================
unsigned long getTime;
void getDistance()
{
if(pulseDir == LOW)
{
    if(digitalRead(PIN_LIMIT) == LOW)
    {
      Timer1.stop();
    }   
}
if(pulseDir == HIGH)
{
    if(digitalRead(PIN_ZERO) == LOW)
    {
      Timer1.stop();
    }      
}

if(FlagRun == true)
{   
    if(digitalRead(PIN_ENCODER) == LOW)
    {
      unsigned int delayTime = 0;
      if(targetSpeed == 50)
      {
      delayTime = 480;
      }
      else
      {
      delayTime = 2400;
      }
      if((millis() - getTime) > delayTime)
      {
      getTime = millis();
      Distence++;
      unsigned char setChar = {
          0x5A,0xA5,0x05,0x82,0x00,0x09+SampleID,Distence>>8,Distence & 0x00FF };
      Serial.write(setChar,8);
      }
    }
}
}

void pulse()
{
getDistance();

pulseLevel =! pulseLevel;
digitalWrite(PIN_PULSE,pulseLevel);
}

void initTimer1()
{
Timer1.initialize(58);
Timer1.attachInterrupt(pulse);
Timer1.stop();
}

void moveMotor(unsigned int mySpeed ,boolean myDir)
{
digitalWrite(PIN_DIR,myDir);
pulseDir = myDir;
switch(mySpeed)
{
case 10:
    Timer1.setPeriod(2344);
    break;   
case 50:
    Timer1.setPeriod(469);
    break;   
case 400:
    Timer1.setPeriod(58);
    break;   
default:
    Timer1.setPeriod(58);   
    break;
}
Timer1.restart();
}
//========================================================================================================================================
void readSetting()
{
recordMaxID = EEPROM.read(0);
if(recordMaxID > 100)
{
    recordMaxID = 1;
    EEPROM.write(0,recordMaxID);
}
targetTemp = (EEPROM.read(1)<<8)+EEPROM.read(2);
targetSpeed = EEPROM.read(3);
unsigned int iKp,iKi,iKd;
iKp = (EEPROM.read(4)<<8)+EEPROM.read(5);
iKi = (EEPROM.read(6)<<8)+EEPROM.read(7);
iKd = (EEPROM.read(8)<<8)+EEPROM.read(9);
Ki = float(iKi);
Kd = float(iKd);
Kp = float(iKp);
unsigned char Para = {
    0x5A,0xA5,0x13,0x82,0x00,0x70,0x00,recordMaxID,targetTemp>>8,targetTemp & 0x00FF,0x00,targetSpeed,iKp>>8,iKp & 0x00FF,iKi>>8,iKi & 0x00FF,iKd>>8,iKd & 0x00FF,0x00,targetSpeed,targetTemp>>8,targetTemp & 0x00FF };
Serial.write(Para,22);
}

void saveSetting()
{
String indata = "";
unsigned char getPara = {
    0x5A,0xA5,0x04,0x83,0x00,0x71,0x05 };
Serial.write(getPara,7);
delay(200);
while (Serial.available() > 0)
{
    indata += char(Serial.read());
    delay(2);
}
if(indata.length() > 0)
{
    EEPROM.write(1,indata);
    EEPROM.write(2,indata);
    //targetTemp = (indata<<8)+indata;写法有错但没找到原因,所以用下一句实现
    targetTemp = (EEPROM.read(1)<<8)+EEPROM.read(2);
    EEPROM.write(3,indata);
    targetSpeed = indata;

    EEPROM.write(4,indata);
    EEPROM.write(5,indata);
    Kp = float((indata<<8)+indata);
    EEPROM.write(6,indata);
    EEPROM.write(7,indata);
    Ki = float((indata<<8)+indata);       
    EEPROM.write(8,indata);
    EEPROM.write(9,indata);
    Kd = float((indata<<8)+indata);       

    unsigned char setPara = {
      0x5A,0xA5,0x07,0x82,0x00,0x76,0x00,indata,indata,indata };
    Serial.write(setPara,10);
}
indata = "";
}

void saveRecord()
{
Ductility = Distence;
SampleID++;

if(SampleID == 3)
{
    Timer1.stop();
    FlagRun = false;
    unsigned char TestOFF = {
      0x5A,0xA5,0x05,0x82,0x00,0x12,0x00,0x00
    };
    Serial.write(TestOFF, 8);
    delay(50);
       
    SampleID = 0;
    unsigned int AVG = 0;
    AVG = int((Ductility+Ductility+Ductility)/3);
    unsigned char setPara = {
      0x5A,0xA5,0x05,0x82,0x00,0x0C,AVG>>8,AVG & 0x00FF};
    Serial.write(setPara,8);

    EEPROM.write(0,recordMaxID);
    EEPROM.write(20+recordMaxID*6 + 1,Ductility>>8);
    EEPROM.write(20+recordMaxID*6 + 2,Ductility & 0x00FF);
    EEPROM.write(20+recordMaxID*6 + 3,Ductility>>8);
    EEPROM.write(20+recordMaxID*6 + 4,Ductility & 0x00FF);
    EEPROM.write(20+recordMaxID*6 + 5,Ductility>>8);
    EEPROM.write(20+recordMaxID*6 + 6,Ductility & 0x00FF);          

    if(recordMaxID < 100)
      recordMaxID++;
    else
      recordMaxID = 1;   
}
}

void readRecord(unsigned char id)
{
delay(50);
unsigned int s1,s2,s3,avg;
s1=(EEPROM.read(20+id*6 + 1)<<8)+EEPROM.read(20+id*6 + 2);
s2=(EEPROM.read(20+id*6 + 3)<<8)+EEPROM.read(20+id*6 + 4);
s3=(EEPROM.read(20+id*6 + 5)<<8)+EEPROM.read(20+id*6 + 6);
avg=(s1+s2+s3)/3;

unsigned char setRecord = {
    0x5A,0xA5,0x0D,0x82,0x00,0x13,0x00,id,s1>>8,s1 & 0x00ff,s2>>8,s2 & 0x00ff,s3>>8,s3 & 0x00ff,avg>>8,avg & 0x00ff
};
Serial.write(setRecord,16);
}

//========================================================================================================================================
void readComm()
{
unsigned char TestOFF = {
   0x5A,0xA5,0x05,0x82,0x00,0x12,0x00,0x00};
unsigned char TestON = {
   0x5A,0xA5,0x05,0x82,0x00,0x12,0x00,0x01};
   
unsigned char TempON = {
   0x5A,0xA5,0x05,0x82,0x00,0x0E,0x00,0x01};   
unsigned char TempOFF = {
   0x5A,0xA5,0x05,0x82,0x00,0x0E,0x00,0x00};

unsigned char BumpON = {
   0x5A,0xA5,0x05,0x82,0x00,0x0D,0x00,0x01};
unsigned char BumpOFF = {
   0x5A,0xA5,0x05,0x82,0x00,0x0D,0x00,0x00};   

unsigned char setChar = {
   0x5A,0xA5,0x0b,0x82,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};   
unsigned char setmaxRecordID = {
   0x5A,0xA5,0x05,0x82,0x00,0x70,0x00,recordMaxID};

String indata = "";
while (Serial.available() > 0)
{
    indata += char(Serial.read());
    delay(2);
}
if(indata.length() > 0)
{
    switch (indata)
    {
    case 0x01://归零
      if(FlagRun == false)
      moveMotor(400,HIGH);
      break;
    case 0x02://记录
      if(FlagRun == true)
      saveRecord();
      break;
    case 0x03://前进
      if(FlagRun == false)
      moveMotor(targetSpeed,LOW);
      break;
    case 0x04://后退
      if(FlagRun == false)
      moveMotor(targetSpeed,HIGH);
      break;
    case 0x05://开始   
      if(FlagRun == false)
      {
      FlagRun = true;
        Serial.write(TestON, 8);
      delay(50);               
      
      SampleID = 0;
      Distence = 0;
      Ductility = 0;
      Ductility = 0;
      Ductility = 0;   

      Serial.write(setChar,14);
      delay(50);
      Serial.write(setmaxRecordID,8);
      
      moveMotor(targetSpeed,LOW);      
      }
      break;
    case 0x06://水泵
      BumpState = !BumpState;
      digitalWrite(PIN_BUMP,BumpState);   
      if(BumpState == false)
      {
      Serial.write(BumpON,8);      
      }
      else
      {       
      Serial.write(BumpOFF,8);
      }   
      break;
    case 0x07://温控
      FlagTempCtrl = !FlagTempCtrl;
      if(FlagTempCtrl == false)
      {
      tempCtrlStop();                     
      Serial.write(TempOFF, 8);
      }
      else
      {          
      Serial.write(TempON, 8);
      }
      break;
    case 0x08: //停止
      Timer1.stop();
      FlagRun = false;
      Serial.write(TestOFF, 8);
      break;
    case 0x11: //参数保存
      saveSetting();   
      break;   
    case 0x18://查询前翻页
      readRecordID--;
      if(readRecordID==0)
      {
      readRecordID = 100;
      }
      readRecord(readRecordID);
      break;
    case 0x19://查询后翻页
      readRecordID++;
      if(readRecordID==101)
      {
      readRecordID = 1;
      }
      readRecord(readRecordID);   
      break;
    }                 
}
indata = "";
}
//========================================================================================================================================
void setup()
{
unsigned char LED = {
    0x5A,0xA5,0x03,0x80,0x01,0x40 };
unsigned char PIC = {
    0x5A,0xA5,0x04,0x80,0x03,0x00,0x01 };
   
Serial.begin(9600);
delay(2500);
Serial.write(LED, 7);
delay(50);
Serial.write(PIC, 8);
delay(50);
readSetting();

initGPIO();
init18B20();

windowStartTime = millis();

initTimer1();
//moveMotor(400,HIGH);
}

void loop()
{
readComm();

if(FlagTempCtrl == true)
    tempCtrlStart();
}

mostblack 发表于 2014-12-17 08:19:03

小弟最近在做一款gprs考勤机,希望可以多多交流

zoologist 发表于 2014-12-17 08:33:46

是工业级别的应用还是消费级别的应用?

novelcrab 发表于 2014-12-17 09:04:59

zoologist 发表于 2014-12-17 08:33 static/image/common/back.gif
是工业级别的应用还是消费级别的应用?

算是工业级别吧

zoologist 发表于 2014-12-17 10:02:34

novelcrab 发表于 2014-12-17 09:04 static/image/common/back.gif
算是工业级别吧

不错! 厉害!

451506709 发表于 2014-12-17 14:11:20

5S的控制周期会不会有点大呢?

451506709 发表于 2014-12-17 14:24:15

代码中,“光电位移计数开关”和“极限位置开关”并没有用到外部中断哦

novelcrab 发表于 2014-12-18 21:10:22

451506709 发表于 2014-12-17 14:11 static/image/common/back.gif
5S的控制周期会不会有点大呢?

作为水温控制这样大滞后控制,控制周期为5-20s都是适宜的,所以我取了5秒。

在这个项目中,PID的控制效果应该说还可以,可以控制到0.25摄氏度。

PS:该水温环境有隔热保护,且几乎是密闭的,所以温控效果还令人满意。

novelcrab 发表于 2014-12-18 21:12:54

451506709 发表于 2014-12-17 14:24 static/image/common/back.gif
代码中,“光电位移计数开关”和“极限位置开关”并没有用到外部中断哦

在我尝试的过程中发现引脚的中断和定时器TIMER1这个库函数是有冲突的,会导致无法进入中断,所以采用电平检测的方式,而因为在主线程里有一个18B20测温,里面有几百毫秒的延时(delay函数),所以只能把电平检测挪到定时器函数里进行检测。
页: [1]
查看完整版本: arduino uno 第一次用于商业项目