Jump to content
43oh

nrf24L01 registers/commands and functions(work in progress)


Recommended Posts

  • 4 weeks later...
  • Replies 34
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

nrf24L01.h Contains command and register address definitions. nrf24L01.c Contains functions (tx not tested but should work, rx not tested) regs.h Contains settings for the MSP430 and the n

Official release, sans documentation for the moment-   https://github.com/spirilis/msprf24     Lots of cleanup/changes compared to that .zip file, which I'm going to purge for now (no copyright n

Still working on this, got a pretty complete library coming together although I've been swamped in SPI hell due to it not working right ... Finally figured it out, USICKPH has to be 1. Also yanked US

Update: Lost access to my dev workstation (macbook pro) for a couple weeks due to a HW issue, had it taken care of and now I'm back at it.... I did some bugfixing of the code after a comment on github and some testing of my own yesterday with an actual application. Got it reliably sending 4 integers over the wire to another LP dumping data to the serial port. First 2 int's are 0xDEAD and 0xBEEF, second 2 are 32-bits coming from a MAX31855 thermocouple amplifier. After much head-desking and eventually slapping my logic analyzer on it I realized some type casts were corrupting my data and found a workaround; now it sends packets correctly!

 

Documentation is quite incomplete still, so I'm gonna get on that soon. Lots more to write on the github wiki for the project.

 

Last but not least, here's the example code (G2452) I used to transmit thermocouple data:


/* tcamp
2.4 - MOSFET gate (active=low)
2.5 - TCAMP SPI CS
*/

#include 
#include 
#include "msprf24.h"

int tc[2];
volatile int wdtsleep;
const int packet_preamble[] = {0xDEAD, 0xBEEF};
const char txaddr[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01};
char buf[16];

/* Sleep for  * 47ms */
void wdt_sleep(unsigned int cycles)
{
       wdtsleep = cycles;
       WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL | WDTIS1;  // WDT interval = 512 VLOCLK cycles, about 47ms
       IFG1 &= ~WDTIFG;
       IE1 |= WDTIE;
       LPM3;
       WDTCTL = WDTPW | WDTHOLD;
}

void main()
{
       char c;

       WDTCTL = WDTPW | WDTHOLD;
       DCOCTL = CALDCO_16MHZ;
       BCSCTL1 = CALBC1_16MHZ;
       BCSCTL2 = DIVS_2;  // SMCLK = MCLK/4
       BCSCTL3 = LFXT1S_2;  // ACLK = VLOCLK/1
       BCSCTL3 &= ~(XT2OF|LFXT1OF);

       wdtsleep = 0;

       // Thermocouple Amplifier PWR control, SPI CS pins
       P2DIR |= BIT4|BIT5;
       P2OUT |= BIT4|BIT5;  // MOSFET P-channel gate off (set high), Chip Select disabled (set high)

       // P1.0 LED pin
       P1DIR |= BIT0;
       P1OUT &= ~BIT0;

       /* Initial values for nRF24L01+ library config variables */
       rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit
       rf_addr_width      = 5;
       rf_speed_power     = RF24_SPEED_MIN | RF24_POWER_MIN;
       rf_channel         = 60;
       msprf24_init();
       msprf24_set_pipe_packetsize(0, 0);  // Dynamic packet sizes
       msprf24_open_pipe(0, 1);  // Open pipe#0 with Enhanced ShockBurst for receiving Auto-ACKs

       /* Main loop */
       while (1) {
               P2OUT &= ~BIT4;  // Activate TCAMP P-channel MOSFET
               // Wait 235ms for thermocouple amplifier to power up & do a successful conversion
               wdt_sleep(5);

               // Read TC amplifier
               P2OUT &= ~BIT5;  // TCAMP CS active
               tc[0] = spi_transfer16(0x0000);
               tc[1] = spi_transfer16(0x0000);
               P2OUT |= BIT5;   // TCAMP CS inactive
               P2OUT |= BIT4;  // Shut off TCAMP P-channel MOSFET

               // Compose nRF message
               for (c=0; c<16; c++)
                       buf[c] = '\0';
               memcpy(buf, &packet_preamble, sizeof(int)*2);
               memcpy(buf+sizeof(int)*2, &tc, sizeof(int)*2);

               // Online the nRF24L01+ transceiver
               msprf24_standby();
               flush_tx();

               w_tx_addr( (char*)txaddr );
               w_rx_addr(0, (char*)txaddr );
               w_tx_payload(sizeof(int)*4, buf);
               msprf24_activate_tx();
               LPM4;

               if (RF24_IRQ_FLAGGED & rf_irq) {
                       rf_irq &= RF24_IRQ_FLAGGED;
                       msprf24_get_irq_reason();
                       if (rf_irq & RF24_IRQ_TX)
                               P1OUT &= BIT0;  // Successful TX, clear red LED
                       if (rf_irq & RF24_IRQ_TXFAILED)
                               P1OUT |= BIT0;  // Unsuccessful TX, set red LED
                       msprf24_irq_clear(RF24_IRQ_MASK);
               }
               msprf24_powerdown();  // Put the RF transceiver back to sleep

               wdt_sleep(21);  // Sleep for ~1 second
       }
}

// WDT overflow/STOP
#pragma vector=WDT_VECTOR
__interrupt void WDT_ISR(void)
{
       if (wdtsleep) {
               wdtsleep--;
       } else {
               IFG1 &= ~WDTIFG;
               IE1 &= ~WDTIE;
               __bic_SR_register_on_exit(LPM3_bits);
       }
}

 

The nrf_userconfig.h for the G2452 TC amplifier transmitter-

/* nrf_userconfig.h
* User configuration of nRF24L01+ connectivity parameters, e.g.
* IRQ, CSN, CE pin assignments, Serial SPI driver type
*
*
* Copyright (c) 2012, Eric Brundick 
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted, provided that the above copyright notice
* and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
* OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef _NRF_USERCONFIG_H
#define _NRF_USERCONFIG_H

/* CPU clock cycles for the specified amounts of time--accurate minimum delays
* required for reliable operation of the nRF24L01+'s state machine.
*/
/* Settings for 1MHz MCLK.
#define DELAY_CYCLES_5MS       5000
#define DELAY_CYCLES_130US     130
#define DELAY_CYCLES_10US      10
*/

/* Settings for 16MHz MCLK */
#define DELAY_CYCLES_5MS       80000
#define DELAY_CYCLES_130US     2080
#define DELAY_CYCLES_10US      160

/* SPI port--Select which USCI port we're using.
* Applies only to USCI devices.  USI users can keep these
* commented out.
*/
//#define RF24_SPI_DRIVER_USCI_A 1
//#define RF24_SPI_DRIVER_USCI_B 1

/* Define whether this library should use LPM0+IRQs during SPI I/O and whether this library should provide the ISR. */
//#define RF24_SPI_DRIVER_USCI_USE_IRQ 1
//#define RF24_SPI_DRIVER_USCI_PROVIDE_ISR 1


/* Operational pins -- IRQ, CE, CSN (SPI chip-select)
*/

/* IRQ */
#define nrfIRQport 2
#define nrfIRQpin BIT0

/* CSN SPI chip-select */
#define nrfCSNport 2
#define nrfCSNportout P2OUT
#define nrfCSNpin BIT1

/* CE Chip-Enable (used to put RF transceiver on-air for RX or TX) */
#define nrfCEport 2
#define nrfCEportout P2OUT
#define nrfCEpin BIT2

#endif

 

Receiver code (G2553):


#include 
#include 
#include "msprf24.h"
#include "uart.h"

volatile int wdtsleep;
const char rxaddr[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01};
const char* digits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};  // For easy printing.

/* Sleep for  * 47ms */
void wdt_sleep(unsigned int cycles)
{
       wdtsleep = cycles;
       WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL | WDTIS1;  // WDT interval = 512 VLOCLK cycles, about 47ms
       IFG1 &= ~WDTIFG;
       IE1 |= WDTIE;
       LPM3;
       WDTCTL = WDTPW | WDTHOLD;
}

void dump_packet(char *buf, char len)
{
       int i;

       for (i=0; i                uart_print( (char*)digits[(buf[i] & 0xF0) >> 4] );
               uart_print( (char*)digits[buf[i] & 0x0F] );
               uart_print(" ");
       }
       uart_print("\n");
}

char buf[16];

void main()
{
       char c;
       int pa[2];

       WDTCTL = WDTPW | WDTHOLD;
       DCOCTL = CALDCO_16MHZ;
       BCSCTL1 = CALBC1_16MHZ;
       BCSCTL2 = DIVS_2;  // SMCLK = MCLK/4
       BCSCTL3 = LFXT1S_2;  // ACLK = VLOCLK/1
       BCSCTL3 &= ~(XT2OF|LFXT1OF);

       wdtsleep = 0;
       uart_init();

       // P1.0 LED pin
       P1DIR |= BIT0;
       P1OUT &= ~BIT0;

       /* Initial values for nRF24L01+ library config variables */
       rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit
       rf_addr_width      = 5;
       rf_speed_power     = RF24_SPEED_MIN | RF24_POWER_MIN;
       rf_channel         = 60;
       msprf24_init();
       if (msprf24_current_state() == RF24_STATE_NOTPRESENT) {
               P1OUT |= BIT0; // Set red LED
               _DINT();
               LPM4;          // Halt completely
       }
       msprf24_set_pipe_packetsize(0, 0);  // Dynamic packet sizes
       msprf24_open_pipe(0, 1);  // Open pipe#0 with Enhanced ShockBurst for receiving Auto-ACKs

       // Initialize RF receiver
       w_rx_addr(0, (char*) rxaddr);
       msprf24_activate_rx();

       /* Main loop */
       while (1) {

               if (!(RF24_IRQ_FLAGGED & rf_irq))  // Catch additional incoming packets that may have come in during UART writing
                       wdt_sleep(42);  // No pending packets?  Sleep for ~2sec
                       // Note: wdt_sleep stops the WDT timer after waking from LPM3, so no WDT IRQs should fire until we run
                       // wdt_sleep() again.
               if (RF24_IRQ_FLAGGED & rf_irq) {  // We woke up due to an incoming transmission, not the WDT sleep.
                       // Address the IRQ
                       rf_irq &= ~RF24_IRQ_FLAGGED;
                       msprf24_get_irq_reason();
                       if (RF24_IRQ_RX & rf_irq) {
                               if ( (c = r_rx_peek_payload_size()) != sizeof(int)*4 ) {
                                       flush_rx();
                                       msprf24_irq_clear(RF24_IRQ_MASK);  // Clear IRQ
                                       uart_begin();
                                       uart_print("RX payload size ");
                                       uart_print( (char*)digits[c/10] );
                                       uart_print( (char*)digits[c - (c/10)*10] );
                                       uart_print(" not correct.\n");
                                       uart_end();
                               } else {
                                       r_rx_payload(c, buf);
                                       msprf24_irq_clear(RF24_IRQ_MASK);  // Clear IRQ

                                       uart_begin();
                                       uart_print("Reading ");
                                       uart_print( (char*)digits[c/10] );
                                       uart_print( (char*)digits[c - (c/10)*10] );
                                       uart_print(" bytes from RX FIFO...\n");
                                       dump_packet(buf, c);

                                       memcpy(pa, buf, sizeof(int)*2);
                                       if ( pa[0] != 0xDEAD || pa[1] != 0xBEEF ) {
                                               uart_begin();
                                               uart_print("Packet preamble does not match 0xDEADBEEF\n");
                                               uart_end();
                                       } else {
                                               uart_begin();
                                               uart_print("Thermocouple data: 0x");
                                               uart_print( (char*)digits[(buf[5] & 0xF0) >> 4] );
                                               uart_print( (char*)digits[(buf[5] & 0x0F)] );

                                               uart_print( (char*)digits[(buf[4] & 0xF0) >> 4] );
                                               uart_print( (char*)digits[(buf[4] & 0x0F)] );
                                               uart_print( (char*)digits[(buf[7] & 0xF0) >> 4] );
                                               uart_print( (char*)digits[(buf[7] & 0x0F)] );
                                               uart_print( (char*)digits[(buf[6] & 0xF0) >> 4] );
                                               uart_print( (char*)digits[(buf[6] & 0x0F)] );
                                               uart_print("\n");
                                               uart_end();
                                       }
                               }
                       } else {
                               // Just clear the IRQ and ignore what it meant (we only care about RX, and should only be receiving RX IRQs!)
                               // i.e. this section should never actually execute...
                               msprf24_irq_clear(RF24_IRQ_MASK);
                       }
               } else {  // wdt_sleep completed, no data came in
                       uart_begin();
                       uart_print("No thermocouple data for 2 seconds...\n");
                       uart_end();
               }
       }
}

// WDT overflow/STOP
#pragma vector=WDT_VECTOR
__interrupt void WDT_ISR(void)
{
       if (wdtsleep) {
               wdtsleep--;
       } else {
               IFG1 &= ~WDTIFG;
               IE1 &= ~WDTIE;
               __bic_SR_register_on_exit(LPM3_bits);
       }
}

 

Just in case anyone's curious, the "uart.c" code I cooked up (could've used other examples, wanted to get my feet wet though):


#include 
#include 
#include 

void uart_init()
{
       // Init USCI, leave in RESET mode.
       // Assumes SMCLK = MCLK/4 (4MHz)
       IFG2 &= ~(UCA0TXIFG | UCA0RXIFG);
       UCA0CTL0 = 0x00;
       UCA0CTL1 = UCSSEL_2 | UCDORM | UCSWRST;
       UCA0BR0 = 26;
       UCA0BR1 = 0;
       UCA0MCTL = UCBRS_0 | UCBRF_1 | UCOS16;
       P1SEL = BIT1|BIT2;  // USCIA
       P1SEL2 = BIT1|BIT2;
}

void uart_begin()
{
       UCA0CTL1 &= ~UCSWRST;
       IE2 |= UCA0TXIE;
}

void uart_end()
{
       //UCA0CTL1 |= UCSWRST;  // doing this after every block added some glitch to the output
       IE2 &= ~UCA0TXIE;
}

void uart_print(char *str)
{
       int i=0, j;

       j = strlen(str);
       for (; j; j--) {
               UCA0TXBUF = str[i];
               LPM0;
               i++;

       }
}

// USCI continue with next char
#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void)
{
       if (IFG2 & UCA0TXIFG) {
               IFG2 &= ~UCA0TXIFG;
               __bic_SR_register_on_exit(LPM0_bits);
       }
}

Link to post
Share on other sites
  • 2 weeks later...

Yep the library works, documentation is incomplete atm. Took a hiatus to recover from sinus troubles and now I'm back at it ... There's someone on github posting comments on one of the commits too, some of the minor code mistakes he's making underlines the need for me to add a thorough walkthrough on what the different functions do/why you need them/how to plan out your connections.

Link to post
Share on other sites
  • 1 month later...

I've definitely got more than 20 feet, actually I had probably 40 feet with some glass/metal doors, faux-wood panelled walls between the two transceivers...

 

I also have a powered one, meaning the $20-ish units with a PA+LNA amplifier onboard and WiFi antenna ... They work a hell of a lot better when they're acting as a transmitter, IMO, as a receiver they don't add nearly as much range.

This makes sense though, since it's the signal that's getting lost by obstacles/etc... transmitting a higher power signal is probably more important than amplifying the received signal.

Link to post
Share on other sites
  • 1 month later...

Discovered something today in the midst of debugging my grill monitor. The "Enhanced ShockBurst" auto-acknowledgement protocol doesn't work at 250Kbps speeds. There is no explicit mention of this in the datasheet, but there is a "hint" back in the appendix where they give examples of doing Enhanced ShockBurst transmissions vs. legacy (nRF24L01, non-plus) transmissions. They mention the data rate should be set to 1Mbps or 2Mbps for Enhanced ShockBurst.

 

Who on earth writes these datasheets? Yargh!

 

 

edit:

Actually I take that back, it's even more confusing; in the section on Auto-Retransmit Delays, they provide delay timing minimums for 250Kbps (along with 1Mbps, 2Mbps). There is no point in setting the ARD register if you're not using auto-ack.

Link to post
Share on other sites
  • 4 weeks later...

Spirilis your lib is nice but is not very friendly. Yes, it's good if you want to send or receive something simple, but if you need to use it with UART or I2C and if you need to service interrupts from other pins ... things can get messy. 

But my primary concern is your lack of pair Rx and Tx examples. So I added such example, but it need beta testers, because it works for me but it may not work properly for you.

 

Edit: I just realize that I post this in the wrong thread, so I moved it here.

NRF24L01-MSP430G2553.rar

Link to post
Share on other sites
  • 4 weeks later...

Library updated with F5xxx/6xxx USCI_A0/USCI_B0 support (minor differences; mainly the IRQ IE/IFG registers are different and the ISR layout is different) and confirmed working on an F5172.

 

My nRF24L01+ BoosterPack needs a minor change; P2.2 (actually PJ.2 on the F5172 LP) isn't interrupt capable so the solder-jumper is cut and a jumper wire installed from the IRQ pad to the 3rd pad down from the top right (normally labeled XOUT or P2.7 on the value-line launchpad, but on the F5172 it's P1.6 and is interrupt-capable).

Link to post
Share on other sites
  • 3 weeks later...
  • 4 weeks later...
  • 1 month later...

Two things:

The attached files main.c, and others point to nrf testdebug.txt (at least with firefox and chrome).


 

Most likely will have to start a new thread but spirilis' code doesn't appear to work with msp430g2553's at least with ike's examples. (My hardware is fine, as since spirilis' code for the energia works perfectly) Have attempted to use both RF24_SPI_DRIVER_USCI_B and A (commenting each userconfig.h file accordingly). Swapped the necessary pins. Don't know what else could be wrong? Any help would be great.

 

Note: Code stalls at     if (rf_irq & RF24_IRQ_FLAGGED) {      indefinitely for both tx and rx

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...