Outils pour utilisateurs

Outils du site


wiki:projets:smartphone-geiger:internal_voltage

Mesure précise de tension

Pour que l'asservissement de la haute tension fonctionne correctement, nous avons besoin de faire des mesures précises. Par défaut, le RFduino prend sa tension d'alimentation comme référence, ce qui peut causer des erreurs en cas de baisse de tension des piles.

Nous avons mis au point une expérience pour vérifier cela : nous alimentons le RFduino avec un générateur de tension, nous branchons un multimètre pour vérifier la tension de sortie et nous connectons la pin 3 du RFduino à la pin Vcc (3 V).

Nous téléversons le sketch suivant sur le RFduino :

#include <RFduinoBLE.h>
 
#define V_PIN 3
 
void setup() {
  pinMode(V_PIN, INPUT);
 
  RFduinoBLE.deviceName = "TestTension";
  RFduinoBLE.advertisementData = "";
 
  RFduinoBLE.begin();
}
 
// La fonction map de l'api ne fonctionne que pour des longs
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
 
void loop() {
  float v = analogRead(V_PIN); // On lit la valeur de la pin 3, elle est comprise entre 0 et 1023
 
  v = mapf(v, 0, 1023.0, 0, 3.6); // On convertit v en volts
 
  RFduinoBLE.sendInt((int)(v*1000)); // On envoi le résultat à l'iPad pour l'afficher
 
  delay(2000); // On attend un peu pour ne pas spamer l'iPad
}

La plage de mesure des ADC est de 0 à 3,6 V (c'est dans la datasheet), pour une valeur lue de 0 à 1023, d'où les paramètres du mapf (un peu lourd, certes). Le résultat est celui attendu : même en faisant varier la tension sur le générateur, la valeur mesurée par le RFduino ne varie pas.

On trouve la solution ici : http://forum.rfduino.com/index.php?topic=265.0

Il suffit de sélectionner la tension de référence interne du microcontrôleur. Ce qui donne le sketch suivant :

#include <RFduinoBLE.h>
 
#define V_PIN 3
 
void setup() {
  pinMode(V_PIN, INPUT);
 
  RFduinoBLE.deviceName = "TestTension";
  RFduinoBLE.advertisementData = "";
 
  RFduinoBLE.begin();
 
  analogReference(VBG); // Ligne modifiée
}
 
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
 
void loop() {
  float v = analogRead(V_PIN);
 
  v = mapf(v, 0, 1023.0, 0, 3.6);
 
  RFduinoBLE.sendInt((int)(v*1000));
 
  delay(2000);
}

Avec ce programme, nous obtenons une mesure précise, dont la différence avec la valeur du multimètre est inférieure à 0,01V. Ce qui signifie pour notre application que l'on aura une précision de l'ordre de 177*0,01=1,77V sur la mesure de la haute tension.

Simplification de la génération de PWM

Précédemment, nous avons vu comment générer un PWM a une certaine fréquence avec un rapport cyclique variable. Expérimentalement, nous avons constatés une certaine inconstance dans le PWM ainsi généré. Nous avons donc choisis de passer un peu plus de temps à recoder cette partie :

#include <RFduinoBLE.h>
 
#define PERIOD 2000
#define PWM_RESOLUTION 255
 
#define PWM_PIN 2
 
int pwm_duty_cycle = 0;
int pwm_count = 0;
 
void TIMER1_INTERUPT(void) {
  if (NRF_TIMER1->EVENTS_COMPARE[0] != 0) {
    if (pwm_count < pwm_duty_cycle) {
      digitalWrite(PWM_PIN, HIGH);  
    } else {
      digitalWrite(PWM_PIN, LOW);
    }
    pwm_count++;
    if (pwm_count==PWM_RESOLUTION) {
      pwm_count = 0;  
    }
    NRF_TIMER1->EVENTS_COMPARE[0] = 0;
  }
}
 
void configTimer(NRF_TIMER_Type* nrf_timer, IRQn_Type irqn, callback_t callback) {
  nrf_timer->TASKS_STOP = 1; // Arrete le timer
  nrf_timer->MODE = TIMER_MODE_MODE_Timer;
  nrf_timer->BITMODE = (TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos);
  nrf_timer->PRESCALER = 4; // résolution de 1 usec
  nrf_timer->TASKS_CLEAR = 1;
  nrf_timer->CC[0] = PERIOD / PWM_RESOLUTION;
  nrf_timer->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos;
  nrf_timer->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos);
  attachInterrupt(irqn, callback);
  nrf_timer->TASKS_START = 1; // Redemarre le timer
}
 
void setup() {
  pinMode(PWM_PIN, OUTPUT);
 
  RFduinoBLE.deviceName = "testpwm";
  RFduinoBLE.advertisementData = "";
 
  RFduinoBLE.begin();
 
  configTimer(NRF_TIMER1, TIMER1_IRQn, TIMER1_INTERUPT);
}
 
void loop() {
 
}
 
int getInt(char*data, int len) {
  int value=0;
  int p=1;
  for (int i=len-1 ; i>=0 ; i--) {
    value += p*(data[i]-'0');
    p*=10;
  }
  return value;
}
 
void RFduinoBLE_onReceive(char *data, int len) {
  int i = getInt(data, len);
  pwm_duty_cycle = (int)((i*PWM_RESOLUTION)/500.0);
}

Dans cette version nous utilisons un seul timer. La fonction d'interruption est appelée toute les PERIOD / RESOLUTION, c'est à dire RESOLUTION fois par période. Ensuite nous incrémentons un compteur qui va compter le nombre de tick passés dans la période. Ensuite nous n'avons plus qu'à comparer ce compteur avec la valeur de rapport cyclique demandée (comprise entre 0 et RESOLUTION).

wiki/projets/smartphone-geiger/internal_voltage.txt · Dernière modification: 2016/09/11 13:15 (modification externe)