一, 前言
1. 简介:
本文是基于 STM32F1, 将数据发送至 NRF 模块的寄存器, 并将数据重新读取, 通过串口发送出来的简单 SPI 单通信.
2.SPI 简介:
调过 STM8 的都已经对 SPI 有所了解, 调法都一致, 这里就不做详细的讲解.
3. 准备工作:
软件层:
Keil5 链接: 点击下载 https://pan.baidu.com/s/1YV64CCGBtrW3uUpI_nXPLQ 提取码: wrt9
STMCubeMX5.1.0 版本 链接: 点击下载 https://pan.baidu.com/s/1PWg2yPmhoHwjBjwjIiRY1g 提取码: 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=5;c>0;c--)
- for(b=68;b>0;b--)
- for(a=31;a>0;a--);
- }
- // 这个函数是将读取 / 写入一个字节合二为一的基础函数代码.
- uint32_t uSPI_RW_Byte(uint32_t uByte)
- {
- uint32_t uBits;
- for(uBits=0; uBits<8; uBits++) //8 次循环
- {
- if(uByte & 0x80)
- PIN_MOSI_H; // 该位为 1 则置 1
- else
- PIN_MOSI_L; // 该位为 0 则置 0
- uByte <<= 1; // 左移一位, 可读取 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 = 0;
- for(i=0;i<8;i++)
- {
- temp <<= 1; // 读取 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=0;i<8;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 <<= 1; // 左移抛弃已经输出的最高位
- 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(0);
- 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 上来调试时, 最主要是要注意时钟配置问题, 其他都比较方便, 也没有特别需要注意的问题. 有不对的地方可以留言指出或提问.
posted on 2019-10-18 16:38 莎莎 ting 阅读(...) 评论(...) 编辑 收藏
来源: https://www.cnblogs.com/shasha2019/p/11697698.html