STM32 SPI总线深入理解

文章目录[隐藏]

在这里插入图片描述
SPI总线有4条线
SS:片选使能信号,低电平有效,选中谁和谁进行通信。
SCLK:时钟信号
MOSI:Master Out Slave In
MISO:Master In Slave Out

下图是一对多的链接方式,主机需要用N个片选来选择N个从机
在这里插入图片描述
下图是菊花链的方式,所有的从机使用一条片选型号。从机的输入输出依次相连
在这里插入图片描述

SPI协议中的一个难点,时钟的极性和相位

在这里插入图片描述
SCLK时钟信号极性
SCLK空闲时为0那么极性CPOL=0. 那么打破空闲的是高1,出现跳变沿顺序leading edge 前沿 为上升沿,后沿trailing edge为下降沿。
SCLK空闲时为1,那么CPOL=1.那么打破静默是低 0。出现跳变沿顺序leading edge 前沿 为下降沿,后沿trailing edge为上升沿。

方便记忆,可以理解为时钟默认状态下的电平决定第一个电平的调转方向。
在这里插入图片描述

时钟相位。CPHA 表示数据由第一个跳变沿驱动还是第二个跳变沿驱动。也可以理解为数据有效时时钟的相位。
CPHA=0.代表由时钟的第一个跳变沿采集数据,也就是相位0时,数据有效。
CPHA=1.代表时钟的第二个跳变沿采集数据,也就是相位1时,数据有效。
在这里插入图片描述
片选使能代表选中通信设备,时钟的第一个跳变决定通信的开始。

SPI 模块

SPI模块的核心是位移寄存器,在时钟的驱动下,发送端依次把移位寄存器的内容传输到中线上。同时也移入输入信号线上的数据。
接收发送是同时进行的,构成一个环。如下图所示
在这里插入图片描述
在这里插入图片描述
片选信号低有效,在SCLK第一个跳变沿来临前称为leading delay 这个时SCLK的电平是默认电平由SCLK极性决定。当数据帧传输完成后时钟恢复静默状态到下次开始的时间为trailing delay。

在这里插入图片描述
1.地址和数据总线 可以由CPU和DMA访问
2.发送缓冲区,在位移寄存器数据被读出或者发送完成前缓冲数据
3.位移寄存器,数据接收发送的 主体。
4.接收缓冲
5.MOSI,MISO可以互通,用于菊花链
6.NSS片选使能,决定 主逻辑,通信控制器是否开启
7.主逻辑控制器,对MOSI,MISO进行控制
8.波特率发送器在SPI_CR1寄存器下按照设定产生SCLK。CPOL,CPHA在这可以看到
9.通信控制器 对应SPI_CR2寄存器主要是接收状态和事件。这些标志在I2C,USART模块中经常见到,大同小异。

SPI从模式

从机模式的工作方式是等待片选有效,有效后根据时钟的极性和相位把数据移入数据位移寄存器,然后接收一帧数据后产生接收完成事件。

  1. 设置 DFF 位,以定义 8 或 16 位数据帧格式。
  2. 选择 CPOL 和 CPHA 位
  3. 帧格式(MSB 在前或 LSB 在前取决于 SPI_CR1 寄存器中 LSBFIRST 位的值)必须与Master器件的帧格式相同。
  4. NSS 引脚在整个字节发送序列期间都必须连接到低电平
  5. 将 MSTR 位清零,并将 SPE 位置 1
    在这里插入图片描述
    发送序列
    当从器件在其 MOSI 引脚上收到时钟信号和数据的最高有效位时,发送序列开始。其余位(8 位数据帧格式中的 7 个位, 16 位数据帧格式中的 15 个位)将加载到移位寄存器中。SPI_SR 寄存器中的 TXE 标志在数据从发送缓冲区传输到移位寄存器时置 1,并且在SPI_CR2 寄存器中的 TXEIE 位置 1 时将生成中断。
    在这里插入图片描述
    在这里插入图片描述
    接收序列

对于接收器,在数据传输完成时:
● 移位寄存器中的数据将传输到接收缓冲区,并且 RXNE 标志(SPI_SR 寄存器)置 1
● 如果 SPI_CR2 寄存器中的 RXNEIE 位置 1,则生成中断
在出现最后一个采样时钟边沿后, RXNE 位置 1, 移位寄存器中接收的数据字节被拷贝到接
收缓冲区中。当读取 SPI_DR 寄存器时, SPI 外设将返回此缓冲值。

数据移位寄存器以“发送缓冲区”为数据源,把数据一位一位地通过数据线发送出去;当从外部接收数据的时候,数据移位寄存器把
数据线采样到的数据一位一位地存储到“接收缓冲区”中。通过写 SPI 的“数据寄存器 DR”把数据填充到发送缓冲区中,通过“数据寄存器 DR”,可以获取接收缓冲区中的内容。其中数据帧长度可以通过“控制寄存器 CR1”的“DFF 位”配置成 8 位及 16 位模式

在这里插入图片描述
上图是SPI的传输时序图,不难理解。
TXE,RXNE的理解是关键
数据的写入读取都是对缓冲寄存器操作,分别有写入缓冲,读取缓冲。移位寄存器是无法直接访问的。
TXE代表写入缓冲(发送缓冲)寄存器数据状态,由软件清零。写入一个数据的时候TXE 为低,表示非空状态。当发送缓冲寄存器的
数据被转移到移位寄存器的时候,发送缓冲寄存器为空置位。
同理RXNE,当移位寄存器接收一个完成的数据(8/16 bit)后,就会把数据移动到接收缓冲中。这个时候RXNE为1.表示非空。
在这里插入图片描述
如果使用DMA,其实不用看图也能知道。TXE标志出现的时候就会发起DMA写入请求。
同理RXNE出现的时候,就是DMA搬移数据的时候。
在这里插入图片描述
最后就是传输过程中的中断和控制位
在这里插入图片描述
需要注意的是 NSS引脚是该MCU SPI模块的片选引脚,如果作为主设备应该保证其为高。
练习1 初始化和数据的发送
初始化代码如下
完全按照库函数的简单初始化即可。

STM32F103C8T6  
NSS接高电平master模式

/*
PA4  SPI1_NSS
PA5  SPI1_SCK
PA6  SPI1_MISO
PA7  SPI1_MOSI
*/


void app_spi_train_init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);


	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_4  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_5  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_6  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_7  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;

	
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	app_train_spi_cfg();


}

void app_spi_module_config(void)
{
	SPI_InitTypeDef   SPI_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStruct.SPI_CPHA =SPI_CPHA_2Edge;
	SPI_InitStruct.SPI_CPOL =SPI_CPOL_High;
	SPI_InitStruct.SPI_CRCPolynomial = 7;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_FirstBit =SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
	SPI_InitStruct.SPI_NSS	=SPI_NSS_Hard ;

	SPI_Init(SPI1, &SPI_InitStruct);
	SPI_Cmd(SPI1, ENABLE);
}
void task2_task(void *pvParameters)
{
	
 	app_spi_train_init();
    while(1)
    {
    
 		app_spi_send();
        vTaskDelay(1000);
      
	
    }
    
}

void app_spi_send(void)
{

	static char data=0;
	if(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE))
		SPI_I2S_SendData(SPI1, 0xff & data++);
}

在这里插入图片描述
在这里插入图片描述

练习2
SPI 双机通信实验
STM32F407从机
配置如下
在这里插入图片描述

#define SPI_RX_BUF_LEN 20

unsigned char spi_rx_buf[SPI_RX_BUF_LEN];

/*
PA4  SPI1_NSS   CH1
PA5  SPI1_SCK   CH3
PA6  SPI1_MISO  CH2
PA7  SPI1_MOSI  CH0
*/

void app_spi_train_gpio_init(void)
{

	
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_OType= GPIO_OType_PP ;
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_7;
	GPIO_InitStruct.GPIO_PuPd= GPIO_PuPd_UP ;
	GPIO_InitStruct.GPIO_Speed= GPIO_High_Speed ;

	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_5;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_6;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_4;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

	GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1);


}

void app_spi_train_it(void)
{

	NVIC_InitTypeDef  NVIC_InitStruct;

	
	NVIC_InitStruct.NVIC_IRQChannel=  SPI1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =13;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(& NVIC_InitStruct);


	SPI_I2S_ITConfig(SPI1,SPI_I2S_IT_RXNE,ENABLE);


}



void app_spi_train_init(void)
{

	SPI_InitTypeDef SPI_InitStruct;
	app_spi_train_gpio_init();

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStruct.SPI_CPOL =SPI_CPOL_High ;
	SPI_InitStruct.SPI_CRCPolynomial= 7;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_FirstBit =SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Slave;
	SPI_InitStruct.SPI_NSS =SPI_NSS_Hard;
	app_spi_train_it();
	SPI_Init(SPI1, & SPI_InitStruct);

	SPI_Cmd(SPI1, ENABLE);


}


void SPI1_IRQHandler(void)
{
	static unsigned char index =0;
	spi_rx_buf[index++] = SPI_I2S_ReceiveData(SPI1);
	if (index>SPI_RX_BUF_LEN-1)
	{
		index=0;
	}

}


stm32c8t6主机代码如下
PA8为从机的片选引脚

void app_spi_train_init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);


	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_4  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_5  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_6  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_7  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);



	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_Out_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_8  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	app_train_spi_cfg();


}

void app_train_spi_cfg(void)
{

	SPI_InitTypeDef SPI_InitStruct;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStruct.SPI_CPOL =SPI_CPOL_High ;
	SPI_InitStruct.SPI_CRCPolynomial= 7;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_FirstBit =SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
	SPI_InitStruct.SPI_NSS =SPI_NSS_Hard;
	
	SPI_Init(SPI1, & SPI_InitStruct);

	SPI_Cmd(SPI1, ENABLE);
}



void app_spi_send(void)
{

	static char data=0;

	GPIO_WriteBit(GPIOA,GPIO_Pin_8,0);
	if(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE))
		SPI_I2S_SendData(SPI1, 0xff & data++);
	
	while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY))
	{
	}
	GPIO_WriteBit(GPIOA,GPIO_Pin_8,1);
}


在这里插入图片描述

版权声明:本文为CSDN博主「dengjingg」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/denghuajing/article/details/122393301

在这里插入图片描述
SPI总线有4条线
SS:片选使能信号,低电平有效,选中谁和谁进行通信。
SCLK:时钟信号
MOSI:Master Out Slave In
MISO:Master In Slave Out

下图是一对多的链接方式,主机需要用N个片选来选择N个从机
在这里插入图片描述
下图是菊花链的方式,所有的从机使用一条片选型号。从机的输入输出依次相连
在这里插入图片描述

SPI协议中的一个难点,时钟的极性和相位

在这里插入图片描述
SCLK时钟信号极性
SCLK空闲时为0那么极性CPOL=0. 那么打破空闲的是高1,出现跳变沿顺序leading edge 前沿 为上升沿,后沿trailing edge为下降沿。
SCLK空闲时为1,那么CPOL=1.那么打破静默是低 0。出现跳变沿顺序leading edge 前沿 为下降沿,后沿trailing edge为上升沿。

方便记忆,可以理解为时钟默认状态下的电平决定第一个电平的调转方向。
在这里插入图片描述

时钟相位。CPHA 表示数据由第一个跳变沿驱动还是第二个跳变沿驱动。也可以理解为数据有效时时钟的相位。
CPHA=0.代表由时钟的第一个跳变沿采集数据,也就是相位0时,数据有效。
CPHA=1.代表时钟的第二个跳变沿采集数据,也就是相位1时,数据有效。
在这里插入图片描述
片选使能代表选中通信设备,时钟的第一个跳变决定通信的开始。

SPI 模块

SPI模块的核心是位移寄存器,在时钟的驱动下,发送端依次把移位寄存器的内容传输到中线上。同时也移入输入信号线上的数据。
接收发送是同时进行的,构成一个环。如下图所示
在这里插入图片描述
在这里插入图片描述
片选信号低有效,在SCLK第一个跳变沿来临前称为leading delay 这个时SCLK的电平是默认电平由SCLK极性决定。当数据帧传输完成后时钟恢复静默状态到下次开始的时间为trailing delay。

在这里插入图片描述
1.地址和数据总线 可以由CPU和DMA访问
2.发送缓冲区,在位移寄存器数据被读出或者发送完成前缓冲数据
3.位移寄存器,数据接收发送的 主体。
4.接收缓冲
5.MOSI,MISO可以互通,用于菊花链
6.NSS片选使能,决定 主逻辑,通信控制器是否开启
7.主逻辑控制器,对MOSI,MISO进行控制
8.波特率发送器在SPI_CR1寄存器下按照设定产生SCLK。CPOL,CPHA在这可以看到
9.通信控制器 对应SPI_CR2寄存器主要是接收状态和事件。这些标志在I2C,USART模块中经常见到,大同小异。

SPI从模式

从机模式的工作方式是等待片选有效,有效后根据时钟的极性和相位把数据移入数据位移寄存器,然后接收一帧数据后产生接收完成事件。

  1. 设置 DFF 位,以定义 8 或 16 位数据帧格式。
  2. 选择 CPOL 和 CPHA 位
  3. 帧格式(MSB 在前或 LSB 在前取决于 SPI_CR1 寄存器中 LSBFIRST 位的值)必须与Master器件的帧格式相同。
  4. NSS 引脚在整个字节发送序列期间都必须连接到低电平
  5. 将 MSTR 位清零,并将 SPE 位置 1
    在这里插入图片描述
    发送序列
    当从器件在其 MOSI 引脚上收到时钟信号和数据的最高有效位时,发送序列开始。其余位(8 位数据帧格式中的 7 个位, 16 位数据帧格式中的 15 个位)将加载到移位寄存器中。SPI_SR 寄存器中的 TXE 标志在数据从发送缓冲区传输到移位寄存器时置 1,并且在SPI_CR2 寄存器中的 TXEIE 位置 1 时将生成中断。
    在这里插入图片描述
    在这里插入图片描述
    接收序列

对于接收器,在数据传输完成时:
● 移位寄存器中的数据将传输到接收缓冲区,并且 RXNE 标志(SPI_SR 寄存器)置 1
● 如果 SPI_CR2 寄存器中的 RXNEIE 位置 1,则生成中断
在出现最后一个采样时钟边沿后, RXNE 位置 1, 移位寄存器中接收的数据字节被拷贝到接
收缓冲区中。当读取 SPI_DR 寄存器时, SPI 外设将返回此缓冲值。

数据移位寄存器以“发送缓冲区”为数据源,把数据一位一位地通过数据线发送出去;当从外部接收数据的时候,数据移位寄存器把
数据线采样到的数据一位一位地存储到“接收缓冲区”中。通过写 SPI 的“数据寄存器 DR”把数据填充到发送缓冲区中,通过“数据寄存器 DR”,可以获取接收缓冲区中的内容。其中数据帧长度可以通过“控制寄存器 CR1”的“DFF 位”配置成 8 位及 16 位模式

在这里插入图片描述
上图是SPI的传输时序图,不难理解。
TXE,RXNE的理解是关键
数据的写入读取都是对缓冲寄存器操作,分别有写入缓冲,读取缓冲。移位寄存器是无法直接访问的。
TXE代表写入缓冲(发送缓冲)寄存器数据状态,由软件清零。写入一个数据的时候TXE 为低,表示非空状态。当发送缓冲寄存器的
数据被转移到移位寄存器的时候,发送缓冲寄存器为空置位。
同理RXNE,当移位寄存器接收一个完成的数据(8/16 bit)后,就会把数据移动到接收缓冲中。这个时候RXNE为1.表示非空。
在这里插入图片描述
如果使用DMA,其实不用看图也能知道。TXE标志出现的时候就会发起DMA写入请求。
同理RXNE出现的时候,就是DMA搬移数据的时候。
在这里插入图片描述
最后就是传输过程中的中断和控制位
在这里插入图片描述
需要注意的是 NSS引脚是该MCU SPI模块的片选引脚,如果作为主设备应该保证其为高。
练习1 初始化和数据的发送
初始化代码如下
完全按照库函数的简单初始化即可。

STM32F103C8T6  
NSS接高电平master模式

/*
PA4  SPI1_NSS
PA5  SPI1_SCK
PA6  SPI1_MISO
PA7  SPI1_MOSI
*/


void app_spi_train_init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);


	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_4  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_5  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_6  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_7  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;

	
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	app_train_spi_cfg();


}

void app_spi_module_config(void)
{
	SPI_InitTypeDef   SPI_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStruct.SPI_CPHA =SPI_CPHA_2Edge;
	SPI_InitStruct.SPI_CPOL =SPI_CPOL_High;
	SPI_InitStruct.SPI_CRCPolynomial = 7;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_FirstBit =SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
	SPI_InitStruct.SPI_NSS	=SPI_NSS_Hard ;

	SPI_Init(SPI1, &SPI_InitStruct);
	SPI_Cmd(SPI1, ENABLE);
}
void task2_task(void *pvParameters)
{
	
 	app_spi_train_init();
    while(1)
    {
    
 		app_spi_send();
        vTaskDelay(1000);
      
	
    }
    
}

void app_spi_send(void)
{

	static char data=0;
	if(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE))
		SPI_I2S_SendData(SPI1, 0xff & data++);
}

在这里插入图片描述
在这里插入图片描述

练习2
SPI 双机通信实验
STM32F407从机
配置如下
在这里插入图片描述

#define SPI_RX_BUF_LEN 20

unsigned char spi_rx_buf[SPI_RX_BUF_LEN];

/*
PA4  SPI1_NSS   CH1
PA5  SPI1_SCK   CH3
PA6  SPI1_MISO  CH2
PA7  SPI1_MOSI  CH0
*/

void app_spi_train_gpio_init(void)
{

	
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_OType= GPIO_OType_PP ;
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_7;
	GPIO_InitStruct.GPIO_PuPd= GPIO_PuPd_UP ;
	GPIO_InitStruct.GPIO_Speed= GPIO_High_Speed ;

	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_5;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_6;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_4;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

	GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1);


}

void app_spi_train_it(void)
{

	NVIC_InitTypeDef  NVIC_InitStruct;

	
	NVIC_InitStruct.NVIC_IRQChannel=  SPI1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =13;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(& NVIC_InitStruct);


	SPI_I2S_ITConfig(SPI1,SPI_I2S_IT_RXNE,ENABLE);


}



void app_spi_train_init(void)
{

	SPI_InitTypeDef SPI_InitStruct;
	app_spi_train_gpio_init();

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStruct.SPI_CPOL =SPI_CPOL_High ;
	SPI_InitStruct.SPI_CRCPolynomial= 7;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_FirstBit =SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Slave;
	SPI_InitStruct.SPI_NSS =SPI_NSS_Hard;
	app_spi_train_it();
	SPI_Init(SPI1, & SPI_InitStruct);

	SPI_Cmd(SPI1, ENABLE);


}


void SPI1_IRQHandler(void)
{
	static unsigned char index =0;
	spi_rx_buf[index++] = SPI_I2S_ReceiveData(SPI1);
	if (index>SPI_RX_BUF_LEN-1)
	{
		index=0;
	}

}


stm32c8t6主机代码如下
PA8为从机的片选引脚

void app_spi_train_init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);


	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_4  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_5  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_6  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_7  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);



	GPIO_InitStruct.GPIO_Mode =  GPIO_Mode_Out_PP ;
	GPIO_InitStruct.GPIO_Pin  =  GPIO_Pin_8  ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	app_train_spi_cfg();


}

void app_train_spi_cfg(void)
{

	SPI_InitTypeDef SPI_InitStruct;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStruct.SPI_CPOL =SPI_CPOL_High ;
	SPI_InitStruct.SPI_CRCPolynomial= 7;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_FirstBit =SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
	SPI_InitStruct.SPI_NSS =SPI_NSS_Hard;
	
	SPI_Init(SPI1, & SPI_InitStruct);

	SPI_Cmd(SPI1, ENABLE);
}



void app_spi_send(void)
{

	static char data=0;

	GPIO_WriteBit(GPIOA,GPIO_Pin_8,0);
	if(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE))
		SPI_I2S_SendData(SPI1, 0xff & data++);
	
	while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY))
	{
	}
	GPIO_WriteBit(GPIOA,GPIO_Pin_8,1);
}


在这里插入图片描述

版权声明:本文为CSDN博主「dengjingg」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/denghuajing/article/details/122393301

生成海报
点赞 0

dengjingg

我还没有学会写个人说明!

暂无评论

相关推荐

阿尔法系统时钟和外设时钟

一、系统时钟 1.时钟树 I.MX6U 的系统主频为 528MHz,但是默认情况下内部 boot rom 会将 I.MX6U 的主频设置为396MHz I.MX6U-ALPHA 开发板的系统时钟来源于两部分: 3

SOP:通过电控实现功能

是什么 主要通过单片机实现控制arduino stm32 openmv 英伟达 FPGA等等以下介绍一些常用的功能(结合自己的需要学习) 通信 UART和IIC是单片机中常见的通信协议很多模块可以用到 机械

唐承乾的电赛小站

唐承乾的电赛小站 系列文章 带*的内容,不是本人所写。 扫盲 stm32cubemx输出pwm波,实现呼吸灯*STM32hal库定时器实现微秒延迟*STM32多串口实现printf——基于cubemxstm32简易