Skyward boardcore
Loading...
Searching...
No Matches
ADS1118.cpp
Go to the documentation of this file.
1/* Copyright (c) 2020 Skyward Experimental Rocketry
2 * Author: Alberto Nidasio
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "ADS1118.h"
24
26#include <interfaces/endianness.h>
27
28namespace Boardcore
29{
30
31const ADS1118::ADS1118Config ADS1118::ADS1118_DEFAULT_CONFIG = {
32 SINGLE_SHOT_MODE, FSR_2_048, MUX_AIN0_AIN1, 0, 0,
33 VALID_OPERATION, PULL_UP_EN, ADC_MODE, DR_128};
34
35ADS1118::ADS1118(SPIBusInterface& bus, miosix::GpioPin cs,
36 ADS1118Config config_, SPIBusConfig spiConfig)
37 : ADS1118(SPISlave(bus, cs, spiConfig), config_, false)
38{
39}
40
41ADS1118::ADS1118(SPISlave spiSlave_, ADS1118Config config_, bool busyWait_,
42 int16_t tempDivider_)
43 : spiSlave(spiSlave_), baseConfig(config_), busyWait(busyWait_),
44 tempDivider(tempDivider_)
45
46{
47 // Initialize to 0 all the channels
48 for (auto i = 0; i < NUM_OF_CHANNELS; i++)
49 {
50 channelsConfig[i].word = 0;
51
52 // Set channel id for each ADS1118Data object in values array
53 values[i].channelId = i;
54 }
55
56 // Reset the last written config value
57 lastConfig.word = 0;
58 lastConfigIndex = 0;
59}
60
62{
63 SPIBusConfig spiConfig{};
65 spiConfig.mode = SPI::Mode::MODE_1;
66 return spiConfig;
67}
68
69bool ADS1118::init() { return true; }
70
72{
73 enableInput(mux, baseConfig.bits.rate, baseConfig.bits.pga);
74}
75
77{
78 channelsConfig[mux].bits.mode = baseConfig.bits.mode;
79 channelsConfig[mux].bits.pga = pga;
80 channelsConfig[mux].bits.mux = mux;
81 channelsConfig[mux].bits.singleShot = 1;
82 channelsConfig[mux].bits.noOp = VALID_OPERATION;
83 channelsConfig[mux].bits.pullUp = baseConfig.bits.pullUp;
84 channelsConfig[mux].bits.tempMode = ADC_MODE;
85 channelsConfig[mux].bits.rate = rate;
86
87 // Decrement the sample counter in order to read the temperature earlier on
88 sampleCounter--;
89}
90
91void ADS1118::disableInput(ADS1118Mux mux) { channelsConfig[mux].word = 0; }
92
94{
95 for (auto i = 0; i < NUM_OF_CHANNELS; i++)
96 channelsConfig[i].word = 0;
97}
98
100{
101 channelsConfig[TEMP_CHANNEL].word = TEMP_CONFIG;
102}
103
104void ADS1118::disableTemperature() { channelsConfig[TEMP_CHANNEL].word = 0; }
105
106void ADS1118::enablePullUpResistor() { baseConfig.bits.pullUp = PULL_UP_EN; }
107
108void ADS1118::disablePullUpResistor() { baseConfig.bits.pullUp = PULL_UP_DIS; }
109
110void ADS1118::enableConfigCheck() { configCheck = true; }
111
112void ADS1118::disableConfigCheck() { configCheck = false; }
113
115{
116 readChannel(mux);
117 return getVoltage(mux);
118}
119
125
126ADS1118Data ADS1118::getVoltage(ADS1118Mux mux) { return values[mux]; }
127
133
134int ADS1118::getConversionTime(int8_t channel)
135{
136 if (channel >= 0 && channel <= TEMP_CHANNEL)
137 return CONV_TIME[channelsConfig[channel].bits.rate];
138 else
139 return 0;
140}
141
143{
144 // Save the current configuration
145 bool prevConfigCheck = configCheck;
146 uint16_t prevTempConfig = channelsConfig[TEMP_CHANNEL].word;
147
148 // Enable temperature config in case it is not, just in case no other
149 // configuration is enabled
150 channelsConfig[TEMP_CHANNEL].word = TEMP_CONFIG;
151
152 // Enable configuration check
153 configCheck = true;
154
155 // Check the communication by reading the temperature channel
156 readChannel(TEMP_CHANNEL, INVALID_CHANNEL);
157
158 // Restore the configuration
159 configCheck = prevConfigCheck;
160 channelsConfig[TEMP_CHANNEL].word = prevTempConfig;
161
162 // Return false if an error occurred
163 return lastError == 0; // The sensor class misses a constant for no error
164}
165
174{
175 int8_t i = findNextEnabledChannel(lastConfigIndex + 1);
176
177 // Write the next config and read the value (only if lastConfig is valid)
178 readChannel(i, lastConfig.word != 0 ? lastConfigIndex : INVALID_CHANNEL);
179
180 // Save index and config for the next read
181 lastConfig.word = channelsConfig[i].word;
182 lastConfigIndex = i;
183
184 // Increment the sample counter
185 sampleCounter++;
186
187 // Regardless of the readChannel result, return the value stored
188 return values[lastConfigIndex];
189}
190
191void ADS1118::readChannel(int8_t nextChannel, int8_t prevChannel)
192{
193 uint32_t writeData, transferData;
194
196
197 // Prepare the next configuration data
198 if (nextChannel > INVALID_CHANNEL && nextChannel < NUM_OF_CHANNELS)
199 {
200 writeData = channelsConfig[nextChannel].word;
201 }
202 else
203 {
204 // A valid configuration will always be not equal to 0 since the valid
205 // operation bits must be 0b01
206 writeData = 0x0;
207 }
208
209 // Write next configuration and read previous value if necessary
210 transferData = writeData;
211 {
212 SPITransaction transaction(spiSlave);
213 transaction.transfer((uint8_t*)&transferData, configCheck ? 4 : 2);
214 }
215
216 // If enabled and a valid configuration has just been written, check the
217 // read back configuration
218 if (configCheck && writeData)
219 {
220 // Compare the configuration with the second 16 bit word read
221 if ((channelsConfig[nextChannel].word & CONFIG_MASK) !=
222 (transferData >> 16 & CONFIG_MASK))
223 {
224 // Save the error
226
227 // Disable next value conversion
228 lastConfig.word = 0;
229 }
230 }
231
232 // Convert and save the value if last written configuration is valid
233 if (prevChannel > INVALID_CHANNEL && prevChannel < NUM_OF_CHANNELS)
234 {
235 int16_t rawValue = swapBytes16(transferData);
236
237 // TODO: the timestamp should be taken when the configuration is
238 // written, now we could be reading the value after some time!
239 values[prevChannel].voltageTimestamp = TimestampTimer::getTimestamp();
240
241 if (prevChannel != 8) // Voltage value
242 {
243 values[prevChannel].voltage =
244 rawValue * PGA_LSB_SIZE[channelsConfig[prevChannel].bits.pga] /
245 1000;
246 }
247 else // Temperature value
248 {
249 values[TEMP_CHANNEL].voltage = (rawValue / 4) * TEMP_LSB_SIZE;
250 }
251 }
252}
253
258void ADS1118::readChannel(int8_t channel)
259{
260 readChannel(channel, INVALID_CHANNEL);
261
262 if (busyWait)
263 {
264 // Use a busy wait loop to be as precise as possible
265 miosix::delayUs(getConversionTime(channel));
266 }
267 else
268 {
269 // Converto to milliseconds and increment by one to prevent premature
270 // readings
271 miosix::Thread::sleep(getConversionTime(channel) / 1000 + 1);
272 }
273
274 readChannel(INVALID_CHANNEL, channel);
275}
276
281int8_t ADS1118::findNextEnabledChannel(int8_t startChannel)
282{
283 int8_t& channel = startChannel; // Just a change of name
284
285 for (auto i = 0; i < 2; i++)
286 {
287 // Go to the first channel if channel is too big
288 if (channel >= NUM_OF_CHANNELS)
289 channel = 0;
290
291 // Find next enabled mux config
292 for (; channel < NUM_OF_CHANNELS && channelsConfig[channel].word == 0;
293 channel++)
294 ;
295
296 // Check if the channel is valid and, for the temperature channel, if we
297 // have to read it based on sampleCounter and tempDivider
298 // If invalid try to search again starting from the first channel
299 if (channel == TEMP_CHANNEL && sampleCounter % tempDivider != 0)
300 continue;
301 if (channel < NUM_OF_CHANNELS)
302 return channel;
303 }
304
305 // If no valid channel has been fount return an invalid channel
306 return INVALID_CHANNEL;
307}
308
309} // namespace Boardcore
Driver for ADS1118 adc.
Definition ADS1118.h:72
TemperatureData getTemperature()
Returns the last temperature value.
Definition ADS1118.cpp:128
void enableTemperature()
Definition ADS1118.cpp:99
TemperatureData readTemperatureAndWait()
Reads on the fly the temperature.
Definition ADS1118.cpp:120
int getConversionTime(int8_t channel)
Returns the conversion time in us for the specified channel.
Definition ADS1118.cpp:134
ADS1118Data getVoltage(ADS1118Mux mux)
Returns the last read voltage value for the specified channel.
Definition ADS1118.cpp:126
static constexpr int8_t NUM_OF_CHANNELS
Definition ADS1118.h:164
void enableConfigCheck()
Enables the configuration check after it's writing to the device.
Definition ADS1118.cpp:110
ADS1118(SPIBusInterface &bus, miosix::GpioPin cs, ADS1118Config config_, SPIBusConfig spiConfig=getDefaultSPIConfig())
Construct a new ADS1118 object specifying spi bus, spi config and cs pin as well as device configurat...
Definition ADS1118.cpp:35
void disableConfigCheck()
Disables the configuration check after it's writing to the device.
Definition ADS1118.cpp:112
void enablePullUpResistor()
Definition ADS1118.cpp:106
@ PULL_UP_DIS
Pullup resistor disabled on DOUT pin.
Definition ADS1118.h:122
@ PULL_UP_EN
Pullup resistor enabled on DOUT pin (default)
Definition ADS1118.h:123
ADS1118Data sampleImpl() override
Reads the previously configured channel while writing the next enabled configuration.
Definition ADS1118.cpp:173
void disablePullUpResistor()
Definition ADS1118.cpp:108
static constexpr uint8_t VALID_OPERATION
Indicates a valid configuration.
Definition ADS1118.h:156
bool selfTest() override
Writes the temperature configuration and check if it is read back correctly.
Definition ADS1118.cpp:142
void enableInput(ADS1118Mux mux)
Enables the sapling of a specific mux configuration with the main configuration specified in the cons...
Definition ADS1118.cpp:71
void disableInput(ADS1118Mux mux)
Definition ADS1118.cpp:91
static const ADS1118Config ADS1118_DEFAULT_CONFIG
Default configuration.
Definition ADS1118.h:160
bool init() override
Initialize the configuration.
Definition ADS1118.cpp:69
ADS1118Data readInputAndWait(ADS1118Mux mux)
Reads on the fly the specified input.
Definition ADS1118.cpp:114
@ ADC_MODE
ADC mode (default)
Definition ADS1118.h:116
void disableAllInputs()
Definition ADS1118.cpp:93
void disableTemperature()
Definition ADS1118.cpp:104
static SPIBusConfig getDefaultSPIConfig()
Definition ADS1118.cpp:61
static constexpr int8_t TEMP_CHANNEL
Temperature channel number.
Definition ADS1118.h:162
static constexpr int8_t INVALID_CHANNEL
Definition ADS1118.h:166
SensorErrors lastError
Definition Sensor.h:54
Interface for low level access of a SPI bus as a master.
@ MODE_1
CPOL = 1, CPHA = 0 -> Clock high when idle, sample on first edge.
uint64_t getTimestamp()
Returns the current timer value in microseconds.
This file includes all the types the logdecoder script will decode.
uint64_t voltageTimestamp
Definition SensorData.h:357
SPI Bus configuration for a specific slave.
SPI::ClockDivider clockDivider
< Peripheral clock division
Contains information about a single SPI slave device.
Structure of configuration word.
Definition ADS1118.h:127
struct Boardcore::ADS1118::ADS1118Config::@0 bits
Includes all the configuration bits.
uint16_t word
Representation in word (16-bits) format.
Definition ADS1118.h:147