Line data Source code
1 : #ifndef TEST_HAL_HPP
2 : #define TEST_HAL_HPP
3 :
4 : #include <chrono>
5 : #include <thread>
6 : #include <fmt/format.h>
7 :
8 : #include <RadioLib.h>
9 :
10 : #include <boost/log/trivial.hpp>
11 : #include <boost/format.hpp>
12 :
13 : #if defined(TEST_HAL_LOG)
14 : #define HAL_LOG(...) BOOST_TEST_MESSAGE(__VA_ARGS__)
15 : #else
16 : #define HAL_LOG(...) {}
17 : #endif
18 :
19 : #include "HardwareEmulation.hpp"
20 :
21 : #define TEST_HAL_INPUT (0)
22 : #define TEST_HAL_OUTPUT (1)
23 : #define TEST_HAL_LOW (0)
24 : #define TEST_HAL_HIGH (1)
25 : #define TEST_HAL_RISING (0)
26 : #define TEST_HAL_FALLING (1)
27 :
28 : // number of emulated GPIO pins
29 : #define TEST_HAL_NUM_GPIO_PINS (32)
30 :
31 : #define TEST_HAL_SPI_LOG_LENGTH (512)
32 :
33 : class TestHal : public RadioLibHal {
34 : public:
35 : bool spiLogEnabled = true;
36 :
37 5 : TestHal() : RadioLibHal(TEST_HAL_INPUT, TEST_HAL_OUTPUT, TEST_HAL_LOW, TEST_HAL_HIGH, TEST_HAL_RISING, TEST_HAL_FALLING) { }
38 :
39 5 : void init() override {
40 : HAL_LOG("TestHal::init()");
41 :
42 : // save program start timestamp
43 5 : start = std::chrono::high_resolution_clock::now();
44 :
45 : // init emulated GPIO
46 165 : for(int i = 0; i < TEST_HAL_NUM_GPIO_PINS; i++) {
47 160 : this->gpio[i].mode = 0;
48 160 : this->gpio[i].value = 0;
49 160 : this->gpio[i].event = false;
50 160 : this->gpio[i].func = PIN_UNASSIGNED;
51 : }
52 :
53 : // wipe history log
54 5 : this->spiLogWipe();
55 5 : }
56 :
57 5 : void term() override {
58 : HAL_LOG("TestHal::term()");
59 5 : }
60 :
61 5 : void pinMode(uint32_t pin, uint32_t mode) override {
62 : HAL_LOG("TestHal::pinMode(pin=" << pin << ", mode=" << mode << " [" << ((mode == TEST_HAL_INPUT) ? "INPUT" : "OUTPUT") << "])");
63 :
64 : // check the range
65 5 : BOOST_ASSERT_MSG(pin < TEST_HAL_NUM_GPIO_PINS, "Pin number out of range");
66 :
67 : // check known modes
68 5 : BOOST_ASSERT_MSG(((mode == TEST_HAL_INPUT) || (mode == TEST_HAL_OUTPUT)), "Invalid pin mode");
69 :
70 : // set mode
71 5 : this->gpio[pin].mode = mode;
72 5 : }
73 :
74 6811 : void digitalWrite(uint32_t pin, uint32_t value) override {
75 : HAL_LOG("TestHal::digitalWrite(pin=" << pin << ", value=" << value << " [" << ((value == TEST_HAL_LOW) ? "LOW" : "HIGH") << "])");
76 :
77 : // check the range
78 6811 : BOOST_ASSERT_MSG(pin < TEST_HAL_NUM_GPIO_PINS, "Pin number out of range");
79 :
80 : // check it is output
81 6811 : BOOST_ASSERT_MSG(this->gpio[pin].mode == TEST_HAL_OUTPUT, "GPIO is not output!");
82 :
83 : // check known values
84 6811 : BOOST_ASSERT_MSG(((value == TEST_HAL_LOW) || (value == TEST_HAL_HIGH)), "Invalid output value");
85 :
86 : // set value
87 6811 : this->gpio[pin].value = value;
88 6811 : this->gpio[pin].event = true;
89 6811 : if(radio) {
90 6811 : this->radio->HandleGPIO();
91 : }
92 6811 : this->gpio[pin].event = false;
93 6811 : }
94 :
95 6414 : uint32_t digitalRead(uint32_t pin) override {
96 : HAL_LOG("TestHal::digitalRead(pin=" << pin << ")");
97 :
98 : // check the range
99 6414 : BOOST_ASSERT_MSG(pin < TEST_HAL_NUM_GPIO_PINS, "Pin number out of range");
100 :
101 : // check it is input
102 6414 : BOOST_ASSERT_MSG(this->gpio[pin].mode == TEST_HAL_INPUT, "GPIO is not input");
103 :
104 : // read the value
105 6414 : uint32_t value = this->gpio[pin].value;
106 : HAL_LOG("TestHal::digitalRead(pin=" << pin << ")=" << value << " [" << ((value == TEST_HAL_LOW) ? "LOW" : "HIGH") << "]");
107 6414 : return(value);
108 : }
109 :
110 0 : void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
111 : HAL_LOG("TestHal::attachInterrupt(interruptNum=" << interruptNum << ", interruptCb=" << interruptCb << ", mode=" << mode << ")");
112 :
113 : // TODO implement
114 : (void)interruptNum;
115 : (void)interruptCb;
116 : (void)mode;
117 0 : }
118 :
119 0 : void detachInterrupt(uint32_t interruptNum) override {
120 : HAL_LOG("TestHal::detachInterrupt(interruptNum=" << interruptNum << ")");
121 :
122 : // TODO implement
123 : (void)interruptNum;
124 0 : }
125 :
126 25 : void delay(unsigned long ms) override {
127 : HAL_LOG("TestHal::delay(ms=" << ms << ")");
128 25 : const auto start = std::chrono::high_resolution_clock::now();
129 :
130 : // sleep_for is sufficient for ms-precision sleep
131 25 : std::this_thread::sleep_for(std::chrono::duration<unsigned long, std::milli>(ms));
132 :
133 : // measure and print
134 25 : const auto end = std::chrono::high_resolution_clock::now();
135 25 : const std::chrono::duration<double, std::milli> elapsed = end - start;
136 : HAL_LOG("TestHal::delay(ms=" << ms << ")=" << elapsed.count() << "ms");
137 25 : }
138 :
139 18632 : void delayMicroseconds(unsigned long us) override {
140 : HAL_LOG("TestHal::delayMicroseconds(us=" << us << ")");
141 18632 : const auto start = std::chrono::high_resolution_clock::now();
142 :
143 : // this is incredibly hacky, but there is no reliable way for sub-ms sleeping
144 18632 : std::this_thread::sleep_for(std::chrono::duration<unsigned long, std::milli>(1));
145 :
146 : // measure and print
147 18632 : const auto end = std::chrono::high_resolution_clock::now();
148 18632 : const std::chrono::duration<double, std::milli> elapsed = end - start;
149 :
150 : HAL_LOG("TestHal::delayMicroseconds(us=" << us << ")=" << elapsed.count() << "ms");
151 : (void)us;
152 18632 : }
153 :
154 0 : void yield() override {
155 : HAL_LOG("TestHal::yield()");
156 0 : }
157 :
158 6414 : unsigned long millis() override {
159 : HAL_LOG("TestHal::millis()");
160 6414 : std::chrono::time_point now = std::chrono::high_resolution_clock::now();
161 6414 : auto res = std::chrono::duration_cast<std::chrono::milliseconds>(now - this->start);
162 : HAL_LOG("TestHal::millis()=" << res.count());
163 6414 : return(res.count());
164 : }
165 :
166 715 : unsigned long micros() override {
167 : HAL_LOG("TestHal::micros()");
168 715 : std::chrono::time_point now = std::chrono::high_resolution_clock::now();
169 715 : auto res = std::chrono::duration_cast<std::chrono::microseconds>(now - this->start);
170 : HAL_LOG("TestHal::micros()=" << res.count());
171 715 : return(res.count());
172 : }
173 :
174 0 : long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
175 : HAL_LOG("TestHal::pulseIn(pin=" << pin << ", state=" << state << ", timeout=" << timeout << ")");
176 :
177 : // TODO implement
178 : (void)pin;
179 : (void)state;
180 : (void)timeout;
181 0 : return(0);
182 : }
183 :
184 0 : void spiBegin() {
185 : HAL_LOG("TestHal::spiBegin()");
186 0 : }
187 :
188 3403 : void spiBeginTransaction() {
189 : HAL_LOG("TestHal::spiBeginTransaction()");
190 3403 : }
191 :
192 3403 : void spiTransfer(uint8_t* out, size_t len, uint8_t* in) {
193 : HAL_LOG("TestHal::spiTransfer(len=" << len << ")");
194 :
195 18822 : for(size_t i = 0; i < len; i++) {
196 : // append to log
197 15419 : if(this->spiLogEnabled) {
198 457 : (*this->spiLogPtr++) = out[i];
199 : }
200 :
201 : // process the SPI byte
202 15419 : in[i] = this->radio->HandleSPI(out[i]);
203 :
204 : // artificial delay to emulate SPI running at a finite speed
205 : // this is added because timeouts are based on time duration,
206 : // so we need to make sure some time actually elapses
207 15419 : this->delayMicroseconds(100);
208 :
209 : // output debug
210 : HAL_LOG(fmt::format("out={:#02x}, in={:#02x}", out[i], in[i]));
211 : }
212 3403 : }
213 :
214 3403 : void spiEndTransaction() {
215 : HAL_LOG("TestHal::spiEndTransaction()");
216 3403 : }
217 :
218 0 : void spiEnd() {
219 : HAL_LOG("TestHal::spiEnd()");
220 0 : }
221 :
222 0 : void tone(uint32_t pin, unsigned int frequency, unsigned long duration = 0) {
223 : HAL_LOG("TestHal::tone(pin=" << pin << ", frequency=" << frequency << ", duration=" << duration << ")");
224 :
225 : // TODO implement
226 : (void)pin;
227 : (void)frequency;
228 : (void)duration;
229 0 : }
230 :
231 0 : void noTone(uint32_t pin) {
232 : HAL_LOG("TestHal::noTone(pin=" << pin << ")");
233 :
234 : // TODO implement
235 : (void)pin;
236 0 : }
237 :
238 : // method to compare buffer to the internal SPI log, for verifying SPI transactions
239 12 : int spiLogMemcmp(const void* in, size_t n) {
240 12 : int ret = memcmp(this->spiLog, in, n);
241 12 : this->spiLogWipe();
242 12 : return(ret);
243 : }
244 :
245 17 : void spiLogWipe() {
246 17 : memset(this->spiLog, 0x00, TEST_HAL_SPI_LOG_LENGTH);
247 17 : this->spiLogPtr = this->spiLog;
248 17 : }
249 :
250 : // method that "connects" the emualted radio hardware to this HAL
251 5 : void connectRadio(EmulatedRadio* r) {
252 5 : this->radio = r;
253 5 : this->radio->connect(&this->gpio[EMULATED_RADIO_NSS_PIN],
254 : &this->gpio[EMULATED_RADIO_IRQ_PIN],
255 : &this->gpio[EMULATED_RADIO_RST_PIN],
256 : &this->gpio[EMULATED_RADIO_GPIO_PIN]);
257 5 : }
258 :
259 : private:
260 : // array of emulated GPIO pins
261 : EmulatedPin_t gpio[TEST_HAL_NUM_GPIO_PINS];
262 :
263 : // start time point
264 : std::chrono::time_point<std::chrono::high_resolution_clock> start;
265 :
266 : // emulated radio hardware
267 : EmulatedRadio* radio;
268 :
269 : // SPI history log
270 : uint8_t spiLog[TEST_HAL_SPI_LOG_LENGTH];
271 : uint8_t* spiLogPtr;
272 : };
273 :
274 : #endif
|