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