【ZYNQ】SPI 简介及 EMIO 模拟 SPI 驱动示例

简介: 【ZYNQ】SPI 简介及 EMIO 模拟 SPI 驱动示例

SPI 协议简介

SPI 是串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,是一种高速、全双工、同步通信总线,可以在同一时间发送和接收数据,SPI没有定义速度限制,通常能达到甚至超过10M/bps。


SPI 有主、从两种模式,通常由一个主模块和一个或多个从模块组成(SPI 不支持多主机),主模块选择一个从模块进行同步通信,从而完成数据的交换。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起,当存在多个从设备时,通过各自的片选信号进行管理。

SPI 接口介绍

  • SCK:串行时钟信号,由主设备产生
  • CS/SS:片选信号,由主设备产生,用来控制从设备
  • MOSI:主设备数据输出,从设备数据输入
  • MISO:主设备数据输入,从设备数据输出

SPI 四种模式

SPI 根据时钟极性和时钟相位的不同可以有 4 种工作模式。

  • 时钟极性(CPOL)指通讯设备处于空闲状态(SPI 开始通讯前、CS 线无效)时 SCK 的状态。
  • CPOL = 0:SCK在空闲时为低电平
  • CPOL = 1:SCK在空闲时为高电平
  • 时钟相位(CPHA)指数据的采样时刻位于 SCK 的偶数边沿采样还是奇数边沿采样。
  • CPHA = 0:在SCK的奇数边沿采样
  • CPHA = 1:在SCK的偶数边沿采样

通过时钟极性和时钟相位的不同组合 SPI 总共可以设置为4种工作模式

  • MODE0 : CPOL = 0 CPHA = 0 SCK空闲为低,SCK的奇数次边沿(上升沿)采样
  • MODE1 : CPOL = 0 CPHA = 1 SCK空闲为低,SCK的偶数次边沿(下降沿)采样
  • MODE2 : CPOL = 1 CPHA = 0 SCK空闲为高,SCK的奇数次边沿(下降沿)采样
  • MODE3 : CPOL = 1 CPHA = 1 SCK空闲为高,SCK的偶数次边沿(上升沿)采样

ZYNQ EMIO

EMIO 是扩展的 MIO,ZYNQ 支持通过配置将 PS 的控制器信号通过 EMIO 输出,即 EMIO 是在 PL 侧连接使用 PS 侧资源的扩展通道接口。可扩展到 PIN 上,也可以扩展到运用上。EMIO 与 MIO 一样归属于 GPIO,即经过扩展 PS 一共可以控制 54(MIO) + 64(EMIO) = 118 个引脚。

模拟驱动示例

  • spi_ctrl.c
/**
 * Copyright (c) 2022-2023,HelloAlpha
 *
 * Change Logs:
 * Date           Author       Notes
 */
#include "spi_ctrl.h"

/* CPOL = 0, CPHA = 0, MSB first */
uint8_t SOFT_SPI_RW_MODE0(uint8_t write_dat)
{
    uint8_t i, read_dat;
    for( i = 0; i < 8; i++ )
    {
        if( write_dat & 0x80 )
            MOSI_H();
        else
            MOSI_L();
        write_dat <<= 1;
        SPI_DELAY(1); 
        SCK_H(); 
        read_dat <<= 1;  
        if( MISO() ) 
            read_dat++; 
        SPI_DELAY(1);
        SCK_L(); 
        SPI_DELAY(1);
    }
    return read_dat;
}

/* CPOL=0,CPHA=1, MSB first */
uint8_t SOFT_SPI_RW_MODE1(uint8_t write_dat) 
{
    uint8_t i, read_dat;

    for( i = 0; i < 8; i++ )
    {
        SCK_H();
        if( write_dat & 0x80 )
            MOSI_H();
        else
            MOSI_L();
        write_dat <<= 1;
        SPI_DELAY(100);
        SCK_L();
        read_dat <<= 1;
        if(MISO())
            read_dat++;
        SPI_DELAY(100);
  }
    return read_dat;
}
 
/* CPOL=1,CPHA=0, MSB first */
uint8_t SOFT_SPI_RW_MODE2(uint8_t write_dat) 
{
    uint8_t i, read_dat;

    for( i = 0; i < 8; i++ )
    {
        if( write_dat & 0x80 )
            MOSI_H();
        else
            MOSI_L();
        write_dat <<= 1;
        SPI_DELAY(1);
        SCK_L();
        read_dat <<= 1;
        if(MISO())
            read_dat++;
        SPI_DELAY(1);
        SCK_H();
  }
  return read_dat;
}
 
/* CPOL = 1, CPHA = 1, MSB first */
uint8_t SOFT_SPI_RW_MODE3( uint8_t write_dat )
{
    uint8_t i, read_dat;
    for( i = 0; i < 8; i++ )
    {
        SCK_L(); 
        if( write_dat & 0x80 )
            MOSI_H();  
        else                    
            MOSI_L();  
        write_dat <<= 1;
        SPI_DELAY(1); 
        SCK_H(); 
        read_dat <<= 1;  
        if( MISO() ) 
            read_dat++; 
        SPI_DELAY(1);
    }
    return read_dat;
}


  • spi_ctrl.h
/**
 * Copyright (c) 2022-2023,HelloAlpha
 *
 * Change Logs:
 * Date           Author       Notes
 */
#ifndef __SPI_CTRL_H__
#define __SPI_CTRL_H__

#include "spi_io.h"

#include "sleep.h"

#define SPI_DELAY(...)  usleep(__VA_ARGS__)

#define SPI_START_COMMUNICATION     NSS_L()
#define SPI_STOP_COMMUNICATION      NSS_H()

/**
 * CPOL 配置 SPI 总线的极性
 * CPHA 配置 SPI 总线的相位
 * 
 * 模式0:CPOL=0,CPHA =0  MSB first
 *      SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
 * 模式1:CPOL=0,CPHA =1  MSB first
 *      SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
 * 模式2:CPOL=1,CPHA =0  MSB first
 *      SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
 * 模式3:CPOL=1,CPHA =1  MSB first
 *      SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)
 */

/* CPOL = 0, CPHA = 0,  MSB first*/
uint8_t SOFT_SPI_RW_MODE0(uint8_t write_dat);
/* CPOL=0,CPHA=1, MSB first */
uint8_t SOFT_SPI_RW_MODE1(uint8_t write_dat);
/* CPOL=1,CPHA=0, MSB first */
uint8_t SOFT_SPI_RW_MODE2(uint8_t write_dat);
/* CPOL = 1, CPHA = 1, MSB first */
uint8_t SOFT_SPI_RW_MODE3( uint8_t write_dat );

#endif
  • spi_io.c
/**
 * Copyright (c) 2022-2023,HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#include "spi_io.h"

extern XGpioPs GpioPs;

static void SET_PIN_OUT(uint32_t PIN)
{
    XGpioPs_SetDirectionPin(&GpioPs, PIN, GPIO_MODEL_OUTPUT);
  XGpioPs_SetOutputEnablePin(&GpioPs, PIN, GPIO_OUTPUT_ENABLE);
}

void MOSI_H(void)
{
    SET_PIN_OUT(SPI_MOSI_PIN);
    XGpioPs_WritePin(&GpioPs, SPI_MOSI_PIN, GPIO_SET);
}
 
void MOSI_L(void)
{
    SET_PIN_OUT(SPI_MOSI_PIN);
    XGpioPs_WritePin(&GpioPs, SPI_MOSI_PIN, GPIO_RESET);
}
void SCK_H(void)
{
    SET_PIN_OUT(SPI_SCK_PIN);
    XGpioPs_WritePin(&GpioPs, SPI_SCK_PIN, GPIO_SET);
}
void SCK_L(void)
{
    SET_PIN_OUT(SPI_SCK_PIN);
    XGpioPs_WritePin(&GpioPs, SPI_SCK_PIN, GPIO_RESET);
}
uint32_t MISO(void)
{
    XGpioPs_SetDirectionPin(&GpioPs, SPI_MISO_PIN, GPIO_MODEL_INPUT);
    return XGpioPs_ReadPin(&GpioPs, SPI_MISO_PIN);
}
void NSS_H(void)
{
    SET_PIN_OUT(SPI_NSS_PIN);
    XGpioPs_WritePin(&GpioPs, SPI_NSS_PIN, GPIO_SET);
}
void NSS_L(void)
{
    SET_PIN_OUT(SPI_NSS_PIN);
    XGpioPs_WritePin(&GpioPs, SPI_NSS_PIN, GPIO_RESET);
}
  • spi_io.h
/**
 * Copyright (c) 2022-2023,HelloAlpha
 *
 * Change Logs:
 * Date           Author       Notes
 */
#ifndef __SPI_IO_H__
#define __SPI_IO_H__

#include "xgpiops.h"

#define SPI_SCK_PIN             54
#define SPI_MOSI_PIN            55
#define SPI_MISO_PIN            56
#define SPI_NSS_PIN             57

#define GPIO_MODEL_INPUT        0
#define GPIO_MODEL_OUTPUT       1

#define GPIO_OUTPUT_DISABLE     0
#define GPIO_OUTPUT_ENABLE      1

#define GPIO_RESET              0
#define GPIO_SET                1

void MOSI_H(void);
void MOSI_L(void);
void SCK_H(void);
void SCK_L(void);
uint32_t MISO(void);
void NSS_H(void);
void NSS_L(void);

#endif


相关文章
|
5月前
|
开发者
【经典案例】使用HAL库配置STM32F407的SPI外设
在嵌入式系统开发中,STM32F407是一款广泛应用的微控制器,而SPI(Serial Peripheral Interface)是一种常用的通信接口。本文将详细介绍如何使用STM32的硬件抽象层(HAL)库配置STM32F407的SPI外设,并提供完整的代码示例。
568 1
|
4月前
stm32f407探索者开发板(十四)——IO引脚复用和映射
stm32f407探索者开发板(十四)——IO引脚复用和映射
420 0
|
4月前
|
异构计算 内存技术
FPGA进阶(1):基于SPI协议的Flash驱动控制(一)
FPGA进阶(1):基于SPI协议的Flash驱动控制(一)
185 0
|
4月前
|
异构计算 内存技术
FPGA进阶(1):基于SPI协议的Flash驱动控制(二)
FPGA进阶(1):基于SPI协议的Flash驱动控制
56 0
|
6月前
|
Perl
【ZYNQ】IIC 简介及 EMIO 模拟 IIC 驱动示例
【ZYNQ】IIC 简介及 EMIO 模拟 IIC 驱动示例
263 0
|
6月前
|
测试技术 Perl
【ZYNQ】ZYNQ7000 UART 控制器及驱动应用示例
【ZYNQ】ZYNQ7000 UART 控制器及驱动应用示例
260 0
|
6月前
|
传感器 开发工具 芯片
【ZYNQ】ZYNQ7000 XADC 及其驱动示例
【ZYNQ】ZYNQ7000 XADC 及其驱动示例
156 0
|
存储 芯片 内存技术
STM32F0单片机快速入门十 用 SPI HAL 库读写W25Q128
STM32F0单片机快速入门十 用 SPI HAL 库读写W25Q128
|
Linux
总线驱动--SPI驱动(下)
总线驱动--SPI驱动
193 0
|
Linux API SoC
总线驱动--SPI驱动(上)
总线驱动--SPI驱动
341 0