Select Git revision
spi-uwb.tex
spi-uwb.tex 14.34 KiB
\subsection{SPI \& UWB radio}
\subsectionauthor{Author: Fredrik Grönlund \& Emil Lundell\\Reviewer: TODO???}
\subsubsection{General description}
Communication over SPI is done in multiples of bytes.
All communication is initiated by the master sending a command block of 1-3 bytes, depending on the type of command, followed by data from either the master or the slave.
This could be as much as 1024 bytes, excluding the command block.
Both lines are always transmitting, although one line is always ignored at any one time, either by the slave or master.
The slave ignores the master when returning data, and the master ignores the slave when issuing commands or writing data.
A total of two UWB radios were required, one on the copter and one on the base station.
They are configured to run at a $\SI{6.8}{Mbps}$ with a PRF of $\SI{16}{MHz}$ by default.
These values were adjusted as needed.
Data rate, in particular, may be decreased in favour of range and reliability, should it be required.
Due to the construction of the SPI protocol, the radio, being the slave, cannot request to be read through SPI alone.
Only the master may request a write or read.
The DW1000 is instead configured to set its IRQ pin high which triggers an interrupt in the MCU.
The UWB radio will be used in four different ways:
\begin{itemize}
\item A read is requested by the master.
The request specifies the register to read from.
The master holds the NSS pin low and the clock running, and the slave returns octets as long as this is kept up.
The data on the MOSI line after headers is ignored.
\item A write is requested by the master.
Target register is specified in the header.
The master keeps the clock running as above while writing out data, and the slave enters it into its register.
The data on the MISO line is ignored.
\item A write request to the transmit register is requested by the master, setting the \texttt{TXSTRT} (Transmit start) bit high in the \texttt{SYS\_CTRL} (System Control) register.
The slave then starts sending the contents of its transmit buffer.
\item The radio detects a preamble over UWB.
It receives the data, enters it into its receive register, and sets its external interrupt pin.
This should generate an interrupt, prompting the microcontroller to initialize a read of the received data.
\end{itemize}
A flowchart describing this can be seen in Figure~\ref{pic:UWB_op}.
This also extends to ranging.
\begin{figure}[H]
\centering
\includegraphics[width=0.7\textwidth]{./Pictures/UWB2}
\caption{Basic UWB operations flowchart.}
\label{pic:UWB_op}
\end{figure}
\subsubsection{SPI Implementation}
%Secondly, the data to be sent and received from the LIDAR and IMU was deemed small in comparison to the speed of the SPI and UWB.
%Thus, improvements in speed was deemed useful but not critical.
SPI is defined as follows:
\begin{itemize}
\item SPI instance used is SPI2
\item SPI is full duplex (both MOSI and MISO)
\item Data size is 8 bits
\item Clock polarity is low.
\item Clock Phase is first edge
\item Slave Select is software-managed
\item Clock prescaler is 64 in setup, 8 while running
\end{itemize}
The SPI protocol relies on four pins:
\begin{itemize}
\item SPICSn: SPI Chip Select (also referred to as ``NSS: Slave Select'')
\item SPIMISO: SPI Master In Slave Out (data output from MCU to UWB)
\item SPIMOSI: SPI Master Out Slave In (data input from UWB to MCU)
\item SPICLK: SPI clock
\end{itemize}
Several SPI peripherals are available in the project MCU.
However, SPI2 is used to avoid collisions with other peripherals.
The SPI functions are bound to the following pins:
\begin{itemize}
\item \makebox[2cm][l]{SPICS:} \makebox[2cm][l]{PB12}
\item \makebox[2cm][l]{SPIMISO:} \makebox[2cm][l]{PC2}
\item \makebox[2cm][l]{SPIMOSI:} \makebox[2cm][l]{PC3}
\item \makebox[2cm][l]{SPICLK:} \makebox[2cm][l]{PB10}
\end{itemize}
The NSS pin is handled in software like a standard digital GPIO pin.
Handling NSS (Slave select) in hardware would be faster and easier, however an undocumented hardware bug in the STMF7-series processor causes the hardware-managed NSS to malfunction when NSS Pulse is disabled \cite{web:stm_nss_problem}.
An example of NSS Pulse mode can be seen in Figure \ref{pic:NSSP}.
The DW1000 does not support NSS Pulse communication, so software NSS was required.
All other SPI is implemented using the HAL\footnote{Hardware Abstraction Layer} libraries supplied by STMicroelectronics for this processor.
\begin{figure}[H]
\centering
\includegraphics[width=0.9\textwidth]{./Pictures/NSSP}
\caption{SPI using NSSP, lowest line shows the NSS pin.}
\label{pic:NSSP}
\end{figure}
In asynchronous mode, it utilizes interrupts to allow other code to execute.
In synchronous mode, it blocks other code execution.
\subsubsection{UWB Specifications}
Communication and operation of the DW1000 UWB module is entirely done through the DW1000 library.
This was supplied by Kim Albertsson as a semi-generic library, released as Open Source \cite{code:DW1000_library}.
The library uses two processor-specific files for interfacing against the SPI peripheral.
The file \texttt{dw1000-spi.c} provides the functions the DW1000 library uses to interface with the SPI.
The file \texttt{dw1000-hal.c}\footnote{Arguably misnamed, according to Kim.} provides interrupt functionality.
Sebastian Larsson, a programmer at Conex, assisted with getting the library to work.
The UWB is configured as follows:
\begin{itemize}
\item \makebox[5cm][l]{Transmit frame length} \makebox[3cm][l]{\texttt{(TFLEN)}} = Variable with messages
\item \makebox[5cm][l]{Transmit bit rate} \makebox[3cm][l]{\texttt{(TXBR)}} = $\SI{850}{kbps}$
\item \makebox[5cm][l]{Transmit ranging} \makebox[3cm][l]{\texttt{(TR)}} = $\SI{1}{}$ (enabled)
\item \makebox[5cm][l]{Repetition frequency} \makebox[3cm][l]{\texttt{(TXPRF)}} = $\SI{16}{\mega\hertz}$
\item \makebox[5cm][l]{Transmit preamble} \makebox[3cm][l]{\texttt{(TXPSR+PE)}} = $\SI{128}{symbols}$
\end{itemize}
Note that these differ from the default settings in the DW1000 User Manual\cite{sheet:DW1000_User_Manual}.
This is because the default settings are either extremely suboptimal or non-funtional.
Transmission is done through writing desired data to a transmit buffer internal to the DW1000.
The transmission itself is initiated with current settings when the status bit \texttt{TXSTRT}, Transmission start, in the system control register is written to \texttt{1}.
To facilitate use, the DW1000 module has several GPIO pins and support for external interrupt pin configuration.
This pin can be set high as the radio finishes a receive or transmit, triggering an interrupt in the MCU.
This extra pin is bound as follows:
\begin{itemize}
\item \makebox[3cm][l]{UWB\_IRQ:} \makebox[2cm][l]{PB11}
\end{itemize}
\subsubsection{LAMP - LIDAR and Accelerometer Measurement Protocol}
In order to codify sent and received data, a protocol was constructed.
The protocol needs to pass along data from the LIDAR, the IMU and UWB ranging data.
LAMP packages are currently sent using the WiFi module.
LAMP is defined as follows:
\begin{figure}[H]
\begin{bytefield}[bitwidth=2.6em]{16}
\bitheader{0-15} \\
\bitbox{1}{TYP} & \bitbox{1}{SIZ} & \bitbox{1}{RSL}
& \bitbox{2}{STEP} & \bitbox{2}{ANGL} & \bitbox{9}{\ldots} \\
\bitbox{15}{\ldots up to 24 byte LIDAR data } & \bitbox{1}{CHK}
\end{bytefield}
\caption{LAMP protocol definition - IMU frames}
\end{figure}
\begin{figure}[H]
\begin{bytefield}[bitwidth=2.6em]{16}
\bitheader{0-15} \\
\bitbox{1}{TYP} & \bitbox{1}{SIZ} & \bitbox{1}{RSL}
& \bitbox{2}{STEP} & \bitbox{2}{ANGL} & \bitbox{9}{\ldots} \\
\wordbox[tlr]{4}{\ldots up to 90 byte LIDAR data\ldots } \\
\wordbox[blr]{1}{} \\
\bitbox{1}{\ldots} & \bitbox{1}{CHK}
\end{bytefield}
\caption{LAMP protocol definition - LIDAR frames}
\end{figure}
\begin{figure}[H]
\begin{bytefield}[bitwidth=2.6em]{16}
\bitheader{0-15} \\
\bitbox{1}{TYP} & \bitbox{1}{SIZ} & \bitbox{1}{RSL}
& \bitbox{2}{STEP} & \bitbox{2}{ANGL} & \bitbox{8}{RANGE} & \bitbox{1}{CHK}
\end{bytefield}
\caption{LAMP protocol definition - RANGE frames}
\end{figure}
Field definitions:
\begin{itemize}
\item [TYP:] Contains type of the current frame.
\begin{itemize}
\item [\texttt{0x00}:] IMU frame
\item [\texttt{0x01}:] LIDAR frame, 1 byte data per point
\item [\texttt{0x02}:] LIDAR frame, 2 byte data per point
\item [\texttt{0x03}:] LIDAR frame, 3 byte data per point (default)
\item [\texttt{0x04}:] DIST, UWB distance polling reporting
\end{itemize}
\item [SIZ:] Total size of the whole frame, in bytes.
\item [RSL:] Resolution of LIDAR measurment points.
\begin{itemize}
\item [\texttt{0x01}:] $\SI{0.25}{\degree}$ resolution
\item [\texttt{0x02}:] $\SI{0.5}{\degree}$ resolution
\item [\texttt{0x04}:] $\SI{1}{\degree}$ resolution
\item [\texttt{0x08}:] $\SI{2}{\degree}$ resolution
\end{itemize}
Should be set to 0 when LIDAR frames are not used.
\item [STEP:] Current step position of the stepper motor, in total steps from zero point.
Should be set 0 when LIDAR frames are not used.
\item [ANGL:] LIDAR starting angle at the start of measurement, in $\SI{0.25}{\degree}$ intervals from starting point.
Should be set 0 when LIDAR frames are not used.
\item [LIDAR:] LIDAR data points, as defined by TYP and RSL.
May be up to 90 bytes.
\item [IMU:] Current IMU data.
\item [RANGE:] Ranging data generated by the UWB ranging function.
\item [CHK:] Checksum of the package, calculated as an XOR of every byte of the frame.
\end{itemize}
The size of the packets must conform to the 127 byte maximum packet size of the DW1000.
Additionally, LAMP LIDAR packages were designed to not require sending partial packages of data.
Using the default configuration and most variations on it, LIDAR data should fit into a number of whole packages and require no partial packages.
The LIDAR has a measurement sweep of $\SI{270}{\degree}$, with four data points per degree at max.
When requesting data from it, it is possible to return only some data points, typically every other, every forth or so on.
The accuracy-to-data-size compromise was decided as $\SI{1}{\degree}$, although LAMP was set up to support changes to this on-the-fly without having to alter the protocol.
\emph{Example:}\\
The total LIDAR measurement range is an $\SI{270}{\degree}$ arc.
A typical package, with default TYP and $RSL = \texttt{0x04}$, a total of 270 measuring points.
This gives
\begin{align*}
\frac{270 \cdot \frac{1}{1}}{90 / 3} = 9\text{ packets}
\end{align*}
At minimum precision and truncated data (\SI{2}{\degree} resolution and 1 byte per point), the data obtained is
\begin{align*}
\SI{270}{\degree} \cdot \frac{1}{4} = \SI{135}{bytes}
\end{align*}
which fits into two packages with \SI{90}{bytes} payload, although one will not be filled completely.
At maximum precision (\SI{0.25}{\degree} resolution and 3 byte per point) a total of $\frac{\SI{270}{\degree}}{0.25} = 1080$ data points, resulting in 3240 bytes of data.
This gives
\begin{align*}
\frac{1080}{90 / 3} = 36\text{ packets.}
\end{align*}
\subsubsection{UWB Ranging}
The basic operation of ranging supported by the UWB is defined relatively simply.
In the project use case, there exists one \emph{tag} and one \emph{anchor} unit, mounted on the copter and base station respectively.
After the anchor detects a tag, it initializes ranging and calculates the distance from the timestamps of received messages.
Basic operation is as follows:
\begin{enumerate}
\item Tag broadcasts a \texttt{BLINK} message periodically.
\item Anchor detects the \texttt{BLINK} and replies with a \texttt{RANGE\_INIT}.
\item Tag receives the \texttt{RANGE\_INIT} and responds with a \texttt{POLL}.\\
Once tag receives the \texttt{RANGE\_INIT} it is automatically paired to the anchor.
\item Anchor receives \texttt{POLL} and responds with \texttt{RESPONSE}
\item Tag receives \texttt{RESPONSE} and sends \texttt{FINAL}.
\item Anchor receives \texttt{FINAL} and calculates the distance between the units.
\end{enumerate}
Further accuracy can be achieved by calibrating for antenna delay on an individual basis.
UWB ranging was not completed.
A prototype, adapted from the DW1000 library examples, can be found in the project folder.
It uses the files \texttt{dw-range.c} and \texttt{dw-range.h} for parsing and constructing range packets.