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

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

后续文章会同时发表在个人博客(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 headervoid 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.hqwc.cn/news/174753.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】MySQL中的锁

全局锁 全局锁是对整个数据库实例加锁&#xff0c;整个库处于只读状态。 flush tables with read lock 适用场景 全局锁适用于做全库逻辑备份&#xff0c;但是整个库处于只读状态&#xff0c;在备份期间&#xff0c;所有的更新操作、DDL将会被阻塞&#xff0c;会对业务产生影…

SAP ABAP 主动调用外部系统的REST接口(x-www-form-urlencoded)

如何在SAP ECC中调用外部系统提供的REST接口地址&#xff1f; Postman中使用Body中参数情况&#xff0c;使用链接的情况 x-www-form-urlencoded POST成功调用样例如下&#xff1a; SAP中实现如下&#xff1a; 1. 事务码STRUST,导入对方系统证书 2. 事务码SM59配置destinati…

代码随想录Day45 动态规划13 LeetCode T1143最长公共子序列 T1135 不相交的线 T53最大子数组和

LeetCode T1143 最长公共子序列 题目链接:1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 题目思路: 动规五部曲分析 1.确定dp数组的含义 这里dp数组的含义是结尾分别为i-1,j-1的text1和text2的最长公共子序列长度 至于为什么是i-1,j-1我之前已经说过了,这里再…

房产中介租房小程序系统开发搭建:详细指南教你如何构建

随着微信小程序的日益普及&#xff0c;越来越多的企业和个人开始尝试开发自己的小程序。以下是制作一个房地产微信小程序的详细教程&#xff0c;希望对大家有所帮助。 一、注册登录乔拓云平台&#xff0c;进入后台 首先&#xff0c;需要注册并登录乔拓云平台&#xff0c;该平台…

【极客时间-系列教程】Vim 实用技巧必知必会-更多常用命令:应对稍复杂的编辑任务

文章目录 更多常用命令&#xff1a;应对稍复杂的编辑任务光标移动文本修改文本对象选择 更多常用命令&#xff1a;应对稍复杂的编辑任务 几个基本的命令已经了解了&#xff0c;可以操作简单的任务&#xff0c;但一些很复杂的命令&#xff0c;并没有了解到&#xff0c;只知道几…

文件上传 [ACTF2020 新生赛]Upload1

打开题目&#xff0c;发现是一道文件上传题目 随便上传个一句话木马上去 发现网站前端有白名单限制&#xff0c;只能上传含有jpg&#xff0c;png&#xff0c;gif的后缀文件 那我们便传个2.jpg的一句话木马上去&#xff0c;bp抓包 我们改成php文件后缀试试&#xff0c;发现重发…

基于入侵杂草算法优化概率神经网络PNN的分类预测 - 附代码

基于入侵杂草算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于入侵杂草算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于入侵杂草优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

将铜互连扩展到2nm的研究

晶体管尺寸在3nm时达到临界点&#xff0c;纳米片FET可能会取代finFET来满足性能、功耗、面积和成本目标。同样&#xff0c;正在评估2nm铜互连的重大架构变化&#xff0c;此举将重新配置向晶体管传输电力的方式。 芯片制造商也可能会在2nm节点开始用钌或钼在一定程度上取代铜。…

【系统安装】ubuntu20.04安装,正经教程,小白安装教程,百分百成功安装

1、安装的前提是有启动盘&#xff0c;这个比较好处理&#xff0c;清华源找到ubuntu20.04.iso镜像文件下载&#xff0c;然后用Rufus来制作启动盘就可以了&#xff0c;需要注意的是目标文件系统需要是UEFI&#xff0c;其他的话就没太多要求了&#xff0c;如果卡在这一步的话&…

Java学习之路 —— 异常、集合

文章目录 1. 异常2. 集合2.1 遍历2.1.1 迭代器2.1.2 增强for循环2.1.3 Lambda 2.2 List2.3 Set2.3.1 HashSet2.3.2 LinkedHashSet2.3.3 TreeSet 2.4 Map 1. 异常 Exception&#xff1a;叫异常&#xff0c;是程序员可以捕捉的。异常又分为了2类&#xff1a; 运行时异常&#x…

Web安全:Vulfocus 靶场搭建.(漏洞集成平台)

Web安全&#xff1a;Vulfocus 靶场搭建.&#xff08;漏洞集成平台&#xff09; Vulfocus 是一个包含了多种漏洞靶场的镜像。每个靶场都有具体的漏洞环境和攻击点。Vulfocus 的靶场包括了 Web 安全漏洞、系统安全漏洞、网络安全漏洞、密码学漏洞等多种类型。通关这个靶场我们可以…

IP-guard flexpaper远程命令执行漏洞复现 [附POC]

文章目录 IP-guard flexpaper RCE漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 IP-guard flexpaper RCE漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测…