неделя, 24 юни 2018 г.

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

Това е четвъртия ми декодер изпълнен с Ардуино Pro-Mini и
този път използвах моторен драйвър LB1838.
Използвах него защото имах локомотив на фирма Joef
на който поставих мотор от сд ром който работи на 9v захранване.
Както може да се види от описанието на LB1838 тя също има макс захранване 10,5v
което е направи подходяща за моя случай.

https://www.onsemi.com/pub/Collateral/LB1838M-D.PDF


Ако разгледате файла ще забележите че се управлява малко по различно
от предходните чипове и това налага леки корекций на кода .
Впредните схеми съм забравил да отбележа че масата на ардуино също се връзва към общата маса надявам се да сте се сетили и сами за това .

Ето я и самата електлическа схема

Използвал съм друг тип оптрон и стабилизатор на 9в .
Също така използвах за осветление два светодиода вместо само един
и стойноста на съпротивлението от 300 намалих на 100 ома
разбира се двата диода трябва да са абсолютно еднакви аз използвах два от светодиодна лента.


Това е и кода който трябва да заредите в ардуино



/* Декодер за локомотиви H0 с Arduino nano и Н-bridge
 * Библиотека 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 4 //Променете този номер, за да промените адреса на локомотива
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;  //PWM     
const int velPwm1 = 6;  // L-напред   H-назад
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 = 40;        //начално напрежение за скорост 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 )
{
  //  locSpeed = Speed;
  acSpeed = Speed;
  int SpeedStep = (SpeedSteps - 1);
  steps = SpeedStep;
  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)
{
  
  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:
    FuncState ? Luces = 1 : Luces = 0;
      break;
    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)
{
  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);

  // 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 (velPwm1, HIGH);
        analogWrite (velPwm0, locSpeed);
        delay(intervalDec);
      }
      dirState1();

      for (int locSpeed = CV2; locSpeed < locSpeedup; locSpeed++) {
        digitalWrite (velPwm1, HIGH);
        analogWrite (velPwm0, 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();
    }
  }
  
}
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);
locSpeed > (CV5-1) ? digitalWrite(velPwm0, HIGH) : analogWrite (velPwm0, locSpeed); //ако locSpeed е на макс пуска макс захранване не импулси

  if (Luces == 1) {
    digitalWrite (lucesDelanteras, HIGH);
    digitalWrite (lucesTraseras, LOW);
  }
  else {
    digitalWrite (lucesDelanteras, LOW);
    digitalWrite (lucesTraseras, LOW);
  }
  return;
}
void dirState1() 
{
  digitalWrite (velPwm1, HIGH);
  //analogWrite (velPwm1, locSpeed);
locSpeed > (CV5-1) ? digitalWrite(velPwm0, HIGH) : analogWrite (velPwm0, locSpeed); //ако locSpeed е на макс пуска макс захранване не импулси

  if (Luces == 1) {
    digitalWrite (lucesDelanteras, LOW);
    digitalWrite (lucesTraseras, HIGH);
  }
  else {
    digitalWrite (lucesDelanteras, LOW);
    digitalWrite (lucesTraseras, LOW);
  }
  return;
}

Ето и две снимки на готовия вече лок
изпълнението ми може да получи оценка ужасно 
но обещавам следващия вече да е направен както трябва


И малко видео за нагледност как се държи на релсите локомотива



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

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