finished tagging acronyms

master
Ondřej Hruška 6 years ago
parent 84ffd33b19
commit 73129df46e
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 3
      ch.conclusion.tex
  2. 2
      ch.existing_solutions.tex
  3. 2
      ch.freertos.tex
  4. 179
      ch.fw_structure.tex
  5. 14
      ch.gex_units.tex
  6. 50
      ch.hw_buses.tex
  7. 64
      ch.hw_functions.tex
  8. 32
      ch.tinyframe.tex
  9. 20
      ch.unit.1wire.tex
  10. 8
      ch.unit.adc.tex
  11. 8
      ch.unit.dac.tex
  12. 2
      ch.unit.do.tex
  13. 8
      ch.unit.i2c.tex
  14. 8
      ch.unit.npx.tex
  15. 4
      ch.unit.pwmdim.tex
  16. 20
      ch.unit.sipo.tex
  17. 6
      ch.unit.spi.tex
  18. 2
      ch.unit.touch.tex
  19. 4
      ch.unit.usart.tex
  20. 43
      ch.wireless.tex
  21. 3
      ctuth-templates.tex
  22. 4
      document_config.tex
  23. 40
      thesis.acronyms.tex
  24. BIN
      thesis.pdf
  25. 3
      thesis.tex

@ -0,0 +1,3 @@
\chapter{Conclusion}
bla

@ -36,7 +36,7 @@ The Raspberry Pi could be used for the same quick evaluations or experiments we
%http://dangerousprototypes.com/blog/about/
% Dangerous Prototypes and manufactured by Seeed Studio\todo{link}
Bus Pirate, a project by Ian Lesnet, is a USB-attached device providing access to hardware interfaces like \gls{SPI}, \gls{I2C}, \gls{USART} and 1-Wire, as well as frequency measurement and direct pin access. The board aims to make it easy for users to familiarize themselves with new chips and modules; it also provides a range of programming interfaces for flashing microcontroller firmwares and memories. It communicates with the PC using a FTDI USB-serial bridge.
Bus Pirate, a project by Ian Lesnet, is a USB-attached device providing access to hardware interfaces like \gls{SPI}, \gls{I2C}, \gls{USART} and 1-Wire, as well as frequency measurement and direct pin access. The board aims to make it easy for users to familiarize themselves with new chips and modules; it also provides a range of programming interfaces for flashing microcontroller firmwares and memories. It communicates with the \gls{PC} using a FTDI USB-serial bridge.
Bus Pirate is open source and is, in its scope, similar to GEX. It can be scripted and controlled from the PC, connects to USB and provides a wide selection of hardware interfaces.

@ -20,7 +20,7 @@ Tasks can be in one of four states: Suspended, Ready, Blocked, Running. The Susp
Task switching occurs periodically in a SysTick interrupt, usually every 1\,ms. After one tick of run time, the running task is paused (enters Ready state), or continues to run if no higher priority task is available. If a high priority task waits for an object and this is made available in an interrupt, the previously running task is paused and the waiting task is resumed immediately (enters the Running state).
Only a subset of the FreeRTOS \ref{API} can be accessed from interrupt routines, for example it's not possible to use the delay function or wait for an object with a timeout, because the SysTick interrupt which increments the tick counter has the lowest priority and couldn't run. This is by design to prevent unexpected context switching in nested interrupts.
Only a subset of the FreeRTOS \gls{API} can be accessed from interrupt routines, for example it's not possible to use the delay function or wait for an object with a timeout, because the SysTick interrupt which increments the tick counter has the lowest priority and couldn't run. This is by design to prevent unexpected context switching in nested interrupts.
FreeRTOS uses a \textit{priority inheritance} mechanism to prevent situations where a high priority task waits for an object held by a lower priority task (called \textit{priority inversion}). The blocking task's priority is temporarily raised to the level of the blocked high priority task so it can finish faster and release the held object. Its priority is then degraded back to the original value. When the lower priority task itself is blocked, the same process can be repeated.

@ -2,23 +2,82 @@
GEX is designed to be modular and easy to extend. It's composed of a set of functional blocks, sometimes available in more than one instance, which can be configured by the user to fit their application needs. The firmware is built around a \textit{core framework} which provides services to the functional blocks, such as a settings storage, resource allocation, message delivery and periodic updates.
In this chapter, we will focus on the general function of the GEX module and will look at the services provided by the core framework. Individual functional blocks and the control API will be described in the following chapters.\todo{references}
In this chapter, we will focus on the general function of the GEX module and will look at the services provided by the core framework. Individual functional blocks and the control \gls{API} will be described in the following chapters.\todo{references}
\textit{A writing style note:} This and the following parts were written after implementing and evaluating the first hardware prototype and its firmware, therefore rather than describing the development process, it tends to talk about the completed solution and the decisions taken.
\iffalse
This and the following parts were written after implementing and evaluating the first hardware prototype and its firmware, therefore rather than describing the development process, it tends to talk about the completed solution and the decisions taken.
\fi
\section{User's View of GEX}
Before going into implementation details, we'll have a look at GEX from the outside, how an end user will see it. This should give the reader some context to better orient themselves in the following sections and chapters investigating the internal structure of the firmware and the communication protocol.
The GEX firmware can be flashed to a STM32 Nucleo or Discovery board or a custom PCB. It's equipped with a USB connector to connect to the host PC. GEX loads its configuration from the non-volatile memory, configures its peripherals, sets up the function blocks and enables the selected communication interface(s). When USB is connected to the board, the PC enumerates it and either recognizes the communication interface as CDC/ACM (Virtual serial port), or leaves it without a software driver attached, to be accessed directly as raw USB endpoints. This can be configured. The user can now access the functional blocks using the client library and the serial protocol, as well as modify the configuration files.
The GEX firmware can be flashed to a STM32 Nucleo or Discovery board or a custom \gls{PCB}. It's equipped with a \gls{USB} connector to connect to the host \gls{PC}. GEX loads its configuration from the non-volatile memory, configures its peripherals, sets up the function blocks and enables the selected communication interface(s). When \gls{USB} is connected to the board, the \gls{PC} enumerates it and either recognizes the communication interface as \gls{CDCACM} (Virtual serial port), or leaves it without a software driver attached, to be accessed directly as raw \gls{USB} endpoints. This can be configured. The user can now access the functional blocks using the client library and the serial protocol, as well as modify the configuration files.
The board is equipped with a button or a jumper labeled LOCK. When the button is pressed or the jumper removed, the Mass Storage USB interface is enabled. For the user this means a new disk will be detected by their PC's operating system that they can open in a file manager. This disk provides read and write access to configuration INI files and other files with useful information, like a list of supported features and available hardware resources. The user now edits a configuration file and saves it back to the disk. GEX processes the new content, tries to apply the changes and generates an updated version of the file that includes error messages if there was a problem. For the PC OS to recognize this change, the Mass Storage device momentarily reports that the media is unavailable to force the OS to reload it. This is a similar mechanism to what happens when a memory card is removed from a reader. Now the user must reload the file in their editor, inspect the updated content and perform any changes needed. The settings, when applied successfully, should now be available to test using the communication interface. When everything is to the user's satisfaction, the updated settings are committed to the device's non-volatile memory by pressing the LOCK button again, or replacing the jumper.
The board is equipped with a button or a jumper labeled LOCK. When the button is pressed or the jumper removed, the Mass Storage \gls{USB} interface is enabled. For the user this means a new disk will be detected by their \gls{PC}'s operating system that they can open in a file manager. This disk provides read and write access to configuration INI files and other files with useful information, like a list of supported features and available hardware resources. The user now edits a configuration file and saves it back to the disk. GEX processes the new content, tries to apply the changes and generates an updated version of the file that includes error messages if there was a problem. For the \gls{PC} \gls{OS} to recognize this change, the Mass Storage device momentarily reports that the media is unavailable to force the \gls{OS} to reload it. This is a similar mechanism to what happens when a memory card is removed from a reader. Now the user must reload the file in their editor, inspect the updated content and perform any changes needed. The settings, when applied successfully, should now be available to test using the communication interface. When everything is to the user's satisfaction, the updated settings are committed to the device's non-volatile memory by pressing the LOCK button again, or replacing the jumper.
For boards without a USB re-enumeration capability (notably with older microcontrollers like the STM32F103) that use a jumper, this must be removed before plugging the board to the host USB so that the Mass Storage is enabled immediately at start-up and a re-enumeration is not needed.
For boards without a \gls{USB} re-enumeration capability (notably with older microcontrollers like the STM32F103) that use a jumper, this must be removed before plugging the board to the host \gls{USB} so that the Mass Storage is enabled immediately at start-up and a re-enumeration is not needed.
In the case when a wireless communication module is installed on the PCB and GEX is configured to use it, this will be used as a fallback when the USB peripheral does not receive an address (get enumerated) within a short time after start-up. The wireless link works in the same way as any other communication interface: it can be used to read and modify the configuration files and to access the functional blocks. To use it, the user needs to connect a wireless gateway module to their host PC and use the radio link instead of a USB cable. The gateway could support more than once GEX board at once.
In the case when a wireless communication module is installed on the \gls{PCB} and GEX is configured to use it, this will be used as a fallback when the \gls{USB} peripheral does not receive an address (get enumerated) within a short time after start-up. The wireless link works in the same way as any other communication interface: it can be used to read and modify the configuration files and to access the functional blocks. To use it, the user needs to connect a wireless gateway module to their host \gls{PC} and use the radio link instead of a \gls{USB} cable. The gateway could support more than once GEX board at once.
Now that GEX is connected and configured, the user can start using it. This involves writing a program in C or Python that uses the GEX client library, using the Python library from MATLAB, or controlling GEX using a GUI front-end built on those libraries. The configuration can be stored in the module, but it's also possible to temporarily (or permanently) replace it using the communication API. This way the settings can be loaded automatically when the user's program starts.
Now that GEX is connected and configured, the user can start using it. This involves writing a program in C or Python that uses the GEX client library, using the Python library from MATLAB, or controlling GEX using a \gls{GUI} front-end built on those libraries. The configuration can be stored in the module, but it's also possible to temporarily (or permanently) replace it using the communication \gls{API}. This way the settings can be loaded automatically when the user's program starts.
\section{Functional Blocks} \label{sec:units-function}
GEX's user-facing functions, also called functional blocks or \textit{units}, are implemented in \textit{unit drivers}. Those are independent modules in the firmware that the user can enable and configure using the GEX configuration files. In principle, there can be multiple instances of each unit type. However, we are limited by hardware constraints: there may be only one \gls{ADC} peripheral, two \gls{SPI} ports and so on. The mutually exclusive assignment of resources to units is handled by the \textit{resource registry} (\ref{sec:res-allocation}).
Each unit is defined by a section in the configuration file \verb|UNITS.INI|. It is given a name and a \textit{callsign}, which is a number that serves as an address for message delivery. A unit is internally represented by a data object with the following structure:
\begin{itemize}
\item Name
\item Callsign
\item Configuration parameters loaded from the unit settings
\item State variables updated at run-time by user commands or internal functions
\item A reference to the unit driver
\end{itemize}
The unit driver handles commands sent from the host \gls{PC}, initializes and de-initializes the unit based on its settings, and implements other aspects of the unit's function, such as periodic updates and interrupt handling. Unit drivers may expose public \gls{API} functions to make it possible to control the unit from a different driver, allowing the creation of "macro units".
\section{Source Code Layout}
\begin{wrapfigure}[20]{r}{0.4\textwidth}
\scriptsize\vspace{-3em}
\begin{verbatim}
├── build
   ├── firmware.bin
   └── firmware.dfu
├── Drivers
   ├── CMSIS
     └── Device / ST / STM32F0xx
   └── STM32F0xx_HAL_Driver
├── Middlewares / Third_Party / FreeRTOS
├── Src
│ └── main.c
├── User
│ ├── USB / STM32_USB_Device_Library
│ │   ├── Class
│ │     ├── CDC
│ │     ├── MSC
│ │     └── MSC_CDC
│ │   └── Core
│ ├── platform
│ │   ├── plat_compat.h
│ │   └── platform.c
│ ├── units
│ │   ├── adc
│ │   ├── digital_out
│ │   ...
│ ├── FreeRTOSConfig.h
│ └── gex.mk
└── Makefile
\end{verbatim}
\vspace{-1em}
\cprotect\caption{\label{fig:repo-structure} The general structure of the source code repository}
\end{wrapfigure}
Looking at the source code repository (fig. \ref{fig:repo-structure}), at the root we'll find device specific driver libraries and 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.c| includes platform-specific headers and defines e.g. which pin to use for a status \gls{LED} or the LOCK button.
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}.
\section{Functions of the Core Framework}
@ -29,16 +88,16 @@ The core framework forms the skeleton of the firmware and usually doesn't need a
\item Hardware resource allocation (\ref{sec:res-allocation})
\item Settings storage and loading (\ref{sec:settings-storage})
\item Functional block (\textit{units}) initialization (\ref{sec:units-function})
\item The communication port with different back-ends: USB, UART, wireless (\ref{sec:com-ports})
\item The communication port with different back-ends: \gls{USB}, \gls{UART}, wireless (\ref{sec:com-ports})
\item Message sending and delivery (\ref{sec:message_passing})
\item Interrupt management and routing to functional blocks (\ref{sec:irq-routing})
\item Virtual mass storage for configuration file editing
\end{itemize}
When the firmware needs to be ported to a different STM32 microcontroller, the core framework is relatively straightforward to adapt and the whole process can be accomplished in a few hours. The time consuming part is modifying the functional blocks to work correctly with the new device's hardware.
When the firmware needs to be ported to a different STM32 microcontroller, the core framework is relatively straightforward to adapt and the whole process can be accomplished in a few hours. The time consuming part is modifying the functional blocks to work correctly with the new device's hardware.
\section{Resource Allocation} \label{sec:res-allocation}
\subsection{Resource Allocation} \label{sec:res-allocation}
\begin{figure}[h]
\centering
@ -46,14 +105,14 @@ When the firmware needs to be ported to a different STM32 microcontroller, the c
\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 (SPI, I2C, UART\textellipsis), DMA channels. If two units tried to control the same pin, the results would be unpredictable; similarly, with a multiple access to a serial port, the output would be a mix of the data streams and completely useless.
The microcontroller provides a number of hardware resources that require exclusive access: GPIO pins, peripheral blocks (\gls{SPI}, \gls{I2C}, \gls{UART}\textellipsis), \gls{DMA} channels. If two units tried to control the same pin, the results would be unpredictable; similarly, with a multiple access to a serial port, the output would be a mix of the data streams and completely useless.
To prevent a multiple access, the firmware includes a \textit{resource registry} (fig. \ref{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 (i.e. a GPIO pin PD1 may be disabled if not present on the microcontroller's package).
To prevent a multiple access, the firmware includes a \textit{resource registry} (fig. \ref{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 (i.e. a 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 LED, the LOCK button, USB pins, the communication UART, the pins and an SPI peripheral connecting the wireless module, pins used for the crystal oscillator, and the timer/counter which provides the system timebase.
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.
\section{Settings Storage} \label{sec:settings-storage}
\subsection{Settings Storage} \label{sec:settings-storage}
\begin{figure}[h]
\centering
@ -65,102 +124,44 @@ The system and unit settings are written, in a binary form, into designated page
As the settings persist after a firmware update, it's important to maintain backwards compatibility. This is achieved by prefixing the unit's settings by a version number. When the settings are loaded by a new version of the firmware, it first checks the version and decides whether to use the old or new format. When the settings are next changed, the new format will be used.
The INI files, which can be edited through the communication API or using a text editor with the virtual mass storage, are parsed and generated on demand and are never stored in the Flash or RAM, other than in short temporary buffers. The INI parser processes the byte stream on-the-fly as it is received, and a similar method is used to build a INI file from the configured units and system settings.
\section{Functional Blocks} \label{sec:units-function}
GEX's user-facing functions, also called functional blocks or \textit{units}, are implemented in \textit{unit drivers}. Those are independent modules in the firmware that the user can enable and configure using the GEX configuration files. In principle, there can be multiple instances of each unit type. However, we are limited by hardware constraints: there may be only one ADC peripheral, two SPI ports and so on. The mutually exclusive assignment of resources to units is handled by the \textit{resource registry} (\ref{sec:res-allocation}).
Each unit is defined by a section in the configuration file \verb|UNITS.INI|. It is given a name and a \textit{callsign}, which is a number that serves as an address for message delivery. A unit is internally represented by a data object with the following structure:
\begin{itemize}
\item Name
\item Callsign
\item Configuration parameters loaded from the unit settings
\item State variables updated at run-time by user commands or internal functions
\item A reference to the unit driver
\end{itemize}
The unit driver handles commands sent from the host PC, initializes and de-initializes the unit based on its settings, and implements other aspects of the unit's function, such as periodic updates and interrupt handling. Unit drivers may expose public API functions to make it possible to control the unit from a different driver, allowing the creation of "macro units".
\section{Source Code Layout}
\begin{wrapfigure}[20]{r}{0.4\textwidth}
\scriptsize\vspace{-3em}
\begin{verbatim}
├── build
   ├── firmware.bin
   └── firmware.dfu
├── Drivers
   ├── CMSIS
     └── Device / ST / STM32F0xx
   └── STM32F0xx_HAL_Driver
├── Middlewares / Third_Party / FreeRTOS
├── Src
│ └── main.c
├── User
│ ├── USB / STM32_USB_Device_Library
│ │   ├── Class
│ │     ├── CDC
│ │     ├── MSC
│ │     └── MSC_CDC
│ │   └── Core
│ ├── platform
│ │   ├── plat_compat.h
│ │   └── platform.c
│ ├── units
│ │   ├── adc
│ │   ├── digital_out
│ │   ...
│ ├── FreeRTOSConfig.h
│ └── gex.mk
└── Makefile
\end{verbatim}
\vspace{-1em}
\cprotect\caption{\label{fig:repo-structure} The general structure of the source code repository}
\end{wrapfigure}
Looking at the source code repository (fig. \ref{fig:repo-structure}), at the root we'll find device specific driver libraries and 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.c| includes platform-specific headers and defines e.g. which pin to use for a status LED or the LOCK button.
The 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 USB.
The INI files, which can be edited through the communication \gls{API} or using a text editor with the virtual mass storage, are parsed and generated on demand and are never stored in the Flash or \gls{RAM}, other than in short temporary buffers. The INI parser processes the byte stream on-the-fly as it is received, and a similar method is used to build a INI file from the configured units and system settings.
\section{Communication Ports} \label{sec:com-ports}
\subsection{Communication Ports} \label{sec:com-ports}
The firmware supports three different communication ports: hardware UART, USB (virtual serial port), and a wireless connection. Each interface is configured and accessed in a different way, but for the rest of the firmware (and for the PC-side application) they all appear as a full duplex serial port. To use interfaces other than USB, the user must configure those in the system settings (a file \verb|SYSTEM.INI| on the configuration disk).
The firmware supports three different communication ports: hardware \gls{UART}, \gls{USB} (virtual serial port), and a wireless connection. Each interface is configured and accessed in a different way, but for the rest of the firmware (and for the \gls{PC}-side application) they all appear as a full duplex serial port. To use interfaces other than \gls{USB}, the user must configure those in the system settings (a file \verb|SYSTEM.INI| on the configuration disk).
At start-up, the firmware enables the USB peripheral, configures the device library and waits for enumeration by the host PC. When not enumerated, it concludes the USB cable is not connected, and tries some other interface. The UART interface can't be tested as reliably, but it's possible to measure the voltage on the Rx pin. When idle, a UART Rx line should be high (here 3.3\,V). The wireless module, when connected using SPI, can be detected by reading a register with a known value and comparing those.
At start-up, the firmware enables the \gls{USB} peripheral, configures the device library and waits for enumeration by the host \gls{PC}. When not enumerated, it concludes the \gls{USB} cable is not connected, and tries some other interface. The \gls{UART} interface can't be tested as reliably, but it's possible to measure the voltage on the Rx pin. When idle, a \gls{UART} Rx line should be high (here 3.3\,V). The wireless module, when connected using \gls{SPI}, can be detected by reading a register with a known value and comparing those.
\subsection{USB Connection}
\subsubsection{USB Connection}
GEX uses vid:pid \verb|1209:4c60| and the wireless gateway \verb|1209:4c61|. The USB interface uses the CDC/ACM USB class (\ref{sec:cdc-acm}) and consists of two bulk endpoints with a payload size of up to 64 bytes.
GEX uses vid:pid \verb|1209:4c60| and the wireless gateway \verb|1209:4c61|. The \gls{USB} interface uses the \gls{CDCACM} \gls{USB} class (\ref{sec:cdc-acm}) and consists of two bulk endpoints with a payload size of up to 64 bytes.
\subsection{Communication UART}
\subsubsection{Communication UART}
The parameters of the communication UART (such as the baud rate) are defined in \verb|SYSTEM.INI|. It's mapped to pins PA2 and PA3; this is useful with STM32 Nucleo boards that don't include a User USB connector, but provide a USB-serial bridge using the on-board ST-Link programmer, connected to those pins.
The parameters of the communication \gls{UART} (such as the baud rate) are defined in \verb|SYSTEM.INI|. It's mapped to pins PA2 and PA3; this is useful with STM32 Nucleo boards that don't include a User \gls{USB} connector, but provide a \gls{USB}-serial bridge using the on-board ST-Link programmer, connected to those pins.
This is identical to the USB connection from the PC application's side, except a physical UART is necessarily slower and does not natively support flow control. The use of the Xon and Xoff software flow control is not practical with binary messages that could include those bytes by accident, and the ST-Link USB-serial adapter does not implement hadware flow control.
This is identical to the \gls{USB} connection from the \gls{PC} application's side, except a physical \gls{UART} is necessarily slower and does not natively support flow control. The use of the Xon and Xoff software flow control is not practical with binary messages that could include those bytes by accident, and the ST-Link \gls{USB}-serial adapter does not implement hadware flow control.
\subsection{Wireless Connection}
\subsubsection{Wireless Connection}
The wireless connection uses an on-board communication module and a separate device, a wireless gateway, that connects to the PC. The wireless gateway is interfaced differently from the GEX board itself, but it also shows as a virtual serial port on the host PC. This is required to allow communicating with the gateway itself through the CDC/ACM interface in addition to addressing the end devices.
The wireless connection uses an on-board communication module and a separate device, a wireless gateway, that connects to the \gls{PC}. The wireless gateway is interfaced differently from the GEX board itself, but it also shows as a virtual serial port on the host \gls{PC}. This is required to allow communicating with the gateway itself through the \gls{CDCACM} interface in addition to addressing the end devices.
This interface will be explained in more detail in chapter \ref{sec:wireless}.
\section{Message Passing} \label{sec:message_passing}
\subsection{Message Passing} \label{sec:message_passing}
One of the key functions of the core framework is to deliver messages from the host PC to the right units. This functionality resides above the framing protocol, which will be described in chapter \ref{sec:tinyframe}.
One of the key functions of the core framework is to deliver messages from the host \gls{PC} to the right units. This functionality resides above the framing protocol, which will be described in chapter \ref{sec:tinyframe}.
A message that is not a response in a multi-part session (this is handled by the framing library) is identified by its Type field. Two main groups of messages exist: \textit{system messages} and \textit{unit messages}. System messages can access the INI files, query a list of the available units, restart the module etc. Unit messages are addressed to a particular unit by their callsign (see \ref{sec:units-function}), and their payload format is defined by the unit driver. The framework reads the message type, then the callsign byte, and tries to find a matching unit in the unit list. If no unit with the callsign is found, an error response is sent back, otherwise the unit driver is given the message to handle it as required.
The framework provides one more messaging service to the units: event reporting. An asynchronous event, such as an external interrupt, an ADC trigger or an UART data reception needs to be reported to the host. This message is annotated by the unit callsign so the user application knows it's origin.
The framework provides one more messaging service to the units: event reporting. An asynchronous event, such as an external interrupt, an \gls{ADC} trigger or an \gls{UART} data reception needs to be reported to the host. This message is annotated by the unit callsign so the user application knows it's origin.
\section{Interrupt Routing} \label{sec:irq-routing}
\subsection{Interrupt Routing} \label{sec:irq-routing}
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.
In the Cortex-M0-based STM32F072, used in the initial GEX prototypes, the interrupt handlers table, defining which routine is called for which interrupt, is stored in the program memory and can't be changed at run-time. This is a complication for the modular structure of GEX where different unit drivers may use the same peripheral, and we would want to dynamically assign the interrupt handlers based on the active configuration. Let's have a look at an interrupt handler, in this case handling four different DMA channels, as is common in STM32 microcontrollers:
In the Cortex-M0-based STM32F072, used in the initial GEX prototypes, the interrupt handlers table, defining which routine is called for which interrupt, is stored in the program memory and can't be changed at run-time. This is a complication for the modular structure of GEX where different unit drivers may use the same peripheral, and we would want to dynamically assign the interrupt handlers based on the active configuration. Let's have a look at an interrupt handler, in this case handling four different \gls{DMA} channels, as is common in STM32 microcontrollers:
\begin{minted}{c}
void DMA1_Channel4_5_6_7_IRQHandler(void)
@ -172,7 +173,7 @@ void DMA1_Channel4_5_6_7_IRQHandler(void)
}
\end{minted}
It is evident that multiple units might need to use the same interrupt handler, even at the same time, since each 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 deinitializes a unit, it removes all interrupt handlers it used, freeing the redirection table slots for other use.
It is evident that multiple units might need to use the same interrupt 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 deinitializes a unit, it removes all interrupt handlers it used, freeing the redirection table slots for other use.

@ -1,19 +1,21 @@
\chapter{Units Overview and API}
\chapter{Units Overview, Commands and Events Description}
This chapter describes all functional blocks (units) implemented in GEX at the time of publication of this work. Each unit supports a different set of binary commands and events. The term "unit" will be used 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 at the time of publication of this work. Each unit supports a different set of binary commands and events. The term ``unit'' will be used 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. When the command's request 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 if the command succeeds.
\todo[inline]{link to the chapter about tinyframe as it's important for context here}
\todo[inline]{also some diagram here}
Each unit's description will be accompanied by a corresponding snippet from the configuration file, and a list of supported commands and events. When the command's request 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 if the command succeeds.
\section{Naming Conventions and Common Principles}
\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''.
\subsection{Packed Pin Access}
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.
\begin{figure}[h]
\centering
@ -21,7 +23,7 @@ Several units facilitate an access to a group of GPIO pins, such as the digital
\caption{\label{fig:pin-packing}Pin packing}
\end{figure}
For instance, we could only have the pins 0, 1, 12--15 available on a 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.
% here are the unit sections, all following a common pattern

@ -15,13 +15,13 @@ The \gls{USART} has a long history and is still in widespread use today. It is t
\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 frame, pictured in figure \ref{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, usually between one and two bits long.
RS232 uses the \gls{UART} framing, but its logic 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 RS232 levels and \gls{TTL} (5\,V) levels, a level-shifting circuit such as the MAX232 can be used. In RS232, 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; 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.
\subsection{Examples of Devices Using UART}
\begin{itemize}
\item \textbf{MH-Z19B} - \gls{NDIR} CO$_2$ concentration sensor
\item \textbf{MH-Z19B} - \gls{NDIR} CO$_2$ concentration sensor
\item \textbf{NEO-M8} - uBlox \gls{GPS} module
\item \textbf{ESP8266} with AT firmware - a WiFi module
\item \textbf{MFRC522} - \gls{NFC} MIFARE reader/writer \gls{IC} (also supports other interfaces)
@ -29,11 +29,11 @@ RS232 uses the \gls{UART} framing, but its logic levels are different: logical 1
\section{SPI} \label{sec:theory-spi}
SPI (Serial Peripheral Interface) is a point-to-point or multi-drop master-slave interface based on shift registers. The SPI connection with multiple slave devices is depicted in figure \ref{fig:spi-multislave}. It uses at least 4 wires: SCK (Serial Clock), MOSI (Master Out Slave In), MISO (Master In Slave Out) and SS (Slave Select). SS is often marked CSB (Chip Select Bar) or NSS (Negated Slave Select) to indicate it's active low. Slave devices are addressed using their Slave Select input while the data connections are shared. A slave that's not addressed releases the MISO line to a high impedance state so it doesn't interfere in ongoing communication.
SPI (Serial Peripheral Interface) 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 figure \ref{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 it's active low. Slave devices are addressed using their \gls{SS} input while the data connections are shared. A slave that's not addressed releases the \gls{MISO} line to a high impedance state so it doesn't interfere in ongoing communication.
Transmission and reception on the SPI bus happen simultaneously. A bus master asserts the SS pin of a slave it wishes to address and then sends data on the MOSI line while receiving a response on MISO. It's customary that the slave responds with zeros or a status byte as the first byte of the response.
Transmission and reception on the \gls{SPI} bus happen simultaneously. A bus master asserts the 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}. It's customary that the slave responds with zeros or a status byte as the first byte of the response.
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's a read or write access, and an address field selecting the target register.
\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's a read or write access, and an address field selecting the target register.
\begin{figure}[h]
\centering
@ -52,41 +52,41 @@ SPI devices often provide a number of control, configuration and status register
\item \textbf{BME680} - air quality sensor
\item \textbf{ENC28J60} - Ethernet controller
\item \textbf{L6470} - intelligent stepper motor driver
\item \textbf{AD9833} - DDS-based DAC / waveform generator (MOSI only)
\item \textbf{ADE7912} - triple $\Sigma$-$\Delta$ ADC for power metering applications
\item \textbf{AD9833} - waveform generator (\gls{MOSI} only)
\item \textbf{ADE7912} - triple $\Sigma$-$\Delta$ \gls{ADC} for power metering applications
\item \textbf{SD cards}
\item SPI-interfaced EEPROMs and Flash memories
\item SPI-interfaced EEPROM and Flash memories
\end{itemize}
\section{I2C} \label{sec:theory-i2c}
\section{I\textsuperscript{2}C} \label{sec:theory-i2c}
I2C is a two-wire (SDA--\textit{Serial Data}, SCL--\textit{Serial Clock}), open-drain bus that supports multi-master operation. The protocol was developed by Philips Semiconductor (now NXP Semiconductors) and until 2006 implementors were required to pay licensing fees, leading to the development of compatible implementations with different names, such as Atmel's Two Wire Interface (TWI) or Dallas Semiconductor's "Serial 2-wire Interface" (e.g. used in the DS1307 RTC chip). I2C is the basis of the SMBus and PMBus protocols which add additional constraints and rules for a more robust operation.
\gls{I2C} is a two-wire (\gls{SDA}, \gls{SCL}), open-drain bus that supports multi-master operation. The protocol was developed by Philips Semiconductor (now NXP Semiconductors) and until 2006 implementors were 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 the basis of the \gls{SMBus} and \gls{PMBus} protocols which add additional constraints and rules for a more robust operation.
I2C uses two addressing modes: 7-bit and 10-bit. Due to the small address space, exacerbated by many devices implementing only the 7-bit addressing, collisions between chips from different manufacturers 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. I2C allows slow slave devices to stop the master from sending more data by holding the SCL line low at the end of a byte. As the bus is open-drain, the line can't go high until all participants release it. This function is called \textit{Clock Stretching}.
\gls{I2C} uses two addressing modes: 7-bit and 10-bit. Due to the small address space, exacerbated by many devices implementing only the 7-bit addressing, collisions between chips from different manufacturers 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. \gls{I2C} allows slow slave devices to stop the master from sending more data by holding the SCL line low at the end of a byte. As the bus is open-drain, the line can't go high until all participants release it. This function is called \textit{Clock Stretching}.
\begin{figure}[h]
\centering
\includegraphics[width=.9\textwidth] {img/i2c-frame.png}
\caption[I2C message diagram]{\label{fig:i2c-frame}An I2C message diagram. The frame starts with a start condition and stops with a stop condition, defined by an SDA edge while SCL is high. The address and data bytes are acknowledged by the slave by sending a 0 on the open-drain SDA line in the following clock cycle. A slave can terminate the transaction by sending 1 in place of the acknowledge bit. (\textit{Diagram taken from the I2C specification UM10204 by NXP Semiconductors})}
\caption[I2C message diagram]{\label{fig:i2c-frame}An \gls{I2C}message diagram. The 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. (\textit{Diagram taken from the \gls{I2C} specification UM10204 by NXP Semiconductors})}
\end{figure}
The bus supports multi-master operation, which leads to the problem of collisions. Multi-master capable devices must implement a bus arbitration scheme as specified by the I2C standard. This feature is not often used in intelligent sensors and modules; the most common topology is multi-drop single-master, similar to SPI, with the advantage of using only two pins on the microcontroller.
The bus supports multi-master operation, which leads to the problem of collisions. Multi-master capable devices must implement a bus arbitration scheme as specified by the \gls{I2C} standard. This feature is not often used in intelligent sensors and modules; the most common topology is multi-drop single-master, similar to \gls{SPI}, with the advantage of using only two pins on the microcontroller.
\subsection{Examples of Devices Using I2C}
\begin{itemize}
\item \textbf{APDS-9960} - ambient light, proximity and gesture sensor
\item \textbf{L3GD20}, \textbf{BMP280}, \textbf{BME680} - listed as SPI devices, those also support I2C
\item \textbf{DS1307} - RTC; I2C is not mentioned in the entire datasheet, presumably to avoid paying license fees, but it is fully compatible
\item \textbf{IS31FL3730} - LED matrix driver
\item Cameras with an SCCB port can be accessed through I2C
\item \textbf{L3GD20}, \textbf{BMP280}, \textbf{BME680} - listed as \gls{SPI} devices, those also support \gls{I2C}
\item \textbf{DS1307} - \gls{RTC}; \gls{I2C} is not mentioned in the entire datasheet, presumably to avoid paying license fees, but it is fully compatible
\item \textbf{IS31FL3730} - \gls{LED} matrix driver
\item Cameras with an SCCB port can be accessed through \gls{I2C}
\end{itemize}
\section{1-Wire} \label{sec:theory-1wire}
The 1-Wire bus, developed by Dallas Semiconductor, uses a single bi-directional data line which can also power the slave devices, reducing the number of required wires to just two (compare with 3 in I2C and 5 in SPI, all including GND).
The 1-Wire bus, developed by Dallas Semiconductor, uses a single bi-directional data line which can also power the slave devices, reducing the number of required wires to just two (compare with 3 in \gls{I2C} and 5 in \gls{SPI}, all including \gls{GND}).
1-Wire is open-drain and the communication consists of short pulses sent by the master and (for bit reading) the line continuing to be held low by the slave. The pulse timing (fig. \ref{fig:1w-pulses}) defines if it's a read or write operation and what bit value it carries. A transaction is started by a 480us long "reset" pulse send by master and ended by a 1-byte CRC checksum.
1-Wire is open-drain and the communication consists of short pulses sent by the master and (for bit reading) the line continuing to be held low by the slave. The pulse timing (fig. \ref{fig:1w-pulses}) defines if it's a read or write operation and what bit value it carries. A transaction is started by a 480us long "reset" pulse send by master and ended by a 1-byte \gls{CRC} code.
\begin{figure}[h]
\centering
@ -94,7 +94,7 @@ The 1-Wire bus, developed by Dallas Semiconductor, uses a single bi-directional
\caption{\label{fig:1w-topology}1-Wire topology (by \textit{Dallas Semiconductor})}
\end{figure}
1-Wire is a master-slave multi-drop bus. Devices are addressed by their unique 64-bit ID numbers (called ROMs); those IDs are found by the bus master with the cooperation from slaves using a ROM search protocol. If only one device is connected, a special command set can be used to skip addressing.
1-Wire is a master-slave multi-drop bus. Devices are addressed by their unique 64-bit ID numbers called ROMs; ROMs are found by the bus master with the cooperation from slaves using a ROM Search protocol. If only one device is connected, a wildcard command can be used to skip addressing.
\subsection{Examples of Devices Using 1-Wire}
@ -111,16 +111,16 @@ Since 1-Wire is a proprietary protocol, there is a much smaller choice of availa
\centering
\includegraphics[width=.85\textwidth] {img/1w-rw.png}
\includegraphics[width=.85\textwidth] {img/1w-reset.png}
\caption{\label{fig:1w-pulses}The 1-Wire DIO pulse timing (by \textit{Dallas Semiconductor})}
\caption{\label{fig:1w-pulses}The 1-Wire data line pulse timing (by \textit{Dallas Semiconductor})}
\end{figure}
\section{NeoPixel} \label{sec:theory-neo}
NeoPixel is a marketing name of the \textbf{WS2811}, \textbf{WS2812} and compatible intelligent LED drivers that is commonly used in "addressable LED strips". Those chips include the control logic, PWM drivers and usually the LED diodes all in one miniature package.
NeoPixel is a marketing name of the \textbf{WS2811}, \textbf{WS2812} and compatible intelligent \gls{LED} drivers that is commonly used in "addressable \gls{LED} strips". Those chips include the control logic, PWM drivers and usually the \gls{LED} diodes all in one miniature package.
The NeoPixel protocol is unidirectional, using only one data pin. The LED drivers are chained together. Ones and zeros are encoded by a pulse length on the data pin; after loading the color data to the LED string, a longer "reset" pulse is issued by the bus master and the set colors are displayed. The timing diagram and constraints are shown 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 a pulse length on the data pin; after loading the color data to the \gls{LED} string, a longer "reset" pulse is issued by the bus master and the set colors are displayed. The timing diagram and constraints are shown in table \ref{fig:ws2812-dia}.
The NeoPixel timing is very sensitive to pulse length accuracy. Reliable ways to implement it use DMA with a hardware timer, or a I2S peripheral. An easier method that does not use any additional hardware resources is implementing the protocol as delay loops in the firmware; care must be taken to disable interrupts in the sensitive parts of the timing.
The NeoPixel timing is very sensitive to pulse length accuracy. Reliable ways to implement it use \gls{DMA} with a hardware timer, or a \gls{I2S} peripheral. An easier method that does not use any additional hardware resources is implementing the protocol as delay loops in the firmware; care must be taken to disable interrupts in the sensitive parts of the timing.
\begin{figure}[h]
\centering
@ -140,7 +140,7 @@ The NeoPixel timing is very sensitive to pulse length accuracy. Reliable ways to
1 & Low level & $0.8\,\mu\mathrm{s}\pm150\mathrm{ns}$ \\
-- & Reset pulse (low) & $>50\,\mu\mathrm{s}$ \\
\bottomrule
\end{tabular}
\end{tabular}
% \includegraphics[width=.4\textwidth] {img/neo-diagram.png}
% \includegraphics[width=\textwidth] {img/neo-lengths.png}
\caption{\label{fig:ws2812-dia}NeoPixel pulse timing, according to the datasheet}

@ -1,28 +1,28 @@
\chapter{Non-communication Hardware Functions}
In addition to communication buses, described in chapter \ref{ch:hw_buses}, GEX implements several measurement and output functions that take advantage of the microcontroller's peripheral blocks, such as timers/counters and DAC. The more complicated ones are described here; simpler functions, such as the raw GPIO access, will be described later together with their control API.
In addition to communication buses, described in chapter \ref{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}.
\section{Frequency Measurement} \label{sec:theory-fcap}
Applications like motor speed measurement and the reading of a VCO (voltage-controlled-oscillator) or 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 is to use a timer/counter peripheral of a microcontroller, such as the STM32F072 used in GEX.
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 is to use a timer/counter peripheral of a microcontroller, such as the STM32F072 used in GEX.
Two basic methods to measure frequency exist, each with it's advantages and drawbacks:
\begin{itemize}
\item The \textit{direct method} (fig. \ref{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.
\item The \textit{direct method} (fig. \ref{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.
\item The \textit{indirect} or \textit{reciprocal method} (fig. \ref{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} (fig. \ref{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.
Te reciprocal method's resolution depends on the counter's clock speed; if driven at 48\,MHz, the tick period is 20.83\,ns, which defines the granularity of our time measurement. It is common to measure several pulses and average the obtained values to further increase the precision.
Te reciprocal method's resolution depends on the counter's clock speed; if driven at 48\,MHz, the tick period is 20.83\,ns, which defines the granularity of our time measurement. It is common to measure several pulses and average the obtained values to further increase the precision.
We can easily achieve a sub-hertz resolution with this method, but its performance degrades at high frequencies where the time measurement precision becomes insufficient. The input frequency range can be extended using a hardware prescaller\footnote{\textit{Prescaller} is a divider implemented as part of the timer/counter peripheral block that can be optionally enabled and configured to a desired division factor.}, which is also applicable to the direct method, should the measurement of frequencies outside the counter's supported range be required. A duty cycle measurement available in this method can be used to read the output of sensors that use a pulse-width modulation.
\end{itemize}
\begin{figure}[h]
@ -47,13 +47,13 @@ Which method to use depends on the frequency we want to measure; the worst-case
A good approach to a universal measurement, when we don't know the expected frequency beforehand, could be to first obtain an estimate using the direct method, and if the frequency is below the worst-case error crossing point (here 7\,kHz), 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 PCB, temperature effects etc., causing measurement errors. A higher accuracy could be achieved using a temperature-compensated oscillator (TCO), or, in the direct method, by using the synchronization pulse provided by a GPS receiver to time the measurement interval.
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}
A very common need in experiments involving the measurement of physical properties is the acquisition of analog signals, respective voltages. Those can be roughly divided into DC and time-changing signals. Analog signals are converted to digital values using ADCs (Analog to Digital Converters). Several principles of analog signal measurement exist with different cost, speed, resolution, and many other factors which determine their suitability for a particular application.
A very common need in experiments involving the measurement of physical properties is the acquisition of analog signals, respective voltages. Those 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.
DC signals can be measured by taking several samples and calculating their average value; In the presence of a 50\,Hz or 60\,Hz mains interference, its advisable to spread those samples over the 20\,ms (resp. 16.7\,ms) time of one period so that the interfering waveform cancels out. Time-changing signals can be captured by taking isochronous samples at a frequency conforming to the Nyquist theorem, that is, at least twice that of the measured signal. In practice, a frequency several times higher is preferred for a more accurate capture.
\gls{DC} signals can be measured by taking several samples and calculating their average value; In the presence of a 50\,Hz or 60\,Hz mains interference, its advisable to spread those samples over the 20\,ms (resp. 16.7\,ms) time of one period so that the interfering waveform cancels out. Time-changing signals can be captured by taking isochronous samples at a frequency conforming to the Nyquist theorem, that is, at least twice that of the measured signal. In practice, a frequency several times higher is preferred for a more accurate capture.
\begin{figure}
\centering
@ -61,17 +61,17 @@ DC signals can be measured by taking several samples and calculating their avera
\caption{\label{fig:adc-sar}A diagram of the SAR type ADC}
\end{figure}
The ADC type commonly available in microcontrollers, including our STM32F072, uses a \textit{successive approximation} method (it is often called the SAR type ADC, after its main component, the \textit{successive approximation register}). Its diagram is shown in figure \ref{fig:adc-sar}.
The \gls{ADC} type commonly available in microcontrollers, including our STM32F072, uses a \textit{successive approximation} method. It is often called the SAR type \gls{ADC}, after its main component, the \gls{SAR}. A diagram of this type of \gls{ADC} is shown in figure \ref{fig:adc-sar}.
The SAR type converter uses a DAC (Digital to Analog Converter) which approximates the input voltage, bit by bit, starting from the MSB, following the algorithm outlined below.
The \gls{SAR} type converter uses a \gls{DAC} which approximates the input voltage, bit by bit, starting from the MSB, following the algorithm outlined below.
\begin{enumerate}
\item The SAR is cleared to all zeros.
\item The DAC generates an approximation voltage.
\item The \gls{SAR} is cleared to all zeros.
\item The \gls{DAC} generates an approximation voltage.
\item Its output is compared with the sampled input, and the comparator's output is stored as the active bit in the approximation register.
\item The approximation continues with step 2 and the following (less significant) bit.
\item When all bits of the data word were found, an interrupt request is generated and the application program can read it from the SAR.
\end{enumerate}
\item When all bits of the data word were found, an interrupt request is generated and the application program can read it from the \gls{SAR}.
\end{enumerate}
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}.
@ -79,11 +79,11 @@ A change of the input value would make this principle unreliable, which is why t
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 DAC peripheral can produce a DC level on the output pin based on a control word. When we periodically change its digital input, it produces an analog waveform.
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}
A straightforward implementation of the waveform generator is illustrated in figure \ref{fig:wavegen-naive}. This approach has its advantages: it's simple and works entirely in the background, with no interrupt handling required. It could even be implemented entirely in software, using a loop periodically updating the DAC values, of course such approach is less flexible and we would run into problems with asynchronous interrupts.
A straightforward implementation of the waveform generator is illustrated in figure \ref{fig:wavegen-naive}. This approach has its advantages: it's simple and works entirely in the background, with no interrupt handling required. It could even be implemented entirely in software, using a loop periodically updating the \gls{DAC} values, of course such approach is less flexible and we would run into problems with asynchronous interrupts.
\begin{figure}[h]
\centering
@ -91,13 +91,13 @@ A straightforward implementation of the waveform generator is illustrated in fig
\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 DC plateaus or steps when observed with an oscilloscope, producing harmonic components similar to those of a square wave.
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 pre-defined value, after which the counting register is reset. The counting speed is derived from the system clock frequency $f_\mathrm{c}$ using a prescaller $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}
There are situations where the simple waveform generation method is not sufficient, particularly when a fine tuning or on-line frequency and phase changes are required. Those are the strengths of a signal generation method called \textit{Direct Digital Synthesis} (DDS).
There are situations where the simple waveform generation method is not sufficient, particularly when a fine tuning or on-line frequency and phase changes are required. Those are the strengths of a signal generation method called \gls{DDS}.
\begin{figure}[h]
\centering
@ -105,17 +105,17 @@ There are situations where the simple waveform generation method is not sufficie
\caption{\label{fig:wavegen-dds}A block diagram of a DDS-based waveform generator}
\end{figure}
A diagram of a possible DDS implementation in the STM32 firmware is shown in figure \ref{fig:wavegen-dds}. It is based on a \textit{numerically controlled oscillator} (NCO). 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 figure \ref{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 on 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.
\subsubsection{DDS Implemented in Hardware}
DDS may be implemented in hardware, including the look-up table, often together with the DAC itself, which is then called a \textit{Complete DDS}. That is the case of e.g. AD9833 from Analog Devices. As the software implementation depends on a periodic interrupt, it's 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 SPI port.
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. AD9833 from Analog Devices. As the software implementation depends on a periodic interrupt, it's 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}
The used microcontroller, STM32F072, includes a touch sensing controller (TSC) peripheral block. It can be accessed from GEX as a demonstration of capacitive touch sensing, and could possibly be used for simple touch sensors as well, such as measuring the level of water in a tank.
The used microcontroller, STM32F072, includes a \gls{TSC} peripheral block. It can be accessed from GEX as a demonstration of capacitive touch sensing, and could possibly be used for simple touch sensors as well, such as measuring the level of water in a tank.
The 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 figure \ref{fig:disco-touch}.
@ -125,12 +125,12 @@ The TSC requires a specific topology with a sampling capacitor connected close t
\caption{\label{fig:disco-touch}The touch slider on a STM32F072 Discovery board}
\end{figure}
The principle of capacitive touch sensing is well explained in the microcontroller's reference manual XXX\todo{ref}. A key part of the TSC is a set of analog switches, which can be combined to form several different signal paths between the external pins, Vdd, 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 GND.
The principle of capacitive touch sensing is well explained in the microcontroller's reference manual XXX\todo{ref}. A key part of the \gls{TSC} is a set of analog switches, which can be combined to form several different signal paths between the external pins, Vdd, \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}.
Capacitive sensing is a sequential process described in the following steps:
\begin{enumerate}
\item The sensing capacitor is discharged by connecting its free end to GND.
\item The sensing capacitor is discharged by connecting its free end to \gls{GND}.
\item The sensing pad is connected to Vdd and, acting as a capacitor, charged to 3.3\,V. It stores a small amount of charge, depending on its capacitance---this is the variable property we are trying to measure.
\item The free terminals of the two capacitors (the sensing pad and the sampling capacitor) are connected together and their voltages reach an equilibrium as a portion of the stored charge leaves the sensing pad and flows into the bigger capacitor.
\item The steps (2) and (3) are repeated the sampling capacitor's voltage exceeds a fixed threshold (set to a half of the supply voltage). The number of cycles needed to charge the sampling capacitor corresponds to the capacitance of the sensing pad.
@ -141,7 +141,7 @@ Capacitive sensing is a sequential process described in the following steps:
\includegraphics[width=.9\textwidth] {img/tsc-wfm-bw.png} \\
\vspace{5mm}
\includegraphics[width=.9\textwidth] {img/tsc-wfm2-bw.png}
\caption{\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 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 after the analog comparator was disabled.}
\end{figure}

@ -1,8 +1,8 @@
\chapter{Communication Protocol} \label{sec:tinyframe}
GEX can be controlled through a hardware UART, the USB or over a wireless link. To minimize the firmware complexity, all the three connection methods are handled by the same protocol stack and are interchangeable.
GEX can be controlled through a hardware \gls{UART}, the \gls{USB} or over a wireless link. To minimize the firmware complexity, all the three connection methods are handled by the same protocol stack and are functionally interchangeable.
The communication is organized in transactions. A transaction consists of one or more messages going in either direction. Messages can be stand-alone, or chained with a response or a follow-up message using the transaction ID. Both peers, GEX and the client application running on the PC, are equal in the communication: either side can independently initiate a transaction at any time.
The communication is organized in transactions. A transaction consists of one or more messages going in either direction. Messages can be stand-alone, or chained with a response or a follow-up message using the transaction ID. Both peers, GEX and the client application running on the host PC, are equal in the communication: either side can independently initiate a transaction at any time.
GEX uses a framing library \textit{TinyFrame}, 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, checksum calculation and a system of message listeners.
@ -14,19 +14,19 @@ Message frames have the following structure:
\centering
%\hspace{-1.5em}
\begin{tabular}{rccccccc}
\toprule
\toprule
\multicolumn{1}{c|}{} &
\multicolumn{5}{c}{Header}&
\multicolumn{2}{|c}{Body} \\
\midrule
%
\textit{Field} &
\textbf{SOF} &
\textbf{Frame ID} &
\makecell{ \Gape{\textbf{Payload}} \\ \Gape{\textbf{Length}} } &
\makecell{ \textbf{Frame} \\ \textbf{type} } &
\makecell{ \textbf{Header} \\ \textbf{checksum} } &
\textbf{Payload} &
\textit{Field} &
\textbf{SOF} &
\textbf{Frame ID} &
\makecell{ \Gape{\textbf{Payload}} \\ \Gape{\textbf{Length}} } &
\makecell{ \textbf{Frame} \\ \textbf{type} } &
\makecell{ \textbf{Header} \\ \textbf{checksum} } &
\textbf{Payload} &
\makecell{ \textbf{Payload} \\ \textbf{checksum} } \\
%
\midrule
@ -51,11 +51,11 @@ The field widths shown here are those used in GEX; TinyFrame is flexible and the
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.
\textit{Frame type} describes the payload and does not have any prescribed format; the values are defined by application (here, GEX). A \textit{type listener} may be registered to handle all incoming messages with a given frame type. It works in a similar way to an ID listener and has a lower priority.
\textit{Frame type} describes the payload and does not have any prescribed format; the values are defined by application (here, GEX). A \textit{type listener} may be registered to handle all incoming messages with a given frame type. It works in a similar way to an ID listener and has a lower priority.
Each message can be handled by only one listener, unless it explicitly requests the message to be passed on to a lower priority one. Messages unhandled by any listener are given to a default listener, which can e.g. write an error to a debug log.
\section{Designated Frame Types in GEX}
\section{Designated Frame Types}
The following table lists all frame types used by GEX. It is divided into four logical sections: General, Bulk Read/Write, Unit Access, and Settings.
@ -74,10 +74,10 @@ The following table lists all frame types used by GEX. It is divided into four l
0x05 & Bulk Write Offer & \textit{An offer to receive a bulk write transaction} \\
0x06 & Bulk Data & \textit{Used for both reading and writing} \\
0x07 & Bulk End & \textit{Marks the last "Bulk Data" frame} \\
0x08 & Bulk Abort & \textit{} \\
0x08 & Bulk Abort & \textit{} \\
\midrule
0x10 & Unit Request & \textit{Request from PC to a unit} \\
0x11 & Unit Report & \textit{Spontaneous event generated by a unit} \\
0x10 & Unit Request & \textit{Request to a unit} \\
0x11 & Unit Report & \textit{Spontaneous event generated by a unit} \\
\midrule
0x20 & List Units & \textit{Read a list of all instantiated units} \\
0x21 & INI Read & \textit{Request a bulk read transaction of an INI file} \\
@ -92,7 +92,7 @@ The following table lists all frame types used by GEX. It is divided into four l
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).
The reason for splitting a long file into multiple messages, rather than sending it all in one, lies in the hardware limitations of the platform, specifically its small amount of RAM (the STM32F072 has only 16\,kB). A message cannot be processed until its payload checksum is received and verified; however, the configuration file can have several kilobytes, owning to the numerous explanatory comments, which would require a prohibitively large data buffer. The chunked transaction could, additionally, be extended to support message re-transmission on timeout without sending the entire file again.
The reason for splitting a long file into multiple messages, rather than sending it all in one, lies in the hardware limitations of the platform, specifically its small amount of \gls{RAM} (the STM32F072 has only 16\,kB). A message cannot be processed until its payload checksum is received and verified; however, the configuration file can have several kilobytes, owning to the numerous explanatory comments, which would require a prohibitively large data buffer. The chunked transaction could, additionally, be extended to support message re-transmission on timeout without sending the entire file again.
A read or write transaction can be aborted by a frame 0x08 (Bulk Abort) 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 won't be called for the subsequent messages, and the transaction will time out.

@ -2,9 +2,9 @@
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 section \ref{sec:theory-1wire}.
This unit implements the ROM Search algorithm that is used to find the unique IDs 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.
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.
Devices are addressed using their 64-bit (8-byte) identifiers. When only one device is connected, the value 0 may be used and the addressing will be skipped. Its ID may be recovered using the READ\_ADDR command or by the search algorithm.
Devices are addressed using their ROM codes, unique 64-bit (8-byte) identifiers that work as addresses. When only one device is connected, the value 0 may be used instead and the addressing will be skipped. Its ROM code may be recovered using the READ\_ADDR command or by the search algorithm.
\subsection{1-Wire Configuration}
@ -29,41 +29,41 @@ parasitic=N
Start the search algorithm.
& \begin{cmdresp}
\cfield{u8} should continue (0, 1)
\cfield{u64[]} device addresses
\cfield{u64[]} ROM codes
\end{cmdresp} \\
2 & \cname{SEARCH\_ALARM}
Start the search algorithm, finding only devices in an alarm state.
& \begin{cmdresp}
\cfield{u8} should continue (0, 1)
\cfield{u64[]} device addresses
\cfield{u64[]} ROM codes
\end{cmdresp} \\
3 & \cname{SEARCH\_CONTINUE}
Continue a previously started search
& \begin{cmdresp}
\cfield{u8} should continue (0, 1)
\cfield{u64[]} device addresses
\cfield{u64[]} ROM codes
\end{cmdresp} \\
4 & \cname{READ\_ADDR}
Read a device address (single device only)
& \begin{cmdresp}
\cfield{u64} device address
\cfield{u64} ROM code
\end{cmdresp} \\
10 & \cname{WRITE}
Write bytes to a device.
& \begin{cmdreq}
\cfield{u64} device address
\cfield{u64} ROM code
\cfield{u8[]} bytes to write
\end{cmdreq} \\
11 & \cname{READ}
Write a request and read response.
&
&
\begin{cmdreq}
\cfield{u64} device address
\cfield{u64} ROM code
\cfield{u16} read length
\cfield{u8} verify checksum (0, 1)
\cfield{u8[]} request bytes
@ -71,7 +71,7 @@ parasitic=N
\cjoin
\begin{cmdresp}
\cfield{u8[]} read bytes
\end{cmdresp}
\end{cmdresp}
\\
20 & \cname{POLL\_FOR\_1}

@ -1,8 +1,8 @@
\section{ADC Unit}
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 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 ADC unit can also be switched to a continuous streaming mode.
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.
It's possible to activate any number of the 16 analog inputs of the 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'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.
@ -88,7 +88,7 @@ avg_factor=500
\\
2 & \cname{READ\_CAL\_CONSTANTS}
Read factory calibration constants from the MCU's ROM
Read factory calibration constants from the \gls{MCU}'s \gls{ROM}
&
\begin{cmdresp}
\cfield{u16} VREFINT\_CAL (raw word)
@ -196,7 +196,7 @@ avg_factor=500
\\
31 & \cname{SET\_SAMPLE\_TIME}
Set the sample time of the ADC's sample\&hold circuit.
Set the sample time of the \gls{ADC}'s sample\&hold circuit.
&
\begin{cmdreq}
\cfield{u8} sample time 0--7

@ -1,6 +1,6 @@
\section{DAC Unit}
The digital/analog unit works with the two-channel DAC peripheral of the microcontroller. It can be used in two modes: DC output, and waveform generation.
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 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.
@ -34,14 +34,14 @@ Channels are specified in all commands as bit map:
\begin{cmdlist}
0 & \cname{WAVE\_DC}
Set a DC level, disable DDS for the channel
Set a \gls{DC} level, disable \gls{DDS} for the channel
& \begin{cmdreq}
\cfield{u8} channels
\cfield{u16} level (0--4095)
\end{cmdreq} \\
1 & \cname{WAVE\_SINE}
Start a DDS sine waveform
Start a sine waveform
& \begin{cmdreq}
\cfield{u8} channels
\end{cmdreq} \\
@ -92,7 +92,7 @@ Channels are specified in all commands as bit map:
\end{cmdreq} \\
22 & \cname{SET\_DITHER}
Control the dithering function of the DAC block. A high noise amplitude can cause an overflow to the other end of the output range due to a bug in the DAC peripheral. Use value 255 to leave the parameter unchanged.
Control the dithering function of the \gls{DAC} block. A high noise amplitude can cause an overflow to the other end of the output range due to a bug in the \gls{DAC} peripheral. Use value 255 to leave the parameter unchanged.
& \begin{cmdreq}
\cfield{u8} channels

@ -1,6 +1,6 @@
\section{Digital Output}
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 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 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}
\subsection{Digital Output Configuration}

@ -1,6 +1,6 @@
\section{I2C Unit}
\section{I\textsuperscript{2}C Unit}
The I2C unit provides access to one of the microcontroller's 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. 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.
\subsection{I2C Configuration}
@ -21,7 +21,7 @@ analog-filter=Y
digital-filter=0
\end{inicode}
\subsection{I2C Commands}
\subsection{I\textsuperscript{2}C Commands}
\begin{cmdlist}
@ -44,7 +44,7 @@ digital-filter=0
\end{cmdresp} \\
2 & \cname{WRITE\_REG}
Write to a slave register. Sends the register number and the data in the same 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 \gls{I2C} transaction. Multiple registers can be written to slaves supporting auto-increment.
& \begin{cmdreq}
\cfield{u16} slave address
\cfield{u8} register number

@ -1,6 +1,6 @@
\section{NeoPixel Unit}
The NeoPixel unit implements the protocol needed to control a digital LED strip with WS2812, WS2811, or compatible 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 protocol timing is implemented in software, therefore it is available on any 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.
@ -18,7 +18,7 @@ pixels=32
\begin{cmdlist}
0 & \cname{CLEAR}
Switch all LEDs off (sets them to black) & \\
Switch all \glspl{LED} off (sets them to black) & \\
1 & \cname{LOAD}
Load a sequence of R,G,B bytes
@ -56,9 +56,9 @@ pixels=32
\end{cmdreq} \\
10 & \cname{GET\_LEN}
Get number of LEDs in the strip
Get number of \glspl{LED} in the strip
& \begin{cmdresp}
\cfield{u16} number of LEDs
\cfield{u16} number of \glspl{LED}
\end{cmdresp} \\
\end{cmdlist}

@ -1,9 +1,11 @@
\section{PWM Unit}
The PWM unit uses a timer/counter to generate a 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} signal. There are four outputs with a common frequency and phase, but independent duty cycles. Each channel can be individually enabled or disabled.
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}
\subsection{PWM Configuration}
\begin{inicode}

@ -27,15 +27,15 @@ data-pins=3
\begin{cmdlist}
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 Store pulse.
&
\begin{cmdreq}
\cfield{u16} trailing data
\item For each output (same size)
\begin{pldlist}
\cfield{u8[]} data to load
\end{pldlist}
\end{cmdreq}
\end{cmdreq}
\\
1 & \cname{DIRECT\_DATA}
@ -45,16 +45,16 @@ data-pins=3
\cfield{u16} values to write
\end{cmdreq} \\
2 &
\cname{DIRECT\_CLEAR}
2 &
\cname{DIRECT\_CLEAR}
Pulse the Clear pin, erasing the registers' data & \\
3 &
3 &
\cname{DIRECT\_SHIFT}
Pulse the Shift pin & \\
4 &
\cname{DIRECT\_STORE}
4 &
\cname{DIRECT\_STORE}
Pulse the Store pin & \\
\end{cmdlist}

@ -1,10 +1,10 @@
\section{SPI Unit}
The SPI unit provides access to one of the microcontroller's 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. It can be configured to use any of the different speeds, clock polarity and phase settings available in its control registers.
The unit handles up to 16 slave select (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 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.
The QUERY command of this unit, illustrated by figure \ref{fig:spi_query}, is flexible enough to support all types of 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. The slave select pin is held low during the entire transaction.
\begin{figure}[h]
\centering

@ -1,6 +1,6 @@
\section{Touch Sense Unit}
The touch sensing unit provides an access to the touch sensing controller. Its function is explained in section \ref{sec:theory-touch}. The unit configures the TSC and reads the output values of each enabled touch pad.
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.
\subsection{Touch Sense Configuration}

@ -1,6 +1,6 @@
\section{USART Unit}
The USART unit provides access to one of the microcontroller's USART peripherals. All USART parameters can be configured to match the application's needs. The peripheral is capable of driving RS485 transceivers with the Driver Enable (DE) output for switching between reception and transmission.
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 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.
@ -69,7 +69,7 @@ de-clear-time=8
\begin{cmdlist}
0 & \cname{WRITE}
Add data to the transmit buffer. Sending is asynchronous, but the command may wait for free space in the DMA buffer.
Add data to the transmit buffer. Sending is asynchronous, but the command may wait for free space in the \gls{DMA} buffer.
& \begin{cmdreq}
\cfield{u8[]} bytes to write
\end{cmdreq} \\

@ -1,6 +1,6 @@
\chapter{Wireless Interface} \label{sec:wireless}
Four methods of a wireless connection have been considered: Bluetooth (e.g. with CC2541), WiFi with ESP8266, LoRA or GFSK-modulated 868\,MHz link with SX1276, and a 2.4\,GHz link with nRF24L01+. Bluetooth was dismissed early for its complexity and ESP8266 for its high consumption in continuous reception mode, although both solutions might be viable for certain applications and with more time for evaluation.
Four methods of a wireless connection have been considered: Bluetooth (e.g. with CC2541), WiFi with ESP8266, a 868\,MHz long range link with SX1276, and a 2.4\,GHz link with nRF24L01+. Bluetooth was dismissed early for its complexity, and ESP8266 for its high consumption in continuous reception mode, although both solutions might be viable for certain applications and with more development time.
The Semtech SX1276 and Nordic Semiconductor nRF24L01+ transceivers have both been tested using the first GEX prototype, confirming its usefulness as a hardware development tool, and it's been confirmed they could fulfill the requirements of the application.
@ -10,6 +10,33 @@ The Semtech SX1276 and Nordic Semiconductor nRF24L01+ transceivers have both bee
\caption{Test setup with a GEX prototype controlling two nRF24L01+ modules}
\end{figure}
\section{Modulations Overview}
A brief overview of the different signal modulation techniques is presented here to aid the reader with understanding of table \ref{fig:nrf-sx-comparison} and the rest of the chapter.
\subsection{On-Off Keying (OOK)}
In \gls{OOK}, the carrier generator is switched on and off to transmit ones and zeros.
\subsection{Frequency Shift Keying (FSK)}
\Gls{FSK} uses a change of the carrier frequency to transmit data. The simplest form of \gls{FSK} is \gls{BFSK}, which uses a pair of alternating frequencies to transmit ones and zeros.
\subsection{Gaussian Frequency Shift Keying (GFSK)}
\Gls{GFSK} is an improvement over basic \gls{FSK} which does not switch between the different frequencies instantaneously, but uses a Gaussian filter to make the changes less abrupt, which reduces the side-band interference otherwise generated by the sharp edges. This scheme can be imagined as sending the binary waveform through a Gaussian filter and then modulating a \gls{VCO} with its output, rather than changing the \gls{VCO}'s control voltage discretely. \Gls{GFSK} is used in the Bluetooth standard.
\subsection{Minimum-Shift Keying (MSK)}
\Gls{MSK} is another \gls{FSK}-based modulation scheme. In \gls{MSK}, the frequencies representing different symbols are chosen such that there are no sharp changes in the phase of the output waveform, the modulation is \textit{phase-coherent}. This is another way to reduce side-band interference.
\subsection{Gaussian Minimum-Shift Keying (GMSK)}
\Gls{GMSK} is a variant of \gls{MSK} which uses a gaussian filter to shape the digital signal before sending it to the oscillator. The principle is similar to \gls{GFSK}, and it is yet another way to further reduce side-band interference and increase spectral efficiency. GMSK is used in the \gls{GSM}.
\subsection{LoRa Modulation}
LoRa is a patented proprietary modulation developed by Semtech. It uses a direct sequence frequency hopping spread spectrum modulation and can achieve very long range transmission (over 10\,km is not uncommon). LoRa is available only with transceiver \glspl{IC} produced by Semtech and for this reason it is rather expensive.
\section{Comparing SX1276 vs. nRF24L01+}
@ -61,7 +88,7 @@ A separate device, the \textit{GEX wireless gateway}, was developed to provide t
\caption{A block diagram of the wireless connection}
\end{wrapfigure}
The gateway presents itself to the host as a CDC/ACM device, much like the GEX modules themselves (here called \textit{nodes}) when connected over USB. It implements a simple protocol which encapsulates the binary data sent to or from a connected node. The wrapped GEX protocol (chapter \ref{sec:tinyframe}) remains unchanged.
The gateway presents itself to the host as a \gls{CDCACM} device, much like the GEX modules themselves (here called \textit{nodes}) when connected over \gls{USB}. It implements a simple protocol which encapsulates the binary data sent to or from a connected node. The wrapped GEX protocol (chapter \ref{sec:tinyframe}) remains unchanged.
The gateway has a 4-byte network ID, a number derived from the microcontroller's unique ID. The network ID must be entered into all nodes that wish to communicate with it. Additionally, each module must be assigned a unique 1-byte number, which, together with the four network ID bytes, uniquely identifies the node. The gateway can connect to up to 6 nodes at once.
@ -73,18 +100,18 @@ All messages sent to or from the gateway are a multiple of 64 bytes long, padded
\toprule
\textbf{Function} & \textbf{Message structure} \\
\midrule
Send a message & 'm' (\verb|u8|)address (\verb|u16|)length (\verb|u8[]|)payload \\
Restart \& remove nodes & 'r' \\
Add nodes & 'n' (\verb|u8|)count (\verb|u8[]|)addresses \\
Get the network ID & 'i' \\
Response to 'i' & 0x01 (\verb|u8[4]|)net\_id \\
Send a message & `m' (\verb|u8|)address (\verb|u16|)length (\verb|u8[]|)payload \\
Restart \& remove nodes & `r' \\
Add nodes & `n' (\verb|u8|)count (\verb|u8[]|)addresses \\
Get the network ID & `i' \\
Response to `i' & 0x01 (\verb|u8[4]|)net\_id \\
Received message & 0x02 (\verb|u8|)address (\verb|u8|)length (\verb|u8[]|)data \\
\bottomrule
\end{tabulary}
\caption[Wireless gateway commands and messages]{\label{fig:rf-dongle-commands}Wireless gateway commands and messages; control characters in the printable range are shown as ASCII}
\end{table}
The gateway may be restarted using the 'r' command when the PC application starts. Then it adds all node addresses with the 'n' command and can begin communication. The 'i' command, reading the network ID, can additionally be used as a ping command to verify the USB connection.
The gateway may be restarted using the `r' command when the \gls{PC} application starts. Then it adds all node addresses with the `n' command and can begin communication. The `i' command, reading the network ID, can additionally be used as a ping command to verify the \gls{USB} connection.
\todo[inline]{Add additional commands. Also remove magic bytes form the real messages, they're useless}

@ -165,7 +165,8 @@
\ctutemplateset{twocolumn.glossaries}{
\selectctulanguage{main}
\printglossary
% \printglossary[style=index]
\printglossary[style=mcolindex]
}
% Abstract template includes the Acknowledgements, Declaration and two Abstracts

@ -66,9 +66,11 @@
%% ACRONYM CONFIG
\usepackage[xindy,nomain,acronym,nopostdot,toc=false]{glossaries}
\usepackage[xindy,nonumberlist,nomain,acronym,nopostdot,toc=false]{glossaries}
%\glssetwidest{ABCD}
\renewcommand*{\glsclearpage}{}
\usepackage{glossary-mcols}
\renewcommand*{\glspostdescription}{}
\makeglossaries
\glsaddall
%\renewcommand*{\glstextformat}[1]{\textcolor{black}{#1}}

@ -5,7 +5,8 @@
\newacronym{SPI}{SPI}{Serial Peripheral Interconnect}
\newacronym{USART}{USART}{Universal Synchronous/Asynchronous Receiver/Transmitter}
\newacronym{UART}{UART}{Universal Asynchronous Receiver/Transmitter}
\newacronym{I2C}{I$^2$C}{Inter-Integrated Circuit}
\newacronym{I2C}{I\textsuperscript{2}C}{Inter-Integrated Circuit}
\newacronym{I2S}{I\textsuperscript{2}S}{Inter-IC Sound}
\newacronym{USB}{USB}{Universal Serial Bus}
\newacronym{CAN}{CAN}{Controller Area Network}
\newacronym{HART}{HART}{Highway Addressable Remote Transducer}
@ -31,12 +32,15 @@
\newacronym{MBR}{MBR}{master boot record}
\newacronym{NVIC}{NVIC}{Nested Vectored Interrupt Controller}
\newacronym{GPS}{GPS}{Global Positioning System}
\newacronym{TWI}{TWI}{Two-Wire Interface}
\newacronym{SMBus}{SMBus}{System Management Bus}
\newacronym{PMBus}{PMBus}{Power Management Bus}
% Common names
\newacronym{RMS}{RMS}{root mean square}
\newacronym{PC}{PC}{personal computer}
\newacronym{PCB}{PCB}{printed circuit board}
\newacronym{TVS}{TVS}{transiet voltage suppressor}
\newacronym{TVS}{TVS}{transiet-voltage suppressor}
\newacronym{GPIO}{GPIO}{general purpose input/output}
\newacronym{IC}{IC}{integrated circuit}
\newacronym{PWM}{PWM}{pulse width modulation}
@ -46,13 +50,43 @@
\newacronym{API}{API}{application programming interface}
\newacronym{LED}{LED}{light emitting diode}
\newacronym{MCU}{MCU}{microcontroller unit}
\newacronym{RAM}{RAM}{read-only memory}
\newacronym{RAM}{RAM}{random-access memory}
\newacronym{ROM}{ROM}{read-only memory}
\newacronym{TTL}{TTL}{transistor-transistor logic}
\newacronym{RTS}{RTS}{Ready To Send}
\newacronym{CTS}{CTS}{Clear To Send}
\newacronym{SCK}{SCK}{Serial Clock}
\newacronym{MOSI}{MOSI}{Master Out, Slave In}
\newacronym{MISO}{MISO}{Master In, Slave Out}
\newacronym{NSS}{NSS}{Negated Slave Select}
\newacronym{DE}{DE}{Driver Enable}
\newacronym{CSB}{CSB}{Chip Select Bar}
\newacronym{SS}{SS}{Slave Select}
\newacronym{SDA}{SDA}{Serial Data Line}
\newacronym{SCL}{SCL}{Serial Clock Line}
\newacronym{DTR}{DTR}{Data Terminal Ready}
\newacronym{NDIR}{NDIR}{nondispersive infrared}
\newacronym{NFC}{NFC}{near-field communication}
\newacronym{RTC}{RTC}{real-time clock}
\newacronym{GND}{GND}{ground}
\newacronym{CRC}{CRC}{cyclic redundancy check}
\newacronym{VCO}{VCO}{voltage-controlled oscillator}
\newacronym{TCO}{TCO}{temperature-compensated oscillator}
\newacronym{NCO}{NCO}{numerically controlled oscillator}
\newacronym{DC}{DC}{direct current}
\newacronym{SAR}{SAR}{successive approximation register}
\newacronym{AC}{AC}{alternating current}
\newacronym{TSC}{TSC}{Touch Sensing Controller}
\newacronym{OOK}{OOK}{on-off keying}
\newacronym{FSK}{FSK}{frequency-shift keying}
\newacronym{GFSK}{GFSK}{Gaussian frequency-shift keying}
\newacronym{MSK}{MSK}{minimum-shift keying}
\newacronym{GMSK}{GMSK}{Gaussian minimum-shift keying}
\newacronym{BFSK}{BFSK}{binary frequency-shift keying}
\newacronym{GSM}{GSM}{Global System for Mobile communications}
\glsunset{UART}
\glsunset{CDC}

Binary file not shown.

@ -37,6 +37,9 @@
\input{ch.tinyframe}
\input{ch.wireless}
\input{ch.gex_units}
\part{Results}
\input{ch.conclusion}
\appendix % začátek příloh

Loading…
Cancel
Save