more explanation of the software apis
This commit is contained in:
+138
-13
@@ -40,13 +40,14 @@ The library is composed of a \textit{transport}, the core class called \textit{c
|
||||
|
||||
The unit classes wrap the command and event \gls{API} described in \cref{sec:units-overview}; all classes and methods are annotated by documentation comments for easy understanding.
|
||||
|
||||
An example Python program showing a pattern with the \gls{LED} matrix driver IS31FL3730 is presented below as an illustration of the library usage. A photo of the produced pattern can be seen in \cref{fig:pydemo}.
|
||||
An example Python program displaying a test pattern on a \gls{LED} matrix using the \gls{I2C}-connected driver chip IS31FL3730 is presented in \cref{lst:py_api} as an illustration of the library usage. A photo of the produced \gls{LED} pattern can be seen in \cref{fig:pydemo}.
|
||||
|
||||
\todo[inline]{add left line next to this listing}
|
||||
\begin{minted}{python}
|
||||
#!/bin/env python3
|
||||
import gex
|
||||
with gex.Client(gex.TrxRawUSB()) as client:
|
||||
\begin{listing}[h]
|
||||
\begin{pythoncode}
|
||||
#!/bin/env python3
|
||||
# The I2C unit called 'i2c' is configured to use PB6 and PB7
|
||||
import gex
|
||||
with gex.Client(gex.TrxRawUSB()) as client:
|
||||
bus = gex.I2C(client, 'i2c')
|
||||
addr = 0x61
|
||||
bus.write_reg(addr, 0x00, 0b00011000) # dual matrix
|
||||
@@ -64,32 +65,156 @@ with gex.Client(gex.TrxRawUSB()) as client:
|
||||
])
|
||||
# update display
|
||||
bus.write_reg(addr, 0x0C, 0x01)
|
||||
\end{minted}
|
||||
\end{pythoncode}
|
||||
\caption{\label{lst:py_api} An example Python program using the GEX client library}
|
||||
\end{listing}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\includegraphics[width=.7\textwidth] {img/phatmtx.jpg}
|
||||
\caption{\label{fig:pydemo}GEX Zero with the Micro Dot pHAT add-on board showing a test pattern}
|
||||
\caption{\label{fig:pydemo}GEX Zero with the Micro Dot pHAT add-on board, showing a test pattern defined in a Python script}
|
||||
\end{figure}
|
||||
|
||||
\section{MATLAB integration}
|
||||
|
||||
The Python library can be accessed from MATLAB scripts thanks to the MATLAB's two-way Python integration~\cite{matlabpy}. Controlling GEX from MATLAB may be useful when additional processing is required, e.g. with data from the \gls{ADC}; however, in many cases, an open source alternative native to Python exists that could be used for the same purpose, such as the NumPy and SciPy libraries~\cite{numpyscipy}.
|
||||
The Python library can be accessed from MATLAB scripts thanks to the MATLAB's two-way Python integration~\cite{matlabpy}. Controlling GEX from MATLAB may be useful when additional processing is required, e.g., with data from the \gls{ADC}; however, in many cases, an open source alternative native to Python exists that could be used for the same purpose, such as the NumPy and SciPy libraries~\cite{numpyscipy}.
|
||||
|
||||
The example in \cref{lst:matlab_api} demonstrates the use of MATLAB to calculate the frequency spectrum of an analog signal captured with GEX. The syntax needed to use the serial port transport (instead of a raw access to USB endpoints) is shown in a comment.
|
||||
|
||||
\begin{listing}
|
||||
\begin{matlabcode}
|
||||
% The ADC unit called 'adc' is configured to use PA1 as Channel 0
|
||||
|
||||
%trx = py.gex.TrxSerialThread(pyargs('port', '/dev/ttyUSB1', ...
|
||||
% 'baud', 115200));
|
||||
trx = py.gex.TrxRawUSB();
|
||||
client = py.gex.Client(trx);
|
||||
adc = py.gex.ADC(client, 'adc');
|
||||
|
||||
L = 1000;
|
||||
Fs = 1000;
|
||||
adc.set_sample_rate(uint32(Fs)); % casting to unsigned integer
|
||||
data = adc.capture(uint32(L));
|
||||
data = double(py.array.array('f',data)); % numpy array to matlab format
|
||||
|
||||
Y = fft(data);
|
||||
P2 = abs(Y/L);
|
||||
P1 = P2(1:L/2+1);
|
||||
P1(2:end-1) = 2*P1(2:end-1);
|
||||
f = Fs*(0:(L/2))/L;
|
||||
|
||||
plot(f,P1)
|
||||
client.close()
|
||||
\end{matlabcode}
|
||||
\caption{\label{lst:matlab_api} Calling the Python GEX library from a MATLAB script}
|
||||
\end{listing}
|
||||
|
||||
\todo[inline]{add a matlab example}
|
||||
|
||||
\section{C Library}
|
||||
|
||||
The C library is more simplistic than the Python one; it supports only the serial port transport (\gls{UART} or \gls{CDCACM}) and does not implement asynchronous polling or the unit support drivers. What \textit{is} implement---the transport, a basic protocol handler, and payload building and parsing utilities---is sufficient for most applications, though less convenient than the Python library.
|
||||
|
||||
This low-level library is intended for applications where the performance of the Python implementation is insufficient, or where an integration with existing C code is required. The full \gls{API} can be found in the library header files. A C version of the example Python script controlling a \gls{LED} matrix driver follows:
|
||||
This low-level library is intended for applications where the performance of the Python implementation is insufficient, or where an integration with existing C code is required. The full \gls{API} can be found in the library header files. A C version of the example Python script shown above, controlling a \gls{LED} matrix driver, is presented in \cref{lst:c_api_full}.
|
||||
|
||||
\todo[inline]{add the example}
|
||||
\begin{listing}
|
||||
\begin{ccode}
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include "gex.h"
|
||||
|
||||
static GexClient *gex;
|
||||
|
||||
\todo[inline]{Measurement / evaluation examples here...}
|
||||
/** Signal handler closing the serial port at exit */
|
||||
static void sigintHandler(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
if (gex != NULL) GEX_DeInit(gex);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
signal(SIGINT, sigintHandler);
|
||||
|
||||
// Initialize GEX and the I2C unit handle
|
||||
gex = GEX_Init("/dev/ttyACM0", 200);
|
||||
assert(NULL != gex);
|
||||
GexUnit *bus = GEX_GetUnit(gex, "i2c", "I2C");
|
||||
assert(NULL != bus);
|
||||
|
||||
// Configure the matrix driver
|
||||
GEX_Send(bus, 2, (uint8_t*) "\x61\x00\x00\x18", 4);
|
||||
GEX_Send(bus, 2, (uint8_t*) "\x61\x00\x0d\x0e", 4);
|
||||
GEX_Send(bus, 2, (uint8_t*) "\x61\x00\x19\x40", 4);
|
||||
|
||||
// Load data
|
||||
GEX_Send(bus, 2, (uint8_t*) "\x61\x00\x01"
|
||||
"\xAA\x55\xAA\x55\xAA\x55\xAA\x55", 11);
|
||||
GEX_Send(bus, 2, (uint8_t*) "\x61\x00\x0e"
|
||||
"\xFF\x00\xFF\x00\xFF\x00\xFF\x00", 11);
|
||||
// Update display
|
||||
GEX_Send(bus, 2, (uint8_t*) "\x61\x00\x0c\x01", 4);
|
||||
}
|
||||
\end{ccode}
|
||||
\caption{\label{lst:c_api_full} An example C program controlling GEX using the low-level GEX library; this code has the same effect as the Python script shown in \cref{lst:py_api}.}
|
||||
\end{listing}
|
||||
|
||||
The reader might point out that this example is unnecessarily obtuse, and that the payloads could be constructed in a more readable way. Indeed, two better methods are available.
|
||||
|
||||
\subsection{Structure-based Payload Construction}
|
||||
|
||||
\begin{listing}
|
||||
\begin{ccode}
|
||||
struct i2c_write {
|
||||
uint16_t address;
|
||||
uint8_t reg;
|
||||
uint8_t value[8]; // largest needed payload size
|
||||
} __attribute__((packed));
|
||||
|
||||
// 1-byte value
|
||||
GEX_Send(bus, 2, (void *) &(struct i2c_write) {
|
||||
.address = 0x61,
|
||||
.reg = 0x00,
|
||||
.value = {0x18},
|
||||
}, 3 + 1); // use only 1 byte of the value array
|
||||
|
||||
// 8-byte value
|
||||
GEX_Send(bus, 2, (void *) &(struct i2c_write) {
|
||||
.address = 0x61,
|
||||
.reg = 0x01,
|
||||
.value = {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55},
|
||||
}, 3 + 8);
|
||||
\end{ccode}
|
||||
\caption{\label{lst:c_api_struct} The variable-length struct approach to payload building}
|
||||
\end{listing}
|
||||
|
||||
The structure-based method utilizes C structs to access individual fields in the payload. Simple payloads can be represented by a struct without problems, but payloads of a dynamic length pose a challenge; we can either define a new struct for each required length, or, when the variable-length array is located at the end of the payload, a struct with the largest needed payload size is defined and the real length is then specified when sending the message. The latter approach is illustrated in \cref{lst:c_api_struct}.
|
||||
|
||||
\subsection{Using the Payload Builder Utility}
|
||||
|
||||
\begin{listing}
|
||||
\begin{ccode}
|
||||
uint8_t buff[20];
|
||||
PayloadBuilder pb;
|
||||
|
||||
pb = pb_init(&buff, 20, NULL);
|
||||
pb_u16(&pb, 0x61);
|
||||
pb_u8(&pb, 0x00);
|
||||
pb_u8(&pb, 0x18);
|
||||
GEX_Send(bus, 2, buff, pb_length(&pb));
|
||||
|
||||
pb_rewind(&pb); // reset the builder for a new frame
|
||||
|
||||
uint8_t screen[8] = {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55};
|
||||
pb_u16(&pb, 0x61);
|
||||
pb_u8(&pb, 0x01);
|
||||
pb_buf(&pb, &screen, 8);
|
||||
GEX_Send(bus, 2, buff, pb_length(&pb));
|
||||
\end{ccode}
|
||||
\caption{\label{lst:c_api_pb}Building and sending payloads using the PayloadBuilder utility}
|
||||
\end{listing}
|
||||
|
||||
The Payload Builder utility, which comes with the TinyFrame library, may be used to construct message payloads; its usage is shown in \cref{lst:c_api_pb}. The third parameter of \mono{pb\_init()} is optional: a pointer to a function called when the buffer overflows; this callback is used to flush the buffer and rewind it, or to throw an error.
|
||||
|
||||
Payload Builder is accompanied by Payload Parser, a tool doing exactly the opposite, parsing a binary payload. That is not needed in our example, but we will find Payload Parser useful when processing the response to a query command, or an asynchronous event payload. Where a variable is written to a payload with \mono{pb\_u8(\&pb, var)}, it is retrieved with \mono{pp\_u8(\&pp)}. The full API of those payload utilities can be found in their well-commented header files.
|
||||
|
||||
|
||||
+4
-4
@@ -1,13 +1,13 @@
|
||||
\section{\IIC Unit}
|
||||
\section{\texorpdfstring{\IIC}{I2C} Unit}
|
||||
|
||||
The \gls{I2C} unit provides access to one of the microcontroller's \gls{I2C} peripherals. More on the \IIC bus can be found in \cref{sec:theory-i2c}.
|
||||
|
||||
The unit can be configured to use either of the three standard speeds (Standard, Fast and Fast+) and supports both 10-bit and 7-bit addressing. 10-bit addresses can be used in commands by setting their highest bit (0x8000), as a flag to the unit; the 7 or 10 least significant bits will be used as the actual address.
|
||||
|
||||
\subsection{\IIC Configuration}
|
||||
\subsection{\texorpdfstring{\IIC}{I2C} Configuration}
|
||||
|
||||
\begin{inicode}
|
||||
[I2C:d@4]
|
||||
[I2C:i2c@4]
|
||||
# Peripheral number (I2Cx)
|
||||
device=1
|
||||
# Pin mappings (SCL,SDA)
|
||||
@@ -23,7 +23,7 @@ analog-filter=Y
|
||||
digital-filter=0
|
||||
\end{inicode}
|
||||
|
||||
\subsection{\IIC Commands}
|
||||
\subsection{\texorpdfstring{\IIC}{I2C} Commands}
|
||||
|
||||
\begin{cmdlist}
|
||||
|
||||
|
||||
@@ -4,4 +4,8 @@
|
||||
\LetLtxMacro{\cminted}{\minted}
|
||||
\let\endcminted\endminted
|
||||
\xpretocmd{\cminted}{\RecustomVerbatimEnvironment{Verbatim}{BVerbatim}{}}{}{}
|
||||
|
||||
\newminted{ini}{frame=leftline,autogobble=true}
|
||||
\newminted{python}{frame=leftline,autogobble=true}
|
||||
\newminted{c}{frame=leftline,autogobble=true}
|
||||
\newminted{matlab}{frame=leftline,autogobble=true}
|
||||
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user