1:环境
stm32f103c8t6 2个(1个当主机,1个当从机)
keil5
杜邦线 若干

2:代码
主机 main.c

#include "stm32f10x.h"

// 引脚定义(SPI1)
#define SPI_MASTER_PORT    GPIOA
#define SPI_SCK_PIN        GPIO_Pin_5
#define SPI_MOSI_PIN       GPIO_Pin_7
#define SPI_MISO_PIN       GPIO_Pin_6
#define SPI_NSS_PIN        GPIO_Pin_4  // 软件控制片选

// LED引脚定义 (PC13)
#define LED_PIN         GPIO_Pin_13
#define LED_GPIO_PORT   GPIOC
#define LED_GPIO_CLK    RCC_APB2Periph_GPIOC

////////////////////////////////////////////////////////////////////////
int f_us=9;
int f_ms=9000;
void delay_init(void)
{
	//??????? 72MHZ/8=9MHZ
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//9MHZ
}

//????16,777,216??,??????1,864,135us
int delay_us(int nus)//1us?9??,500us?500*9
{
	u32 temp;
	//????????
	SysTick->LOAD = nus*f_us-1;
	
	//??????0,????,??VAL?0?,????????????
	SysTick->VAL=0X00;
	
	//?????
	SysTick->CTRL|=(0X01<<0);
	
	do
	{
		temp=SysTick->CTRL;
	}//?????????,???????????
	while((temp&(0x01<<0))&&(!(temp&(0x01<<16))));
	
	//?????
	SysTick->CTRL&=~(0X01<<0);
	return 0;
}

//????16,777,216??,??????1,864ms
int delay_ms(int nms)
{
	u32 temp;
	//????????
	SysTick->LOAD = nms*f_ms-1;
	
	//??????0,????,??VAL?0?,????????????
	SysTick->VAL=0X00;
	
	//?????
	SysTick->CTRL|=(0X01<<0);
	
	do
	{
		temp=SysTick->CTRL;
	}//?????????,???????????
	while((temp&(0x01<<0))&&(!(temp&(0x01<<16))));
	
	//?????
	SysTick->CTRL&=~(0X01<<0);
	return 0;
}

int delay_s(int ns)
{
	int n;
	for(n=0;n<ns;n++)
	{
		delay_ms(1000);
	}
	return 0;
}

void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能LED引脚时钟
    RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
    
    // 配置LED引脚(PC13)为推挽输出
    GPIO_InitStructure.GPIO_Pin = LED_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);
    
    // 初始状态:LED熄灭
    GPIO_SetBits(LED_GPIO_PORT, LED_PIN);  // 假设LED是低电平点亮
}

// 点亮LED
void LED_On(void) {
    GPIO_ResetBits(LED_GPIO_PORT, LED_PIN);
}

// 熄灭LED
void LED_Off(void) {
    GPIO_SetBits(LED_GPIO_PORT, LED_PIN);
}
////////////////////////////////////////////////////////////////////////

// 初始化 SPI 主模式
void SPI_Master_Init(void) {
    SPI_InitTypeDef SPI_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;

    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    // 配置SPI引脚(SCK/PA5, MOSI/PA7, MISO/PA6为复用推挽;NSS/PA4为通用推挽)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // MISO为输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // NSS软件控制
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_SetBits(GPIOA, GPIO_Pin_4); // 初始拉高片选

    // SPI主模式配置
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // 36MHz/16=2.25Mbps
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStruct.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_Cmd(SPI1, ENABLE);
}

int8_t Master_Send_Receive(uint8_t tx_data) {
    GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 拉低片选,选中从设备
    delay_us(1); // 延时确保从设备就绪

    // 发送数据
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1, tx_data);

    // 接收从设备返回数据
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    uint8_t rx_data = SPI_I2S_ReceiveData(SPI1);

    GPIO_SetBits(GPIOA, GPIO_Pin_4); // 拉高片选,结束通信
    return rx_data;
}


int main(void) {
	delay_init();
	LED_Init();
  uint8_t send_data = 0x11;  // 初始发送数据
  uint8_t recv_data =0;
	
  
  SPI_Master_Init();
  
  while (1) {
    recv_data = Master_Send_Receive(send_data);
    // 示例:如果从设备返回 0xEE,则表示接收成功
    if (recv_data == send_data+1) {
      send_data++;  // 下次发送+1
			if(send_data >=127) send_data = 1;
			LED_On();
			delay_ms(500);
			LED_Off();
			
    }
    // 简单延时(约1秒)
		delay_ms(800);
  }
	return 0;
}

/*
硬件连接(主从均为 F103C8T6)
主设备引脚	从设备引脚	信号名称
PA5	PA5	SCK(时钟线)
PA7	PA7	MOSI(主发从收)
PA6	PA6	MISO(主收从发)
PA4	PA4	NSS(片选,主控制)
GND	GND	共地(必须连接)
*/

从机 main.c

#include "stm32f10x.h"
#include "stm32f10x_spi.h"

// 引脚定义(SPI1)
#define SPI_SLAVE_PORT     GPIOA
#define SPI_SCK_PIN        GPIO_Pin_5
#define SPI_MOSI_PIN       GPIO_Pin_7
#define SPI_MISO_PIN       GPIO_Pin_6
#define SPI_NSS_PIN        GPIO_Pin_4  // 硬件片选(由主设备控制)

// LED引脚定义 (PC13)
#define LED_PIN         GPIO_Pin_13
#define LED_GPIO_PORT   GPIOC
#define LED_GPIO_CLK    RCC_APB2Periph_GPIOC


////////////////////////////////////////////////////////////////////////
int f_us=9;
int f_ms=9000;
void delay_init(void)
{
	//??????? 72MHZ/8=9MHZ
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//9MHZ
}

//????16,777,216??,??????1,864,135us
int delay_us(int nus)//1us?9??,500us?500*9
{
	u32 temp;
	//????????
	SysTick->LOAD = nus*f_us-1;
	
	//??????0,????,??VAL?0?,????????????
	SysTick->VAL=0X00;
	
	//?????
	SysTick->CTRL|=(0X01<<0);
	
	do
	{
		temp=SysTick->CTRL;
	}//?????????,???????????
	while((temp&(0x01<<0))&&(!(temp&(0x01<<16))));
	
	//?????
	SysTick->CTRL&=~(0X01<<0);
	return 0;
}

//????16,777,216??,??????1,864ms
int delay_ms(int nms)
{
	u32 temp;
	//????????
	SysTick->LOAD = nms*f_ms-1;
	
	//??????0,????,??VAL?0?,????????????
	SysTick->VAL=0X00;
	
	//?????
	SysTick->CTRL|=(0X01<<0);
	
	do
	{
		temp=SysTick->CTRL;
	}//?????????,???????????
	while((temp&(0x01<<0))&&(!(temp&(0x01<<16))));
	
	//?????
	SysTick->CTRL&=~(0X01<<0);
	return 0;
}

int delay_s(int ns)
{
	int n;
	for(n=0;n<ns;n++)
	{
		delay_ms(1000);
	}
	return 0;
}

void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能LED引脚时钟
    RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
    
    // 配置LED引脚(PC13)为推挽输出
    GPIO_InitStructure.GPIO_Pin = LED_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);
    
    // 初始状态:LED熄灭
    GPIO_SetBits(LED_GPIO_PORT, LED_PIN);  // 假设LED是低电平点亮
}

// 点亮LED
void LED_On(void) {
    GPIO_ResetBits(LED_GPIO_PORT, LED_PIN);
}

// 熄灭LED
void LED_Off(void) {
    GPIO_SetBits(LED_GPIO_PORT, LED_PIN);
}
////////////////////////////////////////////////////////////////////////

// 初始化 SPI 从模式
void SPI_Slave_Init(void) {
    SPI_InitTypeDef SPI_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;

    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    // 配置SPI引脚(SCK/PA5, MOSI/PA7为输入;MISO/PA7为复用推挽;NSS/PA4为输入)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 从设备SCK/MOSI/NSS为输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出!
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;//GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStruct);

    // SPI从模式配置
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode = SPI_Mode_Slave; // 关键:从模式
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // 与主设备一致
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // 与主设备一致
    SPI_InitStruct.SPI_NSS = SPI_NSS_Hard; // 硬件片选(由主设备控制)
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 从模式下无效
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStruct.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_Cmd(SPI1, ENABLE);
}
// 从设备接收数据并发送应答
uint8_t Slave_Receive_Send(void) {
    uint8_t rx_data, tx_data = 0x22;
	
	// 等待片选信号被拉低(主设备选中)
    while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4) == SET);

    // 等待主设备发送数据(NSS拉低后)
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    rx_data = SPI_I2S_ReceiveData(SPI1);

    // 发送返回数据(与接收同步)
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1, rx_data+1);
	
	// 等待片选信号被拉高(通信结束)
    while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4) == RESET);

    return rx_data; // 返回收到的主设备数据
}

int main(void) {
	delay_init();
	LED_Init();
  uint8_t recv_data,last_recv_data;
	last_recv_data = 0x11;
  
  SPI_Slave_Init();
  
  while (1) {
    recv_data = Slave_Receive_Send();
    // 可在此处处理接收的数据(如判断、存储等)
		if(recv_data == last_recv_data){
			last_recv_data++ ;
			if(last_recv_data >=127) last_recv_data = 1;
			
			LED_On();
			delay_ms(500);
			LED_Off();
		}else{
			delay_ms(200);
		}
  }
	return 0;
}

/*
硬件连接(主从均为 F103C8T6)
主设备引脚	从设备引脚	信号名称
PA5	PA5	SCK(时钟线)
PA7	PA7	MOSI(主发从收)
PA6	PA6	MISO(主收从发)
PA4	PA4	NSS(片选,主控制)
GND	GND	共地(必须连接)
*/

说明
GPIO 接线
硬件连接(主从均为 F103C8T6)
主设备引脚 从设备引脚 信号名称
PA5 PA5 SCK(时钟线)
PA7 PA7 MOSI(主发从收)
PA6 PA6 MISO(主收从发)
PA4 PA4 NSS(片选,主控制)
GND GND 共地(必须连接)

在这里插入图片描述

3 1主2从
从机片选都是PA4,所以从机代码一样的,

主机代码

#include "stm32f10x.h"

// 引脚定义(SPI1)
#define SPI_MASTER_PORT    GPIOA
#define SPI_SCK_PIN        GPIO_Pin_5
#define SPI_MOSI_PIN       GPIO_Pin_7
#define SPI_MISO_PIN       GPIO_Pin_6
#define SPI_NSS_PIN        GPIO_Pin_4  // 软件控制片选

#define SPI_NSS_PIN_2        GPIO_Pin_2  // 软件控制片选

// LED引脚定义 (PC13)
#define LED_PIN         GPIO_Pin_13
#define LED_GPIO_PORT   GPIOC
#define LED_GPIO_CLK    RCC_APB2Periph_GPIOC

////////////////////////////////////////////////////////////////////////
int f_us=9;
int f_ms=9000;
void delay_init(void)
{
	//??????? 72MHZ/8=9MHZ
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//9MHZ
}

//????16,777,216??,??????1,864,135us
int delay_us(int nus)//1us?9??,500us?500*9
{
	u32 temp;
	//????????
	SysTick->LOAD = nus*f_us-1;
	
	//??????0,????,??VAL?0?,????????????
	SysTick->VAL=0X00;
	
	//?????
	SysTick->CTRL|=(0X01<<0);
	
	do
	{
		temp=SysTick->CTRL;
	}//?????????,???????????
	while((temp&(0x01<<0))&&(!(temp&(0x01<<16))));
	
	//?????
	SysTick->CTRL&=~(0X01<<0);
	return 0;
}

//????16,777,216??,??????1,864ms
int delay_ms(int nms)
{
	u32 temp;
	//????????
	SysTick->LOAD = nms*f_ms-1;
	
	//??????0,????,??VAL?0?,????????????
	SysTick->VAL=0X00;
	
	//?????
	SysTick->CTRL|=(0X01<<0);
	
	do
	{
		temp=SysTick->CTRL;
	}//?????????,???????????
	while((temp&(0x01<<0))&&(!(temp&(0x01<<16))));
	
	//?????
	SysTick->CTRL&=~(0X01<<0);
	return 0;
}

int delay_s(int ns)
{
	int n;
	for(n=0;n<ns;n++)
	{
		delay_ms(1000);
	}
	return 0;
}

void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能LED引脚时钟
    RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
    
    // 配置LED引脚(PC13)为推挽输出
    GPIO_InitStructure.GPIO_Pin = LED_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);
    
    // 初始状态:LED熄灭
    GPIO_SetBits(LED_GPIO_PORT, LED_PIN);  // 假设LED是低电平点亮
}

// 点亮LED
void LED_On(void) {
    GPIO_ResetBits(LED_GPIO_PORT, LED_PIN);
}

// 熄灭LED
void LED_Off(void) {
    GPIO_SetBits(LED_GPIO_PORT, LED_PIN);
}
////////////////////////////////////////////////////////////////////////

// 初始化 SPI 主模式
void SPI_Master_Init(void) {
    SPI_InitTypeDef SPI_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;

    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    // 配置SPI引脚(SCK/PA5, MOSI/PA7, MISO/PA6为复用推挽;NSS/PA4为通用推挽)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // MISO为输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // NSS软件控制
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_SetBits(GPIOA, GPIO_Pin_4); // 初始拉高片选
	
		GPIO_InitStruct.GPIO_Pin = SPI_NSS_PIN_2;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // NSS软件控制2
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_SetBits(GPIOA, SPI_NSS_PIN_2); // 初始拉高片选

    // SPI主模式配置
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // 36MHz/16=2.25Mbps
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStruct.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_Cmd(SPI1, ENABLE);
}

int8_t Master_Send_Receive(uint8_t tx_data) {
    GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 拉低片选,选中从设备
    delay_us(1); // 延时确保从设备就绪

    // 发送数据
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1, tx_data);

    // 接收从设备返回数据
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    uint8_t rx_data = SPI_I2S_ReceiveData(SPI1);

    GPIO_SetBits(GPIOA, GPIO_Pin_4); // 拉高片选,结束通信
    return rx_data;
}

int8_t Master_Send_Receive_2(uint8_t tx_data) {
    GPIO_ResetBits(GPIOA, SPI_NSS_PIN_2); // 拉低片选,选中从设备
    delay_us(1); // 延时确保从设备就绪

    // 发送数据
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1, tx_data);

    // 接收从设备返回数据
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    uint8_t rx_data = SPI_I2S_ReceiveData(SPI1);

    GPIO_SetBits(GPIOA, SPI_NSS_PIN_2); // 拉高片选,结束通信
    return rx_data;
}


int main(void) {
	delay_init();
	LED_Init();
  uint8_t send_data1 = 0x11;  // 初始发送数据
	uint8_t send_data2 = 0x11;  // 初始发送数据
  uint8_t recv_data1 =0;
	uint8_t recv_data2 =0;
	uint8_t   bswitch = 0;
	
  
  SPI_Master_Init();
  
  while (1) {
		if(bswitch == 0){ //丛机1 
			bswitch =1 ;
			recv_data1 = Master_Send_Receive(send_data1);
    // 示例:如果从设备返回 0xEE,则表示接收成功
			if (recv_data1== send_data1+1) {
				send_data1++;  // 下次发送+1
				if(send_data1 >=127) send_data1 = 1;
				LED_On();
				delay_ms(500);
				LED_Off();
			}
		}else{  //丛机2
			bswitch =0;
			recv_data2 = Master_Send_Receive_2(send_data2);
    // 示例:如果从设备返回 0xEE,则表示接收成功
			if (recv_data2== send_data2+1) {
				send_data2++;  // 下次发送+1
				if(send_data2 >=127) send_data2 = 1;
				LED_On();
				delay_ms(200);
				LED_Off();
			}
		}
    
		delay_ms(500);
  }
	return 0;
}

主 到从1 从2(从1 从2 这4个线是并联)
PA5 PA5 SCK(时钟线)
PA7 PA7 MOSI(主发从收)
PA6 PA6 MISO(主收从发)
GND GND 都跟接主的地

不同的地方
主 从1
PA4 PA4 NSS(片选,主控制)
主 从2
PA2 PA4 NSS(片选,主控制)

在这里插入图片描述

4测试
1主1从
地址1

1从

1主2从
地址2

1主2从

5:如果对你又帮助,麻烦点个赞,加个关注
demo 工程

更多推荐