{"id":8689,"date":"2024-03-07T05:49:19","date_gmt":"2024-03-07T13:49:19","guid":{"rendered":"https:\/\/visualgdb.com\/w\/?p=8689"},"modified":"2024-03-07T05:49:19","modified_gmt":"2024-03-07T13:49:19","slug":"using-software-tracing-to-record-adc-samples","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/tracing\/embedded\/adc\/","title":{"rendered":"Using Software Tracing to Record ADC Samples"},"content":{"rendered":"<p>This tutorial shows how to use VisualGDB&#8217;s <a href=\"https:\/\/visualgdb.com\/documentation\/tracing\/\">Software Tracing<\/a> feature to record the data processed by the embedded application without disrupting its workflow.<\/p>\n<p>We will create a basic program that continuously samples an analog input of an STM32 chip using ADC and DMA, computes the root mean square of the deviation (corresponding to signal noisiness) and toggles an LED if the deviation exceeds the threshold. We will then use the software tracing to record all deviation values computed by the program, the exact inputs that triggered the check, and will also use it to check the state of the DMA controller at different parts of the program, to make sure the check has sufficient time to run.<\/p>\n<ol>\n<li>Start Visual Studio and begin creating a new Embedded VisualGDB project:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/01-newprj.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8690\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/01-newprj.png\" alt=\"\" width=\"1014\" height=\"675\" \/><\/a><\/li>\n<li>Select the name and location of the project: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/02-name.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8691\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/02-name.png\" alt=\"\" width=\"1014\" height=\"675\" \/><\/a><\/li>\n<li>Proceed with creating a CMake-based embedded application: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/03-cmake.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8692\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/03-cmake.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>Select the ARM toolchain and the device you would like to target:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/04-device.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8693\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/04-device.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>In this tutorial we will use the STM32F4Discovery board that has numerous examples in the STM32 SDKs. Hence, pick the <strong>ADC_RegularConversion_DMA<\/strong> sample and click <strong>Next<\/strong> to proceed:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/05-adc.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8694\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/05-adc.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>Plug your board into the USB port and make sure VisualGDB recognizes it and fills the debug settings automatically. Then, click Finish to create the project: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/06-debug.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8695\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/06-debug.png\" alt=\"\" width=\"914\" height=\"693\" \/><\/a><\/li>\n<li>Once the project is created, check the ADC channel used by it. The STM32 example project for STM32F4Discovery uses ADC1 channel 8:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/07-adc.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8696\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/07-adc.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>The example project keeps writing the captured ADC samples into the same memory location, which is not very practical. Instead, we will define a 512-sample buffer, and will configure DMA to write it in a circular fashion. Declare the following global variable:\n<pre class=\"\">__IO uint16_t g_ConvertedData[512];<\/pre>\n<p>Then, change the <strong>HAL_ADC_Start_DMA()<\/strong> call in <strong>main()<\/strong> to look like this:<\/p>\n<pre class=\"\">if (HAL_ADC_Start_DMA(&amp;AdcHandle,\r\n    (uint32_t*)g_ConvertedData, sizeof(g_ConvertedData) \/ sizeof(g_ConvertedData[0])) != HAL_OK)\r\n{\r\n    Error_Handler(); \r\n}<\/pre>\n<p>Finally, switch the peripheral and memory data alignment in <strong>stm32f4xx_hal_msp.c<\/strong> file to half-word, matching the 16-bit sample size:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/08-halfword.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8697\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/08-halfword.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Now add a function that would process the captured data. Each time half of the DMA buffer gets filled, it will compute the mean value of the data in it together with the deviation, and will toggle the LED5 if the deviation exceeds a certain threshold:\n<pre class=\"\">static void ProcessDataBlock(volatile uint16_t *pData, int count)\r\n{\r\n    int sum = 0;\r\n    for (int i = 0; i &lt; count; i++)\r\n        sum += pData[i];\r\n    int mean = sum \/ count;\r\n    int deviationSquareSum = 0;\r\n\r\n    for (int i = 0; i &lt; count; i++)\r\n    {\r\n        int deviation = pData[i] - mean;\r\n        deviationSquareSum += (deviation * deviation);\r\n    }\r\n\r\n    int rootMeanSquareDeviation = (int)sqrt(deviationSquareSum);\r\n    if (rootMeanSquareDeviation &gt; 150)\r\n    {\r\n        BSP_LED_Toggle(LED5);\r\n    }\r\n}\r\n\r\nenum {\r\n    kHalfBufferSize = sizeof(g_ConvertedData) \/ sizeof(g_ConvertedData[0]) \/ 2\r\n};\r\n\r\nvoid HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)\r\n{\r\n    ProcessDataBlock(g_ConvertedData + kHalfBufferSize, kHalfBufferSize);\r\n}\r\n\r\nvoid HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *AdcHandle)\r\n{\r\n    ProcessDataBlock(g_ConvertedData, kHalfBufferSize);\r\n}<\/pre>\n<\/li>\n<li>Depending on your toolchain version, you may get an error about missing <strong>__errno<\/strong>. If this happens, simply declare it as a global variable (<strong>int __errno<\/strong>):<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/09-process.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8698\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/09-process.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Press F5 to start debugging and set a breakpoint in <strong>ProcessDataBlock().<\/strong> You can set a regular breakpoint in <strong>ProcessDataBlock()<\/strong> and even visualize the contents of the buffer, however once the breakpoint triggers, the device will stop processing further data until it is resumed:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/10-graph.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8699\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/10-graph.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Software Tracing is designed to avoid exactly that. Instead of stopping at a breakpoint, it quickly captures the selected data in an internal buffer and continues running. Create a tracepoint in the line that checks <strong>rootMeanSquareDeviation<\/strong>. Call it &#8220;computed deviation&#8221; and enable tracing of the computed value: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/11-traced.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8700\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/11-traced.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Start debugging. The program will start running and the tracepoint will be generating events in real time, as long as the JTAG connection is fast enough to read them out from the RAM: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/12-dropped.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8701\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/12-dropped.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>If you experience too many dropped events, try increasing the trace buffer size via VisualGDB Project Properties -&gt; Software Tracing:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/13-32k.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8702\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/13-32k.png\" alt=\"\" width=\"1105\" height=\"734\" \/><\/a><\/li>\n<li>Open the <strong>Debug-&gt;Windows-&gt;Tracepoints <\/strong>window while the program is running. You will see the list of all events, along with the data captured by them. Select any of the events, locate the <strong>rootMeanSquareDeviation<\/strong> in the Trace Data window, right-click on it and select &#8220;Plot in a new graph view&#8221;:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/14-plot.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8703\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/14-plot.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>VisualGDB will show the graph of all computed deviation values: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/15-plotted.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8704\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/15-plotted.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a>If the trace buffer was large enough to not drop any events, the graph will show every single value ever processed by the firmware.<\/li>\n<li>Add another tracepoint on the line that toggles the LED and configure it to trace the entire <strong>g_ConvertedData<\/strong>:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/16-high.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8705\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/16-high.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Connect the ADC to a noisy signal (in this tutorial we will just quickly rotate a potentiometer) and let the tracepoint generate some events. Then, go back to the graph view showing deviation values:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/17-twopoints.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8706\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/17-twopoints.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Select the &#8220;deviation too high&#8221; tracepoint in the tracepoint view and pick any events there. It will highlight the nearest deviation value (captured from another tracepoint) on the graph:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/18-toohigh.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8707\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/18-toohigh.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a>You can also navigate it the other way. Double-click on any point in the graph to see the nearby &#8220;Deviation too high&#8221; events in the event list.<\/li>\n<li>You can use the <strong>Trace Data<\/strong> view to see the exact values in the <strong>g_ConvertedData<\/strong> buffer that lead to the excessive deviation. You can also view them in the Memory view via the &#8220;details&#8221; button, or dump to a file from there:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/19-data.png\"><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8708\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/19-data.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Switch the graph view into a table and drag <strong>g_ConvertedData-&gt;[0]<\/strong> to the bottom area. VisualGDB will now show events from both tracepoints in the table. See how deviation values of <strong>104<\/strong>, <strong>148<\/strong> and <strong>145<\/strong> don&#8217;t trigger the &#8220;too high&#8221; event, and values of <strong>158<\/strong> and <strong>169<\/strong> do (recording the exact input data):<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/20-log.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8709\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/20-log.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Once you finish the debug session, VisualGDB will save all traced data in a trace report that can be replayed later at any time: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/21-report.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8710\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/21-report.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Finally, we will show how to use software tracing to verify that the software can process the data faster than the DMA produces it. Create tracepoints at the beginning and end of <strong>HAL_ADC_ConvCpltCallback()<\/strong> and <strong>HAL_ADC_ConvHalfCpltCallback()<\/strong>, capturing the value of <strong>DMA2_Stream0-&gt;NDTR<\/strong>:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/22-ndtr.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8711\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/22-ndtr.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Once the tracepoints generate enough events, create a new table view: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/23-table.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8712\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/23-table.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Drag the NDTR values from all 4 tracepoints into the bottom area of the view. Normally, the completion event would get triggered when NDTR is reset to 512 after reaching 0, and the half-completion event would correspond to NDTR being at <strong>256<\/strong>. The trace shows that it takes 3 ADC cycles for <strong>HAL_ADC_ConvCpltCallback()<\/strong> to get called by the interrupt handler and <strong>111<\/strong> cycles to process the data, leaving <strong>145<\/strong> cycles until the next half-completion interrupt:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/24-timing.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8713\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/24-timing.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<li>Once you finished the measurements, you can save the tracepoint configuration into an XML file, and reload it later when you want to do the same measurements again:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/25-export.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8714\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2024\/02\/25-export.png\" alt=\"\" width=\"1262\" height=\"823\" \/><\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to use VisualGDB&#8217;s Software Tracing feature to record the data processed by the embedded application without<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[247],"tags":[248,204,61],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8689"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=8689"}],"version-history":[{"count":2,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8689\/revisions"}],"predecessor-version":[{"id":8845,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8689\/revisions\/8845"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=8689"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=8689"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=8689"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}