Audiyofan
Audiyofan




Poster un nouveau sujet Répondre au sujet  [ 11 messages ] 
Auteur Message
 Sujet du message: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Jeu 24 Avr 2014, 15:54 
Hors ligne
Membre Assidu
Avatar de l’utilisateur

Inscription: Mar 22 Aoû 2006, 16:47
Messages: 2665
Localisation: Suisse
Bonjour.

Pour compléter le post http://audiyofan.org/forum/viewtopic.php?f=18&t=9128, je vais détailler et tâcher d'expliquer la partie logiciel utilisée et fonctionnelle de cet ampli.
http://audiyofan.org/forum/viewtopic.php?f=60&t=9024

Dans les grandes lignes
Une fois les interconnections faites, le programme va pouvoir exploiter les données, contrôler et sécuriser votre ampli avec pour seule limite, votre imagination. Les principales fonctions actuellement sont:
• Temporiser la haute tension
• Démarrage en douceur.
• Contrôler que le courant des tubes reste dans des limites normales.
• Réguler les tensions de G1 ou G2 pour positionner le tube correctement.
• Assurer dans le cas des push-pull l’équilibre et zéro courant dans le transfo de sortie.
• Fournir une indication du type de problème, ainsi que du tube responsable (si les points de mesure connectés permettent de l’identifier).

Optionnellement il est assez facile d’ajouter d’autres fonctionnalités, comme:
• Polarisation intelligente en fonction du niveau d’écoute.
• Indication du passage en classe B pour les topologies AB.
• Mise en stand-by automatique si pas de modulation

Le programme est constitué de séquences qui seront exécutées les une après les autres. Le programme Arduino est lui même constitué d’une boucle dont le temps d'exécution dépend du type de puce utilisé et du code à exécuter dans la boucle. En général, mes boucles tournent entre 1 à 2ms si on ne tient pas compte des instructions dans le code qui sont la pour temporiser.

Toutefois, j’évite un maximum de temporiser le temps de boucle, mais travaille plutôt avec des comptages, ceci dans le but de laisser l’Arduino exécuter ces fonction de surveillance en permanence.

Le programme
L’ampli de l'exemple ici contient 8 régulateurs, un bouton rotatif, un instrument à aiguille, une LED bicolore sur la face avant et une LED on-board.

Ceci est le programme minimum d’un Arduino. Il ne fait rien.
Code:
void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:
 
}


La première chose à faire dans un programme est de référencer les entrés sorties par des variables nommées, ça vous évitera au changement de la connectique ou lors d’une réutilisation du code sur un autre type de carte ou de puce de devoir chercher partout dans le code les endroits à modifier. Surtout que des chiffres, c’est pas simple à repérer.

Code:
// Pin Config
#define ledOnBoard   13
#define indicatorPin      2
#define relayPin      3
#define ledRedPin      52
#define ledGreenPin   50
#define reg1LPin      4
#define reg2LPin      5
#define reg3LPin      6
#define reg4LPin      7
#define reg1RPin      8
#define reg2RPin      9
#define reg3RPin      10
#define reg4RPin      11
#define current1LPin              A0   
#define current2LPin              A1   
#define current3LPin              A2   
#define current4LPin              A3   
#define current1RPin              A4   
#define current2RPin              A5   
#define current3RPin              A6   
#define current4RPin              A7   
#define switchPin                      A8      

void setup() {
}

void loop() {
}


Ensuite dans la fonction Setup(), il faut définir la direction des connections digitales. Pas besoin de le faire pour les analogiques et les PWM. Notre fonction Setup devient alors:
Code:
void setup() {               
  pinMode(ledRedPin, OUTPUT);     
  pinMode(ledGreenPin, OUTPUT);
  pinMode(ledOnBoard, OUTPUT);     
}


Tous le reste du code exceptés les déclarations et les fonctions va donc s'exécuter dans la fonction Loop() qui est appelée en permanence et dont le temps entre deux appels dépends comme expliqué précédemment du type de puce et du travail qu’elle a à faire. L'appel des fonctions se fera donc aussi depuis la fonction Loop().

A Suivre

_________________
Tube Curve Tracer, Diagnostic,
Mon GIT


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Jeu 24 Avr 2014, 17:06 
Hors ligne
Membre Assidu
Avatar de l’utilisateur

Inscription: Mar 22 Aoû 2006, 16:47
Messages: 2665
Localisation: Suisse
Sequences
Nous allons ensuite ajouter une variable définissant la séquence en cours et des constantes pour identifier les séquences d'une façon parlante.

Code:
// Sequence:
#define SEQ_DISCHARGE    0  // 0: Discharge
#define SEQ_HEAT         1  // 1: Heat tempo
#define SEQ_STARTING     2  // 2: Starting High Voltage
#define SEQ_REGULATING   3  // 3: Waiting for reg
#define SEQ_FUNCTION     4  // 4: Normal Fonction
#define SEQ_FAIL         5  // 5: Fail
int sequence = SEQ_DISCHARGE;


Les séquences sont:
• SEQ_DISCHARGE: Décharge, ont assure avant de démarrer l'ampli qu'il n'y a pas de courant dans les tubes. C'est surtout utile pour vérifier le bon fonctionnement du relais et des mesures.
• SEQ_HEAT: Chauffage, on attends que les tubes chauffe. Aucune courant ne doit apparaître à ce moment
• SEQ_STARTING: Démarrage. Cette séquence est courte, elle applique la haute tension et vérifie qu'un courant apparaît dans les tubes. Ca permet de vérifier que les régulateurs répandent et ne laisse pas le temps aux résistances shunt de cramer en cas de court circuit.
• SEQ_REGULATING: Régulation. Une fois l'assurance que les régulateurs fonctionnent et que les tubes répondent, on régule le courant dans chacun des tubes.
• SEQ_FUNCTION: Fonction. Il ne passe rien à part de la surveillance, mais en cas de dérive le programme put revenir à la séquence précédente et relancer la régulation
• SEQ_FAIL: Il y a un problème, on coupe la HT, indique la nature et la source du problème. Le seul moyen de sortir de cette séquence est un reset et un redémarrage du cycle complet.

Notre fonction Loop() devient donc
Code:
void loop()
{
  switch (sequence)
  { 
    case SEQ_DISCHARGE:   
    case SEQ_HEAT:
    case SEQ_STARTING:
    case SEQ_REGULATING:
    case SEQ_FUNCTION:
    default: // CASE SEQ_FAIL
  } 
}


En dehors de ce switch case dépendant de la séquence, il y a certaines opérations qui seront faites à chaque tours comme:
• L'affichage de la LED
• L'affichage de l'indicateur
• Les mesures de courant et le moyennage des mesures

Mesures et moyennage
Les mesures de courant sont moyennées de deux façons
• La première consiste à mesurer n fois et au bout de n à diviser la somme par n
n est représenté par measureAverageCount, reg1LCurrentSum est la somme des mesures, et reg1LCurrentAverage la moyenne calculée après n boucles.
Code:
#define measureAverageCount          10

long reg1LCurrentSum = 0;
int measureLoopCount = 0;
float reg1LCurrentAverage;

reg1LCurrentSum += analogRead(current1LPin);
if (++measureLoopCount >= measureAverageCount)
{
  reg1LCurrentAverage =  reg1LCurrentSum / measureLoopCount;
  measureLoopCount = 0;
  reg1LCurrentSum = 0;
}


• Le second moyennage consiste non pas à remplacer la valeur moyenne calculée précédemment, mais à la faire dériver par rapport à la différence entre la nouvelle mesure et l'ancienne. Le code précédent devient alors:
Code:
#define measureAverageCount          10
#define regAbsoluteAverageRatio      0.1

long reg1LCurrentSum = 0;
int measureLoopCount = 0;
float reg1LCurrentAverage;

reg1LCurrentSum += analogRead(current1LPin);
if (++measureLoopCount >= measureAverageCount)
{
  reg1LCurrentAverage += ((reg1LCurrentSum / measureLoopCount) - reg1LCurrentAverage) * regAbsoluteAverageRatio;
  measureLoopCount = 0;
  reg1LCurrentSum = 0;
}


regAbsoluteAverageRatio représente une constante qui permet d'ajuster l'efficacité de la nouvelle mesure sur l'ancienne. Ceci évite qu'une mauvaise série de mesure du par exemple à une micro-coupure ou un passage en classe B ne vienne déstabiliser le régulateur et le système. Ne pas oublier qu'il ne se passe que entre 2 et 3 millisecondes entre deux boucles, comme la première moyenne est réglée sur 10, il ne se passe que 20ms à 30ms avant qu'une valeur parvienne de la première moyenne.

Nous avons donc notre mesure de cathode représentée et moyennée dans la variable reg1LCurrentAverage qui vaudra une valeur entre 1 et 1023 proportionnellement au voltage d'entrée sur la pin concernée (0 à 5V)

Il faudra répéter l'opération autant de fois qu'il y a de tubes, mais les constantes restent communes.

Affichage du courant
L'affichage du courant sur l'indicateur à aiguille sera également appelé en dehors du sélecteur de séquences. Il affiche les valeurs mesurées et moyennées par le bout de code précédent en fonction de la position du sélecteur rotatif représentée par une valeur analogique. Voici la fonction:
Code:
#define switchOffset           122        // 0.0049V per Units (0.6V)
#define currentRatioIndicator        0.25

void DisplayIndicator(void)
{
  int switchValue = analogRead(switchPin) + switchOffset/2;
  if (switchValue < switchOffset)
  {
    // Vu-meter read working left position 1
    analogWrite(indicatorPin, reg1LCurrentAverage * currentRatioIndicator);     
  }
  else if (switchValue < 2 * switchOffset)
  {
    // Vu-meter read working left position 2
    analogWrite(indicatorPin, reg2LCurrentAverage * currentRatioIndicator);     
  }
  else if (switchValue < 3 * switchOffset)
  {
    // Vu-meter read working left position 3
    analogWrite(indicatorPin, reg3LCurrentAverage * currentRatioIndicator);     
  }
  else if (switchValue < 4 * switchOffset)
  {
    // Vu-meter read working left position 4
    analogWrite(indicatorPin, reg4LCurrentAverage * currentRatioIndicator);     
  }
  else if (switchValue < 5 * switchOffset)
  {
    // Vu-meter is off
    analogWrite(indicatorPin, 0);     
  }
  else if (switchValue < 6 * switchOffset)
  {
    // Vu-meter read working right position 1
    analogWrite(indicatorPin, reg1RCurrentAverage * currentRatioIndicator);     
  }
  else if (switchValue < 7 * switchOffset)
  {
    // Vu-meter read working right position 2
    analogWrite(indicatorPin, reg2RCurrentAverage * currentRatioIndicator);     
  }
  else if (switchValue < 8 * switchOffset)
  {
    // Vu-meter read working right position 3
    analogWrite(indicatorPin, reg3RCurrentAverage * currentRatioIndicator);     
  }
  else if (switchValue < 9 * switchOffset)
  {
    // Vu-meter read working right position 4
    analogWrite(indicatorPin, reg4RCurrentAverage * currentRatioIndicator);     
  }
  else
  {
    // Vu-meter is off
    analogWrite(indicatorPin, 0);     
  }
}


currentRatioIndicator permet juste d'adapter le range de sortie qui est en général de 0 à 255 pour une tension de 0 à 5V avec celui des mesures qui est de 0 à 1023. Ces valeurs sont propre aux cartes Arduino que j'ai utilisé.

A ce stade, nous avons donc des mesures moyennées, et une fonction appelée à chaque boucles affichant le courant dans le tube choisi par le sélecteur.

A Suivre

_________________
Tube Curve Tracer, Diagnostic,
Mon GIT


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Jeu 24 Avr 2014, 23:26 
Hors ligne
Membre Assidu
Avatar de l’utilisateur

Inscription: Mar 22 Aoû 2006, 16:47
Messages: 2665
Localisation: Suisse
La suite va consister avant de programmer les étapes à ajouter des fonctions qui seront appelées depuis les étapes.

Commande du relais
Je vais ajouter deux fonctions pour l'allumage et l'extinction du relais. N'oublions pas que le relais est sur une sortie PWM et que pour l'allumer, il faut envoyer un signal carré 50/50 qui une fois redressé donnera un maximum de tension continue. Les sorties analogiques/PWM allant de 0 à 255, on obtiens le maximum à 127. Les deux fonctions suivantes permettent d'allumer et d^éteindre le relais en faisant abstraction de ce détails de PWM.

Code:
void RelayOn(void)
{
  analogWrite(relayPin, 127);
}

void RelayOff(void)
{
  analogWrite(relayPin, 0);
}


La LED
La gestion de la LED est un peu complexe car il faut pouvoir
• Choisir la couleur
• Choisir la fréquence de clignotement.
Avec ces deux informations, il est possible d'indiquer une multitude d'états pour autant qu'on sache à quoi ça correspond.

La première fonction allume ou éteint la LED. On passe en paramètre, la couleur et l'état. Comme j'ai une LED à l'intérieur de l'ampli, j'en profite pour lui faire suivre l'état général quelque soit la couleur. C'est assez utile quand l'ampli est ouvert, et permet aussi de voir qu'il est allumé même retourné.
Code:
// LED Colors
#define LED_GREEN   0
#define LED_RED      1

void LedToogle(int color, int state)

  switch (color)
  {
  case LED_GREEN:
    // State is inverted, because LED is common anode
    digitalWrite(ledGreenPin, !state);
    digitalWrite(ledRedPin, HIGH);
    break;

  case LED_RED:
    // State is inverted, because LED is common anode
    digitalWrite(ledRedPin, !state);
    digitalWrite(ledGreenPin, HIGH);
    break;

  }

  digitalWrite(ledOnBoard, state);
}


Deux fonction pour allumer ou éteindre la LED courante en fonction d'une valeur globale currentLedColor
Code:
int currentLedColor = LED_GREEN;

void LedOn()
{
  LedToogle(currentLedColor, HIGH);
}

void LedOff()
{
  LedToogle(currentLedColor, LOW);
}


Et une fonction pour gérer le clignotement en fonction de deux paramètres globaux
• ledBlinkOn indique le nombre de cycle durant lesquels la LED va rester allumée
• ledBlinkMax indique le nombre de cycles totaux avant de ré-allumer la LED

Code:
int ledBlinkCount = 0;
int ledBlinkMax = 0;
int ledBlinkOn = 0;

void LedBlinking(void)
{
  ledBlinkCount++;
  if (ledBlinkMax == 0 || ledBlinkCount > ledBlinkMax)
  {
    LedOff();   
    ledBlinkCount = 0;
  }   
  else if (ledBlinkOn == ledBlinkMax)
  {
    LedOn();
    ledBlinkCount = 0;
  }
  else if (ledBlinkCount < ledBlinkOn)
  {
    LedOn();
  }
  else
  {
    LedOff();
  }
}


Et la dernière fonction permet d'initialiser ces valeurs
Code:
void BlinkLed(int enabledCount,int totalCount)
{
  ledBlinkMax = totalCount;
  ledBlinkOn = enabledCount;
}


Un appel de
BlinkLed(500, 1000); fera clignoter la LED pendant 500 cycles sur un total de 1000.
BlinkLed(100, 800); fera clignoter la LED sur un total de 800.

A noter encore que LedBlinking(); est appelé à chaque tour de boucle pour permettre les comptages.

La fonction de positionnement des régulateurs
Cette fonction permet de positionner tous les régulateurs à une valeur fixe. C'est utile soit pour les mettre tous à zero, soit pour les mettre tous à une valeur de départ lors de la vérification de leur fonctionnement.
Code:
float reg1LOutput = 0;
float reg2LOutput = 0;
float reg3LOutput = 0;
float reg4LOutput = 0;
float reg1ROutput = 0;
float reg2ROutput = 0;
float reg3ROutput = 0;
float reg4ROutput = 0;

void SetRegulatorsOutput(int value)
{
  reg1LOutput = value;
  reg2LOutput = value;
  reg3LOutput = value;
  reg4LOutput = value;
  reg1ROutput = value;
  reg2ROutput = value;
  reg3ROutput = value;
  reg4ROutput = value;
  SetMasterRegulatorsOutput();
  SetSlaveRegulatorsOutput(); 
}

void SetMasterRegulatorsOutput(void)
{
  reg1LOutput = constrain(reg1LOutput, 0, 255);
  reg2LOutput = constrain(reg2LOutput, 0, 255);
  reg1ROutput = constrain(reg1ROutput, 0, 255);
  reg2ROutput = constrain(reg2ROutput, 0, 255);

  analogWrite(reg1LPin, reg1LOutput);
  analogWrite(reg2LPin, reg2LOutput);
  analogWrite(reg1RPin, reg1ROutput);
  analogWrite(reg2RPin, reg2ROutput);
}

void SetSlaveRegulatorsOutput(void)
{
  reg3LOutput = constrain(reg3LOutput, 0, 255);
  reg4LOutput = constrain(reg4LOutput, 0, 255);
  reg3ROutput = constrain(reg3ROutput, 0, 255);
  reg4ROutput = constrain(reg4ROutput, 0, 255);

  analogWrite(reg3LPin, reg3LOutput);
  analogWrite(reg4LPin, reg4LOutput);
  analogWrite(reg3RPin, reg3ROutput);
  analogWrite(reg4RPin, reg4ROutput);
}


La fonction Reset
La fonction Reset permet essentiellement de placer l'ampli dans son état initial, régulateurs à zero et relais HT off. Il suffit simplement de rappeler les fonctions précédentes.
Code:
void Reset(void)
{
  SetRegulatorsOutput(0); 
  RelayOff();
}


La fonction CheckInRange
Cette fonction va permettre de vérifier si tous les tubes sont dans les limites passées à la fonction en paramètres, et mémorisera le tube responsable en cas de dépassement, ce qui permettra par la suite de l'afficher. Les limites sont passées en paramètre, car elle dépendent de l'étape en cours. Par exemple pas de courant dans les tubes pendant le chauffage est normal, mais en fonctionnement normal, ça peut être considéré comme un dysfonctionnement.

Code:
boolean CheckInRange(int minValue, int maxValue)
{
  if (reg1LCurrentAverage < minValue || reg1LCurrentAverage > maxValue)
  {
    errorTubeNumber = 1;
    return false;
  }

  if (reg2LCurrentAverage < minValue || reg2LCurrentAverage > maxValue)
  {
    errorTubeNumber = 2;
    return false;
  }

  if (reg3LCurrentAverage < minValue || reg3LCurrentAverage > maxValue)
  {
    errorTubeNumber = 3;
    return false;
  }

  if (reg4LCurrentAverage < minValue || reg4LCurrentAverage > maxValue)
  {
    errorTubeNumber = 4;
    return false;
  }

  if (reg1RCurrentAverage < minValue || reg1RCurrentAverage > maxValue)
  {
    errorTubeNumber = 5;
    return false;
  }

  if (reg2RCurrentAverage < minValue || reg2RCurrentAverage > maxValue)
  {
    errorTubeNumber = 6;
    return false;
  }

  if (reg3RCurrentAverage < minValue || reg3RCurrentAverage > maxValue)
  {
    errorTubeNumber = 7;
    return false;
  }

  if (reg4RCurrentAverage < minValue || reg4RCurrentAverage > maxValue)
  {
    errorTubeNumber = 8;
    return false;
  }

  return true;
}


A Suivre...

_________________
Tube Curve Tracer, Diagnostic,
Mon GIT


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Ven 25 Avr 2014, 08:08 
Hors ligne
Membre Assidu
Avatar de l’utilisateur

Inscription: Mar 22 Aoû 2006, 16:47
Messages: 2665
Localisation: Suisse
Les étapes

Les étapes décrites précédemment, vont maintenant pouvoir être complétées en utilisant la série de fonctions disponibles. C'est la variable sequence qui va conditionner l'aiguillage et mémoriser l’étape en cours.

La première étape vérifie simplement avant de démarrer l'ampli que les mesures soient cohérentes, et que l'ampli soit déchargé. On général, on ne s'y attarde jamais.
Code:
#define startCurrent                 30         // 12.45/mA

case SEQ_DISCHARGE:   
    // Discharging
    Reset();
    BlinkLed(800, 1000);

    if(measureLoopCount != 0 || !CheckInRange(-100, startCurrent))
    {
      break;
    }

    sequence++;


Pendant cette séquence, je reset l'ampli (positionne tous à zero et assure que le relais est off) et fais clignoter la LED d'une façon qu'elle reste allumée 800 cycles sur un total de 1000. Ensuite, le programme reste coincé dans la séquence, tant que l'ampli mesure des courants supérieurs à la constante startCurrent, sinon on passe à la séquence suivante.

La séquence suivante est le chauffage, il suffit simplement d'attendre. La LED clignote différement à chaque étape, la c'est 50/50 pendant 800 cycles.
Code:
#define heatTime                     10000      // ~430/seconds. Heat time
int heatCount = 0;

#define ERR_2  2      // 2: Current during heat time
#define ERR_UNK  100  // Unknown error
int errorNumber = ERR_UNK;

case SEQ_HEAT:
    // Startup tempo
    if(!CheckInRange(-100, startCurrent + 10))
    {
      // Fail, no current allowed now
      sequence = SEQ_FAIL;
      errorNumber = ERR_2;
      break;
    }

    Reset();
    BlinkLed(400, 800);

    if(heatCount++ < heatTime)
    {
      break;
    }

    currentLedColor = LED_RED;
    LedOn();
    delay(2000); 
    sequence++;


Le programme tourne simplement dans la boucle tant que heatCount est inférieur à heatTime. Il vérifie qu'aucun courant n'arrive dans les tubes, ce qui pourrait être le cas par exemple si un relais HT reste collé en permanence. En cas d'erreur un code d'erreur est mémorisé de façon à pouvoir l'afficher sur la LED et savoir ce qui c'est passé. A la fin de la séquence, la LED rouge reste allumée 2 secondes avant de passer à la séquence suivante. Comme la séquence suivante enclenche la HT, ça permet de prévenir de façon é retirer les doigts quand on les as dans l'ampli, et fermer les yeux :mrgreen:

La séquence suivant s'appelle démarrage, elle est très courte et permet essentiellement de vérifier que les régulateurs répondent. Pour cela le programme place les consignes des régulateurs à une valeur fixe et vérifie qu'un courant apparaît. Si un des régulateurs ne réponds pas, la HT est coupée et un code d'erreur sera affiché. La LED clignote rapidement par impulsions.
Code:
#define startingOutput               40         // Starting offset before regulating
#define startingTime                 4000       // ~430/seconds. Time required to the reg to start the current (Before 3000)
#define maxCurrent                   750        // 12.45/mA
int startingCount = 0;

#define ERR_3  3      // 3: Out of range during starting
#define ERR_7  7      // 7: Starting too long

case SEQ_STARTING:
    // Starting High Voltage
    currentLedColor = LED_GREEN;
    RelayOn();   
    SetRegulatorsOutput(startingOutput);   
    BlinkLed(20, 200);

    if (!CheckInRange(-100, maxCurrent))
    {
      // Fail left current error
      sequence = SEQ_FAIL;
      errorNumber = ERR_4;
      break;     
    }

    if(startingCount++ > startingTime)
    {
      // Fail, too late
      sequence = SEQ_FAIL;
      errorNumber = ERR_7;
      errorTubeNumber = 0;
      break;
    }

    if(!CheckInRange(regulatingCurrent, maxCurrent))
    {
      break;
    }

    sequence++;


Le programme vérifie aussi à partir de ce moment que le courant ne parte pas à toc, avec la consigne appliquée, ça ne devrait jamais être le cas. La encore si ça arrive, je coupe tout et code d'erreur. Dès que la limite inférieur de courant pour la consigne appliquée est atteinte par tous les tubes, alors je passe à la séquence suivante.

La séquence suivante est la plus compliquée, non pas en matière de ligne de code, mais elle demande un peu de théorie. C'est la régulation. On se passera d'appliquer les formules, je vous rassure, les régulateurs se règlent à tâtonnement, mais il faut quand même un peu comprendre ce qui se passe et comment ça fonctionne. Au pire, si on se loupe, le régulateur oscille, mais comme on n'est pas dans une centrale nucléaire (russe), ça ne vas pas exploser. Et il y a toujours la surveillance des limite qui coupera la HT si on abuse. Ce qu'il faut comprendre c'est qu'est-ce qu'un régulateur et les trois types de contrôles PID.

http://fr.wikipedia.org/wiki/R%C3%A9gulateur_PID

A Suivre...

_________________
Tube Curve Tracer, Diagnostic,
Mon GIT


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Ven 25 Avr 2014, 11:10 
Hors ligne
Membre Assidu
Avatar de l’utilisateur

Inscription: Mar 22 Aoû 2006, 16:47
Messages: 2665
Localisation: Suisse
Une fois la régulation assimilée, nous allons appliquer la théorie à la pratique. Pour cette usage, le but est donc d'ajuster le courant dans un tube en modifiant sa tension de G2 par l'intermédiaire d'un régulateur haute-tension. L'avantage est que la régulation n'a pas besoins d'être rapide, on lui favorisera la stabilité. Le second point intéressant est que les paramètres susceptible de modifier le point de travail proviennent de deux phénomènes qui varient à très long terme:
• La montée en température de l'ampli
• L'usure des tubes.

Donc passer 40s à ajuster le courant ne pose pas de problèmes. Je n'ai donc pas pris en compte la composant I et n'ai réalisé qu'un régulateur P+D.

La fonction principale de l'intégrale est d’accélérer le régulateur lorsqu'il est très loin de sa consigne. On peut imaginer que lorsqu'on veut passer rapidement la vitesse d'une voiture de 0 à 60km/h, on va mettre pied au plancher, puis relâcher progressivement l'accélérateur au fur et à mesure qu'elle accélère pour stabiliser la vitesse à une certaine position de la pédale. Le fait d'aller au delà de la position finale (pied au plancher) est une régulation I. Si la route change de pente, on va devoir modifier légèrement la position de la pédale pour conserver la même vitesse. C'est l'élément D, on dérive le point de travail suite à une modification de l’environnement extérieur. L'élément P proportionnel consiste juste à appliquer une valeur qui est proportionnelle à la différence entre la consigne et la mesure actuelle.

Le seul moyen d'arriver à la consigne avec un régulateur uniquement P est d'avoir un gain infini. Sinon, le régulateur va se stabiliser à une position qui est un compromis entre l'erreur restante et le gain du régulateur. L'exemple typique du régulateur P est celui du régulateur de G2 avec l'ampli opérationnel. C'est un régulateur P dont l'erreur restante est inversement proportionnelle au gain de l'ampli op. Mais comme il est en boucle ouverte, il est presque infini, donc l'erreur restante est presque 0. Ceci fonctionne car le temps de réponse de la boucle est très rapide.

Dans le cas du courant du tube, il n'est pas possible de se contenter d'un simple régulateur P, le temps de réaction de la boucle est trop lent, et la précision nécessaire pour garantir l'équilibrage du push-pull et zero courant dans le transfo. Voici à quoi ressemblerait le régulateur P pour un tube.
Code:
reg1LOutput = gain * (referenceCurrent - reg1LCurrentAverage);

La sortie est proportionnelle à la différence entre la référence et la mesure au gain près.

Avec l'élément D, ça donne:
Code:
reg1LOutput += gain * (referenceCurrent - reg1LCurrentAverage);


La sortie va dériver au fur et à mesure de la différence persiste, au gain près. Ca fonctionne aussi en négatif.

Donc notre step de régulation pour les 8 régulateurs devient
Code:
#define regAbsoluteAverageRatio      0.1
#define regRelativeAverageRatio      0.1
#define referenceMinCurrent          270        // 12.45/mA
#define referenceMaxCurrent          500        // 12.45/mA
#define stabilizationTime            24000      // ~430/seconds. Time required to the reg to stabilize the current
#define tresholdInStabilization      5
#define tresholdInFunction           25
#define tresholdAtStartup            100

#define ERR_4  4      // 4: Stabilization too long
#define ERR_5  5      // 5: Out of range during stabilization

int stabilizationCount = 0;
int referenceCurrent = referenceMinCurrent;
boolean stabilized = false;
int treshold = tresholdAtStartup;

case SEQ_REGULATING:
    // Waiting for reg   
    if (measureLoopCount == 0)
    {
      reg1LOutput += regAbsoluteLoopGain * (referenceCurrent - reg1LCurrentAverage);
      reg2LOutput += regAbsoluteLoopGain * (referenceCurrent - reg2LCurrentAverage);
      reg1ROutput += regAbsoluteLoopGain * (referenceCurrent - reg1RCurrentAverage);
      reg2ROutput += regAbsoluteLoopGain * (referenceCurrent - reg2RCurrentAverage);

      SetMasterRegulatorsOutput();

      reg3LOutput += regRelativeLoopGain * (reg1LCurrentAverage - reg3LCurrentAverage);
      reg4LOutput += regRelativeLoopGain * (reg2LCurrentAverage - reg4LCurrentAverage);
      reg3ROutput += regRelativeLoopGain * (reg1RCurrentAverage - reg3RCurrentAverage);
      reg4ROutput += regRelativeLoopGain * (reg2RCurrentAverage - reg4RCurrentAverage);

      SetSlaveRegulatorsOutput();
    }           

    BlinkLed(20, !stabilized ? 600 : 1200);

    if(!stabilized && stabilizationCount++ > stabilizationTime)
    {
      // Fail, too late
      sequence = SEQ_FAIL;
      errorNumber = ERR_4;
      errorTubeNumber = 0;
      break;
    }

    if (!CheckInRange(stabilized ? minCurrent : regulatingCurrent, maxCurrent))
    {
      // Fail current error
      sequence = SEQ_FAIL;
      errorNumber = ERR_5;
      break;     
    }

    if(!CheckInRange(referenceCurrent - treshold, referenceCurrent + treshold))
    {
      break;
    }

    if (!stabilized)
    {
      // Affine without dead time       
      treshold = tresholdInStabilization;
      stabilized = true;   
      break;   
    }

    sequence++;


On retrouve notre 4 régulateurs principaux qui s'ajustent par rapport à une consigne en dure dans le code, et les 4 secondaires correspondant au tubes en face du push-pull qui s'ajustent par rapport à leur homologues respectifs.
Il y a deux niveaux de régulation, celui ou le régulateur est loin du point de travail, celui ou il est proche. Le temps du premier niveau est compté, alors que le second est libre. Une fois le régulateur arrivé à sa consigne à 25mV près sur l'entrée de l'Arduino, il passe à l'étape suivante.

Pendant l'étape suivante, il ne fais rien, juste vérifie que les courants ne descendent pas en dessous d'un minimum ni passent en dessus d'un maximum. C'est le fonctionnement normal, et c'est sur cette étape que va rester l'ampli en permanence. Si le courant d'un tube sort d'une fourchette réglée à 100mv près de la consigne sur l'entrée de l'Arduino, il retourne à l'étape précédente et reprends la régulation puis reviendra à cette étape une fois régulé. Ceci arrive souvent couramment la première heure, et pratiquement plus après. la LED reste éteinte.

Code:
  case SEQ_FUNCTION:
    // Normal Fonction, wait and see       
    BlinkLed(0, 0);
    RelayOn();

    if(!CheckInRange(minCurrent, maxCurrent))
    {
      // Fail left current error
      sequence = SEQ_FAIL;
      errorNumber = ERR_6;
      break;     
    }

    if(!CheckInRange(referenceCurrent - tresholdInFunction, referenceCurrent + tresholdInFunction))
    {
      stabilizationCount++;     
    }
    else
    {
      stabilizationCount = 0;
    }

    // If 3 times fail, regulate again
    if (stabilizationCount > 3)
    {   
      // Regulate again
      stabilizationCount = 0;
      sequence--;
      break;
    }   

    break;


A Suivre....

_________________
Tube Curve Tracer, Diagnostic,
Mon GIT


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Ven 25 Avr 2014, 15:20 
Hors ligne
Membre Assidu
Avatar de l’utilisateur

Inscription: Mar 22 Aoû 2006, 16:47
Messages: 2665
Localisation: Suisse
La dernière étape est l'étape d'erreur. On ne termine ici qu'en cas de soucis majeur, et un code d'erreur ainsi que le numéro du tube responsable si c'est applicable. peuvent être affichés. Le bouton rotatif, celui permettant de sélectionner les courants sur l'instrument sera utilisé pour sélectionner l'affichage de la LED sur le code ou le tube d'erreur. Ca tombe bien, comme il n'y a plus de HT, il n'y a plus de courant donc le rotatif est libre. L'affichage du code d'erreur se fait en vert, celui du tube en rouge, et la LED clignote pour indiquer le numéro, puis fait une pose de 1.2s et recommence à clignoter autant de fois que le numéro à indiquer. Ca va jusqu'à 8 pour les tubes, et 7 pour les codes.

Code:
default:
    // Fail, protect mode
    Reset();
    currentLedColor = displayTubeNumber ? LED_RED : LED_GREEN;
    BlinkLed(100, 200);

    if (ledBlinkCount == 0)
    {
      blinkErrorCount++;
    }

    if (blinkErrorCount > (displayTubeNumber ? errorTubeNumber : errorNumber))
    {
      blinkErrorCount = 0;
      delay(1200);
    }
  } 


Pour remettre l'ampli en route, il faut soit faire un reset de la carte, soit faire un switch off, switch on de l'ampli.

Le dernier point ajouté après coup sur cet ampli est la polarisation adaptative ou intelligente. Rien ne m'irrite plus que de manger 70W en classe A lorsque j'écoute de la musique tout doucement la nuit, en plus, sur du haut rendement. Bref ça chauffe et bouffe de l’énergie inutilement. Certes, je pourrais avoir un ampli de 1W pour la nuit, 8W la journée et 25W quand j'écoute 'la blague qui tue' mais encore une fois l'Arduino peut apporter une solution partielle. L'idée est de modifier la consigne de polarisation, et le la descendre lorsque le niveau de modulation est insuffisant. On aplatit ainsi le niveau de courbes, vu que la régulation se fait par les G2 et tout en gardant la même tension de G1, réduis le courant dans l'étage final. La première chose à faire à été d'établir une tabelle pour savoir quel niveau de mesure correspond à quel puissance lorsque l'ampli passe en classe B. Après cela, il suffit de calculer avec une bonne marge de sécurité un niveau de polarisation qui se fera pousser par la modulation pour garantir le fonctionnement en classe A le maximum possible. Malheureusement, les régulateurs shunts utilisés à l'origine dans cet ampli ne se prêtent pas bien à ce jeu, car la baisse d'une tension sur la G2, se traduit par une augmentation du courant dans la resistance et le transistor shunt. Ce qu'on gagne dans les tubes, on le perd en partie dans les transistors. J'ai donc limiter la polarisation minimum à la moitié de la polarisation normale. Je vais essayer dans le prochain ampli, (GI30) de résoudre ce problème et de tester les limites de ce type de polarisation.

Pour répondre à cette fonctionnalité, il faut ajouter sur les cathodes, ou ailleurs, une mesure de la modulation peak. Pour cela, j'ai connecté les deux cathodes en plus de la mesure par aop comme cela:
Image

J'ai pris des diodes Schottky a faible chute, mais des diodes type OA90 seraient mieux adaptées. Nous avons donc en entrée A9, une valeur qui varie en fonction du niveau de modulation.

J'ajoute à chaque tour de boucle dans la fonction Loop() une lecture de la valeur et un moyennage
Code:
 
#define peakModulationAverageRatio   1

long peakLModulationSum = 0;
float peakLModulationAverage;

peakLModulationSum += analogRead(peakLModulationPin);

  if (++measureLoopCount >= measureAverageCount)
  {
    peakLModulationAverage += ((peakLModulationSum / measureLoopCount) - peakLModulationAverage) * peakModulationAverageRatio;
 
   peakLModulationSum = 0;
  }


Ensuite la valeur peak va pousser la reference de courant qui va redescendre très très lentement tant que la modulation ne la repousse pas. Il faut compter environ 20 minutes pour que la consigne de polarisation passe du maximum au minimum.
Code:
#define referenceMinCurrent          270        // 12.45/mA
#define referenceMaxCurrent          500        // 12.45/mA
#define peakModulationMin            367
#define peakModulationMax            918

float peakModulation = 0;
int referenceCurrent = referenceMinCurrent;
float referenceRatio = 0;              //0-255
float referenceComputed;

    if (stabilized)
    {
      peakModulation -= 0.008;
      if (peakModulation < peakLModulationAverage)
      {
        peakModulation = peakLModulationAverage;
      }

      if (peakModulation < peakRModulationAverage)
      {
        peakModulation = peakRModulationAverage;
      }

      referenceRatio = (peakModulation - peakModulationMin) * 255 / (peakModulationMax - peakModulationMin);
    }

    referenceRatio = constrain(referenceRatio, 0, 255); 
    referenceComputed = referenceMinCurrent + ((referenceMaxCurrent - referenceMinCurrent) * referenceRatio) / 255;
    referenceCurrent = (int)referenceComputed;


referenceCurrent est la valeur de consigne utilisée par les régulateurs.
Le principe fonctionne bien, l'ampli vu mon rendement est 98% du temps à la polar min, avec du bas rendement, il en serait tout autre. Je n'ai constaté aucune différence sur le rendu entre polar min et polar max, donc j'essayerai d'étendre un peu plus ce procédé dans le GI30.

Je pense qu'avec cette mesure peak, et avec des diodes bien adaptée ou un redresseur 0 offset à aop, il est possible de détecter qu'il n'y pas de musique et passer l'ampli en stand-by HT au bout d'un moment. Je testerai également cette fonctionnalité dans le GI30.

Tous les bouts de codes expliqués ici viennent du programme posté ici ou vous pouvez les retrouver bout à bout à la bonne place et éventuellement des déclarations oubliées.
https://dl.dropboxusercontent.com/u/31629593/Forums/QQE03-12DPP/_QQE0312DPPPAdap.ino

Des questions? :D

Serge

_________________
Tube Curve Tracer, Diagnostic,
Mon GIT


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Ven 25 Avr 2014, 17:48 
Hors ligne
Site Admin
Avatar de l’utilisateur

Inscription: Mer 01 Oct 2003, 22:47
Messages: 8920
Localisation: St Mathieu de Tréviers près de Montpellier
Bonsoir Vapkse

Très très bon boulot tub18 tub18

Comme me l'a suggéré Chanmix pour ma bobineuse , pourquoi ne pas créer un Github
pour les sources , ça permettrai aussi d'avoir des Fork éventuel, si d'autre tente l'expérience
et de versionner le cas échéant pour tes futures développements

A +

_________________
Ce qui est pris n'est plus a prendre


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Sam 26 Avr 2014, 08:59 
Hors ligne
Membre Assidu
Avatar de l’utilisateur

Inscription: Lun 06 Avr 2009, 10:09
Messages: 3113
Localisation: Nantes
Hello Serge,

Merci pour ce retour, comme nous en avions déjà parlé il y a maintenant plusieurs années chez Yves, c'est un projet vraiment intéressant, tu débroussailles une terre vierge :)

Je rebondis et approuve la remarque de Totof d'autant que chaque répo github permet d'avoir un wiki dédié et tout cela gratuitement pour les projets open-source.

J'ai créé une organisation appelée "audiyofan" (si personne n'y voit d'inconvénient :oops: ) et y ai transféré les projets du 6S19P et de l'EL90 à correction différentielle. Tous les autres projets du forum y ont leur place et peuvent ainsi être travaillés, versionnés et documenté à plusieurs.

Je vais ouvrir un fil de discussion à ce sujet, faites moi passer votre identifiant github pour que je puisse vous ajouter dans l'organisation.

Amicalement,
Grégoire

_________________
Utile : un générateur de papiers millimétrés (log, lin), un traceur de fonctions, un simulateur didactique de circuits, baudline frequency analyzer.


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Sam 26 Avr 2014, 11:10 
Hors ligne
Membre Assidu
Avatar de l’utilisateur

Inscription: Mar 22 Aoû 2006, 16:47
Messages: 2665
Localisation: Suisse
Salut Gregoire et Totof.

J'ai en effet essayé de mettre un projet sur GIT, mais autant je vois l'utilité et les avantages lorsque l'on y place un projet de code avec plusieurs fichiers devant être versionnés, voir des branches tirées pour des développements parallèles, autant ici, je ne vois pas bien les avantages qu'on en tire comparé aux inconvénients.

Déjà ça fait rédiger les trucs à double comparé au forum, et en deux langues, et il me semble que le forum est quand même plus adapté à la rédaction que le wiki de GIT. L'interface utilisateur sous windows est toute pourrie, et il faut se le farcir en ligne de commande, quand au versioning, la encore, le code Arduino est un seul fichier, fortement lié au hardware, donc un code par ampli, et qui ne change pas beaucoup une fois au point. Et on n'as pas les avantages du forum au niveau de la communication bi-directionnelle.

Le seul avantage que je vois est le regroupement des projet à un seul endroit et un sommaire, mais c'est aussi faisable ici, en faisant par exemple un post bloqué au sommet de la rubrique Lampes, qui résume tous les projets comme le fais régulièrement XDA avec les firmware de téléphone. http://forum.xda-developers.com/showthread.php?t=2544361

Serge

_________________
Tube Curve Tracer, Diagnostic,
Mon GIT


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Sam 26 Avr 2014, 12:43 
Hors ligne
Site Admin
Avatar de l’utilisateur

Inscription: Mer 01 Oct 2003, 22:47
Messages: 8920
Localisation: St Mathieu de Tréviers près de Montpellier
Bonjour

Bonne idée quand même ce github Audiyofan , ça permettrai au minimum de regrouper
les projets phare du forum , et plus précisément les projets collaboratifs :mrgreen:

De plus AMHA si ton post ici se multiplie comme le post de l'IVTCY, un résumé sur Github
rend la lectrure plus aisée au nouvel inscrit ou arrivant :wink:

C'est complémentaire je pense, a discuter :oops:

Bonne après midi

_________________
Ce qui est pris n'est plus a prendre


Haut
 Profil  
 
 Sujet du message: Re: Utiliser un Arduino dans un ampli à tubes (Software)
MessagePosté: Dim 27 Avr 2014, 10:46 
Hors ligne
Membre Assidu

Inscription: Dim 09 Jan 2011, 09:52
Messages: 488
Localisation: Drome
bonjour a tous
Serge,sacré travail d'écriture que tu as fait, bien expliqué.
tub18
mes amitiés
Alain


Haut
 Profil  
 
Afficher les messages postés depuis:  Trier par  
Poster un nouveau sujet Répondre au sujet  [ 11 messages ] 

Heures au format UTC + 1 heure


Qui est en ligne

Utilisateurs parcourant ce forum: Aucun utilisateur enregistré et 3 invités


Vous ne pouvez pas poster de nouveaux sujets
Vous ne pouvez pas répondre aux sujets
Vous ne pouvez pas éditer vos messages
Vous ne pouvez pas supprimer vos messages
Vous ne pouvez pas joindre des fichiers

Aller à:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Traduction par: phpBB-fr.com