STM32与ESP32的硬件SPI通信(个人学习记录)

一、硬件配置

        STM32F103ZET6作为主机,使用SPI2,ESP32S2作为从机,进行SPI双向通信;硬件接线如下:

                                                主机                        从机

CS                                           PB12 ——————  14

MOSI                                        PB6 ——————    2

MISO                                       PB14 ——————  13

CLK                                         PB15 ——————  12

HANDSHAKE                         PB13 ——————   15

GND                                        GND ——————   GND   

(地线一定相连在一起,不然传输的数据会乱码)

二、主机代码

#define SPI2READY	PBin(6)    //读取握手线是否准备好

void SPI2_Init(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;

	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO, ENABLE );
	RCC_APB1PeriphClockCmd(	RCC_APB1Periph_SPI2,  ENABLE );//SPI2时钟使能 	
	
    //CS片选线
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //PB12推挽输出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
	
    //HANDSHAKE 握手线
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6;	     
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;     //PB6下拉输入 
	GPIO_Init(GPIOB, &GPIO_InitStructure);            
	
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB

 	GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15上拉

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	//SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		    //串行同步时钟的空闲状态为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	    //第一个跳变沿数据被采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		    //软件管理
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;        	//CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);                 //初始化外设SPIx寄存器
 
	SPI_Cmd(SPI2, ENABLE);                             //使能SPI外设
}  

//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) 
	{
        retry++;
        if(retry>200) return 0;    //发送上一个数据时间过长,报错
    }			  
	SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
	retry=0;
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) 
	{
		retry++;
		if(retry>200) return 0;
	}	  						    
	return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据					    
}

u8 TXBuffer[128];
u8 RXBuffer[128];

 int main(void)
 {	 
	u16 i=0;
	delay_init();	    	 //延时函数初始化	  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2
	uart_init(115200);	 	//串口初始化为115200
	LED_Init();		  		//初始化与LED连接的硬件接口	
	SPI2_Init();
	
	 for (i = 0; i < 10; i++)    //给发送数组赋初值
    {
        TXBuffer[i] = 0x47+i;
    }
	 
	while(1)
	{
		
		if(SPI2READY==1)        //如果从机准备好接收
		{
			GPIO_ResetBits(GPIOB,GPIO_Pin_12);	    //拉低CS
			for(i=0;i<10;i++)                       //发送数据并接收从机返回数据
			{
				RXBuffer[i]=SPI2_ReadWriteByte(TXBuffer[i]);
			}
			GPIO_SetBits(GPIOB,GPIO_Pin_12);        //拉高CS
			LED0 = ~LED0;                           //指示灯闪烁
		}
		printf("%s\r\n",RXBuffer);                  //串口打印从机接收到的数据
		delay_ms(1000);                                
	}
}




 三、从机代码

根据官方所给例程修改

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_log.h"
#include "driver/spi_slave.h"
#include "driver/gpio.h"

#define GPIO_HANDSHAKE 2
#define GPIO_MOSI 12
#define GPIO_MISO 13
#define GPIO_SCLK 15
#define GPIO_CS 14

#ifdef CONFIG_IDF_TARGET_ESP32
#define RCV_HOST    HSPI_HOST
#else
#define RCV_HOST    SPI2_HOST
#endif

在事务进入队列并准备由master提取时调用。我们用这个来设置握手线
void my_post_setup_cb(spi_slave_transaction_t *trans) {
    gpio_set_level(GPIO_HANDSHAKE, 1);
}

//事务发送/接收后调用。我们用这个来降低握手线。
void my_post_trans_cb(spi_slave_transaction_t *trans) {
    gpio_set_level(GPIO_HANDSHAKE, 0);
}

//Main application
void app_main(void)
{
    int n=0;
    esp_err_t ret;

    //Configuration for the SPI bus
    spi_bus_config_t buscfg={
        .mosi_io_num=GPIO_MOSI,
        .miso_io_num=GPIO_MISO,
        .sclk_io_num=GPIO_SCLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
    };

    //Configuration for the SPI slave interface
    spi_slave_interface_config_t slvcfg={
        .mode=0,                    //CPOL 0,CPHA 0 (注意模式配置,需要与主机一致) 
        .spics_io_num=GPIO_CS,
        .queue_size=3,              
        .flags=0,
        .post_setup_cb=my_post_setup_cb,
        .post_trans_cb=my_post_trans_cb
    };

    //Configuration for the handshake line
    gpio_config_t io_conf={
        .intr_type=GPIO_INTR_DISABLE,
        .mode=GPIO_MODE_OUTPUT,
        .pin_bit_mask=(1<<GPIO_HANDSHAKE)
    };

    //Configure handshake line as output
    gpio_config(&io_conf);
    //Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
    gpio_set_pull_mode(GPIO_MOSI, GPIO_PULLUP_ONLY);
    gpio_set_pull_mode(GPIO_SCLK, GPIO_PULLUP_ONLY);
    gpio_set_pull_mode(GPIO_CS, GPIO_PULLUP_ONLY);

    //Initialize SPI slave interface
    ret=spi_slave_initialize(RCV_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO);
    assert(ret==ESP_OK);

    WORD_ALIGNED_ATTR char sendbuf[129]="";
    WORD_ALIGNED_ATTR char recvbuf[129]="";
    memset(recvbuf, 0, 33);     //将前33个字节填充为0     
    spi_slave_transaction_t t;  
    memset(&t, 0, sizeof(t));

    while(1) {
        //Clear receive buffer, set send buffer to something sane
        memset(recvbuf, 0x00, 129);     //将129个字节全部填充为0xA5

        for(int j=0;j<26;j++)    //给从机发送缓存区赋值
        {
            sendbuf[j]=0x41+j;
        }

        //Set up a transaction of 128 bytes to send/receive
        t.length=20*8;             //设置一次接收/发送的最大值,必须比主机一次发送的数据大小要大,否则会乱序
        t.tx_buffer=sendbuf;
        t.rx_buffer=recvbuf;
        
        ret=spi_slave_transmit(RCV_HOST, &t, portMAX_DELAY);
       // spi_slave_queue_trans(RCV_HOST, &t, portMAX_DELAY);    //可以用这个函数,但是没有验证,容易出错

        printf("Received: %s\n", recvbuf);
        n++;
    }
}

 

运行顺序应该是从机接收先复位,再等待主机复位;如果主机先复位,则接收时序可能会不正确。

 四、运行结果

主机接收到从机返回的数据

 

从机接收到主机发送的数据

### ESP8266STM32硬件连接方法 ESP8266 Wi-Fi模块可以通过串口STM32单片机进行通信,实现数据交换和控制功能。以下是具体的接线方法: #### 接线说明 1. **电源供电** STM32通常工作在3.3V电压下,因此需要确保ESP8266也使用3.3V供电[^1]。如果STM32板上的稳压电路能够支持额外负载,则可以直接从STM32取电;否则建议单独为ESP8266提供稳定的3.3V电源。 2. **UART接口连接** 使用USART协议作为主要通讯手段时,需完成以下信号线对接: - 将ESP8266的TXD管脚接到STM32的RXD管脚上; - 同样地,把ESP8266的RXD连至STM32对应的TXD端子上。 需要注意的是,由于ESP8266的工作逻辑电平也是3.3V,而某些型号的STM32可能接受5V输入,在这种情况下无需担心损坏设备,但如果不确定兼容情况最好还是加装电平转换器来保护双方器件。 3. **复位使能引脚处理** 对于一些版本较新的ESP模组而言,存在RST(Reset)以及CH_PD(CPU Power Down)两个重要控制针脚。为了正常启动并维持运行状态,应将这两个分别拉高到3.3伏特水平。 4. **GND共地设置** 不可遗漏最后一步操作即把两者的接地(Ground)统一起来形成完整的回路结构以便顺利传递电信号。 ```c // 示例代码展示如何初始化串口用于同esp8266交互 void UART_Init(void){ USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); GPIO_InitStructre(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // TX & RX Pins GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA,&GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200;//波特率设定匹配esp8266默认速率 USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No ; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART1,&USART_InitStructure); } ``` 上述C语言片段演示了针对ARM Cortex-M系列MCU配置通用异步收发传输器(UART)的过程,从而建立物理层面上的数据链路基础架构以服务于后续高级别的网络应用层面开发需求。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值