CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_BattMonitor/AP_BattMonitor_AD7091R5.cpp
Views: 1798
1
#include "AP_BattMonitor_AD7091R5.h"
2
3
/**
4
* @brief You can use it to Read Current and voltage of 1-3 batteries from a ADC extender IC over I2C.
5
* AD7091R5 is a ADC extender and we are using it to read current and voltage of multiple batteries.
6
* Examples of Pin combination:
7
* 1)Pin 50 = Voltage 51,52,53 = Current. For 3 battery combination Voltage will be same accross.
8
* 2)Pin 50,51 = Voltage and Current Battery 1 - Pin 52,53 = Voltage and Current Battery 2
9
* Only the First instance of Battery Monitor will be reading the values from IC over I2C.
10
* Make sure you understand the method of calculation used in this driver before using it.
11
* e.g. using pin 1 on IC to read voltage of 2 batteries and pin 2 and 3 to read current from individual battery.
12
* Pin number represents 50 = pin 1, 51 = pin 2 and so on 52, 53
13
* BATT2_Monitor = 24 , BATT3_Monitor = 24
14
* BATT2_VOLT_PIN = 50 , BATT3_VOLT_PIN = 50
15
* BATT2_CURR_PIN = 51 , BATT3_CURR_PIN = 52
16
*
17
*
18
*/
19
20
#if AP_BATTERY_AD7091R5_ENABLED
21
22
#include <AP_HAL/AP_HAL.h>
23
#include <AP_Common/AP_Common.h>
24
#include <AP_Math/AP_Math.h>
25
26
//macro defines
27
#define AD7091R5_I2C_ADDR 0x2F // A0 and A1 tied to GND
28
#define AD7091R5_I2C_BUS 0
29
#define AD7091R5_RESET 0x02
30
#define AD7091R5_RESULT_ADDR 0x00
31
#define AD7091R5_CHAN_ADDR 0x01
32
#define AD7091R5_CONF_ADDR 0x02
33
#define AD7091R5_CH_ID(x) ((x >> 5) & 0x03)
34
#define AD7091R5_RES_MASK 0x0F
35
#define AD7091R5_REF 3.3f
36
#define AD7091R5_RESOLUTION (float)4096
37
#define AD7091R5_PERIOD_USEC 100000
38
#define AD7091R5_BASE_PIN 50
39
40
41
extern const AP_HAL::HAL& hal;
42
const AP_Param::GroupInfo AP_BattMonitor_AD7091R5::var_info[] = {
43
44
// @Param: VOLT_PIN
45
// @DisplayName: Battery Voltage sensing pin on the AD7091R5 Ic
46
// @Description: Sets the analog input pin that should be used for voltage monitoring on AD7091R5.
47
// @Values: -1:Disabled
48
// @User: Standard
49
// @RebootRequired: True
50
AP_GROUPINFO("VOLT_PIN", 56, AP_BattMonitor_AD7091R5, _volt_pin, 0),
51
52
// @Param: CURR_PIN
53
// @DisplayName: Battery Current sensing pin
54
// @Description: Sets the analog input pin that should be used for Current monitoring on AD7091R5.
55
// @Values: -1:Disabled
56
// @User: Standard
57
// @RebootRequired: True
58
AP_GROUPINFO("CURR_PIN", 57, AP_BattMonitor_AD7091R5, _curr_pin, 0),
59
60
// @Param: VOLT_MULT
61
// @DisplayName: Voltage Multiplier
62
// @Description: Used to convert the voltage of the voltage sensing pin (@PREFIX@VOLT_PIN) to the actual battery's voltage (pin_voltage * VOLT_MULT).
63
// @User: Advanced
64
AP_GROUPINFO("VOLT_MULT", 58, AP_BattMonitor_AD7091R5, _volt_multiplier, 0),
65
66
// @Param: AMP_PERVLT
67
// @DisplayName: Amps per volt
68
// @Description: Number of amps that a 1V reading on the current sensor corresponds to.
69
// @Units: A/V
70
// @User: Standard
71
AP_GROUPINFO("AMP_PERVLT", 59, AP_BattMonitor_AD7091R5, _curr_amp_per_volt, 0),
72
73
// @Param: AMP_OFFSET
74
// @DisplayName: AMP offset
75
// @Description: Voltage offset at zero current on current sensor
76
// @Units: V
77
// @User: Standard
78
AP_GROUPINFO("AMP_OFFSET", 60, AP_BattMonitor_AD7091R5, _curr_amp_offset, 0),
79
80
// @Param: VLT_OFFSET
81
// @DisplayName: Volage offset
82
// @Description: Voltage offset on voltage pin. This allows for an offset due to a diode. This voltage is subtracted before the scaling is applied
83
// @Units: V
84
// @User: Advanced
85
AP_GROUPINFO("VLT_OFFSET", 61, AP_BattMonitor_AD7091R5, _volt_offset, 0),
86
87
// CHECK/UPDATE INDEX TABLE IN AP_BattMonitor_Backend.cpp WHEN CHANGING OR ADDING PARAMETERS
88
89
AP_GROUPEND
90
};
91
92
93
//Variable initialised to read from first instance.
94
AP_BattMonitor_AD7091R5::AnalogData AP_BattMonitor_AD7091R5::_analog_data[AD7091R5_NO_OF_CHANNELS];
95
bool AP_BattMonitor_AD7091R5::_first = true;
96
bool AP_BattMonitor_AD7091R5::_health = false;
97
98
/**
99
* @brief Construct a new ap battmonitor ad7091r5::ap battmonitor ad7091r5 object
100
*
101
* @param mon
102
* @param mon_state
103
* @param params
104
*/
105
AP_BattMonitor_AD7091R5::AP_BattMonitor_AD7091R5(AP_BattMonitor &mon,
106
AP_BattMonitor::BattMonitor_State &mon_state,
107
AP_BattMonitor_Params &params) :
108
AP_BattMonitor_Backend(mon, mon_state, params)
109
{
110
AP_Param::setup_object_defaults(this, var_info);
111
_state.var_info = var_info;
112
}
113
114
/**
115
* @brief probe and initialize the sensor and register call back
116
*
117
*/
118
void AP_BattMonitor_AD7091R5::init()
119
{
120
// voltage and current pins from params and check if there are in range
121
if (_volt_pin.get() >= AD7091R5_BASE_PIN && _volt_pin.get() <= AD7091R5_BASE_PIN + AD7091R5_NO_OF_CHANNELS &&
122
_curr_pin.get() >= AD7091R5_BASE_PIN && _curr_pin.get() <= AD7091R5_BASE_PIN + AD7091R5_NO_OF_CHANNELS) {
123
volt_buff_pt = _volt_pin.get() - AD7091R5_BASE_PIN;
124
curr_buff_pt = _curr_pin.get() - AD7091R5_BASE_PIN;
125
}
126
else{
127
return; //pin values are out of range
128
}
129
130
// only the first instance read the i2c device
131
if (_first) {
132
_first = false;
133
// probe i2c device
134
_dev = hal.i2c_mgr->get_device(AD7091R5_I2C_BUS, AD7091R5_I2C_ADDR);
135
136
if (_dev) {
137
WITH_SEMAPHORE(_dev->get_semaphore());
138
_dev->set_retries(10); // lots of retries during probe
139
//Reset and config device
140
if (_initialize()) {
141
_dev->set_retries(2); // drop to 2 retries for runtime
142
_dev->register_periodic_callback(AD7091R5_PERIOD_USEC, FUNCTOR_BIND_MEMBER(&AP_BattMonitor_AD7091R5::_read_adc, void));
143
}
144
}
145
}
146
}
147
148
/**
149
* @brief read - read the voltage and curren
150
*
151
*/
152
void AP_BattMonitor_AD7091R5::read()
153
{
154
155
WITH_SEMAPHORE(sem);
156
//copy global health status to all instances
157
_state.healthy = _health;
158
159
//return if system not healthy
160
if (!_state.healthy) {
161
return;
162
}
163
164
//voltage conversion
165
_state.voltage = (_data_to_volt(_analog_data[volt_buff_pt].data) - _volt_offset) * _volt_multiplier;
166
167
//current amps conversion
168
_state.current_amps = (_data_to_volt(_analog_data[curr_buff_pt].data) - _curr_amp_offset) * _curr_amp_per_volt;
169
170
// calculate time since last current read
171
uint32_t tnow = AP_HAL::micros();
172
uint32_t dt_us = tnow - _state.last_time_micros;
173
174
// update total current drawn since startup
175
update_consumed(_state, dt_us);
176
177
// record time
178
_state.last_time_micros = tnow;
179
}
180
181
/**
182
* @brief read all four channels and store the results
183
*
184
*/
185
void AP_BattMonitor_AD7091R5::_read_adc()
186
{
187
uint8_t data[AD7091R5_NO_OF_CHANNELS*2];
188
//reset and reconfigure IC if health status is not good.
189
if (!_state.healthy) {
190
_initialize();
191
}
192
//read value
193
bool ret = _dev->transfer(nullptr, 0, data, sizeof(data));
194
WITH_SEMAPHORE(sem);
195
if (ret) {
196
for (int i=0; i<AD7091R5_NO_OF_CHANNELS; i++) {
197
uint8_t chan = AD7091R5_CH_ID(data[2*i]);
198
_analog_data[chan].data = ((uint16_t)(data[2*i]&AD7091R5_RES_MASK)<<8) | data[2*i+1];
199
}
200
_health = true;
201
} else {
202
_health = false;
203
}
204
}
205
206
/**
207
* @brief config the adc
208
*
209
* @return true
210
* @return false
211
*/
212
bool AP_BattMonitor_AD7091R5::_initialize()
213
{
214
//reset the device
215
uint8_t data[3] = {AD7091R5_CONF_ADDR, AD7091R5_CONF_CMD | AD7091R5_RESET, AD7091R5_CONF_PDOWN0};
216
217
if(_dev->transfer(data, sizeof(data), nullptr, 0)){
218
//command mode, use external 3.3 reference, all channels enabled, set address pointer register to read the adc results
219
uint8_t data_2[6] = {AD7091R5_CONF_ADDR, AD7091R5_CONF_CMD, AD7091R5_CONF_PDOWN0, AD7091R5_CHAN_ADDR, AD7091R5_CHAN_ALL, AD7091R5_RESULT_ADDR};
220
return _dev->transfer(data_2, sizeof(data_2), nullptr, 0);
221
}
222
return false;
223
}
224
225
/**
226
* @brief convert binary reading to volts
227
*
228
* @param data
229
* @return float
230
*/
231
float AP_BattMonitor_AD7091R5::_data_to_volt(uint32_t data)
232
{
233
return (AD7091R5_REF/AD7091R5_RESOLUTION)*data;
234
}
235
236
#endif // AP_BATTERY_AD7091R5_ENABLED
237
238