ESP32S3(主模式) 与 STM32(从模式) 进行SPI全双工通信

开发环境:

ESP-IDF: ESP32S3

Keil5:STM32F103C8T6

通信方式:SPI 全双工通信

两天从0摸索的,大部分时间被卡是因为STM32 引脚模式配置错误
效果:

主设备收到上次的数据,是因为全双工原理,可以理解为一个U形管道,通过挤压,配置收发缓冲区进行收发数据

STM32 SPI 从模式配置表:

STM32:

使用SPI2,配置流程:

  1. 时钟配置
  2. 引脚配置
  3. SPI 参数配置
c 复制代码
#ifndef __SPI_COM_H
#define __SPI_COM_H

#include "stm32f10x.h"

#define SPI_NAME  SPI2
#define SPI_MOSI  GPIO_Pin_15// 发送端 SDA    
#define SPI_MISO  GPIO_Pin_14 // 接收端 SDI     
#define SPI_CLK   GPIO_Pin_13 // 时钟 SCL      
#define SPI_CS    GPIO_Pin_12 // 片选 CS       


void SPI_COM_Init(void);

void send_data(void);
#endif
c 复制代码
#include "spi_com.h"

// 定义全局变量
static uint8_t rx_data = 0x00;
        // 处理接收数据,并准备发送数据(全双工)
static uint8_t tx_data = 0x00;

// 初始化 SPI 从机
void SPI_COM_Init(void) {
	    SPI_InitTypeDef SPI_InitStructure;
	    GPIO_InitTypeDef GPIO_InitStructure;
	
	    // 使能 SPI_NAME 和 GPIOA 时钟
	     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
		
		 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);


	      // 配置 SPI_NAME 的引脚
		 GPIO_InitStructure.GPIO_Pin = SPI_MOSI; 
		 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
		 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		 GPIO_Init(GPIOB, &GPIO_InitStructure);
 
	 
	 
		GPIO_InitStructure.GPIO_Pin =  SPI_CLK;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_Init(GPIOB, &GPIO_InitStructure);


		GPIO_InitStructure.GPIO_Pin = SPI_MISO;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_Init(GPIOB, &GPIO_InitStructure);

		 GPIO_InitStructure.GPIO_Pin = SPI_CS; // SS 引脚
		 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 
		 GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 配置 SPI_NAME 为从机模式
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;   // 72M / 4 = 18MHZ 
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 0; //CRC冗余位数
    SPI_Init(SPI_NAME, &SPI_InitStructure);

    // 使能 SPI_NAME
    SPI_Cmd(SPI_NAME, ENABLE);
//	  // 使能 SPI 接收中断
//    SPI_I2S_ITConfig(SPI_NAME, SPI_I2S_FLAG_RXNE, ENABLE);

	 
//	 // 使能 NVIC 中断
//    NVIC_InitTypeDef NVIC_InitStructure;
//    NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;
//    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
//    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
//    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//    NVIC_Init(&NVIC_InitStructure);

}

// 中断服务函数
void SPI2_IRQHandler(void) {
    if (SPI_I2S_GetITStatus(SPI_NAME, SPI_I2S_IT_RXNE) == SET) {
        rx_data = SPI_I2S_ReceiveData(SPI_NAME);
        // 处理接收数据,并准备发送数据(全双工)
        tx_data = rx_data;  // 填充要发送的数据
        SPI_I2S_SendData(SPI_NAME, rx_data);
		 
    }
}

// 软件接收法,可优化为中断控制
void send_data(void){

	   while (SPI_I2S_GetFlagStatus(SPI_NAME, SPI_I2S_FLAG_TXE) == RESET);
	   SPI_I2S_SendData(SPI_NAME, rx_data); 

		while (SPI_I2S_GetFlagStatus(SPI_NAME, SPI_I2S_FLAG_RXNE) == RESET); 
		rx_data = SPI_I2S_ReceiveData(SPI_NAME);
		tx_data = rx_data;

		printf("ReciveData:%d\r\n",rx_data);
		printf("SendData:%d\r\n",tx_data);

}

ESP32S3:

使用SPI3,配置流程:

  1. 总线配置
  2. 设备配置
c 复制代码
#ifndef _SPI_COM_H_
#define _SPI_COM_H_

#include <stdio.h>
#include <string.h>
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h" 

// 定义 SPI 主机编号
#define SPI_HOST_NUM  SPI3_HOST 

// 定义 SPI 最大传输大小
#define SPI_MAX_TRANSFER_SIZE  1024 

#define SPI_SEND_RECIVE_LEN  8*1
// 定义 SPI 引脚编号
#define PIN_NUM_MOSI  GPIO_NUM_1// 发送端 SDA 14
#define PIN_NUM_MISO  GPIO_NUM_14 // 接收端 SDI
#define PIN_NUM_CLK   GPIO_NUM_21 // 时钟 SCL
#define PIN_NUM_CS    GPIO_NUM_47 // 片选 CS

void spi_com_init(void);
esp_err_t spi3_send_data(uint8_t *data);
#endif
c 复制代码
#include "SPI_COM.h"

// 定义日志标签
static const char *TAG = "SPI_COM_TEST";

// SPI句柄
spi_device_handle_t spi;


// SPI2初始化函数
void spi_com_init(void)
{
    spi_bus_config_t buscfg;
    buscfg.miso_io_num = PIN_NUM_MISO;
    buscfg.mosi_io_num = PIN_NUM_MOSI;
    buscfg.sclk_io_num = PIN_NUM_CLK;
    buscfg.quadwp_io_num = -1;
    buscfg.quadhd_io_num = -1;
    buscfg.max_transfer_sz = SPI_MAX_TRANSFER_SIZE;
    buscfg.flags = SPICOMMON_BUSFLAG_MASTER;
    buscfg.isr_cpu_id = ESP_INTR_CPU_AFFINITY_1;
    buscfg.intr_flags = 0;
    // 初始化SPI总线
    esp_err_t ret = spi_bus_initialize(SPI_HOST_NUM, &buscfg, SPI_DMA_CH_AUTO);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "SPI bus 初始化失败: %s", esp_err_to_name(ret));
        return;
    }


    spi_device_interface_config_t devcfg;
    devcfg.command_bits = 0;
    devcfg.address_bits = 0;
    devcfg.dummy_bits = 0;
    devcfg.mode = 0;
    devcfg.clock_source = SPI_CLK_SRC_APB;
    devcfg.clock_speed_hz = 1 * 1000 * 1000; // 1MHz
    devcfg.spics_io_num = PIN_NUM_CS;
    devcfg.queue_size = 10;
    devcfg.flags = 0; // 全双工

    // 将设备添加到SPI总线
    ret = spi_bus_add_device(SPI_HOST_NUM, &devcfg, &spi);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "SPI device 初始化失败: %s", esp_err_to_name(ret));
        spi_bus_free(SPI_HOST_NUM);
        return;
    }

    ESP_LOGI(TAG, "SPI3 初始化成功!");
}
esp_err_t spi3_send_data(uint8_t *data)
{
    if (!spi)
    {
        return ESP_FAIL;
    }


    uint8_t rx_buffer[1];
    spi_transaction_t t;
    t.length = SPI_SEND_RECIVE_LEN; 
    t.rxlength = SPI_SEND_RECIVE_LEN;
    t.tx_buffer = data;
    t.rx_buffer = rx_buffer;
    t.flags = 0;

    // 启动传输
    esp_err_t er = spi_device_transmit(spi, &t);

    ESP_LOGI(TAG, "发送数据:%d",  data[0]);
    ESP_LOGI(TAG, "接收数据:%d",  rx_buffer[0]);
    return er;
}

测试代码

STM32:

c 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "delay.h"
#include "usart.h"
#include "spi_com.h"


// 任务句柄
TaskHandle_t app_start_handle = NULL;
TaskHandle_t app_task1_handle = NULL;
TaskHandle_t app_task2_handle = NULL;

// 任务函数声明
void App_Start(void* pvParamenters);
void App_Task1(void* pvParamenters);
void App_Task2(void* pvParamenters);

// 主函数
int main(void)
{
	//将全部优先级设为抢占优先级 0-15 级
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

    //创建任务
    xTaskCreate((TaskFunction_t)App_Start,          //任务入口函数
               (const char *)"App_Start",          //任务名字
               (uint16_t)configMINIMAL_STACK_SIZE*2, //任务栈大小
               (void*)NULL,                        //任务入口函数参数
               (UBaseType_t)1,                     //任务优先级  提高优先级
               (TaskHandle_t*)&app_start_handle);  //任务句柄
					
	 //开启任务调度器                        
    vTaskStartScheduler();
}

void App_Start(void *pvParameters)
{ 
	
	taskENTER_CRITICAL(); //进入临界区
	usart1_init(115200); // 初始化串口,波特率为 115200
	SPI_COM_Init();
	taskEXIT_CRITICAL();//退出临界区
	 
	 
    xTaskCreate((TaskFunction_t)App_Task1,          //任务入口函数
               (const char *)"App_Task1",          //任务名字
               (uint16_t)configMINIMAL_STACK_SIZE*2, //任务栈大小
               (void*)NULL,                        //任务入口函数参数
               (UBaseType_t)1,                     //任务优先级  提高优先级
               (TaskHandle_t*)&app_task1_handle);  //任务句柄
    //创建任务
    xTaskCreate((TaskFunction_t)App_Task2,          //任务入口函数
               (const char *)"App_Task2",          //任务名字
               (uint16_t)configMINIMAL_STACK_SIZE*2, //任务栈大小
               (void*)NULL,                        //任务入口函数参数
               (UBaseType_t)1,                     //任务优先级
               (TaskHandle_t*)&app_task2_handle);  //任务句柄
					
    vTaskDelete(NULL);	 
}


void App_Task1(void *pvParameters)
{ 
	while(1)
	{
		delay_ms(2000);
		printf("App_Task1 is running...\r\n");
	}

}

void App_Task2(void *pvParameters)
{	

	while(1)
	{
	    delay_ms(10);
        printf("Hello, this is a test message!\r\n");
		send_data();
	}
}

ESP32S3:

c 复制代码
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "SPI_COM.h"
static const char *TAG = "MAIN_TEST";

// 任务句柄
TaskHandle_t task1_handle = NULL;
TaskHandle_t task2_handle = NULL;

// 函数声明
void task_1(void *pvParameters);
void task_2(void *pvParameters);


// 入口函数导出
extern "C" void app_main()
{

    // 创建 task_1 任务,指定运行在核心 0
    BaseType_t result1 = xTaskCreatePinnedToCore(task_1, "task_1", 1024 * 10, NULL, 1, &task1_handle, 0);

    // 创建 task_2 任务,指定运行在核心 0
    BaseType_t result2 = xTaskCreatePinnedToCore(task_2, "task_2", 1024 * 10, NULL, 1, &task2_handle, 0);
}

// task_1 任务函数
void task_1(void *pvParameters)
{
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟 1 秒
        ESP_LOGI(TAG, "这是 task_1 在运行");
    }
}

// task_2 任务函数
void task_2(void *pvParameters)
{
    spi_com_init();
    vTaskDelay(pdMS_TO_TICKS(500));
    uint8_t test_data[] = {0x12};

    while (1)
    {
        ESP_LOGI(TAG, "这是 task_2 在运行");
        vTaskDelay(pdMS_TO_TICKS(500));
        test_data[0]++;
        test_data[0] = test_data[0] % 15;
        spi3_send_data(test_data);
    }
}
相关推荐
Orange--Lin18 分钟前
【ESP32指向鼠标】——icm20948与esp32通信
c语言·单片机·嵌入式硬件
Lllongroad7 小时前
SPI通信及设备驱动
linux·stm32·单片机
零下273°15 小时前
51单片机蜂鸣器铃声代码
单片机·嵌入式硬件·51单片机
猿~~~15 小时前
STM32的HAL库开发---高级定时器PWM输入模式实验
stm32·单片机·嵌入式硬件
聪聪060615 小时前
使用STM32F103C8T6和ESP8266链接阿里云
stm32·嵌入式硬件·阿里云
鹿屿二向箔15 小时前
单片机上SPI和IIC的区别
单片机·嵌入式硬件
weixin_5803820616 小时前
STC51 P0 口 与P1 口输出
单片机·嵌入式硬件
爱吃奶酪的松鼠丶17 小时前
51单片机之使用Keil uVision5创建工程以及使用stc-isp进行程序烧录步骤
嵌入式硬件·51单片机·接口隔离原则
一只搬砖的猹18 小时前
PCA9685舵机控制板使用
stm32·单片机·嵌入式硬件·mcu·pwm·舵机·舵机驱动