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
- nrf24l01之python
- STM32模块之NRF24L01
- nrf24l01+
- NRF24L01+
- 小四轴DIY之NRF24L01初始化
- NRF24L01坎坷调通之路
- NRF24L01通道之我见及基于NRF24L01的无线组网方案的几点看法
- nRF24L01发送失败原因之“MAX_RT”未清零
- NRF24L01 接收
- nRF24L01+使用说明
- PIC18F4520 + NRF24L01
- nRF24L01随笔
- NRF24L01驱动程序
- stm32 NRF24L01
- nRF24L01介绍
- STM32F103: NRF24L01
- NRF24L01学习
- NRF24L01芯片基本特性
- Pythonday5高级特性和高级函数
- 2423-Fence Repair
- 每日小记1
- 使用MATLAB模糊数据工具箱和simulink实现单交叉路口交通灯实时配时算法(一)
- 注释在Python中的使用
- nrf24l01之python
- jQuery插件之Validate
- 入门赛2
- 2482-二叉排序树
- 电商分类
- OpenCV-证件照蓝底换成白底(或其他颜色如红色)
- 福布斯系列之数据采集 | Python数据分析项目实战
- Unity Ui点击事件实现
- 自己实现memcpy函数