专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

NRF24L01+无线遥控小车(C51)

作者:佚名   来源:本站原创   点击数:  更新时间:2014年04月10日   【字体:

2012.6月18日—2012.6月30日终于完成了无线遥控小车的程序和整理。再次特别感谢我的铁哥们-张文为这个程序的后续完善所打下的坚实基础和程序设计的指导和领路人-柳飞大哥的友情帮助以及好友-彭飞兄的C的语法帮助!正是有你们,我的小车才能完工,程序才能稳定运行!拍些照片留作纪念~



*************************************************************************发射部分****************************************************************************
#include <reg52.h>
#include <Intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit MISO=P1^4; //主收从发
sbit MOSI=P1^3; //主发从收
sbit SCK=P1^2; //串行时钟信号
sbit CE=P1^1; //芯片使能
sbit CSN=P1^5; //片选信号
sbit IRQ=P1^6; //中断查询
/*************************************************************/
uchar seg[10]={~0x3f,~0x06,~0x5b,~0x4f,~0x66};
bit TxBufEndFlg = 1; //数据发送完成标志
uchar checkack();
uchar TxBuf[20]={0};
uchar RxBuf[20]={0};
uchar SPI_RW(uchar uchars);
uchar SPI_Read(uchar reg);
uchar SPI_RW_Reg(uchar reg, uchar value);
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars);
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars);
uchar KeyScanPrg();
uchar KeyInState,KeyStateCnt;
/************************************************************/
#define TX_ADR_WIDTH    5   //发送地址长度为5个字节
#define RX_ADR_WIDTH    5   //接收地址长度为5个字节
#define TX_PLOAD_WIDTH  20  //发送数据长度为20个字节
#define RX_PLOAD_WIDTH  20  //接收数据长度为20个字节
uchar code TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //本地地址
uchar code RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //接收地址
//SPI命令
#define READ_REG        0x00  //读第0个寄存器
#define WRITE_REG       0x20  //写第0个寄存器
#define RD_RX_PLOAD     0x61  //在接收模式下使用,读有效数据
#define WR_TX_PLOAD     0xA0  //在发送模式下使用,写有效数据
#define FLUSH_TX        0xE1  //在发送模式下使用,清TX FIFO寄存器
#define FLUSH_RX        0xE2  //在接收模式下使用,清RX FIFO寄存器
#define REUSE_TX_PL     0xE3  //发送方使用,重复发送最后的数据
#define NOP             0xFF  //空操作,用于读状态寄存器STATUS的值
//NRF24L01寄存器地址
#define CONFIG          0x00  //配置寄存器,8bit
#define EN_AA           0x01  //自动应答设置寄存器,8bit
#define EN_RXADDR       0x02  //接收地址设置寄存器,8bit
#define SETUP_AW        0x03   //地址宽度设置寄存器,8bit
#define SETUP_RETR      0x04   //自动重复发送设置寄存器,8bit
#define RF_CH           0x05   // RF通道寄存器,8bit(工作频率设置)
#define RF_SETUP        0x06   //RF设置寄存器,8bit(发射速率、功耗功能设置)
#define STATUS          0x07   //状态寄存器,8bit
#define OBSERVE_TX      0x08   //发送检测寄存器,8bit
#define CD              0x09   //载波检测寄存器,8bit         
#define RX_ADDR_P0      0x0A   //接收地址数据通道0,40bit
#define RX_ADDR_P1      0x0B
#define RX_ADDR_P2      0x0C
#define RX_ADDR_P3      0x0D
#define RX_ADDR_P4      0x0E
#define RX_ADDR_P5      0x0F           
#define TX_ADDR         0x10   //发送地址,发送方使用,40bit
#define RX_PW_P0        0x11   //通道0接收的有效数据字节长度(1-32字节),8bit
#define RX_PW_P1        0x12
#define RX_PW_P2        0x13
#define RX_PW_P3        0x14
#define RX_PW_P4        0x15
#define RX_PW_P5        0x16
#define FIFO_STATUS     0x17   //FIFO栈入栈出状态寄存器,8bit
//*********************************************************************************
void Delay(uint s);
void delayms(uchar z);
void inerDelay_us(uchar n);
void init_NRF24L01();
void nRF24L01_TxPacket(uchar * tx_buf);
void KeyDealPrg(uchar KeyState);
//**********************************************************************************
void inerDelay_us(uchar n)
{
 for(;n>0;n--)
  _nop_();
}

//************************************************************************************
uchar  bdata sta;   //状态标志
sbit RX_DR =sta^6;
sbit TX_DS =sta^5;
sbit MAX_RT =sta^4;
//***************************checkack函数********************************************
uchar checkack()
{
 sta=SPI_Read(STATUS);   // 返回状态寄存器
 if(TX_DS || MAX_RT)   // 发送完毕中断
 {
 if(MAX_RT)
 init_NRF24L01();
 TxBufEndFlg = 1;
    SPI_RW_Reg(WRITE_REG+STATUS,0xff);   // 清除TX_DS或MAX_RT中断标志
  CSN=0;
        SPI_RW(FLUSH_TX); //在接收模式下,清空RX FIFO寄存器。在传输应答信号时不应执行此操作,否则不能传输完整的应答信号
     CSN=1; 
    return(1);
 }
 else
    return(0);
}
//***************************NRF24L01初始化********************************************
void init_NRF24L01(void)
{
 inerDelay_us(100);
 CE=0;    //芯片启动
 CSN=1;   //终止SPI读写
 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 + SETUP_RETR, 0x4a); //自动延时1250us,自动重发10次
 SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      //ENAA_P0=1,数据通道0自动应答
 SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  // EN_RXADDR=1,接收地址允许
 SPI_RW_Reg(WRITE_REG + RF_CH, 0);        //设置信道工作为2.4GHZ,收发必须一致
 SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度,本次设置为20字节
 SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);     //设置发射速率为1MHZ,发射功率为0dBm,低噪声放大器增益
}
//***************************************SPI操作***********************************************
 
uchar SPI_RW(uchar uchars)//写一个字节到NRF24L01,并返回此时NRF24L01的状态及数据
{
 uchar bit_ctr;
    for(bit_ctr=0;bit_ctr<8;bit_ctr++)  //先写字节的高位,再写低位
    {
  MOSI = (uchars & 0x80);         //MOSI取uhcars最高位
  uchars = (uchars << 1);           //uchars左移一位
  SCK = 1;                      //SCK从高到低时开始写入
  uchars |= MISO;           // 获取MISO位,从MOSI写命令的同时,MISO返回NRF24L01的状态及数据
  SCK = 0;              
    }
    return(uchars);
}
 
uchar SPI_Read(uchar reg)//读寄存器reg状态字
{
 uchar reg_val;
 CSN = 0;                //CSN为0时,才能进行SPI读写
 SPI_RW(reg);            //选择寄存器reg
 reg_val = SPI_RW(0);    //写0,什么操作也不进行,仅仅为了读寄存器状态
 CSN = 1;                //终止SPI读写
 return(reg_val);     
}

uchar SPI_RW_Reg(uchar reg, uchar value)//将字节value写入寄存器reg
{
 uchar status;
 CSN = 0;                   //CSN为0时,才能进行SPI读写
 status = SPI_RW(reg);      //选择寄存器reg
 SPI_RW(value);             //写字节value到该寄存器.
 CSN = 1;                   //终止SPI读写
 return(status);          
}
 
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars)//从寄存器reg读出数据,典型应用是读RX数据或RX/TXF地址
{
 uchar status,uchar_ctr;
 CSN = 0;                      //CSN为0时,才能进行SPI读写
 status = SPI_RW(reg);         //选择寄存器reg并返回其状态字
 for(uchar_ctr=0;uchar_ctr<uchars;uchar_ctr++)
 pBuf[uchar_ctr] = SPI_RW(0);    //从寄存器读数据
 CSN = 1;                         //终止SPI读写
 return(status);                    //返回状态值
}

uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)//将数据写入寄存器,如TX数据,RX/TX地址等
{
 uchar status,uchar_ctr;
 CSN = 0;            // CSN为0时,才能进行SPI读写
    status = SPI_RW(reg);  //选择寄存器reg并返回其状态字
 for(uchar_ctr=0; uchar_ctr<uchars; uchar_ctr++)
 SPI_RW(*pBuf++); //写数据到寄存器
 CSN = 1;           //终止SPI读写
 return(status);    //返回状态值
}

/***********************************************************************************************************
/*函数:void nRF24L01_TxPacket(unsigned char * tx_buf)
/*功能:发送 tx_buf中数据
/**********************************************************************************************************/
void nRF24L01_TxPacket(unsigned char * tx_buf)
{
   if(TxBufEndFlg)
 {
 CE=0;   //待机模式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);      //CRC使能,CRC校验值为2字节,上电,设置为发射模式
 CE=1;   //置高CE,激发数据发送
 TxBufEndFlg = 0; //标志位清0
 inerDelay_us(1000);
 }
}
/*****************************************************************************/
/*函数:
/*功能:按键采集扫描
/*****************************************************************************/
uchar KeyScanPrg(void)
{
 uchar KeyStateRAM,KeyState=0;
 KeyStateRAM  = P2;
 KeyStateRAM &= 0xf0; //用到的其实是P2.7 2.6 2.5 2.4这四个按键
 if(KeyStateRAM==KeyInState)
    {
  if(KeyStateCnt>20)
  {
  KeyState = KeyInState;
  }
  else
  KeyStateCnt++;
    }
 else
    {
    KeyInState = KeyStateRAM;
    KeyStateCnt = 0;
    }
 return(KeyState);
}
/*****************************************************************************/
/*函数:
/*功能:按键处理
/*****************************************************************************/
void KeyDealPrg(uchar KeyState)
{
 switch(KeyState&0xf0)
  {
  case 0x70:P3=seg[1];TxBuf[1]=1;TxBuf[5]=0;
   break;
  case 0xb0:P3=seg[2];TxBuf[2]=1;TxBuf[5]=0;
   break;
  case 0xd0:P3=seg[3];TxBuf[3]=1;TxBuf[5]=0;
   break;
  case 0xe0:P3=seg[4];TxBuf[4]=1;TxBuf[5]=0;
   break;
  default  :P3=seg[0];
   TxBuf[1]=0;
   TxBuf[2]=0;
   TxBuf[3]=0;
   TxBuf[4]=0;
   TxBuf[5]=1;
  }
  nRF24L01_TxPacket(TxBuf);
  checkack();
}
/*****************************************************************************/
/*函数:
/*功能:主函数
/*****************************************************************************/
 void main(void)
{
   init_NRF24L01();
 while(1)
 {
 KeyDealPrg(KeyScanPrg());
 }
}
 

 
//**************************************************************************接收部分**********************************************************************
#include <reg52.h>
#include <intrins.h>
typedef unsigned char uchar; //等同于#define uchar unsigned char
typedef unsigned char uint;
sbit IRQ=P1^5; //中断查询
sbit MISO=P1^4; //主收从发
sbit MOSI=P1^3; //主发从收
sbit SCK=P1^2; //串行时钟信号
sbit CSN=P1^1; //片选信号
sbit CE=P1^0; //芯片使能
sbit ENB=P0^5;
sbit ENA=P0^4;
sbit EN4=P0^3;
sbit EN3=P0^2;
sbit EN2=P0^1;
sbit EN1=P0^0;
//**************************************************************************************
uchar seg[10]={~0x3f,~0x06,~0x5b,~0x4f};
#define TX_ADR_WIDTH    5   //发送地址长度为5个字节
#define RX_ADR_WIDTH    5   //接收地址长度为5个字节
#define TX_PLOAD_WIDTH  20  //发送数据长度为20个字节
#define RX_PLOAD_WIDTH  20  //接收数据长度为20个字节
uchar code TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //本地地址
uchar code RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //接收地址
//SPI命令
#define READ_REG        0x00  //读第0个寄存器
#define WRITE_REG       0x20  //写第0个寄存器
#define RD_RX_PLOAD     0x61  //在接收模式下使用,读有效数据
#define WR_TX_PLOAD     0xA0  //在发送模式下使用,写有效数据
#define FLUSH_TX        0xE1  //在发送模式下使用,清TX FIFO寄存器
#define FLUSH_RX        0xE2  //在接收模式下使用,清RX FIFO寄存器
#define REUSE_TX_PL     0xE3  //发送方使用,重复发送最后的数据
#define NOP             0xFF  //空操作,用于读状态寄存器STATUS的值
//NRF24L01寄存器地址
#define CONFIG          0x00  //配置寄存器,8bit
#define EN_AA           0x01  //自动应答设置寄存器,8bit
#define EN_RXADDR       0x02  //接收地址设置寄存器,8bit
#define SETUP_AW        0x03   //地址宽度设置寄存器,8bit
#define SETUP_RETR      0x04   //自动重复发送设置寄存器,8bit
#define RF_CH           0x05   // RF通道寄存器,8bit(工作频率设置)
#define RF_SETUP        0x06   //RF设置寄存器,8bit(发射速率、功耗功能设置)
#define STATUS          0x07   //状态寄存器,8bit
#define OBSERVE_TX      0x08   //发送检测寄存器,8bit
#define CD              0x09   //载波检测寄存器,8bit         
#define RX_ADDR_P0      0x0A   //接收地址数据通道0,40bit
#define RX_ADDR_P1      0x0B
#define RX_ADDR_P2      0x0C
#define RX_ADDR_P3      0x0D
#define RX_ADDR_P4      0x0E
#define RX_ADDR_P5      0x0F           
#define TX_ADDR         0x10   //发送地址,发送方使用,40bit
#define RX_PW_P0        0x11   //通道0接收的有效数据字节长度(1-32字节),8bit
#define RX_PW_P1        0x12
#define RX_PW_P2        0x13
#define RX_PW_P3        0x14
#define RX_PW_P4        0x15
#define RX_PW_P5        0x16
#define FIFO_STATUS     0x17   //FIFO栈入栈出状态寄存器,8bit
//**************************************************************************************
uchar SPI_RW(uchar uchars);
uchar SPI_Read(uchar reg);
uchar SPI_RW_Reg(uchar reg, uchar value);
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars);
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars);
uchar nRF24L01_RxPacket(uchar* rx_buf);
//*****************************************电机驱动*****************************************
void zhengzhuan()
{ENA=1;
 ENB=1;
 EN1=1;
 EN2=0;
 EN3=0;
 EN4=1;
}
void fanzhuan()
{ENA=1;
 ENB=1;
 EN1=0;
 EN2=1;
 EN3=1;
 EN4=0;
}
void tingzhi()
{ENA=1;
 ENB=1;
 EN1=0;
 EN2=0;
 EN3=0;
 EN4=0;
}
void zuozhuan()
{ENA=1;
 ENB=1;
 EN1=1;
 EN2=0;
 EN3=1;
 EN4=0;
}
void youzhuan()
{ENA=1;
 ENB=1;
 EN1=0;
 EN2=1;
 EN3=0;
 EN4=1;
}
//***************************************************************************************
void inerDelay_us(uchar n)
{
 for(;n>0;n--)
  _nop_();
}
//******************************************************************************************
uint  bdata sta;   //状态标志
sbit RX_DR =sta^6;
sbit TX_DS =sta^5;
sbit MAX_RT =sta^4;
//****************************************************************************************************
uchar SPI_RW(uchar uchars)//写一个字节到NRF24L01,并返回此时NRF24L01的状态及数据
{
 uchar bit_ctr;
    for(bit_ctr=0;bit_ctr<8;bit_ctr++)  //先写字节的高位,再写低位
    {
  MOSI = (uchars & 0x80);         //MOSI取uhcars最高位
  uchars = (uchars << 1);           //uchars左移一位
  SCK = 1;                      //SCK从高到低时开始写入
  uchars |= MISO;           // 获取MISO位,从MOSI写命令的同时,MISO返回NRF24L01的状态及数据
  SCK = 0;              
    }
    return(uchars);
}
uchar SPI_Read(uchar reg)//读寄存器reg状态字
{
 uchar reg_val;
 CSN = 0;                //CSN为0时,才能进行SPI读写
 SPI_RW(reg);            //选择寄存器reg
 reg_val = SPI_RW(0);    //写0,什么操作也不进行,仅仅为了读寄存器状态
 CSN = 1;                //终止SPI读写
 return(reg_val);     
}
uchar SPI_RW_Reg(uchar reg, uchar value)//将字节value写入寄存器reg
{
 uchar status;
 CSN = 0;                   //CSN为0时,才能进行SPI读写
 status = SPI_RW(reg);      //选择寄存器reg
 SPI_RW(value);             //写字节value到该寄存器.
 CSN = 1;                   //终止SPI读写
 return(status);          
}
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars)//从寄存器reg读出数据,典型应用是读RX数据或RX/TXF地址
{
 uchar status,uchar_ctr;
 CSN = 0;                      //CSN为0时,才能进行SPI读写
 status = SPI_RW(reg);         //选择寄存器reg并返回其状态字
 for(uchar_ctr=0;uchar_ctr<uchars;uchar_ctr++)
 pBuf[uchar_ctr] = SPI_RW(0);    //从寄存器读数据
 CSN = 1;                         //终止SPI读写
 return(status);                    //返回状态值
}
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)//将数据写入寄存器,如TX数据,RX/TX地址等
{
 uchar status,uchar_ctr;
 CSN = 0;            // CSN为0时,才能进行SPI读写
    status = SPI_RW(reg);  //选择寄存器reg并返回其状态字
 for(uchar_ctr=0; uchar_ctr<uchars; uchar_ctr++)
 SPI_RW(*pBuf++); //写数据到寄存器
 CSN = 1;           //终止SPI读写
 return(status);    //返回状态值
}//功能: 用于写数据:为寄存器地址,pBuf:为待写入数据地址,uchars:写入数据的个数
/****************************************************************************************************/
/*函数:void SetRX_Mode(void)
/*功能:数据接收配置
/****************************************************************************************************/
void SetRX_Mode()
{
 CE=0;
 SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);     // IRQ收发完成中断响应,CRC使能,CRC校验值为2字节,上电,接收模式
 CE = 1;
 inerDelay_us(130);
}
/******************************************************************************************************/
/*函数:unsigned char nRF24L01_RxPacket(unsigned char* rx_buf)
/*功能:数据读取后放如rx_buf接收缓冲区中
/******************************************************************************************************/
uchar nRF24L01_RxPacket(uchar* rx_buf)
{
    uchar revale=0;
 sta=SPI_Read(STATUS);      // 读取状态寄存其来判断数据接收状况
 if(RX_DR)    // 判断是否接收到数据
 {
     CE = 0;    //SPI使能
  SPI_Read_Buf(RD_RX_PLOAD,rx_buf,TX_PLOAD_WIDTH);
  revale =1; //读取数据完成标志
        CSN=0;
        SPI_RW(FLUSH_RX); //在接收模式下,清空RX FIFO 寄存器,并写入NRF2401
        CSN=1; 
 }
 SPI_RW_Reg(WRITE_REG+STATUS,sta); //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标志
        CSN=0;
       SPI_RW(FLUSH_RX);
        CSN=1; 
 return revale;
}
//***************************NRF24L01初始化********************************************
void init_NRF24L01(void)
{
 inerDelay_us(100);
 CE=0;    //芯片启动
 CSN=1;   //终止SPI读写
 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 + SETUP_RETR, 0x4a); //自动延时1250us,自动重发10次
 SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      //ENAA_P0=1,数据通道0自动应答
 SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  // EN_RXADDR=1,接收地址允许
 SPI_RW_Reg(WRITE_REG + RF_CH, 0);        //设置信道工作为2.4GHZ,收发必须一致
 SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度,本次设置为20字节
 SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);     //设置发射速率为1MHZ,发射功率为0dBm,低噪声放大器增益
}
//**********************************************************************************************
 void main()
{
 uchar TxBuf[20]={0};
 uchar RxBuf[20]={0};
 init_NRF24L01();
 while(1)
 {
  SetRX_Mode();
  nRF24L01_RxPacket(RxBuf);
  if(RxBuf[1]|RxBuf[2]|RxBuf[3]|RxBuf[4]|RxBuf[5])
  {
   if(RxBuf[1]==1)
   {
    P3=seg[1];
    zhengzhuan();RxBuf[5]=0;
   }
   else if(RxBuf[2]==1)
   {
    P3=seg[2];
    fanzhuan();RxBuf[5]=0;
   }
   else if(RxBuf[3]==1)
   {
    P3=seg[3];
    zuozhuan();RxBuf[5]=0;
   }
   else if(RxBuf[4]==1)
   {
    P3=seg[4];
    youzhuan();RxBuf[5]=0;
   }
   else if(RxBuf[5]==1)
   {
    P3=seg[0];
    tingzhi();RxBuf[5]=0;
   }
  
  }
  RxBuf[1]=0;
  RxBuf[2]=0;
  RxBuf[3]=0;
  RxBuf[4]=0;
  RxBuf[5]=0;
 }
}

关闭窗口