{"id":1705,"date":"2025-09-04T21:13:59","date_gmt":"2025-09-04T12:13:59","guid":{"rendered":"https:\/\/twarelab.com\/?p=1705"},"modified":"2025-09-04T21:55:47","modified_gmt":"2025-09-04T12:55:47","slug":"stm32f7how-to-exchange-data-between-two-devices-using-spi-communication","status":"publish","type":"post","link":"https:\/\/twarelab.com\/en\/blog\/stm32f7how-to-exchange-data-between-two-devices-using-spi-communication\/","title":{"rendered":"[STM32F7]SPI \ud1b5\uc2e0\uc744 \uc774\uc6a9\ud55c \ub450 \uc7a5\uce58\uac04 \ub370\uc774\ud130 \uad50\ud658 \ubc29\ubc95"},"content":{"rendered":"<p class=\"translation-block\">When working with microcontrollers (MCUs), one of the most common tasks is enabling communication between devices. Among the available options\u2014UART, I\u00b2C, and SPI\u2014<strong>SPI (Serial Peripheral Interface) <\/strong>stands out for its speed and simplicity.<\/p>\n\n\n\n<p class=\"translation-block\">Typically, SPI communication involves a <strong>microcontroller acting as the Master and a dedicated IC functioning as the Slave<\/strong>. The Master generates the clock signal, and the Slave synchronizes to this clock to either send or receive data. This arrangement works seamlessly when the Slave is a specialized IC with hardware logic designed for SPI.<\/p>\n\n\n\n<p class=\"translation-block\">However, things become more complicated when <strong>two MCUs need to talk to each other over SPI<\/strong>. Each MCU is busy running its own application tasks, making it difficult to rely on the conventional SPI mechanism without running into blocking calls, resource contention, or data loss.<\/p>\n\n\n\n<p>In this article, I\u2019ll walk you through why this problem arises, what requirements must be met, and how we designed a practical solution using a dual-SPI setup with DMA.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why Not Just Use UART?<\/h2>\n\n\n\n<p>One might suggest simply using UART for MCU-to-MCU communication.<\/p>\n\n\n\n<p class=\"translation-block\">While UART is widely supported and easy to use, in our case, all UART ports on both devices were already reserved for other functions. That meant UART was not an option, leaving us with <strong>SPI as the most viable path forward<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Requirements for a Robust SPI Solution<\/h2>\n\n\n\n<p>When defining the communication method, we had to meet several important requirements:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Each device must continue running its own tasks without interruption.<\/li>\n\n\n\n<li class=\"translation-block\">The SPI link must be <strong>non-blocking<\/strong>, avoiding scenarios where one device waits indefinitely for data.<\/li>\n\n\n\n<li>Data transfers should be as fast as possible.<\/li>\n\n\n\n<li><strong>- <strong>No data loss can be tolerated<\/strong>, even under heavy workloads.<\/strong><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Design Approach<\/h2>\n\n\n\n<p>To achieve these goals, we implemented a <strong>dual-SPI configuration<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"translation-block\">Two SPI ports are used\u2014one dedicated to transmitting and the other to receiving.<\/li>\n\n\n\n<li><strong>Each port operates in <strong>Half Duplex mode<\/strong>.<\/strong>\n<ul class=\"wp-block-list\">\n<li>The Master port handles transmission only.<\/li>\n\n\n\n<li>The Slave port handles reception only.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li class=\"translation-block\"><strong>DMA (Direct Memory Access)<\/strong> is employed to offload data transfers from the CPU, ensuring minimal MCU resource usage and avoiding bottlenecks.<\/li>\n<\/ul>\n\n\n\n<p>\uc774 \ubc29\uc2dd\uc740 MCU \ub9ac\uc18c\uc2a4\ub97c \uc808\uc57d\ud558\uba74\uc11c\ub3c4 \ube60\ub974\uace0 \uc548\uc815\uc801\uc778 \ub370\uc774\ud130 \uad50\ud658\uc744 \uac00\ub2a5\ud558\uac8c \ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Detailed Implementation<\/h2>\n\n\n\n<p>The figure below shows the conceptual block diagram of the implementation.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"471\" src=\"https:\/\/twarelab.com\/wp-content\/uploads\/2025\/09\/SPI-\uc5f0\uacb0\ub3c4-1024x471.png\" alt=\"\" class=\"wp-image-1706\" srcset=\"https:\/\/twarelab.com\/wp-content\/uploads\/2025\/09\/SPI-\uc5f0\uacb0\ub3c4-1024x471.png 1024w, https:\/\/twarelab.com\/wp-content\/uploads\/2025\/09\/SPI-\uc5f0\uacb0\ub3c4-600x276.png 600w, https:\/\/twarelab.com\/wp-content\/uploads\/2025\/09\/SPI-\uc5f0\uacb0\ub3c4-300x138.png 300w, https:\/\/twarelab.com\/wp-content\/uploads\/2025\/09\/SPI-\uc5f0\uacb0\ub3c4-768x353.png 768w, https:\/\/twarelab.com\/wp-content\/uploads\/2025\/09\/SPI-\uc5f0\uacb0\ub3c4-18x8.png 18w, https:\/\/twarelab.com\/wp-content\/uploads\/2025\/09\/SPI-\uc5f0\uacb0\ub3c4.png 1119w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"translation-block\">To ensure synchronization and data integrity, we introduced two control signals per SPI channel: <strong>NSS<\/strong> and <strong>nCS.<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"translation-block\"><strong>NSS (Hardware-controlled)<\/strong>: Provides timing synchronization for SPI communication.<\/li>\n\n\n\n<li class=\"translation-block\"><strong>nCS (Software-controlled)<\/strong>: Acts as a frame delimiter, indicating when a data frame begins and ends during DMA transfers.<\/li>\n<\/ul>\n\n\n\n<p><strong>Master Side<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"translation-block\">Configure <strong>nCS<\/strong> as a GPIO output.<\/li>\n\n\n\n<li class=\"translation-block\">Before transmitting data, drive <strong>nCS Low<\/strong>.<\/li>\n\n\n\n<li class=\"translation-block\">When the DMA transfer is complete (signaled by the Tx Complete interrupt), set <strong>nCS High<\/strong> again.<\/li>\n<\/ul>\n\n\n\n<p><strong>Slave Side<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"translation-block\">Configure <strong>nCS as a GPIO input<\/strong> with EXTI interrupt capability.<\/li>\n\n\n\n<li class=\"translation-block\">On the Falling Edge<\/strong>, mark the start of a receive frame (Rx Start flag).<\/li>\n\n\n\n<li class=\"translation-block\">On the <strong>Rising Edge<\/strong>, mark the end of the frame (Rx Complete), then move the received data from the DMA buffer into the application buffer for processing.<\/li>\n<\/ul>\n\n\n\n<p>This division of roles keeps the communication non-blocking, synchronized, and efficient.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Test Results<\/h2>\n\n\n\n<p>In real-world testing, the system handled substantial workloads without data loss. Specifically, we successfully exchanged:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>7 UART channels worth of data,<\/strong><\/li>\n\n\n\n<li><strong>24 GPIO control signals, and<\/strong><\/li>\n\n\n\n<li><strong>18 streams of sensor data<\/strong><\/li>\n<\/ul>\n\n\n\n<p class=\"translation-block\">all in real time and without any issues.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p class=\"translation-block\">Establishing reliable communication between two MCUs over SPI is not as straightforward as connecting an MCU to a dedicated IC. By carefully designing the communication scheme\u2014using dual SPI channels, DMA transfers, and additional control signals\u2014it is possible to achieve <strong>fast, non-blocking, and lossless data exchange<\/strong>.<\/p>\n\n\n\n<p>This approach has proven effective in demanding scenarios where multiple data streams must be handled concurrently, and it provides a solid foundation for robust MCU-to-MCU communication in embedded systems.<\/p>\n\n\n\n<p><\/p>","protected":false},"excerpt":{"rendered":"<p>To achieve fast, non-blocking, and lossless data exchange using SPI Communication between two MCUs<\/p>","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":true,"template":"","format":"standard","meta":{"_themeisle_gutenberg_block_has_review":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,13,12],"tags":[76,51,77,74,75],"class_list":["post-1705","post","type-post","status-publish","format-standard","hentry","category-blog","category-en","category-ko","tag-dma","tag-spi","tag-spi-half-duplex","tag-stm32f","tag-stm32f7"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/posts\/1705","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/comments?post=1705"}],"version-history":[{"count":8,"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/posts\/1705\/revisions"}],"predecessor-version":[{"id":1717,"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/posts\/1705\/revisions\/1717"}],"wp:attachment":[{"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/media?parent=1705"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/categories?post=1705"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/twarelab.com\/en\/wp-json\/wp\/v2\/tags?post=1705"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}