This chapter describes all functional blocks (units) implemented in GEX, version 1.0. The term ``unit'' will be used here to refer to both unit types (drivers) or their instances where the distinction is not important.
This chapter describes all functional blocks (units) implemented in GEX, version 1.0. The term ``unit'' will be used here to refer to both unit types (drivers) or their instances where the distinction is not important.
Each unit's description will be accompanied by a corresponding snippet from the configuration file, and a list of supported commands and events. The commands and events described here form the payload of TinyFrame messages 0x10 (Unit Request) and 0x11 (Unit Report), as described in section \ref{sec:unit_requests_reports}. The number in the first column of the command (or event) tables, marked as ``Code'', is the command number (or report type) used in the payload to identify how the message data should be treated.
Each unit's description will be accompanied by a corresponding snippet from the configuration file, and a list of supported commands and events. The commands and events described here form the payload of TinyFrame messages 0x10 (Unit Request) and 0x11 (Unit Report), as described in section \ref{sec:unit_requests_reports}.
When the request or response payload is empty, it's omitted from the table. The same applies to commands with no response, in which case adding 0x80 to the command number triggers a SUCCESS response after the command is finished.
The number in the first column of the command (or event) tables, marked as ``Code'', is the command number (or report type) used in the payload to identify how the message data should be treated. When the request or response payload is empty, it's omitted from the table. The same applies to commands with no response, in which case adding 0x80 to the command number triggers a SUCCESS response after the command is finished.
\section{Naming Conventions and Common Principles}
\subsection{Unit Naming}
\subsection{Unit Naming}
Unit types are named in uppercase (e.g. SPI, 1WIRE, NPX) in the INI file and in the list of units. Unit instances can be named in any way the user desires; using lowercase makes it easier to distinguish them from unit types. It is advisable to use descriptive names, e.g. not ``pin1'' but rather ``button''.
Unit types are named in uppercase (e.g. SPI, 1WIRE, NPX) in the INI file and in the list of units. Unit instances can be named in any way the user desires; using lowercase makes it easier to distinguish them from unit types. It is advisable to use descriptive names, e.g. not ``pin1'' but rather ``button''.
Several units facilitate an access to a group of GPIO pins, such as the digital input and output units, or the SPI unit's slave select pins. The STM32 microcontroller's ports have 16 pins each, most of which can be configured to one of several alternate functions (e.g. SPI, PWM outputs, ADC input). As a consequence, it's common to be left with a discontiguous group of pins after assigning all the alternate functions needed by an application.
Several units facilitate an access to a group of GPIO pins, such as the digital input and output units, or the SPI unit's slave select pins. The STM32 microcontroller's ports have 16 pins each, most of which can be configured to one of several alternate functions (e.g. SPI, PWM outputs, ADC input). As a consequence, it's common to be left with a discontiguous group of pins after assigning all the alternate functions needed by an application.
@ -24,8 +22,6 @@ Several units facilitate an access to a group of GPIO pins, such as the digital
For instance, we could only have the pins 0, 1, 12--15 available on a \gls{GPIO} port. GEX provides a helpful abstraction to bridge the gaps in the port: The selected pins are packed together and represented, in commands and events, as a block of six pins (0x3F) instead of their original positions in the register (0xF003). This scheme is shown in figure \ref{fig:pin-packing}. The translation is done in the unit driver and works transparently, as if the block of pins had no gaps---all the referenced pins are updated simultaneously without glitches. Where pin numbers are used, the order in the packed word should be provided---in our example, that would be 0--5, counting from the least significant bit.
For instance, we could only have the pins 0, 1, 12--15 available on a \gls{GPIO} port. GEX provides a helpful abstraction to bridge the gaps in the port: The selected pins are packed together and represented, in commands and events, as a block of six pins (0x3F) instead of their original positions in the register (0xF003). This scheme is shown in figure \ref{fig:pin-packing}. The translation is done in the unit driver and works transparently, as if the block of pins had no gaps---all the referenced pins are updated simultaneously without glitches. Where pin numbers are used, the order in the packed word should be provided---in our example, that would be 0--5, counting from the least significant bit.
\todo[inline]{Expand unit descriptions, add diagrams... (text was written before the tables and ini snippets were added)}
% here are the unit sections, all following a common pattern
% here are the unit sections, all following a common pattern
@ -130,7 +130,7 @@ Since 1-Wire is a proprietary protocol, there is a much smaller choice of availa
\section{NeoPixel}\label{sec:theory-neo}
\section{NeoPixel}\label{sec:theory-neo}
NeoPixel is a marketing name of the \textbf{WS2812} and compatible intelligent \gls{LED} drivers that are commonly used in ``addressable \gls{LED} strips''. Details about the protocol may be found in the WS2812B datasheet \cite{neopixel-ds}. Those chips include the control logic, PWM drivers and the \gls{LED} diodes all in one 5$\times$5\,mm SMD package.
NeoPixel is a marketing name of the \textbf{WS2812} and compatible intelligent \gls{LED} drivers that are commonly used in ``addressable \gls{LED} strips''. Additional technical details about the chips and their protocol may be found in the WS2812B datasheet \cite{neopixel-ds}. Those chips include the control logic, PWM drivers and the \gls{LED} diodes all in one 5$\times$5\,mm SMD package.
The NeoPixel protocol is unidirectional, using only one data pin. The \gls{LED} drivers are chained together. Ones and zeros are encoded by pulses of a defined length on the data pin; after the color data was loaded into the \gls{LED} string, a longer "reset" pulse (low level) is issued by the bus master and the set colors are displayed. The timing constraints are listed in table \ref{fig:ws2812-dia}.
The NeoPixel protocol is unidirectional, using only one data pin. The \gls{LED} drivers are chained together. Ones and zeros are encoded by pulses of a defined length on the data pin; after the color data was loaded into the \gls{LED} string, a longer "reset" pulse (low level) is issued by the bus master and the set colors are displayed. The timing constraints are listed in table \ref{fig:ws2812-dia}.
@ -42,6 +42,7 @@ The unit classes wrap the command and event \gls{API} described in chapter \ref{
An example Python program showing a pattern with the \gls{LED} matrix driver IS31FL3730 is presented below as an illustration of the library usage. A photo of the produced pattern can be seen in figure \ref{fig:pydemo}.
An example Python program showing a pattern with the \gls{LED} matrix driver IS31FL3730 is presented below as an illustration of the library usage. A photo of the produced pattern can be seen in figure \ref{fig:pydemo}.
The analog/digital converter unit is one of the most complicated units implemented in the project. The unit can measure the voltage on an input pin, either as its immediate value, or averaged with exponential forgetting. Isochronous sampling is available as well: It's possible to capture a fixed-length block of data on demand, or as a response to a triggering condition on any of the enabled input pins. The \gls{ADC} must continuously sample the inputs to make the averaging and level based triggering possible; as a consequence, a pre-trigger buffer is available that can be read together with the block of samples following a trigger. The \gls{ADC} unit can also be switched to a continuous streaming mode.
The analog/digital converter unit is one of the most complicated and powerful units implemented in the project. The unit can measure the voltage on an input pin, either as its immediate value, or averaged with exponential forgetting. Isochronous sampling is available as well: it is possible to capture a fixed-length block of data on demand, or as a response to a triggering condition on any of the enabled input pins. The \gls{ADC} must continuously sample the inputs to make the averaging and level based triggering possible; as a consequence, a pre-trigger buffer is available that can be read together with the block of samples following a trigger. The \gls{ADC} unit can also be switched to a continuous streaming mode, a block capture which continues indefinitely, until the host decides to stop the stream.
It's possible to activate any number of the 16 analog inputs of the \gls{ADC} peripheral simultaneously. The maximum continuous sampling frequency, which reaches 70\,ksps with one channel, lowers with an increasing number of enabled channels as the amount of data to transfer to the host increases.
It is possible to activate any number of the 16 analog inputs of the \gls{ADC} peripheral simultaneously, together with the internal input channels. The maximum continuous sampling frequency, which reaches 70\,ksps with one channel, lowers with an increasing number of enabled channels, as the amount of data to transfer host increases. Those high speeds are achievable in shorter block captures, taking advantage of the (configurable) data buffer. A streamed or too long block capture may be aborted after the buffer is exhausted.
The digital/analog unit works with the two-channel \gls{DAC} peripheral of the microcontroller. It can be used in two modes: \gls{DC} output, and waveform generation.
The digital/analog unit works with the two-channel \gls{DAC} hardware peripheral. It can be used in two modes: \gls{DC} output, and waveform generation.
The waveform mode implements direct digital synthesis (explained in section \ref{sec:theory-dac-dds}) of a sine, rectangle, sawtooth or triangle wave. The generated frequency can be set with a sub-hertz precision up to the lower tens of kHz. The two outputs can use a different waveform shape, be synchronized, and their phase offset, as well as frequency, is dynamically adjustable.
The waveform mode implements direct digital synthesis (explained in section \ref{sec:theory-dac-dds}) of a sine, rectangle, sawtooth or triangle wave. The generated frequency can be set with a sub-hertz precision up to the lower tens of kHz. The two outputs can use a different waveform shape, can be synchronized, and their phase offset and frequency are dynamically adjustable.
\subsection{DAC Configuration}
\subsection{DAC Configuration}
@ -24,7 +23,7 @@ ch2_noise-level=3
\subsection{DAC Commands}
\subsection{DAC Commands}
Channels are specified in all commands as bit map:
Channels are specified in all commands as a bit map:
The digital input unit is the input counterpart of the digital output unit.
The digital input unit is the input counterpart of the digital output unit. In addition to reading the immediate digital levels of the selected pins, this unit can report asynchronous events on a pin change.
In addition to reading the immediate digital levels of the selected pins, this unit can generate asynchronous events on a pin change. The state of the entire input port, together with a microsecond timestamp (as is the case for all asynchronous events), is reported to the host either on a rising, falling, or any pin change.
The pin change event can be configured independently for each pin. In order to receive a pin change event, we must arme the pin first; it can be armed for a single event, or it may be re-armed automatically with a hold-off time. It's further possible to automatically arm selected pin triggers on start-up.
All pins of the unit may be configured either for a rising, falling, or any change detection; due to a hardware limitation, the same pin number may not be used for event detection on different ports (e.g. A1 and B1) at the same time. In order to receive a pin change event, we must arm the pin first, using a command; it can be armed for a single event, or it may be re-armed automatically with a hold-off time. It is, further, possible to automatically arm selected pins on start-up, removing the need to arm them e.g. after the module restarts or is re-connected.
\subsection{Digital Input Configuration}
\subsection{Digital Input Configuration}
@ -14,17 +11,17 @@ The pin change event can be configured independently for each pin. In order to r
The digital output unit provides a write access to one or more pins of a GPIO port. This unit additionally supports pulse generation on any of its pins. This is implemented in software with the timing derived from the system timebase, as the hardware timer outputs, otherwise used for \gls{PWM} or pulse generation, are available only on several dedicated pins. The timing code is optimized to reduce jitter. \todo{Measure jitter and add it here}
The digital output unit provides a write access to one or more pins of a \gls{GPIO} port. This unit additionally supports pulse generation on any of its pins; this is implemented in software, with timing derived from the system timebase, in order to support pulses on all pins regardless of hardware \gls{PWM} support. Pins in commands are expressed in the packed format (\ref{sec:packedpins}).
\subsection{Digital Output Configuration}
\subsection{Digital Output Configuration}
@ -41,7 +41,7 @@ open-drain=
\end{cmdreq}\\
\end{cmdreq}\\
4 &\cname{PULSE}
4 &\cname{PULSE}
Generate a pulse on the selected pins. The $\mu$s scale may be used only for 0--999\,$\mu$s.
Generate a pulse on the selected pins. The microsecond scale may be used only for 0--999\,$\mu$s.
The frequency capture unit implements both the frequency measurement methods explained in section \ref{sec:theory-fcap}: direct and reciprocal.
The frequency capture unit implements both the frequency measurement methods explained in section \ref{sec:theory-fcap}: direct and reciprocal.
The unit has several operational modes: idle, reciprocal continuous, reciprocal burst, direct continuous, direct burst, free counting, and single pulse. Burst mode is an on-demand measurement with possible averaging. Continuous mode doesn't support averaging, but the latest measurement can be read at any time without a delay.
The unit has several operational modes: idle, reciprocal continuous, reciprocal burst, direct continuous, direct burst, free counting, and single pulse. Burst mode is an on-demand measurement with optional averaging. Continuous mode doesn't support averaging, but the latest measurement can be read at any time without a delay.
The \gls{I2C} unit provides access to one of the microcontroller's \gls{I2C} peripherals. It can be configured to use either of the three speeds (Standard, Fast and Fast+) and supports both 10-bit and 7-bit addressing. 10-bit addresses can be used in commands by setting their highest bit (0x8000), as a flag to the unit.
The \gls{I2C} unit provides access to one of the microcontroller's \gls{I2C} peripherals. More on the \IIC bus can be found in section \ref{sec:theory-i2c}.
The unit can be configured to use either of the three standard speeds (Standard, Fast and Fast+) and supports both 10-bit and 7-bit addressing. 10-bit addresses can be used in commands by setting their highest bit (0x8000), as a flag to the unit; the 7 or 10 least significant bits will be used as the actual address.
\subsection{\IIC Configuration}
\subsection{\IIC Configuration}
@ -26,14 +28,14 @@ digital-filter=0
\begin{cmdlist}
\begin{cmdlist}
0 &\cname{WRITE}
0 &\cname{WRITE}
Raw write transaction
Perform a raw write transaction
&\begin{cmdreq}
&\begin{cmdreq}
\cfield{u16} slave address
\cfield{u16} slave address
\cfield{u8[]} bytes to write
\cfield{u8[]} bytes to write
\end{cmdreq}\\
\end{cmdreq}\\
1 &\cname{READ}
1 &\cname{READ}
Raw read transaction
Perform a raw read transaction.
&\begin{cmdreq}
&\begin{cmdreq}
\cfield{u16} slave address
\cfield{u16} slave address
\cfield{u16} number of read bytes
\cfield{u16} number of read bytes
@ -44,7 +46,7 @@ digital-filter=0
\end{cmdresp}\\
\end{cmdresp}\\
2 &\cname{WRITE\_REG}
2 &\cname{WRITE\_REG}
Write to a slave register. Sends the register number and the data in the same \gls{I2C} transaction. Multiple registers can be written to slaves supporting auto-increment.
Write to a slave register. Sends the register number and the data in the same transaction. Multiple registers can be written at once if the slave supports auto-increment.
&\begin{cmdreq}
&\begin{cmdreq}
\cfield{u16} slave address
\cfield{u16} slave address
\cfield{u8} register number
\cfield{u8} register number
@ -52,7 +54,7 @@ digital-filter=0
\end{cmdreq}\\
\end{cmdreq}\\
3 &\cname{READ\_REG}
3 &\cname{READ\_REG}
Read from a slave register. Writes the register number and issues a read transaction of the given length. Multiple registers can be read from slaves supporting auto-increment.
Read from a slave register. Writes the register number and issues a read transaction of the given length. Multiple registers can be read at once if the slave supports auto-increment.
The NeoPixel unit implements the protocol needed to control a digital \gls{LED} strip with WS2812, WS2811, or compatible \gls{LED} driver chips. The protocol timing is implemented in software, therefore it is available on any GPIO pin of the module.
The NeoPixel unit implements the protocol needed to control a digital \gls{LED} strip with WS2812, WS2811, or compatible \gls{LED} driver chips. The NeoPixel protocol (explained in section \ref{sec:theory-neo}) is implemented in software, therefore it is available on any \gls{GPIO} pin of the module.
The color data can be loaded in five different format: as packed bytes, or as the little-endian or big-endian encoding of colors in the 32-bit format 0x00RRGGBB or 0x00BBGGRR. This data format is convenient when the colors are already represented by an array of 32-bit integers.
The color data can be loaded in five different format: as packed bytes (3$\times$8 bits color), or as the little- or big-endian encoding of colors in a 32-bit format: 0x00RRGGBB or 0x00BBGGRR. The 32-bit format is convenient when the colors are already represented as an array of 32-bit integers, e.g. extracted from a screen capture or an image.
The \gls{PWM} unit uses a timer/counter to generate a \gls{PWM} signal. There are four outputs with a common frequency and phase, but independent duty cycles. Each channel can be individually enabled or disabled.
The \gls{PWM} unit uses a timer/counter to generate a \gls{PWM} (pulse train) signal. There are four outputs with a common frequency and independent duty cycles. Each channel can be individually enabled or disabled. This unit is intended for applications such as light dimming, heater regulation, or the control of H-bridges.
This unit is intended for applications like light dimming, heater regulation, or the control of H-bridges.
\todo[inline]{diagram, also show what is duty cycle}
The shift registers driver unit is designed for the loading of data into \textit{serial-in, parallel-out} (SIPO) shift registers, such as 74HC4094 or 74HC595. Those are commonly used to control segmented LED displays, LED matrices etc.
The shift registers driver unit is designed for the loading of data into serial-in/parallel-out (SIPO) shift registers, such as 74xx4094 or 74xx595. Those are commonly used to control segmented LED displays, LED user interfaces, etc.
This unit handles both the \textit{Shift} and \textit{Store} signals and is capable of loading multiple shift registers simultaneously, reducing visible glitches in the display. It's also possible to set the data lines to arbitrary level(s) before sending the Store pulse, which can be latched and used for some additional feature of the LED display, such as brightness control.
Those devices may be daisy-chained: the output of one is connected to the input of another, sharing the same clock and other signals, and they work together as one longer shift register.
A SIPO shift register has the following pins (possibly named differently with chips from different vendors):
\begin{itemize}
\item\textit{Shift} -- \gls{SCK}; shifts the data in the register by one bit
\item\textit{Data In} -- \gls{MOSI}; serial data to load into the register
\item\textit{Data Out} -- output for daisy-chaining with other shift registers
\item\textit{Store} -- latches the current register data and shows it on the output
\item\textit{Clear} -- erases the latched data and clears the display
\end{itemize}
This unit automatically handles both the \textit{Shift} and \textit{Store} signals, provides access to the \textit{Clear} output, and is capable of loading multiple shift registers in parallel (an arrangement sometimes used instead of daisy-chaining). The polarity (active level) of all signals can be configured.
It is, additionally, possible to set the data lines to arbitrary ``idle'' level(s) before sending the \textit{Store} pulse; this may be latched and used for some additional feature on the user interface, such as a brightness control.
\subsection{SIPO Configuration}
\subsection{SIPO Configuration}
@ -25,9 +39,11 @@ data-pins=3
\subsection{SIPO Commands}
\subsection{SIPO Commands}
The WRITE and CLEAR\_DIRECT commands are the only ones normally used. The others provide manual control over all the output signals for debugging or testing.
\begin{cmdlist}
\begin{cmdlist}
0 &\cname{WRITE}
0 &\cname{WRITE}
Load the shift registers and leave the data outputs in the ``trailing data'' state before sending the Store pulse.
Load the shift registers and leave the data outputs in the ``trailing data'' state before sending the \textit{Store} pulse.
&
&
\begin{cmdreq}
\begin{cmdreq}
\cfield{u16} trailing data
\cfield{u16} trailing data
@ -39,7 +55,7 @@ data-pins=3
\\
\\
1 &\cname{DIRECT\_DATA}
1 &\cname{DIRECT\_DATA}
Directly write to the data pins (same like the DO unit's WRITE command)
Directly write to the data pin(s)
&
&
\begin{cmdreq}
\begin{cmdreq}
\cfield{u16} values to write
\cfield{u16} values to write
@ -47,15 +63,15 @@ data-pins=3
2 &
2 &
\cname{DIRECT\_CLEAR}
\cname{DIRECT\_CLEAR}
Pulse the Clear pin, erasing the registers' data&\\
The \gls{SPI} unit provides access to one of the microcontroller's \gls{SPI} peripherals. It can be configured to use any of the different speeds, clock polarity and phase settings available in its control registers.
The \gls{SPI} unit provides access to one of the microcontroller's \gls{SPI} peripherals. The unit can be configured to any of the hardware-supported speeds, clock polarity, and clock phase settings. Explanation of those options, including diagrams, can be found in section \ref{sec:theory-spi}.
The unit handles up to 16 slave select (\gls{NSS}) signals and supports message multi-cast (addressing more than one slaves at once). Protection resistors should be used if a multi-cast transaction is issued with \gls{MISO} connected.
The unit handles up to 16 slave select (\gls{NSS}) signals and supports message multi-cast (addressing more than one slaves at once). Protection resistors should be used if a multi-cast transaction is issued with \gls{MISO} connected to prevent a short circuit between slaves transmitting the opposite logical level.
The QUERY command of this unit, illustrated by figure \ref{fig:spi_query}, is flexible enough to support all types of \gls{SPI} transactions: read-only, write-only, and read-write with different request and response lengths. The slave select pin is held low during the entire transaction.
The QUERY command of this unit, illustrated by figure \ref{fig:spi_query}, is flexible enough to support all types of \gls{SPI} transactions: read-only, write-only, and read-write, with different request and response lengths and paddings. The slave select signal is asserted during the entire transaction.
\begin{figure}[h]
\begin{figure}[h]
\centering
\centering
@ -42,7 +42,7 @@ pins=0
\begin{cmdlist}
\begin{cmdlist}
0 &\cname{QUERY}
0 &\cname{QUERY}
Exchange bytes with a slave device
Exchange bytes with a slave device; see the diagram in figure \ref{fig:spi_query}
&\begin{cmdreq}
&\begin{cmdreq}
\cfield{u8} slave number 0--16
\cfield{u8} slave number 0--16
\cfield{u16} response padding
\cfield{u16} response padding
@ -55,7 +55,7 @@ pins=0
\end{cmdresp}\\
\end{cmdresp}\\
1 &\cname{MULTICAST}
1 &\cname{MULTICAST}
Send a message to multiple slaves at once. The address is a bit map (e.g. 0x8002 = slaves 1 and 15).
Send a message to multiple slaves at once. The ``addressed slaves'' word uses the packed pins format (\ref{sec:packedpins}).
The touch sensing unit provides an access to the \gls{TSC} peripheral. Its function is explained in section \ref{sec:theory-touch}. The unit configures the \gls{TSC} and reads the output values of each enabled touch pad.
The touch sensing unit provides an access to the \gls{TSC} peripheral, explained in section \ref{sec:theory-touch}. The unit configures the \gls{TSC} and reads the output values of each enabled touch pad. Additionally, a threshold-based digital input function is implemented to make the emulation of touch buttons easier. The hysteresis and debounce time can be configured in the configuration file or set using a command. The threshold of individual pads must be set using a command.
The \gls{USART} unit provides access to one of the microcontroller's \gls{USART} peripherals. All \gls{USART} parameters can be configured to match the application's needs. The peripheral is capable of driving RS485 transceivers with the \gls{DE} output for switching between reception and transmission.
The \gls{USART} unit provides access to one of the microcontroller's \gls{USART} peripherals. See section \ref{sec:theory-usart} for more information about the interface.
The unit implements asynchronous reception and transmission using DMA and a circular buffer. Received data is sent to the host in asynchronous events when either half of the buffer is filled, or after a fixed timeout from the last received byte.
Most \gls{USART} parameters available in the hardware peripheral's configuration registers can be adjusted to match the application's needs. The peripheral is capable of driving RS-485 transceivers, using the \gls{DE} output for switching between reception and transmission.
The unit implements asynchronous reception and transmission with \gls{DMA} and a circular buffer. Received data is sent to the host in asynchronous events when a half of the buffer is filled, or after a fixed timeout from the last received byte. The write access is, likewise, implemented using \gls{DMA}.
\todo[inline]{add a diagram of the dma-based reception}