The idea of making it easier to interact with low-level hardware from a \gls{PC} is not new. Several solutions to this problem have been developed, each with its own advantages and drawbacks. Some examples will be presented in this chapter.
@ -24,7 +24,7 @@ The storage medium is organized into \textit{sectors} (or \textit{blocks}), usua
Data area & Organized in \textit{clusters}\\
\bottomrule
\end{tabular}
\caption{\label{tab:fat16-disk-areas}Areas of a FAT-formatted disk}
\caption{\label{tab:fat16_disk_areas}Areas of a FAT-formatted disk}
\end{table}
\subsection{Boot Sector}
@ -51,7 +51,7 @@ The bad cluster mark, 0xFFF7, is used for clusters which are known to corrupt da
A directory is a record on the disk that can span several clusters and holds information about the files and sub-directories contained in it. The root directory has the same structure as any other directory; the difference lies in the fact that it is allocated with a fixed position and size when the disk is formatted, whereas other directories are stored in the same way as ordinary files and their capacity can be increased by simply expanding to another cluster.
Directories are organized in 32-byte entries representing individual files. \Cref{tab:fat16-dir-entry} shows the structure of one such entry. The name and extension fields form the well-known ``8.3'' file name format known from MS DOS\footnote{``8.3'' refers to the byte size of the name and extension fields in the directory entry.}. Longer file names are encoded using the \gls{LFN} scheme~\cite{fat-lfn} as special hidden entries stored in the directory table alongside the regular ``8.3'' ones kept for backward compatibility.
Directories are organized in 32-byte entries representing individual files. \Cref{tab:fat16_dir_entry} shows the structure of one such entry. The name and extension fields form the well-known ``8.3'' file name format known from MS DOS\footnote{``8.3'' refers to the byte size of the name and extension fields in the directory entry.}. Longer file names are encoded using the \gls{LFN} scheme~\cite{fat-lfn} as special hidden entries stored in the directory table alongside the regular ``8.3'' ones kept for backward compatibility.
\begin{table}
\centering
@ -69,7 +69,7 @@ Directories are organized in 32-byte entries representing individual files. \Cre
28 & 4 & File size (bytes) \\
\bottomrule
\end{tabular}
\caption{\label{tab:fat16-dir-entry}Structure of a FAT16 directory entry}
\caption{\label{tab:fat16_dir_entry}Structure of a FAT16 directory entry}
\end{table}
\noindent
@ -84,12 +84,12 @@ The first byte of the file name has special meaning:
The attributes field contains flags such as \textit{directory}, \textit{volume label}, \textit{read-only} and \textit{hidden}. Volume label is a special entry in the root directory defining the disk's label shown by the host \gls{OS}. A file with the directory bit set is actually a pointer to a subdirectory, meaning that when we open the linked cluster, we will find another directory table.
\Cref{fig:fat-example} shows a possible organization of the GEX file system with two INI files, one spanning two clusters, the other being entirely inside one. The clusters need not be used completely; the exact sizes are stored in the files' directory entries.
\Cref{fig:fat_example} shows a possible organization of the GEX file system with two INI files, one spanning two clusters, the other being entirely inside one. The clusters need not be used completely; the exact sizes are stored in the files' directory entries.
\begin{figure}[h]
\centering
\includegraphics[scale=1.3]{img/fat-links.pdf}
\caption{\label{fig:fat-example}An example of the GEX virtual file system}
\caption{\label{fig:fat_example}An example of the GEX virtual file system}
\end{figure}
@ -103,7 +103,7 @@ A FAT16 emulator was developed as part of the open-source \mbed DAPLink project~
The GEX firmware adapts several parts of the DAPLink code, optimizing its \gls{RAM} usage and porting it to work with FreeRTOS. The emulator source code is located in the \mono{User/vfs} folder of the GEX repository; the original Apache 2.0 open-source software license headers, as well as the file names, have been retained.
As shown in \cref{tab:fat16-disk-areas}, the disk consists of several areas. The boot sector is immutable and can be loaded from the flash memory when requested. The handling of the other disk areas (\gls{FAT}, data area) depends on the type of access: read or write.
As shown in \cref{tab:fat16_disk_areas}, the disk consists of several areas. The boot sector is immutable and can be loaded from the flash memory when requested. The handling of the other disk areas (\gls{FAT}, data area) depends on the type of access: read or write.
@ -4,7 +4,7 @@ FreeRTOS is a free, open-source real-time operating system kernel targeted at em
FreeRTOS provides a task scheduler, forming the central part of the system, and implements queues, semaphores, and mutexes for message passing and the synchronization of concurrent tasks. Those features are summarily called \textit{synchronization objects}, or simply \textit{objects}.
The system is used in GEX for its synchronization objects that allow us to safely pass messages between interrupts and working threads, without deadlocks or race conditions; the particular usage of FreeRTOS features will be explained in \cref{sec:rtos-in-gex}. The built-in stack overflow protection helps us optimize task memory allocation\footnote{The stack monitor reports how much stack space was really used, so we can expand or shrink it as needed to make the application work reliably, without wasting memory that would never be used.}, and the heap allocator provided by FreeRTOS enables thread-safe dynamic allocation with a shared heap.
The system is used in GEX for its synchronization objects that allow us to safely pass messages between interrupts and working threads, without deadlocks or race conditions; the particular usage of FreeRTOS features will be explained in \cref{sec:rtos_in_gex}. The built-in stack overflow protection helps us optimize task memory allocation\footnote{The stack monitor reports how much stack space was really used, so we can expand or shrink it as needed to make the application work reliably, without wasting memory that would never be used.}, and the heap allocator provided by FreeRTOS enables thread-safe dynamic allocation with a shared heap.
\section{Basic FreeRTOS Concepts and Functions}
@ -12,13 +12,13 @@ The system is used in GEX for its synchronization objects that allow us to safel
Threads in FreeRTOS are called \textit{tasks}. Each task is assigned a memory area to use as its stack space, and a holding structure with its name, saved \textit{context}, and other metadata used by the kernel. A task context includes the program counter, stack pointer and other register values. Task switching is done by saving and restoring this context by manipulating the values on the stack before leaving an \gls{ISR}. The FreeRTOS website provides an example with the AVR port~\cite{freertos-task-switching} demonstrating how its internal functionality is implemented, including the context switch.
At start-up the firmware initializes the kernel, registers tasks to run, and starts the scheduler. From this point onward the scheduler is in control and runs the tasks using a round robin scheme, always giving a task one tick of run time (usually 1\,ms) before interrupting it. Which task should run is determined primarily by their priority numbers, but there are other factors, as will be shown in \cref{sec:task-switching}.
At start-up the firmware initializes the kernel, registers tasks to run, and starts the scheduler. From this point onward the scheduler is in control and runs the tasks using a round robin scheme, always giving a task one tick of run time (usually 1\,ms) before interrupting it. Which task should run is determined primarily by their priority numbers, but there are other factors, as will be shown in \cref{sec:task_switching}.
\subsubsection{Task Run States}
Tasks can be in one of four states: Suspended, Ready, Blocked, and Running. The Suspended state does not normally occur in a task's life cycle, it is entered and left using API calls from the application. A task is in the Ready state when it can run, but is currently paused because a higher priority task is running. It enters the Running state when the scheduler switches to it. A Running task can wait for a synchronization object (e.g., a mutex) to be available; at this point it enters a Blocked state and the scheduler runs the next Ready task. When no tasks can run, the Idle Task takes control; it can either enter a sleep state to save power, or wait in a loop until another task is available. The Idle task is always either Ready or Running and has the lowest priority of all tasks.
\subsubsection{Task Switching and Interrupts}\label{sec:task-switching}
\subsubsection{Task Switching and Interrupts}\label{sec:task_switching}
Task switching occurs periodically in a timer interrupt, usually every 1\,ms; in \armcm chips this is typically the SysTick interrupt, a timer designed for this purpose that is included in the core itself and thus available on all derived platforms.
\item Settings storage and loading (\cref{sec:settings_storage})
\item Unit life cycle management (\cref{sec:units_function})
\item Message sending and delivery (\cref{sec:message_passing})
\item Interrupt routing (\cref{sec:irq-routing})
\item Interrupt routing (\cref{sec:irq_routing})
\end{itemize}
\section{Unit Life Cycle and Internal Structure}\label{sec:units-function}
\section{Unit Life Cycle and Internal Structure}\label{sec:units_function}
GEX's user-facing functions, units, are implemented in \textit{unit drivers}. Those are independent modules in the firmware that the user can enable and configure, in one or more instances. In practice, we are limited by hardware constraints: i.e., there may be only one \gls{ADC} peripheral, or two \gls{SPI} ports. The assignment of those hardware resources to units is handled by the \textit{resource registry} (\cref{sec:res-allocation}).
GEX's user-facing functions, units, are implemented in \textit{unit drivers}. Those are independent modules in the firmware that the user can enable and configure, in one or more instances. In practice, we are limited by hardware constraints: i.e., there may be only one \gls{ADC} peripheral, or two \gls{SPI} ports. The assignment of those hardware resources to units is handled by the \textit{resource registry} (\cref{sec:res_allocation}).
@ -45,17 +45,17 @@ The unit driver handles commands sent from the host \gls{PC}, initializes and de
When the units configuration file is modified, all units are de-initialized and removed. The binary settings are then updated based on the new values, verifying that the requested resources are available, and the units that can be enabled are subsequently initialized and made available to the user.
\caption{\label{fig:resource-repository}An example allocation in the resource registry}
\caption{\label{fig:resource_repository}An example allocation in the resource registry}
\end{figure}
The microcontroller provides a number of hardware resources that require exclusive access: GPIO pins, peripheral blocks (\gls{SPI}, \gls{I2C}, \gls{UART}\textellipsis), and \gls{DMA} channels. If two units tried to control the same pin, the results would be unpredictable; similarly, the output of a multiply-accessed serial port could become a useless mix of the different data streams.
To prevent multiple access, the firmware includes a \textit{resource registry} (\cref{fig:resource-repository}). Each individual resource is represented by a field in a resource table together with its owner's callsign. Initially all resources are free, except for those not available on the particular platform (e.g., a \gls{GPIO} pin PD1 may be disabled if not present on the microcontroller's package).
To prevent multiple access, the firmware includes a \textit{resource registry} (\cref{fig:resource_repository}). Each individual resource is represented by a field in a resource table together with its owner's callsign. Initially all resources are free, except for those not available on the particular platform (e.g., a \gls{GPIO} pin PD1 may be disabled if not present on the microcontroller's package).
The resources used by the core framework are taken by a virtual unit \verb|SYSTEM| on start-up to prevent conflicts with the user's units. This is the case of the status \gls{LED}, the Lock button, \gls{USB} pins, the communication \gls{UART}, the pins and an \gls{SPI} peripheral connecting the wireless module, pins used for the crystal oscillator, and the timer/counter which provides the system timebase.
@ -78,7 +78,7 @@ The INI files, which can be edited through the communication \gls{API} or using
One of the key functions of the core framework is to deliver messages from the host \gls{PC} to the right units. The \textit{TinyFrame} protocol is used, described in detail in \cref{sec:tinyframe}; it is represented by the blocks ``TinyFrame Parser'' and ``TinyFrame Builder'' in \cref{fig:gex-internal}.
One of the key functions of the core framework is to deliver messages from the host \gls{PC} to the right units. The \textit{TinyFrame} protocol is used, described in detail in \cref{sec:tinyframe}; it is represented by the blocks ``TinyFrame Parser'' and ``TinyFrame Builder'' in \cref{fig:gex_internal}.
Two groups of messages exist: \textit{system messages} and \textit{unit messages}. System messages can, for instance, access the INI files, or request a list of available units. Unit messages are addressed to a particular unit, and their payload format is defined by the unit's driver. An incoming message is inspected and delivered to the appropriate recipient, or responded to with an error message.
@ -87,7 +87,7 @@ In addition to message delivery, the core framework also provides event reportin
This high-level functionality resides above the framing protocol, which will be described in \cref{sec:tinyframe}. The message format is shown in \cref{sec:unit_requests_reports}.
Interrupts are an important part of almost any embedded application. They provide a way to rapidly react to asynchronous external or internal events, temporarily leaving the main program, jumping to an interrupt handler routine, and then returning back after the event is handled. Interrupts are also the way FreeRTOS implements multitasking without a multi-core processor.
It is evident that multiple units might need to use the same handler, even at the same time, since each \gls{DMA} channel is configured, and works, independently. GEX implements a redirection scheme to accomplish such interrupt sharing: All interrupt handlers are defined in one place, accompanied by a table of function pointers. When a unit driver wants to register an interrupt handler, it stores a pointer to it in this redirection table. Then, once an interrupt is invoked, the common handler checks the corresponding entry in the table and calls the referenced routine, if any. Conversely, when a unit driver de-initializes a unit, it removes all interrupt handlers it used, freeing the redirection table slots for other use.
The firmware is built around FreeRTOS (\cref{sec:freertos}) and a number of its synchronization objects and patterns are used to make its operation more robust.
@ -118,7 +118,7 @@ The message and job queue, seen in \cref{fig:gexinternal}, is used to decouple a
The ``transmission complete'' interrupt signals this fact to the message processing task using a binary semaphore. The semaphore is released in the \gls{ISR} and taken when a new block of data is to be transmitted. If more data needs to be transmitted, the queue task waits for the semaphore and enters a Blocked state until the semaphore becomes available again.
Two mutexes are used in the firmware: one that guards access to TinyFrame until the previous message was fully transmitted, and one to guard a shared memory buffer (reused in several different places to save memory and avoid its re-allocation). The hardware resource registry (explained in \cref{sec:res-allocation}) does not need mutexes for individual resources, as concurrent access to those fields can never happen; resources are always taken or released sequentially by the same task.
Two mutexes are used in the firmware: one that guards access to TinyFrame until the previous message was fully transmitted, and one to guard a shared memory buffer (reused in several different places to save memory and avoid its re-allocation). The hardware resource registry (explained in \cref{sec:res_allocation}) does not need mutexes for individual resources, as concurrent access to those fields can never happen; resources are always taken or released sequentially by the same task.
\section{Source Code Layout}
@ -155,10 +155,10 @@ Two mutexes are used in the firmware: one that guards access to TinyFrame until
└── Makefile
\end{verbatim}
\vspace{-1em}
\caption{\label{fig:repo-structure} The general structure of the source code repository}
\caption{\label{fig:repo_structure} The general structure of the source code repository}
\end{wrapfigure}
Looking at the GEX source code repository (\cref{fig:repo-structure}), at the root we'll find the device specific driver libraries and support files provided by ST Microelectronics, the FreeRTOS middleware, and a folder called \verb|User| containing the GEX application code. This division is useful when porting the firmware to a different microcontroller, as the GEX folder is mostly platform-independent and can be simply copied (of course, adjustments are needed to accompany different hardware peripheral versions etc.). The GEX core framework consists of everything in the \verb|User| folder, excluding the \verb|units| directory in which the individual units are implemented. Each unit driver must be registered in the file \verb|platform.c| to be available for the user to select. The file \verb|plat_compat.h| includes platform-specific headers and macros, defining parameters such as pin assignments or the clock speed.
Looking at the GEX source code repository (\cref{fig:repo_structure}), at the root we'll find the device specific driver libraries and support files provided by ST Microelectronics, the FreeRTOS middleware, and a folder called \verb|User| containing the GEX application code. This division is useful when porting the firmware to a different microcontroller, as the GEX folder is mostly platform-independent and can be simply copied (of course, adjustments are needed to accompany different hardware peripheral versions etc.). The GEX core framework consists of everything in the \verb|User| folder, excluding the \verb|units| directory in which the individual units are implemented. Each unit driver must be registered in the file \verb|platform.c| to be available for the user to select. The file \verb|plat_compat.h| includes platform-specific headers and macros, defining parameters such as pin assignments or the clock speed.
The \gls{USB} Device library, which had to be modified to support a composite class, is stored inside the \verb|User| folder too, as it is compatible with all STM32 microcontrollers that support \gls{USB}.
@ -8,7 +8,7 @@ GEX is designed to be modular and easy to extend. The user-facing functionality
\caption[GEX conceptual overview]{\label{fig:conceptual}The ``GEX stack'', from a user application down to hardware}
\end{figure}
When we work with GEX, it is through units. The platform without units would be just an empty shell, the bare core framework; this underlying system will be described in \cref{sec:coreframework}. We will explore the individual units in \cref{sec:units-overview}, after going through the hardware realizations in \cref{sec:hwreal} and covering the communication protocol in \cref{sec:tinyframe}.
When we work with GEX, it is through units. The platform without units would be just an empty shell, the bare core framework; this underlying system will be described in \cref{sec:coreframework}. We will explore the individual units in \cref{sec:units_overview}, after going through the hardware realizations in \cref{sec:hwreal} and covering the communication protocol in \cref{sec:tinyframe}.
\section{Physical User Interface}
@ -17,11 +17,11 @@ The firmware can be flashed to a STM32 development board, or a custom \gls{PCB}.
\begin{figure}[h]
\centering
\includegraphics[scale=.95]{img/users-view.pdf}
\caption{\label{fig:users-view-of-gex}Physical user interface of a GEX module}
\caption{\label{fig:users_view_of_gex}Physical user interface of a GEX module}
\end{figure}
\noindent
All GEX hardware platforms have some common characteristics (\cref{fig:users-view-of-gex}):
All GEX hardware platforms have some common characteristics (\cref{fig:users_view_of_gex}):
\begin{itemize}
\item\textbf{Power \gls{LED}} -- a simple indication that the board is powered on
@ -35,12 +35,12 @@ All GEX hardware platforms have some common characteristics (\cref{fig:users-vie
\section{GEX-PC Connection}
\Cref{fig:users-view-of-gex} shows three ways to connect the module to a \gls{PC}. Each communication interface has its advantages and drawbacks, and is suitable for different use-cases.
\Cref{fig:users_view_of_gex} shows three ways to connect the module to a \gls{PC}. Each communication interface has its advantages and drawbacks, and is suitable for different use-cases.
\begin{itemize}
\item\textbf{Direct \gls{USB} connection}
This is the primary and most straightforward connection method. We use the \gls{CDCACM} and \gls{MSC}\gls{USB} classes to have the module appear as a virtual serial port and a mass storage device, as described in \cref{sec:usb-classes}. This method is the fastest of the three and works out-of-the-box on Linux and MacOS. On MS Windows it may require the right software driver to be installed and assigned manually\footnote{The STM32 Virtual COM port driver~\cite{stm-vcom} has been tested to work with GEX on MS Windows version 7 and 8, though it must be manually assigned to the device in the Device Manager. MS Windows 10 and later should support \gls{CDCACM} natively.}.
This is the primary and most straightforward connection method. We use the \gls{CDCACM} and \gls{MSC}\gls{USB} classes to have the module appear as a virtual serial port and a mass storage device, as described in \cref{sec:usb_classes}. This method is the fastest of the three and works out-of-the-box on Linux and MacOS. On MS Windows it may require the right software driver to be installed and assigned manually\footnote{The STM32 Virtual COM port driver~\cite{stm-vcom} has been tested to work with GEX on MS Windows version 7 and 8, though it must be manually assigned to the device in the Device Manager. MS Windows 10 and later should support \gls{CDCACM} natively.}.
\chapter{Units Overview, Commands and Events Description}\label{sec:units-overview}
\chapter{Units Overview, Commands and Events Description}\label{sec:units_overview}
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.
@ -19,10 +19,10 @@ Several units facilitate an access to a group of GPIO pins, such as the digital
\begin{figure}[h]
\centering
\includegraphics[scale=1]{img/pin-packing.pdf}
\caption{\label{fig:pin-packing}Pin packing}
\caption{\label{fig:pin_packing}Pin packing}
\end{figure}
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 \cref{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 \cref{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.
% here are the unit sections, all following a common pattern
Hardware buses implemented in GEX are presented in this chapter. The description of each bus is accompanied by several examples of devices that can be interfaced with it. The reader is advised to consult the official specifications and particular devices' datasheets for additional details.
\section{UART and USART}\label{sec:theory-usart}
\section{UART and USART}\label{sec:theory_usart}
The \acrfull{USART} has a long history and is still in widespread use today. It is the protocol used in RS-232, which was once a common way of connecting modems, printers, mice and other devices to personal computers. RS-232 can be considered the ancestor of \gls{USB} in its widespread availability and use. \gls{UART} framing is also used in the industrial bus RS-485 and the automotive interconnect bus \gls{LIN}.
\caption[UART frame format]{\label{fig:uart-frame}\gls{USART} frame format in the 8-bit configuration with parity}
\caption[UART frame format]{\label{fig:uart_frame}\gls{USART} frame format in the 8-bit configuration with parity}
\end{figure}
\gls{UART} and \gls{USART} are two variants of the same interface. \gls{USART} includes a separate clock signal, while the \gls{UART} timing relies on a well-known clock speed and the bit clock is synchronized by start bits. \gls{USART} was historically used in modems to achieve higher bandwidth, but is now mostly obsolete.
\gls{USART}, as implemented by microcontrollers such as the STM32 family, is a two-wire full duplex interface that uses 3.3\,V or 5\,V logic levels. The data lines are in the high logical level when idle. A \gls{USART} frame, shown in \cref{fig:uart-frame}, starts by a start-bit (low level for the period of one bit) followed by \textit{n} data bits (typically eight), an optional parity bit, and a period of high level called a stop bit (or stop bits), dividing consecutive frames.
\gls{USART}, as implemented by microcontrollers such as the STM32 family, is a two-wire full duplex interface that uses 3.3\,V or 5\,V logic levels. The data lines are in the high logical level when idle. A \gls{USART} frame, shown in \cref{fig:uart_frame}, starts by a start-bit (low level for the period of one bit) followed by \textit{n} data bits (typically eight), an optional parity bit, and a period of high level called a stop bit (or stop bits), dividing consecutive frames.
RS-232 uses the \gls{UART} framing, but its levels are different: logical 1 is represented by negative voltages $-3$ to $-25$\,V and logical 0 uses the same range, but positive. To convert between RS-232 levels and \gls{TTL} (5\,V) levels, a level-shifting circuit such as the MAX232 can be used. In RS-232, the two data lines (Rx and Tx) are accompanied by \gls{RTS}, \gls{CTS}, and \gls{DTR}, which facilitate handshaking and hardware flow control. In practice, those additional signals are often unused or their function differs from their historical meaning; for instance, Arduino boards (using a USB-serial converter) use the \gls{DTR} line as a reset signal to automatically enter their bootloader for firmware flashing~\cite{arduinodtr}.
@ -27,23 +27,23 @@ RS-232 uses the \gls{UART} framing, but its levels are different: logical 1 is r
\item\textbf{MFRC522} -- \gls{NFC} MIFARE reader/writer \gls{IC} (also supports other interfaces)
\end{itemize}
\section{SPI}\label{sec:theory-spi}
\section{SPI}\label{sec:theory_spi}
\acrfull{SPI} is a point-to-point or multi-drop master-slave interface based on shift registers. The \gls{SPI} connection with multiple slave devices is depicted in \cref{fig:spi-multislave}. It uses at least 4 wires: \gls{SCK}, \gls{MOSI}, \gls{MISO} and \gls{SS}. \gls{SS} is often marked \gls{CSB} or \gls{NSS} to indicate that its active state is 0. Slave devices are addressed using their \gls{SS} input while the data connections are shared. A slave that is not addressed releases the \gls{MISO} line to a high impedance state so it does not interfere in ongoing communication.
\acrfull{SPI} is a point-to-point or multi-drop master-slave interface based on shift registers. The \gls{SPI} connection with multiple slave devices is depicted in \cref{fig:spi_multislave}. It uses at least 4 wires: \gls{SCK}, \gls{MOSI}, \gls{MISO} and \gls{SS}. \gls{SS} is often marked \gls{CSB} or \gls{NSS} to indicate that its active state is 0. Slave devices are addressed using their \gls{SS} input while the data connections are shared. A slave that is not addressed releases the \gls{MISO} line to a high impedance state so it does not interfere in ongoing communication.
\begin{figure}[h]
\centering
\includegraphics[scale=.9]{img/spi-timing.pdf}
\caption[SPI timing diagram]{\label{fig:spi-timing}SPI timing diagram explaining the CPOL and CPHA settings (shown on 3 data bits; a real message will use at least 8 bits)}
\caption[SPI timing diagram]{\label{fig:spi_timing}SPI timing diagram explaining the CPOL and CPHA settings (shown on 3 data bits; a real message will use at least 8 bits)}
\caption[SPI master with multiple slaves]{\label{fig:spi-multislave}A SPI bus with 1 master and 3 slaves, each enabled by its own Slave Select signal}
\caption[SPI master with multiple slaves]{\label{fig:spi_multislave}A SPI bus with 1 master and 3 slaves, each enabled by its own Slave Select signal}
\end{figure}
Transmission and reception on the \gls{SPI} bus happen simultaneously. A bus master asserts the \gls{SS} pin of a slave it wishes to address and then sends data on the \gls{MOSI} line while receiving a response on \gls{MISO}. The slave normally responds with 0x00 or a status register as the first byte of the response, before it can process the received command. A timing diagram is shown in \cref{fig:spi-timing}, including two configurable parameters: \gls{CPOL} and \gls{CPHA}.
Transmission and reception on the \gls{SPI} bus happen simultaneously. A bus master asserts the \gls{SS} pin of a slave it wishes to address and then sends data on the \gls{MOSI} line while receiving a response on \gls{MISO}. The slave normally responds with 0x00 or a status register as the first byte of the response, before it can process the received command. A timing diagram is shown in \cref{fig:spi_timing}, including two configurable parameters: \gls{CPOL} and \gls{CPHA}.
\gls{SPI} devices often provide a number of control, configuration and status registers that can be read and written by the bus master. The first byte of a command usually contains one bit that determines if it is a read or write access, and an address field selecting the target register. The slave then either stores the following \gls{MOSI} byte(s) into the register, or sends its content back on \gls{MISO} (or both simultaneously).
@ -64,14 +64,14 @@ Transmission and reception on the \gls{SPI} bus happen simultaneously. A bus mas
\acrfull{I2C} is a two-wire, open-drain bus that supports multi-master operation.
It uses two connections (plus \gls{GND}): \gls{SDA} and \gls{SCL}, both open-drain with a pull-up resistor.
The protocol was developed by Philips Semiconductor (now NXP Semiconductors), and its implementors were, until 2006, required to pay licensing fees, leading to the development of compatible implementations with different names, such as Atmel's \gls{TWI} or Dallas Semiconductor's ``Serial 2-wire Interface'' (e.g., used in the DS1307 \gls{RTC} chip). \gls{I2C} is a basis of the \gls{SMBus} and \gls{PMBus}, which add additional constraints and rules for a more robust operation.
The frame format is shown and explained in \cref{fig:i2c-frame}; more details may be found in the specification~\cite{i2c-spec} or application notes and datasheets offered by chip vendors, such as the white paper from Texas Instruments~\cite{understanding-i2c}. A frame starts with a start condition and stops with a stop condition, defined by an \gls{SDA} edge while the \gls{SCL} is high. The address and data bytes are acknowledged by the slave by sending a 0 on the open-drain \gls{SDA} line in the following clock cycle. A slave can terminate the transaction by sending 1 in place of the acknowledge bit. Slow slave devices may stop the master from sending more data by holding the SCL line low at the end of a byte, a feature called \textit{Clock Stretching}. As the bus is open-drain, the line cannot go high until all participants release it.
The frame format is shown and explained in \cref{fig:i2c_frame}; more details may be found in the specification~\cite{i2c-spec} or application notes and datasheets offered by chip vendors, such as the white paper from Texas Instruments~\cite{understanding-i2c}. A frame starts with a start condition and stops with a stop condition, defined by an \gls{SDA} edge while the \gls{SCL} is high. The address and data bytes are acknowledged by the slave by sending a 0 on the open-drain \gls{SDA} line in the following clock cycle. A slave can terminate the transaction by sending 1 in place of the acknowledge bit. Slow slave devices may stop the master from sending more data by holding the SCL line low at the end of a byte, a feature called \textit{Clock Stretching}. As the bus is open-drain, the line cannot go high until all participants release it.
Two addressing modes are defined: 7-bit and 10-bit. Due to the small address space, exacerbated by many devices implementing only the 7-bit addressing, collisions between different chips on a shared bus are common; many devices thus offer several pins to let the board designer choose a few bits of the address by connecting them to different logic levels.
@ -80,7 +80,7 @@ The bus supports multi-master operation, which leads to the problem of collision
\caption[\IIC message diagram]{\label{fig:i2c-frame}An \gls{I2C} message diagram (\textit{taken from the \gls{I2C} specification~\cite{i2c-spec}})}
\caption[\IIC message diagram]{\label{fig:i2c_frame}An \gls{I2C} message diagram (\textit{taken from the \gls{I2C} specification~\cite{i2c-spec}})}
\end{figure}
\subsection{Examples of Devices Using \texorpdfstring{\IIC}{I2C}}
@ -93,7 +93,7 @@ The bus supports multi-master operation, which leads to the problem of collision
\item The \gls{SCCB} used to configure camera modules is derived from \gls{I2C}
\end{itemize}
\section{1-Wire}\label{sec:theory-1wire}
\section{1-Wire}\label{sec:theory_1wire}
The 1-Wire bus, developed by Dallas Semiconductor (acquired by Maxim Integrated), uses a single, bi-directional data line, which can also power the slave devices in a \textit{parasitic mode}, reducing the number of required wires to just two (compare with 3 in \gls{I2C} and 5 in \gls{SPI}, all including \gls{GND}). The parasitic operation is possible thanks to the data line resting at a high logic level most of the time, charging an internal capacitor.
@ -104,7 +104,7 @@ The communication consists of short pulses sent by the master and (for bit readi
\begin{figure}[h]
\centering
\includegraphics[scale=1]{img/1w-connection.pdf}
\caption{\label{fig:1w-topology}1-Wire connection topology with four slave devices}
\caption{\label{fig:1w_topology}1-Wire connection topology with four slave devices}
\end{figure}
Devices are addressed by their unique 64-bit ID numbers called ROM codes or ROMs; they can be found by the bus master, with a cooperation from slaves, using a ROM Search algorithm. The search algorithm is explained in~\cite{ow-appnote}, including a possible implementation example. If only one device is connected, a wild card command Skip ROM can be used to address the device without a known ROM code.
@ -123,23 +123,23 @@ Since 1-Wire is a proprietary protocol, there is a much smaller choice of availa
\caption{\label{fig:1w-pulses}The 1-Wire data line pulse timing (by \textit{Dallas Semiconductor})}
\caption{\label{fig:1w_pulses}The 1-Wire data line pulse timing (by \textit{Dallas Semiconductor})}
\end{figure}
\fi
\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''. Additional technical details about the chips and their protocol may be found in the WS2812B datasheet~\cite{neopixel-ds}. These 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 \cref{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 \cref{fig:ws2812_dia}.
The NeoPixel timing is sensitive to pulse length accuracy; a deviation from the specified timing may cause the data to be misinterpreted by the drivers. Some ways to implement the timing use hardware timers or the \gls{I2S} peripheral. An easier method that does not require any additional hardware resources beyond the \gls{GPIO} pin is to implement the timing using delay loops in the firmware; care must be taken to disable interrupts in the sensitive parts of the timing; it may be advantageous to implement it in assembly for a tighter control.
In addition to communication buses, described in \cref{ch:hw_buses}, GEX implements several measurement and output functions that take advantage of the microcontroller's peripheral blocks, such as timers/counters and \gls{DAC}. The more complicated ones are described here; simpler functions, such as the raw \gls{GPIO} access, will be described later together with their control \gls{API}.
Applications like motor speed measurement and the reading of a \gls{VCO} or \gls{VCO}-based sensor's output demand a tool capable of measuring frequency. This can be done using a laboratory instrument such as the Agilent 53131A. A low-cost solution can be realized using a timer/counter peripheral of a microcontroller.
@ -10,13 +10,13 @@ Applications like motor speed measurement and the reading of a \gls{VCO} or \gls
Two basic methods to measure frequency exist~\cite{fcap-twotypes}, each with its advantages and drawbacks:
\begin{itemize}
\item The \textit{direct method} (\cref{fig:fcap-direct-dia}) is based on the definition of frequency as a number of cycles $n$ in a fixed-length time window $\tau$ (usually 1\,s); the frequency is then calculated as $f=n/\tau$.
\item The \textit{direct method} (\cref{fig:fcap_direct_dia}) is based on the definition of frequency as a number of cycles $n$ in a fixed-length time window $\tau$ (usually 1\,s); the frequency is then calculated as $f=n/\tau$.
One timer generates the time window and its output gates the input of another, configured as a pulse counter. At the end of the measurement window an interrupt is generated and we can read the pulse count from the counter's register.
The direct method has a resolution of 1\,Hz with a sampling window of 1\,s (only a whole number of pulses can me counted). The resolution can be increased by using a longer time window, provided the measured signal is stable enough to make averaging possible without distorting the result. Further increase of precision is possible through analog or digital interpolation~\cite{fcap-increasing}, a method used in some professional equipment.
\item The \textit{indirect} or \textit{reciprocal method} (\cref{fig:fcap-reci-dia}) measures one period $T$ as the time interval between two pulses and this is then converted to frequency as $f=1/T$.
\item The \textit{indirect} or \textit{reciprocal method} (\cref{fig:fcap_reci_dia}) measures one period $T$ as the time interval between two pulses and this is then converted to frequency as $f=1/T$.
This method needs only one timer/counter. Cycles of the system clock are counted for the duration of one period on the input pin (between two rising edges). If we additionally detect the falling edge in between, the counter's value gives us the duty cycle when related to the overall period length.
@ -29,28 +29,28 @@ Two basic methods to measure frequency exist~\cite{fcap-twotypes}, each with its
\begin{figure}[h]
\centering
\includegraphics[scale=1]{img/fcap-direct.pdf}
\caption{\label{fig:fcap-direct-dia}Direct frequency measurement method}
\caption{\label{fig:fcap_direct_dia}Direct frequency measurement method}
\caption{\label{fig:fcap-reci-dia}Reciprocal frequency measurement method}
\caption{\label{fig:fcap_reci_dia}Reciprocal frequency measurement method}
\end{figure}
Which method to use depends on the frequency we want to measure; the worst-case measurement errors of both methods, assuming an ideal 48\,MHz system clock, are plotted in \cref{fig:freqmethods-graph}. It can be seen that the reciprocal method leads in performance up to 7\,kHz where the direct method overtakes it. If a higher error is acceptable, the reciprocal method could be used also for higher frequencies to avoid a reconfiguration and to take advantage of its higher speed.
Which method to use depends on the frequency we want to measure; the worst-case measurement errors of both methods, assuming an ideal 48\,MHz system clock, are plotted in \cref{fig:freqmethods_graph}. It can be seen that the reciprocal method leads in performance up to 7\,kHz where the direct method overtakes it. If a higher error is acceptable, the reciprocal method could be used also for higher frequencies to avoid a reconfiguration and to take advantage of its higher speed.
\caption[Frequency measurement methods comparison]{\label{fig:freqmethods-graph}Worst-case error using the two frequency measurement methods with an ideal 48\,MHz timer clock. The crossing lies at 7\,kHz with an error of 0.015\,\%, or 1.05\,Hz.}
\caption[Frequency measurement methods comparison]{\label{fig:freqmethods_graph}Worst-case error using the two frequency measurement methods with an ideal 48\,MHz timer clock. The crossing lies at 7\,kHz with an error of 0.015\,\%, or 1.05\,Hz.}
\end{figure}
A good approach to a universal measurement, for cases where we do not know the expected frequency beforehand, could be to obtain an estimate using the direct method first, and if the frequency is below the worst-case error crossing point (here 7\,kHz, according to \cref{fig:freqmethods-graph}), to take a more precise measurement using the reciprocal method.
A good approach to a universal measurement, for cases where we do not know the expected frequency beforehand, could be to obtain an estimate using the direct method first, and if the frequency is below the worst-case error crossing point (here 7\,kHz, according to \cref{fig:freqmethods_graph}), to take a more precise measurement using the reciprocal method.
The system clock's frequency, which we use to measure pulse lengths and to gate the pulse counter, will be affected by tolerances of the used components, the layout of the \gls{PCB}, temperature effects etc., causing measurement errors. A higher accuracy could be achieved using a \gls{TCO}, or, in the direct method, with the synchronization pulse provided by a \gls{GPS} receiver to time the measurement interval.
\section{Analog Signal Acquisition}\label{sec:theory-adc}
\section{Analog Signal Acquisition}\label{sec:theory_adc}
A very common need in experiments involving the measurement of physical properties is the acquisition of analog signals, respective voltages. These can be roughly divided into \gls{DC} and \gls{AC} or time-changing signals. Analog signals are converted to digital values using \glspl{ADC}. Several principles of analog signal measurement exist with different cost, speed, resolution, and many other factors which determine their suitability for a particular application.
@ -59,10 +59,10 @@ A very common need in experiments involving the measurement of physical properti
\begin{figure}
\centering
\includegraphics[scale=1]{img/sar-adc.pdf}
\caption{\label{fig:adc-sar}A diagram of the SAR type ADC}
\caption{\label{fig:adc_sar}A diagram of the SAR type ADC}
\end{figure}
The \gls{ADC} type commonly available in microcontrollers, including our STM32F072, uses a \textit{successive approximation} method. It is called the \textit{SAR type \gls{ADC}}, after its main component, the \gls{SAR}. A diagram of this \gls{ADC} is shown in \cref{fig:adc-sar}.
The \gls{ADC} type commonly available in microcontrollers, including our STM32F072, uses a \textit{successive approximation} method. It is called the \textit{SAR type \gls{ADC}}, after its main component, the \gls{SAR}. A diagram of this \gls{ADC} is shown in \cref{fig:adc_sar}.
The \gls{SAR} type converter uses a \gls{DAC}, controlled by the value in the \gls{SAR}, which approximates the input voltage, bit by bit, following the algorithm described in~\cite{adc-sar} and outlined below:
@ -76,37 +76,37 @@ The \gls{SAR} type converter uses a \gls{DAC}, controlled by the value in the \g
A change of the input value would make this principle unreliable, which is why the input is buffered by a sample \& hold circuit. The holding capacitor is charged to the input voltage and maintains this level during the conversion. The duration for which the capacitor is connected to the input is called a \textit{sampling time}.
A waveform generator is a useful tool in many experiments and measurements. A sine stimulus is the basis of a lock-in amplifier; it can be used to measure impedance; with a frequency sweep, we can obtain the frequency response of an analog filter, etc. We can, of course, generate other waveforms, such as a triangle, ramp, or rectangle wave.
The \gls{DAC} peripheral can produce a \gls{DC} level on the output pin based on a control word. When we periodically change its digital input, it produces an analog waveform.
\subsection{Waveform Generation with DMA and a Timer}\label{sec:theory-dac-simple}
\subsection{Waveform Generation with DMA and a Timer}\label{sec:theory_dac_simple}
A straightforward, intuitive implementation of the waveform generator is illustrated in \cref{fig:wavegen-naive}. This approach has its advantages: it is simple and works autonomously, with no interrupt handling or interventions from the program. It could be implemented without the use of \gls{DMA} as well, using a loop periodically updating the \gls{DAC} values; of course, such approach is less flexible and we would run into problems with interrupt handling affecting the timing accuracy.
A straightforward, intuitive implementation of the waveform generator is illustrated in \cref{fig:wavegen_naive}. This approach has its advantages: it is simple and works autonomously, with no interrupt handling or interventions from the program. It could be implemented without the use of \gls{DMA} as well, using a loop periodically updating the \gls{DAC} values; of course, such approach is less flexible and we would run into problems with interrupt handling affecting the timing accuracy.
\caption[A simple implementation of the waveform generator]{\label{fig:wavegen-naive}A simple implementation of the waveform generator, using DMA and a look-up table}
\caption[A simple implementation of the waveform generator]{\label{fig:wavegen_naive}A simple implementation of the waveform generator, using DMA and a look-up table}
\end{figure}
The highest achievable output frequency largely depends on the size of our look-up table. For instance, assuming a timer frequency of 48\,MHz and a 8192-word table, holding one period of the waveform, the maximum frequency would be short of 6\,kHz, whereas if we shorten the table to just 1024 words, we can get almost 47\,kHz on the analog output. The downside of a shorter table is a lower resolution, which will appear as \gls{DC} plateaus or steps when observed with an oscilloscope, producing harmonic components similar to those of a square wave.
A major disadvantage of this simple generation method is given by the limitations of the used timer, which defines the output frequency. Its output trigger fires when the internal counter reaches a predefined value, after which the counting register is reset. The counting speed is derived from the system clock frequency $f_\mathrm{c}$ using a prescaler $P$ and the set maximum value $N$. Only output frequencies that can be exactly expressed as $f=f_\mathrm{c}/(P\cdot N \cdot\mathrm{TableSize})$ can be accurately produced. Still, this simple and efficient method may be used where fine tuning is not required to take advantage of its fully asynchronous operation.
\subsection{Direct Digital Synthesis}\label{sec:theory-dac-dds}
\subsection{Direct Digital Synthesis}\label{sec:theory_dac_dds}
There are situations where the simple waveform generation method is not sufficient, particularly when fine tuning, or on-line frequency and phase changes are required. Those are the strengths of \gls{DDS}, an advanced digital waveform generation method well explained in~\cite{all-about-dds}.
\begin{figure}[h]
\centering
\includegraphics[scale=1]{img/wavegen-dds.pdf}
\caption{\label{fig:wavegen-dds}A block diagram of a DDS-based waveform generator}
\caption{\label{fig:wavegen_dds}A block diagram of a DDS-based waveform generator}
\end{figure}
A diagram of a possible \gls{DDS} implementation in the STM32 firmware is shown in \cref{fig:wavegen-dds}. It is based on a \gls{NCO}. The \gls{NCO} consists of a \textit{phase accumulator} register and a \textit{tuning word} which is periodically added to it at a constant rate in a timer interrupt handler. The value of the tuning word determines the output waveform frequency. The look-up table must have a power-of-two length so that it can be addressed by the \textit{n} most significant bits of the phase accumulator. An additional control word could be added to this address to implement a phase offset for applications like a phase-shift modulation.
A diagram of a possible \gls{DDS} implementation in the STM32 firmware is shown in \cref{fig:wavegen_dds}. It is based on a \gls{NCO}. The \gls{NCO} consists of a \textit{phase accumulator} register and a \textit{tuning word} which is periodically added to it at a constant rate in a timer interrupt handler. The value of the tuning word determines the output waveform frequency. The look-up table must have a power-of-two length so that it can be addressed by the \textit{n} most significant bits of the phase accumulator. An additional control word could be added to this address to implement a phase offset for applications like a phase-shift modulation.
The output frequency is calculated as \(f_\mathrm{out}=\dfrac{M\cdot f_\mathrm{c}}{2^n}\), where $M$ is the tuning word, $n$ is the bit length of the phase accumulator, and $f_c$ is the frequency of the phase-updating interrupt. The number of bits used to address the look-up table does not affect the output frequency; the table can be as large as the storage space allows. A tuning word value exceeding the lower part of the phase accumulator (including bits which directly enter the look-up address) will cause some values from the table to be skipped. A smaller tuning word, conversely, makes some values appear at the output more than once. This can be observed as steps or flat areas on the output. When the tuning word does not evenly divide $2^n$, that is, the modulo is non-zero, we can also observe jitter.
@ -114,16 +114,16 @@ The output frequency is calculated as \(f_\mathrm{out} = \dfrac{M\cdot f_\mathrm
DDS may be implemented in hardware, including the look-up table, often together with the \gls{DAC} itself, which is then called a \textit{Complete \gls{DDS}}. That is the case of, e.g., the AD9833 from Analog Devices. As the software implementation depends on a periodic interrupt, it is often advantageous to use a component like this when we need higher output frequencies where the use of an interrupt is not possible. GEX can control an external waveform generator like the AD9833 using an \gls{SPI} port.
\section{Touch Sensing}\label{sec:theory-touch}
\section{Touch Sensing}\label{sec:theory_touch}
The STM32F072 microcontroller includes a \gls{TSC} peripheral block. This device is meant to be used in touch-based user interfaces, e.g., for kitchen appliances or toys. We include it in GEX to serve as a demonstration of capacitive touch sensing, and it could possibly be used for simple capacitive sensors as well, such as a water level measurement.
The \gls{TSC} requires a specific topology with a sampling capacitor connected close to the microcontroller pin, which may not be possible on a universal GEX module; for this reason, the touch sensing feature is best demonstrated on the STM32F072 Discovery development kit, which includes a 4-segment touch slider shown in \cref{fig:disco-touch}.
The \gls{TSC} requires a specific topology with a sampling capacitor connected close to the microcontroller pin, which may not be possible on a universal GEX module; for this reason, the touch sensing feature is best demonstrated on the STM32F072 Discovery development kit, which includes a 4-segment touch slider shown in \cref{fig:disco_touch}.
\caption{\label{fig:disco-touch}The touch slider on a STM32F072 Discovery board}
\caption{\label{fig:disco_touch}The touch slider on a STM32F072 Discovery board}
\end{figure}
The principle of capacitive touch sensing using the \gls{TSC} is well explained in the microcontroller's reference manual~\cite{f072-rm}, the \gls{TSC} product training materials~\cite{stm-tsc-training, stm-tsc-ppt} and application notes from ST Microelectronics~\cite{stm-tsc-an1, stm-tsc-an2, stm-tsc-an3, stm-tsc-an4}. A key part of the \gls{TSC} is a set of analog switches which can be combined to form several different signal paths between external pins, V$_\mathrm{DD}$, \gls{GND}, and an analog comparator. Two input pins are needed for every touch sensing channel: the sensing pad connects to one, the other is connected through a sampling capacitor (47\,nF on the Discovery board) to \gls{GND}.
@ -131,7 +131,7 @@ The principle of capacitive touch sensing using the \gls{TSC} is well explained
\begin{figure}[h]
\centering
\includegraphics[scale=1]{img/tsc-function.pdf}
\caption{\label{fig:tsc-schem}A simplified schematic of the touch sensing circuit}
\caption{\label{fig:tsc_schem}A simplified schematic of the touch sensing circuit}
\end{figure}
\noindent
@ -145,14 +145,14 @@ Capacitive sensing is a sequential process described in the following steps:
\end{enumerate}
\noindent
A real voltage waveform measured on the sensing pad using an oscilloscope is shown in \cref{fig:tsc-wfm}.
A real voltage waveform measured on the sensing pad using an oscilloscope is shown in \cref{fig:tsc_wfm}.
\caption[TSC operation oscilloscope screenshots]{\label{fig:tsc-wfm}A voltage waveform measured on the touch sensing pad. The bottom side of the envelope equals the sampling capacitor's voltage---this is the phase where both capacitors are connected. The detailed view (middle) shows the individual charging cycles. The bottom screenshot captures the entire waveform, left to continue until a timeout, after the analog comparator was disabled.}
\caption[TSC operation oscilloscope screenshots]{\label{fig:tsc_wfm}A voltage waveform measured on the touch sensing pad. The bottom side of the envelope equals the sampling capacitor's voltage---this is the phase where both capacitors are connected. The detailed view (middle) shows the individual charging cycles. The bottom screenshot captures the entire waveform, left to continue until a timeout, after the analog comparator was disabled.}
@ -16,9 +16,9 @@ Today, when we want to perform measurements using a digital sensor, the usual ro
The design and implementation of such a universal instrument is the object of this work. For technical reasons, such as naming the source code repositories, we need a name for the project; it shall be, hereafter, called \textbf{GEX}, a name originating from ``\textbf{G}PIO \textbf{Ex}pander''.
It has been a long-time desire of the author to create a universal instrument connecting low-level hardware to a computer, and, with this project, it is finally being realized. Several related projects approaching this problem from different angles can be found on the internet; some of these will be presented in \cref{sec:prior-art}.
It has been a long-time desire of the author to create a universal instrument connecting low-level hardware to a computer, and, with this project, it is finally being realized. Several related projects approaching this problem from different angles can be found on the internet; some of these will be presented in \cref{sec:prior_art}.
Our project is not meant to end with a tinkering tool that will be produced in a few prototypes and then forgotten. By creating an extensible, open-source platform, GEX can become the foundation for future projects which others can expand, re-use and adapt to their specific needs.
With the communication protocol clearly defined in \cref{sec:tinyframe,sec:units-overview}, respective \cref{sec:wireless} for the wireless gateway, the implementation of a client software is relatively straightforward. Two client libraries have been developed, in languages C and Python.
With the communication protocol clearly defined in \cref{sec:tinyframe,sec:units_overview}, respective \cref{sec:wireless} for the wireless gateway, the implementation of a client software is relatively straightforward. Two client libraries have been developed, in languages C and Python.
\section{General Library Structure}
@ -21,7 +21,7 @@ The structure of a GEX client library is in all cases similar:
The host side of the communication protocol described in \cref{sec:tinyframe} should be implemented as a part of the library. This includes the reading and writing of configuration files, unit list read-out, command payload building, and asynchronous event parsing.
Additional utilities may be defined on top of this basic protocol support for the command API of different GEX units, as described in \cref{sec:units-overview}. Those unit-specific ``drivers'' are available in the provided Python library.
Additional utilities may be defined on top of this basic protocol support for the command API of different GEX units, as described in \cref{sec:units_overview}. Those unit-specific ``drivers'' are available in the provided Python library.
\end{itemize}
\section{Python Library}
@ -153,7 +153,7 @@ This low-level library is intended for applications where the performance of the
GEX_DeInit(gex);
}
\end{ccode}
\caption{\label{lst:c_api_full} An example C program (GNU C99) controlling GEX using the low-level GEX library; this code has the same effect as the Python script shown in \cref{lst:py_api}, with payloads built following the command tables from \cref{sec:units-overview}.}
\caption{\label{lst:c_api_full} An example C program (GNU C99) controlling GEX using the low-level GEX library; this code has the same effect as the Python script shown in \cref{lst:py_api}, with payloads built following the command tables from \cref{sec:units_overview}.}
When adding a new digital sensor or a module to a hardware project, we want to test it first, learn how to properly communicate with it, and confirm its performance. Based on this evaluation we decide whether the module matches our expectations and learn how to properly connect it, which is needed for a successful \gls{PCB} layout.
@ -28,7 +28,7 @@ Generating an analog signal is possible using a \gls{PWM} or by a dedicated digi
\subsection{Logic Level Input and Output}
We have covered some more advanced features, but skipped the simplest feature: direct access to \gls{GPIO} pins. Considering the latencies of \gls{USB} and the \gls{PC}'s \gls{OS}, this cannot be used reliably for ``bit banging''; however, we can still accomplish a lot with just changing logic levels---e.g., to control character \glspl{LCD}, or emulate some interfaces that include a clock line, like \gls{SPI}. As mentioned in \cref{sec:uses-digital-ifaces}, many digital sensors and modules use plain \glspl{GPIO} in addition to the communication bus for out-of-band signaling or features like chip selection or reset.
We have covered some more advanced features, but skipped the simplest feature: direct access to \gls{GPIO} pins. Considering the latencies of \gls{USB} and the \gls{PC}'s \gls{OS}, this cannot be used reliably for ``bit banging''; however, we can still accomplish a lot with just changing logic levels---e.g., to control character \glspl{LCD}, or emulate some interfaces that include a clock line, like \gls{SPI}. As mentioned in \cref{sec:uses_digital_ifaces}, many digital sensors and modules use plain \glspl{GPIO} in addition to the communication bus for out-of-band signaling or features like chip selection or reset.
\subsection{Pulse Generation and Measurement}
@ -82,7 +82,7 @@ Summarizing the preceding discussion, we obtain the following list of features t
\section{Microcontroller Selection}
As discussed in \cref{sec:expected-outcome}, this project will be based on microcontrollers from the STM32 family. The STM32F072 model was selected for the initial hardware and firmware design due to its low cost, advanced peripherals, and the availability of development boards. The firmware can be ported to other \glspl{MCU} later (e.g., to STM32L072, STM32F103 or STM32F303).
As discussed in \cref{sec:expected_outcome}, this project will be based on microcontrollers from the STM32 family. The STM32F072 model was selected for the initial hardware and firmware design due to its low cost, advanced peripherals, and the availability of development boards. The firmware can be ported to other \glspl{MCU} later (e.g., to STM32L072, STM32F103 or STM32F303).
The STM32F072 is an \armcm device with 128\,KiB of flash memory, 16\,KiB of \gls{RAM} and running at 48\,MHz. It is equipped with a \gls{USB} Full Speed peripheral block, a 12-bit \gls{ADC} and \gls{DAC}, a number of general-purpose timers/counters, SPI, I$^2$C, and USART peripherals, among others. It supports crystal-less \gls{USB}, using the USB SOF packet for synchronization of the internal 48\,MHz RC oscillator; naturally, a real crystal resonator will provide better timing accuracy.
@ -92,7 +92,7 @@ To effectively utilize the time available for this work, only the STM32F072 firm
While the GEX firmware can be used with existing evaluation boards from ST Microelectronics (see \cref{fig:discovery} for an example of one such board), we wish to design and realize a few custom hardware prototypes that will be smaller and more convenient to use.
Three possible form factors are drawn in \cref{fig:ff-sketches}. The use of a common connector layout and pin assignments, here Arduino and Raspberry Pi, makes it possible to reuse add-on boards from those platforms. When we copy the physical form factor of another product, in this example the Raspberry Pi Zero, we can further take advantage of existing enclosures designed for it.
Three possible form factors are drawn in \cref{fig:ff_sketches}. The use of a common connector layout and pin assignments, here Arduino and Raspberry Pi, makes it possible to reuse add-on boards from those platforms. When we copy the physical form factor of another product, in this example the Raspberry Pi Zero, we can further take advantage of existing enclosures designed for it.
\begin{figure}[h]
\centering
@ -103,7 +103,7 @@ Three possible form factors are drawn in \cref{fig:ff-sketches}. The use of a co
@ -6,7 +6,7 @@ GEX can be controlled through a hardware \gls{UART}, the \gls{USB}, or over a wi
\vspace{-1em}
\centering
\includegraphics[scale=1]{img/tf-conceptual.pdf}
\caption{\label{fig:tf-conceptual}TinyFrame API}
\caption{\label{fig:tf_conceptual}TinyFrame API}
\end{wrapfigure}
GEX uses the \textit{TinyFrame}~\cite{tinyframerepo} framing library, developed, likewise, by the author, but kept as a separate project for easier re-use in different applications. The library implements frame building and parsing, including checksum calculation, and provides high-level \gls{API}.
@ -81,7 +81,7 @@ Message frames have the following structure (all little-endian):
\textit{Frame ID}, which could be better described as \textit{Transaction ID}, uniquely identifies each transaction. The most significant bit is set to a different value in each peer to avoid ID conflicts, and the rest of the ID field is incremented with each initiated transaction.
After sending a message that should receive a response, the peer registers an \textit{ID listener} with the ID of the sent message. A response reuses the original frame ID and when it is received, this listener is called to process it. ID listeners can also be used to receive multi-part messages re-using the original ID.
@ -91,7 +91,7 @@ Each message can be handled by only one listener, unless the listener explicitly
\section{Designated Frame Types}
\Cref{fig:tf-types} lists the frame types defined by GEX. It is divided into four logical sections: General, Bulk Read/Write, Unit Access, and Settings. The payloads belonging to those frame types will be outlined in the following sections.
\Cref{fig:tf_types} lists the frame types defined by GEX. It is divided into four logical sections: General, Bulk Read/Write, Unit Access, and Settings. The payloads belonging to those frame types will be outlined in the following sections.
\begin{table}[h]
\centering
@ -119,11 +119,11 @@ Each message can be handled by only one listener, unless the listener explicitly
0x23 & Persist Config &\textit{Write updated configuration to flash}\\
\bottomrule
\end{tabular}
\caption{\label{fig:tf-types}Frame types used by GEX}
\caption{\label{fig:tf_types}Frame types used by GEX}
\end{table}
\section{Bulk Read and Write Transactions}\label{sec:tf-bulk-rw}
\section{Bulk Read and Write Transactions}\label{sec:tf_bulk_rw}
The bulk read and write transactions are generic, multi-message exchanges which are used to transfer the INI configuration files. They could additionally be used by some future unit requiring to transfer a large amount of data (e.g., to read image data from a camera).
@ -131,12 +131,12 @@ The reason for splitting a long file into multiple messages, rather than sending
A read or write transaction can be aborted by a frame \CmdBulkAbort at any time, though aborting a write transaction may leave the configuration in a corrupted state. As hinted in the introduction of this chapter, a transaction is defined by sharing a common frame ID. Thus, all frames in a bulk transaction must have the same ID, otherwise the ID listeners would not be called for the subsequent messages.
\Cref{fig:bulk-rw} shows a diagram of the bulk read and write data flow.
\Cref{fig:bulk_rw} shows a diagram of the bulk read and write data flow.
\cfield{u8} command number, handled by the unit driver
\cfield{u8[]} command payload, handled by the unit driver; its size and content depend on the unit driver and the particular command number, as defined in \cref{sec:units-overview}
\cfield{u8[]} command payload, handled by the unit driver; its size and content depend on the unit driver and the particular command number, as defined in \cref{sec:units_overview}
\end{boxedpayload}
The most significant bit of the command byte (0x80) has a special meaning: when set, the message delivering routine responds with 0x00 (Success) after the command completes, unless an error occurred. That is used to get a confirmation that the message was delivered and the module operates correctly (as opposed to, e.g., a lock-up resulting in a watchdog reset). Requests which normally generate a response (e.g., reading a value from the unit) should not be sent with this flag, as that would produce two responses at once.
@ -239,7 +239,7 @@ Several unit types can produce asynchronous events, such as reporting a pin chan
\cfield{u8} unit callsign
\cfield{u8} report type, defined by the unit driver
\cfield{u64} event time (microseconds since power-on)
\cfield{u8[]} report payload; similar to requests, the payload structure depends on the unit driver and the particular report type, as defined in \cref{sec:units-overview}
\cfield{u8[]} report payload; similar to requests, the payload structure depends on the unit driver and the particular report type, as defined in \cref{sec:units_overview}
The 1-Wire unit implements the Dallas Semiconductor's 1-Wire protocol, most commonly used to interface smart thermometers (DS18x20). The protocol is explained in \cref{sec:theory-1wire}.
The 1-Wire unit implements the Dallas Semiconductor's 1-Wire protocol, most commonly used to interface smart thermometers (DS18x20). The protocol is explained in \cref{sec:theory_1wire}.
This unit implements the ROM Search algorithm that is used to find the ROM codes of all 1-Wire devices connected to the bus. The algorithm can find up to 32 devices in one run; more devices can be found by issuing the SEARCH\_CONTINUE command.
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 \cref{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.
The waveform mode implements direct digital synthesis (explained in \cref{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.
The frequency capture unit implements both the frequency measurement methods explained in \cref{sec:theory-fcap}: direct and reciprocal.
The frequency capture unit implements both the frequency measurement methods explained in \cref{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 optional averaging. Continuous mode does not 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. More on the \IIC bus can be found in \cref{sec:theory-i2c}.
The \gls{I2C} unit provides access to one of the microcontroller's \gls{I2C} peripherals. More on the \IIC bus can be found in \cref{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.
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 \cref{sec:theory-neo}) is implemented in software, therefore it is available on any \gls{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 \cref{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 (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{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 \cref{sec:theory-spi}.
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 \cref{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 to prevent a short circuit between slaves transmitting the opposite logical level.
The touch sensing unit provides an access to the \gls{TSC} peripheral, explained in \cref{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 touch sensing unit provides an access to the \gls{TSC} peripheral, explained in \cref{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. See \cref{sec:theory-usart} for more information about the interface.
The \gls{USART} unit provides access to one of the microcontroller's \gls{USART} peripherals. See \cref{sec:theory_usart} for more information about the interface.
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.
\caption[USB hierarchical structure]{\label{fig:usb-hierarchy}The hierarchical structure of the USB bus}
\caption[USB hierarchical structure]{\label{fig:usb_hierarchy}The hierarchical structure of the USB bus}
\end{figure}
\gls{USB} is a hierarchical bus with a single master (\textit{host}) and multiple slave devices. A \gls{USB} device that provides functionality to the host is called a \textit{function}~\cite{usb-function}.
@ -19,7 +19,7 @@ Communication between the host and a function is organized into virtual channels
\caption{\label{fig:usb-logical}The logical structure of USB}
\caption{\label{fig:usb_logical}The logical structure of USB}
\end{figure}
Endpoints can be either unidirectional or bidirectional; the direction from the host to a function is called OUT, the other direction (function to host) is called IN. A bidirectional endpoint is technically composed of IN and OUT endpoints with the same number. All transactions (both IN and OUT) are initiated by the host; functions have to wait for their turn. Endpoint 0 is bidirectional, always enabled, and serves as a \textit{control endpoint}. The host uses the control endpoint to read information about the device and configure it as needed.
@ -51,7 +51,7 @@ Each descriptor starts with a declaration of its length (in bytes), followed by
The topmost descriptor holds information about the entire function, including the vendor and product IDs which uniquely identifies the device model. It is followed by a Configuration descriptor, grouping a set of interfaces. More than one configuration may be present and available for the host to choose from; however, this is rarely used or needed. Each configuration descriptor is followed by one or more interface descriptors, each with its class-specific sub-descriptors and/or endpoint descriptors.
The descriptor table used by GEX is captured in \cref{fig:gex-descriptors} for illustration. The vendor and product IDs were obtained from the pid.codes repository~\cite{pidcodes} providing free product codes to open source projects. The official way of obtaining the unique code involves high recurring fees (\$4000 per annum) to the USB Implementers Forum, Inc. and is therefore not affordable for non-commercial use; alternatively, a product code may be obtained from some \gls{MCU} vendors if their product is used in the device.
The descriptor table used by GEX is captured in \cref{fig:gex_descriptors} for illustration. The vendor and product IDs were obtained from the pid.codes repository~\cite{pidcodes} providing free product codes to open source projects. The official way of obtaining the unique code involves high recurring fees (\$4000 per annum) to the USB Implementers Forum, Inc. and is therefore not affordable for non-commercial use; alternatively, a product code may be obtained from some \gls{MCU} vendors if their product is used in the device.
\newpage
\input{fig.gex-descriptors}
@ -62,12 +62,12 @@ The descriptor table used by GEX is captured in \cref{fig:gex-descriptors} for i
The \gls{USB} cable contains 4 conductors: V$_\mathrm{BUS}$ (+5\,V), D+, D--, and \gls{GND}. The data lines, D+ and D--, are also commonly labeled DP and DM. This differential pair should be routed in parallel on the \gls{PCB} and kept at the same length.
\gls{USB} versions that share the same connector are backward compatible. The desired bus speed is requested by the device using a 1.5\,k$\Omega$ pull-up resistor to 3.3\,V on one of the data lines: D+ pulled high for Full Speed (shown in \cref{fig:usb-pullup-fs}), D-- pulled high for Low Speed. The polarity of the differential signals is also inverted depending on the used speed, as the idle level changes. Some microcontrollers integrate the correct pull-up resistor inside the \gls{USB} peripheral block (including out STM32F072), removing the need for an external resistor.
\gls{USB} versions that share the same connector are backward compatible. The desired bus speed is requested by the device using a 1.5\,k$\Omega$ pull-up resistor to 3.3\,V on one of the data lines: D+ pulled high for Full Speed (shown in \cref{fig:usb_pullup_fs}), D-- pulled high for Low Speed. The polarity of the differential signals is also inverted depending on the used speed, as the idle level changes. Some microcontrollers integrate the correct pull-up resistor inside the \gls{USB} peripheral block (including out STM32F072), removing the need for an external resistor.
\begin{figure}[h]
\centering
\includegraphics[scale=1]{img/usb-resistors.pdf}
\caption[USB pull-ups]{\label{fig:usb-pullup-fs}Pull-up and pull-down resistors near the host and a Full Speed function, as prescribed by the USB specification rev. 2.0}
\caption[USB pull-ups]{\label{fig:usb_pullup_fs}Pull-up and pull-down resistors near the host and a Full Speed function, as prescribed by the USB specification rev. 2.0}
\end{figure}
When a function needs to be re-enumerated by the host, which causes a reload of the descriptor table and the re-attachment of software drivers, it can momentarily remove the pull-up resistor, which the host will interpret as if the device was disconnected. With an internal pull-up, this can be done by flipping a bit in a control register. An external resistor may be connected through a transistor controlled by a \gls{GPIO} pin. As discussed in~\cite{eev-gpio-pu}, a GPIO pin might be used to drive the pull-up directly, though this has not been verified by the author.
@ -76,7 +76,7 @@ The V$_\mathrm{BUS}$ line supplies power to \textit{bus-powered} devices. \texti
\noindent More details about the electrical and physical connection may be found in~\cite{usb-nutshell}, sections \textit{Connectors} through \textit{Power}.
\section{USB Classes}\label{sec:usb-classes}
\section{USB Classes}\label{sec:usb_classes}
This section explains the classes used in the GEX firmware. A list of all standard classes with a more detailed explanation can be found in~\cite{usb-class-list}.
@ -98,7 +98,7 @@ This command set lets the host read information about the attached storage devic
In order to emulate a mass storage device without having a physical storage medium, we need to generate and parse the file system on-the-fly as the host \gls{OS} tries to access it. This will be discussed in \cref{sec:fat16}.
@ -12,7 +12,7 @@ The Semtech SX1276~\cite{semtech-manual} and Nordic Semiconductor nRF24L01+ ~\ci
\section{Modulations Overview}
A brief overview of the different signal modulation techniques is presented here to aid the reader with understanding of \cref{fig:nrf-sx-comparison} and the rest of the chapter.
A brief overview of the different signal modulation techniques is presented here to aid the reader with understanding of \cref{fig:nrf_sx_comparison} and the rest of the chapter.
\subsection{On-Off Keying (OOK)}
@ -40,7 +40,7 @@ LoRa is a patented proprietary modulation developed by Semtech. It uses a direct
\section{Comparing SX1276 and nRF24L01+}
The two transceivers are compared in \cref{fig:nrf-sx-comparison}. It is apparent that each of them has its strengths and weaknesses, which will be discussed below.
The two transceivers are compared in \cref{fig:nrf_sx_comparison}. It is apparent that each of them has its strengths and weaknesses, which will be discussed below.
\begin{table}[h]
\centering
@ -63,7 +63,7 @@ The two transceivers are compared in \cref{fig:nrf-sx-comparison}. It is apparen
\textbf{Price}&\$7.3 &\$1.6 \\
\bottomrule
\end{tabulary}
\caption[Comparison of the SX1276 and nRF24L01+ wireless transceivers]{\label{fig:nrf-sx-comparison}Comparison of the SX1276 and nRF24L01+ wireless transceivers, using data from their datasheets (price in USD from DigiKey in a 10\,pcs. quantity, recorded on May 6th 2018)}
\caption[Comparison of the SX1276 and nRF24L01+ wireless transceivers]{\label{fig:nrf_sx_comparison}Comparison of the SX1276 and nRF24L01+ wireless transceivers, using data from their datasheets (price in USD from DigiKey in a 10\,pcs. quantity, recorded on May 6th 2018)}
\end{table}
SX1276 supports additional modulation modes, including a proprietary LoRa scheme with a frequency-hopping spread spectrum modulation that can be received at a distance up to 20\,km in ideal conditions. The long-range capability is reflected in a higher consumption during transmission. However, its consumption in receiver mode is slightly lower than that of the nRF24L01+.
@ -81,7 +81,7 @@ A separate device, the \textit{GEX wireless gateway}, was developed to provide t
\todo[inline]{TODO the above --remove/update/link to the hw chapter}