LCOV - code coverage report
Current view: top level - extras/test/unit/include - TestHal.hpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 69 91 75.8 %
Date: 2025-02-19 18:47:57 Functions: 15 24 62.5 %

          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

Generated by: LCOV version 1.14