1 /*************************************************************************//**
2 * @file
3 * @brief This file is part of the AFBR-S50 SDK example application.
4 *
5 * @copyright
6 *
7 * Copyright (c) 2023, Broadcom Inc.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright notice, this
14 * list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of the copyright holder nor the names of its
21 * contributors may be used to endorse or promote products derived from
22 * this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *****************************************************************************/
35
36 #include <stdio.h>
37 #include <api/argus_api.h>
38 #include <platform/argus_irq.h>
39
40 #define SPI_SLAVE 1
41
42 /*!***************************************************************************
43 * Global measurement data ready event counter.
44 *
45 * Determines the number of measurement data ready events happened and thus
46 * the number of timer the #Argus_EvaluateData function must be called to
47 * free API internal date structures that buffer the raw sensor readout data.
48 *
49 * The #Argus_EvaluateData function must be called outside of the interrupt
50 * callback scope (i.e. from the main thread/task) to avoid huge delays due
51 * to the heavy data evaluation.
52 *
53 * Note that the #Argus_EvaluateData function must be called once for each
54 * callback event since it clears the internal state of the raw data buffer.
55 * If not called, the API gets stuck waiting for the raw data buffer to be
56 * freed and ready to be filled with new measurement data.
57 *
58 * In automatic measurement mode, i.e. if the measurements are automatically
59 * triggered on a time based schedule from the periodic interrupt timer (PIT),
60 * the callback may occur faster than the #Argus_EvaluateData function gets
61 * called from the main thread/task. This usually happens at high frame rates
62 * or too much CPU load on the main thread/task. In that case, the API delays
63 * new measurements until the previous buffers are cleared. Since the API
64 * contains two distinct raw data buffers, this counter raises up to 2 in the
65 * worst case scenario.
66 *****************************************************************************/
67 static volatile uint8_t myDataReadyEvents = 0;
68
69 /*!***************************************************************************
70 * @brief A callback function from the example code whenever an error occurs.
71 *
72 * @details The example code calls this function whenever an unexpected error
73 * occurs, for example, if an API function returns an error code.
74 *
75 * This implementation of the function will print the error message.
76 * If specified, the program execution will be stopped with an
77 * infinite loop. Otherwise, the program will continue to run and the
78 * error is printed and ignored.
79 *
80 * @warning This is only a simple example implementation that does not handle
81 * errors in a production system. It is intended to demonstrate the
82 * usage of the API and to provide a starting point for custom
83 * applications.
84 *
85 * This function needs to be replaced with a more sophisticated
86 * implementation to handle errors in a production system.
87 * For example, it could reset the device or try to recover from
88 * the error by re-initializing the device.
89 *
90 * @param status The specified status to be checked for errors.
91 * @param stop Whether to stop the program (e.g. in case of a critical error).
92 * @param msg The associated error message to be printed in case of errors.
93 *****************************************************************************/
HandleError(status_t status,bool stop,char const * msg)94 void HandleError(status_t status, bool stop, char const * msg)
95 {
96 /* Check for status < 0 and print message and halt the program execution. */
97 if (status < STATUS_OK)
98 {
99 printf("ERROR (%d): %s\n", status, msg);
100 if (stop)
101 {
102 printf(" --> Stopped execution due to a critical issue!\n"
103 " Check the hardware end reset the board!\n");
104 while (1) __asm("nop"); // stop!
105 }
106 }
107 }
108
109 /*!***************************************************************************
110 * @brief Creates and initializes a new device instance.
111 *
112 * @param slave The SPI slave identifier number that is passed to the S2PI
113 * layers by the API whenever it calls a function.
114 *
115 * @return The pointer to the handle of the created device instance. Used to
116 * identify the calling instance in case of multiple devices.
117 *****************************************************************************/
InitializeDevice(s2pi_slave_t slave)118 static argus_hnd_t* InitializeDevice(s2pi_slave_t slave)
119 {
120 /* Get device instance already initialized with default settings by the OS */
121 argus_hnd_t *device = Argus_GetHandle(SPI_SLAVE);
122 HandleError(device ? STATUS_OK : ERROR_FAIL, true, "Argus_CreateHandle failed!");
123
124 /* Adjust additional configuration parameters by invoking the dedicated API methods.
125 * Note: The maximum frame rate is limited by the amount of data sent via UART.
126 * See #PrintResults function for more information. */
127 status_t status = Argus_SetConfigurationFrameTime(device, 100000); // 0.1 second = 10 Hz
128 HandleError(status, true, "Argus_SetConfigurationFrameTime failed!");
129
130 return device;
131 }
132
133 /*!***************************************************************************
134 * @brief Measurement data ready callback function.
135 *
136 * @param status The measurement/device status from the last measurement cycle.
137 *
138 * @param device The pointer to the handle of the calling API instance. Used to
139 * identify the calling instance in case of multiple devices.
140 *
141 * @return Returns the \link #status_t status\endlink (#STATUS_OK on success).
142 *****************************************************************************/
MeasurementReadyCallback(status_t status,argus_hnd_t * device)143 static status_t MeasurementReadyCallback(status_t status, argus_hnd_t * device)
144 {
145 (void)device; // unused in this example...
146
147 HandleError(status, false, "Measurement Ready Callback received error!");
148
149 /* Count the data ready events, i.e. the number of times the
150 * Argus_EvaluateData function must be called from main thread.
151 *
152 * Note: Since only a single device is used, the device parameter
153 * can be ignored. In case of multiple device, the device
154 * parameter determines the calling device instance.
155 *
156 * Note: Do not call the Argus_EvaluateMeasurement method
157 * from within this callback since it is invoked in
158 * a interrupt service routine and should return as
159 * soon as possible. */
160 myDataReadyEvents++;
161
162 return STATUS_OK;
163 }
164
165 /*!***************************************************************************
166 * @brief Prints measurement results via UART.
167 *
168 * @details Prints some measurement data via UART in the following format:
169 *
170 * ```
171 * 123.456789 s; Range: 123456 mm; Amplitude: 1234 LSB; Quality: 100; Status: 0
172 * ```
173 *
174 * @param res A pointer to the latest measurement results structure.
175 *****************************************************************************/
PrintResults(argus_results_t const * res)176 static void PrintResults(argus_results_t const * res)
177 {
178 /* Print the recent measurement results:
179 * 1. Time stamp in seconds since the last MCU reset.
180 * 2. Range in mm (converting the Q9.22 value to mm).
181 * 3. Amplitude in LSB (converting the UQ12.4 value to LSB).
182 * 4. Signal Quality in % (100% = good signal).
183 * 5. Status (0: OK, <0: Error, >0: Warning.
184 *
185 * Note: Sending data via UART creates a large delay which might prevent
186 * the API from reaching the full frame rate. This example sends
187 * approximately 80 characters per frame at 115200 bps which limits
188 * the max. frame rate of 144 fps:
189 * 115200 bps / 10 [bauds-per-byte] / 80 [bytes-per-frame] = 144 fps */
190 printf("%4d.%06d s; Range: %5d mm; Amplitude: %4d LSB; Quality: %3d; Status: %d\n",
191 res->TimeStamp.sec,
192 res->TimeStamp.usec,
193 res->Bin.Range / (Q9_22_ONE / 1000),
194 res->Bin.Amplitude / UQ12_4_ONE,
195 res->Bin.SignalQuality,
196 res->Status);
197 }
198
199 /*!***************************************************************************
200 * @brief Prints information about the initialized devices.
201 *
202 * @param device The pointer to the device handler.
203 *****************************************************************************/
PrintDeviceInfo(argus_hnd_t * device)204 static void PrintDeviceInfo(argus_hnd_t * device)
205 {
206 /* Print some information about current API and connected device. */
207 const uint32_t value = Argus_GetAPIVersion();
208 const uint8_t a = (uint8_t)((value >> 24) & 0xFFU);
209 const uint8_t b = (uint8_t)((value >> 16) & 0xFFU);
210 const uint8_t c = (uint8_t)(value & 0xFFFFU);
211 const uint32_t id = Argus_GetChipID(device);
212 const char * m = Argus_GetModuleName(device);
213
214 printf("\n##### AFBR-S50 API - Advanced Example #########################\n"
215 " API Version: v%d.%d.%d\n"
216 " Chip ID: %d\n"
217 " Module: %s\n"
218 "###############################################################\n\n",
219 a, b, c, id, m);
220 }
221
main(void)222 int main(void)
223 {
224 /* Instantiate and initialize the device handlers. */
225 argus_hnd_t * device = InitializeDevice(SPI_SLAVE);
226
227 /* Print a device information message. */
228 PrintDeviceInfo(device);
229
230 /* Start the measurement timers within the API module.
231 * The callback is invoked every time a measurement has been finished.
232 * The callback is used to schedule the data evaluation routine to the
233 * main thread by the user.
234 * Note that the timer based measurement is not implemented for multiple
235 * instance yet! */
236 status_t status = Argus_StartMeasurementTimer(device, &MeasurementReadyCallback);
237 HandleError(status, true, "Argus_StartMeasurementTimer failed!");
238
239 /* The program loop ... */
240 for (;;)
241 {
242 /* Check if new measurement data is ready. */
243 if (myDataReadyEvents)
244 {
245 IRQ_LOCK();
246 myDataReadyEvents--;
247 IRQ_UNLOCK();
248
249 /* The measurement data structure. */
250 argus_results_t res;
251
252 /* Evaluate the raw measurement results. */
253 status = Argus_EvaluateData(device, &res);
254 HandleError(status, false, "Argus_EvaluateData failed!");
255
256 /* Use the obtain results, e.g. print via UART. */
257 PrintResults(&res);
258 }
259 }
260
261 return 0;
262 }
263