понеделник, 23 април 2018 г.

DCC Dekoder с Ардуино Pro-Mini и BA6209

Това ще бъде третия ми декодер изпълнен с Ардуино Pro-Mini
Този път за управление на мотора ще използвам интеграла BA6209
Кода ще бъде абсолютно същия като от първия ми пост
защото този интеграл се управлява с два входа пиновете 5 и 6
които по случайност съвпадат с пиновете 5 и 6 на Ардуино Pro-Mini
и ща бъдат вързани 5 със 5 и 6 със 6 като може да се види от следната
схема която ще ползвам




Ето и самия код за ардуино същия е като в първия ми проект
с малки промени адреса е вече 6 и CV2 = 100 но това
са настройки които зависят от конкретния двигател и драйвъра за управлението му
За функцията F1 съм закачил вградения светодиот на ардуиното понеже за конкретния локомотив няма за какво да я ползвам друго

/* Декодер за локомотиви H0 с Arduino nano и BA6209
 * Библиотека NmraDcc.h
 * Стандартният адрес на локомотива е #define This_Decoder_Address
 * Изходите PWM на Arduino Nano са 5 и 6 
 * Светлините са свързани към пинове 3 и 4 на Arduino Nano
  * Основни CV: CV2 = Начално напрежение; CV3 = скорост на ускоряване; CV4 = степен на спиране; CV5 = максимално скоростно напрежение
*/
#include <NmraDcc.h>

#define This_Decoder_Address 6 //Променете този номер, за да промените адреса на локомотива
const int FunctionPin0 = 3;// pin 3 and 4 light
//const int FunctionPin1 = 7;
const int FunctionPin1 = LED_BUILTIN;
const int FunctionPin2 = 8;
const int FunctionPin3 = 9;
const int FunctionPin4 = 10;
const int DccAckPin = 15 ;//за индикация на промяна на CV
const int PWM_MAX = 254;
//const int left = 10;        //Pin изход към left L293
//const int right = 11;       //Pin изход към right L293
const int velPwm0 = 5;       
const int velPwm1 = 6;
const int lucesDelanteras = 3; //pin изход към предни светлини
const int lucesTraseras = 4;   //pin изход към задни светлини
int currentSpeed = 0;
int rateSteps;
int acSpeed;
int locSpeed = 0;
int rateSpeed = 0;
int dirState = 0;     //Променлива промяна на посоката
int dirFlag1 = 0;
int dirFlag2 = 0;
int steps;            //Pasos (14, 28, 128) стъпки
//int maniobras = 0;    //маневрени
int Luces = 0;        //Variable включване на светлините
//-------------CV конфигуриране
int CV2 = 100;        //начално напрежение за скорост 1
int CV3 = 4;          //Степен на ускоряване (CV3 * intervalAcc)
int CV4 = 4;          //Скорост на спиране (CV4 * intervalDec)
int CV5 = 254;        //Максимално напрежение
//-------------
int topSpeed =  CV5;
long previousDebug = 0;
long previousAcc = 0;
long previousDec = 0;
long intervalDebug = (500);
long intervalAcc = (25 * CV3);
long intervalDec = (25 * CV4);
struct CVPair
{
  uint16_t  CV;
  uint8_t   Value;
};

CVPair FactoryDefaultCVs [] =
{
  // The CV Below defines the Short DCC Address
  {CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address},
  {CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
  // These two CVs define the Long DCC Address
  {CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
  {CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, This_Decoder_Address},

  // ONLY uncomment 1 CV_29_CONFIG line below as approprate
  //  {CV_29_CONFIG,                                      0}, // Short Address 14 Speed Steps
  {CV_29_CONFIG,                       CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps
  //  {CV_29_CONFIG, CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, // Long  Address 28/128 Speed Steps
};

NmraDcc  Dcc ;
DCC_MSG  Packet ;

uint8_t FactoryDefaultCVIndex = 0;

// Uncomment this line below to force resetting the CVs back to Factory Defaults
// FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);

void notifyCVResetFactoryDefault()
{
  // Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
  // to flag to the loop() function that a reset to Factory Defaults needs to be done
  FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs) / sizeof(CVPair);
};

// Uncomment the #define below to print all Speed Packets
#define NOTIFY_DCC_SPEED
#ifdef  NOTIFY_DCC_SPEED
void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps )
{
  // Serial.print("notifyDccSpeed: Addr: ");
  // Serial.print(Addr, DEC);
  // Serial.print( (AddrType == DCC_ADDR_SHORT) ? "-S" : "-L" );
  // Serial.print(" Speed: ");
  // Serial.print(Speed, DEC);
  //  locSpeed = Speed;
  acSpeed = Speed;
  // Serial.print(" Steps: ");
  int SpeedStep = (SpeedSteps - 1);
  steps = SpeedStep;
  //  Serial.print(SpeedStep, DEC);
  //  Serial.print(" Dir: ");
  //  Serial.println( (Dir == DCC_DIR_FWD) ? "Forward" : "Reverse" );
  dirState = Dir;
};
#endif

// Uncomment the #define below to print all Function Packets
#define NOTIFY_DCC_FUNC
//#ifdef  NOTIFY_DCC_FUNC
void notifyDccFunc(uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState)
{
  // Serial.print("notifyDccFunc: Addr: ");
  //Serial.print(Addr, DEC);
  //Serial.print( (AddrType == DCC_ADDR_SHORT) ? 'S' : 'L' );
  //Serial.print("  Function Group: ");
  //Serial.print(FuncGrp, DEC);
  switch ( FuncGrp )
  {
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
    case FN_0:
      //  Serial.print(" FN0: ");
      //  Serial.println((FuncState & FN_BIT_00) ? "1  " : "0  ");
      break;
#endif
    case FN_0_4:
      exec_function( 0, (FuncState & FN_BIT_00) >> 4 );
      exec_function( 1, (FuncState & FN_BIT_01));
      exec_function( 2, (FuncState & FN_BIT_02) >> 1);
      exec_function( 3, (FuncState & FN_BIT_03) >> 2 );
      exec_function( 4, (FuncState & FN_BIT_04) >> 3 );
      if (Dcc.getCV(CV_29_CONFIG) & CV29_F0_LOCATION) // Only process Function 0 in this packet if we're not in Speed Step 14 Mode
      {
        //  Serial.print(" FN 0: ");
        //  Serial.print((FuncState & FN_BIT_00) ? "1  " : "0  ");
        // delay (100);
      }
      break;

    case FN_5_8:
      exec_function( 5, (FuncState & FN_BIT_05));
      exec_function( 6, (FuncState & FN_BIT_06) >> 1 );
      exec_function( 7, (FuncState & FN_BIT_07) >> 2 );
      exec_function( 8, (FuncState & FN_BIT_08) >> 3 );
      break;

    case FN_9_12:
      exec_function( 9, (FuncState & FN_BIT_09));
      exec_function( 10, (FuncState & FN_BIT_10) >> 1 );
      exec_function( 11, (FuncState & FN_BIT_11) >> 2 );
      exec_function( 12, (FuncState & FN_BIT_12) >> 3 );
      break;
  }
}
void exec_function (int f_index, int FuncState)  
{
  switch (f_index) {
    case 0:
      if (FuncState == 1) {
        Luces = 1;
      }
      else {
        Luces = 0;
      }
    case 1:
    FuncState ? digitalWrite(FunctionPin1, 1) : digitalWrite(FunctionPin1, 0);
      break;
    case 2:
    FuncState ? digitalWrite(FunctionPin2, 1) : digitalWrite(FunctionPin2, 0);
      break;
    case 3:
    FuncState ? digitalWrite(FunctionPin3, 1) : digitalWrite(FunctionPin3, 0);  
      break;
    case 4:
    FuncState ? digitalWrite(FunctionPin4, 1) : digitalWrite(FunctionPin4, 0);  
      break;  
    default:
      break;
  }
}

// This function is called by the NmraDcc library when a DCC ACK needs to be sent
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read

void notifyCVAck(void)
{
  //Serial.println("notifyCVAck") ;

  digitalWrite( DccAckPin, HIGH );
  delay( 8 );
  digitalWrite( DccAckPin, LOW );
}

void setup()
{
  pinMode(velPwm0, OUTPUT);//pin 5
  pinMode(velPwm1, OUTPUT);//pin 6
  pinMode (lucesDelanteras, OUTPUT);//pin 3
  pinMode (lucesTraseras, OUTPUT);//pin 4
  pinMode (FunctionPin1, OUTPUT);//pin 7
  digitalWrite(FunctionPin1, 0);
  pinMode (FunctionPin2, OUTPUT);//pin 8
  digitalWrite(FunctionPin2, 0);
  pinMode (FunctionPin3, OUTPUT);//pin 9
  digitalWrite(FunctionPin3, 0);
  pinMode (FunctionPin4, OUTPUT);//pin 10
  digitalWrite(FunctionPin4, 0);
  Serial.begin(115200);
  Serial.println("NMRA Dcc Multifunction Decoder Demo 1");

  // Configure the DCC CV Programing ACK pin for an output
  pinMode( DccAckPin, OUTPUT );
  digitalWrite( DccAckPin, LOW );

  // Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
  // Първият параметър е номерът за прекъсване, вторият е pin, а третият е състоянието на вътрешния pullup резистор (1 означава разрешено )
  Dcc.pin(0, 2, 0);

  // Call the main DCC Init function to enable the DCC Receiver
  //Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 );
//Първите два параметъра са производителят и версията на декодера С третия параметър можете да промените по подразбиране поведението на библиотеката: последният е адресът EEPROM, от който библиотеката съхранява своите стойности
  Dcc.init( MAN_ID_DIY, 10, FLAGS_MY_ADDRESS_ONLY, 0 );

  // Uncomment to force CV Reset to Factory Defaults
  notifyCVResetFactoryDefault();
}

void loop()
{
  // You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
  Dcc.process();

  if ( FactoryDefaultCVIndex && Dcc.isSetCVReady())
  {
    FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
    Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
  }

  if (dirState == 0) {
    if (dirFlag1 == 1) {
      int locSpeedup = locSpeed;
      for (int locSpeed = locSpeedup; locSpeed > CV2; locSpeed--) {
        digitalWrite (velPwm1, LOW);
        analogWrite (velPwm0, locSpeed);
        delay(intervalDec);
      }
      dirState0();

      for (int locSpeed = CV2; locSpeed < locSpeedup; locSpeed++) {
        digitalWrite (velPwm1, LOW);
        analogWrite (velPwm0, locSpeed);
        delay(intervalAcc);
      }
      dirFlag1 = 0;
    }
    dirFlag2 = 1;
    dirState0();
  }

if (dirState == 1) {
    if (dirFlag2 == 1) {
      int locSpeedup = locSpeed;
      for (int locSpeed = locSpeedup; locSpeed > CV2; locSpeed--) {
        digitalWrite (velPwm0, LOW);
        analogWrite (velPwm1, locSpeed);
        delay(intervalDec);
      }
      dirState1();

      for (int locSpeed = CV2; locSpeed < locSpeedup; locSpeed++) {
        digitalWrite (velPwm0, LOW);
        analogWrite (velPwm1, locSpeed);
        delay(intervalAcc);
      }
      dirFlag2 = 0;
    }
    dirFlag1 = 1;
    dirState1();
  }
  if (currentSpeed <= 1) {
    locSpeed = 0;
    digitalWrite (velPwm0, LOW);
    digitalWrite (velPwm1, LOW);
  }
  else {
    rateSpeed = ((PWM_MAX - CV2) / steps);
  }
  if (currentSpeed != acSpeed ) { //ако currentSteps е различен от стъпките
    if (currentSpeed < acSpeed) {
      Acc();
    }
    if (currentSpeed > acSpeed) {
      Dec();
    }
  }
  //analogWrite (velPwm, locSpeed);
  Debugger();
}
void Acc() 
{
  unsigned long currentAccMillis = millis();
  if (currentAccMillis - previousAcc > intervalAcc ) {
    previousAcc = currentAccMillis;
    locSpeed = (CV2 + (currentSpeed * rateSpeed));

    if (locSpeed >= CV5) {  //ако скоростта на локомотива е по - голяма от CV5
      locSpeed = CV5;
    }
    currentSpeed = (currentSpeed + 1);
    if (currentSpeed >= acSpeed) {
      currentSpeed = acSpeed;
    }
  }
  return;
}
void Dec() 
{
  unsigned long currentDecMillis = millis();
  if (currentDecMillis - previousDec > intervalDec) {
    previousDec = currentDecMillis;
    locSpeed = (CV2 + (currentSpeed * rateSpeed));
    currentSpeed = (currentSpeed - 1);
    if (currentSpeed <= acSpeed) {
      currentSpeed = acSpeed;
   }
    return;
  }
}
void dirState0() 
{
  digitalWrite (velPwm1, LOW);
  analogWrite (velPwm0, locSpeed);

  if (Luces == 1) {
    digitalWrite (lucesDelanteras, HIGH);
    digitalWrite (lucesTraseras, LOW);
  }
  else {
    digitalWrite (lucesDelanteras, LOW);
    digitalWrite (lucesTraseras, LOW);
  }
  return;
}
void dirState1() 
{
  digitalWrite (velPwm0, LOW);
  analogWrite (velPwm1, locSpeed);

  if (Luces == 1) {
    digitalWrite (lucesDelanteras, LOW);
    digitalWrite (lucesTraseras, HIGH);
  }
  else {
    digitalWrite (lucesDelanteras, LOW);
    digitalWrite (lucesTraseras, LOW);
  }
  return;
}
void Debugger() 
{
  unsigned long currentDebugMillis = millis();
  if (currentDebugMillis - previousDebug > intervalDebug) {
    previousDebug = currentDebugMillis;
    Serial.print(" Adr N: ");
    Serial.print(This_Decoder_Address);
    Serial.print(" sped PWM : ");
    Serial.print(locSpeed);
    Serial.print(" step : ");
    Serial.print(steps, DEC);
    Serial.print(" Dir: ");
    //Serial.println( (dirState == DCC_DIR_FWD) ? "Forward" : "Reverse" );
    Serial.println(dirState);
  }
  return;
}

Накрая ми се ще да спомена някой проблеми които могат да възникнат
поради факта че аз използвам най различни оптрони каквито имам същото е и за диода
Ако декодера не разпознава командите тоест на ардуиното свети червения диот
че има захранване от релсите но не реагира на командите на станцията
на мен ми се наложи да намаля стойноста на съпротивлението R3 по схемата
това което е във веригата на светодиода на оптрона .За различните оптрони стойноста е различна и варира от 4,7 к до 2,2к . Това ми отне малко време докато го разбера
защото го гледам на осцилоскопа импулсите са си перфектни и чак като се загледах по внимателно видях че при голяма стойност на съпротивлението дефакто П-образния импулс не слиза под 1волт и съответно ардуиното не го разпознавакато логическа 0 .
С намаляването на стойноста на съпротивлението от 4,7к на 3,6к или даже и по малко на 2,4к
нещата вече се оправят .Разбира се това зависи и от използвания диот така че аз закачвам на мястото на съпротивлението един потенциометър от 6,8к и едно ардуино уно с инсталирано на него примера от NmraDcc библиотеката NmraDccMultiFunctionDecoder_1 
закачва се пин 2 и + и - и отваряте серйния монитор и започвате бавно да намалявате потенциометара докато на екрана започнат да вървят командите след това измервате съпротивлението и слагате стандартна по малка стойност от измерената .
И друго нещо което може да възникне като проблем е да объркате колектор и емитер на транзистора на оптрона .Въпреки че аз за всеки отварям файла на производителя
това не гарантира че инфото е коректно казвам го защото два пъти ми се е случвало .
Ако всичко е вързано както трябва схемата тръгва от раз
и единствените настройки които трябва да направите в последствие евентуално е
стойноста на CV2 = 100  В зависимост от използвания мотор и драйвър може да варира от 60 до 120 . Но тя може и от централата да се настройва както и останалите от CV1 до CV5
Аз обаче предпочитам да го настроя в програмата и да презаредя много по лесно ми е .

Ето и две снимки на монтираните части към шасито на локомотива





Няма коментари:

Публикуване на коментар