• [单片机框架][bsp层][N32G4FR][bsp_spi] spi配置和使用


    SPI 简介

    本模块中 SPI/I2S 接口复用,默认工作在 SPI 模式,可通过配置切换到 I2S 模式。二者都是同步串行接口通讯协议。串行外设接口(SPI)可工作于主机或从机模式,支持全双工、单工高速通信模式,具有硬件 CRC计算并可配置多主模式。片上音频接口(I2S)在单工通讯中可在主、从两种模式运行,支持飞利浦 I2S 标 准、MSB 对齐标准、LSB 对齐标准和 PCM 四种音频标准。

    SPI 和 I2S 主要特征

    SPI 特征

    全双工和单工同步传输
     支持主模式、从模式、多主模式
     8 或 16 位数据帧格式
     数据位顺序可编程
     软件或硬件进行 NSS 管理
     时钟极性和相位可编程
     发送和接收支持硬件 CRC 计算、校验
     支持 DMA

    I2S 功能

     单工同步传输
     支持主模式、从模式
     支持四种音频标准:飞利浦 I2S 标准、MSB 对齐标准、LSB 对齐标准和 PCM 标准
     可配置 8KHz 到 96kHz 的音频采样频率
     支持 16 位,24 位或 32 位数据长度、数据帧格式(根据需求配置)
     稳定态时钟极性可编程
     数据方向总是 MSB 在先
     支持 DMA

    在这里插入图片描述
    SPI 共 4 个引脚与外部器件相连:
     SCLK:串口时钟,作为主设备的输出,从设备的输入
     MISO:主设备输入/从设备输出管脚。在主模式下接收数据,从模式下发送数据。
     MOSI:主设备输出/从设备输入管脚。在主模式下发送数据,从模式下接收数据。
     NSS:片选管脚。分为内部管脚和外部管脚,内部管脚检测到高电平时,SPI 工作在主模式;反之,工作在从模式。从设备的 NSS 管脚可以由主设备的一个标准 I/O 引脚来控制。

    软件 NSS 模式
    SPI_CTRL1 寄存器的 SSMEN=1 启用软件从设备管理(见图 22-2)。
    该模式下不使用 NSS 管脚,通过写 SPI_CTRL1 的 SSEL 位驱动内部 NSS 信号电平(主模式 SSEL=1,从模
    式 SSEL=0)。

    硬件 NSS 模式
    SPI_CTRL1 寄存器的 SSMEN=0 禁止软件从设备管理。
    输入模式:配置为主模式、NSS 输出禁止(MSEL=1, SSOEN=0),允许操作于多主模式。主设备(从设
    备)在整个数据帧传输期间应把 NSS 脚外接到高电平(低电平)。

    输出模式:配置为主模式、NSS 输出使能(MSEL=1,SSOEN=1),SPI 作为主设备必须将 NSS 拉低,所有
    被设置为 NSS 硬件模式并与之相连的的 SPI 设备会检测到低电平,自动进入从设备状态。若主设备不能拉
    低 NSS,则进入从模式,并产生主模式失效错误 MODERR 位置‘1’。

    注:软件模式或硬件模式的选择,取决于通讯协议中是否需要 NSS 控制。如果不需要,可以选择软件模式,
    释放一个 GPIO 管脚另作他用。

    SPI 时序模式
    CLKPOL 时钟极性和 CLKPHA 时钟相位的组合选择数据捕捉的时钟边沿。配置 SPI_CTRL1 寄存器的
    CLKPOL 和 CLKPHA 位,有以下四种时序关系。
    CLKPOL=0,CLKPHA=0:SCLK 引脚在空闲状态保持低电平,数据采样时在第一个边沿,即上升沿;
    CLKPOL=0,CLKPHA=1:SCLK 引脚在空闲状态保持低电平,数据采样时在第二个边沿,即下降沿;
    CLKPOL=1,CLKPHA=0:SCLK 引脚在空闲状态保持高电平,数据采样时在第一个边沿,即下降沿;
    CLKPOL=1,CLKPHA=1:SCLK 引脚在空闲状态保持高电平,数据采样时在第二个边沿,即上升沿。
    无论采用何种时序模式,必须保证主从配置相同。
    图 22-4 是 SPI_CTRL1 寄存器 LSBFF=0 时,SPI 传输的 4 种 CLKPHA 和 CLKPOL 位组合时序。
    在这里插入图片描述
    数据格式
    配置 SPI_CTRL1 寄存器中的 LSBFF 位改变数据顺序,LSBFF=0 时,SPI 先发送高位数据(MSB);LSBFF=1
    时,SPI 先发送低位数据(LSB)。
    配置 SPI_CTRL1 寄存器的 DATFF 位选择 8 位或 16 位数据帧。

    SPI 工作模式

     主机全双工模式(MSEL=1,BIDIRMODE=0,RONLY=0)
    寄存器SPI_DAT(发送缓冲器)写入数据后,传输过程开始。在发送第一位数据的同时,数据被并行地从
    发送缓冲器传送到8位的移位寄存器中,SPI根据LSBFF的配置按顺序将数据串行地移位到MOSI管脚上;
    同时,在MISO管脚上接收到的数据,按相同顺序被串行地移位进入8位的移位寄存器中,然后被并行地传
    送到SPI_DAT寄存器(接收缓冲器)中。软件操作流程如下(见图22-5主机全双工模式下连续传输时,
    TE/RNE/BUSY的变化示意图):

    1. 使能SPI模块,SPIEN置‘1’;
    2. 将第一个待发送数据写入SPI_DAT寄存器(该操作将标志位TE清零);
    3. 等待TE置‘1’,将第二个待发送数据写入SPI_DAT。等待RNE置‘1’,读取SPI_DAT得到第一个
      接收到的数据,读SPI_DAT的同时RNE位清零。重复以上操作,发送后续的数据同时接收n-1个数
      据;
    4. 等待RNE置‘1’,接收最后一个数据;
    5. 等待TE置‘1’,然后等待BUSY清零,关闭SPI模块。

    数据收发的过程也可以在 RNE 或 TE 标志上升沿产生的中断处理程序中实现。

    在这里插入图片描述

    主机单向只发送模式(MSEL=1,BIDIRMODE=0,RONLY=0)
    单向只发送模式的传输原理同全双工模式。不同的是,该模式不会读取接收到的数据,故 SPI_STS 寄存器
    中的 OVER 位会置’1’,软件无需理会。软件操作流程如下(见图 22-6 主机单向只发送模式下连续传输时,
    TE/BUSY 变化示意图):

    1. 使能SPI模块,SPIEN置‘1’;
    2. 将第一个待发送数据写入 SPI_DAT 寄存器(该操作将标志位 TE 清零);

    主机单向只接收模式(MSEL=1,BIDIRMODE=0,RONLY=1)

    主机双向发送模式(MSEL=1,BIDIRMODE=1,BIDIROEN=1,RONLY=0)

    主机双向接收模式(MSEL=1,BIDIRMODE=1,BIDIROEN=0,RONLY=0)

    从机全双工模式(MSEL=0,BIDIRMODE=0 并且 RONLY=0)

    从机单向只发送模式(MSEL=0,BIDIRMODE=0 并且 RONLY=0)

    利用 DMA 的 SPI 通信

    SPI 利用 DMA 传输数据,将应用程序从读写收发缓冲区的过程中释放出来,大大提高了系统效率。
    当发送缓冲区 DMA 使能(SPI_CTRL2 寄存器 TDMAEN=1),每次 TE 被置’1’时发出 DMA 请求,DMA 自
    动将数据写入 SPI_DAT 寄存器,该操作会清除 TE 标志。当接收缓冲区 DMA 使能(SPI_CTRL2 寄存器
    RDMAEN =1),每次 RNE 被置’1’时发出 DMA 请求,DMA 自动从 SPI_DAT 寄存器读出数据,该操作会
    清除 RNE 标志。
    当只使用 SPI 发送数据时,只需使能 SPI 的发送 DMA 通道。此时,因为没有读取收到的数据,OVER 被置
    为’1’,此时软件无需处理这个标志。
    当只使用 SPI 接收数据时,只需使能 SPI 的接收 DMA 通道。
    在发送模式下,当 DMA 已经传输了所有要发送的数据(DMA_INTSTS 寄存器的 TXCF 标志变为’1’)后,
    可以通过监视 BUSY 标志以确认 SPI 通信结束,这样可以避免在关闭 SPI 或进入停止模式时,破坏最后一
    个数据的传输。因此软件需要先等待 TE=1,然后等待 BUSY=0。

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

    #include 
    
    #include "n32g4fr.h"
    #include "n32g4fr_gpio.h"
    #include "modules.h"
    #include "atcmd.h"
    #include "errorno.h"
    #include "x_stype.h"
    
    #include "bsp_spi.h"
    
    #define SPI_CS_GPIO_CLK                 RCC_APB2_PERIPH_GPIOA
    #define SPI_MOSI_GPIO_CLK               RCC_APB2_PERIPH_GPIOA
    #define SPI_MISO_GPIO_CLK               RCC_APB2_PERIPH_GPIOA
    #define SPI_SCK_GPIO_CLK                RCC_APB2_PERIPH_GPIOA
    #define SPI1_CLK                        RCC_APB2_PERIPH_SPI1
    
    #define SPI_CS_PIN                      GPIO_PIN_4 
    #define SPI_SCK_PIN                     GPIO_PIN_5 
    #define SPI_MISO_PIN                    GPIO_PIN_6 
    #define SPI_MOSI_PIN                    GPIO_PIN_7 
    
    #define SPI_SCK_GPIO_PORT               GPIOA      
    #define SPI_MISO_GPIO_PORT              GPIOA      
    #define SPI_MOSI_GPIO_PORT              GPIOA      
    #define SPI_CS_GPIO_PORT                GPIOA      
    
    #define SPI1_BUS                        SPI1
    #define SPI2_BUS                        SPI2
    #define SPI3_BUS                        SPI3
    
    #define TRUE                            1
    #define FALSE                           0
    
    typedef enum 
    {
        SPI_CS_LOW = 0,
        SPI_CS_HIGH,
    } spi_cs_level_t;
    
    
    static void spi_rcc_cfg(bsp_spi_bus_t bus)
    {
        if (BSP_SPI_BUS1 == bus) 
        {
            RCC_EnableAPB2PeriphClk(
                SPI_CS_GPIO_CLK | SPI_MOSI_GPIO_CLK | SPI_MISO_GPIO_CLK | SPI_SCK_GPIO_CLK | RCC_APB2_PERIPH_GPIOD, ENABLE);
    
            RCC_EnableAPB2PeriphClk(SPI1_CLK, ENABLE);
        }
    }
    
    static void spi_gpio_cfg(bsp_spi_bus_t bus)
    {
        GPIO_InitType GPIO_InitStructure;
    
        if (BSP_SPI_BUS1 == bus) 
        {
            GPIO_InitStructure.Pin        = SPI_SCK_PIN;
            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
            GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
            GPIO_InitPeripheral(SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
    
            GPIO_InitStructure.Pin = SPI_MOSI_PIN;
            GPIO_InitPeripheral(SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
    
            GPIO_InitStructure.Pin       = SPI_MISO_PIN;
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;         
            GPIO_InitPeripheral(SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
    
            GPIO_InitStructure.Pin       = SPI_CS_PIN;
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
            GPIO_InitPeripheral(SPI_CS_GPIO_PORT, &GPIO_InitStructure);
        }
    }
    
    static void bsp_spi_cs_level(bsp_spi_bus_t bus, spi_cs_level_t level)
    {
        if (BSP_SPI_BUS1 == bus)
        {
            if (SPI_CS_LOW == level)
            {
                GPIO_ResetBits(GPIOA, SPI_CS_PIN);
            }
            else if (SPI_CS_HIGH == level)
            {
                GPIO_SetBits(GPIOA, SPI_CS_PIN);
            }
        }   
    }
    
    void bsp_spi_init(bsp_spi_bus_t bus)
    {
        SPI_InitType spi_type;
        spi_type.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX,
        spi_type.SpiMode       = SPI_MODE_MASTER,
        spi_type.DataLen       = SPI_DATA_SIZE_8BITS,
        spi_type.CLKPOL        = SPI_CLKPOL_HIGH,
        spi_type.CLKPHA        = SPI_CLKPHA_SECOND_EDGE,
        spi_type.NSS           = SPI_NSS_SOFT,
        spi_type.BaudRatePres  = SPI_BR_PRESCALER_4,
        spi_type.FirstBit      = SPI_FB_MSB,
        spi_type.CRCPoly       = 7,
    
        spi_rcc_cfg(bus);
        spi_gpio_cfg(bus);
        bsp_spi_cs_level(bus, SPI_CS_HIGH);
        if (BSP_SPI_BUS1 == bus)
        {
            SPI_Init(SPI1, &spi_type);
            SPI_Enable(SPI1, ENABLE);
        }
    }
    
    int32_t bsp_spi_transmit_byte(bsp_spi_bus_t spi_bus, uint8_t data)
    {
        return bsp_spi_transmit_bytes(spi_bus, &data, sizeof(uint8_t));   
    }
    
    int32_t bsp_spi_transmit_bytes(bsp_spi_bus_t spi_bus, uint8_t *tx_buf, int16_t len)
    {
        int16_t time_out = 0;
    
        if ((NULL == tx_buf) || (len <= 0))
        {
            return RETVAL(E_NULL);
        }
            
        if (BSP_SPI_BUS1 == spi_bus) 
        {
            bsp_spi_cs_level(BSP_SPI_BUS1, SPI_CS_LOW);
    
            while (len--)
            {  
                while (SPI_I2S_GetStatus(SPI1_BUS, SPI_I2S_TE_FLAG) == RESET) 
                {
                    time_out++;
                    if (time_out > 200)
                    {
                        return RETVAL(E_SEND);
                    }    
                }
                SPI_I2S_TransmitData(SPI1_BUS, *tx_buf);
                time_out = 0;
    
                while (SPI_I2S_GetStatus(SPI1_BUS, SPI_I2S_RNE_FLAG) == RESET) 
                {
                    time_out++;
                    if (time_out > 200)
                    {
                        return RETVAL(E_RECV);
                    }            
                }
                SPI_I2S_ReceiveData(SPI1_BUS);
    
                tx_buf++;
                time_out = 0;
            }      
    
            bsp_spi_cs_level(BSP_SPI_BUS1, SPI_CS_HIGH);
        }
        return RETVAL(E_OK);  
    }
    
    int32_t bsp_spi_transfer(bsp_spi_bus_t spi_bus, uint8_t *tx_buf, int16_t len, uint8_t *rx_buf)
    {
        int16_t time_out = 0;
    
        if ((NULL == tx_buf) || (NULL == rx_buf) || (len <= 0))
        {
            return RETVAL(E_NULL);
        }
    
        if (BSP_SPI_BUS1 == spi_bus) 
        {
            bsp_spi_cs_level(BSP_SPI_BUS1, SPI_CS_LOW);
    
            while (len--)
            {
                while (SPI_I2S_GetStatus(SPI1_BUS, SPI_I2S_TE_FLAG) == RESET) 
                {
                    time_out++;
                    if (time_out > 200)
                    {
                        return RETVAL(E_SEND);
                    }    
                }
                SPI_I2S_TransmitData(SPI1_BUS, *tx_buf);
                time_out = 0;
    
                while (SPI_I2S_GetStatus(SPI1_BUS, SPI_I2S_RNE_FLAG) == RESET) 
                {
                    time_out++;
                    if (time_out > 200)
                    {
                        return RETVAL(E_RECV);
                    }            
                }
                *rx_buf = SPI_I2S_ReceiveData(SPI1_BUS);
    
                tx_buf++;
                rx_buf++;
                time_out = 0;
            }
    
            bsp_spi_cs_level(BSP_SPI_BUS1, SPI_CS_HIGH);
        }
        return RETVAL(E_OK);  
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    #ifndef  __BSP_SPI_H__
    #define  __BSP_SPI_H__
    
    typedef enum
    {
        BSP_SPI_BUS1,
        BSP_SPI_BUS_NUM,
    } bsp_spi_bus_t;
    
    void bsp_spi_init(bsp_spi_bus_t bus);
    int32_t bsp_spi_transfer(bsp_spi_bus_t spi_bus, uint8_t *tx_buf, int16_t len, uint8_t *rx_buf);
    int32_t bsp_spi_transmit_byte(bsp_spi_bus_t spi_bus, uint8_t data);
    int32_t bsp_spi_transmit_bytes(bsp_spi_bus_t spi_bus, uint8_t *tx_buf, int16_t len);
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    S2SH志愿者捐赠管理系统|捐助计算机毕业论文Java项目源码下载
    是时候来点现代c++了 c++11之超级重要之smart pointer详细解析
    vue的axios是干什么的
    数学建模笔记(十五):多元统计分析及R语言建模(含数据代码注释,均可供运行,高版本无法导入包则使用自编代码计算)
    如何使用 Delphi/Lazarus 代码在 FastReport VCL 中生成二维码?
    EBI、DDD及其演变架构史
    SonarQube的使用心得
    数据库入门知识基本简介
    Java之方法(6)
    计算机网络之数据链路层
  • 原文地址:https://blog.csdn.net/qq_29246181/article/details/126776662