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