Skip to content

Commit 4e3b0d2

Browse files
PhazonicRidleykammce
authored andcommitted
✨ Added a generic N port Adc mux driver
1 parent 52a689b commit 4e3b0d2

2 files changed

Lines changed: 216 additions & 0 deletions

File tree

include/libhal-soft/adc_mux.hpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
#include <array>
17+
#include <libhal-util/map.hpp>
18+
#include <libhal-util/math.hpp>
19+
#include <libhal/adc.hpp>
20+
#include <libhal/error.hpp>
21+
#include <libhal/output_pin.hpp>
22+
#include <libhal/steady_clock.hpp>
23+
#include <span>
24+
25+
using namespace hal::literals;
26+
using namespace std::chrono_literals;
27+
28+
namespace hal {
29+
30+
// This function does not take into account setup and hold times.
31+
hal::status set_mux_channel(uint32_t p_position,
32+
std::span<hal::output_pin*> p_signal_pins)
33+
{
34+
35+
for (int i = 0; i < static_cast<int>(p_signal_pins.size()); i++) {
36+
37+
bool value = bool(p_position & (1 << i));
38+
HAL_CHECK(p_signal_pins[i]->level(value));
39+
}
40+
return hal::success();
41+
}
42+
43+
/**
44+
* @brief A driver for Analog to Digital mulitplexers, creates adc pin objects
45+
* for each mux output
46+
*/
47+
class adc_multiplexer
48+
{
49+
public:
50+
/**
51+
* @brief An internal class of a hal ADC implimentation to represent a
52+
* multiplexer pin
53+
*/
54+
class adc_mux_pin : public hal::adc
55+
{
56+
public:
57+
/** @brief An internal constructor to build an adc mux pin.
58+
* @param p_mux_port the channel port of the pin on the mux itself.
59+
* @param p_mux A pointer to the multiplexer.
60+
* */
61+
adc_mux_pin(uint8_t p_mux_port, adc_multiplexer* p_mux)
62+
: m_mux_port{ p_mux_port }
63+
, m_mux{ p_mux } {}; // TODO add m_mux
64+
65+
/**
66+
* @brief Reads the pin.
67+
*/
68+
hal::result<read_t> driver_read() override
69+
{
70+
return m_mux->read_channel(m_mux_port);
71+
}
72+
73+
private:
74+
const uint8_t m_mux_port;
75+
adc_multiplexer* m_mux;
76+
};
77+
78+
/**
79+
* @param p_signal_pins A span of output pins to represent the signal pins of
80+
* the mux, assuming the mux signals digitally.
81+
* @param p_source_pin The adc source pin that reads the output of the mux.
82+
*/
83+
adc_multiplexer(std::span<hal::output_pin*> p_signal_pins,
84+
hal::adc* p_source_pin)
85+
: m_signal_pins{ p_signal_pins }
86+
, m_source_pin{ p_source_pin } {};
87+
88+
/**
89+
* @brief Returns a multiplexer ADC pin
90+
* @param p_channel The channel number of the pin.
91+
*/
92+
hal::result<adc_mux_pin> pin_factory(uint8_t p_channel)
93+
{
94+
// TODO: Ask if we should keep track of channels in use, if a port is asked
95+
// for again, disallow it.
96+
const std::uint32_t max_size = 1 << m_signal_pins.size();
97+
if (p_channel >= max_size) {
98+
return hal::new_error(
99+
"Unable to add any more pins to this multiplexer.\n");
100+
}
101+
102+
return adc_mux_pin(p_channel, this);
103+
}
104+
105+
private:
106+
/**
107+
* @brief Reads the channel
108+
*
109+
* @param p_mux_port
110+
* @return hal::result<hal::adc::read_t>
111+
*/
112+
hal::result<hal::adc::read_t> read_channel(uint8_t p_mux_port)
113+
{
114+
set_mux_channel(p_mux_port, m_signal_pins);
115+
return HAL_CHECK(m_source_pin->read());
116+
}
117+
118+
private:
119+
std::span<hal::output_pin*> m_signal_pins;
120+
hal::adc* m_source_pin;
121+
};
122+
123+
} // namespace hal

tests/adc_mux.test.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "../include/libhal-soft/adc_mux.hpp"
16+
#include <boost/ut.hpp>
17+
#include <iostream>
18+
#include <libhal-mock/adc.hpp>
19+
#include <libhal-mock/output_pin.hpp>
20+
#include <queue>
21+
#include <vector>
22+
23+
namespace hal {
24+
25+
void adc_mux_test()
26+
{
27+
using namespace boost::ut;
28+
using level_t = hal::output_pin::level_t;
29+
using read_t = hal::adc::read_t;
30+
using adc_mux_pin = hal::adc_multiplexer::adc_mux_pin;
31+
// configure virtual testing pins
32+
const int NUM_PINS = 2;
33+
std::array<hal::output_pin*, NUM_PINS> signal_pins = { new mock::output_pin,
34+
new mock::output_pin };
35+
for (auto& s : signal_pins) {
36+
s->level() = level_t{ .state = false };
37+
}
38+
39+
mock::adc* source_adc = new mock::adc();
40+
auto adc_sample_pool =
41+
std::vector<read_t>{ read_t{ .sample = 0 }, read_t{ .sample = 1.5 } };
42+
auto sample_queue = std::queue<read_t>();
43+
for (auto& s : adc_sample_pool) {
44+
sample_queue.push(s);
45+
}
46+
source_adc->set(sample_queue);
47+
48+
auto signal_span = std::span<output_pin*>(signal_pins);
49+
50+
"adc_mux"_test = [signal_span, source_adc]() {
51+
// test the adc mux
52+
adc_multiplexer test_mux = adc_multiplexer(signal_span, source_adc);
53+
54+
adc_mux_pin test_adc_port_zero = test_mux.pin_factory(0).value();
55+
expect(that % 0 == test_adc_port_zero.read().value().sample);
56+
};
57+
58+
"adc_mux_pin_switch"_test = [signal_span, source_adc]() {
59+
adc_multiplexer test_mux = adc_multiplexer(signal_span, source_adc);
60+
61+
// simulate reading from another pin
62+
adc_mux_pin test_adc_port_one = test_mux.pin_factory(1).value();
63+
expect(that % 1.5 == test_adc_port_one.read().value().sample);
64+
// verify pins were switched
65+
expect(that % 1 == signal_span[0]->level().value().state);
66+
expect(that % 0 == signal_span[1]->level().value().state);
67+
};
68+
69+
"adc_mux_error"_test = [signal_span, source_adc]() {
70+
adc_multiplexer test_mux = adc_multiplexer(signal_span, source_adc);
71+
72+
auto real_mux_pins =
73+
std::array<hal::result<adc_mux_pin>, 4>{ test_mux.pin_factory(0),
74+
test_mux.pin_factory(1),
75+
test_mux.pin_factory(2),
76+
test_mux.pin_factory(3) };
77+
auto test_pin = test_mux.pin_factory(4);
78+
79+
for (auto& p : real_mux_pins) {
80+
expect(that % true == p.has_value());
81+
}
82+
83+
expect(that % true == test_pin.has_error());
84+
};
85+
86+
// free virtual pins
87+
for (auto s : signal_pins) {
88+
delete s;
89+
}
90+
delete source_adc;
91+
};
92+
93+
} // namespace hal

0 commit comments

Comments
 (0)