1.简介
通过SPI方式与NRF24L01模块进行通讯,接收到的数据通过串口1打印出来,实时监测是否收到数据,发送的数据是“2.4G TEST”,当收不到数据时打印“no data”。一块STM32F103ZET6开发板接收数据,另一块STM32F103RBT6开发板发送数据,两个淘宝买的2.4G NRF24L01模块。还用到一块USB转TTL模块用来电平转换传送数据,串口调试助手接收串口发送数据。
2.代码部分
---------nrf24l01.h-----------
#ifndef __24L01_H
#define __24L01_H
#include "sys.h"
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////
//NRF24L01寄存器操作命令
#define NRF_READ_REG 0x00 //读配置寄存器,低5位为寄存器地址
#define NRF_WRITE_REG 0x20 //写配置寄存器,低5位为寄存器地址
#define RD_RX_PLOAD 0x61 //读RX有效数据,1~32字节
#define WR_TX_PLOAD 0xA0 //写TX有效数据,1~32字节
#define FLUSH_TX 0xE1 //清除TX FIFO寄存器.发射模式下用
#define FLUSH_RX 0xE2 //清除RX FIFO寄存器.接收模式下用
#define REUSE_TX_PL 0xE3 //重新使用上一包数据,CE为高,数据包被不断发送.
#define NOP 0xFF //空操作,可以用来读状态寄存器
//SPI(NRF24L01)寄存器地址
#define CONFIG 0x00 //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;
//bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能
#define EN_AA 0x01 //使能自动应答功能 bit0~5,对应通道0~5
#define EN_RXADDR 0x02 //接收地址允许,bit0~5,对应通道0~5
#define SETUP_AW 0x03 //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节;
#define SETUP_RETR 0x04 //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us
#define RF_CH 0x05 //RF通道,bit6:0,工作通道频率;
#define RF_SETUP 0x06 //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益
#define STATUS 0x07 //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发
//bit5:数据发送完成中断;bit6:接收数据中断;
#define MAX_TX 0x10 //达到最大发送次数中断
#define TX_OK 0x20 //TX发送完成中断
#define RX_OK 0x40 //接收到数据中断
#define OBSERVE_TX 0x08 //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,
重发计数器
#define CD 0x09 //载波检测寄存器,bit0,载波检测;
#define RX_ADDR_P0 0x0A //数据通道0接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P1 0x0B //数据通道1接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P2 0x0C //数据通道2接收地址,最低字节可设置,高字节,必须同
RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P3 0x0D //数据通道3接收地址,最低字节可设置,高字节,必须同
RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P4 0x0E //数据通道4接收地址,最低字节可设置,高字节,必须同
RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P5 0x0F //数据通道5接收地址,最低字节可设置,高字节,必须同
RX_ADDR_P1[39:8]相等;
#define TX_ADDR 0x10 //发送地址(低字节在前),ShockBurstTM模式
下,RX_ADDR_P0与此地址相等
#define RX_PW_P0 0x11 //接收数据通道0有效数据宽度(1~32字节),设置为0则
非法
#define RX_PW_P1 0x12 //接收数据通道1有效数据宽度(1~32字节),设置为0则
非法
#define RX_PW_P2 0x13 //接收数据通道2有效数据宽度(1~32字节),设置为0则
非法
#define RX_PW_P3 0x14 //接收数据通道3有效数据宽度(1~32字节),设置为0则
非法
#define RX_PW_P4 0x15 //接收数据通道4有效数据宽度(1~32字节),设置为0则
非法
#define RX_PW_P5 0x16 //接收数据通道5有效数据宽度(1~32字节),设置为0则
非法
#define NRF_FIFO_STATUS 0x17 //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX
FIFO满标志;bit2,3,保留
//bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,
循环发送上一数据包.0,不循环;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////
//24L01操作线
#define NRF24L01_CE_High GPIO_SetBits(GPIOB,GPIO_Pin_4) //24l01片选
#define NRF24L01_CE_Low GPIO_ResetBits(GPIOB,GPIO_Pin_4) //24L01片选信号
#define NRF24L01_CSN_High GPIO_SetBits(GPIOB,GPIO_Pin_5) //SPI片选信号
#define NRF24L01_CSN_Low GPIO_ResetBits(GPIOB,GPIO_Pin_5)
#define NRF24L01_IRQ GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6) //IRQ主机数据输入
//24L01发送接收数据宽度定义
#define TX_ADR_WIDTH 5 //5字节的地址宽度
#define RX_ADR_WIDTH 5 //5字节的地址宽度
#define TX_PLOAD_WIDTH 32 //32字节的用户数据宽度
#define RX_PLOAD_WIDTH 32 //32字节的用户数据宽度
void NRF24L01_Init(void); //初始化
void NRF24L01_RX_Mode(void); //配置为接收模式
void NRF24L01_TX_Mode(void); //配置为发送模式
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 u8s);//写数据区
u8 NRF24L01_Read_Buf(u8 reg, u8 *pBuf, u8 u8s); //读数据区
u8 NRF24L01_Read_Reg(u8 reg); //读寄存器
u8 NRF24L01_Write_Reg(u8 reg, u8 value); //写寄存器
u8 NRF24L01_Check(void); //检查24L01是否存在
u8 NRF24L01_TxPacket(u8 *txbuf); //发送一个包的数据
u8 NRF24L01_RxPacket(u8 *rxbuf); //接收一个包的数据
#endif
-----------nrf24l01.c----------------
#include "24l01.h"
#include "delay.h"
#include "usart.h"
///////////////////////////////////////////////////////////////////////////////
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;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI 单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
SPI_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_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
//使能SPI外设
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_4|GPIO_Pin_5; //PB12上拉防止W25X的干扰
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PG6 输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
//使能SPI外设
SPI_Cmd(SPI2, ENABLE);
NRF24L01_CE_Low; //使能24L01
NRF24L01_CSN_High; //SPI片选取消
}
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); //检查指定的SPI标志位设置与否:发送缓存空标志位
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
delay_us(2);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);//检查指定的SPI标志位设置与否:接受缓存非空标志位
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
delay_us(2);
}
//检测24L01是否存在
//返回值:0,成功;1,失败
u8 NRF24L01_Check(void)
{
u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
u8 i;
//SPI2_SetSpeed(SPI_BaudRatePrescaler_4); //spi速度为9Mhz(24L01的最大SPI 时钟为10Mhz)
NRF24L01_Write_Buf(NRF_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
}
//SPI写寄存器
//reg:指定寄存器地址
//value:写入的值
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
{
u8 status;
NRF24L01_CSN_Low; //使能SPI传输
status =SPI2_ReadWriteByte(reg);//发送寄存器号
SPI2_ReadWriteByte(value); //写入寄存器的值
NRF24L01_CSN_High; //禁止SPI传输
return(status); //返回状态值
}
//读取SPI寄存器值
//reg:要读的寄存器
u8 NRF24L01_Read_Reg(u8 reg)
{
u8 reg_val;
NRF24L01_CSN_Low; //使能SPI传输
SPI2_ReadWriteByte(reg); //发送寄存器号
reg_val=SPI2_ReadWriteByte(0XFF);//读取寄存器内容
NRF24L01_CSN_High; //禁止SPI传输
return(reg_val); //返回状态值
}
//在指定位置读出指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//len:数据长度
//返回值,此次读到的状态寄存器值
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)
{
u8 status,u8_ctr;
NRF24L01_CSN_Low; //使能SPI传输
status=SPI2_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值
for(u8_ctr=0;u8_ctr NRF24L01_CSN_High; //关闭SPI传输 return status; //返回读到的状态值 } //在指定位置写指定长度的数据 //reg:寄存器(位置) //*pBuf:数据指针 //len:数据长度 //返回值,此次读到的状态寄存器值 u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len) { u8 status,u8_ctr; NRF24L01_CSN_Low; //使能SPI传输 status = SPI2_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值 for(u8_ctr=0; u8_ctr NRF24L01_CSN_High; //关闭SPI传输 return status; //返回读到的状态值 } //启动NRF24L01发送一次数据 //txbuf:待发送数据首地址 //返回值:发送完成状况 u8 NRF24L01_TxPacket(u8 *txbuf) { u8 sta; // SPI2_SetSpeed(SPI_BaudRatePrescaler_8);//spi速度为9Mhz(24L01的最大SPI时钟为10Mhz) NRF24L01_CE_Low; NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF 32个字节 NRF24L01_CE_High;//启动发送 //while(NRF24L01_IRQ!=0);//等待发送完成 sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值 NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志if(sta&MAX_TX)//达到最大重发次数 { NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器 return MAX_TX; } if(sta&TX_OK)//发送完成 { return TX_OK; } return 0xff;//其他原因发送失败 } //启动NRF24L01发送一次数据 //txbuf:待发送数据首地址 //返回值:0,接收完成;其他,错误代码 u8 NRF24L01_RxPacket(u8 *rxbuf) { u8 sta; //SPI2_SetSpeed(SPI_BaudRatePrescaler_8); //spi速度为9Mhz(24L01的最大SPI 时钟为10Mhz) sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值 NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志if(sta&RX_OK)//接收到数据 { NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据 NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器 return 0; } return 1;//没收到任何数据 } //该函数初始化NRF24L01到RX模式 //设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR //当CE变高后,即进入RX模式,并可以接收数据了 void NRF24L01_RX_Mode(void) { NRF24L01_CE_Low; NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);/ /写RX节点地址 NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答 NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);//使能通道0的接收地址 NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //设置RF通信频率 NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度 NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启 NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 NRF24L01_CE_High; //CE为高,进入接收模式 } //该函数初始化NRF24L01到TX模式 //设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR //PWR_UP,CRC使能 //当CE变高后,即进入RX模式,并可以接收数据了 //CE为高大于10us,则启动发送. void NRF24L01_TX_Mode(void) { NRF24L01_CE_Low; NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址 NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次 NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //设置RF通道为40 NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启 NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断 NRF24L01_CE_High;//CE为高,10us后启动发送 } -------usart.h---------- #ifndef __USART_H #define __USART_H #include "stdio.h" #include "sys.h" /////////////////////////////////////////////////////////////////////////////// void uart_init(u32 bound); void usart1_send_string(u8 *BuffToSend); #endif ---------usart.c--------------- #include "sys.h" #include "usart.h" /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void uart_init(u32 bound){ //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟 //USART1_TX GPIOA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9 //USART1_RX GPIOA.10初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 //USART 初始化设置 USART_https://www.doczj.com/doc/3c700326.html,ART_BaudRate = bound;//串口波特率 USART_https://www.doczj.com/doc/3c700326.html,ART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_https://www.doczj.com/doc/3c700326.html,ART_StopBits = USART_StopBits_1;//一个停止位 USART_https://www.doczj.com/doc/3c700326.html,ART_Parity = USART_Parity_No;//无奇偶校验位 USART_https://www.doczj.com/doc/3c700326.html,ART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_https://www.doczj.com/doc/3c700326.html,ART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断 USART_Cmd(USART1, ENABLE); //使能串口1 } //打印字符串 void usart1_send_string(u8 *BuffToSend) { u8 i=0; while(BuffToSend[i]!='\0') { USART_SendData(USART1, BuffToSend[i]); while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET); i++; } } 收发主函数是分开写的,因为用的不同的芯片。收发就主函数不一样,其他代码相同。如果是相同的芯片可以写在一个主函数里面,用标志位加以限制。 ----------接收主函数main------------------------ #include "delay.h" #include "sys.h" #include "usart.h" #include "24l01.h" #include "string.h" u8 tmp_buf[20]; u8 nodata[]={"no data"}; int main(void) { delay_init(); //延时函数初始化 uart_init(115200); //串口初始化为11520 NRF24L01_Init(); //初始化NRF24L01 NRF24L01_RX_Mode();//接收模式 while(1) { NRF24L01_RxPacket(tmp_buf); if(tmp_buf[0]==0) { usart1_send_string(nodata); delay_ms(1000); memset(tmp_buf,0,20); } else { //LCD_ShowString(50,190,lcddev.width-1,32,16,tmp_buf); usart1_send_string(tmp_buf); delay_ms(1000); memset(tmp_buf,0,20); } } } ----------------发送主函数----------- #include "stm32f10x.h" #include "24l01.h" #include "delay.h" u8 buffToSend[]={"2.4G TEST"}; int main(void) { delay_init(); NRF24L01_Init();//初始化NRF24L01 NRF24L01_TX_Mode();//发送模式 while(1) { NRF24L01_TxPacket(buffToSend); } } 3结果 (1)发送模块断电情况 (2)发送模块上电情况 NRF24L01无线模块收发程序(实测成功多图) 本模块是NRF24L01无线传输模块,用于无线传输数据,距离不远,一般只是能够满足小距离的传输,目测是4-5m,价格一般是4元左右,可以方便的买到。 51最小系统学习板就可以,当时是用了两块学习板,一块用于发送,一块用于接收。 小车也是比较容易购到的,四个端口控制两个电机,两个控制一个电机,当两个端口高低电平不同时电机就会转动,即为赋值1和0是电机转动,赋值可以用单片机作用,当然这是小车启动部分,前进后退左转右转就是你赋值0和1的顺序问题了。 整体思路是用发射端的按键控制小车,即为按键按下就前进,再按其他按键实现其他功能,本次程序是在用NRF24L01发射数据在接收端用1602显示的基础上改变。 下面是程序源码(有好几个文件,分别创建) ////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////// #include #include #include'1602.h' #include'delay.h' #include 'nrf24l01.h' #define uint unsigned int #define uchar unsigned char uint Weight_Shiwu=1234; unsigned char KeyScan(void);//键盘扫描 // unsigned char KeyScan(void);//键盘扫描 //#define KeyPort P0 sbit KEY1 = P0^0; sbit KEY2 = P0^1; sbit KEY3 = P0^2; sbit KEY4 = P0^3; sbit KEY5 = P0^4; void main() { // char TxDate[4]; // LCD_Init(); //初始化液晶屏 // LCD_Clear(); //清屏 #include //下面是接收的NRF24L01的程序。 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include 详解SPI总线规范 SPI是英文Serial Peripheral Interface的缩写,中文意思是串行外围设备接口,SPI是Motorola公司推出的一种同步串行通讯方式,是一种三线同步总线,因其硬件功能很强,与SPI有关的软件就相当简单,使CPU有更多的时间处理其他事务。 SPI概述 SPI:高速同步串行口。3~4线接口,收发独立、可同步进行. SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCX X系列处理器上定义的。SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如AT91RM9200. SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。外围设置FLASH RAM、网络控制器、LCD显示驱动器、A/D转换器和MCU等。SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:串行时钟线(SCK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOSI和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT或INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。 SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)。 (1)SDO –主设备数据输出,从设备数据输入 (2)SDI –主设备数据输入,从设备数据输出 (3)SCLK –时钟信号,由主设备产生 (4)CS –从设备使能信号,由主设备控制 其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。 接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。 要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。 一文了解SPI总线工作原理应用案例 一文了解SPI总线工作原理应用案例将微控制器连接到传感器,显示器或其他模块时,您是否考虑过两个设备之间如何通信?他们到底在说什么?他们如何能够相互理解? 电子设备之间的通信就像人类之间的通信,双方都需要说同一种语言。在电子学中,这些语言称为通信协议。幸运的是,在构建大多数DIY电子项目时,我们只需要了解一些通信协议。在本系列文章中,我们将讨论三种最常见协议的基础知识:串行外设接口(SPI),内部集成电路(I2C)和通用异步接收器/发送器(UART)驱动通信。 首先,我们将从一些关于电子通信的基本概念开始,然后详细解释SPI的工作原理。 SPI,I2C和UART比USB,以太网,蓝牙和WiFi等协议慢得多,但它们更简单,使用的硬件和系统资源也更少。SPI,I2C和UART非常适用于微控制器之间以及微控制器和传感器之间的通信,在这些传感器中不需要传输大量高速数据。 串行与并行通信 电子设备通过物理连接在设备之间的导线发送数据位来相互通信,有点像一个字母中的字母,除了26个字母(英文字母表中),一个位是二进制的,只能是1或0。通过电压的快速变化,位从一个设备传输到另一个设备。在工作电压为5V的系统中,0位作为0V的短脉冲通信,1位通过5V的短脉冲通信。 数据位可以并行或串行形式传输。在并行通信中,数据位是同时发送的,每个都通过单独的线路。下图显示了二进制(01000011)中字母“C”的并行传输: 在串行通信中,通过单线逐个发送这些位。下图显示了二进制(01000011)中字母“C”的串行传输: 许多设备都采用了SPI通用通信协议。例如,SD卡模块,RFID读卡器模块和2.4GHz无线发送器/接收器都使用SPI 与微控制器通信。 SPI的一个独特优势是可以不间断地传输数据。可以连续流发送或接收任意数量的比特。使用I2C和UART,数据以数据包形式发送,限制为特定的位数。启动和停止条件定义每个数据包的开始和结束,因此数据在传输过程中会被 中断。 //许多人都在找nrf24l01无线模块的c程序;我以前刚接触无线//时用的就是nrf24l01模块;搜索了许多程序有很多都没法直接用;甚至还怀疑模块是不是被我搞坏了;拿去让别人检测模块又是好的;为避免大家走弯路;我将我的程序发出来供大家参考; 这是nrf24l01无线模块pcb图; 下面有Nrf24l01无线模块的收发c程序;以下程序经本人亲自测试;绝对能用!! 请注意以下几点: 1、24L01模块的电源电压是否为3V-3.6V之间; 2、如果您用的单片机是5V的话,请在IO口与模块接口之间串一个1K电阻; 3、检查模块的GND是否与单片机的GND相连接 4、先用程序进行调试,如果IO口不同,请更改IO口或相关时序; 5、如果是51系列单片机,晶振请选用11.0592M Hz; 模块供电最好用asm1117 5v转3.3v 稳压 测试单片机是stc89c52;at89c52 通用; 收发一体; 一大截不废话了;上程序;此程序是按键控制led;当按下s的时候对应接受的led会闪闪发光;很简单的~如果要实现其他更先进的功能;自己发掘吧~~ 务必将硬件连接正确;否则;它不会工作的~~当然做什么都要严谨~~错一点就差大了~~ 《《收发一体程序》》 #include *************************************** sbit M ISO =P1^3; sbit M OSI =P1^4; sbit SCK =P1^2; sbit CE =P1^1; sbit CSN =P3^2; sbit IRQ =P3^3; //************************************按键*************************************************** sbit KEY=P2^0; //***************************************************************************** sbit led=P2^1; //*********************************************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 20 // 20 uints TX payload #define RX_PLOAD_WIDTH 20 // 20 uints TX payload uint 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接收数据地址 SPI总线知识小结 同步外设接口(SPI)是由摩托罗拉公司开发的全双工同步串行总线,该总线大量用在与EEPROM、ADC、FRAM和显示驱动器之类的慢速外设器件通信。 SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。SPI 接口由SDI(串行数据输入),SDO(串行数据输出),SCK(串行移位时钟),CS(从使能信号)四种信号构成,CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟来发起通讯。通讯时,数据由SDO 输出,SDI 输入,数据在时钟的上升或下降沿由SDO 输出,在紧接着的下降或上升沿由SDI 读入,这样经过8/16 次时钟的改变,完成8/16 位数据的传输。 SPI通信 该总线通信基于主-从配置。它有以下4个信号: MOSI:主出/从入 MISO:主入/从出 SCK:串行时钟 SS:从属选择 芯片上“从属选择”(slave-select)的引脚数决定了可连到总线上的器件数量。 在SPI传输中,数据是同步进行发送和接收的。数据传输的时钟基于来自主处理器的时钟脉冲,摩托罗拉没有定义任何通用SPI的时钟规范。然而,最常用的时钟设置基于时钟极性(CPOL)和时钟相位(CPHA)两个参数,CPOL定义SPI串行时钟的活动状态,而CPHA定义相对于SO-数据位的时钟相位。CPOL和CPHA的设置决定了数据取样的时钟沿。 数据方向和通信速度 SPI传输串行数据时首先传输最高位。波特率可以高达5Mbps,具体速度大小取决于SPI硬件。例如,Xicor公司的SPI串行器件传输速度能达到5MHz。 SPI总线接口及时序 NRF24L01无线模块C语言程序 24MHz晶振 #include #include #include #include #include #include #define U8 unsigned char #define U16 unsigned int #define TX_ADDR_WITDH 5 //发送地址宽度设置为5个字节 #define RX_ADDR_WITDH 5 //接收地址宽度设置为5个字节 #define TX_DATA_WITDH 1//发送数据宽度1个字节 #define RX_DATA_WITDH 1//接收数据宽度1个字节 #define R_REGISTER 0x00//读取配置寄存器 #define W_REGISTER 0x20//写配置寄存器 #define R_RX_PAYLOAD 0x61//读取RX有效数据 #define W_TX_PAYLOAD 0xa0//写TX有效数据 #define FLUSH_TX 0xe1//清除TXFIFO寄存器 #define FLUSH_RX 0xe2//清除RXFIFO寄存器 #define REUSE_TX_PL 0xe3//重新使用上一包有效数据 #define NOP 0xff//空操作 #define CONFIG 0x00//配置寄存器 #define EN_AA 0x01//使能自动应答 #define EN_RXADDR 0x02//接收通道使能0-5个通道 #define SETUP_AW 0x03//设置数据通道地址宽度3-5 #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接收地址 SPI总线通俗易懂讲解 SPI总线 MOTOROLA公司的SPI总线的基本信号线为3根传输线,即SI、SO、SCK。传输的速率由时钟信号SCK决定,SI为数据输入、SO为数据输出。采用SPI总线的系统如图8-27所示,它包含了一个主片和多个从片,主片通过发出片选信号-CS来控制对哪个从片进行通信,当某个从片的-CS信号有效时,能通过SI接收指令、数据,并通过SO发回数据。而未被选中的从片的SO端处于高阻状态。 图8-27 SPI总线的系统 主片在访问某一从片时,必须使该从片的片选信号有效;主片在SCK信号的同步下,通过SI线发出指令、地址信息;如需将数据输出,则接着写指令,由SCK同步在SI线上发出数据;如需读回数据,则接着读指令,由主片发出SCK,从片根据SCK的节拍通过SO发回数据。 因而对具有SPI接口的从片器件来讲,SCK、SI是输入信号,SO是输出信号。SCK 用于主片和从片通信的同步。SI用于将信息传输到器件,输入的信息包括指令、地址和数据,指令、地址和数据的变化在SCK的低电平期间进行,并由SCK信号的上升沿锁存。 SO用于将信息从器件传出,传出的信息包括状态和数据,信息在SCK信号的下降沿移出。 Microchip公司的25XX系列的串行E2PROM采用了SPI总线,该系列器件的性能如表8-2所示。 表8-2 Microchip公司的25XX系列的串行E2PROM 以25XX320为例,该器件是4K字节的E2PROM,结构如图8-28所示,接口信号为SCK、SI和SO,此外还具有-CS、-WP、-HOLD信号线。其中-CS为器件选中信号,当此信号为低电平时器件被选中,高电平时器件处于等待状态。 图8-28 25XX320的结构 与并行接口电路不同的是,在并行接口电路中对器件进行操作的控制信号,在串行接口电路中只能用指令实现,25XX320的操作指令有数据读指令、写操作的允许和禁止指令、 IIC vs SPI 现今,在低端数字通信应用领域,我们随处可见IIC (Inter- Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身影。 原因是这两种通信协议非常适合近距离低速芯片间通信。Philips(for IIC)和Motorola(for SPI)出于不同背景和市场需求制定了这两种标准通信 协议。 IIC 开发于1982年,当时是为了给电视机内的CPU和外围芯片提供更 简易的互联方式。电视机是最早的嵌入式系统之一,而最初的嵌入系统是使 用内存映射(memory-mapped I/O)的方式来互联微控制器和外围设备的。 要实现内存映射,设备必须并联入微控制器的数据线和地址线,这种方式在 连接多个外设时需大量线路和额外地址解码芯片,很不方便并且成本高。 为了节省微控制器的引脚和和额外的逻辑芯片,使印刷电路板更简单, 成本更低,位于荷兰的Philips实验室开发了‘Inter-Integrated Circuit’,IIC 或 IIC ,一种只使用二根线接连所有外围芯片的总线协议。最初的标准定义总线速度为100kbps。经历几次修订,主要是1995年的 400kbps,1998的3.4Mbps。 有迹象表明,SPI总线首次推出是在1979年,Motorola公司将SPI总 线集成在他们第一支改自68000微处理器的微控制器芯片上。SPI总线是微 控制器四线的外部总线(相对于内部总线)。与IIC不同,SPI没有明文标准,只是一种事实标准,对通信操作的实现只作一般的抽象描述,芯片厂商 与驱动开发者通过data sheets和application notes沟通实现上的细节。SPI 对于有经验的数字电子工程师来说,用SPI互联两支数字设备是相当直 NRF24L01使用文档 基于c8051f330单片机 目录 芯片简介 (3) 1 NRF24L01功能框图 (4) 2 NRF24L01状态机 (5) 3 Tx与Rx的配置过程 (7) 3.1 Tx 模式初始化过程 (7) 3.2 Rx模式初始化过程 (8) 4控制程序详解 (9) 4.1 函数介绍 (9) 4.1.1 uchar SPI_RW(uchar byte) (9) 4.1.2 uchar SPI_RW_Reg (uchar reg, uchar value) (10) 4.1.3 uchar SPI_Read (uchar reg); (10) 4.1.4 uchar SPI_Read_Buf (uchar reg, uchar *pBuf, uchar bytes); (11) 4.1.5 uchar SPI_Write_Buf (uchar reg, uchar *pBuf, uchar bytes); (11) 4.1.6 void RX_Mode(void) (12) 4.1.7 void TX_Mode(void) (13) 4.2 NRF24L01相关命令的宏定义 (13) 4.3 NRF24L01相关寄存器地址的宏定义 (14) 5 实际通信过程示波器图 (16) 1)发射节点CE与IRQ信号 (17) 2)SCK与IRQ信号(发送成功) (18) 3)SCK与IRQ信号(发送不成功) (19) 芯片简介 NRF24L01是NORDIC公司最近生产的一款无线通信通信芯片,采用FSK调制,内部集成NORDIC自己的Enhanced Short Burst 协议。可以实现点对点或是1对6的无线通信。无线通信速度可以达到2M(bps)。NORDIC公司提供通信模块的GERBER文件,可以直接加工生产。嵌入式工程师或是单片机爱好者只需要为单片机系统预留5个GPIO,1个中断输入引脚,就可以很容易实现无线通信的功能,非常适合用来为MCU系统构建无线通信功能。 NRF24L01 简易教程 先来看接口电路,使用的IO 口不是唯一的哦,可随意定义接口,当然是在使用IO 口模拟SPI 且IRQ 中断引脚不使用的使用查询方法判断接收状态的情况下了。作为初探我们就是用简单的IO 模拟SPI 的方法了,中断使用查询的方式。那么该教程讲解的接口与单片机的连接如下: 首先您需要了解NRF24L01,请参阅“NRF24L01 芯片中文资料”或者“NRF24L01 芯片英文资料”。 我们的教程是以一个简单的小项目为大家展示NRF24L01 的使用方法与乐趣。我们所写的教程均是以这种方式的呢,让您在学习的时候明白它能做什么,使您学起来不至于枯燥无味。 作为简易的教程,我们只需要知道它是怎么使用的就够了,我们本教程的目的是用NRF24L01 发送数据和接收数据,且接收方会对比发送的数据与接收的数据,若完全相同则控制LED 闪烁一次,并且把接收到的数据通过串口发送到PC 端,通过串口工具查看接收到的数据。 具体的要求如下: 1、具备发送和接收的能力。 2、发送32 个字节的数据,接收方接收到正确数据之后给予提示,通过LED 闪烁灯形 式。 3、把接收到的数据传送到PC 进行查看。 4、发送端每隔大约1.5 秒发送一次数据,永久循环。以上是程序的要求,若您想自行 设计出硬件接口,您也是可以添加一条呢:使用DIY 方 式设计NRF24L01 的接口板,且包含含单片机平台,使用PCB 方式或者万用板方式均可。如果您想让自己学的很扎实,那么推荐您自行做出接口板子呢。当然若您的能力不足,那么我们不推荐自行做板呢,因为这样会增加您学习的难度,反而起到了反效果呢。 我们使用的方式是画PCB 的方式呢,若您自己做了接口板子,那么您可以对比下一呢,O(∩_∩)O! 我们知道NRF24L01 的供电电压是1.9V~3.6V 不能超过这个范围,低了不工作,高了可能烧毁NRF24L01 芯片。我们常用的STC89C52 的单片机的供电电压是5V,我们不能直接给24L01 这个模块供电,我们需要使用AMS1117-3.3V 稳压芯片把5V 转成3.3V 的电压为24L01 模块供电。 为此我们的设计原理图如下:包含单片机最小系统、供电系统、下载程序接口、5V 转3.3V 电路、NRF24L01 模块接口。并且全部引出单片机的IO 口,另外还加了5 个电源输出接口,为扩展使用。还包括了电源指示LED 以及一个IO 口独立控制的LED,这个独立控制的LED用于NRF24L01 接收成功闪烁指示。为了保证系统的稳定性,在设计中添加了两个滤波电容。 SPI通信协议(SPI总线)学习 各位读友大家好!你有你的木棉,我有我的文章,为了你的木棉,应读我的文章!若为比翼双飞鸟,定是人间有情人!若读此篇优秀文,必成天上比翼鸟! SPI通信协议(SPI总线)学习1、什么是SPI?SPI是串行外设接口(Serial Peripheral Interface)的缩写。是Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线。2、SPI优点支持全双工通信通信简单数据传输速率块3、缺点没有指定的流控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据可靠性上有一定的缺陷。4、特点1):高速、同步、全双工、非差分、总线式2):主从机通信模式5、协议通信时序详解1):SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。(1)SDO/MOSI –主设备数据输出,从设备数据输入;(2)SDI/MISO –主设备数据输入,从设备数据输出;(3)SCLK –时钟信号,由主设备产生;(4)CS/SS –从设备使能信号,由主设备控制。当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时将需要将从设备对应的片选引脚电平拉低或者是拉高。2):需要说明的是,我们SPI通信有4种不同的模式,不 同的从设备可能在出厂是就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来控制我们主设备的通信模式,具体如下:Mode0:CPOL=0,CPHA=0Mode1:CPOL=0,CPHA=1Mode2:CPOL=1,CPHA=0Mode3:CPOL=1,CPHA=1时钟极性CPOL 是用来配置SCLK的电平出于哪种状态时是空闲态或者有效态,时钟相位CPHA是用来配置数据采样是在第几个边沿:CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时CPHA=0,表示数据采样是在第1个边沿,数据发送在第2个边沿CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿例如:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。CPOL=0,CPHA=1:此时空闲态时,SCLK 处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第 (相关人员如觉得本人水平低下,还请见谅) Nrf24L01的使用程序和使用方法和简单操作: 功能: 无线对发程序。两个模块a,b,实现按下一个按键,会在对方的数码管上显示3或4,在本机上显示1,2。 当一个模块,比如a模块。当两个按键按下其中一个,则会在另一个模块b上显示数字3,4(具体根据按下哪个按键)。以上功能描述,B模块按键按下,如同a模块一样的功能,不做系统性描述了。 下面给出程序中几个地方的解释: #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 // 保留 类似这种的描述,可以等同于READ_REG =0x00;这个是经过实际程序测试出来的,比如 以下程序: #include MOSI = (uchar & 0x80); // output 'uchar', MSB to MOSI uchar = (uchar << 1); // shift next bit into MSB.. SCK = 1; // Set SCK high.. uchar |= MISO; // capture current MISO bit SCK = 0; 此处为spi的核心,是spi协议的编程,其中uchar |= MISO; 表示uchar |= MISO | uchar; MOSI = (uchar & 0x80);其中0x80是1000 0000,与上uchar,这种&,是按位与,故可以从uchar提取出一个电平给mosi。 MOSI = (uchar & 0x80); // output 'uchar', MSB to MOSI uchar = (uchar << 1); 这两句组合起来用,就实现了把uchar编程8位2进制数后的每一位都可以发送给mosi;Uchar的只待对象,就是上面的诸如#define FLUSH_TX 0xE1 这样的数,或者是相关的发送数据。 *pBuf这个并不是一个主要的问题,实际这个是涉及指针问题的,带*的跟地址有关系,但是我们其实不需要很关心编译的时候数据被具体存入哪个地址,即使是很重要的数据。 void init_NRF24L01(void) { inerDelay_us(100); CE=0; // chip enable CSN=1; // Spi disable SCK=0; // SPI_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,如果需要多频道可以参考Page21 SPI_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,发射功率为最大值0dB } 在整个初始化中我们看到: CE=0; // chip enable CSN=1; // Spi disable 这是设置整个的状态。如过状态设置成待机,则引脚可能变为高阻。(以上并非全部引脚) SPI总线协议及SPI时序图详解 SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。 下面为一种情况例举: 上升沿发送、下降沿接收、高位先发送。 上升沿到来的时候,sdo上的电平将被发送到从设备的寄存器中。 下降沿到来的时候,sdi上的电平将被接收到主设备的寄存器中。 假设主机和从机初始化就绪:并且主机的sbuff=0xaa (10101010),从机的sbuff=0x55 (01010101),下面将分步对spi的8个时钟周期的数据情况演示一遍(假设上升沿发送数据)。 --------------------------------------------------- 脉冲主机sbuff 从机sbuff sdi sdo --------------------------------------------------- 0 00-0 10101010 01010101 0 0 --------------------------------------------------- 1 0--1 0101010x 10101011 0 1 1 1--0 0101010010101011 0 1 --------------------------------------------------- 2 0--1 1010100x 01010110 1 0 2 1--0 1010100101010110 1 0 --------------------------------------------------- 3 0--1 0101001x 10101101 0 1 3 1--0 0101001010101101 0 1 --------------------------------------------------- 4 0--1 1010010x 01011010 1 0 4 1--0 1010010101011010 1 0 --------------------------------------------------- 5 0--1 0100101x 10110101 0 1 5 1--0 0100101010110101 0 1 --------------------------------------------------- 6 0--1 1001010x 01101010 1 0 6 1--0 1001010101101010 1 0 --------------------------------------------------- 7 0--1 0010101x 11010101 0 1 7 1--0 0010101011010101 0 1 --------------------------------------------------- 8 0--1 0101010x 10101010 1 0 8 1--0 01010101 10101010 1 0 这样就完成了两个寄存器8位的交换,上面的0--1表示上升沿、1--0表示下降沿,sdi、 sdo相对于主机而言的。根据以上分析,一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根据主机的名准备数据,主机在下一个8位时钟周期才把数据读回来。 SPI总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数NRF24L01无线模块收发程序(实测成功 多图)
NRF24L01发送程序
NRF24L01无线模块收发程序例程
详解SPI总线应用
一文了解SPI总线工作原理应用案例
nrf24l01无线模块NRF24L01模块收发c程序
SPI总线知识小结
NRF24L01无线模块C语言程序
SPI总线 通俗易懂讲解
总线I2C和SPI详解
NRF24L01功能使用文档
NRF24L01无线发射简易教程
SPI通信协议(SPI总线)学习
NRF24L01参考程序(包含多个实例)
SPI总线协议SPI时序图详解