完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、
无线通信模块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相关函数。调用方法点击跳转: 在此我想说的是,即使调用了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, time import pyb from 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*1000 nrf_tx_buff = 'H+!0' NRF_SPI = SPI(2) # 配置nrf的CE,io输出模式,初始电平为0 CE = 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() # CS CS = 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~32Byte RX_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 register NRF_WRITE_REG = 0x20 # Define write command to register RD_RX_PLOAD = 0x61 # Define RX payload register address WR_TX_PLOAD = 0xA0 # Define TX payload register address FLUSH_TX = 0xE1 # Define flush TX register command FLUSH_RX = 0xE2 # Define flush RX register command REUSE_TX_PL = 0xE3 # Define reuse TX payload register command NOP = 0xFF # Define No Operation, might be used to read status register # SPI(nRF24L01) registers(addresses) ,NRF24L01 相关寄存器地址的宏定义 CONFIG = 0x00 # 'Config' register address EN_AA = 0x01 # 'Enable Auto Acknowledgment' register address EN_RXADDR = 0x02 # 'Enabled RX addresses' register address SETUP_AW = 0x03 # 'Setup address width' register address SETUP_RETR= 0x04 # 'Setup Auto. Retrans' register address RF_CH = 0x05 # 'RF channel' register address RF_SETUP = 0x06 # 'RF setup' register address STATUS = 0x07 # 'Status' register address OBSERVE_TX= 0x08 # 'Observe TX' register address CD = 0x09 # 'Carrier Detect' register address RX_ADDR_P0= 0x0A # 'RX address pipe0' register address RX_ADDR_P1= 0x0B # 'RX address pipe1' register address RX_ADDR_P2= 0x0C # 'RX address pipe2' register address RX_ADDR_P3= 0x0D # 'RX address pipe3' register address RX_ADDR_P4= 0x0E # 'RX address pipe4' register address RX_ADDR_P5= 0x0F # 'RX address pipe5' register address TX_ADDR = 0x10 # 'TX address' register address RX_PW_P0 = 0x11 # 'RX payload width, pipe0' register address RX_PW_P1 = 0x12 # 'RX payload width, pipe1' register address RX_PW_P2 = 0x13 # 'RX payload width, pipe2' register address RX_PW_P3 = 0x14 # 'RX payload width, pipe3' register address RX_PW_P4 = 0x15 # 'RX payload width, pipe4' register address RX_PW_P5 = 0x16 # 'RX payload width, pipe5' register address FIFO_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 0 while(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 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1916 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1680 浏览 1 评论
1172 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
771 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1732 浏览 2 评论
1974浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
808浏览 4评论
stm32f4下spi+dma读取数据不对是什么原因导致的?
257浏览 3评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
625浏览 3评论
634浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-25 01:28 , Processed in 0.662200 second(s), Total 45, Slave 39 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号