nrf24l01之python

来源:互联网 发布:腰部按摩器 知乎 编辑:程序博客网 时间:2024/06/05 03:57

一、
无线通信模块nrf24l01采用2.4G技术,同样,蓝牙和wifi模块也是采用的2.4G技术,只是后者在技术的基础之上做了扩展,封装更高,那么我们在做通信的时候,如果只是单纯想完成两个设备之间的通信,我的建议是使用nrf24l01模块*2。之前做过蓝牙模块之间的通信,其优点在于有指定的指令集,集成度高,操作起来十分方便,但是传输速度不快,质量不可靠,实时性不高,所以需要nrf24l01。

二、
在openmv上编写nrf24l01的发送代码,Python。
openmv是用于做图像处理的一个开源项目,将摄像头和一块stm32芯片集成在一起,通过python语言来完成对单片机的控制,以及调用内部的库函数来完成图像处理的部分内容。
首先得知道nrf24l01是如何完成通信的。
1、nrf24l01需要与单片机正常通信,这是通过spi总线来完成的,四根信号线,mosi、miso、sck、cs,注意,并不是nrf24l01上的mosi与单片机的miso连接,这里我在连线的时候是有过犹豫的,之后通过实践证明以及百度,证实了mosi与mosi相连,miso与miso相连。
nrf24l01与单片机是通过spi串行通信,但具体如何完成通信,这涉及到spi协议,网上资料还是很多的,在此是调用了openmv的库内的spi相关函数。调用方法点击跳转:
http://docs.openmv.io/library/pyb.SPI.html#pyb-spi

在此我想说的是,即使调用了spi的库,仍然被一个地方卡住了,正确的使用是这样的:

CS.value(0)NRF_SPI.send_recv(buff, buff, timeout=500)CS.value(1)

可以看到,需要经过一次片选的完成才能正确的完成数据的通信,而不是一直片选中该模块就可以完成通信的。

2、nrf24l01的通信,上面说到,四根信号线用在了spi上,还有两个CE、IRQ来完成模块的自身需求,通过配置模块内部的寄存器来完成数据的发送,此处应有代码。
(如需详细了解模块内部的寄存器可以仔细阅读数据手册,那上面说的很清楚,另外,此处只将nrf24l01作为发送端,接收端同理)

def nrf_writereg(reg, dat): # NRF24L01+写寄存器

def nrf_readreg(reg):

def nrf_writebuf(reg, pBuf, datalen): #pBuf为TX的地址

上面这三个函数是对nrf24l01的寄存器进行的基本操作,下面上全部的代码,表示在我的设备上已正常测试通过

import sensor, image, timeimport pybfrom pyb import Pin, SPI, ExtInt# 用户配置 发送和 接收地址,频道TX_ADDRESS = (0x34, 0x43, 0x10, 0x10, 0x01)   # 定义一个静态发送地址RX_ADDRESS = (0x34, 0x43, 0x10, 0x10, 0x01)CHANAL    =      40                              #频道选择buff = bytearray(2)def callback(line): #中断服务函数    state = nrf_readreg(FIFO_STATUS) #读取FIFO_STATUS寄存器的值,正常为17    #print("FIFO_STATUS = ", state)    state = nrf_readreg(STATUS)     #读取status寄存器的值,state为发送状态,数值46: 正常发送完成, 30:重发超过次数    #print("state = ", state)    nrf_writereg(NRF_WRITE_REG + STATUS, state)  #清除中断标志    global nrf_irq_tx_flag    #if(state & RX_DR)  #接收到数据        #不会接受到数据的,忽略该种情况    if(state & TX_DS):  #发送完数据        nrf_irq_tx_flag = 0        nrf_writereg(FLUSH_TX, NOP)    #清除TX FIFO寄存器        print("\nTX_DS")    if(state & MAX_RT):      #发送超时,达到最多重发次数标志位        nrf_irq_tx_flag = 0                            #标记发送失败        nrf_writereg(FLUSH_TX, NOP)                    #清除TX FIFO寄存器        #有可能是 对方也处于 发送状态        #放弃本次发送        print("\nMAX_RT")    if(state & TX_FULL):    #TX FIFO 满        print("\nTX_FULL")# 配置spi协议端口,波特率12500*1000nrf_tx_buff = 'H+!0'NRF_SPI = SPI(2)# 配置nrf的CE,io输出模式,初始电平为0CE = pyb.Pin(pyb.Pin.board.P4, pyb.Pin.OUT)# 配置IRQ为下降沿触发中断,handler为回调函数(中断服务函数)NRF_IRQ = pyb.ExtInt(pyb.Pin.board.P5, pyb.ExtInt.IRQ_FALLING, pyb.Pin.PULL_UP, callback)NRF_IRQ.enable()# CSCS = pyb.Pin(pyb.Pin.board.P3, pyb.Pin.OUT)DATA_PACKET        =     32      #一次传输最大可支持的字节数(1~32)RX_FIFO_PACKET_NUM =     80      #接收 FIFO 的 包 数目 ( 总空间 必须要大于 一副图像的大小,否则 没法接收完 )ADR_WIDTH          =     5       #定义地址长度(3~5)IS_CRC16           =     1       #1表示使用 CRC16,0表示 使用CRC8 (0~1)nrf_irq_tx_flag    =     0# 内部配置参量TX_ADR_WIDTH  =  ADR_WIDTH       #发射地址宽度TX_PLOAD_WIDTH = DATA_PACKET     #发射数据通道有效数据宽度0~32ByteRX_ADR_WIDTH  =  ADR_WIDTH       #接收地址宽度RX_PLOAD_WIDTH=  DATA_PACKET     #接收数据通道有效数据宽度0~32Byte# /******************************** NRF24L01+ 寄存器命令 宏定义***************************************/# SPI(nRF24L01) commands , NRF的SPI命令宏定义,详见NRF功能使用文档NRF_READ_REG  =  0x00    # Define read command to registerNRF_WRITE_REG =  0x20    # Define write command to registerRD_RX_PLOAD  =   0x61    # Define RX payload register addressWR_TX_PLOAD  =   0xA0    # Define TX payload register addressFLUSH_TX     =   0xE1    # Define flush TX register commandFLUSH_RX     =   0xE2    # Define flush RX register commandREUSE_TX_PL  =   0xE3    # Define reuse TX payload register commandNOP          =   0xFF    # Define No Operation, might be used to read status register# SPI(nRF24L01) registers(addresses) ,NRF24L01 相关寄存器地址的宏定义CONFIG   =   0x00        # 'Config' register addressEN_AA    =   0x01        # 'Enable Auto Acknowledgment' register addressEN_RXADDR =  0x02        # 'Enabled RX addresses' register addressSETUP_AW  =  0x03        # 'Setup address width' register addressSETUP_RETR=  0x04        # 'Setup Auto. Retrans' register addressRF_CH     =  0x05        # 'RF channel' register addressRF_SETUP  =  0x06        # 'RF setup' register addressSTATUS    =  0x07        # 'Status' register addressOBSERVE_TX=  0x08        # 'Observe TX' register addressCD        =  0x09        # 'Carrier Detect' register addressRX_ADDR_P0=  0x0A        # 'RX address pipe0' register addressRX_ADDR_P1=  0x0B        # 'RX address pipe1' register addressRX_ADDR_P2=  0x0C        # 'RX address pipe2' register addressRX_ADDR_P3=  0x0D        # 'RX address pipe3' register addressRX_ADDR_P4=  0x0E        # 'RX address pipe4' register addressRX_ADDR_P5=  0x0F        # 'RX address pipe5' register addressTX_ADDR   =  0x10        # 'TX address' register addressRX_PW_P0  =  0x11        # 'RX payload width, pipe0' register addressRX_PW_P1  =  0x12        # 'RX payload width, pipe1' register addressRX_PW_P2  =  0x13        # 'RX payload width, pipe2' register addressRX_PW_P3  =  0x14        # 'RX payload width, pipe3' register addressRX_PW_P4  =  0x15        # 'RX payload width, pipe4' register addressRX_PW_P5  =  0x16        # 'RX payload width, pipe5' register addressFIFO_STATUS= 0x17        # 'FIFO Status Register' register address#几个重要的状态标记TX_FULL   =  0x01        #TX FIFO 寄存器满标志。 1 为 满,0为 不满MAX_RT    =  0x10        #达到最大重发次数中断标志位TX_DS     =  0x20        #发送完成中断标志位RX_DR     =  0x40        #接收到数据中断标志位def nrf_writereg(reg, dat):     # NRF24L01+写寄存器    buff[0] = reg          #先发送寄存器    buff[1] = dat          #再发送数据    CS.value(0)    NRF_SPI.send_recv(buff, buff, timeout=500)    CS.value(1)    return buff[0]def nrf_readreg(reg):    buff[0] = reg         #先发送寄存器    buff[1] = 0    CS.value(0)    NRF_SPI.send_recv(buff, buff, timeout=500)    CS.value(1)    return buff[1]  #返回读取的值def nrf_writebuf(reg, pBuf, datalen):    #pBuf为TX的地址    buff = bytearray(datalen+1)    buff[0] = reg    ctr = 1    for i in pBuf:        buff[ctr] = i        ctr += 1    CS.value(0)    NRF_SPI.send_recv(buff, buff, timeout=500)    CS.value(1)    return reg    #返回NRF24L01的状态def nrf_link_check():           #检测NRF24L01+与MCU是否正常连接    NRF_CHECH_DATA = 0xd2       #此值为校验数据时使用,可修改为其他值    buff = bytearray(6)    buff[0] = NRF_WRITE_REG + TX_ADDR    buff[1] = NRF_CHECH_DATA    buff[2] = NRF_CHECH_DATA    buff[3] = NRF_CHECH_DATA    buff[4] = NRF_CHECH_DATA    buff[5] = NRF_CHECH_DATA    CS.value(0)    NRF_SPI.send_recv(buff, buff)    CS.value(1)    buff[0] = TX_ADDR    CS.value(0)    NRF_SPI.send_recv(buff, buff)    CS.value(1)    #比较    for i in (buff[1], buff[2], buff[3], buff[4], buff[5]):        if i != NRF_CHECH_DATA:            return 0        #MCU与NRF不正常连接    return 1                #MCU与NRF成功连接def nrf_init():    ## 配置nrf的寄存器    # CE置低,即将开始配置    NRF_SPI.init(SPI.MASTER, baudrate=12500000,polarity=0, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None)    CE.value(0)    nrf_writereg(NRF_WRITE_REG + SETUP_AW, ADR_WIDTH - 2)          #设置地址长度为 TX_ADR_WIDTH    nrf_writereg(NRF_WRITE_REG + RF_CH, CHANAL)                    #设置RF通道为CHANAL    nrf_writereg(NRF_WRITE_REG + RF_SETUP, 0x0f)                   #设置TX发射参数,0db增益,2Mbps,低噪声增益开启    nrf_writereg(NRF_WRITE_REG + EN_AA, 0x01)                      #使能通道0的自动应答    nrf_writereg(NRF_WRITE_REG + EN_RXADDR, 0x01)                  #使能通道0的接收地址    #RX模式配置    #nrf_writebuf(NRF_WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH)           #写RX节点地址    nrf_writereg(NRF_WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH)         #选择通道0的有效数据宽度    #nrf_writereg(NRF_WRITE_REG + CONFIG, 0x0B | (IS_CRC16 << 2));       #配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式    #TX模式配置    nrf_writebuf(NRF_WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH)           #写TX节点地址    nrf_writereg(NRF_WRITE_REG + SETUP_RETR, 0x00)                 #设置自动重发间隔时间:250us + 86us;最大自动重发次数:15次    nrf_writereg(FLUSH_TX, NOP)                                    #清除TX FIFO寄存器    # CE置高,配置完成    CE.value(1)    time.sleep(1)    return nrf_link_check()def nrf_tx(txbuf, datalen):     #NRF24L01+数据发送    if(txbuf == None or datalen == 0):        return 0    global nrf_irq_tx_flag    if(nrf_irq_tx_flag == 0):   #上一包发送完之后才能发送下一个包        #默认每次发送一个32byte的包        nrf_irq_tx_flag = 1        #需要 先发送一次数据包后才能 中断发送        CE.value(0)    #ce为低,进入待机模式1        nrf_writebuf(NRF_WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH)           #写TX节点地址        nrf_writebuf(NRF_WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH) #设置RX节点地址 ,主要为了使能ACK!!!        nrf_writereg(NRF_WRITE_REG + CONFIG, 0x0A | (IS_CRC16 << 2)) #配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,发射模式,开启所有中断        nrf_writebuf(WR_TX_PLOAD, txbuf, datalen)   #写数据到TX BUF 最大 32个字节        CE.value(1)   #CE为高,txbuf非空,发送数据包        i = 0x0fff        while(i != 0):            i -= 1        #print(nrf_readreg(STATUS))        return 1    else:        return 0while(nrf_init() == 0):    print('nrf not link openmv')print('nrf link openmv')print(NRF_SPI)while(True):    time.sleep(1000)    nrf_tx(nrf_tx_buff.encode('utf-8'), DATA_PACKET)

三、头疼的坑,勿入
1、需要先测试nrf24l01与单片机是否通过spi正常通信(串行),这里有个坑1之前提到过就是片选CS的问题
2、坑2:引脚irq触发中断,其中中断服务函数必须带一个参数line,即使函数里并没用到这个参数,也不用传入参数,但必须这么申明,即def callback(line):
3、坑3:中断服务函数里面需要清除nrf的状态标志寄存器state,清除的方法:从state读到什么状态就往寄存器写什么状态即完成清除。
nrf_writereg(NRF_WRITE_REG + STATUS, state) #清除中断标志
4、坑4:在写入需要发送的数据之前,之前,之前,必须再次写TX、RX节点地址,虽然在初始化里面已经做过这项工作了,目的是:主要为了使能ACK!!!

四、
调试告一段落,过程中,虽多次心态崩了,但还是有收获的。
上面的代码是最大众化的,有了上面的基础,到后来的发送模式,多通道通信也就不在话下了,已测试通过,在此就不在赘述了。
–end

原创粉丝点击