ESP32S3(主模式) 与 STM32(从模式) 进行SPI全双工通信
开发环境:
ESP-IDF: ESP32S3
Keil5:STM32F103C8T6
通信方式:SPI 全双工通信
两天从0摸索的,大部分时间被卡是因为STM32 引脚模式配置错误
效果:
主设备收到上次的数据,是因为全双工原理,可以理解为一个U形管道,通过挤压,配置收发缓冲区进行收发数据
STM32 SPI 从模式配置表:
STM32:
使用SPI2,配置流程:
- 时钟配置
- 引脚配置
- SPI 参数配置
#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
#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,配置流程:
- 总线配置
- 设备配置
#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
#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:
#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:
#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);
}
}