diff --git a/CMakeLists.txt b/CMakeLists.txt index d5bef2b..62a20e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,11 @@ libhal_test_and_make_library( SOURCES src/pca9685.cpp + src/tla2528.cpp + src/tla2528_adapters.cpp TEST_SOURCES tests/pca9685.test.cpp + tests/tla2528.test.cpp tests/main.test.cpp ) diff --git a/datasheets/tla2528.pdf b/datasheets/tla2528.pdf new file mode 100644 index 0000000..a38a91e Binary files /dev/null and b/datasheets/tla2528.pdf differ diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index eff48ed..42d5e28 100644 --- a/demos/CMakeLists.txt +++ b/demos/CMakeLists.txt @@ -19,6 +19,9 @@ project(demos LANGUAGES CXX) libhal_build_demos( DEMOS pca9685 + tla2528_analog_input + tla2528_input_pin + tla2528_output_pin INCLUDES . diff --git a/demos/applications/tla2528_analog_input.cpp b/demos/applications/tla2528_analog_input.cpp new file mode 100644 index 0000000..505e544 --- /dev/null +++ b/demos/applications/tla2528_analog_input.cpp @@ -0,0 +1,36 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace hal::literals; +using namespace std::chrono_literals; + +void application(resource_list& p_map) +{ + auto& terminal = *p_map.console.value(); + auto& i2c = *p_map.i2c.value(); + auto& steady_clock = *p_map.clock.value(); + auto adc_mux = hal::expander::tla2528(i2c); + std::array adcs{ + make_adc(adc_mux, 0), make_adc(adc_mux, 1), make_adc(adc_mux, 2), + make_adc(adc_mux, 3), make_adc(adc_mux, 4), make_adc(adc_mux, 5), + make_adc(adc_mux, 6), make_adc(adc_mux, 7) + }; + + while (true) { + hal::print(terminal, "\nvalues:\n"); + for (int i = 0; i < 8; i++) { + hal::print<64>(terminal, "%d:%f\n", i, adcs[i].read()); + } + hal::delay(steady_clock, 500ms); + } +} diff --git a/demos/applications/tla2528_input_pin.cpp b/demos/applications/tla2528_input_pin.cpp new file mode 100644 index 0000000..17c8ad2 --- /dev/null +++ b/demos/applications/tla2528_input_pin.cpp @@ -0,0 +1,37 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace hal::literals; +using namespace std::chrono_literals; + +void application(resource_list& p_map) +{ + auto& terminal = *p_map.console.value(); + auto& i2c = *p_map.i2c.value(); + auto& steady_clock = *p_map.clock.value(); + auto gpi_expander = hal::expander::tla2528(i2c); + std::array gpis{ + make_input_pin(gpi_expander, 0), make_input_pin(gpi_expander, 1), + make_input_pin(gpi_expander, 2), make_input_pin(gpi_expander, 3), + make_input_pin(gpi_expander, 4), make_input_pin(gpi_expander, 5), + make_input_pin(gpi_expander, 6), make_input_pin(gpi_expander, 7) + }; + + while (true) { + hal::print(terminal, "\nvalues:"); + for (int i = 0; i < 8; i++) { + hal::print<4>(terminal, "%x", gpis[i].level()); + } + hal::delay(steady_clock, 500ms); + } +} diff --git a/demos/applications/tla2528_output_pin.cpp b/demos/applications/tla2528_output_pin.cpp new file mode 100644 index 0000000..7ac0415 --- /dev/null +++ b/demos/applications/tla2528_output_pin.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace hal::literals; +using namespace std::chrono_literals; + +void application(resource_list& p_map) +{ + constexpr bool demo_open_drain = false; + + auto& terminal = *p_map.console.value(); + auto& i2c = *p_map.i2c.value(); + auto& steady_clock = *p_map.clock.value(); + auto gpo_expander = hal::expander::tla2528(i2c); + constexpr hal::output_pin::settings output_pin_config = { + .resistor = hal::pin_resistor::none, .open_drain = demo_open_drain + }; + std::array gpos{ + make_output_pin(gpo_expander, 0, output_pin_config), + make_output_pin(gpo_expander, 1, output_pin_config), + make_output_pin(gpo_expander, 2, output_pin_config), + make_output_pin(gpo_expander, 3, output_pin_config), + make_output_pin(gpo_expander, 4, output_pin_config), + make_output_pin(gpo_expander, 5, output_pin_config), + make_output_pin(gpo_expander, 6, output_pin_config), + make_output_pin(gpo_expander, 7, output_pin_config) + }; + + // output counts in binary to go though all out put combinations + hal::byte counter = 0; + hal::print(terminal, "Starting Binary Count\n"); + while (true) { + counter++; + for (int i = 0; i < 8; i++) { + gpos[i].level(hal::bit_extract(hal::bit_mask::from(i), counter)); + } + hal::print<16>(terminal, "count:%x\n", counter); + hal::delay(steady_clock, 200ms); + } +} diff --git a/include/libhal-expander/tla2528.hpp b/include/libhal-expander/tla2528.hpp new file mode 100644 index 0000000..f5186bd --- /dev/null +++ b/include/libhal-expander/tla2528.hpp @@ -0,0 +1,142 @@ +#pragma once +#include +#include +#include + +namespace hal::expander { + +// adapters +class tla2528_adc; +class tla2528_input_pin; +class tla2528_output_pin; + +/** + * @brief tla2528 is a gpio expander & adc mux driver + * + * tla2528 has 8 pins which can be independently operated as an adc, + * digital input, and digital out over i2c. The i2c address is configured by + * resistors connected to the chip There are no options for internal pull up or + * pull down resistors. The output pins have the option of push-pull or + * open-drain. When in adc mode there is an option (UNIMPLEMENTED) to increase + * reading granularity though sampling averaging. + */ +class tla2528 +{ +public: + enum class pin_mode : hal::byte + { + analog_input, + digital_input, + digital_output_open_drain, + digital_output_push_pull + }; + + /** + * @param p_i2c i2c bus of the device + * + * @param p_i2c_address i2c address configured on the tla, by default is set + * to the i2c address of no resistors attached to address config pins. + */ + tla2528(hal::i2c& p_i2c, hal::byte p_i2c_address = default_address); + + /** + * @brief set what service a pin will provide + * + * @param p_mode tla2528::pin_mode enum of desired pin mode + * @param p_channel which pin to configure + * @throws hal::argument_out_of_domain - if p_channel out of range (>7) + * @throws hal::resource_unavailable_try_again - if adapters are made for a + * pin an exception may be thrown to prevent invalid behavior + */ + void set_pin_mode(pin_mode p_mode, hal::byte p_channel); + + /** + * @brief set digital output level pin + * + * @param p_channel pin to set output + * @param p_high the output level of the pin, true is high, false is low. + * @throws hal::argument_out_of_domain - if p_channel out of range (>7) + */ + void set_digital_out(hal::byte p_channel, bool p_high); + /** + * @brief set digital output levels on all pins + * + * @param p_values The byte is used as a bit field of bool values to set the + * pin outputs. i.e the 0th bit in the byte will set the 0 pin. If a bit is + * 1 it is high. If the bit is 0 it is low. + */ + void set_digital_bus_out(hal::byte p_values); + /** + * @brief read digital output state register of an output pin + * + * @param p_channel pin you would like to get the ouput value + * @return true if the pin's output value register is high. If a pin is not + * set to output pin the returned state will be used once it changes to an + * output pin. + * @throws hal::argument_out_of_domain - if p_channel out of range (>7) + */ + bool get_digital_out(hal::byte p_channel); + /** + * @brief read digital output state register of all output pins + * + * @return The byte is used as a bit field of bool values to give the pins' + * register. i.e the 0th bit in the byte will be the 0 pin's stored value. If + * a bit is 1 it is high. If the bit is 0 it is low. If the pin is not set to + * output pin the returned state will be used once it changes to an output + * pin. + */ + hal::byte get_digital_bus_out(); + + /** + * @brief read the digital level of a pins + * + * @return true if the pin's digital read value is high. If a pin is not set + * to digital input or output the returned value may not correlate with the + * true value. + * @throws hal::argument_out_of_domain - if p_channel out of range. (>7) + * + */ + bool get_digital_in(hal::byte p_channel); + /** + * @brief read the digital levels of all pins + * + * @return The byte is used as a bit field of bool values to give the pins' + * digital read values. i.e the 0th bit in the byte will be the 0 pin's stored + * value. If a bit is 1 it is high. If the bit is 0 it is low. If the pin is + * not set to digital input or output the returned value may not correlate + * with the true value. + * + */ + hal::byte get_digital_bus_in(); + + /** + * @brief read the analog input of a pin. + * + * @param p_channel if out of range (>7) an exception will be thrown + * @return adc reading as a float between 0 and 1 inclusive. If the pin is not + * set to analog input the returned value may not correlate with the true + * value. + * @throws hal::argument_out_of_domain - if p_channel out of range. (>7) + */ + float get_analog_in(hal::byte p_channel); + + friend tla2528_adc; + friend tla2528_input_pin; + friend tla2528_output_pin; + +private: + // i2c address for no resistors + static constexpr hal::byte default_address = 0x10; + + void set_analog_channel(hal::byte p_channel); + void throw_if_invalid_channel(hal::byte p_channel); + void throw_if_channel_occupied(hal::byte p_channel); + void reset(); + + hal::i2c& m_i2c_bus; + hal::byte m_i2c_address; + hal::byte m_channel = 0x08; // stores selected channel to reduce i2c requests + hal::byte m_object_created = 0x00; // tracks adapter channel reservations + hal::byte m_gpo_value = 0x00; +}; +} // namespace hal::expander diff --git a/include/libhal-expander/tla2528_adapters.hpp b/include/libhal-expander/tla2528_adapters.hpp new file mode 100644 index 0000000..f378d4f --- /dev/null +++ b/include/libhal-expander/tla2528_adapters.hpp @@ -0,0 +1,102 @@ +#pragma once +#include +#include +#include +#include + +namespace hal::expander { + +class tla2528_output_pin : public hal::output_pin +{ +public: + ~tla2528_output_pin(); + friend tla2528_output_pin make_output_pin( + tla2528& p_tla2528, + hal::byte p_channel, + hal::output_pin::settings const& p_settings); + +private: + tla2528_output_pin(tla2528& p_tla2528, + hal::byte p_channel, + hal::output_pin::settings const& p_settings); + void driver_configure(settings const& p_settings); + void driver_level(bool p_high); + bool driver_level(); + tla2528* m_tla2528 = nullptr; + hal::byte m_channel; +}; +/** + * @brief create a hal::output_pin driver using the tla2528 driver + * + * @param p_tla2528 tla2528 controling the output pin + * @param p_channel pin acting as an output pin + * @param p_settings output pin settings, default is push-pull and no resistor + * @throws hal::argument_out_of_domain - if p_channel out of range (>7) + * @throws hal::resource_unavailable_try_again - if an adapter has already been + * been made for the pin + * @throws hal::operation_not_supported - if the settings could not be achieved. + */ +tla2528_output_pin make_output_pin( + tla2528& p_tla2528, + hal::byte p_channel, + hal::output_pin::settings const& p_settings = { + .resistor = pin_resistor::none }); + +class tla2528_input_pin : public hal::input_pin +{ +public: + ~tla2528_input_pin(); + friend tla2528_input_pin make_input_pin( + tla2528& p_tla2528, + hal::byte p_channel, + hal::input_pin::settings const& p_settings); + +private: + tla2528_input_pin(tla2528& p_tla2528, + hal::byte p_channel, + hal::input_pin::settings const& p_settings); + void driver_configure(settings const& p_settings); + bool driver_level(); + tla2528* m_tla2528 = nullptr; + hal::byte m_channel; +}; +/** + * @brief create a hal::input_pin driver using the tla2528 driver + * + * @param p_tla2528 tla2528 controling the input pin + * @param p_channel pin acting as an input pin + * @param p_settings input pin settings, default is no resistor + * @throws hal::argument_out_of_domain - if p_channel out of range (>7) + * @throws hal::resource_unavailable_try_again - if an adapter has already been + * been made for the pin + * @throws hal::operation_not_supported - if the settings could not be achieved. + */ +tla2528_input_pin make_input_pin(tla2528& p_tla2528, + hal::byte p_channel, + hal::input_pin::settings const& p_settings = { + .resistor = pin_resistor::none }); + +class tla2528_adc : public hal::adc +{ +public: + ~tla2528_adc(); + friend tla2528_adc make_adc(tla2528& p_tla2528, hal::byte p_channel); + +private: + tla2528_adc(tla2528& p_tla2528, hal::byte p_channel); + float driver_read(); + tla2528* m_tla2528 = nullptr; + hal::byte m_channel; +}; +/** + * @brief create a hal::adc driver using the tla2528 driver + * + * @param p_tla2528 tla2528 controling the adc + * @param p_channel pin acting as an input pin + * @throws hal::argument_out_of_domain - if p_channel out of range (>7) + * @throws hal::resource_unavailable_try_again - if an adapter has already been + * been made for the pin + */ +tla2528_adc make_adc(tla2528& p_tla2528, hal::byte p_channel); + +} // namespace hal::expander diff --git a/src/tla2528.cpp b/src/tla2528.cpp new file mode 100644 index 0000000..8865452 --- /dev/null +++ b/src/tla2528.cpp @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include + +namespace { +enum op_codes : hal::byte +{ + single_register_read = 0b0001'0000, + single_register_write = 0b0000'1000, + set_bit = 0b0001'1000, + clear_bit = 0b0010'0000, + // Continuously reads data from a group of registers. Provide the first + // address to read from, if it runs out of valid addresses to read, it + // returns zeros. (See Figure 30 on datasheet) + continuous_register_read = 0b0011'0000, + // Continuously writes data to a group of registers. Provide the first + // address to write to.The data sent will automatically write the data to + // the next register in ascending order. (See Figure 32 on datasheet) + continuous_register_write = 0b0010'1000 +}; + +enum register_addresses : hal::byte +{ + system_status = 0x0, + general_cfg = 0x1, + data_cfg = 0x2, + osr_cfg = 0x3, + opmode_cfg = 0x4, + pin_cfg = 0x5, + gpio_cfg = 0x7, + gpo_drive_cfg = 0x9, + gpo_value = 0xB, + gpi_value = 0xD, + sequence_cfg = 0x10, + channel_sel = 0x11, + auto_seq_ch_sel = 0x12 +}; +} // namespace +namespace hal::expander { + +tla2528::tla2528(hal::i2c& p_i2c, hal::byte p_i2c_address) + : m_i2c_bus(p_i2c) + , m_i2c_address(p_i2c_address) +{ + reset(); +} + +void tla2528::reset() +{ + // TODO(#9): implement reset command +} + +void tla2528::set_analog_channel(hal::byte p_channel) +{ + throw_if_invalid_channel(p_channel); + if (p_channel == m_channel) { + return; + } + std::array cmd_buffer = { op_codes::single_register_write, + register_addresses::channel_sel, + p_channel }; + hal::write(m_i2c_bus, m_i2c_address, cmd_buffer); +} + +void tla2528::set_pin_mode(pin_mode p_mode, hal::byte p_channel) +{ + throw_if_invalid_channel(p_channel); + std::array data_buffer; + constexpr std::array read_cmd_buffer = { + op_codes::continuous_register_read, register_addresses::pin_cfg + }; + hal::write_then_read(m_i2c_bus, m_i2c_address, read_cmd_buffer, data_buffer); + + hal::byte pin_cfg_reg = data_buffer[0]; + hal::byte gpio_cfg_reg = data_buffer[2]; + hal::byte gpo_drive_cfg_reg = data_buffer[4]; + + hal::bit_mask channel_mask = hal::bit_mask::from(p_channel); + if (p_mode == pin_mode::digital_output_push_pull || + p_mode == pin_mode::digital_output_open_drain) { + bool is_analog = hal::bit_extract(channel_mask, pin_cfg_reg); + bool is_input = hal::bit_extract(channel_mask, gpio_cfg_reg); + if (!(is_analog || is_input)) { + throw_if_channel_occupied(p_channel); + } + hal::bit_modify(pin_cfg_reg).set(channel_mask); + hal::bit_modify(gpio_cfg_reg).set(channel_mask); + if (p_mode == pin_mode::digital_output_push_pull) { + hal::bit_modify(gpo_drive_cfg_reg).set(channel_mask); + } else { + hal::bit_modify(gpo_drive_cfg_reg).clear(channel_mask); + } + } else { + throw_if_channel_occupied(p_channel); + if (p_mode == pin_mode::analog_input) { + hal::bit_modify(pin_cfg_reg).clear(channel_mask); + } else { // must be pin_mode::digitalInput + hal::bit_modify(pin_cfg_reg).set(channel_mask); + hal::bit_modify(gpio_cfg_reg).clear(channel_mask); + } + } + + std::array write_cmd_buffer = { + op_codes::continuous_register_write, + register_addresses::pin_cfg, + pin_cfg_reg, + 0x00, + gpio_cfg_reg, + 0x00, + gpo_drive_cfg_reg + }; + hal::write(m_i2c_bus, m_i2c_address, write_cmd_buffer); +} + +void tla2528::set_digital_bus_out(hal::byte p_values) +{ + // The device will write to a register that caches the desired output state + // weather in output mode or not. When a pin is in a digital output mode it + // will reference the desired state cache. + m_gpo_value = p_values; + std::array cmd_buffer = { op_codes::single_register_write, + register_addresses::gpo_value, + m_gpo_value }; + hal::write(m_i2c_bus, m_i2c_address, cmd_buffer); +} + +void tla2528::set_digital_out(hal::byte p_channel, bool p_high) +{ + throw_if_invalid_channel(p_channel); + if (p_high) { + hal::bit_modify(m_gpo_value).set(hal::bit_mask::from(p_channel)); + } else { + hal::bit_modify(m_gpo_value).clear(hal::bit_mask::from(p_channel)); + } + set_digital_bus_out(m_gpo_value); +} + +bool tla2528::get_digital_out(hal::byte p_channel) +{ + throw_if_invalid_channel(p_channel); + return hal::bit_extract(hal::bit_mask::from(p_channel), + get_digital_bus_out()); +} +hal::byte tla2528::get_digital_bus_out() +{ + std::array data_buffer; + constexpr std::array cmd_buffer = { + op_codes::single_register_read, + register_addresses::gpo_value, + }; + hal::write_then_read(m_i2c_bus, m_i2c_address, cmd_buffer, data_buffer); + return data_buffer[0]; +} + +hal::byte tla2528::get_digital_bus_in() +{ + std::array data_buffer; + constexpr std::array cmd_buffer = { + op_codes::single_register_read, + register_addresses::gpi_value, + }; + hal::write_then_read(m_i2c_bus, m_i2c_address, cmd_buffer, data_buffer); + return data_buffer[0]; +} + +bool tla2528::get_digital_in(hal::byte p_channel) +{ + throw_if_invalid_channel(p_channel); + return hal::bit_extract(hal::bit_mask::from(p_channel), get_digital_bus_in()); +} + +float tla2528::get_analog_in(hal::byte p_channel) +{ + set_analog_channel(p_channel); + // TODO(#8): look into averaging & channel validation + std::array data_buffer; + std::array cmd_buffer = { op_codes::single_register_read }; + hal::write_then_read(m_i2c_bus, m_i2c_address, cmd_buffer, data_buffer); + + // Take 12 bit number stored in first 12 bits of 2 bytes and converting to 16 + // bit num by shifting 4 bit right (See Figure 25 on datasheet) + uint16_t data = data_buffer[0] << 4 | data_buffer[1] >> 4; + return data / (float)4095.0; +} + +void tla2528::throw_if_invalid_channel(hal::byte p_channel) +{ + if (p_channel > 7) { + throw hal::argument_out_of_domain(this); + } +} + +void tla2528::throw_if_channel_occupied(hal::byte p_channel) +{ + if (hal::bit_extract(hal::bit_mask::from(p_channel), m_object_created)) { + throw hal::resource_unavailable_try_again(this); + } +} + +} // namespace hal::expander diff --git a/src/tla2528_adapters.cpp b/src/tla2528_adapters.cpp new file mode 100644 index 0000000..9f1fbee --- /dev/null +++ b/src/tla2528_adapters.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include + +namespace hal::expander { + +tla2528_output_pin make_output_pin(tla2528& p_tla2528, + hal::byte const p_channel, + hal::output_pin::settings const& p_settings) +{ + return { p_tla2528, p_channel, p_settings }; +} +tla2528_output_pin::tla2528_output_pin( + tla2528& p_tla2528, + hal::byte p_channel, + hal::output_pin::settings const& p_settings) + : m_tla2528(&p_tla2528) + , m_channel(p_channel) +{ + m_tla2528->throw_if_channel_occupied(m_channel); + tla2528_output_pin::driver_configure(p_settings); + hal::bit_modify(m_tla2528->m_object_created) + .set(hal::bit_mask::from(m_channel)); +} +tla2528_output_pin::~tla2528_output_pin() +{ + hal::bit_modify(m_tla2528->m_object_created) + .clear(hal::bit_mask::from(m_channel)); +} +void tla2528_output_pin::driver_configure( + hal::output_pin::settings const& p_settings) +{ + if (p_settings.resistor != hal::pin_resistor::none) { + throw hal::operation_not_supported(this); + } + if (p_settings.open_drain) { + m_tla2528->set_pin_mode(tla2528::pin_mode::digital_output_open_drain, + m_channel); + } else { + m_tla2528->set_pin_mode(tla2528::pin_mode::digital_output_push_pull, + m_channel); + } +} +void tla2528_output_pin::driver_level(bool p_high) +{ + m_tla2528->set_digital_out(m_channel, p_high); +} +bool tla2528_output_pin::driver_level() +{ + return m_tla2528->get_digital_in(m_channel); +} + +tla2528_input_pin make_input_pin(tla2528& p_tla2528, + hal::byte p_channel, + hal::input_pin::settings const& p_settings) +{ + return { p_tla2528, p_channel, p_settings }; +} +tla2528_input_pin::tla2528_input_pin(tla2528& p_tla2528, + hal::byte p_channel, + hal::input_pin::settings const& p_settings) + : m_tla2528(&p_tla2528) + , m_channel(p_channel) +{ + m_tla2528->throw_if_channel_occupied(m_channel); + m_tla2528->set_pin_mode(tla2528::pin_mode::digital_input, m_channel); + tla2528_input_pin::driver_configure(p_settings); + hal::bit_modify(m_tla2528->m_object_created) + .set(hal::bit_mask::from(m_channel)); +} +tla2528_input_pin::~tla2528_input_pin() +{ + hal::bit_modify(m_tla2528->m_object_created) + .clear(hal::bit_mask::from(m_channel)); +} +bool tla2528_input_pin::driver_level() +{ + return m_tla2528->get_digital_in(m_channel); +} +void tla2528_input_pin::driver_configure( + hal::input_pin::settings const& p_settings) +{ + if (p_settings.resistor != hal::pin_resistor::none) { + throw hal::operation_not_supported(this); + } +} + +tla2528_adc make_adc(tla2528& p_tla2528, hal::byte p_channel) +{ + return { p_tla2528, p_channel }; +} +tla2528_adc::~tla2528_adc() +{ + hal::bit_modify(m_tla2528->m_object_created) + .clear(hal::bit_mask::from(m_channel)); +} +tla2528_adc::tla2528_adc(tla2528& p_tla2528, hal::byte p_channel) + : m_tla2528(&p_tla2528) + , m_channel(p_channel) +{ + m_tla2528->throw_if_channel_occupied(m_channel); + m_tla2528->set_pin_mode(tla2528::pin_mode::analog_input, m_channel); + hal::bit_modify(m_tla2528->m_object_created) + .set(hal::bit_mask::from(m_channel)); +} +float tla2528_adc::driver_read() +{ + return m_tla2528->get_analog_in(m_channel); +} +} // namespace hal::expander diff --git a/tests/tla2528.test.cpp b/tests/tla2528.test.cpp new file mode 100644 index 0000000..7dd141a --- /dev/null +++ b/tests/tla2528.test.cpp @@ -0,0 +1,31 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include + +namespace hal::expander { +boost::ut::suite test_tla2528 = []() { + using namespace boost::ut; + using namespace std::literals; + + "tla2528::tla2528()"_test = []() { + // Setup + // Exercise + // Verify + }; +}; +} // namespace hal::expander