Transmisia unor date pe SPI de la STM32F103C6 la ESP32
Acest proiect realizează interfațarea între două microcontrolere,STM32F103C6 și ESP32, pentru a transfera date utilizând Protocolul de Interfață Serială (SPI). Datele sunt preluate de la un potentiometru
conectat la STM32F103C6 și sunt trimise către ESP32, care mai departe poate folosi informațiile pentru controlul altor dispozitive. STM32F103C6 va acționa ca dispozitiv master SPI, iar ESP32 ca dispozitiv slave.
Acest proiect demonstrează un mod eficient de a conecta și de a comunica între două microcontrolere, STM32F103C6 și ESP32, folosind protocolul SPI. Rezultatele obținute pot servi drept bază pentru dezvoltarea deaplicații mai complexe, ce necesită transfer rapid și fiabil de date.
Etape de Implementare
- Configurarea Hardware-ului:
- Conectarea potentiometrului la intrarea analogică a STM32F103C6.
- Conectarea pinii SPI între STM32 și ESP32. Asigurați-vă că există o conexiune stabilă între MOSI, MISO, SCK și SS (Chip Select).
- ESP32 – STM32
- D23(MOSI) – B5 (MOSI1)
- D19 (MISO) – B4 (MISO1)
- D18 (SCK) – B3 (SCK1)
- D5 (CS) – A15 (NSS1)
- Configurarea STM32F103C6:
- Programarea STM32 pentru a citi valorile de la potentiometru folosind ADC (MX_ADC2_Init();) (Convertor Analog-Digital), un ADC foarte performant care oferă valori pe 16 biți la o viteză considerabilă.
- Configurarea interfeței SPI ( MX_SPI1_Init();) pentru a trimite datele către ESP32.
- Crearea unei funcții de transmisie SPI care trimite datele de la ADC către ESP32 (vezi funția main de mai jos).
- Configurarea ESP32:
- Configurarea interfeței SPI pentru a primi datele de la STM32 folosind lib-ul ESP32SPISlave.
- Programarea pentru a citi datele primite și pentru a le procesa sau afișa (vezi funcția loop).
- Utilizarea unui serial monitor pentru a verifica datele primite de la STM32 (vezi Serial).
- Validare și Testare:
- Testarea transmisiei SPI pentru a verifica dacă datele sunt transmise corect.
- Validarea datelor primite pe ESP32 pentru a asigura acuratețea acestora.
- Testarea sistemului în diverse condiții pentru a verifica stabilitatea și fiabilitatea
Componente
- https://www.olx.ro/d/oferta/esp32-wifi-bluetooth-development-board-micro-usb-dual-core-30-pini-IDgQpAY.html?bs=olx_pro_listing
- Placa Electronica de Dezvoltare STM32F103C8T6 ARM STM 32
- ST-LINK V2 Simulator Download Programmer STM32F103C8T6 ARM STM32 Minimum System Development Board STM32F401 STM32F411
Schema electronică/sistem
Cod de test
Codul care rulează pe STM32 (Master)
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "spi.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
int counter =0;
uint16_t pot_value;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC2_Init();
MX_SPI1_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Run the ADC calibration */
if (HAL_ADCEx_Calibration_Start(&hadc2) != HAL_OK)
{
/* Calibration Error */
Error_Handler();
}
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_ADC_Start(&hadc2);
HAL_ADC_PollForConversion(&hadc2,1000);
pot_value=HAL_ADC_GetValue(&hadc2);
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
//HAL_GPIO_ReadPin(POT_PIN_GPIO_Port, POT_PIN_Pin);
HAL_Delay(50);
// Transmite counter-ul pe SPI (16-bit)
uint16_t dataToSend = pot_value;
// Chip select pe LOW pentru începutul transmisiei
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
// Transmitere pe SPI
HAL_SPI_Transmit(&hspi1, (uint8_t *)&dataToSend, 1, HAL_MAX_DELAY);
// Chip select pe HIGH pentru finalul transmisiei
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
// Așteapta inainte de urmatoarea iterație
HAL_Delay(50); // Interval intre incrementari si transmisii
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
* Free pins are configured automatically as Analog (this feature is enabled through
* the Code Generation settings)
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_PIN_GPIO_Port, LED_PIN_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = LED_PIN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_PIN_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PC14 PC15 */
GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : PA5 PA6 PA7 PA8
PA9 PA10 PA11 PA12 */
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB1 PB2 PB10
PB11 PB12 PB13 PB14
PB15 PB6 PB7 PB8
PB9 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
|GPIO_PIN_15|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8
|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* ADC2 init function */
void MX_ADC2_Init(void)
{
/* USER CODE BEGIN ADC2_Init 0 */
/* USER CODE END ADC2_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC2_Init 1 */
/* USER CODE END ADC2_Init 1 */
/** Common config
*/
hadc2.Instance = ADC2;
hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc2.Init.ContinuousConvMode = ENABLE;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.NbrOfConversion = 3;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC2_Init 2 */
/* USER CODE END ADC2_Init 2 */
}
/* SPI1 init function */
void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
Codul care rulează pe ESP32 (Slave)
/*
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
upload_port = COM9
monitor_port = COM9
monitor_speed = 115200
lib_deps = hideakitai/ESP32SPISlave@^0.2.0
*/
#include "ESP32SPISlave.h"
ESP32SPISlave slave;
static constexpr uint32_t BUFFER_SIZE{8};
uint8_t spi_slave_tx_buf[BUFFER_SIZE];
uint8_t spi_slave_rx_buf[BUFFER_SIZE];
#define LED 2
void setup()
{
Serial.begin(115200);
delay(2000);
pinMode(LED, OUTPUT);
// begin() after setting
// VSPI = CS: 5, CLK: 18, MOSI: 23, MISO: 19
slave.setDataMode(SPI_MODE0);
slave.begin(VSPI); // you can use VSPI like this
Serial.println("VSPI with SPI_MODE0 configured! ");
// clear buffers
memset(spi_slave_tx_buf, 0, BUFFER_SIZE);
memset(spi_slave_rx_buf, 0, BUFFER_SIZE);
}
void loop()
{
// if there is no transaction in queue, add transaction
if (slave.remained() == 0)
{
slave.queue(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE);
}
// if transaction has completed from master,
// available() returns size of results of transaction,
// and buffer is automatically updated
char data;
while (slave.available())
{
// show received data
Serial.print("Pot value Received: ");
uint16_t receveived_word1 = (spi_slave_rx_buf[0] << 8) | (spi_slave_rx_buf[1]);
uint16_t receveived_word2 = (spi_slave_rx_buf[2] << 8) | (spi_slave_rx_buf[3]);
uint16_t receveived_word3 = (spi_slave_rx_buf[4] << 8) | (spi_slave_rx_buf[5]);
uint16_t receveived_word4 = (spi_slave_rx_buf[6] << 8) | (spi_slave_rx_buf[7]);
Serial.println(receveived_word1);
Serial.println(receveived_word2);
Serial.println(receveived_word3);
Serial.println(receveived_word4);
if (receveived_word1 > 2000)
{
Serial.println("Setting LED active HIGH ");
digitalWrite(LED, HIGH);
}
else
{
Serial.println("Setting LED active LOW ");
digitalWrite(LED, LOW);
}
Serial.println("");
slave.pop();
}
}
Documentație proiect
- https://www.electronicshub.org/how-to-use-spi-in-stm32f103c8t6/
- https://blog.automatic-house.ro/2023/09/26/mini-proiect-52-comunicatia-spi-intre-doua-placi-esp32-trimiterea-valorii-de-la-potentiometru/
- https://github.com/weewStack/STM32F1-Tutorial
- https://community.st.com/t5/stm32-mcus-products/why-is-my-adc-only-showing-zero-as-an-output-while-simulating/m-p/598332#M224978
- https://esp32.com/viewtopic.php?t=2005
Afiliere eMag
Linkurile de la secțiunea “Componente” conțin adresa mea de afiliere la eMag.ro, iar dacă cumperi folosind aceste linkuri vei susține blogul meu, iar 10 % din donații se vor direcționa pentru fundația dăruiește viată. Mulțumesc !
Mulțumesc pentru atenție!
Pentru întrebări și/sau consultanță tehnică vă stau la dispoziție pe blog mai jos în secțiunea de comentarii sau pe email simedruflorin@automatic-house.ro.
O zi plăcută tuturor !