一、前言

1.简介:

本文是基于STM32F1,将数据发送至NRF模块的寄存器,并将数据重新读取,通过串口发送出来的简单SPI单通信。

2.SPI简介:

调过STM8的都已经对SPI有所了解,调法都一致,这里就不做详细的讲解。

3.准备工作:

软件层:

Keil5              链接:   点击下载                提取码:wrt9 

STMCubeMX5.1.0版本         链接:   点击下载                提取码:20xs

           

硬件层:

1. STM32F1ZE开发板         (什么型号的都可以,只要能在CUbe配置好型号就行)

2.NRF24L01模块

二、SPI详解

SPI是串行外设接口(Serial Peripheral Interface)的缩写。是 Motorola 公司推出的一 种同步串行接口技术,是一种高速的,全双工,同步的通信总线。因为其没有指定的流控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据可靠性上有一定的缺陷。

 

1.硬件接线:

VCC、GND不用讲。下面的四条线是完成通信的最基本线,作用已简略给出,需要进一步了解的可以详看数据手册。大家可以按照相应线用示波器观察对应波形,看时序是否正确。

CSN:从设备使能信号,由主设备控制。

SCK:时钟信号,由主设备产生。

MISO:主设备数据输入,从设备数据输出。(主要用于读取数据)

MOSI:主设备数据输出,从设备数据输入。(主要用于写入数据)

(在这里提示用示波器看的同学,记得看读取到数据的波形时,切换到MISO线,在MOSI线上是观测不到读取回来的数据 的)

            

2.Cube配置问题:

一开始调试时用的TIM7来配置Cube,但是一直无法正确读到波形,通过看数据手册和部分历程,我将时钟配置成TIM2,就能正常收发了。其他配置跟平常串口配置一样就行了,没有特别需要注意的。

3.寄存器地址和驱动读写问题:

不同于IIC读取时钟模块,这里只需要寄存器的地址,同时要查询到读指令和写指令来驱动设备读与写的功能。

#define CONFIG 0x00           //配置寄存器地址;

//寄存器读写驱动命令:
#define READ_REG_NRF 0x00 //读配置寄存器,低5位为寄存器地址
#define WRITE_REG_NRF 0x20 //写配置寄存器,低5位为寄存器地址

4.本文部分代码:

最后贴出一些主要的代码,这些都是最基础的代码,看着数据手册写就行了。但是这里我在读取与写入寄存器函数中并没有使用延时,但是也能成功输出。

SPI_test.c

void delay2ms()                              //开机延时2MS,实测
{
unsigned char a,b,c;
for(c=;c>;c--)
for(b=;b>;b--)
for(a=;a>;a--);
} //这个函数是将读取/写入一个字节合二为一的基础函数代码。 uint32_t uSPI_RW_Byte(uint32_t uByte)
{
uint32_t uBits;
for(uBits=; uBits<; uBits++) //8次循环
{
if(uByte & 0x80)             
PIN_MOSI_H; //该位为1则置1
else
PIN_MOSI_L; //该位为0则置0
uByte <<= ; //左移一位,可读取1位,并输出下一位
PIN_SCK_H; //拉高时序线,开始发送数据
if(MISO) //判断MISO电平
uByte|=0x01; //若为1则赋值到相应的位上
PIN_SCK_L; //结束该Byte数据的发送
}
return uByte;
}

如果看不懂这个函数,想分开的函数,可以用下面这两个
/*
** 函数名 : SPI_Read_OneByte
** 返回值 : temp--SPI读取的一字节数据
** 参 数 : None
** 描 述 : 下降沿读数据,每次读取 1 bit
*/ uint32_t SPI_Read_OneByte(void)
{
uint32_t i;
uint32_t temp = ; for(i=;i<;i++)
{
temp <<= ;               //读取MISO 8次输入的值,存入temp。
               //读取最后1byte的最后一位(即LSB)之后,不能再左移了
PIN_SCK_H;
if(MISO) //读取最高位,保存至最末尾,通过左移位完成读整个字节
temp |= 0x01;
else
temp &= ~0x01;
PIN_SCK_L; //下降沿来了(SCK从1-->0),MISO上的数据将发生改变,稳定后读取存入temp
} return temp;
} /*
** 函数名 : SPI_Write_OneByte
** 返回值 : None
** 参 数 : u8_writedata--SPI写入的一字节数据
** 描 述 : 上升沿写数据,每次写入 1 bit
*/ void SPI_Write_OneByte(uint32_t u8_writedata)
{
uint32_t i;
for(i=;i<;i++)
{
if(u8_writedata & 0x80) //判断最高位,总是发送最高位
PIN_MOSI_H;         //MOSI输出1,数据总线准备数据1
else
PIN_MOSI_L;         //MOSI输出0,数据总线准备数据0
PIN_SCK_H;         //上升沿来了(SCK从0-->1),数据总线上的数据写入到器件
u8_writedata <<= ;         //左移抛弃已经输出的最高位
PIN_SCK_L;          //拉低SCK信号,初始化为0
}
} /*
** 函数名: nRF24L01_WriteReg
** 返回值: None
** 参 数 : (1)uint8 addr--寄存器地址
** (2)uint8 value--写入值
** 说 明 : nRF24L01寄存器写函数
*/ void nRF24L01_WriteReg(uint32_t addr, uint32_t value)
{ PIN_CSN_L;   //CS片选拉低
uSPI_RW_Byte(WRITE_REG_NRF+addr);
uSPI_RW_Byte(value);
PIN_CSN_H;   //CS片选拉高 } /*
** 函数名: nRF24L01_ReadReg
** 返回值: value--读取寄存器值
** 参 数 : addr--寄存器地址
** 说 明 : nRF24L01寄存器读函数
*/ uint32_t nRF24L01_ReadReg(uint8_t addr)
{
uint8_t value;
PIN_CSN_L; //CS片选拉低
uSPI_RW_Byte(READ_REG_NRF+addr); //SPI读数据
value=uSPI_RW_Byte();
PIN_CSN_H; //CS片选拉高
return value;
}

以下贴出的是SPI_test.h的部分代码:是对上面的一些定义函数的补充

SPI_test.h

#define PIN_CSN_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET)
#define PIN_CSN_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET) #define PIN_MISO_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET)
#define PIN_MISO_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET) #define PIN_SCK_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET)
#define PIN_SCK_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET) #define PIN_MOSI_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET)
#define PIN_MOSI_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET) #define MISO HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) //判断0或1

最后贴出主函数的调用函数,这里是用调用HAL库函数的串口直接发送的方法:如图

5.输出结果验证:

验证是否读取到数值,可以直接在示波器看波形,也可以采用直接在串口调试器看。但是个人建议先观察示波器波形,比较容易找到问题所在。

三、总结:

相对于STM8,在STM32上来调试时,最主要是要注意时钟配置问题,其他都比较方便,也没有特别需要注意的问题。有不对的地方可以留言指出或提问。

基于STM32F1与NRF24L01模块的SPI简单通信的更多相关文章

  1. 基于AOP和ThreadLocal实现的一个简单Http API日志记录模块

    Log4a 基于AOP和ThreadLocal实现的一个简单Http API日志记录模块 github地址 : https://github.com/EalenXie/log4a 在API每次被请求时 ...

  2. 基于ARM处理器的反汇编器软件简单设计及实现

    写在前面 2012年写的毕业设计,仅供参考 反汇编的目的 缺乏某些必要的说明资料的情况下, 想获得某些软件系统的源代码.设计思想及理念, 以便复制, 改造.移植和发展: 从源码上对软件的可靠性和安全性 ...

  3. python模块介绍- HTMLParser 简单的HTML和XHTML解析器

    python模块介绍- HTMLParser 简单的HTML和XHTML解析器 2013-09-11 磁针石 #承接软件自动化实施与培训等gtalk:ouyangchongwu#gmail.comqq ...

  4. 多模块分布式系统的简单服务访问 - OSGI原形(.NET)

    多模块分布式系统的简单服务访问 - OSGI原形(.NET) 先描述一下本篇描述的适用场景(3台server, 各个模块分布在各个Server上,分布式模块互相依赖.交互的场景): 多个OSIG引擎交 ...

  5. 基于TCP 协议的socket 简单通信

    DNS 服务器:域名解析 socket 套接字 : ​ socket 是处于应用层与传输层之间的抽象层,也是一组操作起来非常简单的接口(接受数据),此接口接受数据之后,交由操作系统 为什么存在 soc ...

  6. Python网络编程02 /基于TCP、UDP协议的socket简单的通信、字符串转bytes类型

    Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes类型 目录 Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes ...

  7. SPI简单解析

    什么是SPI  一种服务加载方式,全名为Service Provider Interface,Service提供者接口 如果我们要抽象里面的模块,在面对对象编程当中,我们模块之间,一般推荐模块之间基于 ...

  8. 基于NCF的多模块协同实例

    简介 这次给大家带来的内容是基于NCF的多模块协同实例 主要讲解的内容是NCF的模块Xncf之间相互调用,相互协作的能力 这里可以把Xncf比作乐高玩具,一个Xncf就是你拥有的乐高玩具的类型,比如你 ...

  9. SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

    SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...

随机推荐

  1. 纯JS实现在一个字符串b中查找另一个字符串a出现的所有位置,并且不使用字符串的方法(递归)

    问题:判断字符串A在中所有出现字符串B中(长度大于1)的索引.不得使用字符串方法indexof,substring等 有小伙伴在面试遇到了这个问题,乍一看如果使用使用字符串方法indexof,subs ...

  2. 持续集成高级篇之Jenkins Pipeline 集成sonarqube

    系列目录 前面章节中我们讲到了Sonarqube的使用,其实Sonarqube获取msbuild结果主要是执行三个命令,开始标记,执行msbuild,结束标记,这些都是命令,是非常容易集成到我们ci流 ...

  3. elk安装和使用

    elk安装和使用 使用的版本都是5.2.0 elasticsearch-5.2.0安装 在官网 下载 elasticsearch tar包 解压安装 tar zxf elasticsearch-5.2 ...

  4. Net基础篇_学习笔记_第十一天_面向对象(类)

    类语法:[public] class 类名{ 字段; 属性; 方法;}写好了一个类之后,我们需要创建这个类的对象,那么,我们管创建这个类的对象过程称之为类的实例化.使用关键字 new. this:表示 ...

  5. 24 (OC)* 加密

    一 .MD5加密 MD5加密是最常用的加密方法之一,是从一段字符串中通过相应特征生成一段32位的数字字母混合码. MD5主要特点是 不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样(也不是 ...

  6. 章节十六、10-TestNG报告和日志

    一.在进行自动化的过程中,日志一般采用log4j 2进行日志记录,但TestNG自己本身也带有日志记录功能(reporter),它的好处在于日志中记录的内容都是testng自动生成的. package ...

  7. 指针生产网络(Pointer-Generator-Network)原理与实战

    0 前言 本文内容主要:介绍Pointer-Generator-Network在文本摘要任务中的背景,模型架构与原理.在中英文数据集上实战效果与评估,最后得出结论.参考的<Get To The ...

  8. LeetCode 430. Faltten a Multilevel Doubly Linked List

    题目链接:LeetCode 430. Faltten a Multilevel Doubly Linked List class Node { public: int val = NULL; Node ...

  9. 夯实Java基础系列6:一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别!

    目录 抽象类介绍 为什么要用抽象类 一个抽象类小故事 一个抽象类小游戏 接口介绍 接口与类相似点: 接口与类的区别: 接口特性 抽象类和接口的区别 接口的使用: 接口最佳实践:设计模式中的工厂模式 接 ...

  10. ubuntu linux重置密码

    (和网上的有点不一样,记录一下) 1)重启系统,同时长时间按住shift键进入grub菜单:GNU GRUB version 1.99-12ubuntu5(如图一) 2)选择Ubuntu, with ...