赞
踩
STM32与ESP32通过SPI进行数据传输,ESP32采用ESP-IDF进行编程;STM32当做主机,esp32当从机进行通讯。
其中STM32芯片采用STM32F446RET6,系统时钟168M,ESP32采用ESP32-WROOM_32UE,默认配置,编程软件采用VS Code。
通讯时使用esp32 spi slave历程,端口总是显示乱码:
出现乱码的原因:
1、主机STM32的SPI工作模式与esp32的SPI工作模式没有一一对应;
2、主机STM32与从机ESP32之间没有共地;
3、ESP32历程中的发送初始化函数未进行修改:memset(recvbuf, 0xA5, 128);
例程中默认使用0xA5初始化,使用0xA5初始化时,esp32与esp32之间通讯正常,但是与stm32通讯的时候,当stm32发送的数据太少,esp32端乱码显示或直接不显示,需要替换为 memset(recvbuf, 0x00, 128);
。
下面直接上程序:spi.h
#ifndef __SPIX_H #define __SPIX_H #include "stm32f4xx.h" //RCC时钟 #define SPI_B_clock RCC_AHB1Periph_GPIOB //GPIOB时钟 #define NRF_CS_clock RCC_AHB1Periph_GPIOD #define SPI_X_clock RCC_APB2Periph_SPI1 //ADC1时钟 //SPI_GPIO #define SPI_GPIOB GPIOB #define NRF_GPIO_CS GPIOD #define NRF_CS_SCL GPIO_Pin_2 #define SPI_X_SCL GPIO_Pin_3 #define ADC_X_MOSI GPIO_Pin_5 #define ADC_X_MISO GPIO_Pin_4 #define SPI__SCL GPIO_PinSource3 #define ADC__MOSI GPIO_PinSource4 #define ADC__MISO GPIO_PinSource5 #define SPIx_X SPI1 void SPI1_Init(void); u8 SPI1_ReadWriteByte(u8 TxData); void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler); u8 spi_Write_Reg(u8 reg,u8 value); //SPI写寄存器 u8 spi_Read_Reg(u8 reg); //读取SPI寄存器值 u8 spi_Read_Buf(u8 reg,u8 *pBuf,u8 len); //在指定位置读出指定长度的数据 u8 spi_Write_Buf(u8 reg, u8 *pBuf, u8 len); //在指定位置写指定长度的数据 #endif
STM32的spi.c程序:
#include "SPIx.h" #include "sys.h" void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHB1PeriphClockCmd(SPI_B_clock, ENABLE);//使能GPIOB时钟 RCC_AHB1PeriphClockCmd(NRF_CS_clock , ENABLE); //使能cs时钟 RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1, ENABLE );//SPI2时钟使能 //CS片选线 GPIO_InitStructure.GPIO_Pin =NRF_CS_SCL; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //PB12推挽输出 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化GPIOB //HANDSHAKE 握手线 GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //PB6下拉输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOFB3,4,5初始化设置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1 GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1 GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1 //这里只针对SPI口初始化 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI // SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; //设置SPI工作模式:设置为从SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构 // SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平 // SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为高电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //定义波特率预分频的值:波特率预分频值为256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式 SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 SPI_Cmd(SPI1, ENABLE); //使能SPI外设 SPI1_ReadWriteByte(0xff);//启动传输 } //SPI1速度设置函数 //SPI速度=fAPB2/分频系数 //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256 //fAPB2时钟一般为84Mhz: void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler) { assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性 SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率 SPI1->CR1|=SPI_BaudRatePrescaler; //设置SPI1速度 SPI_Cmd(SPI1,ENABLE); //使能SPI1 } //SPI1 读写一个字节 //TxData:要写入的字节 //返回值:读取到的字节 u8 SPI1_ReadWriteByte(u8 TxData) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空 SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte 数据 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据 } //SPI写寄存器 //reg:指定寄存器地址 //value:写入的值 u8 spi_Write_Reg(u8 reg,u8 value) { u8 status; PDout(2)=0; //使能SPI传输 status =SPI1_ReadWriteByte(reg);//发送寄存器号 SPI1_ReadWriteByte(value); //写入寄存器的值 PDout(2)=1; //禁止SPI传输 return(status); //返回状态值 } //读取SPI寄存器值 //reg:要读的寄存器 u8 spi_Read_Reg(u8 reg) { u8 reg_val; PDout(2) = 0; //使能SPI传输 SPI1_ReadWriteByte(reg); //发送寄存器号 reg_val=SPI1_ReadWriteByte(0XFF);//读取寄存器内容 PDout(2) = 1; //禁止SPI传输 return(reg_val); //返回状态值 } //在指定位置读出指定长度的数据 //reg:寄存器(位置) //*pBuf:数据指针 //len:数据长度 //返回值,此次读到的状态寄存器值 u8 spi_Read_Buf(u8 reg,u8 *pBuf,u8 len) { u8 status,u8_ctr; status=SPI1_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值 for(u8_ctr=0;u8_ctr<len;u8_ctr++)pBuf[u8_ctr]=SPI1_ReadWriteByte(0x00);//读出数据 return status; //返回读到的状态值 } //在指定位置写指定长度的数据 //reg:寄存器(位置) //*pBuf:数据指针 //len:数据长度 //返回值,此次读到的状态寄存器值 u8 spi_Write_Buf(u8 reg, u8 *pBuf, u8 len) { u8 status,u8_ctr; PDout(2) = 0; //使能SPI传输 status = SPI1_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值 for(u8_ctr=0; u8_ctr<len; u8_ctr++)SPI1_ReadWriteByte(*pBuf++); //写入数据 PDout(2) = 1; //关闭SPI传输 return status; //返回读到的状态值 } SPIx 读写一个字节 TxData:要写入的字节 返回值:读取到的字节 //u8 SPI1_ReadWriteByte(u8 TxData) //{ // u8 retry=0; // while (SPI_I2S_GetFlagStatus(SPIx_X, SPI_I2S_FLAG_TXE) == RESET) // { // retry++; // if(retry>200) {RGB_Color(yellow);return 0;} //发送上一个数据时间过长,报错 // } // SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据 // retry=0; // RGB_Color(red); // while (SPI_I2S_GetFlagStatus(SPIx_X, SPI_I2S_FLAG_TXE) == RESET) // { // retry++; // if(retry>200) return 0; // } // return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据 //}
main.c函数
int main() { delay_init(168); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//串口使用了中断 uart1_init(9600);//通讯串口 LED_Init(); SPI1_Init(); int i; u8 ch[128]={"abcdefghijklmnopqrst"},tm[128]; while(1) { if(PBin(6)==1) { PDout(2)=0; // spi_Write_Buf(0x61,ch,128);//向esp32传输ch内容 spi_Read_Buf(0x61,tm,128);//接收esp32传输回来的内容 PDout(2)=1; RGB_Color(yellow); USARTx_p=USART1; } else { RGB_Color(red); } printf("%s\n",tm); } }
ESP32历程修改
安装好VS CODE的ESP IDF编程环境后,按F1打开命令窗口,找到例程,打开例程,选择spi slave的receiver例程,步骤如下:
修改例程后的代码如下:
#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 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); } 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, .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); // gpio_set_pull_mode(GPIO_MOSI, GPIO_PULLDOWN_ONLY); // gpio_set_pull_mode(GPIO_SCLK, GPIO_PULLDOWN_ONLY); // gpio_set_pull_mode(GPIO_CS, GPIO_PULLDOWN_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); spi_slave_transaction_t t; memset(&t, 0, sizeof(t)); while(1) { //Clear receive buffer, set send buffer to something sane memset(recvbuf, 0x00, 128); sprintf(sendbuf,"bcdefgh"); //Set up a transaction of 128 bytes to send/receive t.length=128*8; t.tx_buffer=sendbuf; t.rx_buffer=recvbuf; /* This call enables the SPI slave interface to send/receive to the sendbuf and recvbuf. The transaction is initialized by the SPI master, however, so it will not actually happen until the master starts a hardware transaction by pulling CS low and pulsing the clock etc. In this specific example, we use the handshake line, pulled up by the .post_setup_cb callback that is called as soon as a transaction is ready, to let the master know it is free to transfer data. */ ret=spi_slave_transmit(RCV_HOST, &t, portMAX_DELAY); //spi_slave_transmit does not return until the master has done a transmission, so by here we have sent our data and //received data from the master. Print it. printf("Received: %s\n", recvbuf); n++; } }
使用上述代码运行结果展示:
STM32接收结果展示:
ESP32端接收主机结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。