STM32_SPI_笔记

SPI主机负责产生时钟,决定了SPI通信速率。可以同时收发数据;

时钟极性(CPOL)决定同步时钟空闲状态;

时钟相位(CPHA):0:CLK的第一个跳变沿(上升沿或下降沿)数据被采样;

1:CLK的第二个跳变沿数据被采样;


写:主机对SPIX->DR写数据,CLK产生时钟,同时数据从MOSI -> MISO

读:主机对SPIX->DR写DUMMY(空字节),CLK产生时钟,DUMMY信号从MOSI -> MISO (该数据对从机无意义),同时要读取的数据从 MISO -> MOSI,主机从SPIX->DR读取数据。然后清除DR,以便接收下一个数据。

读写SPIX -> DR是两个不同的寄存器。


SPI中断事件:

中断事件事件标志使能控制位
发送缓冲器空标志TXETXEIE
接收缓冲器非空标志RXNERXNEIE
主模式错误事件MODFERRIE
溢出错误OVR
CRC错误标志CRCERR

案例1:简单SPI对接通信,SPI1主机,SPI3从机,中断方式,主机->从机

初始化代码

//步骤1 使能时钟
void RCC_Configuration(void)
{
    RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOA|  RCC_APB2PERIPH_GPIOB |  RCC_APB2PERIPH_GPIOC| RCC_APB2PERIPH_AF , ENABLE);
    RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_SPI1,ENABLE);
    RCC_APB1PeriphClock_Enable( RCC_APB1PERIPH_SPI3 ,ENABLE );    
}

//步骤2 配置GPIO
void GPIO_Configuration(void)
{
    GPIO_InitPara GPIO_InitStructure;
    GPIO_PinRemapConfig(GPIO_REMAP_SPI3, ENABLE);
    /* Configure SPI_MASTER pins: SCK and MOSI */
    GPIO_InitStructure.GPIO_Pin =  GPIO_PIN_5| GPIO_PIN_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    /* Configure SPI_SLAVE pins: SCK and MISO */
    /* Configure SCK pin as Alternate Function */
    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_10 ;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_IN_FLOATING;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    /* Configure MISO pin as Alternate Function Push Pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
}

//步骤3 配置SPI,SPI1主机 SPI3从机
void SPI_Configuration(void)
{
	SPI_InitPara SPI_InitStructure;
	
	SPI_InitStructure.SPI_TransType = SPI_TRANSTYPE_BDMTX;
    SPI_InitStructure.SPI_Mode = SPI_MODE_MASTER;
    SPI_InitStructure.SPI_FrameFormat = SPI_FRAMEFORMAT_8BIT;
    SPI_InitStructure.SPI_SCKPL = SPI_SCKPL_LOW;
    SPI_InitStructure.SPI_SCKPH = SPI_SCKPH_2EDGE;
    SPI_InitStructure.SPI_SWNSSEN = SPI_SWNSS_SOFT;
    SPI_InitStructure.SPI_PSC = SPI_PSC_16;
    SPI_InitStructure.SPI_FirstBit = SPI_FIRSTBIT_MSB;
    SPI_InitStructure.SPI_CRCPOL = 7;
    SPI_Init(SPI1, &SPI_InitStructure);

    /* SPI_SLAVE configuration */
    SPI_InitStructure.SPI_TransType = SPI_TRANSTYPE_BDMRX;
    SPI_InitStructure.SPI_Mode = SPI_MODE_SLAVE;
    SPI_Init(SPI3, &SPI_InitStructure);
    
    /* Enable SPI_MASTER TBE interrupt */
    SPI_I2S_INTConfig(SPI1, SPI_I2S_INT_TBE, ENABLE);
    /* Enable SPI_SLAVE RBNE interrupt */
    SPI_I2S_INTConfig(SPI3, SPI_I2S_INT_RBNE, ENABLE);
    /* Enable SPI1 AND SPI3 */
    SPI_Enable(SPI3, ENABLE);
    SPI_Enable(SPI1, ENABLE);
}

//步骤4 配置中断
void NVIC_Configuration(void)
{
    NVIC_InitPara NVIC_InitStructure;
    /* 1 bit for pre-emption priority, 3 bits for subpriority */
    NVIC_PRIGroup_Enable(NVIC_PRIGROUP_1);

    /* Configure and enable SPI_MASTER interrupt */
    NVIC_InitStructure.NVIC_IRQ = SPI1_IRQn;
    NVIC_InitStructure.NVIC_IRQPreemptPriority = 1;
    NVIC_InitStructure.NVIC_IRQSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    /* Configure and enable SPI_SLAVE interrupt */
    NVIC_InitStructure.NVIC_IRQ = SPI3_IRQn;
    NVIC_InitStructure.NVIC_IRQPreemptPriority = 0;
    NVIC_InitStructure.NVIC_IRQSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

中断函数:

 void SPI1_IRQHandler(void)
{
    if (SPI_I2S_GetIntBitState(SPI1, SPI_I2S_INT_TBE) != RESET)
    {   
        /* Send SPI_MASTER data */
        while (SPI_I2S_GetBitState(SPI1, SPI_FLAG_TBE) == RESET);
        SPI_I2S_SendData(SPI1, SPI_MASTER_Buffer_Tx[ TxIdx++ ]);
        if (TxIdx == BufferSize)
        {
            /* Disable SPI_MASTER TBE interrupt */
            SPI_I2S_INTConfig(SPI1, SPI_I2S_INT_TBE, DISABLE);
        }
    }
}

void SPI3_IRQHandler(void)
{
    /* Store SPI_SLAVE received data */
    if (SPI_I2S_GetIntBitState(SPI3, SPI_I2S_INT_RBNE) == SET)
        SPI_SLAVE_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(SPI3);
}


案例2:DMA_SPI收发

 发送时,在每次TXE被设置为’1’时发出DMA请求,DMA控制器则写数据至SPI_DR寄存器,TXE标志因此而被清除。

接收时,在每次RXNE被设置为’1’时发出DMA请求,DMA控制器则从SPI_DR寄存器读出数据,RXNE标志因此而被清除。

DMA1_CHANNEL2:SPI1_RX

DMA1_CHANNEL3:SPI1_TX


配置DMA代码:

void SPI1_DMA_Configuration( void )
{
    DMA_InitTypeDef DMA_InitStructure;
    
    /* DMA1 Channel2 (triggered by SPI1 Rx event) Config */
  DMA_DeInit(DMA1_Channel2);  
  DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr;                          //设置 SPI1 发送外设(0x4001300C) 地址(目的地址)
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff;                    //设置 SRAM 存储地址(目的地址)
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                //传输方向 外设-内存
  DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize;                         //设置 SPI1 发送长度
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel2, &DMA_InitStructure);
  
  DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
  /* Enable SPI1 DMA RX request */
  SPI1->CR2 |= 1<<0;                                                                 //接收缓冲区DMA使能
  DMA_Cmd(DMA1_Channel2, ENABLE);
    
    
    /* DMA1 Channel3 (triggered by SPI1 Tx event) Config */
  DMA_DeInit(DMA1_Channel3);  
  DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr;                          //设置  接收外设(0x4001300C) 地址(源地址)
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff;                    //设置 SRAM 存储地址(源地址)
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                                //传输方向 内存-外设
  DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize;                           //设置 SPI1 接收长度
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                  //外设地址增量(不变)
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                           //内存地址增量(变化)
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;           //外设传输宽度(字节)
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                   //内存传输宽度(字节)
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                     //传输方式,一次传输完停止,不重新加载
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                           //中断方式-高(三级)
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                      //内存到内存方式禁止
  DMA_Init(DMA1_Channel3, &DMA_InitStructure);
  
  DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);                                   //开启 DMA1_Channel3 传输完成中断
  DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE);                                   //开启 DMA1_Channel3 传输错误中断
  /* Enable SPI1 DMA TX request */
  SPI1->CR2 |= 1<<1;                                                                //发送缓冲区DMA使能
  DMA_Cmd(DMA1_Channel3, DISABLE);                                                  //开启 DMA 通道 DMA1_Channel3
}

SPI发送

关闭DMA通道3之前必须等待TXE为1,等待忙标志为0

void SPI1_Send( u8 *buff, u32 len )
{
    DMA1_Channel3->CPAR = SPI1_DR_Addr; //外设地址
    DMA1_Channel3->CMAR = (u32) buff; //mem地址
    DMA1_Channel3->CNDTR = len ; //传输长度
    DMA1_Channel3->CCR = (0 << 14) | // 非存储器到存储器模式
            (2 << 12) | // 通道优先级高
            (0 << 11) | // 存储器数据宽度8bit
            (0 << 10) | // 存储器数据宽度8bit
            (0 <<  9) | // 外设数据宽度8bit
            (0 <<  8) | // 外设数据宽度8bit
            (1 <<  7) | // 存储器地址增量模式
            (0 <<  6) | // 外设地址增量模式(不增)
            (0 <<  5) | // 非循环模式
            (1 <<  4) | // 从存储器读
            (1 <<  3) | // 允许传输错误中断
            (0 <<  2) | // 允许半传输中断
            (1 <<  1) | // 允许传输完成中断
            (1);        // 通道开启
}


SPI接收

必须要先关闭通道2,然后再配置通道2的参数

void SPI1_Recive( u8 *buff, u32 len )
{
    DMA1_Channel2->CCR &= ~( 1 << 0 );          //关闭DMA通道2
    
    DMA1_Channel2->CPAR = SPI1_DR_Addr; //外设地址
    DMA1_Channel2->CMAR = (uint32_t)buff; //mem地址
    DMA1_Channel2->CNDTR = len ; //传输长度
    DMA1_Channel2->CCR = (0 << 14) | // 非存储器到存储器模式
            (2 << 12) | // 通道优先级高
            (0 << 11) | // 存储器数据宽度8bit
            (0 << 10) | // 存储器数据宽度8bit
            (0 <<  9) | // 外设数据宽度8bit
            (0 <<  8) | // 外设数据宽度8bit
            (1 <<  7) | // 存储器地址增量模式
            (0 <<  6) | // 外设地址增量模式(不增)
            (0 <<  5) | // 非循环模式
            (0 <<  4) | // 传输方向 外设-内存
            (0 <<  3) | // 允许传输错误中断
            (0 <<  2) | // 允许半传输中断
            (1 <<  1) | // 允许传输完成中断
            (1);        // 通道开启
}












### STM32 SPI 使用教程 #### 一、SPI简介 串行外设接口(SPI)是一种同步串行通信接口规格,用于短距离通信。它允许微控制器与其他设备之间快速交换数据。 #### 二、SPI的工作方式 SPI支持四种不同的工作模式,这些模式由时钟极性和相位决定。对于STM32而言,在配置SPI之前需了解这四个参数的选择会影响整个系统的正常运行[^1]。 #### 三、STM32中的SPI通信实现 ##### 3.1 SPI内部结构解析 STM32系列单片机内置了多个独立的SPI模块,每个都具备完整的发送接收缓冲区以及控制寄存器等资源。为了使能并操作某个特定的SPI端口,开发者需要先对其进行必要的初始化设置。 ##### 3.2 SPI引脚定义 当使用硬件SPI功能时,必须按照官方文档指定的方式连接相应的GPIO引脚到SPI信号线上去。通常情况下,会涉及到MOSI(主输出/从输入)、MISO(主输入/从输出)、SCLK(时钟线)和NSS(芯片选择)。具体哪个IO被分配给上述哪条线路取决于所使用的开发板型号及其布局设计[^2]。 #### 四、编程实践——基于STM32的标准库API构建SPI应用实例 ##### 4.1 配置流程概述 要启动一次成功的SPI交互过程,首先要完成如下几个关键环节的操作: - **开启相关外设电源**:确保目标SPI通道处于激活状态; - **设定总线速率**:根据实际需求调整波特率因子以匹配期望的数据吞吐量; - **指定期望的工作模式**:比如这里提到的例子中选择了IDLE状态下CPOL=0(CPHA=1),即空闲期间SCK保持低电平而在第一个边沿之后开始采样;另外还启用了软件管理下的NSS信号作为主机身份验证机制的一部分。 ##### 4.2 初始化代码片段展示 下面给出了一段利用STM32标准库编写的简单SPI初始化函数示例,该例子假设读者已经熟悉如何创建项目工程框架并且能够正确调用底层驱动程序提供的服务。 ```c void SPI_Configuration(void){ // Enable the peripheral clock of GPIOA and SPI1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // Configure PA5,PA6,PA7 as alternate function push-pull mode for MOSI,MISO,SCK respectively. GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); // Initialize SPI1 with specified parameters. SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1,&SPI_InitStructure); } ``` ##### 4.3 数据传输处理逻辑 一旦完成了前期准备工作,则可以通过简单的读取或写入指令来操控外部器件。值得注意的是,由于采用了全双工架构,因此可以在同一时刻既向对方发送信息也能监听到来自另一方的消息反馈。此外,针对不同应用场景还可以灵活运用DMA(直接存储访问)技术进一步优化性能表现[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值