软件模拟SPI接口程序代码

article/2025/8/23 7:57:02

目录

SPI协议简介

SPI接口介绍

SPI接口连接图

SPI数据传输方向

 SPI传输模式

模拟SPI程序

SPI协议简介

SPI的通信原理很简单,一般主从方式工作,这种模式通常有一个主设备和一个或者多个从设备,通常采用的是4根线,它们是MISO(数据输入,针对主机来说)、MOSI(数据输出,针对主机来说)、SCLK(时钟,主机产生)、CS/SS(片选,一般由主机发送或者直接使能,通常为低电平有效)

  • SPI接口介绍

SCK:时钟信号,由主设备产生,所以主设备SCK信号为输出模式,从设备的SCK信号为输入模式

CS:使能信号,由主设备控制从设备,所以主设备CS信号为输出模式,从设备的CS信号为输入模式。

MOSI:主设备数据输出,从设备输入,所以主设备MOSI信号为输出模式,从设备的MOSI信号为输入模式

MISO:主设备数据输入,从设备数据输出,所以主设备MISO信号为输入模式,从设备的MISO信号为输出模式

  • SPI接口连接图

注意:MOSI和MISO不能交叉连接(可以把主从机理解为一个整体系统,MOSI为系统主机发送从机接收的数据线,MISO为主机接收从机发送的数据线)

  • SPI数据传输方向

SPI作为全双工的串行通信协议,数据传输时高位在前,低位在后。主机和从机公用由主机产生的SCK信号,所以在每个时钟周期内主机和从机有1bit的数据交换(因为MOSI和MISO数据线上的数据都是在时钟的边沿处被采样)。如下图

SPI协议规定数据采样是在SCK的上升沿或下降沿时刻(由SPI模式决定,下面会说到),观察上图,在SCK的边沿处(上升沿或下降沿),主机会在MISO数据线上采样(接收来从机的数据),从机会在MOSI数据线上采样(接受来自主机的数据),所以每个时钟周期中会有一bit的数据交换。

 SPI传输模式

SPI总线传输一共有4种模式,这4种模式分别由时钟极性(CPOL)和时钟相位(CPHA)来定义。

 CPOL:规定了SCK时钟信号空闲状态的电平

CPHA:规定了数据是在SCK时钟的奇数边沿还是偶数边沿被采样

模式0:CPOL=0,CPHA=0 SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)。

模式1:CPOL=0,CPHA=1 SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)

模式2:CPOL=1,CPHA=0 SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)

模式3:CPOL=1,CPHA=1 SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)

以模式0为例,SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据),在SCK的下降沿奇幻数据线的数据。

  •  在时钟的第一个上升沿(游标1处)(采样点)

MOSI上数据为1,则在此边沿从机采样,数据为1,采样点在MOSI数据线的中间

MISO上数据为0,则在此边沿从机采样,数据为0,采样点在MISO数据线的中间

  • 在时钟的第一个下降沿(游标2处)(触发点)

MOSI上数据由1切换为0,数据在时钟的下降沿时切换数据

MISO上数据由0切换为1,数据在时钟的下降沿时切换数据

  • 在时钟的第2~8个上升沿(采样点),主机在MISO上采样数据,从机在MOSI上采样数据
  • 在时钟的第2~8个下降沿(触发点),主机在MISO上切换数据,从机在MOSI上切换数据

模拟SPI程序

初始化代码

#define    SPI_SCK_PIN            GPIO_PIN_5
#define    SPI_SCK_GPIO_PORT      GPIOA
#define    SPI_MOSI_PIN           GPIO_PIN_7
#define    SPI_MOSI_GPIO_PORT     GPIOA
#define    SPI_MISO_PIN           GPIO_PIN_6
#define    SPI_MISO_GPIO_PORT     GPIOA
#define    SPI_NSS_PIN            GPIO_PIN_14
#define    SPI_NSS_GPIO_PORT      GPIOOD#define    SPI_SCK_GPIO_CLK_ENABLE()     HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT,                                 SPI_MOSI_PIN, GPIO_PIN_SET)  
#define MOSI_L  HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN,                         GPIO_PIN_RESET)  
#define SCK_H   HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_SET)  
#define SCK_L   HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN,             GPIO_PIN_RESET)  
#define MISO    HAL_GPIO_ReadPin(SPI_MISO_GPIO_PORT, SPI_MISO_PIN) 
#define NSS_H   HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET)  
#define NSS_L   HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN,     GPIO_PIN_RESET) void SPI_Init(void)
{  /*##-1- Enable peripherals and GPIO Clocks #########################*//* Enable GPIO TX/RX clock */SPI_SCK_GPIO_CLK_ENABLE();SPI_MISO_GPIO_CLK_ENABLE();SPI_MOSI_GPIO_CLK_ENABLE();SPI_NSS_GPIO_CLK_ENABLE();/*##-2- Configure peripheral GPIO #######################*//* SPI SCK GPIO pin configuration  */GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.Pin       = SPI_SCK_PIN;GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;//GPIO_InitStruct.Pull      = GPIO_PULLDOWN;GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &GPIO_InitStruct);HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_SET);/* SPI MISO GPIO pin configuration  */GPIO_InitStruct.Pin = SPI_MISO_PIN;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &GPIO_InitStruct);/* SPI MOSI GPIO pin configuration  */GPIO_InitStruct.Pin = SPI_MOSI_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &GPIO_InitStruct);HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_SET);GPIO_InitStruct.Pin = SPI_NSS_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;HAL_GPIO_Init(SPI_NSS_GPIO_PORT, &GPIO_InitStruct);HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET);}

模拟SPI4种工作模式:

/*CPOL = 0 , CPHA = 0,MSB first*/
uint8_t SOFT_SPI_RW_MODE0(uint8_t write_data)
{uint8_t i, read_data;for(i=0;i<8;i++){if(write_data & 0x80)MOSI_H;elseMOSI_L;write_data <<= 1;delay_us(1);SCK_H;read_data <<= 1;if(MISO)read_data++;delay_us(1);SCK_L;__nop();    }return read_data;}/* CPOL=0,CPHA=1, MSB first */
uint8_t SOFT_SPI_RW_MODE1(uint8_t byte) 
{uint8_t i,Temp=0;for(i=0;i<8;i++)     // 循环8次{SCK_H;     //拉高时钟if(byte&0x80){MOSI_H;  //若最到位为高,则输出高}else      {MOSI_L;   //若最到位为低,则输出低}byte <<= 1;     // 低一位移位到最高位delay_us(1);SCK_L;     //拉低时钟Temp <<= 1;     //数据左移if(MISO)Temp++;     //若从从机接收到高电平,数据自加一delay_us(1);}return (Temp);     //返回数据
}/* CPOL=1,CPHA=0, MSB first */
uint8_t SOFT_SPI_RW_MODE2(uint8_t byte) 
{uint8_t i,Temp=0;for(i=0;i<8;i++)     // 循环8次{if(byte&0x80){MOSI_H;  //若最到位为高,则输出高}else      {MOSI_L;   //若最到位为低,则输出低}byte <<= 1;     // 低一位移位到最高位delay_us(1);SCK_L;     //拉低时钟Temp <<= 1;     //数据左移if(MISO)Temp++;     //若从从机接收到高电平,数据自加一delay_us(1);SCK_H;     //拉高时钟}return (Temp);     //返回数据
}/* 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;delay_us(1);	SCK_H; read_dat <<= 1;  if( MISO ) read_dat++; delay_us(1);__nop();}return read_dat;
}


http://chatgpt.dhexx.cn/article/9cH24bFw.shtml

相关文章

SPI接口及驱动

1. 简介 SPI接口是Motorola 首先提出的全双工三线同步串行外围接口&#xff0c;采用主从模式&#xff08;Master Slave&#xff09;架构。支持多slave模式应用&#xff0c;一般仅支持单Master。时钟由Master控制&#xff0c;在时钟移位脉冲下&#xff0c;数据按位传输&#xf…

SPI 接口

SPI 接口的全称是“Serial Peripheral Interface”意为串行外围接口&#xff0c;是 Motorola 首先在其 MC68HCxx 系列处理器上定义的。SPI 接口主要应用于 EEPROM、FLASH、实时时钟、AD转换器&#xff0c;还有数字信号处理器和数字信号解码器之间。 SPI 接口是在 CPU 和外围低…

ESP32 SPI 接口的应用

总体介绍 1. ESP32 共有 4 个 SPI 控制器 SPI0、SPI1、SPI2、SPI3&#xff0c;用于连接支持 SPI 协议的设备。 SPI0 控制器作为 cache 访问外部存储单元接口使用;SPI1 作为主机使用;SPI2 和 SPI3 控制器既可作为主机使用又可作为从机使用。作主机使用时&#xff0c;每个 SPI 控…

SPI接口简介-Piyu Dhaker

SPI接口简介 作者&#xff1a; Piyu Dhaker 串行外设接口(SPI)是微控制器和外围IC&#xff08;如传感器、ADC、DAC、移位寄存器、SRAM等&#xff09;之间使用最广泛的接口之一。本文先简要说明SPI接口&#xff0c;然后介绍ADI公司支持SPI的模拟开关与多路转换器&#xff0c;以…

弄懂SPI接口

SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外设接口&#xff09;是Motorola公司提出的一种同步串行数据传输标准&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;在很多器件中被广泛应用。 SPI相关缩写 SS: Slave Select&…

SPI接口

SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外设接口&#xff09;是Motorola公司提出的一种同步串行数据传输标准&#xff0c;在很多器件中被广泛应用。 1. 接口 SPI接口经常被称为4线串行总线&#xff0c;SPI协议是主从模式&#xff1a;从机不主动发起访问&…

SPI接口简介

串行外设接口(SPI)是微控制器和外围IC&#xff08;如传感器、ADC、DAC、移位寄存器、SRAM等&#xff09;之间使用最广泛的接口之一。本文先简要说明SPI接口&#xff0c;然后介绍ADI公司支持SPI的模拟开关与多路转换器&#xff0c;以及它们如何帮助减少系统电路板设计中的数字GP…

SPI接口详解

一、SPI接口简介 SPI 是英语Serial Peripheral interface的缩写&#xff0c;顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。 SPI&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用…

【科普贴】SPI接口详解

一、SPI接口简介 SPI接口是一种同步串行总线&#xff08;Serial Peripheral Interface&#xff09;多用于Flash存储器&#xff08;如NOR Flash&Nand Flash&#xff09;&#xff0c;ADC、LCD控制器等外围器件的通讯接口。大大增强了处理器的外设扩展能力。 SPI接口缩写 SSE…

第四章 - 程序计数器

文章目录 1.PC 寄存器介绍2.PC 寄存器的作用3.代码示例4.两个常见面试题5.CPU时间片 官网文档 1.PC 寄存器介绍 JVM中的程序计数寄存器&#xff08;Program Counter Register&#xff09;&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。…

运行时数据区 - 程序计数器

① 介绍 JVM中的程序计数寄存器(Program Counter Register)中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。 PC寄存器用来存储指向下一条指令的地址(即将要执行的指令代码)&#xff0c;由执行…

操作系统-程序计数器

查考资料&#xff1a;https://blog.csdn.net/xxb2008/article/details/42145649 程序计数器是用于存放下一条指令所在单元的地址的地方。 冯 诺伊曼计算机体系结构的主要内容之一就是“程序预存储&#xff0c;计算机自动执行”&#xff01; 处理器要执行的程序&#xff08;指…

04-程序计数器(PC计数器)

程序计数器 介绍 JVM中的程序计数寄存器&#xff08;Program Counter Register&#xff09;中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。 这里&#xff0c;并非是广义上所指的物理寄存器&…

三、程序计数器(PC寄存器)

文章目录 1.PC Register 介绍介绍作用 2.举例说明3.两个常见问题使用PC寄存器存储字节码指令的地址有什么用&#xff1f;PC寄存器为什么会被设定为线程私有的&#xff1f; 1.PC Register 介绍 介绍 JVM中的程序计时器&#xff08;Program Counter Register&#xff09;中&…

汇编语言、寄存器分类及程序计数器

目录 一、计算机语言 汇编语言 寄存器分类 二、程序计数器 一、计算机语言 计算机是由二进制构成的&#xff0c;它只能听懂二进制也就是机器语言&#xff0c;但是普通人是无法看懂机器语言的&#xff0c;这个时候就需要一种电脑既能识别&#xff0c;人又能理解的语言&…

Java --- JVM程序计数器(PC寄存器)

目录 一、程序计数器(PC寄存器) 二、主要功能作用 三、PC Register 四、PC寄存器面试题 五、CPU时间片 一、程序计数器(PC寄存器) JVM中的程序计数寄存器(Program Counter Register)中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。C…

计数器

文章目录 【 1. 同步计数器 】① 同步二进制计数器同步二进制加法计数器74161 十六进制四位二进制加法计数器74163 同步二进制减法计数器同步二进制加/减计数器单时钟方式 74191双时钟方式 74193 ② 同步十进制计数器同步十进制加法计数器7416074162 同步十进制减法计数器同步十…

4 程序计数器

1、输入日期&#xff0c;转化为毫秒数&#xff1a; 用calendar方法&#xff08;calendar.getTime&#xff09; 代码&#xff1a; public static void main(String[] args) {Calendar calendar Calendar.getInstance();calendar.set(2017, 6, 27, 15, 9, 0);System.out.print…

程序计数器(PC)

1.PC中存放下一次访存的地址。 2.PC自增的确是PC取出指令的长度。 3.PC每次自增都是固定的字长。 ———————————————————————————— 1.程序计数器的工作原理 大概解释一下程序计数器吧&#xff0c;用一个最简单的例子。 首先第一点&#xff1a;PC中存…

4、程序计数器PC

介绍 JVM中的程序计数寄存器&#xff08;Program Counter Register&#xff09;中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。这里&#xff0c;并非是广义上所指的物理寄存器&#xff0c;或许…