groov: Asynchronous Handling of Special Function Registersmichael.caisse@intel.com
auto my_reg = (volatile uint8_t * const)(0x4000'1234);
auto a = 0xd << 4;
auto b = 0b1 << 3;
auto c = 0b10 << 1;
auto d = 0b0 << 0;
*my_reg = a | b | c | d;
auto my_reg = (volatile uint8_t * const)(0x4000'1234);
constexpr auto c_mask = uint8_t(0b11 << 1);
auto c = 0b10 << 1;
*my_reg =
(*my_reg & ~b_mask)
| (c & b_mask)
;
inline void rmw(volatile uint32_t * const address,
uint8_t mask, uint8_t value) {
*address = (*address & ~mask) | (value & mask);
}
auto my_reg = (volatile uint8_t * const)(0x4000'1234);
constexpr auto a_mask = uint8_t(0xf << 4);
constexpr auto b_mask = uint8_t(0b1 << 3);
constexpr auto c_mask = uint8_t(0b11 << 1);
constexpr auto d_mask = uint8_t(0b1 << 0);
auto c = 0b10 << 1;
rmw(my_reg, c_mask, c);
auto my_reg = (volatile uint8_t * const)(0x4000'1234);
constexpr auto a_mask = uint8_t(0xf << 4);
constexpr auto b_mask = uint8_t(0b1 << 3);
constexpr auto c_mask = uint8_t(0b11 << 1);
constexpr auto d_mask = uint8_t(0b1 << 0);
auto a = 0xd << 4;
auto c = 0b10 << 1;
rmw(my_reg, a_mask | c_mask, a | c);
auto my_reg = (volatile uint8_t * const)(0x4000'1234);
constexpr auto a_mask = uint8_t(0xf << 4);
constexpr auto b_mask = uint8_t(0b11 << 2);
constexpr auto c_mask = uint8_t(0b1 << 1);
constexpr auto d_mask = uint8_t(0b1 << 0);
auto a = 0xd << 4;
auto b = 0b1 << 3;
auto c = 0b10 << 1;
auto d = 0b0 << 0;
rmw(my_reg,
a_mask | b_mask | c_mask | d_mask,
a | b | c | d);
auto my_reg = (volatile uint8_t * const)(0x4000'1234);
constexpr auto a_mask = uint8_t(0xf << 4); // read-only field
constexpr auto b_mask = uint8_t(0b1 << 3);
constexpr auto c_mask = uint8_t(0b11 << 1);
constexpr auto d_mask = uint8_t(0b1 << 0);
auto b = 0b1 << 3;
auto c = 0b10 << 1;
auto d = 0b0 << 0;
rmw(my_reg,
b_mask | c_mask | d_mask,
b | c | d);
auto my_reg = (volatile uint8_t * const)(0x4000'1234);
constexpr auto a_mask = uint8_t(0xf << 4); // read-only field
constexpr auto b_mask = uint8_t(0b1 << 3); // write 1 to clear
constexpr auto c_mask = uint8_t(0b11 << 1);
constexpr auto d_mask = uint8_t(0b1 << 0);
auto c = 0b10 << 1;
auto d = 0b0 << 0;
rmw(my_reg,
c_mask | d_mask,
c | d);
int promotion
using my_reg =
reg< "my_reg"
, uint8_t
, 0x4000'1234
// ...
>;
using my_reg =
reg< "my_reg"
, uint8_t
, 0x4000'1234
// ....
, field<"A", uint8_t, 7, 4>
, field<"B", bool , 3, 3>
, field<"C", uint8_t, 2, 1>
, field<"D", bool , 0, 0>
>;
using grp =
group< "my_group"
, groov::mmio_bus<>
, my_reg
// ... additional registers
>;
native style
auto a = 0xd << 4;
auto b = 0b1 << 3;
auto c = 0b10 << 1;
auto d = 0b0 << 0;
*my_reg = a | b | c | d;
groov style
sync_write(grp(
"my_reg.A"_f = 0xd,
"my_reg.B"_f = true,
"my_reg.C"_f = 0b10,
"my_reg.D"_f = false
));
native style
*my_reg =
(0xd << 4)
| (0b1 << 3)
| (0b10 << 1)
| (0b0 << 0)
;
groov style
sync_write(grp(
"my_reg.A"_f = 0xd,
"my_reg.B"_f = true,
"my_reg.C"_f = 0b10,
"my_reg.D"_f = false
));
native style
constexpr auto c_mask =
uint8_t(0b11 << 1);
auto c = 0b10 << 1;
*my_reg =
(*my_reg & ~c_mask)
| (c & c_mask)
;
groov style
sync_write(grp(
"my_reg.C"_f = 0b10
));
native style
constexpr auto a_mask =
uint8_t(0xf << 4);
constexpr auto c_mask =
uint8_t(0b11 << 1);
auto a = 0xd << 4;
auto c = 0b10 << 1;
rmw(my_reg, a_mask | c_mask, a | c);
groov style
sync_write(grp(
"my_reg.A"_f = 0xd,
"my_reg.C"_f = 0b10
));
native style (extra read)
constexpr auto a_mask =
uint8_t(0xf << 4); // ro
constexpr auto b_mask =
uint8_t(0b1 << 3); // wr 1 clr
constexpr auto c_mask =
uint8_t(0b11 << 1);
constexpr auto d_mask =
uint8_t(0b1 << 0);
auto c = 0b10 << 1;
auto d = 0b0 << 0;
rmw(my_reg,
c_mask | d_mask,
c | d);
groov style
using my_reg =
reg< "my_reg"
, uint8_t
, 0x4000'1234
// ....
, field<"A", uint8_t, 7, 4, ro>
, field<"B", bool , 3, 3, wr_1_clr>
, field<"C", uint8_t, 2, 1>
, field<"D", bool , 0, 0>
>;
sync_write(grp(
"my_reg.C"_f = 0b10,
"my_reg.D"_f = false
));
native style
constexpr auto a_mask =
uint8_t(0xf << 4); // ro
auto value =
(*my_reg & a_mask) >> 4;
groov style
auto value =
sync_read(grp / "my_reg.A"_f);
native style
constexpr auto a_mask =
uint8_t(0xf << 4); // ro
constexpr auto c_mask =
uint8_t(0b11 << 1);
auto r = *my_reg;
auto a = (r & a_mask) >> 4;
auto c = (r & c_mask) >> 1;
groov style
auto value =
sync_read(grp(
"my_reg.A"_f,
"my_reg.C"_f));
auto a = value["my_reg.A"_f];
auto c = value["my_reg.C"_f];
native style
constexpr auto a_mask =
uint8_t(0xf << 4); // ro
constexpr auto c_mask =
uint8_t(0b11 << 1);
auto r = *my_reg;
auto a = (r & a_mask) >> 4;
auto c = (r & c_mask) >> 1;
groov style
auto value =
sync_read(grp / "my_reg"_r);
auto a = value["my_reg.A"_f];
auto c = value["my_reg.C"_f];
native style
constexpr auto a_mask =
uint8_t(0xf << 4); // ro
constexpr auto c_mask =
uint8_t(0b11 << 1);
auto r = *my_reg;
auto a = (r & a_mask) >> 4;
auto c = (r & c_mask) >> 1;
groov style
auto value =
sync_read(grp / "my_reg"_r);
auto a = value["A"_f];
auto c = value["C"_f];
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
enum class rs_request : bool {
REQUEST = true
};
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
enum class bit_enable : bool {
ENABLE = true,
DISABLE = false,
ON = true,
OFF = false,
zero = false,
one = true
};
template <typename T>
auto is_enabled(T v) -> bool {
return v == T::ENABLED;
}
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
enum class bit_enable_bar : bool {
ENABLE = false,
DISABLE = true,
ON = false,
OFF = true,
zero = false,
one = true
};
template <typename T>
auto is_enabled(T v) -> bool {
return v == T::ENABLED;
}
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
namespace i2c {
enum class rd_wrn : bool {
WRITE_XFER = false,
READ_XFER = true,
zero = false,
one = true
};
}
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
template <stdx::ct_string Name, std::uint32_t BaseAddress>
using i2cx_t =
groov::group<
Name, groov::mmio_bus<>,
i2c_cr1<BaseAddress>,
i2c_cr2<BaseAddress>,
i2c_oar1<BaseAddress>,
i2c_oar2<BaseAddress>,
i2c_timingr<BaseAddress>,
i2c_timeoutr<BaseAddress>,
i2c_isr<BaseAddress>,
i2c_icr<BaseAddress>,
i2c_pecr<BaseAddress>,
i2c_rxdr<BaseAddress>,
i2c_txdr<BaseAddress>
>;
template <stdx::ct_string Name, std::uint32_t BaseAddress>
using i2cx_t =
groov::group<
Name, groov::mmio_bus<>,
i2c_cr1<BaseAddress>,
i2c_cr2<BaseAddress>,
/* ... more regs ...*/
i2c_txdr<BaseAddress>
>;
constexpr std::uint32_t I2C1_BASE = 0x4000'5400;
constexpr std::uint32_t I2C3_BASE = 0x4000'5c00;
using i2c1_t = i2cx_t<"i2c1", I2C1_BASE>;
constexpr auto i2c1 = i2c1_t{};
using i2c3_t = i2cx_t<"i2c2", I2C3_BASE>;
constexpr auto i2c3 = i2c3_t{};
void i2c1_reset() {
// per docs, reset is
// - write PE = 0
// - check PE = 0
// - write PE = 1
// This sequence ensures the correct APB clock cycles
groov::write(stm32::i2c1("cr1.PE"_f = groov::disable))
| async::seq(groov::read(stm32::i2c1 / "cr1.PE"_f))
| async::seq(groov::write(stm32::i2c1("cr1.PE"_f = groov::enable)))
| async::seq(groov::read(stm32::i2c1 / "cr1.PE"_f))
| async::sync_wait()
;
}
groov::sync_write(
stm32::gpiob(
"moder.6"_f = gpio::mode::alternate,
"otyper.6"_f = gpio::outtype::open_drain,
"ospeedr.6"_f = gpio::speed::high_speed,
"pupdr.6"_f = gpio::pupd::pull_up,
"afrl.6"_f = gpio::afsel::AF4,
"moder.7"_f = gpio::mode::alternate,
"otyper.7"_f = gpio::outtype::open_drain,
"ospeedr.7"_f = gpio::speed::high_speed,
"pupdr.7"_f = gpio::pupd::pull_up,
"afrl.7"_f = gpio::afsel::AF4
));
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
field<"AUTOEND" , bit_enable , 25, 25>,
field<"RELOAD" , bit_enable , 24, 24>,
field<"NBYTES" , uint8_t , 23, 16>,
field<"NACK" , rs_request , 15, 15, access::rs>,
field<"STOP" , rs_request , 14, 14, access::rs>,
field<"START" , rs_request , 13, 13, access::rs>,
field<"HEAD10R" , bit_enable_bar, 12, 12>,
field<"ADD10" , bit_enable , 11, 11>,
field<"RD_WRN" , i2c::rd_wrn , 10, 10>,
field<"SADD" , uint32_t , 9, 0>,
field<"SADD7" , uint8_t , 7, 1>
>;
namespace access {
using rw = groov::w::replace;
using ro = groov::read_only<groov::w::ignore>;
}
template <uint32_t BaseAddress>
using i2c_cr2 =
reg<
"cr2", uint32_t,
BaseAddress+0x04, access::rw,
field<"reserved", uint8_t , 31, 27, access::ro>,
field<"PECBYTE" , rs_request , 26, 26, access::rs>,
// ...
>;
auto w_spec = grp / ("reg"_r = 42);
w_spec["reg.field"_f] = 1;
using my_reg =
reg< "my_reg"
, uint8_t
, 0x4000'1234
// ....
, field<"A", uint8_t, 7, 4>
, field<"B", bool , 3, 3>
, field<"C", uint8_t, 2, 1>
, field<"D", bool , 0, 0>
>;
using remote_reg =
reg< "remote_reg"
, uint8_t
, i2c::address<
port,
addr
>{}
// ....
>;
auto boot_address = [](){
/* ... */
};
using my_reg =
reg< "my_reg"
, uint8_t
, boot_address
// ....
, field<"A", uint8_t, 7, 4>
, field<"B", bool , 3, 3>
, field<"C", uint8_t, 2, 1>
, field<"D", bool , 0, 0>
>;
struct bus {
template <stdx::ct_string RegName, auto Mask, auto IdMask, auto IdValue>
static auto write(auto addr, auto data) -> async::sender auto;
template <stdx::ct_string RegName, auto Mask>
static auto read(auto addr) -> async::sender auto;
}
struct bus {
template <stdx::ct_string RegName, auto Mask, auto IdMask, auto IdValue>
static auto write(auto addr, auto data) -> async::sender auto {
return async::just_result_of([=](){
// if Mask | IdMask covers all bits.... no rmw
auto a = (volatile decltype(Mask)*)(addr);
auto prev = *a & ~(Mask | IdMask);
*a = data | prev | IdValue;
});
}
template <stdx::ct_string RegName, auto Mask>
static auto read(auto addr) -> async::sender auto;
}
struct bus {
template <stdx::ct_string RegName, auto Mask, auto IdMask, auto IdValue>
static auto write(auto addr, auto data) -> async::sender auto;
template <stdx::ct_string RegName, auto Mask>
static auto read(auto addr) -> async::sender auto
return async::just_result_of([=](){
auto a = (volatile decltype(Mask)*)(addr);
return *a;
});
}
}
auto set_brightness(uint8_t bright) -> async::sender auto {
// ...
}
auto set_brightness(uint8_t bright) -> async::sender auto {
auto setup_controller =
groov::write(
stm32::i2c1(
"cr2.ADD10"_f = groov::disable,
"cr2.SADD7"_f = 0x6f,
"cr2.RD_WRN"_f = i2c::rd_wrn::WRITE_XFER,
"cr2.NBYTES"_f = 2,
"cr2.RELOAD"_f = groov::disable,
"cr2.AUTOEND"_f = groov::enable,
"cr2.START"_f = true
))
| seq(spin_for_txis)
;
/* ... more things ... */
}
auto spin_for_txis =
groov::read(stm32::i2c1 / "isr.TXIS"_f)
| async::repeat_until([](auto v) { return v == true; })
;
auto set_brightness(uint8_t bright) -> async::sender auto {
auto setup_controller =
groov::write(
stm32::i2c1(
// ... fields ...
))
| seq(spin_for_txis)
;
/* ... more things ... */
}
template <stdx::ct_string BitName>
auto spin_for_flag() {
using stdx::literals::operator""_cts;
constexpr auto field = groov::path<"isr"_cts, BitName>{};
return
groov::read(stm32::i2c1 / field)
| async::repeat_until([](auto v) { return v == true; })
;
}
auto set_brightness(std::uint8_t bright) -> async::sender auto {
auto setup_controller =
groov::write(
stm32::i2c1(
// ... fields ...
))
| seq(spin_for_flag<"TXIS">())
;
/* ... more things ... */
}
auto set_brightness(std::uint8_t bright) -> async::sender auto {
auto setup_controller =
// setup_controller things
;
auto write_address =
groov::write(
stm32::i2c1(
"txdr.TXDATA"_f = 0x19 // LED Brightness address
))
| seq(spin_for_flag<"TXIS">())
;
/* ... more things ... */
}
auto set_brightness(std::uint8_t bright) -> async::sender auto {
auto setup_controller =
// setup_controller things
;
auto write_address =
// write_address things
;
auto write_data =
groov::write(
stm32::i2c1(
"txdr.TXDATA"_f = bright
))
| seq(spin_for_flag<"STOPF">())
| seq(groov::write(
stm32::i2c1(
"icr.STOPCF"_f = groov::set
)))
;
/* ... more things ... */
}
auto set_brightness(std::uint8_t bright) -> async::sender auto {
auto setup_controller =
// setup_controller things
;
auto write_address =
// write_address things
;
auto write_data =
// write_data things
;
return
setup_controller
| seq(write_address)
| seq(write_data)
;
}
std::uint8_t bright = 0x00;
while(true) {
button::set_brightness(++bright)
| async::sync_wait();
}
std::uint8_t bright = 0x00;
while(true) {
// spin little heater, spin
button::set_brightness(++bright)
| async::sync_wait();
}
auto set_brightness(uint8_t bright) -> async::sender auto {
auto setup_controller =
// ... setup controller things
| seq(spin_for_flag<"TXIS">())
;
auto write_address =
// ... setup address things
| seq(spin_for_flag<"TXIS">())
;
auto write_data =
// ... setup data things
| seq(spin_for_flag<"STOPF">())
// ... send stop
;
return /* ... */ ;
}
auto set_brightness(uint8_t bright) -> async::sender auto {
auto setup_controller =
// ... setup controller things
| seq(wait_for_flag<"TXIS">())
;
auto write_address =
// ... setup address things
| seq(wait_for_flag<"TXIS">())
;
auto write_data =
// ... setup data things
| seq(wait_for_flag<"STOPF">())
// ... send stop
;
return /* ... */ ;
}
template <stdx::ct_string BitName>
auto spin_for_flag() {
using stdx::literals::operator""_cts;
constexpr auto field = groov::path<"isr"_cts, BitName>{};
return
groov::read(stm32::i2c1 / field)
| async::repeat_until([](auto v) { return v == true; })
;
}
template <stdx::ct_string BitName>
auto wait_for_flag() {
using stdx::literals::operator""_cts;
constexpr auto field = groov::path<"isr"_cts, BitName>{};
return
async::trigger_scheduler<"i2c1_ev">{}.schedule()
| seq(groov::read(stm32::i2c1 / field))
| async::repeat_until([](auto v) { return v == true; })
;
}
template <stdx::ct_string BitName>
auto wait_for_flag() {
using stdx::literals::operator""_cts;
constexpr auto field = groov::path<"isr"_cts, BitName>{};
return
async::trigger_scheduler<"i2c1_ev">{}.schedule()
| seq(groov::read(stm32::i2c1 / field))
| async::repeat_until([](auto v) { return v == true; })
;
}
// ......
extern "C" {
void I2C1_EV_Handler(void) {
async::run_triggers<"i2c1_ev">();
}
}
template <stdx::ct_string BitName>
auto wait_for_flag() {
using stdx::literals::operator""_cts;
constexpr auto field = groov::path<"isr"_cts, BitName>{};
using write_spec_t = decltype(groov::sync_read(stm32::i2c1 / "isr"_r));
return
async::trigger_scheduler<"i2c1_ev", write_spec_t>{}.schedule()
| async::repeat_until([field](auto r) { return r[field] == true; })
;
}
// ......
extern "C" {
void I2C1_EV_Handler(void) {
auto v = groov::sync_read(stm32::i2c1 / "isr"_r);
async::run_triggers<"i2c1_ev">(v);
}
}
auto set_brightness(std::uint8_t bright) -> async::sender auto {
auto setup_controller =
// setup_controller things
;
auto write_address =
groov::write(
stm32::i2c1(
"txdr.TXDATA"_f = 0x19 // LED Brightness address
))
| seq(wait_for_flag<"TXIS">())
;
/* ... more things ... */
}
auto set_brightness(std::uint8_t bright) -> async::sender auto {
auto setup_controller =
// setup_controller things
;
auto write_address =
async::when_all(
wait_for_flag<"TXIS">(),
groov::write(
stm32::i2c1(
"txdr.TXDATA"_f = 0x19 // LED Brightness address
))
)
;
/* ... more things ... */
}
auto write_address =
async::when_all(
wait_for_flag<"TXIS">(),
groov::write(
stm32::i2c1(
"txdr.TXDATA"_f = 0x19 // LED Brightness address
))
);
auto tx_done_or_error =
async::when_any(
wait_for_flag<"TXIS">(),
wait_for_error()
);
auto write_address =
async::when_all(
tx_done_or_error,
groov::write(
stm32::i2c1(
"txdr.TXDATA"_f = 0x19 // LED Brightness address
))
);
auto wait_for_error() {
using write_spec_t = decltype(groov::sync_read(stm32::i2c1 / "isr"_r));
return
async::trigger_scheduler<"i2c1_err", write_spec_t>{}.schedule()
| asnyc::then_error([](auto r){ return r; });
}
auto tx_done_or_error =
async::when_any(
wait_for_flag<"TXIS">(),
wait_for_error()
);
auto write_address =
async::when_all(
tx_done_or_error,
groov::write(
stm32::i2c1(
"txdr.TXDATA"_f = 0x19 // LED Brightness address
))
);
auto write_byte = [](std::uint8_t b) {
return
async::when_all(
xfer_done_or_error,
groov::write(stm32::i2c1("txdr.TXDATA"_f = b))
);
};
auto write_last_byte = [](std::uint8_t b) {
return
async::when_all(
xfer_stop_or_error,
groov::write(stm32::i2c1("txdr.TXDATA"_f = b))
)
| async::seq(
groov::write(stm32::i2c1("icr.STOPCF"_f = groov::set))
);
};
template <std::uint8_t i2c_addr>
struct bus {
static constexpr auto control_spec_ =
stm32::i2c1(
"cr2.ADD10"_f = groov::disable,
"cr2.SADD7"_f = i2c_addr,
"cr2.RD_WRN"_f = stm32::i2c::rd_wrn::WRITE_XFER,
"cr2.NBYTES"_f = 0,
"cr2.RELOAD"_f = groov::disable,
"cr2.AUTOEND"_f = groov::enable,
"cr2.START"_f = true
);
template <stdx::ct_string RegName, auto Mask, auto IdMask, auto IdValue>
static auto write(uint8_t addr, decltype(Mask) data) -> async::sender auto;
};
template <std::uint8_t i2c_addr>
struct bus {
template <stdx::ct_string RegName, auto Mask, auto IdMask, auto IdValue>
static auto write(uint8_t addr, decltype(Mask) data) -> async::sender auto {
data |= IdValue;
constexpr std::uint8_t bytes_to_write = sizeof(data) + 1;
auto control_spec = control_spec_;
control_spec["cr2.NBYTES"_f] = bytes_to_write;
auto setup_controller =
async::when_all(
xfer_done_or_error,
groov::write(control_spec)
);
auto values = stdx::bit_unpack<std::uint8_t>(data);
auto write_data =
std::apply([](auto ... v, auto last) {
return
(async::seq(write_byte(v)) | ... | async::seq(write_last_byte(last)));
}, values);
return
setup_controller
| async::seq(write_byte(addr))
| write_data;
}
};
using my_button_t =
qwiic_button::reg::qwiic_button_t<
"my_button", button::bus<0x6f>
>;
my_button_t my_button;
std::uint8_t bright = 0x00;
while(true) {
groov::write(
my_button("led_brightness"_r = ++bright)
)
| async::timeout_after(10ms, timeout_error{})
| async::upon_error(/* ... */)
| async::sync_wait();
}
#include <groov/test.hpp>
namespace groov::test {
using test_bus_list =
make_test_bus_list<
default_test_bus<"my_group">
>;
}
/// more includes
TEST_CASE("read/write the thing", "[test]") {
test::reset_store<grp>();
test::set_value<grp>("reg"_r, 0xdeadbeef);
// do stuff
auto v = test::get_value<grp>("reg"_r);
REQUIRE(v);
CHECK(*v == 0xdeadbef1u);
}
#include <groov/test.hpp>
namespace groov::test {
using test_bus_list =
make_test_bus_list<
default_test_bus<"my_group">
>;
}
/// more includes
TEST_CASE("test write of thing", "[test]") {
test::reset_store<grp>();
// do stuff
auto v = test::get_value<grp>("reg"_r);
REQUIRE(v); // x-propogation testing
CHECK(*v == 0xdeadbef1u);
}
#include <groov/test.hpp>
namespace groov::test {
using test_bus_list =
make_test_bus_list<
default_test_bus<"my_group">,
test_bus<"other_group", my_test_bus>
>;
}
/// more includes
#include <groov/test.hpp>
namespace groov::test {
using test_bus_list =
make_test_bus_list<
test_bus<"my_button", button::test_bus>
>;
}
// .....
Questions?