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从模式
从机模式的工作方式是等待片选有效,有效后根据时钟的极性和相位把数据移入数据位移寄存器,然后接收一帧数据后产生接收完成事件。
- 设置 DFF 位,以定义 8 或 16 位数据帧格式。
- 选择 CPOL 和 CPHA 位
- 帧格式(MSB 在前或 LSB 在前取决于 SPI_CR1 寄存器中 LSBFIRST 位的值)必须与Master器件的帧格式相同。
- NSS 引脚在整个字节发送序列期间都必须连接到低电平
- 将 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从模式
从机模式的工作方式是等待片选有效,有效后根据时钟的极性和相位把数据移入数据位移寄存器,然后接收一帧数据后产生接收完成事件。
- 设置 DFF 位,以定义 8 或 16 位数据帧格式。
- 选择 CPOL 和 CPHA 位
- 帧格式(MSB 在前或 LSB 在前取决于 SPI_CR1 寄存器中 LSBFIRST 位的值)必须与Master器件的帧格式相同。
- NSS 引脚在整个字节发送序列期间都必须连接到低电平
- 将 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
暂无评论