Forums LR PRESSE

Où il est question de trains, petits et grands

  • Advertisement

Projet d'alimentation Analogique/DCC

Toutes les discussions sur l'Arduino !

Modérateur: MOD

Projet d'alimentation Analogique/DCC

Publié: Jeu 21 Mai 2015, 08:16 
Ca faisait un petit moment que j'y pensais... Disposer d'une alimentation polyvalente, permettant sans avoir avoir a débrancher/rebrancher la possibilité de faire circuler mes locos diverses et "avariées", pour certaines numérisée et pour d'autres non, sur mon réseau ou celui du club.
Donc, après quelques réflexions et tentatives, le projet est arrivé (tout du moins pour l'instant) à son terme :
- Bouton de commande rotatif et inverseur de sens de marche avec visualisation
- choix des fonctions via un menu simple
- sélection du mode Analogique ou DCC
- limitation de la vitesse maxi (dans les deux modes)
- choix de la fréquence PWM pour le mode analogique (30hz, 3Khz, 30Khz)
- choix de l'adresse DCC
- en DCC, accès aux fonctions F1 (éclairage) et F4 (Manoeuvre).

Le tout dans un encombrement restreint, pour pouvoir être transporté et posé au bord d'un réseau hôte.

Le coeur de la bête est un arduino "pro mini". Il est assisté d'un LMD18200 pour la sortie de puissance, d'un petit afficheur cristaux liquides, et quelques boutons.
Côté soft, je me suis basé sur la bibliothèque "cmdrArduino", qui fourni une très bonne base pour générer le signal DCC, l'afficheur, un nokia3110 (84x48 pixels) est bien géré par la librairie d'adafruit. Pour le reste, c'est du simple.

Côté investissement, c'est plus que raisonnable, je n'ai pas fait de calcul exacte, mais sans le boitier, la connectique (de récupération pour ma part) et le transfo 15V=/3A (aussi de récupération - alim de vieux portable) ça ne doit pas dépasser les 40€...

Voilà pour le teaser... Je vais mettre à plat les plans, faire quelques photos et nettoyer le code avant de vous donner tout ça en pâture.

A bientôt.
Zebulon91
Bavard
 
Messages: 78
Inscrit le: Dim 16 Mars 2014, 17:39
Localisation: Villebon sur Yvette (91)
Âge: 49
Echelle pratiquée: HO
Prénom: Michel
Club: AMF Villebon/Yvette

Re: Projet d'alimentation Analogique/DCC

Publié: Jeu 21 Mai 2015, 11:13 
Voilà un projet qui semble très intéressant. J'ai hâte d'en savoir plus :wink:
Avatar de l’utilisateur
Arduino
Prolixe
 
Messages: 1634
Inscrit le: Mer 25 Sep 2013, 16:14

Re: Projet d'alimentation Analogique/DCC

Publié: Mer 27 Mai 2015, 23:30 
Après quelques heures de bataille avec DesignSpark (que je ne maitrise pas du tout...) voici une première approche.
Image
Rien d'exceptionnel... un potentiomètre pour la vitesse, un interrupteur 3 positions pour l'arret complet et l(inversion de sens de marche, deux led pour la visualisation du sens de marche.

L'utilisation d'un petit afficheur LCD et de trois poussoirs permet une configuration simple et assez intuitive. En mode de fonctionnement normal, il affiche un bargraphe de vitesse, ainsi que l'adresse de la loco commandée en DCC.
En DCC les touches "up" et "down" permettent de respectivement commander les fonctions F1 et F4.

Le LMD18200 permet en analogique la génération du courant pulsé, et en DCC la génération du courant porteur.
Afin de simplifier, les deux diodes D1 et D2 permettent de commander la broche PWM du LMD18200, soit en mode ON/OFF via la pin 8 et la lib cmdr_arduino (DCC), soit en mode PWM via la pin 10 ; dans ce dernier cas, la pin 8 est configurée en INPUT afin de ne pas influencer le fonctionnement.
La lecture du courant consommée est possible via la pin A7, bien que les premiers tests ne soient pas vraiment concluants.

La liaison entre la sortie D7 de l'arduino et la pin Reset permet de faire un reboot (à froid) lors du changement de mode DC/DCC, du fait de la nécessité de modifier les registres de timers pour le DCC.

Code: Tout sélectionner
/*

  ALIMENTATION pour TRAIN ANALOGIQUE / DCC
  Version 1.0
 
  MBO - 2015

*/
// Display
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
//  DCC
//#include <DCCHardware.h>
#include <DCCPacket.h>
#include <DCCPacketQueue.h>
#include <DCCPacketScheduler.h>

#include <EEPROM.h>
#include <avr/pgmspace.h>        // gestion mémoire Flash

/* MENU PRINCIPAL */
const char menu0_0[] PROGMEM = "MENU";      // Titre
const char menu0_1[] PROGMEM = "MODE";
const char menu0_2[] PROGMEM = "CONFIG.";
const char menu0_3[] PROGMEM = "CONTRASTE";
const char menu0_4[] PROGMEM = "QUITTER";
PGM_P const menu0[] PROGMEM  = {
    menu0_0,
    menu0_1,
    menu0_2,
    menu0_3,
    menu0_4
};

/* MENU CONFIGURATION (ANALOGIQUE) */
const char menu1_0[] PROGMEM = "CONF. (ANA)";  // Titre
const char menu1_1[] PROGMEM = "(INERTIE)";
const char menu1_2[] PROGMEM = "FREQUENCE";
const char menu1_3[] PROGMEM = "LIMITEUR";
const char menu1_4[] PROGMEM = "RETOUR";
PGM_P const menu1[] PROGMEM = {
    menu1_0,
    menu1_1,
    menu1_2,
    menu1_3,
    menu1_4
};

/* MENU CONFIGURATION (DCC) */
const char menu2_0[] PROGMEM = "CONF. (DCC)";  // Titre
const char menu2_1[] PROGMEM = "ADRESSE";
const char menu2_2[] PROGMEM = "FONCTIONS";
const char menu2_3[] PROGMEM = "LIMITEUR";
const char menu2_4[] PROGMEM = "RETOUR";
PGM_P const menu2[] PROGMEM = {
    menu2_0,
    menu2_1,
    menu2_2,
    menu2_3,
    menu2_4
};

/* MENU MODE */
const char menu3_0[] PROGMEM = "MODE";  // Titre
const char menu3_1[] PROGMEM = "ANALOGIQUE";
const char menu3_2[] PROGMEM = "DCC";
const char menu3_3[] PROGMEM = "RETOUR";
PGM_P const menu3[] PROGMEM = {
    menu3_0,
    menu3_1,
    menu3_2,
    menu3_3
};

/* MENU PWM */
const char menu4_0[] PROGMEM = "PWM";  // Titre
const char menu4_1[] PROGMEM = "30Hz"; // 00
const char menu4_2[] PROGMEM = "3Khz"; // 01
const char menu4_3[] PROGMEM = "30Khz";// 10
const char menu4_4[] PROGMEM = "RETOUR";
PGM_P const menu4[] PROGMEM = {
    menu4_0,
    menu4_1,
    menu4_2,
    menu4_3,
    menu4_4
};


#define CONFIG_VERSION "102"
#define CONFIG_START 32

#define DEBUG           1

#define __VERSION__     1.02

#define PIN_LCD_SCE     4  // Pin 3 on LCD
#define PIN_LCD_RESET   5  // Pin 4 on LCD
#define PIN_LCD_DC      6  // Pin 5 on LCD
#define PIN_LCD_SDIN   12  // Pin 6 on LCD
#define PIN_LCD_SCLK   11  // Pin 7 on LCD
#define PIN_LCD_LED    13  // LED LCD
#define PIN_HARD_RESET  7  // Hard reset

//The DC pin tells the LCD if we are sending a command or data
#define LCD_COMMAND 0
#define LCD_DATA  1
//You may find a different size screen, but this one is 84 by 48 pixels
#define LCD_X     84
#define LCD_Y     48

// INPUTS & OUTPUTS
#define PIN_LCD_SCE     4  // Pin 3 on LCD
#define PIN_LCD_RESET   5  // Pin 4 on LCD
#define PIN_LCD_DC      6  // Pin 5 on LCD
#define PIN_LCD_SDIN   12  // Pin 6 on LCD
#define PIN_LCD_SCLK   11  // Pin 7 on LCD
#define PIN_LCD_LED    13  // LED LCD
#define PIN_HARD_RESET  7  // Hard reset
#define PIN_LED_1      A1  // LED Arriere
#define PIN_LED_2      A2  // LED Avant
#define PIN_SW_1        2  // Direction (arriere)
#define PIN_SW_2        3  // Direction (avant)
#define PIN_SW_3       A4  // Navigation 1
#define PIN_SW_4       A5  // Navigation 2
#define PIN_SW_5       A6  // Navigation 3
#define PIN_POT        A0  // Potentiometre vitesse
#define PIN_SENS_AMP   A3  // ??

#define PIN_PWM_EN_DCC  8  // Enable Output (DCC)
#define PIN_PWM_EN_ANA 10  // Pulse (Analog)
#define PIN_PWM_DIR     9  // Direction (Analog) - Pulse (DCC)

#define PIN_SENSOR     A7  // Current sensor

#define BUTTON_LEFT     1
#define BUTTON_MID      2
#define BUTTON_RIGHT    4

#define ANA_LIMIT_MAX   100
#define DCC_LIMIT_MAX   127
#define DCC_REFRESH     100

/*
  FLAGS :
    0:  0=ANALOG / 1=DCC
    1:  PWM Pulse Freq bit 0
    2:  PWM Pulse Freq bit 1
    3:
    4:
    5:
    6:
    7:
*/
int freqDiv[3]= {
  1024,
  64,
  1
};

struct S_CONFIG {
  char version[4];
  byte contrast;
  byte flags;
  byte dccAddress;
  byte anaLimit;
  byte dccLimit;
  byte upSpeed;
  byte downSpeed;
} mainConfig = {
  CONFIG_VERSION,
  // Default config values
  50,
  0B00000000,
  3,
  DCC_LIMIT_MAX,
  ANA_LIMIT_MAX,
  0,
  0
};

boolean menu = false;
boolean emergencySTOP = false;
boolean forceRefresh = true;

DCCPacketScheduler dps;

byte lastButtonDir = 0;
int lastPotRead = 0;
byte lastSpeed = 0;
byte lastButtonCtrl = 0;

int potRead = 0;
byte buttonCtrl = 0;
byte buttonDir = 0;
int currentPower = 0;

byte dccFunctions = 0;

byte currentSpeed = 0;

// Software SPI (slower updates, more flexible pin options):
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
Adafruit_PCD8544 display = Adafruit_PCD8544(PIN_LCD_SCLK,
                                            PIN_LCD_SDIN,
                                            PIN_LCD_DC,
                                            PIN_LCD_SCE,
                                            PIN_LCD_RESET);

void(* softReset) (void) = 0; //Software reset

/*******************************************************************************

  Menu

*******************************************************************************/

void mainMenu() {
  char _buffer[20];
  byte line = 1;
  boolean exit = false;
  byte btn = 0;
  boolean save = false;
  S_CONFIG lastConfig = mainConfig;
 
  while(!exit) {
    display.clearDisplay();
    for (byte i=0; i<5; i++) { // sizeof(menu0)
      strcpy_P(_buffer, (char*)pgm_read_word(&(menu0[i])));
      if (i==0) {
        display.fillRoundRect(0,0,84,8,2,BLACK);
        display.setCursor(10,0);
        display.setTextColor(WHITE);
        display.print(_buffer);     
      } else {
        display.setCursor(8,2+(i*8));
        if (line==i) {
          display.fillRect(0,2+i*8,5,8,BLACK);
        }
        display.setTextColor(BLACK);
        display.print(_buffer);
      }
    }
    display.display();
    delay(150);
    btn = 0;
    // Attendre une action
    while (btn==0){
      btn=readButtons();
      delay(2);
    }
    if ( (btn==BUTTON_LEFT) && (line>1) ) {
      line--;
    }
    if ( (btn==BUTTON_RIGHT) && (line<4) ) {
      line++;
    }
    if ( btn==BUTTON_MID) {
      switch(line) {
        case 1:
          menuMode();
          break;
        case 2:
          configMenu();
          break;
        case 3:
          save = doSetContrast();
          break;
        case 4:
          exit = true;
          break;
      }
    }
  }
  // Sauver en EEPROM les nouvelles valeurs
  saveConfig();
  //
  if ( (mainConfig.flags & 1) == 0) {
    setPwmFrequency(10,freqDiv[(mainConfig.flags & 6)>>1]);
  }
  // Rebbot si necessaire...
  if ( (lastConfig.flags & 1) != (mainConfig.flags & 1) ) {
    // changement de mode : redmarrage (Hard reset)
    pinMode(PIN_HARD_RESET,OUTPUT);
    digitalWrite(PIN_HARD_RESET,LOW);
  }
  menu = false;
}

void configMenu() {
  char _buffer[20];
  byte line = 1;
  boolean exit = false;
  byte btn = 0;
  byte i=0;
 
  while (!exit) {
    display.clearDisplay();
    for (i=0; i<5; i++) { // sizeof(menu0)
      if (mainConfig.flags & 1) {
        strcpy_P(_buffer, (char*)pgm_read_word(&(menu2[i])));
      } else {
        strcpy_P(_buffer, (char*)pgm_read_word(&(menu1[i])));
      }
      if (i==0) {
        display.fillRoundRect(0,0,84,8,2,BLACK);
        display.setCursor(10,0);
        display.setTextColor(WHITE);
        display.print(_buffer);     
      } else {
        display.setCursor(8,2+(i*8));
        if (line==i) {
          display.fillRect(0,2+i*8,5,8,BLACK);
        }
        display.setTextColor(BLACK);
        display.print(_buffer);
      }
    }
    display.display();
    delay(150);
    btn = 0;
    // Attendre une action
    while (btn==0){
      btn=readButtons();
      delay(2);
    }
    if ( (btn==BUTTON_LEFT) && (line>1) ) {
      line--;
    }
    if ( (btn==BUTTON_RIGHT) && (line<4) ) {
      line++;
    }
    if ( btn==BUTTON_MID) {
      switch(line) {
        case 1:
          if (mainConfig.flags & 1) {
            // set DCC address
            mainConfig.dccAddress = (byte)doSetValue(1,255,mainConfig.dccAddress,"ADRESSE");
          } else {
            // Set up/down speed
          }
          break;         
        case 2:
          if (mainConfig.flags & 1) {
            // Functions
          } else {
            // PWM Frequency
            menuFreq();
          }
          break;
        case 3:
          // Set limiteur (Max speed)
          if (mainConfig.flags & 1) {
            mainConfig.dccLimit = (byte)doSetValue(0,DCC_LIMIT_MAX,mainConfig.dccLimit,"LIMITEUR");
          } else {
            mainConfig.anaLimit = (byte)doSetValue(0,ANA_LIMIT_MAX,mainConfig.anaLimit,"LIMITEUR");
          }
          break;
        case 4:
          exit = true;
          break;
      }
    }
  }
}

byte menuMode() {
  char _buffer[20];
  byte line = 1;
  boolean exit = false;
  byte btn = 0;
  byte sel = (mainConfig.flags & 1)+1;
 
  while(!exit) {
    display.clearDisplay();
    for (byte i=0; i<4; i++) { // sizeof(menu0)
      strcpy_P(_buffer, (char*)pgm_read_word(&(menu3[i])));
      if (i==0) {
        display.fillRoundRect(0,0,84,8,2,BLACK);
        display.setCursor(10,0);
        display.setTextColor(WHITE);
        display.print(_buffer);     
      } else {
        display.setCursor(8,2+(i*8));
        if (line==i) {
          display.fillRect(0,2+i*8,5,8,BLACK);
        }
        display.setTextColor(BLACK);
        display.print(_buffer);
        if (sel==i) {
          display.setTextColor(BLACK);
          display.setCursor(9,2+(i*8));
          display.print(_buffer);         
        }
      }
    }
    display.display();
    delay(150);
    btn = 0;
    // Attendre une action
    while (btn==0){
      btn=readButtons();
      delay(2);
    }
    if ( (btn==BUTTON_LEFT) && (line>1) ) {
      line--;
    }
    if ( (btn==BUTTON_RIGHT) && (line<3) ) {
      line++;
    }
    if ( btn==BUTTON_MID) {
      switch(line) {
        case 1:
        case 2:
          sel = line;
          break;
        case 3:
          if (sel==2) {
            bitSet(mainConfig.flags,0);
          } else {
            bitClear(mainConfig.flags,0);
          }
          exit = true;
          break;
      }
    }
  }
}

byte menuFreq() {
  char _buffer[20];
  byte line = 1;
  boolean exit = false;
  byte btn = 0;
  byte freq = (mainConfig.flags & 6)>>1;
 
  while(!exit) {
    display.clearDisplay();
    for (byte i=0; i<5; i++) { // sizeof(menu0)
      strcpy_P(_buffer, (char*)pgm_read_word(&(menu4[i])));
      if (i==0) {
        display.fillRoundRect(0,0,84,8,2,BLACK);
        display.setCursor(10,0);
        display.setTextColor(WHITE);
        display.print(_buffer);     
      } else {
        display.setCursor(8,2+(i*8));
        if (line==i) {
          display.fillRect(0,2+i*8,5,8,BLACK);
        }
        display.setTextColor(BLACK);
        display.print(_buffer);
        if (freq+1==i) {
          display.setTextColor(BLACK);
          display.setCursor(9,2+(i*8));
          display.print(_buffer);         
        }
      }
    }
    display.display();
    delay(150);
    btn = 0;
    // Attendre une action
    while (btn==0){
      btn=readButtons();
      delay(2);
    }
    if ( (btn==BUTTON_LEFT) && (line>1) ) {
      line--;
    }
    if ( (btn==BUTTON_RIGHT) && (line<4) ) {
      line++;
    }
    if ( btn==BUTTON_MID) {
      switch(line) {
        case 1:
        case 2:
        case 3:
          freq = line-1;
          break;
        case 4:
          bitClear(mainConfig.flags,1);
          bitClear(mainConfig.flags,2);
          mainConfig.flags |= (freq<<1);
          exit = true;
          break;
      }
    }
  }
}


bool doSetContrast() {
  byte btn=0;
  boolean exit=false;
  char buffer[3]="";
 
  display.clearDisplay();
  display.fillRoundRect(0,0,84,8,2,BLACK);
  display.setCursor(10,0);
  display.setTextColor(WHITE);
  display.print(F("CONTRASTE"));     
  display.setTextColor(BLACK);
  while (!exit) {
    // Afficher la valeur actuelle
    display.fillRect(35,26,20,8,WHITE);
    display.setCursor(35,26);
    sprintf(buffer, "%02d", mainConfig.contrast-40);
    display.print(buffer);
    levelBar(3,16,80,8,(mainConfig.contrast-40),30);
    display.display();
    // Attendre un bouton
    delay(150);
    btn=0;
    while (btn==0){
      btn=readButtons();
      delay(2);
    }
    switch (btn) {
      case BUTTON_LEFT:
        if (mainConfig.contrast>40) {
          mainConfig.contrast--;
        }
        break;
      case BUTTON_RIGHT:
        if (mainConfig.contrast<70) {
          mainConfig.contrast++;
        }
        break;
      case BUTTON_MID:
        exit = true;
    }
    display.setContrast(mainConfig.contrast);
  }
  return true;
}

int doSetValue(int vMin, int vMax, int vDefault, char title[]) {
  byte btn=0;
  boolean exit=false;
  char buffer[4]="";
  int val = vDefault;
 
  display.clearDisplay();
  display.fillRoundRect(0,0,84,8,2,BLACK);
  display.setCursor((LCD_Y>>1) - (sizeof(title)>>1),0);
  display.setTextColor(WHITE);
  display.print(title);     
 
  // Fonctions
  display.fillRect(0,38,84,9,BLACK);
  display.drawLine(20,38,20,48,WHITE);
  display.drawLine(63,38,63,48,WHITE);
  display.setCursor(3,40);
  display.print(F("-"));
  display.setCursor(36,40);
  display.print(F("OK"));
  display.setCursor(75,40);
  display.print(F("+"));
 
  display.setTextColor(BLACK);
  while (!exit) {
    // Afficher la valeur actuelle
    display.fillRect(35,20,20,8,WHITE);
    display.setCursor(35,20);
    sprintf(buffer, "%03d", val);
    display.print(buffer);
    display.display();
    // Attendre un bouton
    delay(150);
    btn=0;
    while (btn==0){
      btn=readButtons();
      delay(2);
    }
    switch (btn) {
      case BUTTON_LEFT:
        if (val>vMin) {
          val--;
        }
        break;
      case BUTTON_RIGHT:
        if (val<vMax) {
          val++;
        }
        break;
      case BUTTON_MID:
        exit = true;
    }
   
  }
  return val;
}

// Afficher une progressBar.
void levelBar(byte x, byte y, byte width, byte height, int curValue, int maxValue ){
  byte vuValue = map(curValue,0,maxValue,0,width-2);
 
  display.fillRect(x,y,width,height,WHITE);
  display.fillRect(x+1,y,vuValue,height,BLACK);
  for (byte i=0; i<width; i += (width/4)){
    if (i>vuValue) {
      display.drawLine(x+i,y+1,x+i,y+height-2,BLACK);
    } else {
      display.drawLine(x+i,y+1,x+i,y+height-2,WHITE);
    }
  }
  display.drawRect(x,y,width,height,BLACK);
}

/*******************************************************************************

  SETUP

*******************************************************************************/

void setup() {
 
  pinMode(PIN_HARD_RESET,INPUT);
  digitalWrite(PIN_HARD_RESET,HIGH);
 
  pinMode(PIN_LCD_LED, OUTPUT);
  pinMode(PIN_LED_1, OUTPUT);
  pinMode(PIN_LED_2, OUTPUT);
  pinMode(PIN_PWM_EN_ANA, OUTPUT);
 
  pinMode(PIN_PWM_DIR, OUTPUT);
 
  pinMode(PIN_SW_1, INPUT_PULLUP);
  pinMode(PIN_SW_2, INPUT_PULLUP);
  digitalWrite(PIN_SW_1,HIGH);
  digitalWrite(PIN_SW_2,HIGH);
 
  digitalWrite(PIN_LED_1,LOW);
  digitalWrite(PIN_LED_2,LOW);
 
  #ifdef DEBUG
  Serial.begin(115200);
  Serial.print("Alim - V");
  Serial.print(__VERSION__);
  Serial.println(" - DEBUG");
  #endif
 
  loadConfig();
 
  display.begin();
  display.setContrast(mainConfig.contrast);
  display.clearDisplay();   // clears the screen and buffer
  display.display();
  digitalWrite(PIN_LCD_LED,HIGH);
 
  // Configure mode
  if ( (mainConfig.flags & 1) == 0 ) {
    // Analog
    digitalWrite(PIN_PWM_DIR,LOW);
    pinMode(PIN_PWM_EN_DCC, INPUT);
    pinMode(PIN_PWM_EN_ANA,OUTPUT);
    digitalWrite(PIN_PWM_EN_DCC,LOW); // Input HIGH-Z
    digitalWrite(PIN_PWM_EN_ANA,LOW);
    setPwmFrequency(10,freqDiv[(mainConfig.flags & 6)>>1]);
    //setPwmFrequency(10,1024);
  } else {
    // Digital (DCC)
    dps.setup();
    pinMode(PIN_PWM_EN_DCC, OUTPUT);
    digitalWrite(PIN_PWM_EN_DCC,HIGH);
  }
 
}

void loadConfig() {
  // To make sure there are settings, and they are YOURS!
  // If nothing is found it will use the default settings.
  if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
      EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
      EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2])
    for (unsigned int t=0; t<sizeof(mainConfig); t++)
      *((char*)&mainConfig + t) = EEPROM.read(CONFIG_START + t);
}

void saveConfig() {
  for (unsigned int t=0; t<sizeof(mainConfig); t++)
    EEPROM.update(CONFIG_START + t, *((char*)&mainConfig + t));
}

void setPwmFrequency(int pin, int divisor) {
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) {
      TCCR0B = TCCR0B & 0b11111000 | mode;
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode;
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
      default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode;
  }
}

/*******************************************************************************

  Interface utilisateur

*******************************************************************************/

int readPot() {
  byte i;
  int potSum, potRead;
 
  potSum = 0;
  for(i=0; i<10; i++) {
    potSum += analogRead(PIN_POT); 
  }
  potRead = potSum / 10;

  return potRead; 
}

byte readButtons() {
  byte z;
  z = (analogRead(PIN_SW_3)==0)
      + ((analogRead(PIN_SW_4)==0)<<1)
      + ((analogRead(PIN_SW_5)==0)<<2);
  return z;
}

byte readDir() {
  byte z = 0;
  if (!digitalRead(PIN_SW_1)) {
    z = 1;
  } else if (!digitalRead(PIN_SW_2)) {
    z = 2;
  }
  return z;
}

/*

  Actualiser la sortie PWM

*/
void doOutput(byte _speed, byte _dir) {
  static unsigned long lastTime = 0;
  static byte realSpeed = 0;
 
 
  byte anaSpeed = _speed*255/100;
  digitalWrite(PIN_PWM_DIR,(_dir==1));
  analogWrite(PIN_PWM_EN_ANA,anaSpeed);
  Serial.println(anaSpeed);
}

/*

  Actualiser la sortie DCC

*/

void doDccOutput(byte _speed, byte _dir) {
  static char lastSpeed=0;
  static unsigned long lastTime=0;
  char dccSpeed = 0;
 
  switch (_dir) {
    case 1:
      dccSpeed = -_speed;
      break;
    case 2:
      dccSpeed = _speed;
      break;
    case 0:
      dccSpeed = 0;
  }
 
  if ( dccSpeed != lastSpeed ) { // && _dir>0
    if ( dccSpeed==0) { // Prevent E-Stop // _dir>0  &&
      if (lastSpeed<0) {
        dccSpeed = -1;
      } else {
        dccSpeed = 1;
      }
    }
  }
 
  if (dccSpeed != lastSpeed || lastTime+DCC_REFRESH<millis()) { //
    dps.setSpeed128(mainConfig.dccAddress,DCC_SHORT_ADDRESS,dccSpeed);
    dps.setFunctions0to4(mainConfig.dccAddress,DCC_SHORT_ADDRESS,dccFunctions);
    lastTime = millis();
  }
 
  dps.update();
  lastSpeed = dccSpeed;
}

void doOperationLcd() {
  char buffer[] = "";
  String s;
  byte yMax=0;
  static int lastCurrent=0;
 
  /* Afficher état sur le LCD */
 
  if ( (lastButtonDir != buttonDir)
       ||
       (lastSpeed != currentSpeed)
       ||
       forceRefresh
     ) {
    //display.clearDisplay();
    display.fillRect(0,0,84,38,WHITE);
    // Affichage du sens de marche
    display.setTextSize(1);
   
    if (buttonDir==1) {
      s="ARRIERE";
      digitalWrite(PIN_LED_1,HIGH);
      digitalWrite(PIN_LED_2,LOW);
    } else if (buttonDir==2) {
      s="AVANT";
      digitalWrite(PIN_LED_1,LOW);
      digitalWrite(PIN_LED_2,HIGH);
    } else {
      s="STOP";
      digitalWrite(PIN_LED_1,LOW);
      digitalWrite(PIN_LED_2,LOW);
    }
    display.setCursor((LCD_X>>1)-(s.length()*3),2);
    display.drawRoundRect(10,0,64,11,3,BLACK);
    display.print(s);
    // Affichage de la vitesse (numérique)
    display.setCursor(40,14); //25,
    display.setTextSize(2);
    sprintf(buffer, "%3d", currentSpeed);
    display.print(buffer);
    display.setTextSize(1);
   
    // Affichage du bargraph de vitesse
    levelBar(2,30,80,6,currentSpeed,(mainConfig.flags & 1)?mainConfig.dccLimit:mainConfig.anaLimit);
   
    // Affichage du mode courant (ANA/DCC)
    /*
    display.setCursor(0,40);
    display.print((mainConfig.flags&1)?F("DCC"):F("ANA"));
    if ( ((mainConfig.flags & 1) && (mainConfig.dccLimit!=DCC_LIMIT_MAX))
         ||
         (!(mainConfig.flags & 1) && (mainConfig.anaLimit!=ANA_LIMIT_MAX))
       ) {
      display.print(F(" (L)"));   
    }
    */
   
    // Affichage des infos propres au mode
    if (mainConfig.flags & 1) {
      // Mode DCC
      display.setCursor(5,12);
      display.print(F("Id"));
      display.setCursor(2,21);
      display.fillRect(1,20,20,9,BLACK);
      display.setTextColor(WHITE);
      sprintf(buffer,"%3d",mainConfig.dccAddress);
      display.print(buffer);
      display.setTextColor(BLACK);
    }
   
    // Fonctions
    //display.fillRect(0,38,84,9,WHITE);
    display.drawLine(20,38,20,48,BLACK);
    display.drawLine(63,38,63,48,BLACK);
    display.setTextColor(WHITE);
    if (mainConfig.flags & 1>0) {
      // Gauche
      if (bitRead(dccFunctions,0)) { // F1 (Eclairage)
        display.fillRect(0,38,19,9,BLACK);
        display.setTextColor(WHITE);
      } else {
        display.fillRect(0,38,19,9,WHITE);
        display.setTextColor(BLACK);
      }
      display.setCursor(4,39);
      display.print(F("F1"));
      // Droite
      if (bitRead(dccFunctions,3)) { // F4 (Manoeuvres)
        display.fillRect(64,38,19,9,BLACK);
        display.setTextColor(WHITE);
      } else {
        display.fillRect(64,38,19,9,WHITE);
        display.setTextColor(BLACK);
      }
      display.setCursor(68,39);
      display.print(F("F4"));
    } else {
      display.fillRect(0,38,19,9,WHITE);
      display.fillRect(64,38,19,9,WHITE);
    }   
    forceRefresh = true;
  }
 
  if (lastCurrent != currentPower || forceRefresh) { 
    // Affichage consommation
    display.fillRect(21,38,41,9,WHITE);   
    display.setTextColor(BLACK);
    display.setCursor(23,39);
    sprintf(buffer,"%4d",currentPower);
    display.print(buffer);
    display.print("mA");
    lastCurrent = currentPower;
  }
  display.display();
  forceRefresh=false;
}

void loop() { 
  int aRead=0;
  unsigned int average=0;

  // Read values from controler
  potRead = readPot();
  buttonCtrl = readButtons();
  buttonDir = readDir();

  for (byte i=0;i<128;i++) {   
      aRead = max(analogRead(PIN_SENSOR),aRead); //;
      average += aRead;
  }
  currentPower = (average>>7) * 10;
 
  if (mainConfig.flags & 1 > 0) {
    // Alim DCC 0-128
    currentSpeed = (buttonDir>0)?map(potRead,0,1023,0,mainConfig.dccLimit):0;
  } else {
    // Alim Analogique 0-100%
    currentSpeed = (buttonDir>0)?map(potRead,0,1023,0,mainConfig.anaLimit):0;
  }
 
  if (lastButtonCtrl != buttonCtrl) {
    if ( (buttonCtrl == BUTTON_MID) && (currentSpeed==0) ) {
      menu = !menu;
      //delay(10);
    }
    // Fonction 1 : Eclairage Loco
    if ( mainConfig.flags & 1 > 0 && buttonCtrl == BUTTON_LEFT) {
      bitWrite(dccFunctions,0,!bitRead(dccFunctions,0));
      forceRefresh=true;
    }
    // Fonction 4 : Mode manoeuvre
    if ( mainConfig.flags & 1 > 0 && buttonCtrl == BUTTON_RIGHT) {
      bitWrite(dccFunctions,3,!bitRead(dccFunctions,3));
      forceRefresh=true;
    }
   
    Serial.println(dccFunctions,BIN);
    delay(10);
  }
 
  if (!menu) {
    doOperationLcd();
    if (mainConfig.flags & 1 > 0) {
      doDccOutput(currentSpeed, buttonDir);
    } else {
      doOutput(currentSpeed, buttonDir);
    }
  } else {
    mainMenu();
    forceRefresh=true;
  }

  lastButtonDir = buttonDir;
  lastPotRead = potRead;
  lastButtonCtrl = buttonCtrl;
  lastSpeed = currentSpeed;
  //delay(1);
}


Il reste encores quelques bricoles à terminer, particulièrement l'inertie en mode analogique. J'avoue que pour le moment, je n'ai pas trouvé (pas trop cherché non plus ;) ) de solution.

Mais l'essentiel est là : avec une seule alimentation, permettre de faire fonctionner indifféremment des motrices analogiques et digitalisées.
Zebulon91
Bavard
 
Messages: 78
Inscrit le: Dim 16 Mars 2014, 17:39
Localisation: Villebon sur Yvette (91)
Âge: 49
Echelle pratiquée: HO
Prénom: Michel
Club: AMF Villebon/Yvette

Re: Projet d'alimentation Analogique/DCC

Publié: Mer 08 Juil 2015, 07:17 
Bonjour

Du nouveau sur ton projet ?
Jérôme, nouveau breton
Avatar de l’utilisateur
TwInSeN
Intarissable !
 
Messages: 8347
Inscrit le: Mar 05 Fév 2008, 21:05
Localisation: Chantepie (35)

Re: Projet d'alimentation Analogique/DCC

Publié: Mer 08 Juil 2015, 15:13 
Ton projet est très proche de ce que je tente de faire de mon côté. Tout marche aussi sur mon nano à part des problèmes de saturation mémoire qui sont dus à l'utilisation de bibliothèques maison pour l'interface utilisateur (LcdUI) ou la sauvegarde des données (EEPROMex). J'ai aussi sans doute voulu en faire trop avec un simple nano : configuration de plusieurs locos avec un nom en clair, des fonctions, quelques données propres, tout ça sauvé dans l'EEPROM via ma bibliothèque...
Par contre, je n'ai pas changé la fréquence de PWM en analogique et je pense que je vais m'inspirer directement de ce que tu proposes du sujet.
Enfin, dans mon projet le switch de basculement Dc/Dcc force l'utilisateur à redémarrer l'Arduino pour basculer vraiment le mode. C'est volontaire pour éviter d'éventuels accidents, sachant que certaines locos analogiques supportent très mal d'être alimentées en alternatif carré irrégulier !

PS: Toutes les bibliothèques Locoduino sont visibles là http://git.framasoft.org/groups/locoduino.org
Avatar de l’utilisateur
Trusty
Bavard
 
Messages: 63
Inscrit le: Lun 03 Déc 2012, 11:04
Localisation: Melun
Âge: 55
Echelle pratiquée: N
Prénom: Thierry

Re: Projet d'alimentation Analogique/DCC

Publié: Jeu 09 Juil 2015, 11:55 
TwInSeN a écrit:Bonjour

Du nouveau sur ton projet ?


Je n'ai rien ajouté depuis... Mais ar contre j'ai fait pas mal de tests avec des matériels très divers. Ca fonctionne à merveille !
Je ne pense pas ajouter de nouvelles fonctionnalités (en dehors de la gestion de l'inertie en analogique). Le but de cette alim est de pouvoir simplement faire tourner un train, quelque soit le type de loco.
Zebulon91
Bavard
 
Messages: 78
Inscrit le: Dim 16 Mars 2014, 17:39
Localisation: Villebon sur Yvette (91)
Âge: 49
Echelle pratiquée: HO
Prénom: Michel
Club: AMF Villebon/Yvette

Re: Projet d'alimentation Analogique/DCC

Publié: Jeu 09 Juil 2015, 11:59 
La mémoire coute cher sur ces petites bêtes là !!!
C'est aussi pour ça que je n'ai pas cherché à implanter trop de fonctionnalités. Faire simple, c'est souvent la clé du succès !

Parenthèse au sujet de la fréquence PWM : j'ai ajouté ça parce que je me suis aperçu que certains vieux moteurs réagissaient différemment en fonction de cette fréquence. Bien que j'utilise majoritairement le 30K, les très vieux moteurs semblent mieux accrocher avec du 30Hz... Et le 3K, c'est juste énervant d'entendre le sifflement en bas régime !

Je cherche aussi une solution pour détecter une loco analogique ou numérique afin d'éviter les mauvaises surprises.

Pour les projets plus ambitieux, particulièrement avec de l'interface "graphique", j'utilise des mégas...
J'ai une autre alim, plus poussée, avec LCD tactil couleur, et tout les gadgets moderne qu'on peut y associer en cours de développement. Je donnerai des nouvelles dès que ça sera un peu plus avancé..


Trusty a écrit:Ton projet est très proche de ce que je tente de faire de mon côté. Tout marche aussi sur mon nano à part des problèmes de saturation mémoire qui sont dus à l'utilisation de bibliothèques maison pour l'interface utilisateur (LcdUI) ou la sauvegarde des données (EEPROMex). J'ai aussi sans doute voulu en faire trop avec un simple nano : configuration de plusieurs locos avec un nom en clair, des fonctions, quelques données propres, tout ça sauvé dans l'EEPROM via ma bibliothèque...
Par contre, je n'ai pas changé la fréquence de PWM en analogique et je pense que je vais m'inspirer directement de ce que tu proposes du sujet.
Enfin, dans mon projet le switch de basculement Dc/Dcc force l'utilisateur à redémarrer l'Arduino pour basculer vraiment le mode. C'est volontaire pour éviter d'éventuels accidents, sachant que certaines locos analogiques supportent très mal d'être alimentées en alternatif carré irrégulier !

PS: Toutes les bibliothèques Locoduino sont visibles là http://git.framasoft.org/groups/locoduino.org
Zebulon91
Bavard
 
Messages: 78
Inscrit le: Dim 16 Mars 2014, 17:39
Localisation: Villebon sur Yvette (91)
Âge: 49
Echelle pratiquée: HO
Prénom: Michel
Club: AMF Villebon/Yvette

Re: Projet d'alimentation Analogique/DCC

Publié: Ven 17 Juil 2015, 09:24 
bonjour,
C'est une alim par canton ?
Centrale et matériel DCC GFAO.
mazout
Bavard
 
Messages: 67
Inscrit le: Dim 12 Juil 2015, 14:03
Âge: 47
Echelle pratiquée: HO
Prénom: pierre
Club: non

Re: Projet d'alimentation Analogique/DCC

Publié: Dim 19 Juil 2015, 18:57 
mazout a écrit:bonjour,
C'est une alim par canton ?


Je ne suis pas certain de bien comprendre la question...
Il s'agit d'une alimentation "ordinaire", si ce n'est qu'il est possible de commander des locos analogique OU digitales ; dans ce dernier cas, elle ne permet pas de commander plusieurs locos.
Zebulon91
Bavard
 
Messages: 78
Inscrit le: Dim 16 Mars 2014, 17:39
Localisation: Villebon sur Yvette (91)
Âge: 49
Echelle pratiquée: HO
Prénom: Michel
Club: AMF Villebon/Yvette

Re: Projet d'alimentation Analogique/DCC

Publié: Jeu 23 Juil 2015, 06:25 
Bonjour, Excuse moi j'ai lu un peu vite.
Je viens de comprendre. C'est une commande/centrale DCC ou DC pour une seule machine en DC et plusieurs en DCC.
Centrale et matériel DCC GFAO.
mazout
Bavard
 
Messages: 67
Inscrit le: Dim 12 Juil 2015, 14:03
Âge: 47
Echelle pratiquée: HO
Prénom: pierre
Club: non

Re: Projet d'alimentation Analogique/DCC

Publié: Dim 26 Juil 2015, 12:19 
Ou plutot 1 seule à la fois en DCC ; à l'image d'une alim analogique. L'objectif n'est pas de remplacer une centrale DCC, mais d'avoir un outil simple pour faire rouler rapidement toute loco...

mazout a écrit:Bonjour, Excuse moi j'ai lu un peu vite.
Je viens de comprendre. C'est une commande/centrale DCC ou DC pour une seule machine en DC et plusieurs en DCC.
Zebulon91
Bavard
 
Messages: 78
Inscrit le: Dim 16 Mars 2014, 17:39
Localisation: Villebon sur Yvette (91)
Âge: 49
Echelle pratiquée: HO
Prénom: Michel
Club: AMF Villebon/Yvette

Re: Projet d'alimentation Analogique/DCC

Publié: Mer 29 Juil 2015, 13:27 
Oui c'est bien ça.
Le boîtier compact de lenz (pur DCC) était capable de piloter plusieurs machines en jonglant avec les adresses avec le clavier.
Centrale et matériel DCC GFAO.
mazout
Bavard
 
Messages: 67
Inscrit le: Dim 12 Juil 2015, 14:03
Âge: 47
Echelle pratiquée: HO
Prénom: pierre
Club: non

Re: Projet d'alimentation Analogique/DCC

Publié: Jeu 30 Juil 2015, 21:15 
J'ai enfin mis la main sur un appareil photos, voici donc, comme promis, quelques images de la bête :

Image

Image

Le menu principal :
Image
Configuration du mode de fonctionnement :
Image
Configuration du mode Analogique :
Image
Configuration du mode DCC :
Image

Et pour finir, les écrans en fonctionnement (Analogique et DCC) :
Image Image

Image
Zebulon91
Bavard
 
Messages: 78
Inscrit le: Dim 16 Mars 2014, 17:39
Localisation: Villebon sur Yvette (91)
Âge: 49
Echelle pratiquée: HO
Prénom: Michel
Club: AMF Villebon/Yvette

Re: Projet d'alimentation Analogique/DCC

Publié: Ven 31 Juil 2015, 07:28 
Bravo, belle finitions.

:applause:
Cordialement,

Christian.
Avatar de l’utilisateur
likiki
Causant
 
Messages: 257
Inscrit le: Dim 29 Avr 2012, 14:21
Localisation: Corbeil Essonne
Âge: 50
Echelle pratiquée: H0 3R
Prénom: Christian

Re: Projet d'alimentation Analogique/DCC

Publié: Mar 01 Sep 2015, 18:06 
Bravo pour cette belle réalisation qui apporte l'originalité d'un commande PWM pour les locos analogiques et DCC pour les digitales.

L'affichage graphique et le configurateur sont de toute beauté !

Bravo aussi pour agrandir le cercle des réalisateurs FRANÇAIS de centrales à base d'Arduino (avec Trusty et moi-même - voir les articles sur plusieurs forums dont Locoduino)

Centrale DCC
SixtySix
 
Messages: 46
Inscrit le: Dim 05 Jan 2014, 18:53
Echelle pratiquée: N

Suivant

Retour vers Arduino

Qui est en ligne ?

Utilisateur(s) parcourant actuellement ce forum : Aucun utilisateur inscrit et 6 invité(s)