关于使用STM32硬件SPI驱动NRF24L01
- 格式:doc
- 大小:1.26 MB
- 文档页数:18
奋斗版 STM32 开发板例程手册———NRF24L01+转 USB 虚拟串口实验NRF24L01+转 USB 虚拟串口实验实验平台:奋斗版STM32开发板Tiny 实验内容:板子通过USB加电后,先向串口1输出一串测试数据,然后USB被PC识 别出来,虚拟出一个串口号给这个USB设备,此时可以通过在PC端的串口助手类 软件选择该串口号。
进入串口软件界面,可以通过软件无线收发一帧长度最长 为32字节的数据。
该例程可以和V3及MINI板的NRF24L01 UCGUI例程配合使用。
预先需要掌握的知识 2.4G通信模块NRF24L01 1. 产品特性2.4GHz 全球开放ISM 频段,最大0dBm 发射功率,免许可证使用 支持六路通道的数据接收 低工作电压:1.9 1.9~3.6V 低电压工作 高速率:2Mbps,由于空中传输时间很短,极大的降低了无线传输中的碰撞现象(软件设置1Mbps或者2Mbps的空中传输速率) 多频点:125 频点,满足多点通信和跳频通信需要 超小型:内置2.4GHz天线,体积小巧,15x29mm(包括天线) 低功耗:当工作在应答模式通信时,快速的空中传输及启动时间,极大的降低了电流消耗。
低应用成本:NRF24L01 集成了所有与RF协议相关的高速信号处理部分,比如:自动重发丢失数据包和自动产生应答信号等, NRF24L01的SPI接口可以利用单片机的硬件SPI口连接或用单片机I/O口进行模拟,内部有FIFO可以与各种高低速微处理器接口, 便于使用低成本单片机。
便于开发:由于链路层完全集成在模块上,非常便于开发。
自动重发功能,自动检测和重发丢失的数据包,重发时间及重发次数可软件控制 自动存储未收到应答信号的数据包 自动应答功能,在收到有效数据后,模块自动发送应答信号,无须另行编程 载波检测—固定频率检测 内置硬件CRC 检错和点对多点通信地址控制 数据包传输错误计数器及载波检测功能可用于跳频设置 可同时设置六路接收通道地址,可有选择性的打开接收通道 标准插针Dip2.54MM 间距接口,便于嵌入式应用2.基本电气特性淘宝店铺:1奋斗版 STM32 开发板例程手册———NRF24L01+转 USB 虚拟串口实验3. 引脚定义:4.工作方式NRF2401有工作模式有四种: 收发模式 配置模式 空闲模式 关机模式 工作模式由CE 和寄存器内部PWR_UP、PRIM_RX 共同控制,见下表:淘宝店铺:2奋斗版 STM32 开发板例程手册———NRF24L01+转 USB 虚拟串口实验4.1 收发模式收发模式有Enhanced ShockBurstTM收发模式、ShockBurstTM收发模式和直接收发模式三种,收发模式由器件配置字决定,具体 配置将在器件配置部分详细介绍。
基于nRF24L01的SPI远程通讯发射器摘要近年来,随着传感技术、嵌入式计算、现代网络、无线数据传输及分布式信息处理等技术的发展,能够将监测对象的数据通过无线方式发送,使得无线测量和传输技术也越来越受到人们的重视。
同时随着我国PM2.5污染问题日益凸显,以及公众环境意识的增强,对空气质量的关注也越来越多,但对其进行远距离实时监测仍是一个困难。
该设计是通过STM32单片机进行智能控制,通过PM2.5传感器检测空气质量,将检测的数值在OLED屏幕上显示、再通过nRF24L01通信系统将信息远程传输到室内的OLED屏上显示。
关键词: nRF24L01;STM32单片机;PM2.5传感器;1 设计背景及目的伴随着科技的发展及应用,使得无线测量和传输技术越来越受到人们的重视。
常用的无线数据传输技术主要有蓝牙技术、Wi-Fi技术、IrDA技术、超宽带技术等,而蓝牙技术和Wi-Fi技术功耗较高,IrDA技术传输速率较低,超宽带技术开发难度较高。
为了更好的实现无线通信的功能,增加模块的可靠性和使用寿命,本设计一种基于nRF24L01的无线通信系统,该系统具有传输速率高、成本低、功耗小、体积小、便于开发等优点,具有很高的应用价值。
2 基本设计思路本系统由nRF24L01、STM32单片机、PM2.5传感器模块和5V稳压模块组成。
由PM2.5传感器模块作为单片机主要的信号输入源,负责采集空气中PM2.5的含量,STM32单片机负责接收PM2.5传感器输出的检测信号后根据信号的状态不同进行不同处理,再将控制信号输出到nRF24L01,将实时监测到的PM2.5值传送到手机。
5V稳压模块则是在使用非USB供电时,使系统的输入电压保持在5V,是保证工作时系统的稳定性所必须的模块。
系统结构框图如图1所示。
图1 系统结构框图3 硬件设计本设计采用性价比较高的射频芯片nRF24L01为核心搭建无线通信硬件平台。
该平台采用ATmega16L作为微控制器,控制无线发送和接收;通过ATK-ESP8266串口与用户端手机相连,实现无线数据通信。
STM32驱动NRF24L01
1. 简介
NRF24L01 是nordic 的无线通信芯片,它具有以下特点:
1)2.4G 全球开放的ISM 频段(2.400 - 2.4835GHz),免许可证使用;
2)最高工作速率2Mbps,高校的GFSK 调制,抗干扰能力强;
3)125 个可选的频道,满足多点通信和调频通信的需要;
4)内置CRC 检错和点对多点的通信地址控制;
5)低工作电压(1.9~3.6V),待机模式下状态为26uA;掉电模式下为
900nA;
6)可设置自动应答,确保数据可靠传输;
7)工作于EnhancedShockBurst 具有Automatic packet handling,Auto packet transaction handling ,可以实现点对点或是 1 对 6 的无线通信,速度可以达
到2M(bps),具有可选的内置包应答机制,极大的降低丢包率。
8)通过SPI 总线与单片机进行交互,最大通信速率为10Mbps;
1.1 结构框图
如图右侧为六个控制和数据信号,分别为CSN、SCK、MISO、MOSI、IRQ、CE。
信号线功能
CSN 芯片的片选线,CSN 为低电平芯片工作
SCK 芯片控制的时钟线(SPI 时钟)
MISO 芯片控制数据线(Master input slave output)
MOSI 芯片控制数据线(Master output slave input)
IRQ 中断信号。
无线通信过程中MCU 主要是通过IRQ 与NRF24L01 进。
NRF24L01详细教程近来课程的项目需要用到NRF24L01,用来做基本的收发,虽然资料拿到不少,但是,很多资料并不是很清晰、所带的例程并不够简洁或有不少冗余的部分,再加上对应的中文数据手册部分没翻译出来,翻译出来的不够有条理,很多地方模糊,甚至关键的地方看一两次还看不出来,导致了在学NRF24L01时花费了较多时间,所以,学完NRF24L01后,萌生了写个尽量清晰的教程的想法。
教程中的例程虽然是库开发方式,但基本都是最底层的操作才用到库函数譬如发一字节数据、GPIO置位等,虽然用的STM32,但我在看其他板子的例程时,发觉内容与流程都是差不多的,只是不同板引脚不同所导致的引脚配置的不同,不管用什么方式开发,用什么芯片,了解清楚NRF24L01如何配置,了解清楚其收发流程,基本上就会开发了,所以此文档虽然写的是以STM32为例,但看完此文档用NRF24L01基本也没什么大问题了。
教程说明:这教程是基于STMF103ZET6的,是野火的板子,例程也是从野火提供修改例程得来,用的是库开发的方式。
学习NRF24L01的步骤:1.学习SPI,SPI就是NRF24L01传送数据到单片机的一种协议,类似于USB,当然USB还是比较有难度的。
2.了解NRF24L01相关寄存器,结合中文数据手册了解NRF24L01的基本配置,收发数据前后的操作(如何启动发送接收、寄存器清空、标志位重置等)。
3.分析具体代码SPI的简介:具体的SPI教程,大家可以去野火的教程进行学习,在此只是简略介绍一下,SPI是一种一对多协议:一个主机(MCU)对应对多个从机,可以分时与多个从机通讯SPI 总线包含4 条总线,分别为SS、SCK、MOSI、MISO,其含义分别为SS:Slave Select,片选信号线,主机借此信号线选择一个从机,低电平有效。
MOSI:Master Output,Slave Input,主机数据从此线输出到从机,数据方向从主机到从机。
nrf24l01使用与调试经验总结(包括一收多发--1主机最多6从机)----------------------------------------------------------------------------------------------------------------------------主要特性工作在2.4GHz ISM 频段调制方式:GFSK/FSK数据速率:2Mbps/1Mbps/250Kbps超低关断功耗:<0.7uA超低待机功耗:<15uA快速启动时间:<130uS内部集成高PSRR LDO宽电源电压范围:1.9-3.6V数字IO 电压: 3.3V/5V低成本晶振:16MHz±60ppm接收灵敏度:<-83dBm @2MHz最高发射功率:7dBm接收电流(2Mbps):<15mA发射电流(2Mbps):<12mA(0dBm)10MHz 四线SPI 模块内部集成智能ARQ 基带协议引擎收发数据硬件中断输出支持1bit RSSI 输出极少外围器件,降低系统应用成本QFN20 封装或COB 封装注意:C代表了命令,S表示寄存器值,D表示数据写数据:SPI写命令+寄存器地址----->SPI写入数据读数据:SPI写寄存器地址(可以使用读命令+寄存器地址)----->SPI读取数据不论是读取或者写入数据,甚至是读/写len长度的数据都要先写寄存器地址;总的来说时候就三个模式:1.待机模式(待机模式+掉电省电模式)2.发送模式3.接受模式具体各个模式介绍参考数据手册。
----------------------------------------------------------------------------------------------------------------nrf发送数据是以包来发送。
使⽤STM32F030F4P6的SPI协议和NRF24L01模块进⾏通讯实现⽆线数据的收发单⽚机这块纯属个⼈业余爱好, 有很多不⾜的地⽅还请⼤家多多指教, 代码中有些命名不规范的地⽅还请⼤家多多包涵.本⽂只实现⽆线模块的简单的点亮(能收发⼀个字节数据), ⼀直想diy⼀个⽆线遥控的⼩车, 就要使⽤到⽆线模块, 找了好久发现NRF24L01(下⾯简称NRF)是最便宜的⼀款⽆线模块(除过WiFi和蓝⽛模块), 就买了⼏个, 由于stm32f103涨价, 就选择了便宜的stm32f030, ⽹上找了很多资料对于stm32f030的资料很少, 他和stm32f103代码⼤同⼩异, 就试着在stm32f103代码的基础上修改⼀下, 就是不能通讯, 只能发送成功, 不能接收到数据, 搁置了好久最后从新选择了⼀块 HC-12 的⽆线通讯模块, 这个模块⽐较贵⾸次购买⼀套(收发两个模块)⽐较便宜, 空旷视野最远通讯距离1公⾥(没有实测量过), 他使⽤的是串⼝通讯, 写好代码烧录进去后可以通讯, 最后成功diy了⽆线遥控⼩车, 利⽤HC-12感觉⼤材⼩⽤了, 最后闲来⽆事就⼜琢磨⼀下这个NRF模块, 终于可以相互通讯了, 也不知道哪⾥出问题了, 唯⼀不同的是, 之前的是在⽹上找的资料上修修改改, 没有使⽤中断, 只使⽤了while循环进⾏检测, 这次重头开始编写的时候使⽤了中断, 在调试了⼀下就可以通讯.遇到的⼀些问题:1.原理图上PA4 是SPI1的⽚选spi1_nss的复⽤, 配置的时候把PA4也配置成了复⽤模式, 发现不能成功, 需要配置成输出模式解决了问题2.NRF的IRQ脚配置中断的时候需要配置为下降沿触发3.stm32板⼦和NRF模块进⾏连接的时候数据输出和输⼊线不能交叉连接(MCU 的MISO 和 NRF的 MISO 相连, MOSI同理)以下是代码 , 适⽤于stm32f0301. spi配置#ifndef __bsp_spi_h#define __bsp_spi_h#include "stm32f0xx_gpio.h"#define SPIx SPI1 //SPI_1#define SPI1_PORT GPIOA //PA 端⼝#define PORTA_LCK RCC_AHBPeriph_GPIOA //GPIO 时钟#define SPI_LCK RCC_APB2Periph_SPI1//spi 时钟#define SPI1_CSN GPIO_Pin_1 //PA1 NSS#define SPI1_SCK GPIO_Pin_5 //PA5 SCK#define SPI1_MISO GPIO_Pin_6 //PA6 MISO#define SPI1_MOSI GPIO_Pin_7 //PA7 MOSIvoid SPI_Config(void);u8 SPI_SendByte(u8 byte);void Pin_CSN(u8 u);#endifbsp_spi.h#include "bsp_spi.h"#include "stm32f0xx_gpio.h"//初始化void SPI_Config(){GPIO_InitTypeDef GPIO_InitStruct;SPI_InitTypeDef SPI_InitStruct;//端⼝初始化RCC_AHBPeriphClockCmd(PORTA_LCK , ENABLE);//开启GPIO时钟RCC_APB2PeriphClockCmd(SPI_LCK, ENABLE);//开启SPI_1时钟//复⽤模式GPIO_PinAFConfig(SPI1_PORT,GPIO_PinSource5,GPIO_AF_0);//SCKGPIO_PinAFConfig(SPI1_PORT,GPIO_PinSource6,GPIO_AF_0);//MISOGPIO_PinAFConfig(SPI1_PORT,GPIO_PinSource7,GPIO_AF_0);//MOSIGPIO_InitStruct.GPIO_Pin = SPI1_SCK | SPI1_MISO | SPI1_MOSI;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;GPIO_Init(SPI1_PORT, &GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = SPI1_CSN;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_Init(SPI1_PORT , &GPIO_InitStruct);//spi初始化//SPI_I2S_DeInit(SPIx); //将寄存器重设为缺省值//SPI_Cmd(SPIx, DISABLE);//SPI_Direction_2Lines_FullDuplex SPI_Direction_1Line_Rx SPI_Direction_1Line_TxSPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI_Mode_Master 主机 SPI_Mode_Slave 从机SPI_InitStruct.SPI_Mode = SPI_Mode_Master;SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;//SPI_CPOL_Low SPI_CPOL_HighSPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;//SPI_CPHA_1Edge SPI_CPHA_2EdgeSPI_InitStruct.SPI_NSS = SPI_NSS_Soft;SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;//SPI_FirstBit_MSB SPI_FirstBit_LSBSPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStruct.SPI_CRCPolynomial = 7;SPI_Init(SPIx, &SPI_InitStruct);//SPI_I2S_IT_TXE SPI_I2S_IT_RXNE SPI_I2S_IT_ERRSPI_I2S_ITConfig(SPIx, SPI_I2S_IT_TXE | SPI_I2S_IT_RXNE, ENABLE);//中断SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF); //重要,把应答数据位设置为 8 位 SPI_Cmd(SPIx, ENABLE);//使能}//SPI 收发⼀个字节u8 SPI_SendByte(u8 byte){//设置时间溢出u32 SPITimeout = 0xffff;/* 等待发送缓冲区为空,TXE 事件 */while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET){if ((SPITimeout--) == 0) return0;}/* 写⼊数据寄存器,把要写⼊的数据写⼊发送缓冲区 */SPI_SendData8(SPIx, byte);//SPI_I2S_SendData16//设置时间溢出SPITimeout = 0xfffff;/* 等待接收缓冲区⾮空,RXNE 事件 */while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET){if ((SPITimeout--) == 0) return0;}/* 读取数据寄存器,获取接收缓冲区数据 */return SPI_ReceiveData8(SPIx);}//设置⽚选⾼低电平void Pin_CSN(u8 u){if(u==0){SPI1_PORT->BRR = SPI1_CSN;}else{SPI1_PORT->BSRR = SPI1_CSN;}}bsp_spi.c2.nrf配置#ifndef __bsp_nrf0241_h#define __bsp_nrf0241_h#include "stm32f0xx_gpio.h"#define NRF_PORT GPIOA //PA 端⼝#define KEY0 GPIO_Pin_0 //KEY0#define LED0 GPIO_Pin_4 //LED0#define NRF_CE GPIO_Pin_2 //PA2 CE#define NRF_IRQ GPIO_Pin_3 //PA3 IRQ#define NOP 0xFF // 空操作。
#include <reg52.h>#include <intrins.h>typedef unsigned char uchar;typedef unsigned char uint;//****************************************IO端口定义***************************************sbit CSN =P2^0; //SPI 片选使能,低电平使能sbit MOSI =P2^1; //SPI串行输入sbit IRQ =P2^2; //中断.低电平使能sbit MISO =P2^3; //SPI串行输出sbit SCK =P2^4; //SPI时钟sbit CE =P2^5; //芯片使能,高电平使能//***********************************数码管0-9编码******************************************* uchar seg[10]={0xC0,0xCF,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//0~~9段码uchar TxBuf[32]={ /*0x01,0x02,0x03,0x4,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x30,0x31,0x32,*/0x00}; ////************************************按键**********************************************sbit KEY1=P3^6;sbit KEY2=P3^7;//***********************************数码管位选************************************************** sbit led1=P2^1;sbit led0=P2^0;sbit led2=P2^2;sbit led3=P2^3;//*********************************************NRF24L01*************************************#define TX_ADR_WIDTH 5 // 5 uints TX address width#define RX_ADR_WIDTH 5 // 5 uints RX address width#define TX_PLOAD_WIDTH 32 // 20 uints TX payload#define RX_PLOAD_WIDTH 32 // 20 uints TX payloaduint const TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //本地地址uint const RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //接收地址//***************************************NRF24L01寄存器指令*******************************************************#define READ_REG 0x00 // 读寄存器指令#define WRITE_REG 0x20 // 写寄存器指令#define RD_RX_PLOAD 0x61 // 读取接收数据指令#define WR_TX_PLOAD 0xA0 // 写待发数据指令#define FLUSH_TX 0xE1 // 冲洗发送FIFO指令#define FLUSH_RX 0xE2 // 冲洗接收FIFO指令#define REUSE_TX_PL 0xE3 // 定义重复装载数据指令#define NOP 0xFF // 保留//*************************************SPI(nRF24L01)寄存器地址****************************************************#define CONFIG 0x00 // 配置收发状态,CRC校验模式以及收发状态响应方式#define EN_AA 0x01 // 自动应答功能设置#define EN_RXADDR 0x02 // 可用信道设置#define SETUP_AW 0x03 // 收发地址宽度设置#define SETUP_RETR 0x04 // 自动重发功能设置#define RF_CH 0x05 // 工作频率设置#define RF_SETUP 0x06 // 发射速率、功耗功能设置#define STATUS 0x07 // 状态寄存器#define OBSERVE_TX 0x08 // 发送监测功能#define CD 0x09 // 地址检测#define RX_ADDR_P0 0x0A // 频道0接收数据地址#define RX_ADDR_P1 0x0B // 频道1接收数据地址#define RX_ADDR_P2 0x0C // 频道2接收数据地址#define RX_ADDR_P3 0x0D // 频道3接收数据地址#define RX_ADDR_P4 0x0E // 频道4接收数据地址#define RX_ADDR_P5 0x0F // 频道5接收数据地址#define TX_ADDR 0x10 // 发送地址寄存器#define RX_PW_P0 0x11 // 接收频道0接收数据长度#define RX_PW_P1 0x12 // 接收频道0接收数据长度#define RX_PW_P2 0x13 // 接收频道0接收数据长度#define RX_PW_P3 0x14 // 接收频道0接收数据长度#define RX_PW_P4 0x15 // 接收频道0接收数据长度#define RX_PW_P5 0x16 // 接收频道0接收数据长度#define FIFO_STATUS 0x17 // FIFO栈入栈出状态寄存器设置//**************************************************************************************void Delay(unsigned int s);void inerDelay_us(unsigned char n);void init_NRF24L01(void);uint SPI_RW(uint uchar);uchar SPI_Read(uchar reg);void SetRX_Mode(void);uint SPI_RW_Reg(uchar reg, uchar value);uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars);uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars);unsigned char nRF24L01_RxPacket(unsigned char* rx_buf);void nRF24L01_TxPacket(unsigned char * tx_buf);//*****************************************长延时*****************************************void Delay(unsigned int s){unsigned int i;for(i=0; i<s; i++);for(i=0; i<s; i++);}//******************************************************************************************uint bdata sta; //状态标志sbit RX_DR =sta^6;sbit TX_DS =sta^5;sbit MAX_RT =sta^4;/******************************************************************************************/*延时函数/******************************************************************************************/void inerDelay_us(unsigned char n){for(;n>0;n--)_nop_();}//****************************************************************************************/*NRF24L01初始化//***************************************************************************************/void init_NRF24L01(void){inerDelay_us(100);CE=0; // chip enableCSN=1; // Spi disableSCK=0; // Spi clock line init highSPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); // 写本地地址SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); // 写接收端地址SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // 频道0自动ACK应答允许SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // 允许接收地址只有频道0,如果需要多频道可以参考Page21SPI_RW_Reg(WRITE_REG + RF_CH, 0); // 设置信道工作为2.4GHZ,收发必须一致SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度,本次设置为32字节SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); //设置发射速率为1MHZ,发射功率为最大值0dBSPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // IRQ收发完成中断响应,16位CRC,主发送}/**************************************************************************************************** /*函数:uint SPI_RW(uint uchar)/*功能:NRF24L01的SPI写时序/****************************************************************************************************/ uint SPI_RW(uint uchar){uint bit_ctr;for(bit_ctr=0;bit_ctr<8;bit_ctr++) // output 8-bit{MOSI = (uchar & 0x80); // output 'uchar', MSB to MOSIuchar = (uchar << 1); // shift next bit into MSB..SCK = 1; // Set SCK high..uchar |= MISO; // capture current MISO bitSCK = 0; // ..then set SCK low again}return(uchar); // return read uchar}/**************************************************************************************************** /*函数:uchar SPI_Read(uchar reg)/*功能:NRF24L01的SPI时序/****************************************************************************************************/ uchar SPI_Read(uchar reg){uchar reg_val;CSN = 0; // CSN low, initialize SPI communication...SPI_RW(reg); // Select register to read from..reg_val = SPI_RW(0); // ..then read registervalueCSN = 1; // CSN high, terminate SPI communicationreturn(reg_val); // return register value}/****************************************************************************************************/ /*功能:NRF24L01读写寄存器函数/****************************************************************************************************/ uint SPI_RW_Reg(uchar reg, uchar value){uint status;CSN = 0; // CSN low, init SPI transactionstatus = SPI_RW(reg); // select registerSPI_RW(value); // ..and write value to it..CSN = 1; // CSN high againreturn(status); // return nRF24L01 status uchar}/****************************************************************************************************/ /*函数:uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars)/*功能: 用于读数据,reg:为寄存器地址,pBuf:为待读出数据地址,uchars:读出数据的个数/****************************************************************************************************/ uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars){uint status,uchar_ctr;CSN = 0; // Set CSN low, init SPI tranactionstatus = SPI_RW(reg); // Select register to write to and read status ucharfor(uchar_ctr=0;uchar_ctr<uchars;uchar_ctr++)pBuf[uchar_ctr] = SPI_RW(0); //CSN = 1;return(status); // return nRF24L01 status uchar}/**************************************************************************************************** *****/*函数:uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)/*功能: 用于写数据:为寄存器地址,pBuf:为待写入数据地址,uchars:写入数据的个数/**************************************************************************************************** *****/uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars){uint status,uchar_ctr;CSN = 0; //SPI使能status = SPI_RW(reg);for(uchar_ctr=0; uchar_ctr<uchars; uchar_ctr++) //SPI_RW(*pBuf++);CSN = 1; //关闭SPIreturn(status); //}/****************************************************************************************************/ /*函数:void SetRX_Mode(void)/*功能:数据接收配置/****************************************************************************************************/ void SetRX_Mode(void){CE=0;SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f); // IRQ收发完成中断响应,16位CRC ,主接收CE = 1;inerDelay_us(130);}/**************************************************************************************************** **//*函数:unsigned char nRF24L01_RxPacket(unsigned char* rx_buf)/*功能:数据读取后放如rx_buf接收缓冲区中/**************************************************************************************************** **/unsigned char nRF24L01_RxPacket(unsigned char* rx_buf){unsigned char revale=0;sta=SPI_Read(STATUS); // 读取状态寄存其来判断数据接收状况if(RX_DR) // 判断是否接收到数据{CE = 0; //SPI使能SPI_Read_Buf(RD_RX_PLOAD,rx_buf,TX_PLOAD_WIDTH);// read receive payload from RX_FIFO bufferrevale =1; //读取数据完成标志}SPI_RW_Reg(WRITE_REG+STATUS,sta); //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标志return revale;}/**************************************************************************************************** *******/*函数:void nRF24L01_TxPacket(unsigned char * tx_buf)/*功能:发送tx_buf中数据/**************************************************************************************************** ******/void nRF24L01_TxPacket(unsigned char * tx_buf){CE=0; //StandBy I模式SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // 装载接收端地址SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH); // 装载数据// SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // IRQ收发完成中断响应,16位CRC,主发送CE=1; //置高CE,激发数据发送inerDelay_us(10);}/**************************************************************************************************** *******/*函数:init_uart(void)/*功能:初始化串口;波特率4800bps/**************************************************************************************************** ******/void init_uart(void){SCON = 0x50;TMOD = 0x20;TH1 = 0xFA;TL1 = 0xFA;PCON = 0x00;TR1 = 1;}//************************************通过串口将接收到数据发送给PC端**************************************void R_S_Byte(uchar R_Byte){SBUF = R_Byte;while( TI == 0 ); //查询法TI = 0;}//************************************工作指示灯************************************** void power_on(void){P0 = 0xfd;Delay(6000);P0 = 0xff;Delay(6000);}//************************************主函数************************************************************void main(void){uchar i;uchar temp =0;init_uart();init_NRF24L01();nRF24L01_TxPacket(TxBuf); // Transmit Tx buffer dataDelay(6000);//CE = 1;while(1){power_on();nRF24L01_TxPacket(TxBuf);SPI_RW_Reg(WRITE_REG+STATUS,0XFF);Delay(100);//Delay(6000);TxBuf[31] = TxBuf[31] + 1;}}。
STM32F0无法使NRF2401工作,求达人指点迷津!(amoBBS阿莫电子论坛)首先,说一下情况:参考STM32F0开发板的例程中的推挽输出(参照GPIO),上拉输入(参照EXTI),复用输出(参照UART),然后照着正点原子的ALIENTEK MINISTM32 实验无线通信实验完成了连线以及配置,但是,无法使这两个板子通讯,经过检测,发现F0的板子的NRF24L01_Check()一直无法过去,while在那里,现在传上代码,请达人们给予指点,谢谢了程序很简单,就是初始化24L01及检测它的存在主函数部分int main(void){NRF24L01_Init(); //初始化NRF24L01while(NRF24L01_Check());//检测24L01,一直死在这里,很不解while (1){}}24L01函数部分const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址//初始化24L01的IO口void NRF24L01_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA|RCC_AHBPeriph_GPIOC, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ; //推挽输出GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ; //推挽输出GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOC, &GPIO_InitStructure);GPIO_SetBits(GPIOC,GPIO_Pin_4);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN ; //上拉输入GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOC, &GPIO_InitStructure);SPIx_Init(); //初始化SPIClr_NRF24L01_CE; //使能24L01 NRF24L01_CESet_NRF24L01_CSN; //SPI片选取消NRF24L01_CSN}void SPIx_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE );GPIO_DeInit(GPIOA);/* Connect pin to Periph */GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_0);GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_0);GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_0);/* Configure pins as AF pushpull */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 ;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA, &GPIO_InitStructure);SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPISPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //选择了串行时钟的稳态:时钟悬空低电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第一个时钟沿SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器SPI_Cmd(SPI1, ENABLE); //使能SPI外设SPIx_ReadWriteByte(0xff);//启动传输}//检测24L01是否存在//返回值:0,成功;1,失败u8 NRF24L01_Check(void){u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};u8 i;SPIx_SetSpeed(SPI_BaudRatePrescaler_8); //spi速度为9Mhz (24L01的最大SPI时钟为10Mhz)NRF24L01_Write_Buf(NRF24L01_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址.NRF24L01_Read_Buf(TX_ADDR,buf,5); //读出写入的地址for(i=0;i<5;i++)if(buf[i]!=0XA5)break;if(i!=5)return 1;//检测24L01错误return 0; //检测到24L01}//SPIx 读写一个字节//TxData:要写入的字节//返回值:读取到的字节u8 SPIx_ReadWriteByte(u8 TxData){u8 retry=0;while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位{retry++;if(retry>200)return 0;}SPI_I2S_SendData16(SPI1, TxData); //通过外设SPIx发送一个数据retry=0;while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //检查指定的SPI标志位设置与否:接受缓存非空标志位{retry++;if(retry>200)return 0;}return SPI_I2S_ReceiveData16(SPI1); //返回通过SPIx最近接收的数据}红色部分和原子的不一样,因为F0的SPI部分有void SPI_SendData8(SPI_TypeDef* SPIx, uint8_t Data)uint8_t SPI_ReceiveData8(SPI_TypeDef* SPIx)。
关于使用STM32硬件SPI驱动NRF24L01+ 今天是大年初一总算有时间做点想做很久的事了,说到NRF2401可能很多电子爱好者都有用过或是软件模拟驱动又或是用单片机自带的硬件SPI来驱动,但不管是用哪种方法来驱动我想都在调试方面耗费了不少的时间(可能那些所谓的电子工程师不会出现这种情况吧!)网上的资料确实很多,但大多数都并没有经过发贴人认真测试过,有的只是理论上可以行的通但上机测试只能说是拿回来给他修改。
本文作者也是经过无助的多少天才算是调试成功了(基于STM32硬件SPI,软件模拟的以前用51单片机已经调通了今天就不准备再拿来讲了,当然如果以后有朋友有需要也可以告诉我,我也可以重新写一篇关于51的驱动的只要有时间是没有问题的。
)因为我用的是STM32F103C8T6的系统而且是刚接触不知道别的系统和我用的这个系统有多大的差别所以我不会整个代码全贴上来就完事了,我将就着重思路配合代码写出来,这样对于刚接触单片机的朋友会有很好的作用,但是还有一点请大家要原谅,可能会存在一些说的不好的地方,毕竟我没有经过正规渠道系统地学习过电子知识,对于前辈来说存在这样那样的问题不可避免的,在此也希望大家指教!贴个图先:NRF2401+的资料大家上网查一下,我输字的速度有点不好说!下面我来说一下整个调试流程吧1.先把STM32串口一调通(因为我不知道STM32 I/O口不知可不可以像51那样并口输出数据,如果可以那就更方便啰)。
2.与NRF2401建立起通信(这个才是问题的关键);3.利用读NRF2401的一个状态寄存器(STATUS)的值并通过串口发送到PC后通过51下载软件的串口助手显示出来(如果你用液晶来调试那你太有才了,你液晶和NRF2401存在牵连可能就会给寻找不成功的原因造成困难,而且还有不少硬件工作要做)在这说一下本文只调试发送程序,致于接收只改一个程序参数就行了。
我们先来调试STM32F103C8T6的串口1吧(也就是USART1)!它是STM32F103C8T6的片上外设之一,使用它时相对来说简单了不少。
首先我要说一下我们要使用STM32的片上外设那么我们必须先对其进行初始化,实际上就是经过这段初始化代码让外设根据我们的要求来工作:void USART1_AllInit(void)//意思是USART1的所有初始化工作,我的英文不好所以可能涵数名可能也不怎么规范{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟,它是在APB2这条总线上的RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA时钟,它也是在APB2这条总线上的,因为USART1要用到GPIOA的端口所以也要初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA,&GPIO_InitStructure);//到此USART1的IO已经初始化了GPIO_InitStructure要在使用前先通过GPIO_InitTypeDef声明一下。
USART_ART_WordLength=USART_WordLength_8b;USART_ART_StopBits=USART_StopBits_1;USART_ART_Parity=USART_Parity_No;USART_ART_HardwareFlowControl=USART_HardwareFlowControl_None;USART_ART_Mode=USART_Mode_Tx;USART_Init(USART1,&USART_InitStructure);USART_Cmd(USART1,ENABLE);//到此USART1已经初使化完成并使能所以以后就可以用了USART_ART_BaudRate=1200;//从这开始初始化串口的工作波特率1200,每帧数据8位长度,停此位1位,不进行较验,硬件流禁止,工作在发送模式,因为要用到USART_InitStructure所以在这之前因该找个合适的地方先使用串口初始化结构体声明一下,说一下我是在主涵数之前声明的。
}那么我们来发送一个数字到串口然后通过串口助手看下收到的是什么看看串口按要求工作没有;#include<stm32f10x.h>GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;//把上面刚写的串口初始化涵数也加到这来!Int main(void){USART1_AllInit();While(1){USART_SendData(USART1,0XAA);}}然后打开串口助手设置波特率为1200,不较验,1位停止位。
收到数据是0XAA吧!简单吧哈哈!我没有网络不能用QQ剪切屏幕啊要不把效果也贴止来就好了。
好了串口就这样被我们调通了兴奋不?不单可以用在我们今天的调试上哦,在主涵数头你想要它在什么时侯发送什么那就看你高兴怎么用了。
好了下面我们继续调试SPI吧,我用的是SPI2,SPI1认不到是我那息不小心整坏啰不的还有5转3.3V 的LM1137不过已经更换好了,还让我调了三四天时间悲哀不?所以没有一个师傅什么都写什么都伤人呀!还好后来用试波器看了下I/O才发现了原因!也是一样先初始化一下SPI2:void SPI2_AllInit()//SPI2所有初始化乱定义的涵数名不管只要可以用就行你甚至可以用脏话只要你素质达的到就行!{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//SPI2默认I/O在GPIOB上所以RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//这个都不用说了吧RCC_PCLK2Config(RCC_HCLK_Div2);SPI_Cmd(SPI2, DISABLE); //这里还是说一下为什么要先失能SPI2,原因是你不关它在工作就不听你的话了,说的好别扭哦,就像电机在高速运转时你不可能不关电源就云调整它的皮带盘吧,那样以我能想到的结果可能你会被搞出血吧!嘻嘻!GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;//这里的GPIO_InitStructure就用上面调USART时的那个声明就好了因为它的值可以变,是个结构变量,包括下面的。
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_12;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOB, &GPIO_InitStructure);//到此SPI2要用到的I/O已初始化完了;速率50MHZ,作为输出的用的是复用推换输出(PIN13-15),输入的I/O用的是GPIO_Mode_IN_FLOATING(浮空输入)。
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//从这点开始开初初始化SPI,SPI_InitStructure要在之前声名。
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_Init(SPI2, &SPI_InitStructure);SPI_NSSInternalSoftwareConfig(SPI2,SPI_NSSInternalSoft_Set);//到此SPI2的初始化进程完成,主机模式,每帧8位,闲时为低电平,第一个上升沿就采样,片选用软件,波特率预分频256调试时尽量让频率低点只要你赖烦,数据高位先发,SPI_Cmd(SPI2, ENABLE);//然后打开SPI2开始工作了哦}到底工作没有我说了不能算(调个SPI1都调3、4天的人),让事实说话,我们先用SPI读数据的涵数来读个我们可以控制的数来看对不;uchar SPI_RW(uchar value)//这个涵数是写数据的但是同时可以读回一个数{while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET);SPI_I2S_SendData(SPI2,value);while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==RESET);return SPI_I2S_ReceiveData(SPI2);//这个就是读涵数了是库里面的所以你工程要包涵这些用到的库哟}也单独调一下;Int main(void){SPI2_AllInit();//刚才写的初始化涵数加到这用一下While(1){USART_SendData(USART1,SPI_RW(0XFF));//这参数0XFF可以是任何8位数据主要是让SPI_I2S_SendData(SPI2,value);这涵数可以成立也好让SPI产生时钟方便SPI读入数据,SPI_I2S_ReceiveData(SPI2);这个涵数就不会产生时钟用试波器看看吧所以不能单独用它直接收到数据。