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 12049 : 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 12049 : BOOST_ASSERT_MSG(pin < TEST_HAL_NUM_GPIO_PINS, "Pin number out of range");
79 :
80 : // check it is output
81 12049 : BOOST_ASSERT_MSG(this->gpio[pin].mode == TEST_HAL_OUTPUT, "GPIO is not output!");
82 :
83 : // check known values
84 12049 : BOOST_ASSERT_MSG(((value == TEST_HAL_LOW) || (value == TEST_HAL_HIGH)), "Invalid output value");
85 :
86 : // set value
87 12049 : this->gpio[pin].value = value;
88 12049 : this->gpio[pin].event = true;
89 12049 : if(radio) {
90 12049 : this->radio->HandleGPIO();
91 : }
92 12049 : this->gpio[pin].event = false;
93 12049 : }
94 :
95 9792 : uint32_t digitalRead(uint32_t pin) override {
96 : HAL_LOG("TestHal::digitalRead(pin=" << pin << ")");
97 :
98 : // check the range
99 9792 : BOOST_ASSERT_MSG(pin < TEST_HAL_NUM_GPIO_PINS, "Pin number out of range");
100 :
101 : // check it is input
102 9792 : BOOST_ASSERT_MSG(this->gpio[pin].mode == TEST_HAL_INPUT, "GPIO is not input");
103 :
104 : // read the value
105 9792 : uint32_t value = this->gpio[pin].value;
106 : HAL_LOG("TestHal::digitalRead(pin=" << pin << ")=" << value << " [" << ((value == TEST_HAL_LOW) ? "LOW" : "HIGH") << "]");
107 9792 : 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 30626 : void delayMicroseconds(unsigned long us) override {
140 : HAL_LOG("TestHal::delayMicroseconds(us=" << us << ")");
141 30626 : const auto start = std::chrono::high_resolution_clock::now();
142 :
143 : // busy wait is needed for microseconds precision
144 30626 : const auto len = std::chrono::microseconds(us);
145 40537078 : while(std::chrono::high_resolution_clock::now() - start < len);
146 :
147 : // measure and print
148 30626 : const auto end = std::chrono::high_resolution_clock::now();
149 30626 : const std::chrono::duration<double, std::micro> elapsed = end - start;
150 : HAL_LOG("TestHal::delayMicroseconds(us=" << us << ")=" << elapsed.count() << "us");
151 30626 : }
152 :
153 0 : void yield() override {
154 : HAL_LOG("TestHal::yield()");
155 0 : }
156 :
157 9792 : unsigned long millis() override {
158 : HAL_LOG("TestHal::millis()");
159 9792 : std::chrono::time_point now = std::chrono::high_resolution_clock::now();
160 9792 : auto res = std::chrono::duration_cast<std::chrono::milliseconds>(now - this->start);
161 : HAL_LOG("TestHal::millis()=" << res.count());
162 9792 : return(res.count());
163 : }
164 :
165 3334 : unsigned long micros() override {
166 : HAL_LOG("TestHal::micros()");
167 3334 : std::chrono::time_point now = std::chrono::high_resolution_clock::now();
168 3334 : auto res = std::chrono::duration_cast<std::chrono::microseconds>(now - this->start);
169 : HAL_LOG("TestHal::micros()=" << res.count());
170 3334 : return(res.count());
171 : }
172 :
173 0 : long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
174 : HAL_LOG("TestHal::pulseIn(pin=" << pin << ", state=" << state << ", timeout=" << timeout << ")");
175 :
176 : // TODO implement
177 : (void)pin;
178 : (void)state;
179 : (void)timeout;
180 0 : return(0);
181 : }
182 :
183 0 : void spiBegin() {
184 : HAL_LOG("TestHal::spiBegin()");
185 0 : }
186 :
187 6022 : void spiBeginTransaction() {
188 : HAL_LOG("TestHal::spiBeginTransaction()");
189 6022 : }
190 :
191 6022 : void spiTransfer(uint8_t* out, size_t len, uint8_t* in) {
192 : HAL_LOG("TestHal::spiTransfer(len=" << len << ")");
193 :
194 31746 : for(size_t i = 0; i < len; i++) {
195 : // append to log
196 25724 : if(this->spiLogEnabled) {
197 4192 : (*this->spiLogPtr++) = out[i];
198 : }
199 :
200 : // process the SPI byte
201 25724 : in[i] = this->radio->HandleSPI(out[i]);
202 :
203 : // artificial delay to emulate SPI running at a finite speed
204 : // this is added because timeouts are based on time duration,
205 : // so we need to make sure some time actually elapses
206 25724 : this->delayMicroseconds(100);
207 :
208 : // output debug
209 : HAL_LOG(fmt::format("out={:#02x}, in={:#02x}", out[i], in[i]));
210 : }
211 6022 : }
212 :
213 6022 : void spiEndTransaction() {
214 : HAL_LOG("TestHal::spiEndTransaction()");
215 6022 : }
216 :
217 0 : void spiEnd() {
218 : HAL_LOG("TestHal::spiEnd()");
219 0 : }
220 :
221 0 : void tone(uint32_t pin, unsigned int frequency, unsigned long duration = 0) {
222 : HAL_LOG("TestHal::tone(pin=" << pin << ", frequency=" << frequency << ", duration=" << duration << ")");
223 :
224 : // TODO implement
225 : (void)pin;
226 : (void)frequency;
227 : (void)duration;
228 0 : }
229 :
230 0 : void noTone(uint32_t pin) {
231 : HAL_LOG("TestHal::noTone(pin=" << pin << ")");
232 :
233 : // TODO implement
234 : (void)pin;
235 0 : }
236 :
237 : // method to compare buffer to the internal SPI log, for verifying SPI transactions
238 12 : int spiLogMemcmp(const void* in, size_t n) {
239 12 : int ret = memcmp(this->spiLog, in, n);
240 12 : this->spiLogWipe();
241 12 : return(ret);
242 : }
243 :
244 17 : void spiLogWipe() {
245 17 : memset(this->spiLog, 0x00, TEST_HAL_SPI_LOG_LENGTH);
246 17 : this->spiLogPtr = this->spiLog;
247 17 : }
248 :
249 : // method that "connects" the emualted radio hardware to this HAL
250 5 : void connectRadio(EmulatedRadio* r) {
251 5 : this->radio = r;
252 5 : this->radio->connect(&this->gpio[EMULATED_RADIO_NSS_PIN],
253 : &this->gpio[EMULATED_RADIO_IRQ_PIN],
254 : &this->gpio[EMULATED_RADIO_RST_PIN],
255 : &this->gpio[EMULATED_RADIO_GPIO_PIN]);
256 5 : }
257 :
258 : private:
259 : // array of emulated GPIO pins
260 : EmulatedPin_t gpio[TEST_HAL_NUM_GPIO_PINS];
261 :
262 : // start time point
263 : std::chrono::time_point<std::chrono::high_resolution_clock> start;
264 :
265 : // emulated radio hardware
266 : EmulatedRadio* radio;
267 :
268 : // SPI history log
269 : uint8_t spiLog[TEST_HAL_SPI_LOG_LENGTH];
270 : uint8_t* spiLogPtr;
271 : };
272 :
273 : #endif
|