{"id":2680,"date":"2017-05-24T15:30:03","date_gmt":"2017-05-24T22:30:03","guid":{"rendered":"https:\/\/visualgdb.com\/w\/?p=2680"},"modified":"2017-05-24T19:05:57","modified_gmt":"2017-05-25T02:05:57","slug":"using-the-spi-interface-on-stm32-devices","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/spi\/","title":{"rendered":"Using the SPI interface on STM32 devices"},"content":{"rendered":"<p>This tutorial shows how to use the SPI interface of the STM32 devices using the STM32CubeMX HAL API. We will configure the SPI in several different modes, show how they affect the generated signal and setup the double-buffered mode to demonstrate continuous uninterrupted mode.<\/p>\n<p>We will use an STM32F4Discovery board to demonstrate the SPI and a Nucleo-F411RE board with <a href=\"http:\/\/analyzer2go.com\/\">Analyzer2Go<\/a> to capture and analyze the generated SPI signals.<\/p>\n<p>Before you begin, install Visual Studio and VisualGDB:<\/p>\n<ol>\n<li>Start Visual Studio and open the VisualGDB Embedded Project Wizard:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/01-newprj1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2681\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/01-newprj1.png\" alt=\"01-newprj\" width=\"941\" height=\"653\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/01-newprj1.png 941w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/01-newprj1-300x208.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/01-newprj1-392x272.png 392w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/01-newprj1-130x90.png 130w\" sizes=\"(max-width: 941px) 100vw, 941px\" \/><\/a><\/li>\n<li>Proceed with the default settings on the first page:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/02-binary.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2682\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/02-binary.png\" alt=\"02-binary\" width=\"738\" height=\"565\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/02-binary.png 738w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/02-binary-300x230.png 300w\" sizes=\"(max-width: 738px) 100vw, 738px\" \/><\/a><\/li>\n<li>On the next page select your STM32 device. In this tutorial we will use the STM32F4Discovery board that uses the STM32F407VG chip, however the techniques described here will work for other chips as well:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/03-device2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2683\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/03-device2.png\" alt=\"03-device\" width=\"738\" height=\"614\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/03-device2.png 738w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/03-device2-300x250.png 300w\" sizes=\"(max-width: 738px) 100vw, 738px\" \/><\/a><\/li>\n<li>Select the default &#8220;LEDBlink&#8221; example and click &#8220;Next&#8221;:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/04-blink.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2684\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/04-blink.png\" alt=\"04-blink\" width=\"738\" height=\"614\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/04-blink.png 738w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/04-blink-300x250.png 300w\" sizes=\"(max-width: 738px) 100vw, 738px\" \/><\/a><\/li>\n<li>Finally specify your debugging settings. For most STM32 devices programmable via ST-Link we recommend using OpenOCD. Connect your board to the computer and click &#8216;Detect&#8217; to automatically detect your ST-Link interface:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/05-debug2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2685\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/05-debug2.png\" alt=\"05-debug\" width=\"738\" height=\"614\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/05-debug2.png 738w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/05-debug2-300x250.png 300w\" sizes=\"(max-width: 738px) 100vw, 738px\" \/><\/a><\/li>\n<li>Click &#8220;Finish&#8221; to generate the basic project and ensure it builds. Now we will modify the project to send a basic message over the STM32 SPI interface. First of all, locate the datasheet for your STM32 device and find the pins that can be used for SPI. For STM32F407VG we will use pins PA4-PA7 with SPI1 (note tha alternate function number that is AF5 in this case):<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/aftable.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2708\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/aftable.png\" alt=\"aftable\" width=\"1412\" height=\"492\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/aftable.png 1412w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/aftable-300x105.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/aftable-1024x357.png 1024w\" sizes=\"(max-width: 1412px) 100vw, 1412px\" \/><\/a><\/li>\n<li>Replace the contents of the main .cpp file with the following code:\n<pre class=\"\">#include &lt;stm32f4xx_hal.h&gt;\r\n\r\n#ifdef __cplusplus\r\nextern \"C\"\r\n#endif\r\nvoid SysTick_Handler(void)\r\n{\r\n\u00a0\u00a0 \u00a0HAL_IncTick();\r\n\u00a0\u00a0 \u00a0HAL_SYSTICK_IRQHandler();\r\n}\r\n\r\n#include &lt;string.h&gt;\r\n\r\nvoid GenerateTestSPISignal()\r\n{\r\n\r\n}\r\n\r\nint main(void)\r\n{\r\n\u00a0\u00a0 \u00a0HAL_Init();\r\n\u00a0\u00a0\u00a0 GenerateTestSPISignal();\r\n}<\/pre>\n<\/li>\n<li>Add the code that will initialize the SPI peripheral to the GenerateTestSPISignal() function:\n<pre class=\"\">\u00a0\u00a0\u00a0 __SPI1_CLK_ENABLE();\r\n\u00a0\u00a0\u00a0 static SPI_HandleTypeDef spi = { .Instance = SPI1 };\r\n\u00a0\u00a0\u00a0 spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;\r\n\u00a0\u00a0\u00a0 spi.Init.Direction = SPI_DIRECTION_2LINES;\r\n\u00a0\u00a0\u00a0 spi.Init.CLKPhase = SPI_PHASE_2EDGE;\r\n\u00a0\u00a0\u00a0 spi.Init.CLKPolarity = SPI_POLARITY_HIGH;\r\n\u00a0\u00a0\u00a0 spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;\r\n\u00a0\u00a0\u00a0 spi.Init.DataSize = SPI_DATASIZE_8BIT;\r\n\u00a0\u00a0\u00a0 spi.Init.FirstBit = SPI_FIRSTBIT_LSB;\r\n\u00a0\u00a0\u00a0 spi.Init.NSS = SPI_NSS_SOFT;\r\n\u00a0\u00a0\u00a0 spi.Init.TIMode = SPI_TIMODE_DISABLED;\r\n\u00a0\u00a0\u00a0 spi.Init.Mode = SPI_MODE_MASTER; \r\n\u00a0\u00a0\u00a0 if (HAL_SPI_Init(&amp;spi) != HAL_OK)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 asm(\"bkpt 255\");\r\n\u00a0\u00a0\u00a0 }<\/pre>\n<\/li>\n<li>If we begin using the SPI peripheral right after calling HAL_SPI_Init(), the peripheral will appear working from the program&#8217;s point of view, but the SPI signals will not be actually be visible on the chip pins. To fix this, we need to explicitly switch pins PA5-PA7 to the SPI mode. We will leave the PA4 pin to be a generic GPIO pin to demonstrate how to control the NSS signal manually:\n<pre class=\"\">\u00a0\u00a0\u00a0 __GPIOA_CLK_ENABLE();\r\n\u00a0\u00a0\u00a0 GPIO_InitTypeDef\u00a0 GPIO_InitStruct;\r\n\u00a0 \r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Pin\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Mode\u00a0\u00a0\u00a0\u00a0\u00a0 = GPIO_MODE_AF_PP;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Pull\u00a0\u00a0\u00a0\u00a0\u00a0 = GPIO_PULLUP;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Speed\u00a0\u00a0\u00a0\u00a0 = GPIO_SPEED_HIGH;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;\r\n\r\n\u00a0\u00a0\u00a0 HAL_GPIO_Init(GPIOA, &amp;GPIO_InitStruct);\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Pin\u00a0 = GPIO_PIN_4;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;\r\n\u00a0\u00a0\u00a0 HAL_GPIO_Init(GPIOA, &amp;GPIO_InitStruct);<\/pre>\n<p>Note that the GPIO_AF5_SPI1 corresponds to the alternate function number (AF5) for SPI1 from the Alternate Function table in the datasheet.<\/li>\n<li>Finally add the following code that will repeatedly transmit the &#8220;Hello, World&#8221; string over the SPI:\n<pre class=\"\">\u00a0\u00a0\u00a0 char message[] = \"Hello, World\";\r\n\u00a0\u00a0\u00a0 for (;;)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_SPI_Transmit(&amp;spi, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_Delay(10);\r\n\u00a0\u00a0\u00a0 }<\/pre>\n<p>Note that we manually control the NSS signal by setting the PA4 pin value to 0 before the transmission and setting it back 1 after the transmission.<\/li>\n<li>Build the project and ensure that is succeeds without any errors:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/06-build1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2686\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/06-build1.png\" alt=\"06-build\" width=\"983\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/06-build1.png 983w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/06-build1-300x226.png 300w\" sizes=\"(max-width: 983px) 100vw, 983px\" \/><\/a><\/li>\n<li>Set a breakpoint on the first call to HAL_GPIO_WritePin() and start debugging. Ensure that the breakpoint hits:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/07-step.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2687\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/07-step.png\" alt=\"07-step\" width=\"983\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/07-step.png 983w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/07-step-300x226.png 300w\" sizes=\"(max-width: 983px) 100vw, 983px\" \/><\/a><\/li>\n<li>Now we will use a logic analyzer to observe the SPI signals produced by the board. In this tutorial will use <a href=\"http:\/\/analyzer2go.com\/\">Analyzer2Go<\/a> and the ST NUCLEO-F411RE board to capture and analyze the signals. If you don&#8217;t have the Nucleo-F411RE board, you can use any of the other <a href=\"http:\/\/analyzer2go.com\/boards\/\">boards supported by Analyzer2Go<\/a> or an external logic analyzer. First start Analyzer2Go, connect the board you want to use as a logic analyzer and select it in the list:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/08-selboard.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2688\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/08-selboard.png\" alt=\"08-selboard\" width=\"848\" height=\"534\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/08-selboard.png 848w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/08-selboard-300x189.png 300w\" sizes=\"(max-width: 848px) 100vw, 848px\" \/><\/a><\/li>\n<li>Then connect the relevant SPI signals (MOSI, SCLK and NSS) to the inputs of the logic analyzer board that are shown in the Analyzer2Go window. Do not forget to connect the ground between the 2 boards:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/2boards.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2710\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/2boards.jpg\" alt=\"2boards\" width=\"800\" height=\"624\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/2boards.jpg 800w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/2boards-300x234.jpg 300w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/a><\/li>\n<li>Finally click on the connected signals in the Analyzer2Go window to immediately see the live SPI signal:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/09-signal.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2689\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/09-signal.png\" alt=\"09-signal\" width=\"1327\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/09-signal.png 1327w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/09-signal-300x167.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/09-signal-1024x571.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/09-signal-800x445.png 800w\" sizes=\"(max-width: 1327px) 100vw, 1327px\" \/><\/a>Observe the repeated pattern: the NSS signal goes from 1 to 0 (indicating the start of a transmission), then the SCK and MOSI signals quickly change the values and the NSS gets back to 1.<\/li>\n<li>Click in the &#8220;Trigger&#8221; column of the NSS signal so that the data frame gets aligned to the falling edge (start of transmission). Then double-click on any of the solid blocks in the SCK or MOSI signals to zoom in to see the actual signal:\u00a0 <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/10-spi.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2690\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/10-spi.png\" alt=\"10-spi\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/10-spi.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/10-spi-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/10-spi-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/10-spi-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a>Observe the typical SPI signal waveform: the SCK clock generates monotonous clock pulses during the transmission and the MOSI signal (Master Output Slave Input) transmits the data one bit at a time.<\/li>\n<li>Add labels to the SCK and MOSI signals by clicking on &#8220;Click to add description&#8221;, then open the Protocol Analyzers tab, add and connect the SPI protocol analyzer:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/11-decoded.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2691\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/11-decoded.png\" alt=\"11-decoded\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/11-decoded.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/11-decoded-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/11-decoded-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/11-decoded-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a>Observe how the &#8220;Hello, World&#8221; message got decoded.<\/li>\n<li>Now we will look how exactly the message is coded when using different SPI modes. Select the &#8216;H&#8217; character and click on the Notes bar to add a note:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/12-hnote.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2692\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/12-hnote.png\" alt=\"12-hnote\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/12-hnote.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/12-hnote-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/12-hnote-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/12-hnote-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>Then switch the MOSI signal view from SPI to Raw to see the exact bits that correspond to the &#8216;H&#8217; byte:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/13-raw.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2693\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/13-raw.png\" alt=\"13-raw\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/13-raw.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/13-raw-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/13-raw-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/13-raw-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>Each of the 8 clock cycles transfers exactly 1 bit of the 8-byte character (MOSI will be low if the bit value is &#8216;0&#8217; and high if the bit value is &#8216;1&#8217;). To visualize this, select the SCK clock cycles corresponding to the &#8216;H&#8217; character and click &#8216;Set this signal as the primary clock&#8217;:\u00a0<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/14-signals.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2694\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/14-signals.png\" alt=\"14-signals\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/14-signals.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/14-signals-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/14-signals-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/14-signals-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>Analyzer2Go will automatically print the value of MOSI in each clock cycle. As we are transferring the least significant bit first, 00010010 corresponds to 0b01001000 that is equal to 0x48 or the ASCII code for the capital &#8216;H&#8217; character:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/15-value.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2695\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/15-value.png\" alt=\"15-value\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/15-value.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/15-value-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/15-value-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/15-value-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>Note that when the MOSI signal needs to change, it changes exactly at the falling clock edge of SCK. It ensures that the signal will have enough time (1\/2 cycle) to stabilize by the time the receiving SPI device actually samples it:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/16-edge.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2696\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/16-edge.png\" alt=\"16-edge\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/16-edge.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/16-edge-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/16-edge-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/16-edge-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>To change this behavior, set the CLKPolarity field of the SPI initialization structure to SPI_POLARITY_LOW instead:\n<pre class=\"\">\u00a0\u00a0\u00a0 spi.Init.CLKPolarity = SPI_POLARITY_LOW;<\/pre>\n<\/li>\n<li>Observe how the MOSI value now changes on the rising edge of SCLK:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/17-reversed.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2697\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/17-reversed.png\" alt=\"17-reversed\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/17-reversed.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/17-reversed-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/17-reversed-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/17-reversed-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a>The correct value to use depends on the specifications of the device you want to interface over SPI. If it samples the MOSI output on rising edges, configure the SPI peripheral to change its value on falling edges. Otherwise set it to change at rising clock edges.<\/li>\n<li>Another important parameter is the clock phase. It affects the state of the SCK signal when no transmission is active. Note that toggling the clock phase also changes the active edge:<br \/>\n<table>\n<tbody>\n<tr>\n<td>CLKPolarity<\/td>\n<td>CLKPhase<\/td>\n<td>MOSI changes on<\/td>\n<td>SCK value when inactive<\/td>\n<\/tr>\n<tr>\n<td>SPI_POLARITY_HIGH<\/td>\n<td>SPI_PHASE_2EDGE<\/td>\n<td>Falling Edge<\/td>\n<td>High<\/td>\n<\/tr>\n<tr>\n<td>SPI_POLARITY_LOW<\/td>\n<td>SPI_PHASE_2EDGE<\/td>\n<td>Rising Edge<\/td>\n<td>Low<\/td>\n<\/tr>\n<tr>\n<td>SPI_POLARITY_LOW<\/td>\n<td>SPI_PHASE_1EDGE<\/td>\n<td>Falling Edge<\/td>\n<td>Low<\/td>\n<\/tr>\n<tr>\n<td>SPI_POLARITY_HIGH<\/td>\n<td>SPI_PHASE_1EDGE<\/td>\n<td>Rising Edge<\/td>\n<td>High<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Set CLKPhase to SPI_PHASE_1EDGE and observe how the signals change:<\/p>\n<pre class=\"\">spi.Init.CLKPhase = SPI_PHASE_2EDGE;<\/pre>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/18-1edge.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2698\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/18-1edge.png\" alt=\"18-1edge\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/18-1edge.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/18-1edge-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/18-1edge-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/18-1edge-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>Now we will show how to configure the SPI peripheral to automatically control the NSS signal. Normally the slave SPI devices will ignore any SPI communication that takes place when their NSS input is inactive (set to 1). This allows sharing the same MOSI, MISO and SCK signals between several slave devices and using the NSS signals (1 for each device) to select which device should be responding to each communication. So far we have been controlling NSS manually using the HAL_GPIO_WritePin() function, however the STM32 SPI peripheral allows generating the NSS signal automatically. Change the initialization code accordingly:\n<pre class=\"\">\u00a0\u00a0\u00a0 spi.Init.NSS = SPI_NSS_HARD_OUTPUT;\r\n    \/\/...\r\n    GPIO_InitStruct.Pin\u00a0 = GPIO_PIN_4;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;\r\n\u00a0\u00a0\u00a0 HAL_GPIO_Init(GPIOA, &amp;GPIO_InitStruct);<\/pre>\n<p>Finally remove the calls to HAL_GPIO_WritePin().<\/li>\n<li>Run the code and observe the new signals. Note how NSS stays low during and between the transmissions:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/19-hardnss.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2699\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/19-hardnss.png\" alt=\"19-hardnss\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/19-hardnss.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/19-hardnss-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/19-hardnss-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/19-hardnss-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>The only way to override this behavior while still using the hardware NSS mode is to disable the SPI peripheral after each transmission:\n<pre class=\"\">\u00a0\u00a0\u00a0 for (;;)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_SPI_Init(&amp;spi);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_SPI_Transmit(&amp;spi, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_SPI_DeInit(&amp;spi);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_Delay(10);\r\n\u00a0\u00a0\u00a0 }<\/pre>\n<\/li>\n<li>Run the updated code and confirm that the NSS signal reappears:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/20-hardcs2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2700\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/20-hardcs2.png\" alt=\"20-hardcs2\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/20-hardcs2.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/20-hardcs2-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/20-hardcs2-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/20-hardcs2-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>We will now step through the HAL_SPI_DeInit() function to see at which point the NSS goes from 0 to 1. Stop your code at a breakpoint and click the &#8220;Record&#8221; button in the Analyzer2Go to begin recording the signal values continuously, then step into and through HAL_SPI_DeInit():<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/21-contin.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2701\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/21-contin.png\" alt=\"21-contin\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/21-contin.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/21-contin-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/21-contin-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/21-contin-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>The NSS signal will go from 0 to 1 once the __HAL_SPI_DISABLE(hspi) line gets executed:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/22-deinit.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2702\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/22-deinit.png\" alt=\"22-deinit\" width=\"1334\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/22-deinit.png 1334w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/22-deinit-300x166.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/22-deinit-1024x568.png 1024w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/22-deinit-800x445.png 800w\" sizes=\"(max-width: 1334px) 100vw, 1334px\" \/><\/a><\/li>\n<li>Use the &#8220;Preprocess selected lines&#8221; command to see that it actually clears the bit #6 (SPE) in the SPI_CR register:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/23-prepro.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2703\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/23-prepro.png\" alt=\"23-prepro\" width=\"1012\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/23-prepro.png 1012w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/23-prepro-300x219.png 300w\" sizes=\"(max-width: 1012px) 100vw, 1012px\" \/><\/a><\/li>\n<li>The STM32 SPI peripheral also supports a special TI mode. Try switching it on by modifying the startup code as follows:\n<pre class=\"\">\u00a0\u00a0\u00a0 spi.Init.TIMode = SPI_TIMODE_DISABLED;<\/pre>\n<p>Note how the NCS signal now stays at 1 during once cycle before the transmission and then gets back to 0 (the bit order is also switched to MSB-first): <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/24-timode.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2704\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/24-timode.png\" alt=\"24-timode\" width=\"1012\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/24-timode.png 1012w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/24-timode-300x219.png 300w\" sizes=\"(max-width: 1012px) 100vw, 1012px\" \/><\/a><\/li>\n<li>Now we will show how to use the STM32 SPI to efficiently transfer large amounts of data (e.g. a stream of values to a digital-to-analog converter). First we will try the simplest approach: generate a block of values, then simply transmit it via HAL_SPI_Transmit():\n<pre class=\"\">#include &lt;string.h&gt;\r\n\r\nunsigned s_TransferBuffer[256];\r\n\r\nvoid FillNextFrame()\r\n{\r\n\u00a0\u00a0\u00a0 static int s_FrameCounter = 0;\r\n\u00a0\u00a0\u00a0 for (int i = 0; i &lt; sizeof(s_TransferBuffer) \/ sizeof(s_TransferBuffer[0]); i++)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 s_TransferBuffer[i] = (s_FrameCounter &lt;&lt; 8) | (i &amp; 0xFF);\r\n\u00a0\u00a0\u00a0 s_FrameCounter++;\r\n}\r\n\r\nvoid GenerateTestSPISignal()\r\n{\r\n\u00a0\u00a0\u00a0 __GPIOA_CLK_ENABLE();\r\n\u00a0\u00a0\u00a0 __SPI1_CLK_ENABLE();\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 static SPI_HandleTypeDef spi = { .Instance = SPI1 };\r\n\u00a0\u00a0\u00a0 spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;\r\n\u00a0\u00a0\u00a0 spi.Init.Direction = SPI_DIRECTION_2LINES;\r\n\u00a0\u00a0\u00a0 spi.Init.CLKPhase = SPI_PHASE_1EDGE;\r\n\u00a0\u00a0\u00a0 spi.Init.CLKPolarity = SPI_POLARITY_LOW;\r\n\u00a0\u00a0\u00a0 spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;\r\n\u00a0\u00a0\u00a0 spi.Init.DataSize = SPI_DATASIZE_8BIT;\r\n\u00a0\u00a0\u00a0 spi.Init.FirstBit = SPI_FIRSTBIT_LSB;\r\n\u00a0\u00a0\u00a0 spi.Init.NSS = SPI_NSS_HARD_OUTPUT;\r\n\u00a0\u00a0\u00a0 spi.Init.TIMode = SPI_TIMODE_DISABLED;\r\n\u00a0\u00a0\u00a0 spi.Init.Mode = SPI_MODE_MASTER; \r\n\u00a0\u00a0\u00a0 if (HAL_SPI_Init(&amp;spi) != HAL_OK)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 asm(\"bkpt 255\");\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 GPIO_InitTypeDef\u00a0 GPIO_InitStruct;\r\n\u00a0 \r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Pin\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Mode\u00a0\u00a0\u00a0\u00a0\u00a0 = GPIO_MODE_AF_PP;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Pull\u00a0\u00a0\u00a0\u00a0\u00a0 = GPIO_PULLUP;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Speed\u00a0\u00a0\u00a0\u00a0 = GPIO_SPEED_HIGH;\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;\r\n\r\n\u00a0\u00a0\u00a0 HAL_GPIO_Init(GPIOA, &amp;GPIO_InitStruct);\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 GPIO_InitStruct.Pin\u00a0 = GPIO_PIN_4;\r\n\u00a0\u00a0\u00a0 HAL_GPIO_Init(GPIOA, &amp;GPIO_InitStruct); \r\n\r\n\u00a0\u00a0\u00a0 for (;;)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FillNextFrame();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 HAL_SPI_Transmit(&amp;spi, (uint8_t *)s_TransferBuffer, sizeof(s_TransferBuffer), HAL_MAX_DELAY);\r\n\u00a0\u00a0\u00a0 }\r\n}<\/pre>\n<p>FillNextFrame() will fill the transfer buffer with 32-bit values starting from (FrameNumber * 0x100) to (FrameNumber * 0x100 + 0xFF). This allows telling the frame and the position within the frame from the value. E.g. 0x002501 means value #01 (starting from #00) in frame 0x25.<\/li>\n<li>Run the updated code. As FillNextFrame() and HAL_SPI_Transmit() cannot run at the same time, you will see gaps between the values corresponding to different frames: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/25-gap.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2705\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/25-gap.png\" alt=\"25-gap\" width=\"1012\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/25-gap.png 1012w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/25-gap-300x219.png 300w\" sizes=\"(max-width: 1012px) 100vw, 1012px\" \/><\/a><\/li>\n<li>To eliminate the gaps, we will configure the SPI to use DMA (direct memory access) with double-buffering. It will automatically transfer the first half of the buffer while your code will be filling the second half and vice versa, resulting in a 100% uninterrupted transmission. First locate the DMA channel and stream that is connected to the SPI1 TX channel:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/dmarq.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2715\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/dmarq.png\" alt=\"dmarq\" width=\"1479\" height=\"689\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/dmarq.png 1479w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/dmarq-300x140.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/dmarq-1024x477.png 1024w\" sizes=\"(max-width: 1479px) 100vw, 1479px\" \/><\/a><\/li>\n<li>Then replace the code after the last call to HAL_GPIO_Init() by the code initializing and starting the DMA:\n<pre class=\"\">\u00a0\u00a0\u00a0 __DMA2_CLK_ENABLE();\r\n\u00a0\u00a0\u00a0 s_DMA.Init.Channel = DMA_CHANNEL_3;\r\n\u00a0\u00a0\u00a0 s_DMA.Init.Direction = DMA_MEMORY_TO_PERIPH;\r\n\u00a0\u00a0\u00a0 s_DMA.Init.PeriphInc = DMA_PINC_DISABLE;\r\n\u00a0\u00a0\u00a0 s_DMA.Init.MemInc = DMA_MINC_ENABLE;\r\n\u00a0\u00a0\u00a0 s_DMA.Init.Mode = DMA_CIRCULAR;\r\n\u00a0\u00a0\u00a0 s_DMA.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;\r\n\u00a0\u00a0\u00a0 s_DMA.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;\r\n\u00a0\u00a0\u00a0 if (HAL_DMA_Init(&amp;s_DMA) != HAL_OK)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 asm(\"bkpt 255\");\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 __HAL_LINKDMA(&amp;spi, hdmatx, s_DMA);\r\n\r\n\u00a0\u00a0\u00a0 HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);\r\n\u00a0\u00a0\u00a0 if (HAL_SPI_Transmit_DMA(&amp;spi, (uint8_t *)s_TransferBuffer, sizeof(s_TransferBuffer)) != HAL_OK)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 asm(\"bkpt 255\");\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 for (;;)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ;<\/pre>\n<\/li>\n<li>Then add the following code before the GenerateTestSPISignal() function:\n<pre class=\"\">DMA_HandleTypeDef s_DMA = { .Instance = DMA2_Stream5 };\r\n\r\nextern \"C\" void DMA2_Stream5_IRQHandler()\r\n{\r\n\u00a0\u00a0\u00a0 HAL_DMA_IRQHandler(&amp;s_DMA);\r\n}\r\n\r\nstatic int s_FrameCounter = 0;\r\n\r\nvoid HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi)\r\n{\r\n\u00a0\u00a0\u00a0 for (int i = 0; i &lt; sizeof(s_TransferBuffer) \/ sizeof(s_TransferBuffer[0]) \/ 2; i++)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 s_TransferBuffer[i] = (s_FrameCounter &lt;&lt; 8) | (i &amp; 0xFF);\r\n}\r\n\r\nvoid HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)\r\n{\r\n\u00a0\u00a0\u00a0 for (int i = sizeof(s_TransferBuffer) \/ sizeof(s_TransferBuffer[0]) \/ 2; i &lt; sizeof(s_TransferBuffer) \/ sizeof(s_TransferBuffer[0]); i++)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 s_TransferBuffer[i] = (s_FrameCounter &lt;&lt; 8) | (i &amp; 0xFF);\r\n\u00a0\u00a0\u00a0 s_FrameCounter++;\r\n}<\/pre>\n<\/li>\n<li>By using HAL_SPI_Transmit_DMA() instead of HAL_SPI_Transmit() we tell the SPI peripheral to start transmitting s_TransferBuffer using the background direct memory access mechanism and immediately return control. When the first half of the buffer is transmitted, the DMA will raise an interrupt resulting in a call to HAL_SPI_TxHalfCpltCallback() that will generate the first half of the next frame while the current frame is still being transferred. When the entire buffer is transferred, HAL_SPI_TxCpltCallback() will get called and will generate the second half of the frame (while the DMA is already sending the first half of it). This results in 100% uninterrupted transmission without any gaps:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/26-nogap.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2706\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/26-nogap.png\" alt=\"26-nogap\" width=\"996\" height=\"506\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/26-nogap.png 996w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/26-nogap-300x152.png 300w\" sizes=\"(max-width: 996px) 100vw, 996px\" \/><\/a><\/li>\n<li>You can visualize the transfer timings by replacing the empty loop at the end of GenerateTestSPISignal() with the loop toggling the NSS signal (don&#8217;t forget to configure PA4 as GPIO_MODE_OUTPUT_PP):\n<pre class=\"\">\u00a0\u00a0\u00a0 for (;;)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_4);<\/pre>\n<\/li>\n<li>Run the updated code. Observe how the NSS signal value stops changing for a short time (while one of the CpltCallback functions is running), but the SPI transmission runs uninterrupted:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/27-intgap.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2707\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/27-intgap.png\" alt=\"27-intgap\" width=\"996\" height=\"585\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/27-intgap.png 996w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/27-intgap-300x176.png 300w\" sizes=\"(max-width: 996px) 100vw, 996px\" \/><\/a><\/li>\n<li>You can set a breakpoint in one of the CpltCallback functions and use the Call Stack window to see the chain of functions involved in handling the interrupt:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/isr.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2712\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/isr.png\" alt=\"isr\" width=\"1012\" height=\"740\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/isr.png 1012w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2017\/05\/isr-300x219.png 300w\" sizes=\"(max-width: 1012px) 100vw, 1012px\" \/><\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to use the SPI interface of the STM32 devices using the STM32CubeMX HAL API. We will<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16],"tags":[120,61],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/2680"}],"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=2680"}],"version-history":[{"count":5,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/2680\/revisions"}],"predecessor-version":[{"id":2716,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/2680\/revisions\/2716"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=2680"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=2680"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=2680"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}