一、硬件配置
????????STM32F103ZET6作為主機,使用SPI2,ESP32S2作為從機,進行SPI雙向通信;硬件接線如下:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 主機? ? ? ? ? ? ? ? ? ? ? ? 從機
CS? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?PB12 ——————? 14
MOSI? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PB6 ——————? ? 2
MISO? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?PB14 ——————? 13
CLK? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?PB15 ——————? 12
HANDSHAKE? ? ? ? ? ? ? ? ? ? ? ? ?PB13 ——————? ?15
GND? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GND?——————? ?GND? ?
(地線一定相連在一起,不然傳輸?shù)臄?shù)據(jù)會亂碼)
二、主機代碼
#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復(fù)用推挽輸出
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; //設(shè)置SPI工作模式:設(shè)置為主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //SPI發(fā)送接收8位幀結(jié)構(gòu)
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步時鐘的空閑狀態(tài)為高電平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //第一個跳變沿數(shù)據(jù)被采樣
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //軟件管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//預(yù)分頻值為256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //數(shù)據(jù)傳輸從MSB位開始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值計算的多項式
SPI_Init(SPI2, &SPI_InitStructure); //初始化外設(shè)SPIx寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI外設(shè)
}
//SPIx 讀寫一個字節(jié)
//TxData:要寫入的字節(jié)
//返回值:讀取到的字節(jié)
u8 SPI2_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
{
retry++;
if(retry>200) return 0; //發(fā)送上一個數(shù)據(jù)時間過長,報錯
}
SPI_I2S_SendData(SPI2, TxData); //通過外設(shè)SPIx發(fā)送一個數(shù)據(jù)
retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
{
retry++;
if(retry>200) return 0;
}
return SPI_I2S_ReceiveData(SPI2); //返回通過SPIx最近接收的數(shù)據(jù)
}
u8 TXBuffer[128];
u8 RXBuffer[128];
int main(void)
{
u16 i=0;
delay_init(); //延時函數(shù)初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設(shè)置中斷優(yōu)先級分組為組2
uart_init(115200); //串口初始化為115200
LED_Init(); //初始化與LED連接的硬件接口
SPI2_Init();
for (i = 0; i < 10; i++) //給發(fā)送數(shù)組賦初值
{
TXBuffer[i] = 0x47+i;
}
while(1)
{
if(SPI2READY==1) //如果從機準備好接收
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12); //拉低CS
for(i=0;i<10;i++) //發(fā)送數(shù)據(jù)并接收從機返回數(shù)據(jù)
{
RXBuffer[i]=SPI2_ReadWriteByte(TXBuffer[i]);
}
GPIO_SetBits(GPIOB,GPIO_Pin_12); //拉高CS
LED0 = ~LED0; //指示燈閃爍
}
printf("%s\r\n",RXBuffer); //串口打印從機接收到的數(shù)據(jù)
delay_ms(1000);
}
}
?三、從機代碼
根據(jù)官方所給例程修改
#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
在事務(wù)進入隊列并準備由master提取時調(diào)用。我們用這個來設(shè)置握手線
void my_post_setup_cb(spi_slave_transaction_t *trans) {
gpio_set_level(GPIO_HANDSHAKE, 1);
}
//事務(wù)發(fā)送/接收后調(diào)用。我們用這個來降低握手線。
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個字節(jié)填充為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個字節(jié)全部填充為0xA5
for(int j=0;j<26;j++) //給從機發(fā)送緩存區(qū)賦值
{
sendbuf[j]=0x41+j;
}
//Set up a transaction of 128 bytes to send/receive
t.length=20*8; //設(shè)置一次接收/發(fā)送的最大值,必須比主機一次發(fā)送的數(shù)據(jù)大小要大,否則會亂序
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); //可以用這個函數(shù),但是沒有驗證,容易出錯
printf("Received: %s\n", recvbuf);
n++;
}
}
?
運行順序應(yīng)該是從機接收先復(fù)位,再等待主機復(fù)位;如果主機先復(fù)位,則接收時序可能會不正確。
?四、運行結(jié)果
主機接收到從機返回的數(shù)據(jù)
?
從機接收到主機發(fā)送的數(shù)據(jù)文章來源:http://www.zghlxwxcb.cn/news/detail-729967.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-729967.html
到了這里,關(guān)于STM32與ESP32的硬件SPI通信(個人學(xué)習(xí)記錄)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!