单片机SPI主从设备通信配置详解
1. SPI通信基础
(1) 主要特点
全双工通信:主机和从机可同时发送和接收数据。
同步时钟:由主机提供时钟信号(SCK),确保数据同步。
高速传输:通常可达几MHz(远高于I2C和UART)。
无标准协议:数据格式和含义由设备自行定义。
(2) 通信模式
主从架构:1个主机控制1个或多个从机。
从机选择:通过片选信号(CS/SS)选择目标从机。
2. 硬件连接(4线制)
| 信号线 | 全称 | 方向(主机视角) | 作用 |
|---|---|---|---|
| SCK | Serial Clock | 主机→从机 | 同步时钟信号 |
| MOSI | Master Out Slave In | 主机→从机 | 主机发送数据到从机 |
| MISO | Master In Slave Out | 主机←从机 | 从机发送数据到主机 |
| CS/SS | Chip Select/Slave Select | 主机→从机 | 片选信号(低电平有效) |
更多的通信原理我就不多介绍了:
3. 硬件资源:
AT32F403CGT7调试开发板
3.1 板卡原理图:

3.2 连接方式:
片选信号直连,用于开始数据传输使能,SPI通信时钟保持一致,从机时钟来源于主机提供,所以
SCK和SCK直连,主机输出从机输入(MOSI)和主机输入从机输出(MISO)需要交叉反接,另外最重
要的是两块板卡的通信地需要连上。
| 板卡1引脚 | 板卡2引脚 | 备注 |
| PA4 — CS | PA4 — CS | 从机片选需要配置为输入 |
| PA5 — SCK | PA5 — SCK | 从机SCK配置为输入 |
| PA6 — MISO | PA7 — MOSI | 主从通信需要将数据MISO和MOSI反接 |
| PA7 — MOSI | PA6 — MISO | N/A |
| GND | GND | N/A |
3.3 实物连接图:

3.4 配置说明:
通信方式:软件配置使用半双工的通信方式。如果配置全双工方式需要注意:SPI协议的全双工特
性要求从机在接收时必须同时发送数据,即使发送的是无用5数据(如0xFF和0x55),这一设计是
由SPI的硬件机制和同步通信本质决定的)
通信配置:主机发送,从机接收,
接收方式:从机使用中断的方式对数据进行接收(开启接收数据缓冲区满中断
(SPI_I2S_RDBF_INT)),由于配置传输字节为8bit,所以每次传输一个字节,丛姐收到一个字节就会
触发中断。
主从配置方式:使用宏定义SPI_MODE区分设备主从。
信号采样:配置空闲时钟低电平,数据从第二个边沿采样。
4. 软件配置资源
4.1 spi.c
#include "spi.h"
spi_data_info spi_data;
bool get_data = false;
static void spi_gpio_config(void)
{
gpio_init_type gpio_initstructure;
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
//SPI1_CS_PA4
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
if(SPI_MODE == SPI_MODE_MASTER)
{
gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
}
else
{
gpio_initstructure.gpio_mode = GPIO_MODE_INPUT; // 从机CS应为输入
gpio_initstructure.gpio_pull = GPIO_PULL_UP; // 推荐上拉防止干扰
}
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_4;
gpio_init(GPIOA, &gpio_initstructure);
//SPI1_SCK_PA5
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_DOWN;
if(SPI_MODE == SPI_MODE_MASTER)
{
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
}
else
{
gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
}
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_5;
gpio_init(GPIOA, &gpio_initstructure);
//SPI1_MISO_PA6
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
if(SPI_MODE == SPI_MODE_MASTER)
{
gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
}
else
{
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
}
gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_6;
gpio_init(GPIOA, &gpio_initstructure);
//SPI1_MOSI_PA7
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
if(SPI_MODE == SPI_MODE_MASTER)
{
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
}
else
{
gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
}
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_7;
gpio_init(GPIOA, &gpio_initstructure);
}
static void spi_config(void)
{
spi_gpio_config();
spi_init_type spi_init_struct;
crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);
spi_default_para_init(&spi_init_struct);
if(SPI_MODE == SPI_MODE_MASTER)
{
spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_TX;
}
else
{
spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_RX;
}
spi_init_struct.master_slave_mode = SPI_MODE;
spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_8;
spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_LSB;
spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW;
spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;
spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;
spi_init(SPI1, &spi_init_struct);
nvic_irq_enable(SPI1_IRQn, 0, 0);
if(SPI_MODE == SPI_MODE_MASTER)
{
// spi_i2s_interrupt_enable(SPI1, SPI_I2S_TDBE_INT, TRUE);
}
else
{
spi_i2s_interrupt_enable(SPI1, SPI_I2S_RDBF_INT, TRUE);
}
spi_enable(SPI1, TRUE);
}
u8 Flash_RW_Byte(u8 ucTxData)
{
while((SPI1->sts & SPI_I2S_TDBE_FLAG) == (uint16_t)RESET);
SPI3->dt = ucTxData;
while((SPI1->sts & SPI_I2S_RDBF_FLAG) == (uint16_t)RESET);
return SPI1->dt;
}
static void spi_data_communications_test(void)
{
uint8_t data[4] = {0x01,0x02,0x03,0x04};
static uint8_t temp = 0;
temp++;
for(int i = 0;i < 4;i++)
{
gpio_bits_reset(GPIOA,GPIO_PINS_4);
spi_i2s_data_transmit(SPI1,data[i] + temp);
gpio_bits_set(GPIOA,GPIO_PINS_4);
delay_ms(20);
}
}
void spi_data_receive_handle(void)
{
if(get_data)
{
printf("get spi data: ");
for(int i = 0;i < spi_data.len;i++)
{
printf(" 0x%x",spi_data.data[i]);
}
printf("\r\n");
get_data = false;
spi_data.len = 0;
}
}
//SPI中断回调函数
void SPI1_IRQHandler(void)
{
if(spi_i2s_flag_get(SPI1,SPI_I2S_RDBF_FLAG))
{
spi_data.data[spi_data.len] = spi_i2s_data_receive(SPI1);
spi_data.len++;
if(spi_data.len >= 4)
{
get_data = true;
}
}
spi_i2s_flag_clear(SPI1,SPI_I2S_RDBF_FLAG);
}
st_spi_fun spi_fun_info =
{
.spi_config = spi_config,
.spi_data_communications_test = spi_data_communications_test,
.spi_data_receive_handle = spi_data_receive_handle,
};
4.2 spi.h
#ifndef _SPI_H_
#define _SPI_H_
#include "config.h"
#define SPI_MODE SPI_MODE_MASTER //SPI_MODE_SLAVE SPI_MODE_MASTER
typedef struct
{
uint16_t len;
uint16_t data[200];
}spi_data_info;
typedef struct
{
void (*spi_config)(void);
void (*spi_data_communications_test)(void);
void (*spi_data_receive_handle)(void);
}st_spi_fun;
extern spi_data_info spi_data;
extern st_spi_fun spi_fun_info;
#endif
4.3 main.c
int main(void)
{
system_clock_config();
at32_board_init();
//GPIO初始化
GPIO_Configuration();
led_init();
delay_ms(2000);
usart1_config(115200);
spi_fun_info.spi_config();
while(1)
{
//周期翻转小灯
led2_default_toggle();
led3_default_toggle();
//根据主从模式分别进行收发逻辑
if(SPI_MODE == SPI_MODE_MASTER)
{
delay_ms(2000);
//数据发送
spi_fun_info.spi_data_communications_test();
}
else
{
//数据接收
spi_fun_info.spi_data_receive_handle();
delay_ms(1000);
}
}
}
5. 运行结果:

6. 最后分享
第一次自己画板子,可能布线和硬件设计不太合理,后续继续改进,下次挑战分享更复杂的板
卡,最后上传板卡实物图,记录下学习里程碑。

作者:zhouzhouyaonvli