切换到宽版
  • 30040阅读
  • 157回复

[C51]高科技的遛狗器,狗狗再也不会走丢了(简易版/代码优化版本) [复制链接]

上一主题 下一主题
离线yanxue11
 

发帖
484
M币
4757
专家
8
粉丝
48
只看楼主 倒序阅读 使用道具 楼主  发表于: 2016-08-07
— 本帖被 發騷友 设置为精华,作者+3000M币+5专家(2016-08-19) —
5个月前捡了条小狗
最近每天遛狗,又不想拿狗绳天天栓着
让它乱跑吧,我又怕它跑丢了
实际上已经跑丢过两次,不过很快都找到了。
我遛狗的时候拿手机看小说,往往就把狗给溜丢了。
所以一直想DIY一个遛狗器。
我最早的设计就是一个GPRS+GPS芯片的前端
然后加一个手机APP,调用百度地图来定位啥的。
狗跑远了就报警,然后还可以用来定位找狗。
2个月前,安信可发布了新产品的预告,A7模块,
非常符合我的需求,就是下面这个东西。



我等了两个月,它才姗姗来迟,价格38块。
包年的移动GPRS卡10块。
自己DIY的话,100块钱以下肯定没问题。

---------------------------------------------------------------------------

好吧,上面都是以后要做的事情。
一时半会也弄不好,趁着周末,我刚好又有了另外的想法
来完成一个简易版本的遛狗器。
话说我学单片机是因为DIY航模遥控器入门的。
所以对NRF24L01这种2.4G无线模块很熟悉。
航模飞的远了,信号弱了,遥控器就要报警,让航模往回飞。
这个简易版本的遛狗器就是这种思路,不能定位,不能测量距离,
只能简单识别信号的强弱,也就是只能距离远了,信号不好报警。

当然和航模遥控器还有点不同,狗携带的是发射器,人携带的是接收器。
同样,除了远距离报警外,还能切换模式,变成寻狗器。
也就是接收器收到发射器的信号就发出声音报警,而不是收不到信号才报警。

上面说了这么多的废话,也是我一贯的习惯。下面正式开工:



先焊两套2.4G模块和单片机出来。单片机用的是STC15W408AS sop16,
为了避免打板子,我直接用了转接板,这样就可以飞线了。
然后2.4G模块用的是比较好的那种,泽耀的8.5元一块的最小的模块。
然后把2.4G模块飞线到单片机上,一模一样的焊起来就可以了。



然后就是写程序,先写的是发射器的程序。
发射器是电池供电,所以功耗一点要小。
我的设计是每秒只发1次信号,发完信号后,单片机控制2.4G模块进入掉电模式,
然后单片机也进入掉电模式省电。
1秒后,单片机由掉电唤醒定时器叫醒,然后单片机唤醒2.4G模块,发一次数据。
为了最大程度的省电,2.4G模块会自动采用最优的功率发射信号。
如果通信情况不好,则自动增加功率,否则自动降低功率。
下面是我采用的NRF24模块的发射电流和掉电电流。



为了测试设计的有效性,我测量了电路的电流,如下:



分别是掉电模式下的静态电流和最低功率发射信号时的电流。
大概在5mA~8mA左右,而且持续时间很短。
实际上,这种设计也的确比较省电,我用一块POS机上拆下来的小电池,让发射器整整跑了48小时,电压还在3.8V以上。
而且电池本身电量就不是很足。我估计充满电后,150mA的电池,跑个100小时肯定没问题,说不定200小时也能达到。
为了进一步省电,我又给发光二极管串上1K的限流电阻,哈,电流又降低了1mA。


那么发射器如果最大发射功率运行呢?电流果然增大不少。



那么再看看不需要掉电运行的接收器的电流,单片机+最大功率的2.4G模块,电流果然很大


不过,接收器我会配上1500mA的大电池,这点功率消耗无所谓了。

程序写好后,就要考虑电池的事情了。POS机上的锂电池不错,自带保护板,真好啊。
电池自带的线很烂,换两根30AWG的硅胶线。换线的时候先把电池一个极的镍片给焊下来,
否则非常容易短路,到时候就完蛋了。

因为2.4G模块是3.3V供电,所以还要加上3.3V稳压模块。
本来我打算用AMS1117-3.3芯片的,但是这个芯片实在不适合小电池,什么负载都不接,静态电流就有8mA之多,
简直不能忍受。
还好我有1元包邮的POS机,没二话,把上面的SPX5205芯片拆下来吧,这个静态电流只有70uA,非常给力

还要配套的锂电池充电模块,接着把POS机上的TP4055拆下来。
我用sop8转接板,把5205和4055都焊好,配上周边电容电阻啥的。反正都是POS上全套都有。
你能分清下面两个模块,哪个是4055,哪个是5205吗?哈哈



然后充分的利用POS机的外壳,找一个成品的MicroUsb转接板放上去
刚好的样子,当然最后要上热熔胶固定。




下面给充电模块选择一个充电电阻,


按照说明,1.66K的电阻,充电电流是500mA,
POS机上的是16.6K的电阻,充电电流只有50mA。
我这里用的4.7K,充电电流不大不小,200mA刚好。



2楼接着发,稍等,不要占楼哈。
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共36条打赏M币+143
d2767 M币 +15 優秀文章 05-05
dgh M币 +30 - 03-25
yufei8051 M币 +3 手艺不错。不过,话说一个都能跑丢的蠢狗养它干啥? 01-22
anb510769 M币 +3 優秀文章 2016-12-22
chaoshiaoyu M币 +3 謝謝分享 2016-11-09
路过路过2015 M币 +3 这个想法不错 2016-10-14
zxq9781 M币 +3 - 2016-09-30
charles.l M币 +3 優秀文章 2016-09-05
ihouse M币 +3 - 2016-08-28
653131267 M币 +3 - 2016-08-27
关键词: 遛狗GPS
离线yanxue11

发帖
484
M币
4757
专家
8
粉丝
48
只看该作者 1楼 发表于: 2016-08-07
先补一张充电电阻的阻值选择图。



我选的4.7K,电流刚好比200mA大一点。
我一直觉得既然把帖子发到单片机社区,就该介绍一些常识,要不还不如发到创意DIY社区去,
那里人多,大家还能看个热闹。
然后依次把稳压模块,充电模块,和单片机和2.4G模块焊到一起。
然后接一个单刀双掷的拨动开关。
为了修改程序方便,我还单独飞了4根杜邦针。


用高温胶带,把全部的模块都包一遍,免得相互间短路。
另外也能稍微保护一下,防个水啥的。。。。。
最后就是组装。当然少不了热熔胶了。
我拿胶枪把开关放到了原来POS机按键的地方,
把两个LED都黏好。
然后全部的模块用双面胶固定。当然不是普通的双面胶,是专用的那种,不会有残留的。


------------------------------------------
以上就是发射器,基本算完工了,还没盖盖子,怕有改动啥的。下面就是接收器。

首先还是DIY的老问题,外壳。
POS机真是个好东西,手中刚好有两个POS机的外包装是塑料盒。
这个外壳非常完美,刚好能放下我的锂电池。
3块钱买的三星的1500mA的手机电芯。
当然焊铝壳要专用的焊锡丝,并且温度控制到420~450°。
温度高了,焊接飞溅,不起作用。温度低了,焊锡不流动。
还是高温胶带全面包一层,免得短路。



这里我就不从POS机上找充电芯片和稳压芯片了。
直接用那种锂电池充电保护一体板就好了。
在外壳边上用美工刀开个槽,保证充电器插头能插进来就可以了。
模块用双面胶固定。
然后用一个AMS1117的稳压模块,再弄一个手电筒的开关。
壳上开个圆孔,保证开关能露头。
开关同样用双面胶固定到电池上。


接收器零碎比较多,计划需要3个LED,分别是蓝色,红色,黄色。
蓝色表示接收到发射器的信号。
红色是接收器电池低压报警。
黄色是发射器电池低压报警。
还需要一个微动开关来切换遛狗和寻狗的模式。
还需要一个蜂鸣器来发出报警声。
其实我还设计了一个震动模式,需要一个微型的震动电机。
很可惜,我这里没有,所以就算了。



另外,介绍发射器的时候忘了说了。
就是电池电压的测量。
虽然STC的单片机有低压中断的功能,不过我还是打算用ADC来测电压。
利用单片机内部的基准电压和电阻的分压adc,就可以精确的计算出电池的电压来啦。
我发射器和接收器,都是用了两个470K的电阻,把1s锂电池的电压分成一半。


就是上面这个电路。本来我还并联一个0.1uf的电容在R2和GND之间。
但是我发现这样会造成测量的电压值,比实际电压搞出100mV,也就是0.1V样子
所以我就去掉了那个0.1uf的电容了。
也许STC的adc不行,分压电阻阻值太大,造成了它无法精确测量。
本来我用的是两个1M 的电阻分压的,后来修改成470K。
虽然造成电池的功耗增加,反正也没多少,就这样吧。

最后程序写好调试好,组装。这次都是双面胶粘的,没用热熔胶。




最后的成品如图:

晚上拍的,凑合看,不是很清晰。



发射器可以用胶带固定到狗的胸带上,放在狗的身体侧面。
这样不会妨碍到它的行动。
实际上感觉发射器体积和重量还是大了。
没法子,DIY就这样,我还是飞线的。
下次我考虑热转印或者打几个块板子,能把体积降低到4*3*0.8CM的样子是最好的。

楼下继续
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共18条打赏M币+54
huaweiwx M币 +3 - 2016-12-26
anb510769 M币 +3 - 2016-12-22
denglei M币 +3 歡迎探討 2016-11-25
yiranone M币 +3 非常好,很实用,创意新颖! 2016-11-14
信息中心 M币 +3 謝謝分享 2016-10-28
qlkf M币 +3 你做个能让狗狗听到声音就自己回来不就更方便吗 2016-09-10
yke2000 M币 +3 原創內容 2016-09-08
aresboy M币 +3 謝謝分享 2016-08-25
799946503 M币 +3 謝謝分享 2016-08-22
seraphimer M币 +3 原創內容 2016-08-22
12
离线yanxue11

发帖
484
M币
4757
专家
8
粉丝
48
只看该作者 2楼 发表于: 2016-08-07
修改一些bug
今天早上给小狗带上遛狗器,正式去遛狗。
发现一个问题,特别优化代码。

问题:距离过近,而且遮挡的时候也容易报警。哔哔叫的很无语。

解决:优化代码。

1、发射程序只要10次里面有1次发射不成功,就增加发射功率。
2、接收程序不判断收到的数据的返回值是否有效了。另外,把2秒检查一次信号接收,改成4秒检查一次。
如果这样还不能解决,再优化重新发送次数,把3次变10次。当然需要晚上在实践一下。
最后的大杀器,就是接收端,更换大功率带放大的NRF24模块,这种模块都是号称空旷地带2000米的。


2楼是电路图和源代码。
我接收程序里面留有调试信息
波特率115200,打开串口助手就收到。


我把接收器收到的数据通过串口发出来。
是6位数据,前2位是发射器的电池电压,单位是mV。
第3位是发射器的功率(0x21最小,然后0x23,0x25,0x27最大)
第4位是发射信号的序号(从1~10)
第5位和第6位是发射器的用户编码,从0~65535
我这里只做了1对1的,没做1对N的,所以我用来输出接收器的电池电压了,
单位也是mV。


0F 10 21 06 0F 39

以后可能会做带OLED显示屏的高端点的版本
会把这些东西,通过液晶屏显示出来。

还有就是特别重要的烧录,非常重要:



最下面的:在程序区的结尾处添加重要测试参数
这个一定要打勾。
因为我计算电池电压,用的基准电压是单片机的内部BandGap电压。
如果不打这个勾,算出的电压肯定不对。
具体的计算方式,社区有个精华贴,我也是从中学习到的知识。


然后就是电路图,我知道有人不想下载,所以我转了图片,看看就好。
发射器电路图:



接收器电路图:



PDF格式的电路图:





修改了2个小bug

最后就是源代码:






烧录的hex文件,在源代码里面。你也可以用下面的源代码生成。

忘了占楼了,都放到一起算了 。
这里说一下操作方式。

发射器
--------------------------------------
用的时候,打开电源开关就可以了,不用就关掉。
持续工作时间100小时以上吧。
有个LED,我用蓝灯
大概1秒闪烁一次,每次亮1秒,暗1秒的闪烁。
不管数据是否成功发射,都会闪烁。
主要就是提醒你,发射器在工作。

电池电压低压3.8V的时候,蓝灯会常亮,但是因为单片机工作在掉电模式
大概每次唤醒的时候,会有很短暂的闪烁一下,但是基本就算常亮了。

充电的时候,有个红色LED会亮起,充满后红色LED会熄灭,表示充电结束。
我懒的加个绿色LED表示充满了。

-----------------------------------------------------------------------------------------

接收器:
接收器打开电源开关后,默认是遛狗模式。
也就是收不到接收器信号,蜂鸣器就会叫个0.5秒报警。
长按微动开关,3秒以上,
蓝、黄、红,3个LED都会同时点亮,蜂鸣器持续鸣叫。
松开微动开关,就切换到了寻狗模式。

寻狗模式下,收不到接收器的信号,蜂鸣器不叫,每次收到信号会鸣叫0.5秒。

在寻狗模式下,再长按微动开关3秒以上,就又切换到遛狗模式。
或者直接关掉电源再重新打开,默认就是遛狗模式。

微动开关,短按一下,蜂鸣器会叫一下,什么作用都没有。就是让你知道是接收器开机了而已。

接收器电池电压低于3.8V,红色的LED闪烁。
发射器电池电压低于3.8V,黄色的LED闪烁。

-------------------------------------------------------------------------------

为了方便大家,代码我也直接粘出来吧。

发射器代码:
/******************************************/
//
// 发射程序,主频11.0592MHz
//
/******************************************/
#include <reg51.h>
#include <intrins.h>

#define uchar unsigned char
#define uint  unsigned int
#define u8 unsigned char
#define u16  unsigned int

#define ID_ADDR_ROM 0x1ff7//8K程序空间的MCU

//* STC15W408AS寄存器补充
sfr AUXR1 = 0XA2;
sfr AUXR = 0X8E;
sfr TH2 = 0XD6;
sfr TL2 = 0XD7;
sfr P4 = 0xC0;
sfr P5 = 0xC8;

sfr SPSTAT=0xCD;
sfr SPCTL=0xCE;
sfr SPDAT=0xCF;
sfr P1M1=0x91;
sfr P1M0=0x92;
sfr P2M1=0x95;
sfr P2M0=0x96;
sfr P3M1=0xB1;
sfr P3M0=0xB2;
sfr P5M1=0xC9;
sfr P5M0=0xCA;


//定时器2
sfr T2H=0xD6;
sfr T2L=0xD7;
sfr IE2=0xAF;

//ADC
sfr ADC_CONTR=0xBC;    //带AD系列
sfr ADC_RES=0xBD;    //带AD系列
sfr ADC_RESL=0xBE;    //带AD系列
sfr P1ASF=0x9D;
sfr PCON2=0x97;

//掉电模式定时器
sfr WKTCL=0xAA;
sfr WKTCH=0xAB;

#define ADC_POWER    0x80//ADC电源控制位
#define ADC_FLAG    0x10//ADC完成标志
#define ADC_START    0x08//ADC起始控制位
#define ADC_SPEEDLL    0x00//540个时钟转换一次
#define ADC_SPEEDL    0x20//360时钟
#define ADC_SPEEDH    0x40//180时钟
#define ADC_SPEEDHH    0x60//90时钟


//下面是存储用的寄存器定义
sfr WDT_CONTR = 0xC1;
sfr IAP_DATA  = 0xC2;
sfr IAP_ADDRH = 0xC3;
sfr IAP_ADDRL = 0xC4;
sfr IAP_CMD   = 0xC5;
sfr IAP_TRIG  = 0xC6;
sfr IAP_CONTR = 0xC7;


/**********  NRF24L01寄存器操作命令  ***********/
#define READ_REG        0x00  //读配置寄存器,低5位为寄存器地址
#define 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  //空操作,可以用来读状态寄存器    
/**********  NRF24L01寄存器地址   *************/
#define CONFIG          0x00  //配置寄存器地址                            
#define EN_AA           0x01  //使能自动应答功能
#define EN_RXADDR       0x02  //接收地址允许
#define SETUP_AW        0x03  //设置地址宽度(所有数据通道)
#define SETUP_RETR      0x04  //建立自动重发
#define RF_CH           0x05  //RF通道
#define RF_SETUP        0x06  //RF寄存器
#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有效数据宽度(1~32字节)
#define RX_PW_P1        0x12  // 接收数据通道1有效数据宽度(1~32字节)
#define RX_PW_P2        0x13  // 接收数据通道2有效数据宽度(1~32字节)
#define RX_PW_P3        0x14  // 接收数据通道3有效数据宽度(1~32字节)
#define RX_PW_P4        0x15  // 接收数据通道4有效数据宽度(1~32字节)
#define RX_PW_P5        0x16  // 接收数据通道5有效数据宽度(1~32字节)
#define FIFO_STATUS     0x17  // FIFO状态寄存器
/*————————————————————————————————————————————————————————————————————*/

/******   STATUS寄存器bit位定义      *******/
#define MAX_TX      0x10  //达到最大发送次数中断
#define TX_OK       0x20  //TX发送完成中断
#define RX_OK       0x40  //接收到数据中断
/*——————————————————————————————————————————————————*/

/*********     24L01发送接收数据宽度定义      ***********/
#define TX_ADR_WIDTH    5   //5字节地址宽度
#define RX_ADR_WIDTH    5   //5字节地址宽度
#define TX_PLOAD_WIDTH  6  //32字节有效数据宽度
#define RX_PLOAD_WIDTH  6  //32字节有效数据宽度

/*************************/

const uchar TX_ADDRESS[TX_ADR_WIDTH]={0x66,0x77,0x88,0x99,0xaa};
const uchar RX_ADDRESS[RX_ADR_WIDTH]={0x66,0x77,0x88,0x99,0xaa};

sbit NRF_IRQ = P5^5;
sbit NRF_MISO = P1^4;
sbit NRF_MOSI = P1^3;
sbit NRF_SCK = P1^5;
sbit NRF_CSN = P1^2;
sbit NRF_CE = P5^4;

sbit LED_NRF=P1^1;//NRF模块信号不好报警灯

//数据格式:buf0:1 电池电压  buf2 发射功率  buf3 序号(1~10) buf4:5 用户序号(0~65535),默认0
u8 buf[6]={0,0,0,0,0,0};//发射数据
u8 rfpwr=0x21;//发射默认最小功率
u8 cnt1=0;//发射成功次数
u8 cnt2=0;//发射总次数

u16 bandgap;//bandgap预储存校准值,单位毫伏
u16    adc0,adc9;//测量第九通道(bandgap)值
u16 Voltag=0;//电池电压

/***************/
/* 延时函数    */
/***************/
void Delay1ms()        //@11.0592MHz
{
    unsigned char i, j;

    _nop_();
    _nop_();
    _nop_();
    i = 11;
    j = 190;
    do
    {
        while (--j);
    } while (--i);
}


void delay_ms(unsigned char i)
{
    while(i--)
    {
        Delay1ms();
    }
}


/**********************/
/* 初始化硬件SPI口    */
/**********************/
void SPI_Init(void)
{
    SPSTAT |= 0XC0;
    SPCTL = 0XD0;
}

/**********************/
/* SPI数据收发函数    */
/**********************/
uchar SPI_RW(uchar tr_data)
{
    uchar i=0;

    SPSTAT |= 0Xc0; // 清高两位,
    SPDAT=tr_data;
    while(((SPSTAT&0X80)!=0X80)&&(i<20))
    {
        i++;
        delay_ms(1);
    }
    return SPDAT;
}


//// 下面是“模拟SPI”
// uchar SPI_RW(uchar byte)
// {
//     uchar bit_ctr;
//     for(bit_ctr=0;bit_ctr<8;bit_ctr++) // 输出8位
//     {
//         NRF_MOSI=(byte&0x80); // MSB TO MOSI
//         byte=(byte<<1);    // shift next bit to MSB
//         NRF_SCK=1;
//         byte|=NRF_MISO;            // capture current MISO bit
//         NRF_SCK=0;
//     }
//     return byte;
// }


/*********************************************/
/* 函数功能:给24L01的寄存器写值(一个字节) */
/* 入口参数:reg   要写的寄存器地址          */
/*           value 给寄存器写的值            */
/* 出口参数:status 状态值                   */
/*********************************************/
uchar NRF24L01_Write_Reg(uchar reg,uchar value)
{
    uchar status;

    NRF_CSN=0;                  //CSN=0;  
      status = SPI_RW(reg);//发送寄存器地址,并读取状态值
    SPI_RW(value);
    NRF_CSN=1;                  //CSN=1;

    return status;
}
/*************************************************/
/* 函数功能:读24L01的寄存器值 (一个字节)      */
/* 入口参数:reg  要读的寄存器地址               */
/* 出口参数:value 读出寄存器的值                */
/*************************************************/
uchar NRF24L01_Read_Reg(uchar reg)
{
     uchar value;

    NRF_CSN=0;              //CSN=0;  
      SPI_RW(reg);//发送寄存器值(位置),并读取状态值
    value = SPI_RW(NOP);
    NRF_CSN=1;             //CSN=1;

    return value;
}
/*********************************************/
/* 函数功能:读24L01的寄存器值(多个字节)   */
/* 入口参数:reg   寄存器地址                */
/*           *pBuf 读出寄存器值的存放数组    */
/*           len   数组字节长度              */
/* 出口参数:status 状态值                   */
/*********************************************/
uchar NRF24L01_Read_Buf(uchar reg,uchar *pBuf,uchar len)
{
    uchar status,u8_ctr;
    NRF_CSN=0;                   //CSN=0      
      status=SPI_RW(reg);//发送寄存器地址,并读取状态值          
     for(u8_ctr=0;u8_ctr<len;u8_ctr++)
    pBuf[u8_ctr]=SPI_RW(0XFF);//读出数据
    NRF_CSN=1;                 //CSN=1
      return status;        //返回读到的状态值
}
/**********************************************/
/* 函数功能:给24L01的寄存器写值(多个字节)  */
/* 入口参数:reg  要写的寄存器地址            */
/*           *pBuf 值的存放数组               */
/*           len   数组字节长度               */
/**********************************************/
uchar NRF24L01_Write_Buf(uchar reg, uchar *pBuf, uchar len)
{
    uchar status,u8_ctr;
    NRF_CSN=0;
      status = SPI_RW(reg);//发送寄存器值(位置),并读取状态值
      for(u8_ctr=0; u8_ctr<len; u8_ctr++)
    SPI_RW(*pBuf++); //写入数据
    NRF_CSN=1;
      return status;          //返回读到的状态值
}                                                    

/*********************************************/
/* 函数功能:24L01接收数据                   */
/* 入口参数:rxbuf 接收数据数组              */
/* 返回值: 0   成功收到数据                 */
/*          1   没有收到数据                 */
/*********************************************/
//uchar NRF24L01_RxPacket(uchar *rxbuf)
//{
//    uchar state;
//    
//    state=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值        
//    NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
//    if(state&RX_OK)//接收到数据
//    {
//        NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
//        NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
//        return 0;
//    }      
//    return 1;//没收到任何数据
//}
/**********************************************/
/* 函数功能:设置24L01为发送模式              */
/* 入口参数:txbuf  发送数据数组              */
/* 返回值; 0x10    达到最大重发次数,发送失败*/
/*          0x20    成功发送完成              */
/*          0xff    发送失败                  */
/**********************************************/
uchar NRF24L01_TxPacket(uchar *txbuf)
{
    uchar state;
//    uchar i;

    NRF_CE=0;//CE拉低,使能24L01配置
      NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
     NRF_CE=1;//CE置高,使能发送      
    while(NRF_IRQ==1);//等待发送完成
    state=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值      
    NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
    if(state&MAX_TX)//达到最大重发次数
    {
        NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
        return MAX_TX;
    }
    if(state&TX_OK)//发送完成
    {
        return TX_OK;
    }
    return 0xff;//发送失败
}
/********************************************/
/* 函数功能:检测24L01是否存在              */
/* 返回值;  0  存在                        */
/*           1  不存在                      */
/********************************************/
uchar NRF24L01_Check(void)
{
    uchar check_in_buf[5]={0x11,0x22,0x33,0x44,0x55};
    uchar check_out_buf[5]={0x00};

    NRF_CE=0;

    NRF24L01_Write_Buf(WRITE_REG+TX_ADDR, check_in_buf, 5);

    NRF24L01_Read_Buf(READ_REG+TX_ADDR, check_out_buf, 5);

    if((check_out_buf[0] == 0x11)&&\
       (check_out_buf[1] == 0x22)&&\
       (check_out_buf[2] == 0x33)&&\
       (check_out_buf[3] == 0x44)&&\
       (check_out_buf[4] == 0x55))return 0;
    else return 1;
}    

//void NRF24L01_SET_TX_Mode(void)
//{
//    NRF_CE=0;
//    NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0a); // 配置为无线发送模式
//    NRF_CE=1;
//}

//void NRF24L01_SET_RX_Mode(void)
//{
//    NRF_CE=0;
//    NRF24L01_Write_Reg(WRITE_REG+CONFIG, 0x3b); // 配置为无线接收模式
//    NRF_CE=1;
//}

void NRF24L01_SET_RF_PWR(uchar rf)    //设置功率      
{
    NRF_CE=0;
    NRF24L01_Write_Reg(WRITE_REG+RF_SETUP, rf); //250kbps速率下,0x27:0dBm 0x25:-6dBm 0x23:-12dBm 0x21:-18dBm
    NRF_CE=1;
}

void NRF24L01_SET_PWR_DOWN(void)    //掉电模式
{
    NRF_CE=0;
    NRF24L01_Write_Reg(WRITE_REG+CONFIG, 0x08);
    NRF_CE=1;
}
void NRF24L01_Init(void)
{    
    NRF_CE=0;//进行配置,要首先禁止模块功能          
      NRF24L01_Write_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
    NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
    NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
      NRF24L01_Write_Buf(WRITE_REG+TX_ADDR,(uchar*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
      NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(uchar*)RX_ADDRESS,RX_ADR_WIDTH); //设置RX节点地址,主要为了使能ACK      
     NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    
      NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
      NRF24L01_Write_Reg(WRITE_REG+SETUP_RETR,0x13);//使能自动重发;最大自动重发次数:3次
      NRF24L01_Write_Reg(WRITE_REG+RF_CH,100);       //设置RF通道为100,通道总数125
      //NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x21);  //-18dBm增益,250kbps     最小功率运行
      NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0a);    //配置基本工作模式的参数;PWR_UP,EN_CRC,8BIT_CRC,发送模式,开启所有中断
    NRF_CE=1;    //CE置高
}


u16 Get_ADC10bitResult(u8 channel) //获取10位精度的ADC值
{
    u16    adc;
    if(channel > 7)    return    1024;    //错误,返回1024, ADC通道号0~7,不能超过7    
    ADC_RES = 0; //高位ADC清零
    ADC_RESL = 0;//低位ADC清零

    ADC_CONTR = ADC_POWER|ADC_SPEEDLL| ADC_START | channel; //打开指定通道的ADC转换
    _nop_();
    _nop_();
    _nop_();
    _nop_(); //对ADC_CONTR操作后要4T之后才能访问

    while (!(ADC_CONTR & ADC_FLAG));//等待ADC转换完成
    ADC_CONTR &= ~ADC_FLAG;    //ADC_FLAG手工清0 ,关闭 ADC

    adc=ADC_RES;      //得到高8位
    adc<<=2;
    adc+=ADC_RESL;     //得到低2位

    return    adc;
}

void GetBandGap()
{
    u8 code *cptr; //定义ROM(代码)区指针
    cptr = ID_ADDR_ROM;//从程序区读取BandGap电压值(单位:毫伏mV)
    bandgap=*cptr++;
    bandgap<<=8;
    bandgap+=*cptr;
}
/**********************************************/
void main(void)
{
    LED_NRF=0;//关掉报警灯
    P1M0=0x02;//打开强推挽
     SPI_Init(); // 初始化硬件SPI口
    while(NRF24L01_Check())     // 等待检测到NRF24L01,程序才会向下执行
    {
       LED_NRF=1;//状态LED灯常亮表示NRF24L01模块出错。
    }
    GetBandGap();//得到内部基准电压
    WKTCL=0x00;//掉电唤醒定时器    
    WKTCH=0x88;//0x0800=2048=1S  第一位的8表示打开掉电定时器
      //这里的1S并不是很准确,需要准确时间的话,需要读取程序空间的的唤醒定时器的频率,然后重新计算
    //但是没必要追求准确的时间,大概值就好了,所以不去读取和计算了。
    while(1)
    {
        PCON=0x02;//进入掉电模式
        _nop_();//等待MCU稳定
        _nop_();
        P1ASF = 0x00;//0x00表示测量第九通道bandgap的adc值,也就是单片机内部基准电压的值
        adc9=Get_ADC10bitResult(0);//测量第九通道bandgap的adc值,也就是单片机内部基准电压的值
        P1ASF = 0x01;//打开P1.0的ADC转换功能
        adc0=Get_ADC10bitResult(0);//得到电池电压的adc0
        Voltag=(float)bandgap*adc0*2/adc9; //用了2个470K的电阻分压,所以*2即可
        //Voltag=(float)bandgap*1024/adc9;供电电压
        if(Voltag<3800)LED_NRF=0;//小于3.8V报警。3.7V基本剩下10%的电
        //下面要反转LED灯状态,所以0就是常亮,会稍微闪一下。
        buf[0]=Voltag>>8; //buf[0]和buf[1]存放电池电压值,单位mv
        buf[1]=Voltag;    
        //当nRF2401进入掉电模式后,配置字会丢失,在设备想进入任意激活模式前,都必须重新配置,
        //一旦重新配置好后,设备可以直接从掉电模式过渡到你需要的激活模式
        NRF24L01_Init(); //初始化
        //NRF24L01_SET_TX_Mode();//发射模式
        NRF24L01_SET_RF_PWR(rfpwr);//设置功率
        buf[2]=rfpwr;//buf2是发射功率
        if(cnt2==10)
        {
            if(cnt1==10)//10次都成功,减少发射功率省电
                if(rfpwr>0x21)rfpwr-=0x02;//0x21功率最小
            if(cnt1<10)
                if(rfpwr<0x27)rfpwr+=0x02;//只要有1次失败,就增加发射功率    0x27功率最大
            cnt2=0;//重新计数
            cnt1=0;
        }
        cnt2++;//发射总次数    
        buf[3]=cnt2;//发射序号,从1~10循环
        if(NRF24L01_TxPacket(buf)==TX_OK) cnt1++;//发射成功,发射成功次数+1
        //设置最大重发次数时,如果禁止重发,那么返回值会始终会MAX_TX,而不是TX_OK
        //所以我上面使能了自动重发,并且设置了3次重发次数。
        LED_NRF=!LED_NRF; //无论发射成功与否,都闪烁LED灯,LED灯闪烁,可以表示设备在工作状态。
        NRF24L01_SET_PWR_DOWN();//发射完成后,进入掉电模式省电
    }
}




接收器代码:

/******************************************/
//
// 接收程序,主频11.0592MHz
//
/******************************************/
#include <reg51.h>
#include <intrins.h>

#define uchar unsigned char
#define uint  unsigned int
#define u8 unsigned char
#define u16  unsigned int

#define ID_ADDR_ROM 0x1ff7//8K程序空间的MCU

//* STC15W408AS寄存器补充
sfr AUXR1 = 0XA2;
sfr AUXR = 0X8E;
sfr TH2 = 0XD6;
sfr TL2 = 0XD7;
sfr P4 = 0xC0;
sfr P5 = 0xC8;

sfr SPSTAT=0xCD;
sfr SPCTL=0xCE;
sfr SPDAT=0xCF;
sfr P1M1=0x91;
sfr P1M0=0x92;
sfr P2M1=0x95;
sfr P2M0=0x96;
sfr P3M1=0xB1;
sfr P3M0=0xB2;
sfr P5M1=0xC9;
sfr P5M0=0xCA;


//定时器2
sfr T2H=0xD6;
sfr T2L=0xD7;
sfr IE2=0xAF;

//ADC
sfr ADC_CONTR=0xBC;    //带AD系列
sfr ADC_RES=0xBD;    //带AD系列
sfr ADC_RESL=0xBE;    //带AD系列
sfr P1ASF=0x9D;
sfr PCON2=0x97;


#define ADC_POWER    0x80//ADC电源控制位
#define ADC_FLAG    0x10//ADC完成标志
#define ADC_START    0x08//ADC起始控制位
#define ADC_SPEEDLL    0x00//540个时钟转换一次
#define ADC_SPEEDL    0x20//360时钟
#define ADC_SPEEDH    0x40//180时钟
#define ADC_SPEEDHH    0x60//90时钟


//下面是存储用的寄存器定义
sfr WDT_CONTR = 0xC1;
sfr IAP_DATA  = 0xC2;
sfr IAP_ADDRH = 0xC3;
sfr IAP_ADDRL = 0xC4;
sfr IAP_CMD   = 0xC5;
sfr IAP_TRIG  = 0xC6;
sfr IAP_CONTR = 0xC7;


/**********  NRF24L01寄存器操作命令  ***********/
#define READ_REG        0x00  //读配置寄存器,低5位为寄存器地址
#define 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  //空操作,可以用来读状态寄存器    
/**********  NRF24L01寄存器地址   *************/
#define CONFIG          0x00  //配置寄存器地址                            
#define EN_AA           0x01  //使能自动应答功能
#define EN_RXADDR       0x02  //接收地址允许
#define SETUP_AW        0x03  //设置地址宽度(所有数据通道)
#define SETUP_RETR      0x04  //建立自动重发
#define RF_CH           0x05  //RF通道
#define RF_SETUP        0x06  //RF寄存器
#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有效数据宽度(1~32字节)
#define RX_PW_P1        0x12  // 接收数据通道1有效数据宽度(1~32字节)
#define RX_PW_P2        0x13  // 接收数据通道2有效数据宽度(1~32字节)
#define RX_PW_P3        0x14  // 接收数据通道3有效数据宽度(1~32字节)
#define RX_PW_P4        0x15  // 接收数据通道4有效数据宽度(1~32字节)
#define RX_PW_P5        0x16  // 接收数据通道5有效数据宽度(1~32字节)
#define FIFO_STATUS     0x17  // FIFO状态寄存器
/*————————————————————————————————————————————————————————————————————*/

/******   STATUS寄存器bit位定义      *******/
#define MAX_TX      0x10  //达到最大发送次数中断
#define TX_OK       0x20  //TX发送完成中断
#define RX_OK       0x40  //接收到数据中断
/*——————————————————————————————————————————————————*/

/*********     24L01发送接收数据宽度定义      ***********/
#define TX_ADR_WIDTH    5   //5字节地址宽度
#define RX_ADR_WIDTH    5   //5字节地址宽度
#define TX_PLOAD_WIDTH  6  //32字节有效数据宽度
#define RX_PLOAD_WIDTH  6  //32字节有效数据宽度

/*************************/

const uchar TX_ADDRESS[TX_ADR_WIDTH]={0x66,0x77,0x88,0x99,0xaa};
const uchar RX_ADDRESS[RX_ADR_WIDTH]={0x66,0x77,0x88,0x99,0xaa};

sbit NRF_IRQ = P5^5;
sbit NRF_MISO = P1^4;
sbit NRF_MOSI = P1^3;
sbit NRF_SCK = P1^5;
sbit NRF_CSN = P1^2;
sbit NRF_CE = P5^4;

sbit LED_NRF=P1^1;//NRF模块信号不好报警灯
sbit KEY1=P3^7;    
sbit LED_R=P3^6;
sbit LED_Y=P3^3;
sbit Bell=P3^2;    

u8 buf[6]={0,0,0,0,0,0};
//u8 rfpwr=0x27;//发射默认最大功率
u8 cnt1=0; //蓝色闪烁计数器
u16 cnt2=0;//接收数据时间超时计数器
u8 cnt3=0;//红灯,黄灯的闪烁计数器
u16 presscnt=0;//按键计时
bit Rev=0;
bit FindMode=0;//寻狗模式

u16 bandgap;//bandgap预储存校准值,单位毫伏
u16    adc0,adc9;//测量第九通道(bandgap)值
u16 bat1,bat2;//电池电压

/***************/
/* 延时函数    */
/***************/
void Delay1ms()        //@11.0592MHz
{
    unsigned char i, j;

    _nop_();
    _nop_();
    _nop_();
    i = 11;
    j = 190;
    do
    {
        while (--j);
    } while (--i);
}


void delay_ms(unsigned int i)
{
    while(i--)
    {
        Delay1ms();
    }
}


/**********************/
/* 初始化硬件SPI口    */
/**********************/
void SPI_Init(void)
{
    SPSTAT |= 0XC0;
    SPCTL = 0XD0;
}

/**********************/
/* SPI数据收发函数    */
/**********************/
uchar SPI_RW(uchar tr_data)
{
    uchar i=0;

    SPSTAT |= 0Xc0; // 清高两位,
    SPDAT=tr_data;
    while(((SPSTAT&0X80)!=0X80)&&(i<20))
    {
        i++;
        delay_ms(1);
    }
    return SPDAT;
}


// 下面是“模拟SPI”
// uchar SPI_RW(uchar byte)
// {
//     uchar bit_ctr;
//     for(bit_ctr=0;bit_ctr<8;bit_ctr++) // 输出8位
//     {
//         NRF_MOSI=(byte&0x80); // MSB TO MOSI
//         byte=(byte<<1);    // shift next bit to MSB
//         NRF_SCK=1;
//         byte|=NRF_MISO;            // capture current MISO bit
//         NRF_SCK=0;
//     }
//     return byte;
// }


/*********************************************/
/* 函数功能:给24L01的寄存器写值(一个字节) */
/* 入口参数:reg   要写的寄存器地址          */
/*           value 给寄存器写的值            */
/* 出口参数:status 状态值                   */
/*********************************************/
uchar NRF24L01_Write_Reg(uchar reg,uchar value)
{
    uchar status;

    NRF_CSN=0;                  //CSN=0;  
      status = SPI_RW(reg);//发送寄存器地址,并读取状态值
    SPI_RW(value);
    NRF_CSN=1;                  //CSN=1;

    return status;
}
/*************************************************/
/* 函数功能:读24L01的寄存器值 (一个字节)      */
/* 入口参数:reg  要读的寄存器地址               */
/* 出口参数:value 读出寄存器的值                */
/*************************************************/
uchar NRF24L01_Read_Reg(uchar reg)
{
     uchar value;

    NRF_CSN=0;              //CSN=0;  
      SPI_RW(reg);//发送寄存器值(位置),并读取状态值
    value = SPI_RW(NOP);
    NRF_CSN=1;             //CSN=1;

    return value;
}
/*********************************************/
/* 函数功能:读24L01的寄存器值(多个字节)   */
/* 入口参数:reg   寄存器地址                */
/*           *pBuf 读出寄存器值的存放数组    */
/*           len   数组字节长度              */
/* 出口参数:status 状态值                   */
/*********************************************/
uchar NRF24L01_Read_Buf(uchar reg,uchar *pBuf,uchar len)
{
    uchar status,u8_ctr;
    NRF_CSN=0;                   //CSN=0      
      status=SPI_RW(reg);//发送寄存器地址,并读取状态值          
     for(u8_ctr=0;u8_ctr<len;u8_ctr++)
    pBuf[u8_ctr]=SPI_RW(0XFF);//读出数据
    NRF_CSN=1;                 //CSN=1
      return status;        //返回读到的状态值
}
/**********************************************/
/* 函数功能:给24L01的寄存器写值(多个字节)  */
/* 入口参数:reg  要写的寄存器地址            */
/*           *pBuf 值的存放数组               */
/*           len   数组字节长度               */
/**********************************************/
uchar NRF24L01_Write_Buf(uchar reg, uchar *pBuf, uchar len)
{
    uchar status,u8_ctr;
    NRF_CSN=0;
      status = SPI_RW(reg);//发送寄存器值(位置),并读取状态值
      for(u8_ctr=0; u8_ctr<len; u8_ctr++)
    SPI_RW(*pBuf++); //写入数据
    NRF_CSN=1;
      return status;          //返回读到的状态值
}                                                    

/*********************************************/
/* 函数功能:24L01接收数据                   */
/* 入口参数:rxbuf 接收数据数组              */
/* 返回值: 0   成功收到数据                 */
/*          1   没有收到数据                 */
/*********************************************/
uchar NRF24L01_RxPacket(uchar *rxbuf)
{
    uchar state;

    state=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值        
    NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
    if(state&RX_OK)//接收到数据
    {
        NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
        NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
        return 0;
    }      
    return 1;//没收到任何数据
}
/**********************************************/
/* 函数功能:设置24L01为发送模式              */
/* 入口参数:txbuf  发送数据数组              */
/* 返回值; 0x10    达到最大重发次数,发送失败*/
/*          0x20    成功发送完成              */
/*          0xff    发送失败                  */
/**********************************************/
//uchar NRF24L01_TxPacket(uchar *txbuf)
//{
//    uchar state;
//  
//    NRF_CE=0;//CE拉低,使能24L01配置
//      NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
//     NRF_CE=1;//CE置高,使能发送      
//    while(NRF_IRQ==1);//等待发送完成
//    state=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值      
//    NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
//    if(state&MAX_TX)//达到最大重发次数
//    {
//        NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
//        return MAX_TX;
//    }
//    if(state&TX_OK)//发送完成
//    {
//        return TX_OK;
//    }
//    return 0xff;//发送失败
//}
/********************************************/
/* 函数功能:检测24L01是否存在              */
/* 返回值;  0  存在                        */
/*           1  不存在                      */
/********************************************/
uchar NRF24L01_Check(void)
{
    uchar check_in_buf[5]={0x11,0x22,0x33,0x44,0x55};
    uchar check_out_buf[5]={0x00};

    NRF_CE=0;

    NRF24L01_Write_Buf(WRITE_REG+TX_ADDR, check_in_buf, 5);

    NRF24L01_Read_Buf(READ_REG+TX_ADDR, check_out_buf, 5);

    if((check_out_buf[0] == 0x11)&&\
       (check_out_buf[1] == 0x22)&&\
       (check_out_buf[2] == 0x33)&&\
       (check_out_buf[3] == 0x44)&&\
       (check_out_buf[4] == 0x55))return 0;
    else return 1;
}    

//void NRF24L01_SET_TX_Mode(void)
//{
//    NRF_CE=0;
//    NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0a); // 配置为无线发送模式
//    NRF_CE=1;
//}
//
//void NRF24L01_SET_RX_Mode(void)
//{
//    NRF_CE=0;
//    NRF24L01_Write_Reg(WRITE_REG+CONFIG, 0x3b); // 配置为无线接收模式
//    NRF_CE=1;
//}
//
//void NRF24L01_SET_RF_PWR(uchar rf)    //设置功率      
//{
//    NRF_CE=0;
//    NRF24L01_Write_Reg(WRITE_REG+RF_SETUP, rf); //250kbps速率下,0x27:0dBm 0x25:-6dBm 0x23:-12dBm 0x21:-18dBm
//    NRF_CE=1;
//}
//
//void NRF24L01_SET_PWR_DOWN(void)    //掉电模式
//{
//    NRF_CE=0;
//    NRF24L01_Write_Reg(WRITE_REG+CONFIG, 0x08);
//    NRF_CE=1;
//}

void NRF24L01_Init(void)
{    
    NRF_CE=0;//进行配置,要首先禁止模块功能          
      NRF24L01_Write_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
    NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
    NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
      NRF24L01_Write_Buf(WRITE_REG+TX_ADDR,(uchar*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
      NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(uchar*)RX_ADDRESS,RX_ADR_WIDTH); //设置RX节点地址,主要为了使能ACK      
     NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    
      NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
      NRF24L01_Write_Reg(WRITE_REG+SETUP_RETR,0x13);//使能自动重发;最大自动重发次数:3次
      NRF24L01_Write_Reg(WRITE_REG+RF_CH,100);       //设置RF通道为100,通道总数125
      NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x27);  //-18dBm增益,250kbps     最大功率运行
      NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x3b);    //配置基本工作模式的参数;PWR_UP,EN_CRC,8BIT_CRC,接收模式,开启所有中断
    NRF_CE=1;    //CE置高
}


void Timer0Init(void)        //5毫秒@11.0592MHz
{
    AUXR |= 0x80;        //定时器时钟1T模式
    TMOD &= 0xF0;        //设置定时器模式
    TL0 = 0x00;        //设置定时初值
    TH0 = 0x28;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0=1;
    EA=1;
}

void UartInit(void)        //115200bps@11.0592MHz
{
    SCON = 0x50;        //8位数据,可变波特率
    AUXR |= 0x01;        //串口1选择定时器2为波特率发生器
    AUXR |= 0x04;        //定时器2时钟为Fosc,即1T
    T2L = 0xE8;        //设定定时初值
    T2H = 0xFF;        //设定定时初值
    AUXR |= 0x10;        //启动定时器2
}

/******************串口发送数据函数********************/
void SendData(u8 data_buf) //发送一个字符
{
    SBUF = data_buf;
    while(!TI);//TI是发送成功标志
    TI = 0;
}

u16 Get_ADC10bitResult(u8 channel) //获取10位精度的ADC值
{
    u16    adc;
    if(channel > 7)    return    1024;    //错误,返回1024, ADC通道号0~7,不能超过7    
    ADC_RES = 0; //高位ADC清零
    ADC_RESL = 0;//低位ADC清零

    ADC_CONTR = ADC_POWER|ADC_SPEEDLL| ADC_START | channel; //打开指定通道的ADC转换
    _nop_();
    _nop_();
    _nop_();
    _nop_(); //对ADC_CONTR操作后要4T之后才能访问

    while (!(ADC_CONTR & ADC_FLAG));//等待ADC转换完成
    ADC_CONTR &= ~ADC_FLAG;    //ADC_FLAG手工清0 ,关闭 ADC

    adc=ADC_RES;      //得到高8位
    adc<<=2;
    adc+=ADC_RESL;     //得到低2位

    return    adc;
}

void GetBandGap()
{
    u8 code *cptr; //定义ROM(代码)区指针
    cptr = ID_ADDR_ROM;//从程序区读取BandGap电压值(单位:毫伏mV)
    bandgap=*cptr++;
    bandgap<<=8;
    bandgap+=*cptr;
}
void Beep(u8 i)
{
   while(i--)
   {
       Bell=1;
       delay_ms(250);
       Bell=0;
       delay_ms(250);
   }
}


/**********************************************/
void main(void)
{
    u8 i;
    LED_NRF=0;//关掉报警灯
    LED_R=0;
    LED_Y=0;
    Bell=0;
    P1M0=0x02;//蓝灯强推挽
    P3M0=0x4C;//红灯,黄灯,蜂鸣器强推挽
    GetBandGap();//得到内部基准电压
    UartInit();
     SPI_Init(); // 初始化SPI口
    while(NRF24L01_Check())     // 等待检测到NRF24L01,程序才会向下执行
    {
       LED_NRF=1;//状态LED灯常亮表示NRF24L01模块出错。
    }
    NRF24L01_Init(); //初始化
    Timer0Init();
    while (1)
    {
        if(NRF_IRQ==0)//收到数据
        {
            NRF24L01_RxPacket(buf); // 无线接收数据
            //数据格式:buf0:1 发射器电池电压  buf2 发射器发射功率  buf3 发射数据序号(1~10) buf4:5 用户序号(0~65535),默认0
            LED_NRF=1;
            cnt1=0;
            Rev=1;
            bat2=buf[0];
            bat2<<=8;
            bat2+=buf[1];
            if(bat2<3800)LED_Y=1;//发射器电压不足报警

            buf[4]=bat1>>8;//串口输出电池电压
            buf[5]=bat1;
            for(i=0;i<6;i++)//输出调试串口数据
            {
                SendData(buf);
            }
            if(FindMode==1)Bell=1; //寻狗模式中,收到信号就叫
        }

        if(cnt1==100)LED_NRF=0;//蓝灯闪烁0.5秒

        if(cnt2>800&&FindMode==0)//4秒内没收到数据报警
        {
            if(Rev==0)Bell=1;//没有收到一次数据就报警
            else Rev=0;    //收到数据则重置接收
            cnt2=0;//2秒重新计时
        }
        if(cnt2==100)Bell=0;//报警响0.5秒

        if(cnt3==100)
        {
            LED_R=0;//红灯快闪
            LED_Y=0;//黄灯快闪
        }
        if(cnt3==200)//1秒检查一次电池的电压
        {
            P1ASF = 0x00;//0x00表示测量第九通道bandgap的adc值,也就是单片机内部基准电压的值
            adc9=Get_ADC10bitResult(0);//测量第九通道bandgap的adc值,也就是单片机内部基准电压的值
            P1ASF = 0x01;//打开P1.0的ADC转换功能
            adc0=Get_ADC10bitResult(0);//得到电池电压的adc0
            bat1=(float)bandgap*adc0*2/adc9; //用了2个470K的电阻分压,所以*2即可
            //bat1=(float)bandgap*1024/adc9;//供电电压
            if(bat1<3800)LED_R=1;//电压不足,红灯报警
            cnt3=0;
        }

        if(KEY1==0)     //按KEY1
        {
            delay_ms(10);//10ms防抖
            presscnt=0;//按键计时清零

            while(KEY1==0)
            {
                 LED_NRF=0;
                LED_R=0;
                LED_Y=0;
                Bell=0;

                delay_ms(10);//每次延时10ms
                 presscnt++;
                if(presscnt>=300)//长按,按下时间大于等于3秒
                {
                    //切换报警和寻狗模式
                    if(FindMode==0)
                    {
                        FindMode=1;//打开寻狗模式
                        while(KEY1==0)//等待松开按键
                        {
                            Bell=1;
                            LED_NRF=1;
                            LED_R=1;
                            LED_Y=1;
                        }
                        LED_NRF=0;
                        LED_R=0;
                        LED_Y=0;
                        Bell=0;
                        delay_ms(1000);
                    }
                    else
                    {
                        FindMode=0;//打开报警模式
                        while(KEY1==0)//等待松开按键
                        {
                            Beep(1);
                            LED_NRF=1;
                            LED_R=1;
                            LED_Y=1;
                        }
                        LED_NRF=0;
                        LED_R=0;
                        LED_Y=0;
                        delay_ms(1000);
                    }
                }
            }
            if(presscnt<200)//短按,小于1秒,切换功率
            {
                Beep(1);//短按则什么功能也没有
                //本来打算做功率切换的,不做了。以后做带液晶屏的大功率版本再说吧
            }
        }

    }
}

//定时器0
void Timer0() interrupt 1  //5毫秒
{
    cnt1++;
    cnt2++;
    cnt3++;
}
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共43条打赏M币+142
行了吧 M币 +1 聪明,厉害!特以个人私币奖励,别嫌少喔! 前天 20:40
luohaiyong M币 +8 狗狗的眼神好无辜 03-11
wenxueroom M币 +8 回头连狗带gps都丢了 哈哈 03-06
塔里木水手 M币 +1 優秀文章 02-21
mjgzdnw M币 +3 優秀文章 01-26
yiyiren M币 +3 謝謝分享 2016-09-05
latadawang M币 +3 認真發帖 2016-09-04
jfttdgn M币 +3 優秀文章 2016-09-04
xiaoyao_mdj M币 +3 優秀文章 2016-09-02
链接 M币 +10 優秀文章 2016-09-01
离线jackey1

发帖
6439
M币
960
专家
2
粉丝
17
只看该作者 3楼 发表于: 2016-08-08
大神们遛狗方式都科技化了
本帖最近打赏记录:共2条打赏M币+2
boldsun M币 +1 精彩回帖 2016-08-20
unkow M币 +1 - 2016-08-20
离线nokia5320

发帖
7506
M币
3736
专家
8
粉丝
145
只看该作者 4楼 发表于: 2016-08-08
单身狗岂不是狗带了
本帖最近打赏记录:共1条打赏M币+1
qq542731976 M币 +1 哈哈, 那只能两个多带在自己身上了~~ 2016-08-21
离线kelvinwong

发帖
12
M币
3
专家
0
粉丝
6
只看该作者 5楼 发表于: 2016-08-08
牛掰。。。很实用 打算搞一个
离线四个冬季

发帖
64
M币
-347
专家
1
粉丝
1
只看该作者 6楼 发表于: 2016-08-08
不赖不赖,我是来接收成果的,有空做一个给我的狗戴上,2,8月总是找,唉,累人。有了这个就好了😁
离线llcc
发帖
130
M币
2686
专家
7
粉丝
6
只看该作者 7楼 发表于: 2016-08-08
谢谢分享,好创意。

内容来自Android手机客户端

离线小切

发帖
125
M币
1691
专家
1
粉丝
15
只看该作者 8楼 发表于: 2016-08-08
感谢分享!
离线yjgg

发帖
1476
M币
4061
专家
1
粉丝
6
只看该作者 9楼 发表于: 2016-08-08
優秀文章,謝謝分享
快速回复
限80 字节
“新手上路”发帖需审核后才能显示(请认真发帖),达到数码9级后取消此限制
 
上一个 下一个