当前位置: 首页 > news >正文

[单片机框架][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 <stdio.h>

#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);  
}


#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

相关文章:

  • 【微信小程序 | 实战开发】配置微信小程序APPID并快速接入
  • 12、IOC 之基于 Java 的容器配置
  • 最详细的Hashmap源码解析
  • java毕业设计宠物交易平台Mybatis+系统+数据库+调试部署
  • 前端实现分页效果
  • Python 基础记录 - 第1课
  • # 数据结构
  • Day1 计算机网络的概念、组成、功能、分类
  • 随机对联易语言代码
  • 测试2
  • ROS1云课→23turtlesim绘制小结(数学和编程)
  • 身份证合法验证查询易语言代码
  • 【MTU】网络链路MTU大小测试
  • 一个工地狗变成程序猿的故事
  • 猿创征文|『51单片机』串口通信
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 【css3】浏览器内核及其兼容性
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • Electron入门介绍
  • in typeof instanceof ===这些运算符有什么作用
  • Java基本数据类型之Number
  • miaov-React 最佳入门
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • springboot_database项目介绍
  • 从零搭建Koa2 Server
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 跨域
  • 设计模式(12)迭代器模式(讲解+应用)
  • 一起参Ember.js讨论、问答社区。
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • #define 用法
  • (1)(1.11) SiK Radio v2(一)
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (2)STL算法之元素计数
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (ros//EnvironmentVariables)ros环境变量
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (论文阅读40-45)图像描述1
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (转)ObjectiveC 深浅拷贝学习
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .net core webapi 大文件上传到wwwroot文件夹
  • .NET 命令行参数包含应用程序路径吗?
  • .NET企业级应用架构设计系列之技术选型
  • /etc/sudoer文件配置简析
  • @SuppressWarnings注解
  • @test注解_Spring 自定义注解你了解过吗?
  • [ CTF ]【天格】战队WriteUp- 2022年第三届“网鼎杯”网络安全大赛(青龙组)
  • [ vulhub漏洞复现篇 ] ThinkPHP 5.0.23-Rce
  • [2017][note]基于空间交叉相位调制的两个连续波在few layer铋Bi中的全光switch——
  • [Angular] 笔记 20:NgContent
  • [boost]使用boost::function和boost::bind产生的down机一例
  • [BZOJ2208][Jsoi2010]连通数