SPI 协议简介
SPI 是串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,是一种高速、全双工、同步通信总线,可以在同一时间发送和接收数据,SPI没有定义速度限制,通常能达到甚至超过10M/bps。
SPI 有主、从两种模式,通常由一个主模块和一个或多个从模块组成(SPI 不支持多主机),主模块选择一个从模块进行同步通信,从而完成数据的交换。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起,当存在多个从设备时,通过各自的片选信号进行管理。
SPI 接口介绍
- SCK:串行时钟信号,由主设备产生
- CS/SS:片选信号,由主设备产生,用来控制从设备
- MOSI:主设备数据输出,从设备数据输入
- MISO:主设备数据输入,从设备数据输出
SPI 四种模式
SPI 根据时钟极性和时钟相位的不同可以有 4 种工作模式。文章来源:https://www.toymoban.com/news/detail-465985.html
- 时钟极性(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 个引脚。文章来源地址https://www.toymoban.com/news/detail-465985.html
模拟驱动示例
- 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
到了这里,关于【ZYNQ】SPI 简介及 EMIO 模拟 SPI 驱动示例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!