通讯协议学习之路(实践部分):SPI开发实践

news2025/1/12 5:03:06

通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。

后续文章会同时发表在个人博客(jason1016.club)、CSDN;视频会发布在bilibili(UID:399951374)

本文前缀:

通讯协议专栏:通讯协议_JASON丶LI的博客-CSDN博客

UART理论部分:

一、具体实践方案选择

同样的对于SPI也具有软件模拟和硬件外设配置的两种方案,此外也同样可以采用DMA转运数据、中断处理数据、轮询处理数据这三种方案。

软件SPI和硬件SPI之间的关系是,软件SPI是对硬件SPI的一种软件实现。软件SPI可以在没有硬件SPI模块的情况下实现SPI通信,但由于软件实现的限制,软件SPI的速度和可靠性可能不如硬件SPI。在一些资源受限的系统中,软件SPI是一种常用的替代方案。

软件模拟

按照SPI传输的时序与模式,通过对SCK、SS、MOSI、MISO这四个进行高低电平的时序配置是实现SPI通讯协议的模拟。

硬件模式

硬件模式直接配置单片机的SPI外设,使用其封装的库进行协议通信,不需要像软件一样一步步配置其时序,硬件SPI的工作状态主要通过读其SPI内部寄存器进行判断。其信息读取的4种模式按照参考下表。

NSS管脚与片选

NSS管脚及我们熟知的片选信号,作为主设备NSS管脚为高电平,从设备NSS管脚为低电平。当NSS管脚为低电平时,该spi设备被选中,可以和主设备进行通信。在stm32中,每个spi控制器的NSS信号引脚都具有两种功能,即输入和输出。所谓的输入就是NSS管脚的信号给自己。所谓的输出就是将NSS的信号送出去,给从机。

对于NSS的输入,又分为软件输入和硬件输入。

软件输入:

NSS分为内部管脚和外部管脚,通过设置spi_cr1寄存器的ssm位和ssi位都为1可以设置NSS管脚为软件输入模式且内部管脚提供的电平为高电平,其中SSM位为使能软件输入位。SSI位为设置内部管脚电平位。同理通过设置SSM和SSI位1和0则此时的NSS管脚为软件输入模式但内部管脚提供的电平为0。若从设备是一个其他的带有spi接口的芯片,并不能选择NSS管脚的方式,则可以有两种办法,一种是将NSS管脚直接接低电平。另一种就是通过主设备的任何一个gpio口去输出低电平选中从设备。

硬件输入:

主机接高电平,从机接低电平。

二、开发实践

标准库

软件SPI

SPI_Software.c
#include "stm32f10x.h"                  // Device header
#include "SPI_Software.h"

void MySPI_W_SS(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)
{
    return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

void MySPI_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    MySPI_W_SS(1);
    MySPI_W_SCK(0);
}

void MySPI_Start(void)
{
    MySPI_W_SS(0);
}

void MySPI_Stop(void)
{
    MySPI_W_SS(1);
}

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
    uint8_t i, ByteReceive = 0x00;
    
    for (i = 0; i < 8; i ++)
    {
        MySPI_W_MOSI(ByteSend & (0x80 >> i));
        MySPI_W_SCK(1);
        if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
        MySPI_W_SCK(0);
    }
    
    return ByteReceive;
}

//SPI写应该Byte函数
void SPI_WriteByte(uint8_t Byte)
{
    uint8_t i;
    for(i = 0;i < 8;i++)
    {
        //SCK从低电平到高电平(上升沿)时传输数据
        MySPI_W_SCK(0);
        if(Byte & 0x80)            //取出最高为,每次只能传输一个bit的数据
        {
            MySPI_W_MOSI(1);
        }
        else
        {
            MySPI_W_MOSI(0);
        }
        Byte <<= 1;
        MySPI_W_SCK(1);
    }
    MySPI_W_SCK(0);
}
 
//SPI读一个Byte函数
uint8_t SPI_ReadByte(void)
{
    uint8_t i,Byte;
    MySPI_W_SCK(0);
    for(i = 0;i < 8;i++)
    {
        MySPI_W_SCK(1);
        Byte <<= 1;
        if(MySPI_R_MISO())
        {
            Byte ++;
        }
        MySPI_W_SCK(0);
    }
    return Byte;
}

SPI_Software.h
#ifndef __SPISOFTWARE_H
#define __SPISOFTWARE_H

#include "stm32f10x.h"                  // Device header

void MySPI_W_SS(uint8_t BitValue);


void MySPI_W_SCK(uint8_t BitValue);


void MySPI_W_MOSI(uint8_t BitValue);

uint8_t MySPI_R_MISO(void);



void MySPI_Init(void);

void SPI_WriteByte(uint8_t Byte);

uint8_t SPI_ReadByte(void);



#endif
SPI_Control.c
#include "SPI_Control.h"
#include "SPI_Software.h"

//设备为:25AA010A


//EEPROM开启写使能函数
void EEPROM_Write_ENABLE(void)
{
    //MySPI_W_SS(1);
    MySPI_W_SS(0);
    SPI_WriteByte(EEPROM_Address_ENABLE);
    MySPI_W_SS(1);
}
 
//EEPROM关闭写使能函数
void EEPROM_Write_DISABLE(void)
{
    //MySPI_W_SS(1);
    MySPI_W_SS(0);
    SPI_WriteByte(EEPROM_Address_DISABLE);
    MySPI_W_SS(1);
}
 
//从EEPROM中读取数据
uint8_t EEPROM_Read(uint8_t HW_Address,uint8_t SW_Address)
{
    uint8_t date = 0;
    //MySPI_W_SS(1);
    MySPI_W_SS(0);
    SPI_WriteByte(HW_Address);
    SPI_WriteByte(SW_Address);
    date = SPI_ReadByte();
    MySPI_W_SS(1);
    return date;
}
 
//往EEPROM中写数据函数
void EEPROM_Write(uint8_t HW_Address,uint8_t SW_Address,uint8_t date)
{
    //HW_Address:EEPROM硬件地址
    //SW_Address: EEPROM的软件地址,即写出内存的地址
    uint8_t status = 0x01;        
    EEPROM_Write_ENABLE();                            //开启写使能
    //MySPI_W_SS(1);
    MySPI_W_SS(0);
    SPI_WriteByte(HW_Address);
    SPI_WriteByte(SW_Address);
    SPI_WriteByte(date);
    MySPI_W_SS(1);
    
    //读取EEPROM状态寄存器的最低为,当状态寄存器的最低位为1表示还未写完
    while(1)
    {
        //MySPI_W_SS(1);
        MySPI_W_SS(0);
        SPI_WriteByte(EEPROM_Address_REGISTER);
        status = SPI_ReadByte();
        if((status & 0x01) == 0)
        {
            break;
        }
        MySPI_W_SS(1);
    }
    EEPROM_Write_DISABLE();                        //关闭写使能
}
SPI_Control.h
#ifndef __SPICONTROL_H
#define __SPICONTROL_H

#include "stm32f10x.h"                  // Device header

#define EEPROM_Address_W 0x02                                //从指定地址开始写
#define EEPROM_Address_R 0X03                                //从指定地址开始读
#define EEPROM_Address_ENABLE 0x06                    //开启写使能命令
#define EEPROM_Address_DISABLE 0x04                    //关闭写使能命令
#define EEPROM_Address_REGISTER 0x05        //读取寄存器的状态(状态寄存器的值)


void EEPROM_Write_ENABLE(void);

void EEPROM_Write_DISABLE(void);

uint8_t EEPROM_Read(uint8_t HW_Address,uint8_t SW_Address);

void EEPROM_Write(uint8_t HW_Address,uint8_t SW_Address,uint8_t date);


#endif
main.c
#include "stm32f10x.h"                  // Device header
#include <string.h>
#include "delay.h"
#include "sys.h"
#include "led.h"
#include "OLED.h"
#include "key.h"
#include "SPI_Control.h"
#include "SPI_Software.h"

uint8_t RxData;
extern uint8_t num;

int main(void)
{
    led_Init();
    Key_Init();
    OLED_Init();
    MySPI_Init();
//    num = EEPROM_Read(EEPROM_Address_R,0x00);
    while(1)
    {
        OLED_ShowNum(1,1,num,2);
        EEPROM_Write(EEPROM_Address_W,0x00,num);
        led_turn(GPIOB, GPIO_Pin_0);
        Delay_ms(1000);
    }
}

硬件SPI

HAL库

模式设置:

  1. 有主机模式全双工/半双工——Full-Duplex Master
  2. 从机模式全双工/半双工——Ful-Duplex Slave
  3. 只接收主机模式/只接收从机模式——Half-Duplex Master
  4. 只发送主机模式——Half-Duplex Slave

SPI发送和接收轮询、中断、DMA三种模式操作函数

/* IO operation functions  ****************************************************/
/******* Blocking mode: Polling */
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);

/******* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);

HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_EnableListen_IT(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_DisableListen_IT(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_Master_Abort_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress);

/******* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);

HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);

 硬件模式:

SPI的proteus硬件模式仍在调试,后续会持续更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1203500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

xinput1_3.dll丢失的详细解决步骤办法和比较,五种有效的解决办法

今天想和大家分享一个电脑中经常出现的问题——xinput1_3.dll丢失。这个文件丢失是一件常见的问题。不知道小伙伴们有没有遇到过这样的问题&#xff0c;如果你遇到这样的问题今天就教大家xinput1_3.dll丢失的详细解决步骤办法和比较&#xff0c;五种有效的解决办法。 一.xinput…

本地跑项目解决跨域问题

跨域问题&#xff1a; 指的是浏览器不能执行其他网站的脚本&#xff0c;它是由浏览器的同源策略造成的&#xff0c;是浏览器对 javascript 施加的安全限制。 同源策略&#xff1a; 是指协议&#xff08;protocol&#xff09;、域名&#xff08;host&#xff09;、端口号&…

开源7合一万能DIY小程序源码系统 一个后台整合7端 轻松搭建小程序 带完整搭建教程

随着移动互联网的快速发展&#xff0c;小程序已经成为了一个炙手可热的领域。但是&#xff0c;对于很多开发者来说&#xff0c;搭建一个小程序并不容易。为了解决这个问题&#xff0c;罗峰来给大家介绍一款开源的7合一万能DIY小程序源码系统&#xff0c;它可以将7个端整合到一个…

如何利用大模型蒸馏出小模型实现降本

如何让小模型的推理效果在某些领域比 ChatGPT 这样的大模型还要更强&#xff1f;这篇论文提供了一个思路&#xff1a;https://arxiv.org/abs/2212.10071&#xff0c;借助思维链&#xff08;CoT&#xff09;逐步解决复杂推理任务的能力&#xff0c;可以使用大模型作为推理教师&a…

虚拟专用网络

目录 数据安全三要素 加密技术 对称加密 非对称加密 VPN介绍 VPN类型 VPN隧道技术 案例使用 传输模式 隧道模式 IPsecVPN IPsecVPN两阶段 管理连接 命令 数据连接 命令 将IPsecVPN两阶段应用在外网端口 创建map映射表 将map表应用到外网端口 VPN连接查看命…

JOSEF约瑟 反时限过流继电器JGL-115板前接线5A速断保护

系列型号 JGL-111反时限过流继电器&#xff1b;JGL-112反时限过流继电器&#xff1b; JGL-113反时限过流继电器&#xff1b;JGL-114反时限过流继电器&#xff1b; JGL-115反时限过流继电器&#xff1b;JGL-116反时限过流继电器&#xff1b; JGL-117反时限过流继电器&#xff1b…

Docker学习——⑦

文章目录 1、Docker 为什么需要网络管理2、Docker 网络架构简介2.1 CNM2.2 Libnetwork2.3 驱动 3、常见网络类型4、docker 网络管理命令5、网络详解5.2 docker Bridge 网络5.2 docker Host 网络5.3 docker Container 网络5.4 docker none 网络 1、Docker 为什么需要网络管理 容…

制造企业使用设备健康管理平台的好处

智能科技的发展不仅改变了我们的日常生活&#xff0c;也给工业制造领域带来了巨大的变化。在制造业生产线上&#xff0c;每天都在使用各种不同的机器设备来生产我们日常使用的物品。然而&#xff0c;这些设备的维护、维修和状态监测成为了制造企业的一大挑战。随着科技的发展&a…

Asset Bundles Browser

unity Asset Bundles Browser 删除图中的Test后使用&#xff1a; 下载链接https://download.csdn.net/download/qq_38513810/87554838?spm1001.2014.3001.5503

【IDE】【实战系列】掌握这些技巧发现阅读源码不过如此简单

文章目录 IDE 版本前言IDE Debug主界面介绍字段断点&#xff08;field breakpoints&#xff09;使用方式配置EnabledSuspendLog 行断点&#xff08;line breakpoints&#xff09;使用方式配置方式 方法断点&#xff08;method breakpoints&#xff09;使用方式配置方式 异常断点…

Linux编译器---gcc/g++的使用

一、背景知识 在Linux系统中&#xff0c;GCC是一个非常重要的工具&#xff0c;因为它使得开发者可以使用C和C等语言编写高性能的程序。它主要包括四个方面&#xff1a; 预处理&#xff08;进行宏替换) 编译&#xff08;生成汇编) 汇编&#xff08;生成机器可识别代码&#xff0…

ECharts:显示暂无数据

ECharts 是一个使用 JavaScript 实现的开源可视化库&#xff0c;涵盖各行业图表&#xff0c;满足各种需求&#xff0c;实现各种炫酷的统计图表效果。 如上图所示&#xff0c;有数据的时候固然好看&#xff0c;但是当它没有数据的时候&#xff0c;就是光秃秃的一片&#xff0c;所…

Peter算法小课堂—自定义容器

太戈编程第308题 暴力法 cin>>n>>m; for(int i0;i<n;i) cin>>name[i]>>year[i]; for(int i0;i<m;i){int x;string y;cin>>x>>y;int OK0;for(int j0;j<n;j){if(name[j]y&&year[j]x){OK1;break;}}cout<<OK<<…

彭州市民政局“四个聚焦” 推动未成年人保护工作

聚焦机制完善。以“六大保护”为主导&#xff0c;聚焦“27&#xff08;市级部门&#xff09;13&#xff08;镇、街道&#xff09;”整体联动&#xff0c;定期开展信息交流会、跨部门协同工作培训会等活动&#xff0c;不断健全协调机制、完善协同体系&#xff0c;进一步提升全市…

springboot vue mysql的在线竞拍拍卖系统

基于SpringBoot的在线拍卖系统,springboot vue mysql (毕业论文10168字以上,共34页,程序代码,MySQL数据库) 【运行环境】 IDEA, JDK1.8, Mysql, Node, Vue 【技术栈】 Java, SpringBoot, Jquery, Layui, MYSQL, HTML, CSS, JAVASCRIPT, Ajax 7707 java毕业设计 springboot v…

进博会再现上亿大单 EZZ携手HIC海橙嗨选签署2024年度合作备忘录

正在举行的第六届中国国际进口博览会上&#xff0c;再现上亿大单。11月6日&#xff0c;在澳大利亚新南威尔士州政府代表的见证下&#xff0c;澳交所基因组龙头上市公司EZZ生命科学和中国跨境社交电商龙头HIC海橙嗨选签署2024合作备忘录&#xff0c;在未来的一年&#xff0c;EZZ…

vue 数字软键盘 插件 封装 可拖动

1、效果图 2、使用方式 <Keyboard v-if"show" close"show false" :inputDom"$refs.input" /> 封装的数字键盘 Keyboard.vue 组件代码 <template><divclass"keyboard"ref"keyboard":style"{ left: …

日本it培训班,如何选择靠谱的赴日IT培训班?

随着科技的发展&#xff0c;信息技术行业在全球范围内迅速发展&#xff0c;并呈现出蓬勃的发展态势&#xff0c;在日本&#xff0c;IT行业也成为一种极为热门的职业选择。日本专门学校在这个领域内培养了许多IT从业者&#xff0c;成为了众多IT公司的培养基地。如果你对IT产业感…

【前端异常】JavaScript错误处理:分析 Uncaught(in promise) error

这里写目录标题 一、Promise是什么二、什么是 Uncaught(in promise) error三、解决方案3.1 使用catch方法处理Promise的错误3.2 使用 async/await 处理Promise的错误3.3 全局异常处理 四、结论 在开发过程中&#xff0c;JavaScript的错误处理是一个老生常谈的话题。当应用程序发…

AI机器人软件定制流程

一、项目概述 AI机器人软件定制流程是根据客户的需求&#xff0c;定制开发一款具有人工智能功能的机器人软件。本方案将详细介绍AI机器人软件定制的整个流程&#xff0c;包括需求分析、设计、开发、测试和交付等环节。 二、需求分析 在定制AI机器人软件之前&#xff0c;需要…