Statistics
| Branch: | Tag: | Revision:

amiro-os / components / power / adconverter.cpp @ 58fe0e0b

History | View | Annotate | Download (5.14 KB)

1
#include <amiro/power/adconverter.hpp>
2

    
3
#include <global.hpp>
4
#include <amiro/Constants.h>
5
#include <amiro/power/ina219.hpp>
6
#include <amiro/util/util.h>
7

    
8
#include <chprintf.h>
9

    
10
using namespace amiro;
11

    
12
extern Global global;
13

    
14
void adcVsysErrorCallback(ADCDriver *adcp, adcerror_t err)
15
{
16
  if (err == ADC_ERR_WATCHDOG) {
17
    chSysLockFromIsr();
18
    if (adcp->thread != NULL) {
19
      chEvtSignalI(adcp->thread, EVENT_MASK(ADConverter::EVT_WATCHDOG));
20
      adcp->thread = NULL;
21
    }
22
    chSysUnlockFromIsr();
23
  }
24

    
25
  return;
26
}
27

    
28
void adcTimerCallback(Thread *tp)
29
{
30
  if (tp != NULL) {
31
    chEvtSignal(tp, EVENT_MASK(ADConverter::EVT_TIMER));
32
  }
33
  return;
34
}
35

    
36
uint16_t
37
ADConverter::uV2Adc(const uint32_t uV)
38
{
39
  // NOTE: The factor 5.33 is an approximation of the voltage divider.
40
  //       Its value should be 5.0, but due to too large resistors the factor is shifted.
41
  // TODO: 5.33 should be replaced by a non linear function, which needs to be optained from the system.
42

    
43
  // get the current VDD voltage
44
  global.ina219[constants::PowerManagement::INA_VDD].update();
45
  INA219::BusVoltage vdd = global.ina219[constants::PowerManagement::INA_VDD].getVoltage();
46
  return float(uV)/1000000 / 5.33f / (float(vdd.voltage_uV)/1000000) * 4096;
47
}
48

    
49
uint32_t ADConverter::adc2uV(const uint16_t adc)
50
{
51
  // NOTE: The factor 5.33 is an approximation of the voltage divider.
52
  //       Its value should be 5.0, but due to too large resistors the factor is shifted.
53
  // TODO: 5.33 should be replaced by a non linear function, which needs to be optained from the system.
54

    
55
  // get the current VDD voltage
56
  global.ina219[constants::PowerManagement::INA_VDD].update();
57
  INA219::BusVoltage vdd = global.ina219[constants::PowerManagement::INA_VDD].getVoltage();
58
  return float(adc)/4096 * (float(vdd.voltage_uV)/1000000) * 5.33f * 1000000;
59
}
60

    
61
ADConverter::ADConverter(ADCDriver& adcdrv, ADCConversionGroup& adccgrp, const uint32_t threshold_uV) :
62
  driver(adcdrv), conversion_group(adccgrp), watchdog_threshold_uV(threshold_uV)
63
{
64
}
65

    
66

    
67
ADConverter::~ADConverter()
68
{
69
}
70

    
71
msg_t
72
ADConverter::main()
73
{
74
  // initialize a timer event for later use
75
  evtInit(&this->evt_timer, MS2ST(1));
76
  this->evt_source = reinterpret_cast<EvtSource*>(&this->evt_timer.et_es);
77
  this->evt_source->registerOne(&this->evt_listener, EVT_TIMER);
78

    
79
  // initialize the watchdog
80
  this->enableWatchdog(true);
81
  this->conversion_group.htr = ADC_HTR(uV2Adc(this->watchdog_threshold_uV));
82
  this->conversion_group.ltr = 0;
83
  this->driver.thread = this->thread_ref;
84

    
85
  adcStartConversion(&this->driver, &this->conversion_group, global.adc1_buffer, ARRAY_SIZE(global.adc1_buffer));
86

    
87
  while (!this->shouldTerminate())
88
  {
89
    eventmask_t event = this->waitOneEventTimeout(ALL_EVENTS, MS2ST(1000));
90

    
91
    switch (event)
92
    {
93
      // watchdog event (fired by ADC interrupt)
94
      case EVENT_MASK(EVT_WATCHDOG):
95
      {
96
        // disable the chargers
97
        global.bq24103a[constants::PowerManagement::BAT_P7]->enable(false);
98
        global.bq24103a[constants::PowerManagement::BAT_P8]->enable(false);
99

    
100
        // to avoid multiple interrupts because of noise, wait 1ms before setting the chargers and ADC
101
        this->enableWatchdog(false);
102
        evtStart(&this->evt_timer);
103
        break;
104
      }
105

    
106
      // timer event
107
      case EVENT_MASK(EVT_TIMER):
108
      {
109
        evtStop(&this->evt_timer);
110

    
111
        // read the current voltage
112
        this->conversion_group.circular = FALSE;
113
        adcConvert(&this->driver, &this->conversion_group, global.adc1_buffer, ARRAY_SIZE(global.adc1_buffer));
114
        this->conversion_group.circular = TRUE;
115
        this->enableWatchdog(true); // Watchdog was disabled in ISR
116

    
117
        if (global.adc1_buffer[0] > uV2Adc(this->watchdog_threshold_uV))
118
        {
119
          chprintf((BaseSequentialStream*)&SD1, "%f > 9V detected: now charging\n", ((float)adc2uV(global.adc1_buffer[0]))/1000000.0f);
120
          global.robot.getPowerStatus().charging_flags.content.vsys_higher_than_9V = true;
121
          global.bq24103a[constants::PowerManagement::BAT_P7]->enable(true);
122
          global.bq24103a[constants::PowerManagement::BAT_P8]->enable(true);
123
          this->conversion_group.htr = ADC_HTR_HT;
124
          this->conversion_group.ltr = ADC_LTR(uV2Adc(this->watchdog_threshold_uV));
125
        } else {
126
          chprintf((BaseSequentialStream*)&SD1, "%f < 9V detected: not charging\n", ((float)adc2uV(global.adc1_buffer[0]))/1000000.0f);
127
          global.robot.getPowerStatus().charging_flags.content.vsys_higher_than_9V = false;
128
          this->conversion_group.htr = ADC_HTR(uV2Adc(this->watchdog_threshold_uV));
129
          this->conversion_group.ltr = 0;
130
        }
131
        this->driver.thread = this->thread_ref;
132
        adcStartConversion(&this->driver, &this->conversion_group, global.adc1_buffer, ARRAY_SIZE(global.adc1_buffer));
133
        break;
134
      }
135

    
136
      // timeout
137
      case 0:
138

    
139
      default:
140
        break;
141
    }
142
  }
143

    
144
  return RDY_RESET;
145
}
146

    
147
void
148
ADConverter::enableWatchdog(const bool enable)
149
{
150
  if (enable) {
151
    this->conversion_group.cr1 |= ADC_CR1_AWDEN | ADC_CR1_AWDIE;
152
    this->conversion_group.error_cb = &adcVsysErrorCallback;
153
  } else {
154
    this->conversion_group.cr1 &= ~(ADC_CR1_AWDEN | ADC_CR1_AWDIE);
155
    this->conversion_group.error_cb = NULL;
156
  }
157
}
158

    
159