For suitable output, we recommend to use Google Chrome (15+) or Microsoft Edge (79+).
As of 2025-07-24Class Objects
Objects defined for operating networks and peripherals
Class objects are predefined in the MWX library, including the_twelite
for handling TWENET and objects for using peripherals.
Each object must be initialized by calling its .setup()
and .begin()
methods.
(Only the Serial
object that uses UART0
does not require initialization.)
1 - the_twelite
Core class for TWENET usage (mwx::twenet)
The the_twelite
object consolidates procedures for using TWENET, including basic wireless settings, sleep procedures, and other operations to control the wireless microcontroller.
Overview
the_twelite
performs settings and start the_twelite.begin()
within the setup()
function. Settings cannot be done outside of setup()
.
void setup() {
...
the_twelite
<< TWENET::appid(APP_ID)
<< TWENET::channel(CHANNEL)
<< TWENET::rx_when_idle();
...
the_twelite.begin();
}
In the above example, the application ID setting, communication channel setting, and receiver circuit setting are performed.
Various procedures are included.
// Get the serial number
uint32_t u32hwser = the_twelite.get_hw_serial();
// Set channel to 11
the_twelite.change_channel(11);
// Sleep for 1 second
the_twelite.sleep(1000);
// Perform reset
the_twelite.reset_system();
Also, classes that handle wireless networks, board support, and user-written event-driven processing can be registered. By registering these classes, specialized functions can be easily utilized. These classes are referred to as “Behaviors” in this documentation.
void setup() {
/*** SETUP section */
// use PAL_AMB board support.
auto&& brd = the_twelite.board.use<PAL_AMB>();
...
// Register Network
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
nwk << NWK_SIMPLE::logical_id(u8ID);
In the above example, two types are registered: the environmental sensor PAL <PAL_AMB>
and the simple relay network <NWK_SIMPLE>
. By registering these, hardware such as sensors on the environmental sensor PAL can be easily handled. Also, complicated wireless packet handling such as relay processing and automatic discarding of duplicate packets can be implicitly enabled.
Methods
The MWX library defines methods other than those introduced here.
These include methods unrelated to actor description, those that do not function effectively even if set, and those used internally.
<<
operator (Settings)
The <<
operator is used to perform initial settings of the the_twelite
object.
The following setting class objects are input, and if not set, default values are applied.
TWENET::appid(uint32_t id)
Sets the application ID specified by parameter id
. This is mandatory.
Reading the setting is done by uint32_t the_twelite.get_appid()
.
TWENET::channel(uint8_t ch)
Sets the channel number (11
..26
) specified by parameter ch
.
Reading the setting is done by uint8_t the_twelite.get_channel()
.
TWENET::tx_power(uint8_t pw)
Sets the output power setting (0
..3
) specified by parameter pw
. The default is (3: no output attenuation).
Reading the setting is done by uint8_t the_twelite.get_tx_power()
.
TWENET::rx_when_idle(uint8_t bEnable)
If parameter bEnable
is 1
, the receiver circuit is always active to receive wireless packets from others. The default is 0
, meaning mainly transmission only.
Reading the setting is done by uint8_t the_twelite.get_rx_when_idle()
.
TWENET::chmgr(uint8_t ch1 = 18, uint8_t ch2 = 0, uint8_t ch3 = 0)
Enables the channel manager. If multiple channels are specified, transmission and reception are performed on multiple channels. If 0
is specified for ch2
or ch3
, that specification is disabled.
STG_STD
Applies the Interactive Mode settings.
auto&& set = the_twelite.settings.use<STG_STD>();
...
set.reload(); // Load settings
the_twelite << set; // Apply Interactive Mode settings
The applied items are as follows:
app_id
channel
tx_power
- Retransmission count when using MAC ack
There are other setting items in the MWX library code, but as of now, they are either unrelated to library functions or may cause contradictions if set.
begin()
Execute after completing prior settings (<<
operator reference) and behavior registration. Usually placed at the very end of the setup()
function.
the_twelite
setup completed- Behavior initialization
TWENET initialization is also executed after the setup()
function finishes. Since many processes are designed to run after setup()
ends, avoid performing anything other than initialization here.
Example
void setup() {
// use PAL_AMB board support.
auto&& brd = the_twelite.board.use<PAL_AMB>();
// settings
the_twelite
<< TWENET::appid(APP_ID)
<< TWENET::channel(CHANNEL)
<< TWENET::rx_when_idle();
// Register Network
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
nwk << NWK_SIMPLE::logical_id(u8ID);
// some others
// begin the TWENET!
the_twelite.begin();
}
change_channel()
inline bool change_channel(uint8_t u8Channel)
Changes the channel setting. If it fails, the channel is not changed and returns false
.
get_channel_phys()
uint8_t get_channel_phys()
Gets the currently set channel number (11..26). Obtained from the MAC layer API.
get_hw_serial()
inline uint32_t get_hw_serial()
Gets the module’s serial number.
sleep()
inline void sleep(
uint32_t u32Periodms,
bool_t bPeriodic = true,
bool_t bRamOff = false,
uint8_t u8Device = TWENET::SLEEP_WAKETIMER_PRIMARY)
Puts the module to sleep.
Parameter | Description |
---|
u32Periodms | Sleep duration [ms] |
bPeriodic | Recalculates next wake-up time based on the last wake-up time. ※ Sometimes the next wake-up timing may be from the current time due to proximity. |
bRamoff | If set to true , sleep without retaining RAM (after waking up, reinitialization should start from setup() instead of wakeup() ) |
u8Device | Specifies the wake-up timer used for sleep. Specify either TWENET::SLEEP_WAKETIMER_PRIMARY or TWENET::SLEEP_WAKETIMER_SECONDARY . |
Before sleep, the on_sleep()
method of embedded objects and behaviors is called to perform pre-sleep procedures. After wake-up, the on_wakeup()
method is called for recovery processing.
is_wokeup_by_dio()
bool is_wokeup_by_dio(uint8_t port)
Returns true
if the wake-up cause from sleep is the specified digital pin.
is_wokeup_by_wktimer()
bool is_wokeup_by_wktimer()
Returns true
if the wake-up cause from sleep is the wake-up timer.
reset_system()
inline void reset_system()
Resets the system. After reset, processing starts from setup()
.
stop_watchdog()
inline void stop_watchdog()
Stops the watchdog timer. Stop the timer when performing long polling waits.
The watchdog timer is restarted each time in the library’s main loop. It resets after about 4 seconds if the timer expires.
restart_watchdog()
inline void restart_watchdog()
Restarts the watchdog timer.
Behaviors
twe_twelite
can register three behaviors, and the following class objects are defined to hold them.
network
: Behavior implementing the network. Usually register <NWK_SIMPLE>
.network2
: Behavior implementing the network. Used when you want to process packets rejected by network
(due to payload data structure or other criteria) with another network behavior. (Reference: Using NWK_LAYERED and NWK_SIMPLE together)board
: Behavior for board support. Adds procedures for using devices on the board.app
: Behavior describing user applications. Allows writing interrupt or event descriptions and state transitions using state machines. Also allows defining multiple application descriptions and easily selecting completely different applications at startup.settings
: Behavior for executing settings (Interactive Mode). Register <SET_STD>
.
use<B>()
// Example
auto&& brd = the_twelite.board.use<PAL_AMB>();
Registers behavior <B>
. Registration is done inside setup()
. The return value is a reference to the object corresponding to the registered behavior.
After registration, the object can be retrieved with the same syntax as during registration.
If an incorrect behavior is specified, a panic operation (infinite loop) occurs and the program stops running.
Declaring behavior objects as global variables is not intended. Use use<B>()
each time you use it.
void loop() {
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
}
However, it is possible to define a pointer to the object as a global variable and write as follows. (In MWX library, pointer types are minimized and references are preferred, so such usage is not recommended.)
NWK_SIMPLE *pNwk = nullptr;
setup() {
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
pNwk = &nwk;
}
void transmit() {
if (auto&& pkt = pNwk->prepare_tx_packet()) {
...
}
}
Class Objects
the_twelite
defines the three class objects board
, network
, and app
mentioned above, and also defines the following.
tx_status
Notifies the transmission completion status.
is_complete()
bool is_complete(uint8_t cbid)
Returns true
when the packet with the specified ID has completed transmission.
is_success()
bool is_success(uint8_t cbid)
Returns true
when the packet with the specified ID has completed transmission and the transmission was successful.
receiver
Obtains received packets.
the_twelite.receiver
is not recommended.
Previously, processing using the_twelite.receiver
was intended inside loop()
, but due to delay processing by queue, packet loss can occur in principle, and the description tends to be complicated. Therefore, on_rx_packet()
was added.
- In event-driven behavior descriptions, obtain via
receive()
callback.
The received packet data obtained by the read()
method is designed to be overwritten by subsequent packets during reception processing. If you read immediately after available
and perform short processing, it is not a problem, but in principle, read → copy necessary data for application → quickly finish loop()
. For example, performing long delay()
in loop()
may cause packet loss.
available()
Returns true
if there is a received packet not yet read.
read()
Reads a packet.
2 - Analogue
ADC (mwx::periph_analogue.hpp)
Analogue performs ADC execution and value acquisition. Multiple channels can be continuously acquired at once, and this can be sequentially executed according to the cycle of a timer or other periodic event.
Constants
Pin Definitions
Constant | Type | Standard App Pin Name |
---|
uint8_t PIN_ANALOGUE::A1 = 0 | ADC1 Pin | AI1 |
uint8_t PIN_ANALOGUE::A2 = 1 | ADC2 Pin | AI3 |
uint8_t PIN_ANALOGUE::A3 = 2``uint8_t PIN_ANALOGUE::D0 = 2 | ADC3 Pin (DIO0) *1 | AI2 |
uint8_t PIN_ANALOGUE::A4 = 3``uint8_t PIN_ANALOGUE::D1 = 3 | ADC4 Pin (DIO1) *1 | AI4 |
uint8_t PIN_ANALOGUE::VCC = 4 | Vcc Power Supply Voltage | |
In the standard app (App_Twelite), the pin names ADC2/ADC3 in the semiconductor datasheet correspond to AI3/AI2 in the TWELITE DIP layout. Please be aware of this.
*1 The ADC2/ADC3 pins shared by digital and analog have usage procedures and restrictions.
Before starting ADC, set the pins to be used to no pull-up. If this is not done, the ADC will always observe the pull-up voltage.
pinMode(PIN_DIGITAL::DIO0, PIN_MODE::INPUT);
pinMode(PIN_DIGITAL::DIO1, PIN_MODE::INPUT);
In a normal circuit configuration, current leakage occurs during sleep. This cannot be avoided by software alone.
To avoid current leakage during sleep, disconnect the GND of the analog circuit part with a FET switch or similar, leaving it floating during sleep. Also, before sleep, set the pins to input with pull-up enabled.
Methods
setup()
void setup(
bool bWaitInit = false,
uint8_t kick_ev = E_AHI_DEVICE_TICK_TIMER,
void (*fp_on_finish)() = nullptr)
Initializes the ADC. In setup(), it starts the internal semiconductor regulator, specifies the timer device for periodic execution, and specifies a callback function called when all specified ADC channels have finished.
Parameter | Description |
---|
bWaitInit | If true , waits for the internal semiconductor regulator initialization. |
kick_ev | Specifies the timer device for periodic execution. The following five devices can be specified. Except for the first time, ADC starts in the interrupt handler. E_AHI_DEVICE_TICK_TIMER (TickTimer)``E_AHI_DEVICE_TIMER0 .. 4 (Timer0 .. 4) |
fp_on_finish | Callback function called from the interrupt handler after all specified ports’ ADCs finish. Useful for separately storing ADC measurement values in FIFO queues, etc. |
begin()
void begin(uint8_t bmPorts, uint8_t capt_tick = 1)
The first parameter specifies the ports for ADC. The port specification is a bitmap with bits set corresponding to the port numbers defined in the pin definitions. For example, to get values of PIN_ANALOGUE::A2
and PIN_ANALOGUE::VCC
, specify (1 <<PIN_ANALOGUE::A1 | 1<<PIN_ANALOGUE::VCC )
. You can also write pack_bits(PIN_ANALOGUE::A1,PIN_ANALOGUE::VCC)
using pack_bits
.
After calling begin()
, the first ADC process starts promptly, and after its completion interrupt, the next pin process starts. When all processes finish, the callback function (if specified) is called. The next ADC process starts after waiting for the next timer interrupt.
The second parameter specifies the number of timer interrupts before starting ADC. For example, TickTimer
is called every 1ms, and specifying 16
means processing every 16ms.
Starts ADC processing with default ADC pins (PIN_ANALOGUE::A1
,PIN_ANALOGUE::A2
). end()
resumes interrupted ADC processing.
end()
Stops ADC processing and stops the internal semiconductor regulator.
available()
Returns true
after ADC values are obtained. After checking with this function, it returns false
until the next ADC completion.
read()
, read_raw()
inline int16_t read(uint8_t s)
inline int16_t read_raw(uint8_t s)
Reads ADC values. The parameter specifies the ADC pin to read. read()
returns the value converted to mV, and read_raw()
returns the ADC value (0..1023).
It is recommended to read Vcc with read()
. To convert the read_raw()
value to mV, a special conversion formula must be applied.
If you read the value delayed near the timing when the next ADC process is executed after ADC completion (available), the next ADC value may be returned. ADC processing is done in the interrupt handler, so the value may update even during loop()
processing.
ADC Interrupt Handler
The ADC interrupt handler is set to periph_analogue::ADC_handler()
when setup()
is called.
Specifying a handler separately in the semiconductor peripheral library will cause malfunction.
Behavior During Sleep
If ADC is in periodic execution state by begin()
, ADC processing resumes after wake-up from sleep.
Since setup(true)
is automatically called on wake-up from sleep, when using timers other than E_AHI_DEVICE_TICK_TIMER
, explicitly reinitialize in wakeup()
.
For example, inserting the following code reinitializes with E_AHI_DEVICE_TIMER0
on wake-up.
void wakeup()
{
Analogue.setup(true, E_AHI_DEVICE_TIMER0, adc_handler);
Analogue.begin(pack_bits(PIN_ANALOGUE::A1), 1);
}
3 - Buttons
Digital Input Management Class (mwx::periph_buttons)
Detects changes in digital input. This class detects changes when the same detected value is obtained multiple times. It is effective in reducing the effects of mechanical button chatter.
Methods
setup()
void setup(uint8_t max_history);
The parameter max_history
is the maximum number of reference counts that can be set in begin()
. Memory allocation and initialization are performed here.
begin()
void begin(uint32_t bmPortMask,
uint8_t u8HistoryCount,
uint16_t tick_delta);
Starts the operation of Buttons
. The first parameter bmPortMask
specifies the bitmap of digital inputs to be monitored. bit 0 corresponds to DIO 0, …, bit N corresponds to DIO N. Multiple bits can be specified. The second parameter u8HistoryCount
is the number of times required to confirm a value. The third parameter tick_delta
specifies the interval for checking the value in milliseconds.
The confirmation of the value takes u8HistoryCount * tick_delta
[ms]. For example, if u8HistoryCount
=5 and tick_delta
=4, it takes at least about 20 ms to confirm the state.
The check is performed in the event handler of TickTimer
. Since it is not an interrupt handler, it is affected by processing delays, but it is sufficient for suppressing chatter of mechanical buttons and the like.
end()
Stops the operation of Buttons
.
available()
Returns true
when a change is detected. It is cleared when read()
is executed.
read()
bool read(uint32_t& u32port, uint32_t& u32changed)
Called when available is true. u32port
is the bitmap of the current input DIO, and u32changed
is the bitmap of DIOs where changes were detected.
Returns false
if Buttons is not operating.
Operation Details
Initial Value Confirmation
At the time when Buttons starts operating, the input state of DIO is not confirmed. It becomes available when the value is confirmed. At this time, the MSB (bit 31) of the bitmap read by read()
is set to 1.
Since operation confirmation is required, it cannot be used for monitoring ports where the input value changes constantly.
Sleep
If Buttons is running before sleep, it will resume after waking up. After resuming, the initial confirmation is performed.
4 - EEPROM
Perform read and write operations on the built-in EEPROM
Performs read and write operations on the built-in EEPROM of the TWELITE wireless microcontroller.
The built-in EEPROM has 3480 bytes available from address 0x000 to 0xEFF.
The beginning part is used for Settings (Interactive Mode), so when used together, it is recommended to use the latter half of the addresses. The amount of space consumed by the settings (Interactive Mode) depends on its implementation. Even with minimal settings, the first 256 bytes are used, so it is recommended to use addresses after that.
Methods
read()
uint8_t read(uint16_t address)
Reads data corresponding to address
from the EEPROM.
No error detection is performed.
write()
void write(uint16_t address, uint8_t value)
Writes value
to address
in the EEPROM.
No error detection is performed.
update()
void update(uint16_t address, uint8_t value)
Performs writing similar to write()
, but first reads the data at address
and writes only if it is different from value
. This is used to reduce the number of write cycles considering the EEPROM’s rewrite lifespan.
get_stream_helper()
auto&& get_stream_helper()
// The return type is long, so it is abbreviated as auto&&.
Obtains a helper object to perform read and write operations using the later described mwx::stream
.
Through the stream_helper
helper object, use operators and methods of mwx::stream
. Using mwx::stream
allows reading and writing of integer types such as uint16_t
and uint32_t
, fixed-length arrays of uint8_t
, and formatted output via format()
objects.
auto&& strm = EEPROM.get_stream_helper();
// The helper object's type name is long, so auto&& is used to resolve it.
You can use the <<
operator and other interfaces defined in mwx::stream
with this object.
strm.seek(1024); // Move to byte 1024
strm << format("%08x", 12345678); // Record 12345678 as an 8-character hexadecimal
strm << uint32_t(0x12ab34cd); // Record 4 bytes of 0x12ab34cd
uint8_t msg_hello[16] = "HELLO WORLD!";
strm << msg_hello; // Record byte sequence "HELLO WORLD!" (no terminator)
// Result
// 0400: 30 30 62 63 36 31 34 65 12 ab 34 cd 48 45 4c 4c
// 0 0 b c 6 1 4 e 0x12ab34cd H E L L
// 0410: 4f 20 57 4f 52 4c 44 21 00 00 00 00 ff ff ff ff
// O SP W O R L D !
Using .seek()
, the EEPROM address is moved to 1024.
The above writes an 8-byte string (00bc614e
), a 4-byte integer (0x12ab34cd
), a 16-byte byte array (HELLO WORLD!...
), and a 1-byte terminator.
strm.seek(1024);
uint8_t msg1[8];
strm >> msg1;
Serial << crlf << "MSG1=" << msg1;
// MSG1=00bc614e
uint32_t var1;
strm >> var1;
Serial << crlf << "VAR1=" << format("%08x", var1);
// VAR1=12ab34cd
uint8_t msg2[16]; // Number of characters in "HELLO WORLD!"
strm >> msg2;
Serial << crlf << "MSG2=" << msg2;
// MSG2=HELLO WORLD!
Using .seek()
, the EEPROM address is moved to 1024.
Reads the previously written data sequence. Reads an 8-byte string, a 4-byte integer, and a 16-byte string in order using the >>
operator.
5 - PulseCounter
Pulse Counter (mwx::periph_pulse_counter)
The pulse counter is a circuit that counts pulses even when the microcontroller’s CPU is not operating.
There are two pulse counter systems. PC0 is assigned to PulseCounter0
, and PC1 is assigned to PulseCounter1
. The alias PulseCounter
refers to PulseCounter1
.
Methods
begin()
void begin(uint16_t refct = 0,
E_PIN_INT_MODE edge = PIN_INT_MODE::FALLING,
uint8_t debounce = 0)
Initializes the object and starts counting. The first parameter, refct
, is the reference count used to determine whether an interrupt or availability condition is met. When the count exceeds this number, it is reported to the application. A value of 0 may also be specified, in which case it will not trigger a wake-up from sleep.
The second parameter, edge
, specifies whether the interrupt is on the rising (PIN_INT_MODE::RISING
) or falling (PIN_INT_MODE::FALLING
) edge.
The third parameter, debounce
, accepts values 0, 1, 2, or 3. Settings 1 through 3 reduce the effect of noise by requiring consecutive identical values for detection.
Setting | Consecutive Samples | Max Detection Frequency |
---|
0 | - | 100kHz |
1 | 2 | 3.7kHz |
2 | 4 | 2.2kHz |
3 | 8 | 1.2kHz |
end()
Stops pulse detection.
available()
If the specified count (refct
in begin()
) is 0, returns true
when the count is 1 or more.
If the specified count (refct
in begin()
) is 1 or more, returns true
when the count exceeds the specified threshold.
read()
Reads the count value. The value is reset to 0 after reading.
6 - Serial
UART0 port (mwx::serial_jen)
Implements
mwx::stream
and performs input/output on TWELITE’s UART0.
- The
Serial
object is initialized at system startup with UART0 at 115200 bps, and the initialization process is handled within the library. It can be used from setup()
in user code. - The
Serial1
object is provided within the library but is not initialized. To enable UART1, perform the necessary initialization procedures Serial1.setup(), Serial1.begin()
.
Output may become unstable immediately after startup during setup()
, wakeup()
, or just before sleep during flush
processing.
setup()
void setup(uint16_t buf_tx, uint16_t buf_rx)
Initializes the object.
- Allocates memory for TX/RX FIFO buffers
- Allocates memory for the TWE_tsFILE structure
Serial
(UART0) automatically calls setup()
within the library. There is no need for the user to call it.
Also, the buffer size for Serial
(UART0) is determined at compile time. It can be changed by macros MWX_SER_TX_BUFF
(default 768) and MWX_SER_RX_BUFF
(default 256).
Parameter | Description |
---|
buf_tx | FIFO buffer size for TX |
buf_rx | FIFO buffer size for RX |
begin()
void begin(unsigned long speed = 115200, uint8_t config = 0x06)
Initializes the hardware.
Serial
(UART0) automatically calls begin()
within the library. There is no need for the user to call it.
Parameter | Description |
---|
speed | Specifies the UART baud rate |
config | When the serial_jen::E_CONF::PORT_ALT bit is specified, UART1 is initialized on ~DIO14,15 instead of ~DIO11(TxD),9(RxD). If not specified, it initializes on ~DIO11(TxD),9(RxD) instead of ~DIO14(TxD),15(RxD). |
The last two digits of the specified baud rate are rounded to zero. Also, due to hardware limitations, there may be an error from the specified baud rate.
Baud rate calculation involves division and may take processing time. When specifying 9600, 38400, or 115200, the calculation is done without division. For details, see constexpr uint16_t _serial_get_hect_baud(uint32_t baud)
.
end()
(Not implemented) Stops using the hardware.
get_tsFile()
TWE_tsFILE* get_tsFile();
Returns the structure in TWE_tsFILE*
format used by the C library.
For Serial (UART), the _sSerial
structure is defined.
7 - SerialParser
Formatted input for serial port (mwx::serial_parser)
This built-in class is defined as an embedded object intended for use in formatted input via the serial port.
It is defined as the type mwx::serial_parser<mwx::alloc_heap<uint8_t>>
, which allocates internal buffer space from the heap during initialization (begin()
).
For details, refer to the class serparser
.
8 - SPI
Performs reading and writing on the SPI bus (controller side).
Performs reading and writing on the SPI bus (controller side).
Notes
Constants
Constant | Meaning |
---|
const uint8_t
SPI_CONF::MSBFIRST | Set MSB as the first bit |
const uint8_t
SPI_CONF::LSBFIRST | Set LSB as the first bit |
const uint8_t
SPI_CONF::SPI_MODE0 | Set to SPI MODE 0 |
const uint8_t
SPI_CONF::SPI_MODE1 | Set to SPI MODE 1 |
const uint8_t
SPI_CONF::SPI_MODE2 | Set to SPI MODE 2 |
const uint8_t
SPI_CONF::SPI_MODE3 | Set to SPI MODE 3 |
Initialization and Termination
The procedure to use the SPI bus is via the begin()
method.
begin()
void begin(uint8_t slave_select, SPISettings settings)
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
Initializes the hardware.
This process is also required after waking up from sleep.
Parameter | Description |
---|
slave_select | Specifies the select pin of the target peripheral.
0 : DIO19``1 : DIO0 (DIO19 is reserved)``2 : DIO1 (DIO0,19 are reserved) |
settings | Specifies the SPI bus settings.
clock [Hz] specifies the SPI bus frequency. A divisor close to the specified frequency is selected. The value will be 16MHz or an even division of 16MHz.
bitOrder is either SPI_CONF::MSBFIRST or SPI_CONF::LSBFIRST .
dataMode is one of SPI_CONF::SPIMODE0..3 . |
Example
void setup() {
...
SPI.begin(0, SPISettings(2000000, SPI_CONF::MSBFIRST, SPI_CONF::SPI_MODE3));
...
}
void wakeip() {
...
SPI.begin(0, SPISettings(2000000, SPI_CONF::MSBFIRST, SPI_CONF::SPI_MODE3));
...
}
end()
Terminates the use of SPI hardware.
Reading and Writing
There are two types of procedures for reading and writing. Use either of them as needed.
Example
The following sample code reads the temperature every second from the Analog Devices temperature sensor ADT7310 and outputs it to the serial port.
#include <TWELITE>
#include <SM_SIMPLE>
enum class STATE : uint8_t {
INTERACTIVE = 255,
INIT = 0,
INIT_WAIT,
SENSOR,
LOOP_WAIT
};
SM_SIMPLE<STATE> step;
struct SensorData {
uint8_t highByte;
uint8_t lowByte;
uint16_t rawValue;
int32_t tempValue16th;
div_result_i32 temperature;
} sensorData;
void setup() {
step.setup(); // Initialize the state machine
}
void loop() {
do {
switch (step.state()) {
case STATE::INIT: // Initial state
SPI.begin(0 /* Use DIO19 as chip select */
, { 400000UL /* Clock frequency */
, SPI_CONF::MSBFIRST
, SPI_CONF::SPI_MODE3
}
);
// Software reset
SPI.beginTransaction();
for (int i = 0; i < 4; i++) {
SPI.transfer(0xFF);
}
SPI.endTransaction();
// Start Continuous Read mode
SPI.beginTransaction();
SPI.transfer(0x54);
SPI.endTransaction();
step.set_timeout(300); // Set wait time
step.next(STATE::INIT_WAIT);
break;
case STATE::INIT_WAIT: // Wait
if (step.is_timeout()) {
step.next(STATE::SENSOR);
}
break;
case STATE::SENSOR: // Read sensor data
SPI.beginTransaction();
sensorData.highByte = SPI.transfer(0x00); // Send dummy data to generate clock signal
sensorData.lowByte = SPI.transfer(0x00); // Send dummy data to generate clock signal
SPI.endTransaction();
sensorData.rawValue = (((uint16_t)sensorData.highByte << 8) | sensorData.lowByte) >> 3;
if (sensorData.rawValue & 0x1000) {
sensorData.tempValue16th = int32_t(sensorData.rawValue) - 0x2000;
} else {
sensorData.tempValue16th = sensorData.rawValue;
}
// Calculate temperature using div100()
sensorData.temperature = div100((sensorData.tempValue16th * 100) / 16);
// Output result to serial
Serial << crlf << sensorData.temperature.format() << "°C";
step.set_timeout(1000); // Wait until next capture
step.next(STATE::LOOP_WAIT);
break;
case STATE::LOOP_WAIT: // Wait
if (step.is_timeout()) {
step.next(STATE::SENSOR);
}
break;
default:
break;
}
} while (step.b_more_loop());
}
Here, the member function interface is used.
8.1 - SPI (Member Function Version)
SPI (Method-based usage)
After initializing the hardware with the begin()
method, use beginTransaction()
to enable bus communication. When beginTransaction()
is executed, the SPI select pin is asserted. Use the transfer()
function for reading and writing. SPI performs read and write operations simultaneously.
beginTransaction()
void beginTransaction()
void beginTransaction(SPISettings settings)
Starts using the SPI bus. Asserts the SPI select pin.
When called with the settings
parameter, it also configures the bus settings.
endTransaction()
Ends the use of the SPI bus. Deasserts the SPI select pin.
transfer()
, transfer16()
, transfer32()
inline uint8_t transfer(uint8_t data)
inline uint16_t transfer16(uint16_t data)
inline uint32_t transfer32(uint32_t data)
Performs read/write operations on the SPI bus. transfer()
transfers 8 bits, transfer16()
transfers 16 bits, and transfer32()
transfers 32 bits.
8.2 - SPI (Helper Class Version)
SPI (How to use with helper class)
The helper class version is a more abstract implementation. By creating a read/write object transceiver
, you start using the bus, and by destroying the object, you end the use of the bus.
By creating the object inside the conditional expression of an if statement, the lifetime of the object is limited to the scope within the if block, and when exiting the if block, the object is destroyed and the bus release procedure is performed at that point.
uint8_t c;
if (auto&& trs = SPI.get_rwer()) { // Create object and check device communication
// The scope (curly braces) here is the lifetime of trs.
trs << 0x00; // Write 0x00 using mwx::stream interface
trs >> c; // Store the read data into c.
}
// When exiting the if block, trs is destroyed and the bus usage ends
Also, since the read/write object implements the mwx::stream
interface, operators like <<
can be used.
- Matching the bus usage start and end with the object’s lifetime improves code readability and prevents omission of release procedures.
- Unifies read/write procedures using the
mwx::stream
interface.
Read/Write
A reading method using a helper class that performs read operations and their termination procedures within the scope if() { ... }
.
inline uint8_t _spi_single_op(uint8_t cmd, uint8_t arg) {
uint8_t d0, d1;
if (auto&& x = SPI.get_rwer()) {
d0 = x.transfer(cmd); (void)d0;
d1 = x.transfer(arg);
// (x << (cmd)) >> d0;
// (x << (arg)) >> d1;
}
return d1;
}
Above, the x
object created by the get_rwer()
method is used to perform byte-wise read/write.
- The
x
object is created inside the if(...)
. At the same time, the SPI bus select pin is set. (The type is resolved by universal reference auto&&
with type inference.) - The created
x
object defines operator bool ()
which is used for evaluation in the conditional expression. For SPI bus, it always evaluates to true
. - The
x
object defines the method uint8_t transfer(uint8_t)
, which performs an 8-bit read/write transfer to SPI when called. - At the end of the
if() { ... }
scope, the destructor of x
is called, releasing the SPI bus select pin.
get_rwer()
periph_spi::transceiver get_rwer()
Obtains a worker object used for SPI bus read/write.
Worker Object
transfer()
, transfer16()
, transfer32()
uint8_t transfer(uint8_t val)
uint16_t transfer16(uint16_t val)
uint32_t transfer32(uint32_t val)
Perform transfers of 8-bit, 16-bit, and 32-bit respectively, and return the read result with the same data width as the written data.
<<
operator
operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c)
operator << (uint32_t c)
int
and uint8_t
types perform 8-bit transfers.
uint16_t
and uint32_t
types perform 16-bit and 32-bit transfers respectively.
Transfer results are stored in an internal FIFO queue of up to 16 bytes and read out by the >>
operator. Since the buffer is not large, it is assumed to be read out each time after transfer.
>>
operator
operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)
null_stream(size_t i = 1)
operator >> (null_stream&& p)
Specify a variable with the same data width as the previous transfer.
If the read result is not needed, use the null_stream()
object. It skips the number of bytes specified by i
.
9 - TickTimer
System Timer (mwx::periph_ticktimer)
TickTimer
is used internally by TWENET for control purposes and operates implicitly. The timer interval is 1 ms. It defines only the available()
method for writing processing that occurs every 1 ms in the loop()
function based on TickTimer events.
Note that it does not necessarily become available at 1 ms intervals.
Due to the contents of the user program or internal interrupt handling of the system, significant delays may occur, and some events may be skipped.
void loop() {
if (TickTimer.available()) {
if ((millis() & 0x3FF) == 0) { // This may not be processed
Serial << '*';
}
}
}
Methods
available()
Set after the TickTimer
interrupt occurs, and becomes true
in the immediate loop()
execution. It is cleared after the loop()
ends.
10 - Timer0 .. 4
Timer, PWM (mwx::periph_timer)
Timers have two functions: generating software interrupts at specified intervals and producing PWM output at specified intervals. The TWELITE wireless module provides five timers, from 0 to 4.
Although the embedded object names are Timer0..4
, this page refers to them as TimerX
.
Methods
setup()
Initializes the timer. This call allocates the required memory area.
begin()
void begin(uint16_t u16Hz, bool b_sw_int = true, bool b_pwm_out = false)
Starts the timer. The first parameter specifies the timer frequency in Hz. If the second parameter is set to true
, software interrupts are enabled. If the third parameter is set to true
, PWM output is enabled.
You can change the frequency with change_hz()
. This method allows more precise control than begin()
.
You can modify the PWM duty cycle using change_duty()
.
To describe the interrupt handler process, you need to define Application Behavior.
end()
Stops the timer operation.
available()
Becomes true
in the loop()
immediately after a timer interrupt occurs, and turns false
after the loop()
finishes.
change_duty()
void change_duty(uint16_t duty, uint16_t duty_max = 1024)
Sets the duty cycle. The first parameter specifies the duty value (a smaller value brings the average waveform level closer to GND, while a larger value brings it closer to Vcc). The second parameter specifies the maximum duty value.
It is recommended to set duty_max
to one of 1024
, 4096
, or 16384
.
Although internal count value calculations involve division, these three values allow bit-shift operations instead. Other values result in heavier division calculations.
change_hz()
void change_hz(uint16_t hz, uint16_t mil = 0)
Sets the timer frequency. The second parameter specifies the fractional part (three decimal places) of the frequency as an integer. For example, to set 10.4 Hz, specify hz=10, mil=400
.
11 - Wire
Read/write as 2-wire serial (I2C) master (controller) (mwx::periph_wire)
Performs read/write operations as a 2-wire serial (I2C) master (controller).
Definition
Alias Definition
using TwoWire = mwx::periph_twowire<MWX_TWOWIRE_RCVBUFF>;
mwx::periph_wire<MWX_TWOWIRE_RCVBUFF>
can be referenced as TwoWire
.
Type Definitions
The following typedefs are used for argument and return types:
typedef uint8_t size_type;
typedef uint8_t value_type;
Notes
Some APIs do not strictly handle STOP bits.
In addition to this explanation, several argument types are defined for write()
and writer::operator()
:
- Fixed-size array type
uint8_t cmds[]={11,12};
...
Wire.write(cmds);
initializer_list<>
type
Wire.write({11,12})
Initialization and Termination
Creating a Wire
instance
The instance and necessary initialization are handled within the library. In user code, call Wire.begin()
to start using it.
When using the requestFrom()
method, you can specify the size of a FIFO queue for temporary data storage. At compile time, set the number of bytes via the macro MWX_TWOWIRE_BUFF
. The default is 32 bytes.
Example: -DMWX_TWOWIRE_BUFF=16
begin()
void begin(
const size_type u8mode = WIRE_100KHZ,
bool b_portalt = false)
Initializes the hardware.
If you operate Wire without initialization, the TWELITE wireless module will hang.
When waking from sleep, if Wire was active just before sleep, it will restore its previous state.
Parameter | Description |
---|
u8mode | Specifies the bus frequency. Default is 100KHz (WIRE_CONF::WIRE_100KHZ ). You can specify WIRE_CONF::WIRE_??KHZ where ?? can be 50 , 66 , 80 , 100 , 133 , 160 , 200 , 266 , 320 , 400 . |
b_portalt | Changes the hardware pin assignment. |
Example
void setup() {
...
Wire.begin();
...
}
void wakeup() {
...
Wire.begin();
...
}
Reading and Writing
There are two procedures for reading and writing. Use either as needed.
Others
Probe (Device Presence Check)
bool probe(uint8_t address)
Checks if the device specified by address
responds. Returns true
if the device exists.
setClock()
void setClock(uint32_t speed)
This is intended to change the bus frequency, but it performs no action.
11.1 - Wire (Member Function Version)
Wire (using member functions)
This method using member functions has relatively low abstraction and follows a general API structure similar to that provided by C language libraries. It offers a more intuitive procedure for operating a two-wire serial bus.
However, you need to explicitly manage the start and end of bus usage.
Reading
requestFrom()
size_type requestFrom(
uint8_t u8address,
size_type length,
bool b_send_stop = true)
Reads a specified number of bytes in one operation. The result is stored in a queue, so you should call the .read()
method repeatedly until the queue is empty immediately afterward.
Parameter | Description |
---|
u8address | I2C address to read from |
length | Number of bytes to read |
b_send_stop=true | If true , a STOP bit is issued at the end of reading |
Return type size_type | Number of bytes read. 0 indicates failure |
Code Example
int len = Wire.requestFrom(0x70, 6);
for (int i = 0; i < 6; i++) {
if (Wire.available()) {
au8data[i] = Wire.read();
Serial.print(buff[i], HEX);
}
}
// skip the rest (just in case)
// while (Wire.available()) Wire.read(); // normally, not necessary.
Writing
The writing process starts with beginTransmission()
, then proceeds with the write()
method. After the entire write operation is complete, call endTransmission()
.
#define DEV_ADDR (0x70)
const uint8_t msg[2] =
{SHTC3_SOFT_RST_H, SHTC3_SOFT_RST_L};
Wire.beginTransmission(DEV_ADDR);
Wire.write(msg, sizeof(msg));
Wire.endTransmission();
beginTransmission()
void beginTransmission(uint8_t address)
Initializes a write transfer. After the write process, call endTransmission()
promptly.
Parameter | Description |
---|
u8address | I2C address to write to |
write(value)
size_type write(const value_type value)
Writes a single byte.
Parameter | Description |
---|
value | Byte to write |
Return size_type | Number of bytes written. 0 indicates an error. |
write(*value, quantity)
size_type write(
const value_type* value,
size_type quantity)
Writes a sequence of bytes.
Parameter | Description |
---|
*value | Byte sequence to write |
size_type | Number of bytes |
Return size_type | Number of bytes written. 0 indicates an error. |
endTransmission()
uint8_t endTransmission(bool sendStop = true)
Finalizes the write operation.
Parameter | Description |
---|
sendStop = true | Issues a STOP bit |
Return uint8_t | 0: success, 4: failure |
11.2 - Wire (Helper Class Version)
Wire (How to use with helper classes)
The helper class version is a more abstract implementation. Generating objects reader, writer
that correspond to reading and writing marks the start of bus usage, and destroying the object performs the bus usage termination procedure.
By creating objects inside the condition expression of an if statement, the object’s lifetime is limited to the scope of the if block. When exiting the if block, the object is destroyed, and at that time, the bus usage termination procedure is performed.
if (auto&& wrt = Wire.get_writer(...)) { // Object creation and device communication check
// The scope (curly braces) is the lifetime of wrt.
wrt << 0x00; // Write 0x00 using the mwx::stream interface
}
// wrt is destroyed when exiting the if block, and bus termination is performed
Also, since the read/write objects implement the mwx::stream
interface, operators like <<
can be used.
- Matching the start and end of bus usage with the object’s lifetime improves code readability and prevents omission of termination procedures.
- Unified reading and writing procedures via the
mwx::stream
interface.
Reading
This is a reading method using helper classes to perform reading and its termination procedure within a scope if() { ... }
.
const uint8_t DEV_ADDR = 0x70;
uint8_t au8data[6];
if (auto&& rdr = Wire.get_reader(DEV_ADDR, 6)) {
for (auto&& c: au8data) {
c = rdr();
}
}
// same above
uint16_t u16temp, u16humd;
uint8_t u8temp_csum, u8humd_csum;
if (auto&& rdr = Wire.get_reader(SHTC3_ADDRESS, 6)) {
rdr >> u16temp;
rdr >> u8temp_csum;
rdr >> u16humd;
rdr >> u8humd_csum;
}
Above, the rdr
object generated by the get_reader()
method is used to read one byte at a time. The method parameter specifies the two-wire serial ID to read.
- The
rdr
object is created inside if(...)
. (The type is resolved as a universal reference auto&&
by type inference.) - The created
rdr
object defines operator bool ()
and is used for evaluation in the condition. If communication is possible with the specified ID, it returns true
. - The
rdr
object defines int operator () (void)
, which reads one byte from the two-wire serial bus when called. Returns -1
on failure, or the read byte value on success. - The destructor of
rdr
is called at the end of the if() { ... }
scope, issuing a STOP
on the two-wire serial bus.
get_reader(addr, read_count=0)
periphe_wire::reader
get_reader(uint8_t addr, uint8_t read_count = 0)
Obtains a worker object used for I2C reading.
Parameter | Description |
---|
addr | I2C address for reading |
read_count | Number of bytes to read (if specified, a STOP bit is issued at the end of the transfer). If 0 is specified, no STOP bit is issued (some devices may work without it). |
Writing (writer
)
This is a writing method using helper classes to perform writing and its termination procedure within a scope if() { ... }
.
const uint8_t DEV_ADDR = 0x70;
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
wrt(SHTC3_TRIG_H);
wrt(SHTC3_TRIG_L);
}
// same above
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
wrt << SHTC3_TRIG_H; // int type is handled as uint8_t
wrt << SHTC3_TRIG_L;
}
Above, the wrt
object generated by the get_writer()
method is used to write one byte at a time. The method parameter specifies the two-wire serial ID to write.
- The
wrt
object is created inside if(...)
. (The type is resolved as auto to avoid long type names.) - The created
wrt
object defines operator bool ()
and is used for evaluation in the condition. If communication is possible with the specified ID, it returns true
. - The
wrt
object defines int operator () (void)
, which writes one byte to the two-wire serial bus when called. Returns -1
on failure, or the written byte value on success. - The destructor of
wrt
is called at the end of the if() { ... }
scope, issuing a STOP
on the two-wire serial bus.
get_writer()
periph_wire::writer
get_writer(uint8_t addr)
Obtains a worker object used for I2C writing.
Parameter | Description |
---|
addr | I2C address for writing |
Worker Object (writer)
<<
operator
operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c)
operator << (uint32_t c)
int
and uint8_t
types transfer 8 bits. Data order is big-endian (higher byte transferred first).
()
operator
operator() (uint8_t val)
operator() (int val)
Writes one byte.
Worker Object (reader)
>>
operator
operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)
operator >> (uint8_t(&c)[N]) // fixed array of N bytes
Reads the size of each data type. Data order is big-endian (first transferred byte is stored in the higher byte).
()
operator
int operator() (bool b_stop = false)
// example
uint8_t dat[6];
if (auto&& rdr = Wire.get_reader(0x70)) {
for(uint8_t& x : dat) {
x = rdr();
}
}
Reads one byte. Returns -1 on error, or the read byte on success.
If b_stop
is set to true
, a STOP bit is issued on that read.
Example
The following example is a measurement example for the environmental sensor pal’s temperature and humidity sensor SHTC3.
Wire.begin();
// reset (may not necessary...)
if (auto&& wrt = Wire.get_writer(0x70)) {
wrt << 0x80; // SHTC3_SOFT_RST_H
wrt << 0x05; // SHTC3_SOFT_RST_L
}
delay(5); // wait some
// start read
if (auto&& wrt = Wire.get_writer(0x70)) {
wrt << 0x60; // SHTC3_TRIG_H
wrt << 0x9C; // SHTC3_TRIG_L
}
delay(10); // wait some
// read result
uint16_t u16temp, u16humd;
uint8_t u8temp_csum, u8humd_csum;
if (auto&& rdr = Wire.get_reader(0x70, 6)) {
rdr >> u16temp;
rdr >> u8temp_csum;
rdr >> u16humd;
rdr >> u8humd_csum;
}
// checksum 0x31, init=0xFF
if (CRC8_u8CalcU16(u16temp, 0xff) != u8temp_csum) {
Serial << format("{SHTC3 T CKSUM %x}", u8temp_csum); }
if (CRC8_u8CalcU16(u16humd, 0xff) != u8humd_csum) {
Serial << format("{SHTC3 H CKSUM %x}", u8humd_csum); }
// calc temp/humid (degC x 100, % x 100)
int16_t i16Temp = (int16_t)(-4500 + ((17500 * int32_t(u16temp)) >> 16));
int16_t i16Humd = (int16_t)((int32_t(u16humd) * 10000) >> 16);
Serial << "temp=" << int(i16Temp)
<< ",humid=" << int(i16Humd) << mwx::crlf;