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