├── README.md ├── library.json ├── COPYRIGHT ├── TESTPROGS ├── g2553 │ ├── ike-uscia-ReadMe.txt │ ├── pwrup-plus-prx.c │ ├── pwrup-plus-single-xmit.c │ ├── remote-led-receiver.c │ ├── pwrup-plus-prx-configLED.c │ ├── ike-uscia-rx.c │ ├── pwrup-plus-activate-remote-led-lightLED-based-on-TXoutcome.c │ ├── test-basic-pwrup.c │ ├── recv-pushbtn-cmd.c │ ├── ike-uscia-tx.c │ └── ping-remote-led-every500ms-using-vlo.c ├── g2231 │ ├── pwrup-plus-prx.c │ ├── pwrup-plus-single-xmit.c │ ├── pwrup-plus-activate-remote-led.c │ ├── pwrup-plus-activate-remote-led-clearIFG-only-if-no-MAXRT.c │ └── test-basic-pwrup.c └── g2452 │ ├── ping-remote-led-every500ms-using-vlo.c │ └── send-pushbtn-cmd.c ├── msp430_spi.h ├── nrf_userconfig.h ├── Pkt_CPP ├── Pkt.h ├── README.txt └── Pkt.cpp ├── nRF24L01.h ├── msprf24.h ├── msprf24.c └── msp430_spi.c /README.md: -------------------------------------------------------------------------------- 1 | msprf24 2 | ======= 3 | 4 | nRF24L01+ Library for MSP430 Value Line 5 | 6 | See the Wiki for this git repo for full documentation! 7 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nRF24", 3 | "keywords": "rf, radio, wireless, spi", 4 | "description": "The nRF24L01 is a low-cost 2.4GHz ISM transceiver module. It supports a number of channel frequencies in the 2.4GHz band and a range of data rates", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/spirilis/msprf24.git" 9 | }, 10 | "examples": "TESTPROGS/*/*.c", 11 | "platforms": "timsp430", 12 | "build": { 13 | "srcFilter": "+<*.c>" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | * Copyright (c) 2012, Eric Brundick 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any purpose 4 | * with or without fee is hereby granted, provided that the above copyright notice 5 | * and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 10 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 11 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 12 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/ike-uscia-ReadMe.txt: -------------------------------------------------------------------------------- 1 | Example uses 2 msp430 LaunchPads with 2 MSP430G2553 MCUs. 2 | Remove Rx and Tx jumpers form J3 at upper right section of LP. 3 | Leave LED jumpers on J5 at lower left section of LP. 4 | You will need this lib at address https://github.com/spirilis/msprf24/ 5 | 6 | Example uses RF24_SPI_DRIVER_USCI_A (edit nrf_userconfig.h if needed), because we need green LED at P1.6. 7 | 8 | nrf24l01 Vcc - LP pin 1 VCC. 9 | nrf24l01 GND - LP pin 20 GND. 10 | nrf24l01 MI - LP pin 3 P1.1 USCI_A0 SPI mode: slave data out/master in. 11 | nrf24l01 MO - LP pin 4 P1.2 USCI_A0 SPI mode: slave data in/master out. 12 | nrf24l01 SCK - LP pin 6 P1.4 USCI_A0 clock input/output. 13 | nrf24l01 CE - LP pin 8 P2.0 General-purpose digital I/O pin. 14 | nrf24l01 CSN - LP pin 9 P2.1 General-purpose digital I/O pin. 15 | nrf24l01 IRQ - LP pin 10 P2.2 General-purpose digital I/O pin. 16 | 17 | -------------------------------------------------------------------------------- /msp430_spi.h: -------------------------------------------------------------------------------- 1 | /* msp430_spi.c 2 | * Library for performing SPI I/O on a wide range of MSP430 chips. 3 | * 4 | * Copyright (c) 2013, Eric Brundick 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any purpose 7 | * with or without fee is hereby granted, provided that the above copyright notice 8 | * and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 11 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 13 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 14 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 15 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifndef _MSP430_SPI_H_ 19 | #define _MSP430_SPI_H_ 20 | 21 | #include 22 | 23 | void spi_init(); 24 | uint8_t spi_transfer(uint8_t); // SPI xfer 1 byte 25 | uint16_t spi_transfer16(uint16_t); // SPI xfer 2 bytes 26 | uint16_t spi_transfer9(uint16_t); // SPI xfer 9 bits (courtesy for driving LCD screens) 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /TESTPROGS/g2231/pwrup-plus-prx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | volatile unsigned int user; 6 | 7 | int main() 8 | { 9 | uint8_t addr[5]; 10 | uint8_t buf[32]; 11 | 12 | WDTCTL = WDTHOLD | WDTPW; 13 | DCOCTL = CALDCO_1MHZ; 14 | BCSCTL1 = CALBC1_1MHZ; 15 | BCSCTL2 = DIVS_0; // SMCLK = DCOCLK/1 16 | // SPI (USI) uses SMCLK, prefer SMCLK=DCO (no clock division) 17 | user = 0xFE; 18 | 19 | /* Initial values for nRF24L01+ library config variables */ 20 | rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit 21 | rf_addr_width = 5; 22 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 23 | rf_channel = 119; 24 | msprf24_init(); 25 | msprf24_set_pipe_packetsize(0, 32); 26 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst 27 | 28 | // Set our RX address 29 | addr[0] = 0xDE; 30 | addr[1] = 0xAD; 31 | addr[2] = 0xBE; 32 | addr[3] = 0xEF; 33 | addr[4] = 0x00; 34 | w_rx_addr(0, addr); 35 | 36 | // Receive mode 37 | if (!(RF24_QUEUE_RXEMPTY & msprf24_queue_state())) { 38 | flush_rx(); 39 | } 40 | msprf24_activate_rx(); 41 | LPM4; 42 | 43 | while (1) { 44 | if (rf_irq & RF24_IRQ_FLAGGED) { 45 | msprf24_get_irq_reason(); 46 | } 47 | if (rf_irq & RF24_IRQ_RX || msprf24_rx_pending()) { 48 | r_rx_payload(32, buf); 49 | msprf24_irq_clear(RF24_IRQ_RX); 50 | user = data[0]; 51 | } else { 52 | user = 0xFF; 53 | } 54 | LPM4; 55 | } 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/pwrup-plus-prx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | volatile unsigned int user; 6 | 7 | int main() 8 | { 9 | uint8_t addr[5]; 10 | uint8_t buf[32]; 11 | 12 | WDTCTL = WDTHOLD | WDTPW; 13 | DCOCTL = CALDCO_16MHZ; 14 | BCSCTL1 = CALBC1_16MHZ; 15 | BCSCTL2 = DIVS_2; // SMCLK = DCOCLK/4 16 | // SPI (USCI) uses SMCLK, prefer SMCLK < 10MHz (SPI speed limit for nRF24 = 10MHz) 17 | user = 0xFE; 18 | 19 | /* Initial values for nRF24L01+ library config variables */ 20 | rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit 21 | rf_addr_width = 5; 22 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 23 | rf_channel = 119; 24 | msprf24_init(); 25 | msprf24_set_pipe_packetsize(0, 32); 26 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst 27 | 28 | // Set our RX address 29 | addr[0] = 0xDE; 30 | addr[1] = 0xAD; 31 | addr[2] = 0xBE; 32 | addr[3] = 0xEF; 33 | addr[4] = 0x00; 34 | w_rx_addr(0, addr); 35 | 36 | // Receive mode 37 | if (!(RF24_QUEUE_RXEMPTY & msprf24_queue_state())) { 38 | flush_rx(); 39 | } 40 | msprf24_activate_rx(); 41 | LPM4; 42 | 43 | while (1) { 44 | if (rf_irq & RF24_IRQ_FLAGGED) { 45 | msprf24_get_irq_reason(); 46 | } 47 | if (rf_irq & RF24_IRQ_RX || msprf24_rx_pending()) { 48 | r_rx_payload(32, buf); 49 | msprf24_irq_clear(RF24_IRQ_RX); 50 | user = buf[0]; 51 | } else { 52 | user = 0xFF; 53 | } 54 | LPM4; 55 | } 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /TESTPROGS/g2231/pwrup-plus-single-xmit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | volatile unsigned int user; 6 | 7 | int main() 8 | { 9 | uint8_t addr[4]; 10 | uint8_t buf[32]; 11 | 12 | WDTCTL = WDTHOLD | WDTPW; 13 | DCOCTL = CALDCO_1MHZ; 14 | BCSCTL1 = CALBC1_1MHZ; 15 | BCSCTL2 = DIVS_0; // SMCLK = DCOCLK/1 16 | // SPI (USI) uses SMCLK, prefer SMCLK=DCO (no clock division) 17 | user = 0xFE; 18 | 19 | /* Initial values for nRF24L01+ library config variables */ 20 | rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit 21 | rf_addr_width = 4; 22 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 23 | rf_channel = 119; 24 | msprf24_init(); 25 | msprf24_set_pipe_packetsize(0, 15); 26 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst for receiving Auto-ACKs 27 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 28 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 29 | 30 | // Transmit to 0xBEEFBEEF 31 | msprf24_standby(); 32 | user = msprf24_current_state(); 33 | addr[0] = 0xBE; addr[1] = 0xEF; addr[2] = 0xBE; addr[3] = 0xEF; 34 | w_tx_addr(addr); 35 | w_rx_addr(0, addr); // Pipe#0 receives auto-ack's 36 | buf[0] = '0'; 37 | buf[1] = '\0'; 38 | w_tx_payload(15, buf); 39 | msprf24_activate_tx(); 40 | LPM4; 41 | 42 | if (rf_irq & RF24_IRQ_FLAGGED) { 43 | user = ~(msprf24_get_irq_reason()); 44 | } 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/pwrup-plus-single-xmit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | volatile unsigned int user; 6 | 7 | int main() 8 | { 9 | uint8_t addr[4]; 10 | uint8_t buf[32]; 11 | 12 | WDTCTL = WDTHOLD | WDTPW; 13 | DCOCTL = CALDCO_16MHZ; 14 | BCSCTL1 = CALBC1_16MHZ; 15 | BCSCTL2 = DIVS_1; // SMCLK = DCOCLK/2 16 | // SPI (USCI) uses SMCLK, prefer SMCLK < 10MHz (SPI speed limit for nRF24 = 10MHz) 17 | user = 0xFE; 18 | 19 | /* Initial values for nRF24L01+ library config variables */ 20 | rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit 21 | rf_addr_width = 4; 22 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 23 | rf_channel = 119; 24 | msprf24_init(); 25 | msprf24_set_pipe_packetsize(0, 15); 26 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst for receiving Auto-ACKs 27 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 28 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 29 | 30 | // Transmit to 0xBEEFBEEF 31 | msprf24_standby(); 32 | user = msprf24_current_state(); 33 | addr[0] = 0xBE; addr[1] = 0xEF; addr[2] = 0xBE; addr[3] = 0xEF; 34 | w_tx_addr(addr); 35 | w_rx_addr(0, addr); // Pipe#0 receives auto-ack's 36 | buf[0] = '0'; 37 | buf[1] = '\0'; 38 | w_tx_payload(15, buf); 39 | msprf24_activate_tx(); 40 | LPM4; 41 | 42 | if (rf_irq & RF24_IRQ_FLAGGED) { 43 | user = ~(msprf24_get_irq_reason()); 44 | } 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/remote-led-receiver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "msprf24.h" 4 | #include "nrf_userconfig.h" 5 | 6 | volatile unsigned int user; 7 | 8 | int main() 9 | { 10 | uint8_t addr[5]; 11 | uint8_t buf[32]; 12 | 13 | WDTCTL = WDTHOLD | WDTPW; 14 | DCOCTL = CALDCO_16MHZ; 15 | BCSCTL1 = CALBC1_16MHZ; 16 | BCSCTL2 = DIVS_2; // SMCLK = DCOCLK/4 17 | // SPI (USCI) uses SMCLK, prefer SMCLK < 10MHz (SPI speed limit for nRF24 = 10MHz) 18 | user = 0xFE; 19 | 20 | // Red LED will be our output 21 | P1DIR |= BIT0; 22 | P1OUT &= ~BIT0; 23 | 24 | /* Initial values for nRF24L01+ library config variables */ 25 | rf_crc = RF24_EN_CRC; // CRC enabled, 8-bit 26 | rf_addr_width = 5; 27 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MAX; 28 | rf_channel = 120; 29 | msprf24_init(); 30 | msprf24_set_pipe_packetsize(0, 0); 31 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst 32 | 33 | // Set our RX address 34 | memcpy(addr, "\xDE\xAD\xBE\xEF\x01", 5); 35 | w_rx_addr(0, addr); 36 | 37 | // Receive mode 38 | if (!(RF24_QUEUE_RXEMPTY & msprf24_queue_state())) { 39 | flush_rx(); 40 | } 41 | msprf24_activate_rx(); 42 | LPM4; 43 | 44 | while (1) { 45 | if (rf_irq & RF24_IRQ_FLAGGED) { 46 | msprf24_get_irq_reason(); 47 | } 48 | if (rf_irq & RF24_IRQ_RX || msprf24_rx_pending()) { 49 | r_rx_payload(r_rx_peek_payload_size(), buf); 50 | msprf24_irq_clear(RF24_IRQ_RX); 51 | user = buf[0]; 52 | 53 | if (buf[0] == '0') 54 | P1OUT &= ~BIT0; 55 | if (buf[0] == '1') 56 | P1OUT |= BIT0; 57 | 58 | } else { 59 | user = 0xFF; 60 | } 61 | LPM4; 62 | } 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/pwrup-plus-prx-configLED.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | volatile unsigned int user; 6 | 7 | int main() 8 | { 9 | uint8_t addr[5]; 10 | uint8_t buf[32]; 11 | 12 | WDTCTL = WDTHOLD | WDTPW; 13 | DCOCTL = CALDCO_16MHZ; 14 | BCSCTL1 = CALBC1_16MHZ; 15 | BCSCTL2 = DIVS_2; // SMCLK = DCOCLK/4 16 | // SPI (USCI) uses SMCLK, prefer SMCLK < 10MHz (SPI speed limit for nRF24 = 10MHz) 17 | user = 0xFE; 18 | 19 | // Red LED will be our output 20 | P1DIR |= BIT0; 21 | P1OUT &= ~BIT0; 22 | 23 | /* Initial values for nRF24L01+ library config variables */ 24 | rf_crc = RF24_EN_CRC; // CRC enabled, 8-bit 25 | rf_addr_width = 5; 26 | rf_speed_power = RF24_SPEED_2MBPS | RF24_POWER_MIN; 27 | rf_channel = 120; 28 | msprf24_init(); 29 | msprf24_set_pipe_packetsize(0, 32); 30 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst 31 | 32 | // Set our RX address 33 | addr[0] = 0xDE; 34 | addr[1] = 0xAD; 35 | addr[2] = 0xBE; 36 | addr[3] = 0xEF; 37 | addr[4] = 0x00; 38 | w_rx_addr(0, addr); 39 | 40 | // Receive mode 41 | if (!(RF24_QUEUE_RXEMPTY & msprf24_queue_state())) { 42 | flush_rx(); 43 | } 44 | msprf24_activate_rx(); 45 | LPM4; 46 | 47 | while (1) { 48 | if (rf_irq & RF24_IRQ_FLAGGED) { 49 | msprf24_get_irq_reason(); 50 | } 51 | if (rf_irq & RF24_IRQ_RX || msprf24_rx_pending()) { 52 | r_rx_payload(32, buf); 53 | msprf24_irq_clear(RF24_IRQ_RX); 54 | user = buf[0]; 55 | 56 | if (buf[0] == '0') 57 | P1OUT &= ~BIT0; 58 | if (buf[0] == '1') 59 | P1OUT |= BIT0; 60 | 61 | } else { 62 | user = 0xFF; 63 | } 64 | LPM4; 65 | } 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /TESTPROGS/g2231/pwrup-plus-activate-remote-led.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | volatile unsigned int user; 6 | 7 | int main() 8 | { 9 | uint8_t addr[5]; 10 | uint8_t buf[32]; 11 | 12 | WDTCTL = WDTHOLD | WDTPW; 13 | DCOCTL = CALDCO_1MHZ; 14 | BCSCTL1 = CALBC1_1MHZ; 15 | BCSCTL2 = DIVS_0; // SMCLK = DCOCLK/1 16 | // SPI (USI) uses SMCLK, prefer SMCLK=DCO (no clock division) 17 | user = 0xFE; 18 | 19 | /* Initial values for nRF24L01+ library config variables */ 20 | rf_crc = RF24_EN_CRC; // CRC enabled, 16-bit 21 | rf_addr_width = 5; 22 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 23 | rf_channel = 120; 24 | msprf24_init(); // All RX pipes closed by default 25 | msprf24_set_pipe_packetsize(0, 32); 26 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst enabled for receiving Auto-ACKs 27 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 28 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 29 | 30 | // Transmit to 'rad01' (0x72 0x61 0x64 0x30 0x31) 31 | msprf24_standby(); 32 | user = msprf24_current_state(); 33 | addr[0] = 'r'; addr[1] = 'a'; addr[2] = 'd'; addr[3] = '0'; addr[4] = '1'; 34 | w_tx_addr(addr); 35 | w_rx_addr(0, addr); // Pipe 0 receives auto-ack's, autoacks are sent back to the TX addr so the PTX node 36 | // needs to listen to the TX addr on pipe#0 to receive them. 37 | buf[0] = '1'; 38 | buf[1] = '\0'; 39 | w_tx_payload(32, buf); 40 | msprf24_activate_tx(); 41 | LPM4; 42 | 43 | if (rf_irq & RF24_IRQ_FLAGGED) { 44 | msprf24_get_irq_reason(); 45 | user = msprf24_get_last_retransmits(); 46 | } 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /TESTPROGS/g2231/pwrup-plus-activate-remote-led-clearIFG-only-if-no-MAXRT.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | volatile unsigned int user; 6 | 7 | int main() 8 | { 9 | uint8_t addr[5]; 10 | uint8_t buf[32]; 11 | 12 | WDTCTL = WDTHOLD | WDTPW; 13 | DCOCTL = CALDCO_1MHZ; 14 | BCSCTL1 = CALBC1_1MHZ; 15 | BCSCTL2 = DIVS_0; // SMCLK = DCOCLK/1 16 | 17 | user = 0xFE; 18 | 19 | /* Initial values for nRF24L01+ library config variables */ 20 | rf_crc = RF24_EN_CRC; // CRC enabled, 8-bit 21 | rf_addr_width = 5; 22 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 23 | rf_channel = 120; 24 | msprf24_init(); // All RX pipes closed by default 25 | msprf24_set_pipe_packetsize(0, 32); 26 | msprf24_open_pipe(0, 1); // Open pipe #0 with Enhanced ShockBurst enabled for receiving Auto-ACKs 27 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 28 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 29 | 30 | // Transmit to 'rad01' (0x72 0x61 0x64 0x30 0x31) 31 | msprf24_standby(); 32 | addr[0] = 'r'; addr[1] = 'a'; addr[2] = 'd'; addr[3] = '0'; addr[4] = '1'; 33 | w_tx_addr(addr); 34 | w_rx_addr(0, addr); // Pipe 0 receives auto-ack's, autoacks are sent back to the TX addr so the PTX node 35 | // needs to listen to the TX addr on pipe#0 to receive them. 36 | buf[0] = '1'; 37 | buf[1] = '\0'; 38 | w_tx_payload(32, buf); 39 | msprf24_activate_tx(); 40 | LPM4; 41 | 42 | if (rf_irq & RF24_IRQ_FLAGGED) { 43 | msprf24_get_irq_reason(); 44 | user = msprf24_queue_state(); 45 | if (rf_irq & RF24_IRQ_TX) { // No MAX_RT, TX_DS = TX successfully ACK'd 46 | msprf24_irq_clear(RF24_IRQ_TX); 47 | } 48 | } 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/ike-uscia-rx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | #include "stdint.h" 5 | 6 | volatile unsigned int user; 7 | 8 | int main() 9 | { 10 | uint8_t addr[5]; 11 | uint8_t buf[32]; 12 | 13 | WDTCTL = WDTHOLD | WDTPW; 14 | DCOCTL = CALDCO_16MHZ; 15 | BCSCTL1 = CALBC1_16MHZ; 16 | BCSCTL2 = DIVS_1; // SMCLK = DCOCLK/2 17 | // SPI (USCI) uses SMCLK, prefer SMCLK < 10MHz (SPI speed limit for nRF24 = 10MHz) 18 | 19 | // Red LED will be our output 20 | P1DIR |= BIT0+BIT6; 21 | P1OUT &= ~(BIT0+BIT6); 22 | 23 | user = 0xFE; 24 | 25 | /* Initial values for nRF24L01+ library config variables */ 26 | rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit 27 | rf_addr_width = 5; 28 | rf_speed_power = RF24_SPEED_1MBPS | RF24_POWER_0DBM; 29 | rf_channel = 120; 30 | 31 | msprf24_init(); 32 | msprf24_set_pipe_packetsize(0, 32); 33 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst 34 | 35 | // Set our RX address 36 | addr[0] = 0xDE; addr[1] = 0xAD; addr[2] = 0xBE; addr[3] = 0xEF; addr[4] = 0x00; 37 | w_rx_addr(0, addr); 38 | 39 | // Receive mode 40 | if (!(RF24_QUEUE_RXEMPTY & msprf24_queue_state())) { 41 | flush_rx(); 42 | } 43 | msprf24_activate_rx(); 44 | LPM4; 45 | 46 | while (1) { 47 | if (rf_irq & RF24_IRQ_FLAGGED) { 48 | rf_irq &= ~RF24_IRQ_FLAGGED; 49 | msprf24_get_irq_reason(); 50 | } 51 | if (rf_irq & RF24_IRQ_RX || msprf24_rx_pending()) { 52 | r_rx_payload(32, buf); 53 | msprf24_irq_clear(RF24_IRQ_RX); 54 | user = buf[0]; 55 | 56 | if (buf[0] == '0') 57 | P1OUT &= ~BIT0; 58 | if (buf[0] == '1') 59 | P1OUT |= BIT0; 60 | if (buf[1] == '0') 61 | P1OUT &= ~BIT6; 62 | if (buf[1] == '1') 63 | P1OUT |= BIT6; 64 | 65 | } else { 66 | user = 0xFF; 67 | } 68 | LPM4; 69 | } 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/pwrup-plus-activate-remote-led-lightLED-based-on-TXoutcome.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | volatile unsigned int user; 6 | 7 | int main() 8 | { 9 | uint8_t addr[5]; 10 | uint8_t buf[32]; 11 | 12 | WDTCTL = WDTHOLD | WDTPW; 13 | DCOCTL = CALDCO_16MHZ; 14 | BCSCTL1 = CALBC1_16MHZ; 15 | BCSCTL2 = DIVS_1; // SMCLK = DCOCLK/2 16 | // SPI (USCI) uses SMCLK, prefer SMCLK < 10MHz (SPI speed limit for nRF24 = 10MHz) 17 | 18 | // Red, Green LED used for status 19 | P1DIR |= 0x41; 20 | P1OUT &= ~0x41; 21 | 22 | user = 0xFE; 23 | 24 | /* Initial values for nRF24L01+ library config variables */ 25 | rf_crc = RF24_EN_CRC; // CRC enabled, 16-bit 26 | rf_addr_width = 5; 27 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 28 | rf_channel = 120; 29 | msprf24_init(); // All RX pipes closed by default 30 | msprf24_set_pipe_packetsize(0, 32); 31 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst enabled for receiving Auto-ACKs 32 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 33 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 34 | 35 | // Transmit to 'rad01' (0x72 0x61 0x64 0x30 0x31) 36 | msprf24_standby(); 37 | user = msprf24_current_state(); 38 | addr[0] = 'r'; addr[1] = 'a'; addr[2] = 'd'; addr[3] = '0'; addr[4] = '1'; 39 | w_tx_addr(addr); 40 | w_rx_addr(0, addr); // Pipe 0 receives auto-ack's, autoacks are sent back to the TX addr so the PTX node 41 | // needs to listen to the TX addr on pipe#0 to receive them. 42 | buf[0] = '1'; 43 | buf[1] = '\0'; 44 | w_tx_payload(32, buf); 45 | msprf24_activate_tx(); 46 | LPM4; 47 | 48 | if (rf_irq & RF24_IRQ_FLAGGED) { 49 | msprf24_get_irq_reason(); 50 | if (rf_irq & RF24_IRQ_TX) 51 | P1OUT |= 0x40; // Green LED 52 | if (rf_irq & RF24_IRQ_TXFAILED) 53 | P1OUT |= 0x01; // Red LED 54 | 55 | msprf24_irq_clear(rf_irq); 56 | user = msprf24_get_last_retransmits(); 57 | } 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /TESTPROGS/g2231/test-basic-pwrup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | void test_msprf24_init(); 6 | 7 | volatile unsigned int user; 8 | volatile uint8_t dat; 9 | 10 | int main() 11 | { 12 | uint8_t addr[4]; 13 | uint8_t buf[32]; 14 | 15 | WDTCTL = WDTHOLD | WDTPW; 16 | DCOCTL = CALDCO_1MHZ; 17 | BCSCTL1 = CALBC1_1MHZ; 18 | BCSCTL2 = DIVS_0; // SMCLK = DCOCLK/1 19 | // SPI (USI) uses SMCLK, prefer SMCLK=DCO (no clock division) 20 | user = 0xFE; 21 | 22 | /* Initial values for nRF24L01+ library config variables */ 23 | rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit 24 | rf_addr_width = 4; 25 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 26 | rf_channel = 119; 27 | test_msprf24_init(); // Init SPI, output ports only 28 | msprf24_set_config(RF24_PWR_UP); 29 | __delay_cycles(DELAY_CYCLES_5MS); 30 | dat = r_reg(RF24_CONFIG); 31 | 32 | _DINT(); 33 | LPM4; 34 | return(0); 35 | } 36 | 37 | void test_msprf24_init() 38 | { 39 | // Setup SPI 40 | spi_init(); 41 | _EINT(); // Enable interrupts (set GIE in SR) 42 | 43 | // Setup IRQ 44 | #if nrfIRQport == 1 45 | P1DIR &= ~nrfIRQpin; // IRQ line is input 46 | P1OUT |= nrfIRQpin; // Pull-up resistor enabled 47 | P1REN |= nrfIRQpin; 48 | P1IES |= nrfIRQpin; // Trigger on falling-edge 49 | P1IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 50 | P1IE |= nrfIRQpin; // Enable IRQ interrupt 51 | #elif nrfIRQport == 2 52 | P2DIR &= ~nrfIRQpin; // IRQ line is input 53 | P2OUT |= nrfIRQpin; // Pull-up resistor enabled 54 | P2REN |= nrfIRQpin; 55 | P2IES |= nrfIRQpin; // Trigger on falling-edge 56 | P2IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 57 | P2IE |= nrfIRQpin; // Enable IRQ interrupt 58 | #endif 59 | 60 | // Setup CSN/CE ports 61 | #if nrfCSNport == 1 62 | P1DIR |= nrfCSNpin; 63 | #elif nrfCSNport == 2 64 | P2DIR |= nrfCSNpin; 65 | #elif nrfCSNport == 3 66 | P3DIR |= nrfCSNpin; 67 | #endif 68 | CSN_DIS; 69 | 70 | #if nrfCEport == 1 71 | P1DIR |= nrfCEpin; 72 | #elif nrfCEport == 2 73 | P2DIR |= nrfCEpin; 74 | #elif nrfCEport == 3 75 | P3DIR |= nrfCEpin; 76 | #endif 77 | CE_DIS; 78 | } 79 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/test-basic-pwrup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | 5 | void test_msprf24_init(); 6 | 7 | volatile unsigned int user; 8 | volatile uint8_t dat; 9 | 10 | int main() 11 | { 12 | uint8_t addr[4]; 13 | uint8_t buf[32]; 14 | 15 | WDTCTL = WDTHOLD | WDTPW; 16 | DCOCTL = CALDCO_16MHZ; 17 | BCSCTL1 = CALBC1_16MHZ; 18 | BCSCTL2 = DIVS_1; // SMCLK = DCOCLK/2 19 | // SPI (USCI) uses SMCLK, prefer SMCLK < 10MHz (SPI speed limit for nRF24 = 10MHz) 20 | user = 0xFE; 21 | 22 | /* Initial values for nRF24L01+ library config variables */ 23 | rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit 24 | rf_addr_width = 4; 25 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MIN; 26 | rf_channel = 119; 27 | test_msprf24_init(); // Init SPI, output ports only 28 | msprf24_set_config(RF24_PWR_UP); 29 | __delay_cycles(DELAY_CYCLES_5MS); 30 | dat = r_reg(RF24_CONFIG); 31 | 32 | _DINT(); 33 | LPM4; 34 | return(0); 35 | } 36 | 37 | void test_msprf24_init() 38 | { 39 | // Setup SPI 40 | spi_init(); 41 | _EINT(); // Enable interrupts (set GIE in SR) 42 | 43 | // Setup IRQ 44 | #if nrfIRQport == 1 45 | P1DIR &= ~nrfIRQpin; // IRQ line is input 46 | P1OUT |= nrfIRQpin; // Pull-up resistor enabled 47 | P1REN |= nrfIRQpin; 48 | P1IES |= nrfIRQpin; // Trigger on falling-edge 49 | P1IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 50 | P1IE |= nrfIRQpin; // Enable IRQ interrupt 51 | #elif nrfIRQport == 2 52 | P2DIR &= ~nrfIRQpin; // IRQ line is input 53 | P2OUT |= nrfIRQpin; // Pull-up resistor enabled 54 | P2REN |= nrfIRQpin; 55 | P2IES |= nrfIRQpin; // Trigger on falling-edge 56 | P2IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 57 | P2IE |= nrfIRQpin; // Enable IRQ interrupt 58 | #endif 59 | 60 | // Setup CSN/CE ports 61 | #if nrfCSNport == 1 62 | P1DIR |= nrfCSNpin; 63 | #elif nrfCSNport == 2 64 | P2DIR |= nrfCSNpin; 65 | #elif nrfCSNport == 3 66 | P3DIR |= nrfCSNpin; 67 | #endif 68 | CSN_DIS; 69 | 70 | #if nrfCEport == 1 71 | P1DIR |= nrfCEpin; 72 | #elif nrfCEport == 2 73 | P2DIR |= nrfCEpin; 74 | #elif nrfCEport == 3 75 | P3DIR |= nrfCEpin; 76 | #endif 77 | CE_DIS; 78 | } 79 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/recv-pushbtn-cmd.c: -------------------------------------------------------------------------------- 1 | /* recv-pushbtn-cmd 2 | * Receive pushbutton-press commands from a TX device (see g2452/send-pushbtn-cmd.c) 3 | * and blink the red LED to match. 4 | */ 5 | 6 | #include 7 | #include "msprf24.h" 8 | #include 9 | #include 10 | 11 | const uint8_t ouraddr[] = { 0xAB, 0xAC, 0xAD, 0xAE, 0x01 }; // Our RX address 12 | const uint8_t dummyaddr[] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; 13 | 14 | const uint8_t rfpayload[] = { 0xFF, 0x01, 0x01 }; // Data we expect to see indicating a valid pushbutton press 15 | 16 | int main() 17 | { 18 | uint8_t pktlen, pipeid; 19 | uint8_t rfbuf[32]; 20 | 21 | WDTCTL = WDTPW | WDTHOLD; 22 | DCOCTL = CALDCO_16MHZ; 23 | BCSCTL1 = CALBC1_16MHZ; 24 | BCSCTL2 = DIVS_2; // SMCLK = 8MHz 25 | 26 | // Initialize red LED 27 | P1DIR |= BIT6; 28 | P1OUT &= ~BIT6; 29 | 30 | // Initialize nRF24L01+ RF transceiver 31 | rf_crc = RF24_EN_CRC; 32 | rf_addr_width = 5; 33 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MAX; 34 | rf_channel = 8; 35 | msprf24_init(); 36 | msprf24_open_pipe(1, 1); // Receive pipe#1 (could use #0 too, as we don't do any TX...) 37 | msprf24_set_pipe_packetsize(1, 0); // Dynamic payload support 38 | 39 | w_rx_addr(1, (uint8_t*)ouraddr); 40 | msprf24_activate_rx(); // Start listening 41 | // Main loop 42 | while (1) { 43 | // Handle incoming nRF24 IRQs 44 | if (rf_irq & RF24_IRQ_FLAGGED) { 45 | msprf24_get_irq_reason(); 46 | 47 | if (rf_irq & RF24_IRQ_RX || msprf24_rx_pending()) { 48 | pktlen = r_rx_peek_payload_size(); 49 | if (!pktlen || pktlen > 32) { /* Erroneous >32byte packets need to be flushed right away 50 | * I have actually seen these occur, even with CRC enabled, and it 51 | * logjams the FIFOs because r_rx_payload() can't read them... 52 | */ 53 | flush_rx(); 54 | msprf24_irq_clear(RF24_IRQ_RX); 55 | } else { 56 | pipeid = r_rx_payload(pktlen, rfbuf); 57 | msprf24_irq_clear(RF24_IRQ_RX); 58 | if (pipeid == 1) { // Only paying attention to pipe#1 (this should always eval true) 59 | if (!memcmp(rfbuf, rfpayload, 3)) // Valid pushbutton packet 60 | P1OUT ^= BIT6; // Toggle red LED 61 | } 62 | } 63 | } 64 | } 65 | 66 | LPM4; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/ike-uscia-tx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "msprf24.h" 3 | #include "nrf_userconfig.h" 4 | #include "stdint.h" 5 | 6 | volatile unsigned int user; 7 | 8 | int main() 9 | { 10 | uint8_t addr[5]; 11 | uint8_t buf[32]; 12 | 13 | WDTCTL = WDTHOLD | WDTPW; 14 | DCOCTL = CALDCO_16MHZ; 15 | BCSCTL1 = CALBC1_16MHZ; 16 | BCSCTL2 = DIVS_1; // SMCLK = DCOCLK/2 17 | // SPI (USCI) uses SMCLK, prefer SMCLK < 10MHz (SPI speed limit for nRF24 = 10MHz) 18 | 19 | // Red, Green LED used for status 20 | P1DIR |= 0x41; 21 | P1OUT &= ~0x41; 22 | 23 | user = 0xFE; 24 | 25 | /* Initial values for nRF24L01+ library config variables */ 26 | rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit 27 | rf_addr_width = 5; 28 | rf_speed_power = RF24_SPEED_1MBPS | RF24_POWER_0DBM; 29 | rf_channel = 120; 30 | 31 | msprf24_init(); // All RX pipes closed by default 32 | msprf24_set_pipe_packetsize(0, 32); 33 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst enabled for receiving Auto-ACKs 34 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 35 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 36 | 37 | // Transmit to 'rad01' (0x72 0x61 0x64 0x30 0x31) 38 | msprf24_standby(); 39 | user = msprf24_current_state(); 40 | addr[0] = 0xDE; addr[1] = 0xAD; addr[2] = 0xBE; addr[3] = 0xEF; addr[4] = 0x00; 41 | w_tx_addr(addr); 42 | w_rx_addr(0, addr); // Pipe 0 receives auto-ack's, autoacks are sent back to the TX addr so the PTX node 43 | // needs to listen to the TX addr on pipe#0 to receive them. 44 | 45 | while(1){ 46 | __delay_cycles(800000); 47 | if(buf[0]=='0'){buf[0] = '1';buf[1] = '0';} 48 | else {buf[0] = '0';buf[1] = '1';} 49 | w_tx_payload(32, buf); 50 | msprf24_activate_tx(); 51 | LPM4; 52 | 53 | if (rf_irq & RF24_IRQ_FLAGGED) { 54 | rf_irq &= ~RF24_IRQ_FLAGGED; 55 | 56 | msprf24_get_irq_reason(); 57 | if (rf_irq & RF24_IRQ_TX){ 58 | P1OUT &= ~BIT0; // Red LED off 59 | P1OUT |= 0x40; // Green LED on 60 | } 61 | if (rf_irq & RF24_IRQ_TXFAILED){ 62 | P1OUT &= ~BIT6; // Green LED off 63 | P1OUT |= BIT0; // Red LED on 64 | } 65 | 66 | msprf24_irq_clear(rf_irq); 67 | user = msprf24_get_last_retransmits(); 68 | } 69 | } 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /nrf_userconfig.h: -------------------------------------------------------------------------------- 1 | /* nrf_userconfig.h 2 | * User configuration of nRF24L01+ connectivity parameters, e.g. 3 | * IRQ, CSN, CE pin assignments, Serial SPI driver type 4 | * 5 | * 6 | * Copyright (c) 2012, Eric Brundick 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software for any purpose 9 | * with or without fee is hereby granted, provided that the above copyright notice 10 | * and this permission notice appear in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 13 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 14 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 15 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 16 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 17 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | #ifndef _NRF_USERCONFIG_H 21 | #define _NRF_USERCONFIG_H 22 | 23 | /* CPU clock cycles for the specified amounts of time--accurate minimum delays 24 | * required for reliable operation of the nRF24L01+'s state machine. 25 | */ 26 | /* Settings for 1MHz MCLK. 27 | #define DELAY_CYCLES_5MS 5000 28 | #define DELAY_CYCLES_130US 130 29 | #define DELAY_CYCLES_15US 15 30 | */ 31 | 32 | /* Settings for 8MHz MCLK. 33 | #define DELAY_CYCLES_5MS 40000 34 | #define DELAY_CYCLES_130US 1040 35 | #define DELAY_CYCLES_15US 120 36 | */ 37 | 38 | /* Settings for 16MHz MCLK */ 39 | #define DELAY_CYCLES_5MS 80000 40 | #define DELAY_CYCLES_130US 2080 41 | #define DELAY_CYCLES_15US 240 42 | 43 | /* Settings for 24MHz MCLK. 44 | #define DELAY_CYCLES_5MS 120000 45 | #define DELAY_CYCLES_130US 3120 46 | #define DELAY_CYCLES_15US 360 47 | */ 48 | 49 | /* SPI port--Select which USCI port we're using. 50 | * Applies only to USCI devices. USI users can keep these 51 | * commented out. 52 | */ 53 | //#define SPI_DRIVER_USCI_A 1 54 | #define SPI_DRIVER_USCI_B 1 55 | 56 | 57 | /* Operational pins -- IRQ, CE, CSN (SPI chip-select) 58 | */ 59 | 60 | /* IRQ */ 61 | #define nrfIRQport 2 62 | #define nrfIRQpin BIT2 63 | 64 | /* CSN SPI chip-select */ 65 | #define nrfCSNport 2 66 | #define nrfCSNportout P2OUT 67 | #define nrfCSNpin BIT1 68 | 69 | /* CE Chip-Enable (used to put RF transceiver on-air for RX or TX) */ 70 | #define nrfCEport 2 71 | #define nrfCEportout P2OUT 72 | #define nrfCEpin BIT0 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /TESTPROGS/g2452/ping-remote-led-every500ms-using-vlo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "msprf24.h" 4 | #include "nrf_userconfig.h" 5 | 6 | volatile unsigned int user; 7 | volatile int wdtsleep; 8 | 9 | /* Sleep for * 47ms */ 10 | void wdt_sleep(unsigned int cycles) 11 | { 12 | wdtsleep = cycles; 13 | WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL | WDTIS1; // WDT interval = 512 VLOCLK cycles, about 47ms 14 | IFG1 &= ~WDTIFG; 15 | IE1 |= WDTIE; 16 | LPM3; 17 | WDTCTL = WDTPW | WDTHOLD; 18 | } 19 | 20 | // WDT overflow/STOP 21 | #pragma vector=WDT_VECTOR 22 | __interrupt void WDT_ISR(void) 23 | { 24 | if (wdtsleep) { 25 | wdtsleep--; 26 | } else { 27 | IFG1 &= ~WDTIFG; 28 | IE1 &= ~WDTIE; 29 | __bic_SR_register_on_exit(LPM3_bits); 30 | } 31 | } 32 | 33 | 34 | int main() 35 | { 36 | uint8_t addr[5]; 37 | uint8_t buf[32]; 38 | 39 | WDTCTL = WDTHOLD | WDTPW; 40 | DCOCTL = CALDCO_16MHZ; 41 | BCSCTL1 = CALBC1_16MHZ; 42 | BCSCTL2 = DIVS_2; // SMCLK = DCOCLK/4 43 | BCSCTL3 = LFXT1S_2; // ACLK = VLOCLK/1 44 | BCSCTL3 &= ~(XT2OF|LFXT1OF); 45 | 46 | wdtsleep = 0; 47 | 48 | // SPI (USI) uses SMCLK, prefer SMCLK=DCO (no clock division) 49 | user = 0xFE; 50 | 51 | /* Initial values for nRF24L01+ library config variables */ 52 | rf_crc = RF24_EN_CRC; // CRC enabled, 8-bit 53 | rf_addr_width = 5; 54 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MAX; 55 | rf_channel = 120; 56 | msprf24_init(); // All RX pipes closed by default 57 | msprf24_set_pipe_packetsize(0, 0); 58 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst enabled for receiving Auto-ACKs 59 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 60 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 61 | 62 | // Transmit to 0xDEADBEEF01 63 | msprf24_standby(); 64 | user = msprf24_current_state(); 65 | addr[0] = 'r'; addr[1] = 'a'; addr[2] = 'd'; addr[3] = '0'; addr[4] = '1'; 66 | memcpy(addr, "\xDE\xAD\xBE\xEF\x01", 5); 67 | w_tx_addr(addr); 68 | w_rx_addr(0, addr); // Pipe 0 receives auto-ack's, autoacks are sent back to the TX addr so the PTX node 69 | // needs to listen to the TX addr on pipe#0 to receive them. 70 | buf[0] = '0'; 71 | buf[1] = '\0'; 72 | while(1) { 73 | if (rf_irq & RF24_IRQ_FLAGGED) { // Just acknowledging previous packet here 74 | msprf24_get_irq_reason(); 75 | msprf24_irq_clear(RF24_IRQ_MASK); 76 | user = msprf24_get_last_retransmits(); 77 | } else { // WDT sleep completed, send a new packet 78 | if (buf[0] == '1') 79 | buf[0] = '0'; 80 | else 81 | buf[0] = '1'; 82 | w_tx_payload(2, buf); 83 | msprf24_activate_tx(); 84 | } 85 | 86 | wdt_sleep(10); // ~500ms LPM3 sleep 87 | } 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /TESTPROGS/g2553/ping-remote-led-every500ms-using-vlo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "msprf24.h" 4 | #include "nrf_userconfig.h" 5 | 6 | volatile unsigned int user; 7 | volatile int wdtsleep; 8 | 9 | /* Sleep for * 47ms */ 10 | void wdt_sleep(unsigned int cycles) 11 | { 12 | wdtsleep = cycles; 13 | WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL | WDTIS1; // WDT interval = 512 VLOCLK cycles, about 47ms 14 | IFG1 &= ~WDTIFG; 15 | IE1 |= WDTIE; 16 | LPM3; 17 | WDTCTL = WDTPW | WDTHOLD; 18 | } 19 | 20 | // WDT overflow/STOP 21 | #pragma vector=WDT_VECTOR 22 | __interrupt void WDT_ISR(void) 23 | { 24 | if (wdtsleep) { 25 | wdtsleep--; 26 | } else { 27 | IFG1 &= ~WDTIFG; 28 | IE1 &= ~WDTIE; 29 | __bic_SR_register_on_exit(LPM3_bits); 30 | } 31 | } 32 | 33 | 34 | int main() 35 | { 36 | uint8_t addr[5]; 37 | uint8_t buf[32]; 38 | 39 | WDTCTL = WDTHOLD | WDTPW; 40 | DCOCTL = CALDCO_16MHZ; 41 | BCSCTL1 = CALBC1_16MHZ; 42 | BCSCTL2 = DIVS_2; // SMCLK = DCOCLK/4 43 | BCSCTL3 = LFXT1S_2; // ACLK = VLOCLK/1 44 | BCSCTL3 &= ~(XT2OF|LFXT1OF); 45 | 46 | wdtsleep = 0; 47 | 48 | // SPI (USI) uses SMCLK, prefer SMCLK=DCO (no clock division) 49 | user = 0xFE; 50 | 51 | /* Initial values for nRF24L01+ library config variables */ 52 | rf_crc = RF24_EN_CRC; // CRC enabled, 8-bit 53 | rf_addr_width = 5; 54 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MAX; 55 | rf_channel = 120; 56 | msprf24_init(); // All RX pipes closed by default 57 | msprf24_set_pipe_packetsize(0, 0); 58 | msprf24_open_pipe(0, 1); // Open pipe#0 with Enhanced ShockBurst enabled for receiving Auto-ACKs 59 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 60 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 61 | 62 | // Transmit to 0xDEADBEEF01 63 | msprf24_standby(); 64 | user = msprf24_current_state(); 65 | addr[0] = 'r'; addr[1] = 'a'; addr[2] = 'd'; addr[3] = '0'; addr[4] = '1'; 66 | memcpy(addr, "\xDE\xAD\xBE\xEF\x01", 5); 67 | w_tx_addr(addr); 68 | w_rx_addr(0, addr); // Pipe 0 receives auto-ack's, autoacks are sent back to the TX addr so the PTX node 69 | // needs to listen to the TX addr on pipe#0 to receive them. 70 | buf[0] = '0'; 71 | buf[1] = '\0'; 72 | while(1) { 73 | if (rf_irq & RF24_IRQ_FLAGGED) { // Just acknowledging previous packet here 74 | msprf24_get_irq_reason(); 75 | msprf24_irq_clear(RF24_IRQ_MASK); 76 | user = msprf24_get_last_retransmits(); 77 | } else { // WDT sleep completed, send a new packet 78 | if (buf[0] == '1') 79 | buf[0] = '0'; 80 | else 81 | buf[0] = '1'; 82 | w_tx_payload(2, buf); 83 | msprf24_activate_tx(); 84 | } 85 | 86 | wdt_sleep(10); // ~500ms LPM3 sleep 87 | } 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /Pkt_CPP/Pkt.h: -------------------------------------------------------------------------------- 1 | /* Pkt - Logical Link Control layer for nRF24L01+ radio communications 2 | * 3 | * Based on packet_processor.c concept code used in Grill Monitor and other msprf24-based 4 | * applications. 5 | * 6 | * 7 | * 8 | * Copyright (c) 2015, Eric Brundick 9 | * Adapted from the Energia/Arduino lib of the same name by Eric Brundick (2014) 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose 12 | * with or without fee is hereby granted, provided that the above copyright notice 13 | * and this permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 16 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 17 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 18 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 19 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 20 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | 24 | #ifndef PKT_H 25 | #define PKT_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | typedef uint8_t boolean; 33 | 34 | #ifndef true 35 | #define true 1 36 | #endif 37 | 38 | #ifndef false 39 | #define false 0 40 | #endif 41 | 42 | #define PKT_MAX_PKTLEN 16 43 | #define PKT_DEFAULT_QUEUE_DEPTH 3 44 | #define PKT_DEFAULT_PROGID_REGISTRATIONS 4 45 | 46 | typedef void(*PKT_CALLBACK)(const uint8_t progID, const int len, const void *buffer); 47 | 48 | typedef struct { 49 | uint8_t progID; 50 | uint8_t rfaddr[5]; 51 | uint8_t pktlen; 52 | uint8_t buffer[PKT_MAX_PKTLEN]; 53 | } PktTXQueue; 54 | 55 | typedef struct { 56 | uint8_t progID; 57 | PKT_CALLBACK callback; 58 | } PktRXprogram; 59 | 60 | class Pkt { 61 | private: 62 | PktTXQueue **txQueue; 63 | void *txQueueArrayBacking; 64 | unsigned int txQueueDepth; 65 | 66 | PktRXprogram **progRegs; 67 | unsigned int progRegMaxCount; 68 | void *progRegsArrayBacking; 69 | 70 | boolean do_deepsleep_after_tx; 71 | PKT_CALLBACK unknownProgramCallback; 72 | PKT_CALLBACK defaultCallback; 73 | 74 | public: 75 | Pkt(); 76 | void setTXqueueDepth(unsigned int queuedepth); 77 | void setMaxPrograms(unsigned int maxprogs); 78 | void begin(); 79 | void end(); 80 | 81 | // TX methods 82 | boolean send(const uint8_t progID, const uint8_t *rfaddr, const int len, const void *buffer); 83 | void flush(); 84 | void setModeTXonly(boolean yesno); 85 | 86 | // RX methods 87 | boolean available(); 88 | void loop(); 89 | 90 | boolean attachAllPrograms(PKT_CALLBACK callback); 91 | boolean attachProgram(const uint8_t progID, PKT_CALLBACK callback); 92 | boolean detachAllPrograms(); 93 | boolean detachProgram(const uint8_t progID); 94 | 95 | boolean attachUnknownProgram(PKT_CALLBACK callback); 96 | boolean detachUnknownProgram(); 97 | }; 98 | 99 | 100 | #endif /* PKT_H */ 101 | -------------------------------------------------------------------------------- /TESTPROGS/g2452/send-pushbtn-cmd.c: -------------------------------------------------------------------------------- 1 | /* Pushbutton Command Sender 2 | * 3 | * Each press of the pushbutton (debounced by a 92ms wait) sends a packet OTA to 4 | * address 0xAB 0xAC 0xAD 0xAE 0x01 5 | * with the following byte sequence: 6 | * +----+----+----+ 7 | * |0xFF|0x01|0x01| 8 | * +----+----+----+ 9 | * 10 | */ 11 | 12 | #include 13 | #include "msprf24.h" 14 | #include 15 | 16 | volatile uint16_t sleep_counter; 17 | volatile uint8_t pushbutton_int; 18 | 19 | const uint8_t dummyaddr[] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; 20 | const uint8_t destaddr[] = { 0xAB, 0xAC, 0xAD, 0xAE, 0x01 }; // Dest addr for payload 21 | 22 | const uint8_t rfpayload[] = { 0xFF, 0x01, 0x01 }; // Data to send OTA 23 | 24 | int main() 25 | { 26 | uint8_t txretry_count=0, txretry_latch=0; 27 | 28 | WDTCTL = WDTPW | WDTHOLD; 29 | DCOCTL = CALDCO_16MHZ; 30 | BCSCTL1 = CALBC1_16MHZ; 31 | BCSCTL2 = DIVS_2; // SMCLK = 8MHz 32 | BCSCTL3 = LFXT1S_2; // ACLK = VLO 33 | __delay_cycles(8000); // Wait for VLO settling 34 | while (BCSCTL3 & LFXT1OF) 35 | ; 36 | 37 | // Initialize P1.3 (pushbutton) 38 | P1DIR &= ~BIT3; 39 | P1REN |= BIT3; 40 | P1OUT |= BIT3; // Pullup resistor 41 | P1IES |= BIT3; // Active LOW 42 | P1IFG &= ~BIT3; 43 | P1IE |= BIT3; // Enable IRQ 44 | 45 | // Initialize WDT timer 46 | IFG1 &= ~WDTIFG; 47 | IE1 |= WDTIE; 48 | sleep_counter = 0; 49 | pushbutton_int = 0; 50 | 51 | // Initialize nRF24L01+ RF transceiver 52 | rf_crc = RF24_EN_CRC; 53 | rf_addr_width = 5; 54 | rf_speed_power = RF24_SPEED_MIN | RF24_POWER_MAX; 55 | rf_channel = 8; 56 | msprf24_init(); 57 | msprf24_open_pipe(0, 1); // This is only for receiving auto-ACK packets. 58 | msprf24_set_pipe_packetsize(0, 0); // Dynamic payload support 59 | // Note: Pipe#0 is hardcoded in the transceiver hardware as the designated "pipe" for a TX node to receive 60 | // auto-ACKs. This does not have to match the pipe# used on the RX side. 61 | 62 | // Main loop 63 | while (1) { 64 | // Handle any nRF24 IRQs first 65 | if (rf_irq & RF24_IRQ_FLAGGED) { 66 | msprf24_get_irq_reason(); 67 | 68 | if (rf_irq & RF24_IRQ_TX) { 69 | msprf24_irq_clear(RF24_IRQ_TX); 70 | flush_tx(); 71 | w_rx_addr(0, (uint8_t*)dummyaddr); 72 | msprf24_powerdown(); 73 | } 74 | 75 | if (rf_irq & RF24_IRQ_TXFAILED) { 76 | msprf24_irq_clear(RF24_IRQ_TXFAILED); 77 | if (txretry_count) { 78 | txretry_count--; 79 | if (!txretry_latch) { 80 | txretry_latch = 1; 81 | tx_reuse_lastpayload(); 82 | } 83 | pulse_ce(); 84 | } else { 85 | // Ran out of retries, give up 86 | flush_tx(); 87 | w_rx_addr(0, (uint8_t*)dummyaddr); 88 | txretry_latch = 0; 89 | msprf24_powerdown(); 90 | } 91 | } 92 | // No need to handle RX packets since we never enter RX mode 93 | } 94 | 95 | if (pushbutton_int) { 96 | // Software debounce (~92ms with VLOCLK) 97 | sleep_counter = 2; 98 | WDTCTL = WDT_ADLY_16; 99 | while (sleep_counter) 100 | LPM3; 101 | WDTCTL = WDTPW | WDTHOLD; 102 | if ( !(P1IN & BIT3) ) { // P1.3 still active(LOW)? 103 | // OK, looks like we have a real button press... 104 | // But first, do we already have a packet in flight: 105 | if (msprf24_queue_state() & RF24_QUEUE_TXEMPTY) { 106 | // We don't, so proceed. 107 | msprf24_standby(); 108 | w_tx_addr((uint8_t*)destaddr); 109 | w_rx_addr(0, (uint8_t*)destaddr); // To catch the auto-ACK reply 110 | w_tx_payload(3, (uint8_t*)rfpayload); 111 | msprf24_activate_tx(); 112 | txretry_count = 20; // Try damned hard to send this, retrying up to 20 * ARC times (ARC=15 by default) 113 | } 114 | // If not, ignore this press. 115 | } 116 | pushbutton_int = 0; 117 | P1IFG &= ~BIT3; // Clear any pending IRQs that may have fired during our debounce 118 | P1IE |= BIT3; 119 | } 120 | 121 | LPM3; 122 | } 123 | } 124 | 125 | // WDT overflow/timer 126 | #pragma vector=WDT_VECTOR 127 | __interrupt void WDT_ISR(void) 128 | { 129 | IFG1 &= ~WDTIFG; 130 | if (sleep_counter) 131 | sleep_counter--; 132 | else 133 | __bic_SR_register_on_exit(LPM3_bits); 134 | } 135 | 136 | // PORT1 interrupt ISR 137 | #pragma vector=PORT1_VECTOR 138 | __interrupt void P1_ISR(void) 139 | { 140 | if (P1IFG & BIT3) { 141 | P1IFG &= ~BIT3; 142 | P1IE &= ~BIT3; 143 | pushbutton_int = 1; 144 | __bic_SR_register_on_exit(LPM3_bits); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /nRF24L01.h: -------------------------------------------------------------------------------- 1 | /* nRF24L01.h 2 | * Register definitions for manipulating the Nordic Semiconductor 3 | * nRF24L01+ RF transceiver chipsets. 4 | * 5 | 6 | Copyright (c) 2007 Stefan Engelke 7 | Some parts copyright (c) 2012 Eric Brundick 8 | 9 | Permission is hereby granted, free of charge, to any person 10 | obtaining a copy of this software and associated documentation 11 | files (the "Software"), to deal in the Software without 12 | restriction, including without limitation the rights to use, copy, 13 | modify, merge, publish, distribute, sublicense, and/or sell copies 14 | of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be 18 | included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | */ 29 | #ifndef _NRF24L01_H 30 | #define _NRF24L01_H 31 | 32 | /* Register Map */ 33 | #define RF24_CONFIG 0x00 34 | #define RF24_EN_AA 0x01 35 | #define RF24_EN_RXADDR 0x02 36 | #define RF24_SETUP_AW 0x03 37 | #define RF24_SETUP_RETR 0x04 38 | #define RF24_RF_CH 0x05 39 | #define RF24_RF_SETUP 0x06 40 | #define RF24_STATUS 0x07 41 | #define RF24_OBSERVE_TX 0x08 42 | #define RF24_CD 0x09 43 | #define RF24_RPD 0x09 44 | #define RF24_RX_ADDR_P0 0x0A 45 | #define RF24_RX_ADDR_P1 0x0B 46 | #define RF24_RX_ADDR_P2 0x0C 47 | #define RF24_RX_ADDR_P3 0x0D 48 | #define RF24_RX_ADDR_P4 0x0E 49 | #define RF24_RX_ADDR_P5 0x0F 50 | #define RF24_TX_ADDR 0x10 51 | #define RF24_RX_PW_P0 0x11 52 | #define RF24_RX_PW_P1 0x12 53 | #define RF24_RX_PW_P2 0x13 54 | #define RF24_RX_PW_P3 0x14 55 | #define RF24_RX_PW_P4 0x15 56 | #define RF24_RX_PW_P5 0x16 57 | #define RF24_FIFO_STATUS 0x17 58 | #define RF24_DYNPD 0x1C 59 | #define RF24_FEATURE 0x1D 60 | 61 | /* Register Bits */ 62 | #define RF24_MASK_RX_DR BIT6 63 | #define RF24_MASK_TX_DS BIT5 64 | #define RF24_MASK_MAX_RT BIT4 65 | #define RF24_EN_CRC BIT3 66 | #define RF24_CRCO BIT2 67 | #define RF24_PWR_UP BIT1 68 | #define RF24_PRIM_RX BIT0 69 | #define RF24_ENAA_P5 BIT5 70 | #define RF24_ENAA_P4 BIT4 71 | #define RF24_ENAA_P3 BIT3 72 | #define RF24_ENAA_P2 BIT2 73 | #define RF24_ENAA_P1 BIT1 74 | #define RF24_ENAA_P0 BIT0 75 | #define RF24_ERX_P5 BIT5 76 | #define RF24_ERX_P4 BIT4 77 | #define RF24_ERX_P3 BIT3 78 | #define RF24_ERX_P2 BIT2 79 | #define RF24_ERX_P1 BIT1 80 | #define RF24_ERX_P0 BIT0 81 | #define RF24_AW BIT0 82 | #define RF24_ARD BIT4 83 | #define RF24_ARC BIT0 84 | #define RF24_PLL_LOCK BIT4 85 | #define RF24_CONT_WAVE BIT7 86 | #define RF24_RF_DR BIT3 87 | #define RF24_RF_DR_LOW BIT5 88 | #define RF24_RF_DR_HIGH BIT3 89 | #define RF24_RF_PWR BIT1 90 | #define RF24_LNA_HCURR BIT0 91 | #define RF24_RX_DR BIT6 92 | #define RF24_TX_DS BIT5 93 | #define RF24_MAX_RT BIT4 94 | #define RF24_RX_P_NO BIT1 95 | #define RF24_TX_FULL BIT0 96 | #define RF24_PLOS_CNT BIT4 97 | #define RF24_ARC_CNT BIT0 98 | #define RF24_TX_REUSE BIT6 99 | #define RF24_FIFO_FULL BIT5 100 | #define RF24_TX_EMPTY BIT4 101 | #define RF24_RX_FULL BIT1 102 | #define RF24_RX_EMPTY BIT0 103 | #define RF24_EN_DPL BIT2 104 | #define RF24_EN_ACK_PAY BIT1 105 | #define RF24_EN_DYN_ACK BIT0 106 | 107 | /* Instructions */ 108 | #define RF24_R_REGISTER 0x00 109 | #define RF24_W_REGISTER 0x20 110 | #define RF24_REGISTER_MASK 0x1F 111 | #define RF24_R_RX_PAYLOAD 0x61 112 | #define RF24_W_TX_PAYLOAD 0xA0 113 | #define RF24_FLUSH_TX 0xE1 114 | #define RF24_FLUSH_RX 0xE2 115 | #define RF24_REUSE_TX_PL 0xE3 116 | #define RF24_R_RX_PL_WID 0x60 117 | #define RF24_W_ACK_PAYLOAD 0xA8 118 | #define RF24_W_TX_PAYLOAD_NOACK 0xB0 119 | #define RF24_NOP 0xFF 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /Pkt_CPP/README.txt: -------------------------------------------------------------------------------- 1 | Pkt is a C++ library originally developed for Energia/Enrf24 which I have adapted to use 2 | msprf24 so that it may be used in non-Arduino related applications. 3 | 4 | This does require that you use the C++ compiler for your whole project instead of 5 | straight C, however the rest of your applicaton certainly may be coded in C. 6 | 7 | Pkt provides an abstraction layer on top of the nRF24L01+ which interprets packets TX or 8 | RX in terms of sub-packets delimited by an 8-bit "program ID" (0xFF is reserved) 9 | followed by an 8-bit length identifier. Multiple "packets" can be stuffed inside a 10 | single <=32 byte nRF24 payload. 11 | 12 | On the TX side, you can use Pkt's send() followed by flush() functions to pack some data 13 | into its buffers and then transmit them out. These packets can be assigned to different 14 | nRF24L01+ RF addresses, and it will work out how it wants to pack subsequent packets 15 | intended for the same RF address if appropriate. 16 | 17 | Multiple send()'s may be run ending in a single flush(); the default "TX queue depth" 18 | is 3, i.e. three send()'s can be run before it kicks back false and silently discards 19 | your next send(). The payload within each packet can't exceed 16 bytes by default. 20 | 16 bytes would be 18 in the frame (add program ID + length bytes), so that doesn't 21 | quite fit 2 packets per nRF24 frame. 22 | 23 | On the RX side, you will set up the pipes using msprf24 and the RX address with msprf24's 24 | w_rx_addr - keep in mind the Pkt interface is only designed to know about pipe#1 for RX, 25 | since it's derived from Enrf24 which only acknowledged the presence of one RX pipe. 26 | It should in theory work with multiple RX pipes though, but it won't be able to 27 | discriminate how it handles packets coming from pipe #1 vs pipe #2, 3, 4, etc. beyond 28 | the fact that it will interpret each frame's contents in terms of its "program ID" paradigm. 29 | 30 | For RX, the library is designed for the calling firmware to assign "callback functions" 31 | to each program ID. When an RX packet is detected by the user's firmware, the user's 32 | firmware should call Pkt's loop() function to "handle" the incoming RX request; loop() 33 | is where the payload is read and callback functions are dispatched. 34 | 35 | A typical while() loop in a C application may look like this: 36 | 37 | Pkt radio; 38 | 39 | int main() { 40 | ....blahblah... 41 | while(1) { 42 | if (rf_irq & RF24_IRQ_FLAGGED) { 43 | if (radio.available()) { 44 | radio.loop(); // Handle the RX and clear IRQs as necessary 45 | } 46 | } 47 | 48 | if (!(rf_irq & RF24_IRQ_FLAGGED)) 49 | LPM4; 50 | } 51 | return 0; 52 | } 53 | 54 | 55 | A few items of trivia to note: 56 | 57 | 1. This library uses malloc(). It might go away. Probably would be appropriate 58 | to have the library stick to #define's defaults instead of letting it be 59 | dynamic; the dynamic TX queue depth and program ID callback registry was intended 60 | to make it intuitive and configurable for Energia/Arduino users. 61 | 62 | 2. There is a function setModeTXonly(true/false) which will tailor Pkt's behavior 63 | during TX. If this is false, the Pkt library will automatically issue 64 | msprf24_activate_rx() after TX is done. Otherwise, when "true", the Pkt library 65 | will automatically issue msprf24_powerdown() after TX is done. 66 | 67 | 3. The callback function prototype is: 68 | typedef void(*PKT_CALLBACK)(const uint8_t progID, const int len, const void *buffer); 69 | so all callback functions expect to return void, but take a const uint8_t, 70 | const int, and const void * 71 | 72 | 4. The function prototypes for all the callback attach/detach functions is: 73 | 74 | boolean attachAllPrograms(PKT_CALLBACK callback); 75 | boolean attachProgram(const uint8_t progID, PKT_CALLBACK callback); 76 | boolean detachAllPrograms(); 77 | boolean detachProgram(const uint8_t progID); 78 | 79 | boolean attachUnknownProgram(PKT_CALLBACK callback); 80 | boolean detachUnknownProgram(); 81 | 82 | This should be self-explanatory. If you do not issue attachUnknownProgram 83 | or attachAllPrograms but you receive a packet or a subset of a received frame 84 | happens to include a program ID that you do not have a callback for, this data is 85 | silently discarded by the library. 86 | 87 | 5. Default # of individual program IDs that can be registered with a callback is 4. 88 | This can be adjusted using setMaxPrograms() - note this call will induce a free() 89 | followed by malloc() to allocate a new array of program ID + callback function 90 | pointer structures. 91 | ** Note: If I do decide to extricate malloc/free, this function along with the 92 | setTXqueueDepth function will disappear. 93 | 94 | 6. During active TX mode, i.e. within the library's flush() function, the library 95 | will enter LPM0 while waiting for the nRF24 IRQ to fire and rf_irq to get updated. 96 | 97 | 7. If AutoACK is enabled, and TX fails, it does so silently; this library will not 98 | try to re-submit the TX that failed and it has no way of notifying you that it 99 | failed. Might have to think this one through deeper if it's important enough. 100 | -------------------------------------------------------------------------------- /msprf24.h: -------------------------------------------------------------------------------- 1 | /* msprf24.h 2 | * MSP430 library for interfacing with the nRF24L01+ RF transceiver by 3 | * Nordic Semiconductor. 4 | * 5 | * 6 | * Copyright (c) 2012, Eric Brundick 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software for any purpose 9 | * with or without fee is hereby granted, provided that the above copyright notice 10 | * and this permission notice appear in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 13 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 14 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 15 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 16 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 17 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | #ifndef _MSPRF24_H_ 21 | #define _MSPRF24_H_ 22 | 23 | #include 24 | #include "nRF24L01.h" 25 | 26 | /* Configuration variables used to tune RF settings during initialization and for 27 | * runtime reconfiguration. You should define all 4 of these before running msprf24_init(); 28 | */ 29 | extern uint8_t rf_crc; 30 | extern uint8_t rf_addr_width; 31 | extern uint8_t rf_speed_power; 32 | extern uint8_t rf_channel; 33 | 34 | /* Status variable updated every time SPI I/O is performed */ 35 | extern uint8_t rf_status; 36 | /* Test this against RF24_IRQ_FLAGGED to see if the nRF24's IRQ was raised; it also 37 | * holds the last recorded IRQ status from msprf24_irq_get_reason(); 38 | */ 39 | extern volatile uint8_t rf_irq; 40 | 41 | /* RF speed settings -- nRF24L01+ compliant, older nRF24L01 does not have 2Mbps. */ 42 | #define RF24_SPEED_250KBPS 0x20 43 | #define RF24_SPEED_1MBPS 0x00 44 | #define RF24_SPEED_2MBPS 0x08 45 | #define RF24_SPEED_MAX RF24_SPEED_2MBPS 46 | #define RF24_SPEED_MIN RF24_SPEED_250KBPS 47 | #define RF24_SPEED_MASK 0x28 48 | 49 | /* RF transmit power settings */ 50 | #define RF24_POWER_7DBM 0x07 51 | // ^ 7dBm available with SI24R1 Taiwanese knockoff modules 52 | #define RF24_POWER_0DBM 0x06 53 | #define RF24_POWER_MINUS6DBM 0x04 54 | #define RF24_POWER_MINUS12DBM 0x02 55 | #define RF24_POWER_MINUS18DBM 0x00 56 | #define RF24_POWER_MAX RF24_POWER_0DBM 57 | #define RF24_POWER_MIN RF24_POWER_MINUS18DBM 58 | #define RF24_POWER_MASK 0x07 59 | 60 | /* Available states for the transceiver's state machine */ 61 | #define RF24_STATE_NOTPRESENT 0x00 62 | #define RF24_STATE_POWERDOWN 0x01 63 | #define RF24_STATE_STANDBY_I 0x02 64 | #define RF24_STATE_STANDBY_II 0x03 65 | #define RF24_STATE_PTX 0x04 66 | #define RF24_STATE_PRX 0x05 67 | #define RF24_STATE_TEST 0x06 68 | 69 | /* IRQ "reasons" that can be tested. */ 70 | #define RF24_IRQ_TXFAILED 0x10 71 | #define RF24_IRQ_TX 0x20 72 | #define RF24_IRQ_RX 0x40 73 | #define RF24_IRQ_MASK 0x70 74 | // Bit 7 used to signify that the app should check IRQ status, without 75 | // wasting time in the interrupt vector trying to do so itself. 76 | #define RF24_IRQ_FLAGGED 0x80 77 | 78 | /* Queue FIFO states that can be tested. */ 79 | #define RF24_QUEUE_TXFULL RF24_FIFO_FULL 80 | #define RF24_QUEUE_TXEMPTY RF24_TX_EMPTY 81 | #define RF24_QUEUE_RXFULL RF24_RX_FULL 82 | #define RF24_QUEUE_RXEMPTY RF24_RX_EMPTY 83 | 84 | 85 | /* FUNCTIONS! */ 86 | 87 | // SPI driver needs to provide these 88 | void spi_init(); 89 | uint8_t spi_transfer(uint8_t); // SPI xfer 1 byte 90 | uint16_t spi_transfer16(uint16_t); // SPI xfer 2 bytes 91 | uint16_t spi_transfer9(uint16_t); // SPI xfer 9 bits (courtesy for driving LCD screens) 92 | 93 | // Register & FIFO I/O 94 | uint8_t r_reg(uint8_t addr); 95 | void w_reg(uint8_t addr, uint8_t data); 96 | void w_tx_addr(uint8_t *addr); // Configure TX address to send next packet 97 | void w_rx_addr(uint8_t pipe, uint8_t *addr); // Configure RX address of "rf_addr_width" size into the specified pipe 98 | void w_tx_payload(uint8_t len, uint8_t *data); 99 | void w_tx_payload_noack(uint8_t len, uint8_t *data); /* Only used in auto-ack mode with RF24_EN_DYN_ACK enabled; 100 | * send this packet with no auto-ack. 101 | */ 102 | uint8_t r_rx_peek_payload_size(); // Peek size of incoming RX payload 103 | uint8_t r_rx_payload(uint8_t len, uint8_t *data); 104 | void flush_tx(); 105 | void flush_rx(); 106 | void tx_reuse_lastpayload(); /* Enable retransmitting contents of TX FIFO endlessly until flush_tx() or the FIFO contents are replaced. 107 | * Actual retransmits don't occur until CE pin is strobed using pulse_ce(); 108 | */ 109 | void pulse_ce(); // Pulse CE pin to activate retransmission of TX FIFO contents after tx_reuse_lastpayload(); 110 | void w_ack_payload(uint8_t pipe, uint8_t len, uint8_t *data); // Used when RF24_EN_ACK_PAY is enabled to manually ACK a received packet 111 | 112 | 113 | 114 | // Initialization and configuration 115 | void msprf24_init(); /* Set the various configuration variables before running this. 116 | * It will populate the channel/speed/power/default features/etc. values 117 | */ 118 | void msprf24_close_pipe(uint8_t pipeid); // Disable specified RX pipe 119 | void msprf24_close_pipe_all(); // Disable all RX pipes (used during initialization) 120 | void msprf24_open_pipe(uint8_t pipeid, uint8_t autoack); // Enable specified RX pipe, optionally turn auto-ack (Enhanced ShockBurst) on 121 | uint8_t msprf24_pipe_isopen(uint8_t pipeid); // Check if specified RX pipe is active 122 | void msprf24_set_pipe_packetsize(uint8_t pipe, uint8_t size); // Set static length of pipe's RX payloads (1-32), size=0 enables DynPD. 123 | void msprf24_set_retransmit_delay(uint16_t us); // 500-4000uS range, clamped by RF speed 124 | void msprf24_set_retransmit_count(uint8_t count); // 0-15 retransmits before MAX_RT (RF24_IRQ_TXFAILED) IRQ raised 125 | uint8_t msprf24_get_last_retransmits(); // # times a packet was retransmitted during last TX attempt 126 | uint8_t msprf24_get_lostpackets(); /* # of packets lost since last time the Channel was set. 127 | * Running msprf24_set_channel() without modifying rf_channel will reset this counter. 128 | */ 129 | uint8_t msprf24_is_alive(); // Hello World, test if chip is present and/or SPI is working. 130 | uint8_t msprf24_set_config(uint8_t cfgval); 131 | void msprf24_set_speed_power(); // Commit RF speed & TX power from rf_speed_power variable. 132 | void msprf24_set_channel(); // Commit RF channel setting from rf_channel variable. 133 | void msprf24_set_address_width(); // Commit Enhanced ShockBurst Address Width from rf_addr_width variable. 134 | void msprf24_enable_feature(uint8_t feature); /* Enable specified feature (RF24_EN_* from nRF24L01.h, except RF24_EN_CRC) */ 135 | void msprf24_disable_feature(uint8_t feature); /* Disable specified feature */ 136 | 137 | // Change chip state and activate I/O 138 | uint8_t msprf24_current_state(); // Get current state of the nRF24L01+ chip, test with RF24_STATE_* #define's 139 | void msprf24_powerdown(); // Enter Power-Down mode (0.9uA power draw) 140 | void msprf24_standby(); // Enter Standby-I mode (26uA power draw) 141 | void msprf24_activate_rx(); // Enable PRX mode (~12-14mA power draw) 142 | void msprf24_activate_tx(); // Enable Standby-II or PTX mode; TX FIFO contents will be sent over the air (~320uA STBY2, 7-11mA PTX) 143 | uint8_t msprf24_queue_state(); // Read FIFO_STATUS register; user should compare return value with RF24_QUEUE_* #define's 144 | uint8_t msprf24_scan(); // Scan current channel for RPD (looks for any signals > -64dBm) 145 | 146 | // IRQ handling 147 | uint8_t msprf24_rx_pending(); /* Query STATUS register to determine if RX FIFO data is available for reading. */ 148 | uint8_t msprf24_get_irq_reason(); /* Query STATUS register for the IRQ flags, test with RF24_IRQ_* #define's 149 | * Result is stored in rf_irq (note- RF24_IRQ_FLAGGED is not automatically cleared by this 150 | * function, that's the user's responsibility.) 151 | */ 152 | void msprf24_irq_clear(uint8_t irqflag); /* Clear specified Interrupt Flags (RF24_IRQ_* #define's) from the transceiver. 153 | * Required to allow further transmissions to continue. 154 | */ 155 | 156 | #endif 157 | -------------------------------------------------------------------------------- /Pkt_CPP/Pkt.cpp: -------------------------------------------------------------------------------- 1 | /* Pkt - Logical Link Control layer for nRF24L01+ radio communications 2 | * 3 | * Based on packet_processor.c concept code used in Grill Monitor and other msprf24-based 4 | * applications. 5 | * 6 | * Adapted to actually run off msprf24 to give a C++ packet handler flavor on top of 7 | * non-Energia/Arduino applications 8 | * 9 | * 10 | * Copyright (c) 2015, Eric Brundick 11 | * 12 | * Permission to use, copy, modify, and/or distribute this software for any purpose 13 | * with or without fee is hereby granted, provided that the above copyright notice 14 | * and this permission notice appear in all copies. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 17 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 18 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 19 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 20 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 21 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | 24 | #include "Pkt.h" 25 | #include "msprf24.h" 26 | #include "nRF24L01.h" 27 | 28 | 29 | void pkt_do_send_loop(); // Replacement for Enrf24::flush ... this is a loop that performs the TX 30 | 31 | /* Initialization, deinitialization and maintenance/utility functions */ 32 | Pkt::Pkt() 33 | { 34 | txQueueDepth = PKT_DEFAULT_QUEUE_DEPTH; 35 | progRegMaxCount = PKT_DEFAULT_PROGID_REGISTRATIONS; 36 | do_deepsleep_after_tx = false; 37 | 38 | txQueue = NULL; 39 | txQueueArrayBacking = NULL; 40 | progRegs = NULL; 41 | progRegsArrayBacking = NULL; 42 | unknownProgramCallback = NULL; 43 | defaultCallback = NULL; 44 | } 45 | 46 | void Pkt::setTXqueueDepth(unsigned int queuedepth) 47 | { 48 | unsigned int i; 49 | 50 | if (!queuedepth) 51 | return; 52 | 53 | if (txQueue != NULL) { // should always be true 54 | free(txQueue); 55 | free(txQueueArrayBacking); 56 | } 57 | txQueueDepth = queuedepth; 58 | txQueue = (PktTXQueue **)malloc(sizeof(PktTXQueue *) * txQueueDepth); 59 | txQueueArrayBacking = malloc(sizeof(PktTXQueue) * txQueueDepth); 60 | 61 | for (i=0; i < txQueueDepth; i++) { 62 | txQueue[i] = (PktTXQueue *)((uint8_t *)txQueueArrayBacking + sizeof(PktTXQueue)*i); 63 | txQueue[i]->progID = 0xFF; 64 | txQueue[i]->pktlen = 0; 65 | memset(txQueue[i]->rfaddr, 0, 5); 66 | memset(txQueue[i]->buffer, 0, PKT_MAX_PKTLEN); 67 | } 68 | } 69 | 70 | void Pkt::setMaxPrograms(unsigned int maxprogs) 71 | { 72 | unsigned int i; 73 | 74 | if (!maxprogs) 75 | return; 76 | 77 | if (progRegs != NULL) { // should always be true 78 | free(progRegs); 79 | free(progRegsArrayBacking); 80 | } 81 | progRegMaxCount = maxprogs; 82 | progRegs = (PktRXprogram **)malloc(sizeof(PktRXprogram *) * progRegMaxCount); 83 | progRegsArrayBacking = malloc(sizeof(PktRXprogram) * progRegMaxCount); 84 | 85 | for (i=0; i < progRegMaxCount; i++) { 86 | progRegs[i] = (PktRXprogram *)((uint8_t *)progRegsArrayBacking + sizeof(PktRXprogram)*i); 87 | progRegs[i]->progID = 0xFF; 88 | progRegs[i]->callback = NULL; 89 | } 90 | } 91 | 92 | void Pkt::begin() 93 | { 94 | setTXqueueDepth(txQueueDepth); 95 | setMaxPrograms(progRegMaxCount); 96 | 97 | if (!do_deepsleep_after_tx) 98 | msprf24_activate_rx(); 99 | } 100 | 101 | void Pkt::end() 102 | { 103 | if (txQueue != NULL) 104 | free(txQueue); 105 | if (progRegs != NULL) 106 | free(progRegs); 107 | msprf24_powerdown(); 108 | } 109 | 110 | /* Packet TX logic */ 111 | void Pkt::setModeTXonly(boolean yesno) 112 | { 113 | do_deepsleep_after_tx = yesno; 114 | if (do_deepsleep_after_tx) { 115 | msprf24_close_pipe(1); // #1 = RX pipe 116 | msprf24_powerdown(); 117 | } else { 118 | if (msprf24_current_state() != RF24_STATE_PRX) 119 | msprf24_activate_rx(); 120 | } 121 | } 122 | 123 | boolean Pkt::send(const uint8_t progID, const uint8_t *rfaddr, const int len, const void *buffer) 124 | { 125 | unsigned int i; 126 | 127 | if (progID == 0xFF || rfaddr == NULL || (len && (buffer == NULL))) 128 | return false; 129 | 130 | for (i=0; i < txQueueDepth; i++) { 131 | if (txQueue[i]->progID == 0xFF) { 132 | break; 133 | } 134 | } 135 | if (i == txQueueDepth) 136 | return false; // No available queue slots; must run .flush() to send what we have so far. 137 | 138 | txQueue[i]->progID = progID; 139 | memcpy(txQueue[i]->rfaddr, rfaddr, 5); 140 | txQueue[i]->pktlen = len; 141 | if (len) 142 | memcpy(txQueue[i]->buffer, (const uint8_t *)buffer, len); 143 | 144 | return true; 145 | } 146 | 147 | void Pkt::flush(void) 148 | { 149 | unsigned int i, fidx; 150 | uint8_t *cur_rfaddr, rfbuf[32], sz8, reg; 151 | int plen=0; 152 | 153 | // Find out if TX queue has any entries; if not, clear, if so, preload cur_rfaddr pointer position 154 | for (i=0; i < txQueueDepth; i++) { 155 | if (txQueue[i]->progID != 0xFF) { 156 | cur_rfaddr = txQueue[i]->rfaddr; 157 | break; 158 | } 159 | } 160 | if (i == txQueueDepth) 161 | return; // Nothing to transmit! 162 | fidx = i; 163 | reg = r_reg(RF24_EN_AA); 164 | 165 | do { 166 | if (!memcmp(txQueue[fidx]->rfaddr, cur_rfaddr, 5)) { 167 | if (txQueue[i]->pktlen > PKT_MAX_PKTLEN) { // Cull invalid-sized packets from queue 168 | txQueue[fidx]->progID = 0xFF; 169 | } else { 170 | if ( (txQueue[fidx]->pktlen + 2 + plen) <= 32 ) { 171 | rfbuf[plen++] = txQueue[fidx]->progID; 172 | sz8 = txQueue[fidx]->pktlen & 0xFF; 173 | rfbuf[plen++] = sz8; 174 | if (sz8) { 175 | memcpy(&rfbuf[plen], txQueue[fidx]->buffer, sz8); 176 | plen += sz8; 177 | } 178 | // Delete queue entry; it's been handled 179 | txQueue[fidx]->progID = 0xFF; 180 | } else { 181 | // Buffer full; transmit at once! 182 | w_tx_addr(cur_rfaddr); 183 | if (reg & BIT0) 184 | w_rx_addr(0, cur_rfaddr); 185 | w_tx_payload(plen, (uint8_t *)rfbuf); 186 | pkt_do_send_loop(); 187 | plen = 0; 188 | } /* if (length of current packet plus previous will fit in one 32-byte nRF24 frame) */ 189 | } /* if (current packet is a valid length) */ 190 | fidx++; 191 | } else { 192 | // New address; flush packet if necessary 193 | if (plen) { 194 | w_tx_addr(cur_rfaddr); 195 | if (reg & BIT0) 196 | w_rx_addr(0, cur_rfaddr); 197 | w_tx_payload(plen, (uint8_t *)rfbuf); 198 | pkt_do_send_loop(); 199 | plen = 0; 200 | } 201 | // Set cur_rfaddr to this new address, and do not increment fidx so we loop back to process it 202 | // using the existing code above. 203 | cur_rfaddr = txQueue[i]->rfaddr; 204 | } /* if (current packet matches our current RF address) */ 205 | } while (fidx < txQueueDepth && txQueue[fidx]->progID != 0xFF); 206 | 207 | if (plen) { // Unfinished buffer waiting to be sent 208 | w_tx_addr((uint8_t *)cur_rfaddr); 209 | if (reg & BIT0) 210 | w_rx_addr(0, (uint8_t *)cur_rfaddr); 211 | w_tx_payload(plen, (uint8_t *)rfbuf); 212 | pkt_do_send_loop(); 213 | } 214 | if (do_deepsleep_after_tx) 215 | msprf24_powerdown(); 216 | } 217 | 218 | /* Packet RX logic */ 219 | boolean Pkt::available(void) 220 | { 221 | return (rf_irq & (RF24_IRQ_FLAGGED | RF24_IRQ_RX)) == (RF24_IRQ_FLAGGED | RF24_IRQ_RX); 222 | } 223 | 224 | void Pkt::loop(void) 225 | { 226 | unsigned int i, j, plen; 227 | boolean found_reg; 228 | uint8_t rfbuf[32]; 229 | 230 | while ((rf_irq & (RF24_IRQ_FLAGGED | RF24_IRQ_RX)) == (RF24_IRQ_FLAGGED | RF24_IRQ_RX)) { 231 | plen = r_rx_peek_payload_size(); 232 | if (plen == 0 || plen > 32) { 233 | flush_rx(); // Bad packet information; flush RX buffers and exit, nothing to see here. 234 | return; 235 | } 236 | r_rx_payload(plen, rfbuf); 237 | msprf24_irq_clear(RF24_IRQ_RX); 238 | 239 | for (i=0; i < plen; i++) { 240 | if (rfbuf[i] && rfbuf[i] != 0xFF) { 241 | // Only process this packet if the length does not send us past the buffer! 242 | // (otherwise this would make an easy buffer overflow attack vector) 243 | if ((i + rfbuf[i+1] + 1) < plen) { 244 | // Search program registrations for this program ID, or run the UnknownProgram callback if present. 245 | found_reg = false; 246 | for (j=0; j < progRegMaxCount; j++) { 247 | if (progRegs[j]->progID == rfbuf[i]) { 248 | found_reg = true; 249 | progRegs[j]->callback((const uint8_t)rfbuf[i], (const int)rfbuf[i+1], (const void *)&rfbuf[i+2]); 250 | } 251 | } 252 | if (!found_reg) { 253 | } 254 | if (!found_reg && unknownProgramCallback != NULL) { 255 | unknownProgramCallback((const uint8_t)rfbuf[i], (const int)rfbuf[i+1], (const void *)&rfbuf[i+2]); 256 | } 257 | // Run the defaultCallback (attachAllPrograms()) if defined 258 | if (defaultCallback != NULL) { 259 | defaultCallback((const uint8_t)rfbuf[i], (const int)rfbuf[i+1], (const void *)&rfbuf[i+2]); 260 | } 261 | // Flush this packet and advance 262 | i += rfbuf[i+1] + 1; // note; for() logic will advance 'i' one more 263 | } else { 264 | // Invalid packet length field; we're done. 265 | i = plen; 266 | } /* current packet's length is valid */ 267 | } /* rfbuf[i] is a valid progID field */ 268 | } /* for(i = 0 to plen) */ 269 | } /* while (nRF24 RX FIFO isn't empty) */ 270 | } 271 | 272 | /* Program ID callback registration */ 273 | boolean Pkt::attachProgram(const uint8_t progID, PKT_CALLBACK callback) 274 | { 275 | unsigned int i; 276 | 277 | if (!progID || progID == 0xFF) 278 | return false; // Invalid progID 279 | 280 | for (i=0; i < progRegMaxCount; i++) { 281 | if (progRegs[i]->progID == progID) 282 | return false; // Callback already registered for this program ID. 283 | } 284 | 285 | for (i=0; i < progRegMaxCount; i++) { 286 | if (progRegs[i]->progID == 0xFF) { 287 | progRegs[i]->progID = progID; 288 | progRegs[i]->callback = callback; 289 | return true; 290 | } 291 | } 292 | return false; // Ran out of slots for program ID registrations 293 | } 294 | 295 | boolean Pkt::detachProgram(const uint8_t progID) 296 | { 297 | unsigned int i; 298 | 299 | if (!progID || progID == 0xFF) 300 | return false; // Invalid progID 301 | 302 | for (i=0; i < progRegMaxCount; i++) { 303 | if (progRegs[i]->progID == progID) { 304 | progRegs[i]->progID = 0xFF; 305 | progRegs[i]->callback = NULL; 306 | return true; 307 | } 308 | } 309 | return false; // progID not found in registration array 310 | } 311 | 312 | boolean Pkt::attachUnknownProgram(PKT_CALLBACK callback) 313 | { 314 | if (callback == NULL || unknownProgramCallback != NULL) 315 | return false; 316 | 317 | unknownProgramCallback = callback; 318 | return true; 319 | } 320 | 321 | boolean Pkt::detachUnknownProgram(void) 322 | { 323 | if (unknownProgramCallback == NULL) 324 | return false; 325 | 326 | unknownProgramCallback = NULL; 327 | return true; 328 | } 329 | 330 | boolean Pkt::attachAllPrograms(PKT_CALLBACK callback) 331 | { 332 | if (callback == NULL || defaultCallback != NULL) 333 | return false; 334 | 335 | defaultCallback = callback; 336 | return true; 337 | } 338 | 339 | boolean Pkt::detachAllPrograms(void) 340 | { 341 | if (defaultCallback == NULL) 342 | return false; 343 | 344 | defaultCallback = NULL; 345 | return true; 346 | } 347 | 348 | const uint8_t transceiver_default_addr_rx0[] = { 0xE7, 0xE7, 0xE7, 0xE7, 0xE7 }; 349 | 350 | void pkt_do_send_loop() 351 | { 352 | uint8_t reg; 353 | 354 | rf_status = r_reg(RF24_STATUS); 355 | if (rf_status & BIT5) { // TXFULL? 356 | flush_tx(); 357 | return; 358 | } 359 | msprf24_get_irq_reason(); 360 | if (rf_irq & RF24_IRQ_TXFAILED) { 361 | msprf24_irq_clear(RF24_IRQ_TXFAILED); 362 | flush_tx(); 363 | } 364 | if (rf_irq & RF24_IRQ_TX) { 365 | msprf24_irq_clear(RF24_IRQ_TX); 366 | } 367 | reg = msprf24_current_state(); 368 | if (reg == RF24_STATE_POWERDOWN) { 369 | msprf24_standby(); 370 | } 371 | msprf24_activate_tx(); // BANG! 372 | while (1) { 373 | if (rf_irq & RF24_IRQ_FLAGGED) { 374 | if (rf_irq & (RF24_IRQ_TX | RF24_IRQ_TXFAILED)) { 375 | msprf24_irq_clear(RF24_IRQ_TX | RF24_IRQ_TXFAILED); 376 | if (r_reg(RF24_EN_AA) & BIT0) // Restore RX0 pipe to the default as a precaution 377 | w_rx_addr(0, (uint8_t *)transceiver_default_addr_rx0); 378 | if (reg == RF24_STATE_PRX) // previous state before we entered TX mode 379 | msprf24_activate_rx(); 380 | return; 381 | } 382 | } 383 | // don't care about RX events here 384 | if (!(rf_irq & RF24_IRQ_FLAGGED) || (rf_irq == (RF24_IRQ_FLAGGED | RF24_IRQ_RX))) { 385 | LPM0; 386 | } 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /msprf24.c: -------------------------------------------------------------------------------- 1 | /* msprf24.c 2 | * MSP430 library for interfacing with the nRF24L01+ RF transceiver by 3 | * Nordic Semiconductor. 4 | * 5 | * Serial interfaces supported: 6 | * 1. USI - developed on MSP430G2231 7 | * 2. USCI_A - developed on MSP430G2553 8 | * 3. USCI_B - developed on MSP430G2553 9 | * 4. USCI_A F5xxx - developed on MSP430F5172 10 | * 5. USCI_B F5xxx - developed on MSP430F5172 11 | * 12 | * MSP430-specific code inspired/derived from dkedr's nrf24 library posted on the 43oh forums: 13 | * http://www.43oh.com/forum/viewtopic.php?f=10&t=2572 14 | * 15 | * 16 | * Copyright (c) 2012, Eric Brundick 17 | * 18 | * Permission to use, copy, modify, and/or distribute this software for any purpose 19 | * with or without fee is hereby granted, provided that the above copyright notice 20 | * and this permission notice appear in all copies. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 23 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 24 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 25 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 26 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 27 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 28 | */ 29 | 30 | #include 31 | #include "msprf24.h" 32 | #include "msp430_spi.h" 33 | #include "nRF24L01.h" 34 | #include "nrf_userconfig.h" 35 | /* ^ Provides nrfCSNport, nrfCSNportout, nrfCSNpin, 36 | nrfCEport, nrfCEportout, nrfCEpin, 37 | nrfIRQport, nrfIRQpin 38 | Also specify # clock cycles for 5ms, 10us and 130us sleeps. 39 | */ 40 | /* Private library variables */ 41 | uint8_t rf_feature; // Used to track which features have been enabled 42 | 43 | /* CE (Chip Enable/RF transceiver activate signal) and CSN (SPI chip-select) operations. */ 44 | #define CSN_EN nrfCSNportout &= ~nrfCSNpin 45 | #define CSN_DIS nrfCSNportout |= nrfCSNpin 46 | #define CE_EN nrfCEportout |= nrfCEpin 47 | #define CE_DIS nrfCEportout &= ~nrfCEpin 48 | 49 | 50 | 51 | /* SPI drivers now supplied by msp430_spi.c */ 52 | 53 | 54 | 55 | 56 | /* Basic I/O to the device. */ 57 | uint8_t r_reg(uint8_t addr) 58 | { 59 | uint16_t i; 60 | 61 | CSN_EN; 62 | i = spi_transfer16(RF24_NOP | ((addr & RF24_REGISTER_MASK) << 8)); 63 | rf_status = (uint8_t) ((i & 0xFF00) >> 8); 64 | CSN_DIS; 65 | return (uint8_t) (i & 0x00FF); 66 | } 67 | 68 | void w_reg(uint8_t addr, uint8_t data) 69 | { 70 | uint16_t i; 71 | CSN_EN; 72 | i = spi_transfer16( (data & 0x00FF) | (((addr & RF24_REGISTER_MASK) | RF24_W_REGISTER) << 8) ); 73 | rf_status = (uint8_t) ((i & 0xFF00) >> 8); 74 | CSN_DIS; 75 | } 76 | 77 | void w_tx_addr(uint8_t *addr) 78 | { 79 | int i; 80 | 81 | CSN_EN; 82 | rf_status = spi_transfer(RF24_TX_ADDR | RF24_W_REGISTER); 83 | for (i=rf_addr_width-1; i>=0; i--) { 84 | spi_transfer(addr[i]); 85 | } 86 | CSN_DIS; 87 | } 88 | 89 | void w_rx_addr(uint8_t pipe, uint8_t *addr) 90 | { 91 | int i; 92 | 93 | if (pipe > 5) 94 | return; // Only 6 pipes available 95 | CSN_EN; 96 | rf_status = spi_transfer((RF24_RX_ADDR_P0 + pipe) | RF24_W_REGISTER); 97 | if (pipe > 1) { // Pipes 2-5 differ from pipe1's addr only in the LSB. 98 | spi_transfer(addr[rf_addr_width-1]); 99 | } else { 100 | for (i=rf_addr_width-1; i>=0; i--) { 101 | spi_transfer(addr[i]); 102 | } 103 | } 104 | CSN_DIS; 105 | } 106 | 107 | void w_tx_payload(uint8_t len, uint8_t *data) 108 | { 109 | uint16_t i=0; 110 | CSN_EN; 111 | if (len % 2) { // Odd payload size? Make it even by stuffing the command in a 16-bit xfer 112 | // Borrowing 'i' to extract STATUS... 113 | i = spi_transfer16( (RF24_W_TX_PAYLOAD << 8) | (0x00FF & data[0]) ); 114 | rf_status = (i & 0xFF00) >> 8; 115 | i = 1; 116 | } else { 117 | rf_status = spi_transfer(RF24_W_TX_PAYLOAD); 118 | } 119 | for (; i < len; i+=2) { 120 | // SPI transfers MSB first 121 | spi_transfer16( (data[i] << 8) | (0x00FF & data[i+1]) ); 122 | } 123 | CSN_DIS; 124 | } 125 | 126 | void w_tx_payload_noack(uint8_t len, uint8_t *data) 127 | { 128 | uint16_t i=0; 129 | 130 | if ( !(rf_feature & RF24_EN_DYN_ACK) ) // DYN ACK must be enabled to allow NOACK packets 131 | return; 132 | CSN_EN; 133 | if (len % 2) { 134 | // Borrowing 'i' to extract STATUS... 135 | i = spi_transfer16( (RF24_W_TX_PAYLOAD_NOACK << 8) | (0x00FF & data[0]) ); 136 | rf_status = (i & 0xFF00) >> 8; 137 | i = 1; 138 | } else { 139 | rf_status = spi_transfer(RF24_W_TX_PAYLOAD_NOACK); 140 | } 141 | for (; i < len; i+=2) { 142 | // SPI transfers MSB first 143 | spi_transfer16( (data[i] << 8) | (0x00FF & data[i+1]) ); 144 | } 145 | CSN_DIS; 146 | } 147 | 148 | uint8_t r_rx_peek_payload_size() 149 | { 150 | uint16_t i; 151 | 152 | CSN_EN; 153 | i = spi_transfer16(RF24_NOP | (RF24_R_RX_PL_WID << 8)); 154 | rf_status = (uint8_t) ((i & 0xFF00) >> 8); 155 | CSN_DIS; 156 | return (uint8_t) (i & 0x00FF); 157 | } 158 | 159 | uint8_t r_rx_payload(uint8_t len, uint8_t *data) 160 | { 161 | uint16_t i=0,j; 162 | CSN_EN; 163 | if (len % 2) { 164 | // Borrowing 'i' to extract STATUS... 165 | i = spi_transfer16((RF24_R_RX_PAYLOAD << 8) | RF24_NOP); 166 | rf_status = (i & 0xFF00) >> 8; 167 | data[0] = i & 0x00FF; 168 | i = 1; 169 | } else { 170 | rf_status = spi_transfer(RF24_R_RX_PAYLOAD); 171 | } 172 | for (; i < len; i+=2) { 173 | j = spi_transfer16(0xFFFF); 174 | // SPI transfers MSB first 175 | data[i] = (j & 0xFF00) >> 8; 176 | data[i+1] = (j & 0x00FF); 177 | } 178 | CSN_DIS; 179 | // The RX pipe this data belongs to is stored in STATUS 180 | return ((rf_status & 0x0E) >> 1); 181 | } 182 | 183 | void flush_tx() 184 | { 185 | CSN_EN; 186 | rf_status = spi_transfer(RF24_FLUSH_TX); 187 | CSN_DIS; 188 | } 189 | 190 | void flush_rx() 191 | { 192 | CSN_EN; 193 | rf_status = spi_transfer(RF24_FLUSH_RX); 194 | CSN_DIS; 195 | } 196 | 197 | void tx_reuse_lastpayload() 198 | { 199 | CSN_EN; 200 | rf_status = spi_transfer(RF24_REUSE_TX_PL); 201 | CSN_DIS; 202 | } 203 | 204 | void pulse_ce() 205 | { 206 | CE_EN; 207 | __delay_cycles(DELAY_CYCLES_15US); 208 | CE_DIS; 209 | } 210 | 211 | /* Used to manually ACK with a payload. Must have RF24_EN_ACK_PAY enabled; this is not enabled by default 212 | * with msprf24_init() FYI. 213 | * When RF24_EN_ACK_PAY is enabled on the PTX side, ALL transmissions must be manually ACK'd by the receiver this way. 214 | * The receiver (PRX) side needs to have RF24_EN_ACK_PAY enabled too, or else it will automatically ACK with a zero-byte packet. 215 | * 216 | * If you have this enabled on the PTX but not the PRX, the transmission will go through and the PRX will receive/notify about 217 | * the RX payload, but the PTX will ignore the zero-byte autoack from the PRX and perform its retransmit sequence, erroring 218 | * out with MAX_RT (RF24_IRQ_TXFAILED) after (RF24_SETUP_RETR >> RF24_ARC) retransmissions. 219 | * When this occurs, the PRX will still only notify its microcontroller of the payload once (the PID field in the packet uniquely 220 | * identifies it so the PRX knows it's the same packet being retransmitted) but it's obviously wasting on-air time (and power). 221 | */ 222 | void w_ack_payload(uint8_t pipe, uint8_t len, uint8_t *data) 223 | { 224 | uint16_t i=0; 225 | CSN_EN; 226 | 227 | if (pipe > 5) 228 | return; 229 | if ( !(rf_feature & RF24_EN_ACK_PAY) ) // ACK payloads must be enabled... 230 | return; 231 | 232 | if (len % 2) { 233 | // Borrowing 'i' to extract STATUS... 234 | i = spi_transfer16( ((RF24_W_ACK_PAYLOAD | pipe) << 8) | (0x00FF & data[0]) ); 235 | rf_status = (i & 0xFF00) >> 8; 236 | i = 1; 237 | } else { 238 | rf_status = spi_transfer(RF24_W_ACK_PAYLOAD | pipe); 239 | } 240 | for (; i < len; i+=2) { 241 | // SPI transfers MSB first 242 | spi_transfer16( (data[i] << 8) | (0x00FF & data[i+1]) ); 243 | } 244 | CSN_DIS; 245 | } 246 | 247 | 248 | 249 | 250 | 251 | 252 | /* Configuration parameters used to set-up the RF configuration */ 253 | uint8_t rf_crc; 254 | uint8_t rf_addr_width; 255 | uint8_t rf_speed_power; 256 | uint8_t rf_channel; 257 | /* Status variable updated every time SPI I/O is performed */ 258 | uint8_t rf_status; 259 | /* IRQ state is stored in here after msprf24_get_irq_reason(), RF24_IRQ_FLAGGED raised during 260 | * the IRQ port ISR--user application issuing LPMx sleep or polling should watch for this to 261 | * determine if the wakeup reason was due to nRF24 IRQ. 262 | */ 263 | volatile uint8_t rf_irq; 264 | 265 | 266 | 267 | 268 | 269 | /* Library functions */ 270 | void msprf24_init() 271 | { 272 | // Setup SPI 273 | spi_init(); 274 | _EINT(); // Enable interrupts (set GIE in SR) 275 | 276 | // Setup IRQ 277 | #if nrfIRQport == 1 278 | P1DIR &= ~nrfIRQpin; // IRQ line is input 279 | P1OUT |= nrfIRQpin; // Pull-up resistor enabled 280 | P1REN |= nrfIRQpin; 281 | P1IES |= nrfIRQpin; // Trigger on falling-edge 282 | P1IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 283 | P1IE |= nrfIRQpin; // Enable IRQ interrupt 284 | #elif nrfIRQport == 2 285 | P2DIR &= ~nrfIRQpin; // IRQ line is input 286 | P2OUT |= nrfIRQpin; // Pull-up resistor enabled 287 | P2REN |= nrfIRQpin; 288 | P2IES |= nrfIRQpin; // Trigger on falling-edge 289 | P2IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 290 | P2IE |= nrfIRQpin; // Enable IRQ interrupt 291 | #elif nrfIRQport == 3 292 | P3DIR &= ~nrfIRQpin; // IRQ line is input 293 | P3OUT |= nrfIRQpin; // Pull-up resistor enabled 294 | P3REN |= nrfIRQpin; 295 | P3IES |= nrfIRQpin; // Trigger on falling-edge 296 | P3IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 297 | P3IE |= nrfIRQpin; // Enable IRQ interrupt 298 | #elif nrfIRQport == 4 299 | P4DIR &= ~nrfIRQpin; // IRQ line is input 300 | P4OUT |= nrfIRQpin; // Pull-up resistor enabled 301 | P4REN |= nrfIRQpin; 302 | P4IES |= nrfIRQpin; // Trigger on falling-edge 303 | P4IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 304 | P4IE |= nrfIRQpin; // Enable IRQ interrupt 305 | #elif nrfIRQport == 5 306 | P5DIR &= ~nrfIRQpin; // IRQ line is input 307 | P5OUT |= nrfIRQpin; // Pull-up resistor enabled 308 | P5REN |= nrfIRQpin; 309 | P5IES |= nrfIRQpin; // Trigger on falling-edge 310 | P5IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 311 | P5IE |= nrfIRQpin; // Enable IRQ interrupt 312 | #elif nrfIRQport == 6 313 | P6DIR &= ~nrfIRQpin; // IRQ line is input 314 | P6OUT |= nrfIRQpin; // Pull-up resistor enabled 315 | P6REN |= nrfIRQpin; 316 | P6IES |= nrfIRQpin; // Trigger on falling-edge 317 | P6IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 318 | P6IE |= nrfIRQpin; // Enable IRQ interrupt 319 | #elif nrfIRQport == 7 320 | P7DIR &= ~nrfIRQpin; // IRQ line is input 321 | P7OUT |= nrfIRQpin; // Pull-up resistor enabled 322 | P7REN |= nrfIRQpin; 323 | P7IES |= nrfIRQpin; // Trigger on falling-edge 324 | P7IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 325 | P7IE |= nrfIRQpin; // Enable IRQ interrupt 326 | #elif nrfIRQport == 8 327 | P8DIR &= ~nrfIRQpin; // IRQ line is input 328 | P8OUT |= nrfIRQpin; // Pull-up resistor enabled 329 | P8REN |= nrfIRQpin; 330 | P8IES |= nrfIRQpin; // Trigger on falling-edge 331 | P8IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 332 | P8IE |= nrfIRQpin; // Enable IRQ interrupt 333 | #elif nrfIRQport == 9 334 | P9DIR &= ~nrfIRQpin; // IRQ line is input 335 | P9OUT |= nrfIRQpin; // Pull-up resistor enabled 336 | P9REN |= nrfIRQpin; 337 | P9IES |= nrfIRQpin; // Trigger on falling-edge 338 | P9IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 339 | P9IE |= nrfIRQpin; // Enable IRQ interrupt 340 | #elif nrfIRQport == 10 341 | P10DIR &= ~nrfIRQpin; // IRQ line is input 342 | P10OUT |= nrfIRQpin; // Pull-up resistor enabled 343 | P10REN |= nrfIRQpin; 344 | P10IES |= nrfIRQpin; // Trigger on falling-edge 345 | P10IFG &= ~nrfIRQpin; // Clear any outstanding IRQ 346 | P10IE |= nrfIRQpin; // Enable IRQ interrupt 347 | #endif 348 | 349 | // Setup CSN/CE ports 350 | #if nrfCSNport == 1 351 | P1DIR |= nrfCSNpin; 352 | #elif nrfCSNport == 2 353 | P2DIR |= nrfCSNpin; 354 | #elif nrfCSNport == 3 355 | P3DIR |= nrfCSNpin; 356 | #elif nrfCSNport == 4 357 | P4DIR |= nrfCSNpin; 358 | #elif nrfCSNport == 5 359 | P5DIR |= nrfCSNpin; 360 | #elif nrfCSNport == 6 361 | P6DIR |= nrfCSNpin; 362 | #elif nrfCSNport == 7 363 | P7DIR |= nrfCSNpin; 364 | #elif nrfCSNport == 8 365 | P8DIR |= nrfCSNpin; 366 | #elif nrfCSNport == 9 367 | P9DIR |= nrfCSNpin; 368 | #elif nrfCSNport == 10 369 | P10DIR |= nrfCSNpin; 370 | #elif nrfCSNport == J 371 | PJDIR |= nrfCSNpin; 372 | #endif 373 | CSN_DIS; 374 | 375 | #if nrfCEport == 1 376 | P1DIR |= nrfCEpin; 377 | #elif nrfCEport == 2 378 | P2DIR |= nrfCEpin; 379 | #elif nrfCEport == 3 380 | P3DIR |= nrfCEpin; 381 | #elif nrfCEport == 4 382 | P4DIR |= nrfCEpin; 383 | #elif nrfCEport == 5 384 | P5DIR |= nrfCEpin; 385 | #elif nrfCEport == 6 386 | P6DIR |= nrfCEpin; 387 | #elif nrfCEport == 7 388 | P7DIR |= nrfCEpin; 389 | #elif nrfCEport == 8 390 | P8DIR |= nrfCEpin; 391 | #elif nrfCEport == 9 392 | P9DIR |= nrfCEpin; 393 | #elif nrfCEport == 10 394 | P10DIR |= nrfCEpin; 395 | #elif nrfCEport == J 396 | PJDIR |= nrfCEpin; 397 | #endif 398 | CE_DIS; 399 | 400 | /* Straw-man spi_transfer with no Chip Select lines enabled; this is to workaround errata bug USI5 401 | * on the MSP430G2452 and related (see http://www.ti.com/lit/er/slaz072/slaz072.pdf) 402 | * Shouldn't hurt anything since we expect no CS lines enabled by the user during this function's execution. 403 | */ 404 | spi_transfer(RF24_NOP); 405 | 406 | // Wait 100ms for RF transceiver to initialize. 407 | uint8_t c = 20; 408 | for (; c; c--) { 409 | __delay_cycles(DELAY_CYCLES_5MS); 410 | } 411 | 412 | // Configure RF transceiver with current value of rf_* configuration variables 413 | msprf24_irq_clear(RF24_IRQ_MASK); // Forget any outstanding IRQs 414 | msprf24_close_pipe_all(); /* Start off with no pipes enabled, let the user open as needed. This also 415 | * clears the DYNPD register. 416 | */ 417 | msprf24_set_retransmit_delay(2000); // A default I chose 418 | msprf24_set_retransmit_count(15); // A default I chose 419 | msprf24_set_speed_power(); 420 | msprf24_set_channel(); 421 | msprf24_set_address_width(); 422 | rf_feature = 0x00; // Initialize this so we're starting from a clean slate 423 | msprf24_enable_feature(RF24_EN_DPL); // Dynamic payload size capability (set with msprf24_set_pipe_packetsize(x, 0)) 424 | msprf24_enable_feature(RF24_EN_DYN_ACK); // Ability to use w_tx_payload_noack() 425 | 426 | msprf24_powerdown(); 427 | flush_tx(); 428 | flush_rx(); 429 | } 430 | 431 | void msprf24_enable_feature(uint8_t feature) 432 | { 433 | if ( (rf_feature & feature) != feature ) { 434 | rf_feature |= feature; 435 | rf_feature &= 0x07; // Only bits 0, 1, 2 allowed to be set 436 | w_reg(RF24_FEATURE, rf_feature); 437 | } 438 | } 439 | 440 | void msprf24_disable_feature(uint8_t feature) 441 | { 442 | if ( (rf_feature & feature) == feature ) { 443 | rf_feature &= ~feature; 444 | w_reg(RF24_FEATURE, rf_feature); 445 | } 446 | } 447 | 448 | void msprf24_close_pipe(uint8_t pipeid) 449 | { 450 | uint8_t rxen, enaa; 451 | 452 | if (pipeid > 5) 453 | return; 454 | 455 | rxen = r_reg(RF24_EN_RXADDR); 456 | enaa = r_reg(RF24_EN_AA); 457 | 458 | rxen &= ~(1 << pipeid); 459 | enaa &= ~(1 << pipeid); 460 | 461 | w_reg(RF24_EN_RXADDR, rxen); 462 | w_reg(RF24_EN_AA, enaa); 463 | } 464 | 465 | void msprf24_close_pipe_all() 466 | { 467 | w_reg(RF24_EN_RXADDR, 0x00); 468 | w_reg(RF24_EN_AA, 0x00); 469 | w_reg(RF24_DYNPD, 0x00); 470 | } 471 | 472 | void msprf24_open_pipe(uint8_t pipeid, uint8_t autoack) 473 | { 474 | uint8_t rxen, enaa; 475 | 476 | if (pipeid > 5) 477 | return; 478 | 479 | rxen = r_reg(RF24_EN_RXADDR); 480 | enaa = r_reg(RF24_EN_AA); 481 | 482 | if (autoack) 483 | enaa |= (1 << pipeid); 484 | else 485 | enaa &= ~(1 << pipeid); 486 | rxen |= (1 << pipeid); 487 | w_reg(RF24_EN_RXADDR, rxen); 488 | w_reg(RF24_EN_AA, enaa); 489 | } 490 | 491 | uint8_t msprf24_pipe_isopen(uint8_t pipeid) 492 | { 493 | uint8_t rxen; 494 | 495 | if (pipeid > 5) 496 | return 0; 497 | 498 | rxen = r_reg(RF24_EN_RXADDR); 499 | 500 | return ( (1< 5) 508 | return; 509 | 510 | dynpdcfg = r_reg(RF24_DYNPD); 511 | if (size < 1) { 512 | if ( !(rf_feature & RF24_EN_DPL) ) // Cannot set dynamic payload if EN_DPL is disabled. 513 | return; 514 | if (!( (1< 32) 521 | size = 32; 522 | w_reg(RF24_RX_PW_P0 + pipe, size); 523 | } 524 | w_reg(RF24_DYNPD, dynpdcfg); 525 | } 526 | 527 | void msprf24_set_retransmit_delay(uint16_t us) 528 | { 529 | uint8_t c; 530 | 531 | // using 'c' to evaluate current RF speed 532 | c = rf_speed_power & RF24_SPEED_MASK; 533 | if (us > 4000) 534 | us = 4000; 535 | if (us < 1500 && c == RF24_SPEED_250KBPS) 536 | us = 1500; 537 | if (us < 500) 538 | us = 500; 539 | 540 | // using 'c' to save current value of ARC (auto-retrans-count) since we're not changing that here 541 | c = r_reg(RF24_SETUP_RETR) & 0x0F; 542 | us = (us-250) / 250; 543 | us <<= 4; 544 | w_reg(RF24_SETUP_RETR, c | (us & 0xF0)); 545 | } 546 | 547 | void msprf24_set_retransmit_count(uint8_t count) 548 | { 549 | uint8_t c; 550 | 551 | c = r_reg(RF24_SETUP_RETR) & 0xF0; 552 | w_reg(RF24_SETUP_RETR, c | (count & 0x0F)); 553 | } 554 | 555 | uint8_t msprf24_get_last_retransmits() 556 | { 557 | return r_reg(RF24_OBSERVE_TX) & 0x0F; 558 | } 559 | 560 | uint8_t msprf24_get_lostpackets() 561 | { 562 | return (r_reg(RF24_OBSERVE_TX) >> 4) & 0x0F; 563 | } 564 | 565 | inline uint8_t _msprf24_crc_mask() 566 | { 567 | return (rf_crc & 0x0C); 568 | } 569 | 570 | inline uint8_t _msprf24_irq_mask() 571 | { 572 | return ~(RF24_MASK_RX_DR | RF24_MASK_TX_DS | RF24_MASK_MAX_RT); 573 | } 574 | 575 | uint8_t msprf24_is_alive() 576 | { 577 | uint8_t aw; 578 | 579 | aw = r_reg(RF24_SETUP_AW); 580 | return((aw & 0xFC) == 0x00 && (aw & 0x03) != 0x00); 581 | } 582 | 583 | uint8_t msprf24_set_config(uint8_t cfgval) 584 | { 585 | uint8_t previous_config; 586 | 587 | previous_config = r_reg(RF24_CONFIG); 588 | w_reg(RF24_CONFIG, (_msprf24_crc_mask() | cfgval) & _msprf24_irq_mask()); 589 | return previous_config; 590 | } 591 | 592 | void msprf24_set_speed_power() 593 | { 594 | if ( (rf_speed_power & RF24_SPEED_MASK) == RF24_SPEED_MASK ) // Speed setting RF_DR_LOW=1, RF_DR_HIGH=1 is reserved, clamp it to minimum 595 | rf_speed_power = (rf_speed_power & ~RF24_SPEED_MASK) | RF24_SPEED_MIN; 596 | w_reg(RF24_RF_SETUP, (rf_speed_power & 0x2F)); 597 | } 598 | 599 | void msprf24_set_channel() 600 | { 601 | if (rf_channel > 125) 602 | rf_channel = 0; 603 | w_reg(RF24_RF_CH, (rf_channel & 0x7F)); 604 | } 605 | 606 | void msprf24_set_address_width() 607 | { 608 | if (rf_addr_width < 3 || rf_addr_width > 5) 609 | return; 610 | w_reg(RF24_SETUP_AW, ((rf_addr_width-2) & 0x03)); 611 | } 612 | 613 | uint8_t msprf24_current_state() 614 | { 615 | uint8_t config; 616 | 617 | if (!msprf24_is_alive()) // Can't read/detect a valid value from SETUP_AW? (typically SPI or device fault) 618 | return RF24_STATE_NOTPRESENT; 619 | config = r_reg(RF24_CONFIG); 620 | if ( (config & RF24_PWR_UP) == 0x00 ) // PWR_UP=0? 621 | return RF24_STATE_POWERDOWN; 622 | if ( !(nrfCEportout & nrfCEpin) ) // PWR_UP=1 && CE=0? 623 | return RF24_STATE_STANDBY_I; 624 | if ( !(config & RF24_PRIM_RX) ) { // PWR_UP=1 && CE=1 && PRIM_RX=0? 625 | if ( (r_reg(RF24_FIFO_STATUS) & RF24_TX_EMPTY) ) // TX FIFO empty? 626 | return RF24_STATE_STANDBY_II; 627 | return RF24_STATE_PTX; // If TX FIFO is not empty, we are in PTX (active transmit) mode. 628 | } 629 | if ( r_reg(RF24_RF_SETUP) & 0x90 ) // Testing CONT_WAVE or PLL_LOCK? 630 | return RF24_STATE_TEST; 631 | return RF24_STATE_PRX; // PWR_UP=1, PRIM_RX=1, CE=1 -- Must be PRX 632 | } 633 | 634 | // Power down device, 0.9uA power draw 635 | void msprf24_powerdown() 636 | { 637 | CE_DIS; 638 | w_reg(RF24_STATUS, RF24_IRQ_MASK); // Clear all IRQs so the IRQ line isn't draining power during powerdown mode 639 | msprf24_set_config(0); // PWR_UP=0 640 | } 641 | 642 | // Enable Standby-I, 26uA power draw 643 | void msprf24_standby() 644 | { 645 | uint8_t state = msprf24_current_state(); 646 | if (state == RF24_STATE_NOTPRESENT || state == RF24_STATE_STANDBY_I) 647 | return; 648 | CE_DIS; 649 | msprf24_set_config(RF24_PWR_UP); // PWR_UP=1, PRIM_RX=0 650 | if (state == RF24_STATE_POWERDOWN) { // If we're powering up from deep powerdown... 651 | //CE_EN; // This is a workaround for SI24R1 chips, though it seems to screw things up so disabled for now til I can obtain an SI24R1 for testing. 652 | __delay_cycles(DELAY_CYCLES_5MS); // Then wait 5ms for the crystal oscillator to spin up. 653 | //CE_DIS; 654 | } 655 | } 656 | 657 | // Enable PRX mode 658 | void msprf24_activate_rx() 659 | { 660 | msprf24_standby(); 661 | // Purge any existing RX FIFO or RX interrupts 662 | flush_rx(); 663 | w_reg(RF24_STATUS, RF24_RX_DR); 664 | 665 | // Enable PRIM_RX 666 | msprf24_set_config(RF24_PWR_UP | RF24_PRIM_RX); 667 | CE_EN; 668 | // 130uS required for PLL lock to stabilize, app can go do other things and wait 669 | // for incoming I/O. 670 | } 671 | 672 | // Enable Standby-II / PTX mode 673 | /* Standby-II is enabled if the TX FIFO is empty, otherwise the chip enters PTX 674 | * mode to send the TX FIFO buffer contents until it's all done, at which point 675 | * the chip falls back to Standby-II again. 676 | */ 677 | void msprf24_activate_tx() 678 | { 679 | msprf24_standby(); 680 | // Cancel any outstanding TX interrupt 681 | w_reg(RF24_STATUS, RF24_TX_DS|RF24_MAX_RT); 682 | 683 | // Pulse CE for 10us to activate PTX 684 | pulse_ce(); 685 | } 686 | 687 | /* Evaluate state of TX, RX FIFOs 688 | * Compare this with RF24_QUEUE_* #define's from msprf24.h 689 | */ 690 | uint8_t msprf24_queue_state() 691 | { 692 | return r_reg(RF24_FIFO_STATUS); 693 | } 694 | 695 | /* Scan current channel for activity, produce an 8-bit integer indicating % of time 696 | * spent with RPD=1 (valid RF activity present) for a 133ms period. 697 | */ 698 | uint8_t msprf24_scan() 699 | { 700 | int testcount = 1023; 701 | uint16_t rpdcount = 0; 702 | uint8_t last_state; 703 | 704 | last_state = msprf24_current_state(); 705 | if (last_state != RF24_STATE_PRX) 706 | msprf24_activate_rx(); 707 | for (; testcount > 0; testcount--) { 708 | if (r_reg(RF24_RPD)) 709 | rpdcount++; 710 | __delay_cycles(DELAY_CYCLES_130US); 711 | flush_rx(); 712 | w_reg(RF24_STATUS, RF24_RX_DR); /* Flush any RX FIFO contents or RX IRQs that 713 | * may have generated as a result of having PRX active. 714 | */ 715 | } 716 | if (last_state != RF24_STATE_PRX) 717 | msprf24_standby(); // If we weren't in RX mode before, leave it in Standby-I. 718 | return( (uint8_t) (rpdcount/4) ); 719 | } 720 | 721 | // Check if there is pending RX fifo data 722 | uint8_t msprf24_rx_pending() 723 | { 724 | CSN_EN; 725 | rf_status = spi_transfer(RF24_NOP); 726 | CSN_DIS; 727 | 728 | if ((rf_status & 0x0E) < 0x0E) 729 | return 1; 730 | return 0; 731 | } 732 | 733 | // Get IRQ flag status 734 | uint8_t msprf24_get_irq_reason() 735 | { 736 | uint8_t rf_irq_old = rf_irq; 737 | 738 | //rf_irq &= ~RF24_IRQ_FLAGGED; -- Removing in lieu of having this check determined at irq_clear() time 739 | CSN_EN; 740 | rf_status = spi_transfer(RF24_NOP); 741 | CSN_DIS; 742 | rf_irq = (rf_status & RF24_IRQ_MASK) | rf_irq_old; 743 | return rf_irq; 744 | } 745 | 746 | /* Clear IRQ flags */ 747 | void msprf24_irq_clear(uint8_t irqflag) 748 | { 749 | uint8_t fifostat; 750 | 751 | rf_irq = 0x00; // Clear IRQs; afterward analyze RX FIFO to see if we should re-set RX IRQ flag. 752 | CSN_EN; 753 | rf_status = spi_transfer(RF24_STATUS | RF24_W_REGISTER); 754 | spi_transfer(irqflag); 755 | CSN_DIS; 756 | 757 | // Per datasheet procedure, check FIFO_STATUS to see if there's more RX FIFO data to process. 758 | if (irqflag & RF24_IRQ_RX) { 759 | CSN_EN; 760 | rf_status = spi_transfer(RF24_FIFO_STATUS | RF24_R_REGISTER); 761 | fifostat = spi_transfer(RF24_NOP); 762 | CSN_DIS; 763 | if ( !(fifostat & RF24_RX_EMPTY) ) 764 | rf_irq |= RF24_IRQ_RX | RF24_IRQ_FLAGGED; // Signal to user that there is remaining data, even if it's not "new" 765 | } 766 | } 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | /* - - Interrupt vectors - - */ 778 | 779 | // RF transceiver IRQ handling 780 | #if nrfIRQport == 2 781 | #ifdef __GNUC__ 782 | __attribute__((interrupt(PORT2_VECTOR))) 783 | void P2_IRQ (void) { 784 | #else 785 | #pragma vector = PORT2_VECTOR 786 | __interrupt void P2_IRQ (void) { 787 | #endif 788 | if(P2IFG & nrfIRQpin) { 789 | __bic_SR_register_on_exit(LPM4_bits); // Wake up 790 | rf_irq |= RF24_IRQ_FLAGGED; 791 | P2IFG &= ~nrfIRQpin; // Clear interrupt flag 792 | } 793 | } 794 | 795 | #elif nrfIRQport == 1 796 | #ifdef __GNUC__ 797 | __attribute__((interrupt(PORT1_VECTOR))) 798 | void P1_IRQ (void) { 799 | #else 800 | #pragma vector = PORT1_VECTOR 801 | __interrupt void P1_IRQ (void) { 802 | #endif 803 | if(P1IFG & nrfIRQpin) { 804 | __bic_SR_register_on_exit(LPM4_bits); 805 | rf_irq |= RF24_IRQ_FLAGGED; 806 | P1IFG &= ~nrfIRQpin; 807 | } 808 | } 809 | 810 | #elif nrfIRQport == 3 && defined(P3IV_) 811 | #ifdef __GNUC__ 812 | __attribute__((interrupt(PORT3_VECTOR))) 813 | void P3_IRQ (void) { 814 | #else 815 | #pragma vector = PORT3_VECTOR 816 | __interrupt void P3_IRQ (void) { 817 | #endif 818 | if (P3IFG & nrfIRQpin) { 819 | __bic_SR_register_on_exit(LPM4_bits); 820 | rf_irq |= RF24_IRQ_FLAGGED; 821 | P3IFG &= ~nrfIRQpin; 822 | } 823 | } 824 | 825 | #elif nrfIRQport == 4 && defined(P4IV_) 826 | #ifdef __GNUC__ 827 | __attribute__((interrupt(PORT4_VECTOR))) 828 | void P4_IRQ (void) { 829 | #else 830 | #pragma vector = PORT4_VECTOR 831 | __interrupt void P4_IRQ (void) { 832 | #endif 833 | if (P4IFG & nrfIRQpin) { 834 | __bic_SR_register_on_exit(LPM4_bits); 835 | rf_irq |= RF24_IRQ_FLAGGED; 836 | P4IFG &= ~nrfIRQpin; 837 | } 838 | } 839 | 840 | #elif nrfIRQport == 5 && defined(P5IV_) 841 | #ifdef __GNUC__ 842 | __attribute__((interrupt(PORT5_VECTOR))) 843 | void P5_IRQ (void) { 844 | #else 845 | #pragma vector = PORT5_VECTOR 846 | __interrupt void P5_IRQ (void) { 847 | #endif 848 | if (P5IFG & nrfIRQpin) { 849 | __bic_SR_register_on_exit(LPM4_bits); 850 | rf_irq |= RF24_IRQ_FLAGGED; 851 | P5IFG &= ~nrfIRQpin; 852 | } 853 | } 854 | 855 | #elif nrfIRQport == 6 && defined(P6IV_) 856 | #ifdef __GNUC__ 857 | __attribute__((interrupt(PORT6_VECTOR))) 858 | void P6_IRQ (void) { 859 | #else 860 | #pragma vector = PORT6_VECTOR 861 | __interrupt void P6_IRQ (void) { 862 | #endif 863 | if (P6IFG & nrfIRQpin) { 864 | __bic_SR_register_on_exit(LPM4_bits); 865 | rf_irq |= RF24_IRQ_FLAGGED; 866 | P6IFG &= ~nrfIRQpin; 867 | } 868 | } 869 | 870 | #elif nrfIRQport == 7 && defined(P7IV_) 871 | #ifdef __GNUC__ 872 | __attribute__((interrupt(PORT7_VECTOR))) 873 | void P7_IRQ (void) { 874 | #else 875 | #pragma vector = PORT7_VECTOR 876 | __interrupt void P7_IRQ (void) { 877 | #endif 878 | if (P7IFG & nrfIRQpin) { 879 | __bic_SR_register_on_exit(LPM4_bits); 880 | rf_irq |= RF24_IRQ_FLAGGED; 881 | P7IFG &= ~nrfIRQpin; 882 | } 883 | } 884 | 885 | #elif nrfIRQport == 8 && defined(P8IV_) 886 | #ifdef __GNUC__ 887 | __attribute__((interrupt(PORT8_VECTOR))) 888 | void P8_IRQ (void) { 889 | #else 890 | #pragma vector = PORT8_VECTOR 891 | __interrupt void P8_IRQ (void) { 892 | #endif 893 | if (P8IFG & nrfIRQpin) { 894 | __bic_SR_register_on_exit(LPM4_bits); 895 | rf_irq |= RF24_IRQ_FLAGGED; 896 | P8IFG &= ~nrfIRQpin; 897 | } 898 | } 899 | 900 | #elif nrfIRQport == 9 && defined(P9IV_) 901 | #ifdef __GNUC__ 902 | __attribute__((interrupt(PORT9_VECTOR))) 903 | void P9_IRQ (void) { 904 | #else 905 | #pragma vector = PORT9_VECTOR 906 | __interrupt void P9_IRQ (void) { 907 | #endif 908 | if (P9IFG & nrfIRQpin) { 909 | __bic_SR_register_on_exit(LPM4_bits); 910 | rf_irq |= RF24_IRQ_FLAGGED; 911 | P9IFG &= ~nrfIRQpin; 912 | } 913 | } 914 | 915 | #elif nrfIRQport == 10 && defined(P10IV_) 916 | #ifdef __GNUC__ 917 | __attribute__((interrupt(PORT10_VECTOR))) 918 | void P10_IRQ (void) { 919 | #else 920 | #pragma vector = PORT10_VECTOR 921 | __interrupt void P10_IRQ (void) { 922 | #endif 923 | if (P10IFG & nrfIRQpin) { 924 | __bic_SR_register_on_exit(LPM4_bits); 925 | rf_irq |= RF24_IRQ_FLAGGED; 926 | P10IFG &= ~nrfIRQpin; 927 | } 928 | } 929 | #endif 930 | 931 | -------------------------------------------------------------------------------- /msp430_spi.c: -------------------------------------------------------------------------------- 1 | /* msp430_spi.c 2 | * Library for performing SPI I/O on a wide range of MSP430 chips. 3 | * 4 | * Serial interfaces supported: 5 | * 1. USI - developed on MSP430G2231 6 | * 2. USCI_A - developed on MSP430G2553 7 | * 3. USCI_B - developed on MSP430G2553 8 | * 4. USCI_A F5xxx - developed on MSP430F5172, added F5529 9 | * 5. USCI_B F5xxx - developed on MSP430F5172, added F5529 10 | * 11 | * Copyright (c) 2013, Eric Brundick 12 | * 13 | * Permission to use, copy, modify, and/or distribute this software for any purpose 14 | * with or without fee is hereby granted, provided that the above copyright notice 15 | * and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 18 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 19 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 20 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 22 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include "msp430_spi.h" 27 | #include "nrf_userconfig.h" 28 | 29 | 30 | #ifdef __MSP430_HAS_USI__ 31 | void spi_init() 32 | { 33 | /* USI SPI setup */ 34 | USICTL0 |= USISWRST; 35 | USICTL1 = USICKPH; // USICKPH=1 means sampling is done on the leading edge of the clock 36 | USICKCTL = USISSEL_2 | USIDIV_0; // Clock source = SMCLK/1 37 | USICTL0 = USIPE7 | USIPE6 | USIPE5 | USIMST | USIOE; 38 | USISR = 0x0000; 39 | } 40 | 41 | uint8_t spi_transfer(uint8_t inb) 42 | { 43 | USISRL = inb; 44 | USICNT = 8; // Start SPI transfer 45 | while ( !(USICTL1 & USIIFG) ) 46 | ; 47 | return USISRL; 48 | } 49 | 50 | /* What wonderful toys TI gives us! A 16-bit SPI function. */ 51 | uint16_t spi_transfer16(uint16_t inw) 52 | { 53 | USISR = inw; 54 | USICNT = 16 | USI16B; // Start 16-bit SPI transfer 55 | while ( !(USICTL1 & USIIFG) ) 56 | ; 57 | return USISR; 58 | } 59 | 60 | /* Not used by msprf24, but added for courtesy (LCD display support). 9-bit SPI. */ 61 | uint16_t spi_transfer9(uint16_t inw) 62 | { 63 | USISR = inw; 64 | USICNT = 9 | USI16B; // Start 9-bit SPI transfer 65 | while ( !(USICTL1 & USIIFG) ) 66 | ; 67 | return USISR; 68 | } 69 | #endif 70 | 71 | /* USCI 16-bit transfer functions rely on the Little-Endian architecture and use 72 | * an internal uint8_t * pointer to manipulate the individual 8-bit segments of a 73 | * 16-bit integer. 74 | */ 75 | 76 | // USCI for F2xxx and G2xx3 devices 77 | #if defined(__MSP430_HAS_USCI__) && defined(SPI_DRIVER_USCI_A) && !defined(__MSP430_HAS_TB3__) 78 | void spi_init() 79 | { 80 | /* Configure ports on MSP430 device for USCI_A */ 81 | P1SEL |= BIT1 | BIT2 | BIT4; 82 | P1SEL2 |= BIT1 | BIT2 | BIT4; 83 | 84 | /* USCI-A specific SPI setup */ 85 | UCA0CTL1 |= UCSWRST; 86 | UCA0MCTL = 0x00; // Clearing modulation control per TI user's guide recommendation 87 | UCA0CTL0 = UCCKPH | UCMSB | UCMST | UCMODE_0 | UCSYNC; // SPI mode 0, master 88 | UCA0BR0 = 0x01; // SPI clocked at same speed as SMCLK 89 | UCA0BR1 = 0x00; 90 | UCA0CTL1 = UCSSEL_2; // Clock = SMCLK, clear UCSWRST and enables USCI_A module. 91 | } 92 | 93 | uint8_t spi_transfer(uint8_t inb) 94 | { 95 | UCA0TXBUF = inb; 96 | while ( !(IFG2 & UCA0RXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 97 | ; 98 | return UCA0RXBUF; 99 | } 100 | 101 | uint16_t spi_transfer16(uint16_t inw) 102 | { 103 | uint16_t retw; 104 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 105 | 106 | UCA0TXBUF = inw8[1]; 107 | while ( !(IFG2 & UCA0RXIFG) ) 108 | ; 109 | retw8[1] = UCA0RXBUF; 110 | UCA0TXBUF = inw8[0]; 111 | while ( !(IFG2 & UCA0RXIFG) ) 112 | ; 113 | retw8[0] = UCA0RXBUF; 114 | return retw; 115 | } 116 | 117 | uint16_t spi_transfer9(uint16_t inw) 118 | { 119 | uint8_t p1dir_save, p1out_save, p1ren_save; 120 | uint16_t retw=0; 121 | 122 | /* Reconfigure I/O ports for bitbanging the MSB */ 123 | p1ren_save = P1REN; p1out_save = P1OUT; p1dir_save = P1DIR; 124 | P1REN &= ~(BIT1 | BIT2 | BIT4); 125 | P1OUT &= ~(BIT1 | BIT2 | BIT4); 126 | P1DIR = (P1DIR & ~(BIT1 | BIT2 | BIT4)) | BIT2 | BIT4; 127 | P1SEL &= ~(BIT1 | BIT2 | BIT4); 128 | P1SEL2 &= ~(BIT1 | BIT2 | BIT4); 129 | 130 | // Perform single-bit transfer 131 | if (inw & 0x0100) 132 | P1OUT |= BIT2; 133 | P1OUT |= BIT4; 134 | if (P1IN & BIT1) 135 | retw |= 0x0100; 136 | P1OUT &= ~BIT4; 137 | 138 | // Restore port states and continue with 8-bit SPI 139 | P1SEL |= BIT1 | BIT2 | BIT4; 140 | P1SEL2 |= BIT1 | BIT2 | BIT4; 141 | P1DIR = p1dir_save; 142 | P1OUT = p1out_save; 143 | P1REN = p1ren_save; 144 | 145 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 146 | return retw; 147 | } 148 | #endif 149 | 150 | #if defined(__MSP430_HAS_USCI__) && defined(SPI_DRIVER_USCI_B) && !defined(__MSP430_HAS_TB3__) 151 | void spi_init() 152 | { 153 | /* Configure ports on MSP430 device for USCI_B */ 154 | P1SEL |= BIT5 | BIT6 | BIT7; 155 | P1SEL2 |= BIT5 | BIT6 | BIT7; 156 | 157 | /* USCI-B specific SPI setup */ 158 | UCB0CTL1 |= UCSWRST; 159 | UCB0CTL0 = UCCKPH | UCMSB | UCMST | UCMODE_0 | UCSYNC; // SPI mode 0, master 160 | UCB0BR0 = 0x01; // SPI clocked at same speed as SMCLK 161 | UCB0BR1 = 0x00; 162 | UCB0CTL1 = UCSSEL_2; // Clock = SMCLK, clear UCSWRST and enables USCI_B module. 163 | } 164 | 165 | uint8_t spi_transfer(uint8_t inb) 166 | { 167 | UCB0TXBUF = inb; 168 | while ( !(IFG2 & UCB0RXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 169 | ; 170 | return UCB0RXBUF; 171 | } 172 | 173 | uint16_t spi_transfer16(uint16_t inw) 174 | { 175 | uint16_t retw; 176 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 177 | 178 | UCB0TXBUF = inw8[1]; 179 | while ( !(IFG2 & UCB0RXIFG) ) 180 | ; 181 | retw8[1] = UCB0RXBUF; 182 | UCB0TXBUF = inw8[0]; 183 | while ( !(IFG2 & UCB0RXIFG) ) 184 | ; 185 | retw8[0] = UCB0RXBUF; 186 | return retw; 187 | } 188 | 189 | uint16_t spi_transfer9(uint16_t inw) 190 | { 191 | uint8_t p1dir_save, p1out_save, p1ren_save; 192 | uint16_t retw=0; 193 | 194 | /* Reconfigure I/O ports for bitbanging the MSB */ 195 | p1ren_save = P1REN; p1out_save = P1OUT; p1dir_save = P1DIR; 196 | P1REN &= ~(BIT5 | BIT6 | BIT7); 197 | P1OUT &= ~(BIT5 | BIT6 | BIT7); 198 | P1DIR = (P1DIR & ~(BIT5 | BIT6 | BIT7)) | BIT5 | BIT7; 199 | P1SEL &= ~(BIT5 | BIT6 | BIT7); 200 | P1SEL2 &= ~(BIT5 | BIT6 | BIT7); 201 | 202 | // Perform single-bit transfer 203 | if (inw & 0x0100) 204 | P1OUT |= BIT7; 205 | P1OUT |= BIT5; 206 | if (P1IN & BIT6) 207 | retw |= 0x0100; 208 | P1OUT &= ~BIT5; 209 | 210 | // Restore port states and continue with 8-bit SPI 211 | P1SEL |= BIT5 | BIT6 | BIT7; 212 | P1SEL2 |= BIT5 | BIT6 | BIT7; 213 | P1DIR = p1dir_save; 214 | P1OUT = p1out_save; 215 | P1REN = p1ren_save; 216 | 217 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 218 | return retw; 219 | } 220 | #endif 221 | 222 | // USCI for G2xx4/G2xx5 devices 223 | #if defined(__MSP430_HAS_USCI__) && defined(SPI_DRIVER_USCI_A) && defined(__MSP430_HAS_TB3__) 224 | void spi_init() 225 | { 226 | /* Configure ports on MSP430 device for USCI_A */ 227 | P3SEL |= BIT0 | BIT4 | BIT5; 228 | P3SEL2 &= ~(BIT0 | BIT4 | BIT5); 229 | 230 | /* USCI-A specific SPI setup */ 231 | UCA0CTL1 |= UCSWRST; 232 | UCA0MCTL = 0x00; // Clearing modulation control per TI user's guide recommendation 233 | UCA0CTL0 = UCCKPH | UCMSB | UCMST | UCMODE_0 | UCSYNC; // SPI mode 0, master 234 | UCA0BR0 = 0x01; // SPI clocked at same speed as SMCLK 235 | UCA0BR1 = 0x00; 236 | UCA0CTL1 = UCSSEL_2; // Clock = SMCLK, clear UCSWRST and enables USCI_A module. 237 | } 238 | 239 | uint8_t spi_transfer(uint8_t inb) 240 | { 241 | UCA0TXBUF = inb; 242 | while ( !(IFG2 & UCA0RXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 243 | ; 244 | return UCA0RXBUF; 245 | } 246 | 247 | uint16_t spi_transfer16(uint16_t inw) 248 | { 249 | uint16_t retw; 250 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 251 | 252 | UCA0TXBUF = inw8[1]; 253 | while ( !(IFG2 & UCA0RXIFG) ) 254 | ; 255 | retw8[1] = UCA0RXBUF; 256 | UCA0TXBUF = inw8[0]; 257 | while ( !(IFG2 & UCA0RXIFG) ) 258 | ; 259 | retw8[0] = UCA0RXBUF; 260 | return retw; 261 | } 262 | 263 | uint16_t spi_transfer9(uint16_t inw) 264 | { 265 | uint8_t p3dir_save, p3out_save, p3ren_save; 266 | uint16_t retw=0; 267 | 268 | /* Reconfigure I/O ports for bitbanging the MSB */ 269 | p3ren_save = P3REN; p3out_save = P3OUT; p3dir_save = P3DIR; 270 | P3REN &= ~(BIT0 | BIT4 | BIT5); 271 | P3OUT &= ~(BIT0 | BIT4 | BIT5); 272 | P3DIR = (P3DIR & ~(BIT0 | BIT4 | BIT5)) | BIT0 | BIT4; 273 | P3SEL &= ~(BIT0 | BIT4 | BIT5); 274 | P3SEL2 &= ~(BIT0 | BIT4 | BIT5); 275 | 276 | // Perform single-bit transfer 277 | if (inw & 0x0100) 278 | P3OUT |= BIT4; 279 | P3OUT |= BIT0; 280 | if (P3IN & BIT5) 281 | retw |= 0x0100; 282 | P3OUT &= ~BIT0; 283 | 284 | // Restore port states and continue with 8-bit SPI 285 | P3SEL |= BIT0 | BIT4 | BIT5; 286 | P3DIR = p3dir_save; 287 | P3OUT = p3out_save; 288 | P3REN = p3ren_save; 289 | 290 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 291 | return retw; 292 | } 293 | #endif 294 | 295 | #if defined(__MSP430_HAS_USCI__) && defined(SPI_DRIVER_USCI_B) && defined(__MSP430_HAS_TB3__) 296 | void spi_init() 297 | { 298 | /* Configure ports on MSP430 device for USCI_B */ 299 | P3SEL |= BIT1 | BIT2 | BIT3; 300 | P3SEL2 &= ~(BIT1 | BIT2 | BIT3); 301 | 302 | /* USCI-B specific SPI setup */ 303 | UCB0CTL1 |= UCSWRST; 304 | UCB0CTL0 = UCCKPH | UCMSB | UCMST | UCMODE_0 | UCSYNC; // SPI mode 0, master 305 | UCB0BR0 = 0x01; // SPI clocked at same speed as SMCLK 306 | UCB0BR1 = 0x00; 307 | UCB0CTL1 = UCSSEL_2; // Clock = SMCLK, clear UCSWRST and enables USCI_B module. 308 | } 309 | 310 | uint8_t spi_transfer(uint8_t inb) 311 | { 312 | UCB0TXBUF = inb; 313 | while ( !(IFG2 & UCB0RXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 314 | ; 315 | return UCB0RXBUF; 316 | } 317 | 318 | uint16_t spi_transfer16(uint16_t inw) 319 | { 320 | uint16_t retw; 321 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 322 | 323 | UCB0TXBUF = inw8[1]; 324 | while ( !(IFG2 & UCB0RXIFG) ) 325 | ; 326 | retw8[1] = UCB0RXBUF; 327 | UCB0TXBUF = inw8[0]; 328 | while ( !(IFG2 & UCB0RXIFG) ) 329 | ; 330 | retw8[0] = UCB0RXBUF; 331 | return retw; 332 | } 333 | 334 | uint16_t spi_transfer9(uint16_t inw) 335 | { 336 | uint8_t p3dir_save, p3out_save, p3ren_save; 337 | uint16_t retw=0; 338 | 339 | /* Reconfigure I/O ports for bitbanging the MSB */ 340 | p3ren_save = P3REN; p3out_save = P3OUT; p3dir_save = P3DIR; 341 | P3REN &= ~(BIT1 | BIT2 | BIT3); 342 | P3OUT &= ~(BIT1 | BIT2 | BIT3); 343 | P3DIR = (P3DIR & ~(BIT1 | BIT2 | BIT3)) | BIT1 | BIT3; 344 | P3SEL &= ~(BIT1 | BIT2 | BIT3); 345 | P3SEL2 &= ~(BIT1 | BIT2 | BIT3); 346 | 347 | // Perform single-bit transfer 348 | if (inw & 0x0100) 349 | P3OUT |= BIT1; 350 | P3OUT |= BIT3; 351 | if (P3IN & BIT2) 352 | retw |= 0x0100; 353 | P3OUT &= ~BIT3; 354 | 355 | // Restore port states and continue with 8-bit SPI 356 | P3SEL |= BIT1 | BIT2 | BIT3; 357 | P3DIR = p3dir_save; 358 | P3OUT = p3out_save; 359 | P3REN = p3ren_save; 360 | 361 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 362 | return retw; 363 | } 364 | #endif 365 | 366 | // USCI for F5xxx/6xxx devices--F5172 specific P1SEL settings 367 | #if defined(__MSP430_HAS_USCI_A0__) && defined(SPI_DRIVER_USCI_A) 368 | void spi_init() 369 | { 370 | /* Configure ports on MSP430 device for USCI_A */ 371 | #ifdef __MSP430F5172 372 | P1SEL |= BIT0 | BIT1 | BIT2; 373 | #endif 374 | #ifdef __MSP430F5529 375 | P3SEL |= BIT3 | BIT4; 376 | P2SEL |= BIT7; 377 | #endif 378 | 379 | /* USCI-A specific SPI setup */ 380 | UCA0CTL1 |= UCSWRST; 381 | UCA0MCTL = 0x00; // Clearing modulation control per TI user's guide recommendation 382 | UCA0CTL0 = UCCKPH | UCMSB | UCMST | UCMODE_0 | UCSYNC; // SPI mode 0, master 383 | UCA0BR0 = 0x01; // SPI clocked at same speed as SMCLK 384 | UCA0BR1 = 0x00; 385 | UCA0CTL1 = UCSSEL_2; // Clock = SMCLK, clear UCSWRST and enables USCI_A module. 386 | } 387 | 388 | uint8_t spi_transfer(uint8_t inb) 389 | { 390 | UCA0TXBUF = inb; 391 | while ( !(UCA0IFG & UCRXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 392 | ; 393 | return UCA0RXBUF; 394 | } 395 | 396 | uint16_t spi_transfer16(uint16_t inw) 397 | { 398 | uint16_t retw; 399 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 400 | 401 | UCA0TXBUF = inw8[1]; 402 | while ( !(UCA0IFG & UCRXIFG) ) 403 | ; 404 | retw8[1] = UCA0RXBUF; 405 | UCA0TXBUF = inw8[0]; 406 | while ( !(UCA0IFG & UCRXIFG) ) 407 | ; 408 | retw8[0] = UCA0RXBUF; 409 | return retw; 410 | } 411 | 412 | #ifdef __MS430F5172 413 | uint16_t spi_transfer9(uint16_t inw) 414 | { 415 | uint8_t p1dir_save, p1out_save, p1ren_save; 416 | uint16_t retw=0; 417 | 418 | /* Reconfigure I/O ports for bitbanging the MSB */ 419 | p1ren_save = P1REN; p1out_save = P1OUT; p1dir_save = P1DIR; 420 | P1REN &= ~(BIT0 | BIT1 | BIT2); 421 | P1OUT &= ~(BIT0 | BIT1 | BIT2); 422 | P1DIR = (P1DIR & ~(BIT0 | BIT1 | BIT2)) | BIT0 | BIT1; 423 | P1SEL &= ~(BIT0 | BIT1 | BIT2); 424 | 425 | // Perform single-bit transfer 426 | if (inw & 0x0100) 427 | P1OUT |= BIT1; 428 | P1OUT |= BIT0; 429 | if (P1IN & BIT2) 430 | retw |= 0x0100; 431 | P1OUT &= ~BIT0; 432 | 433 | // Restore port states and continue with 8-bit SPI 434 | P1SEL |= BIT0 | BIT1 | BIT2; 435 | P1DIR = p1dir_save; 436 | P1OUT = p1out_save; 437 | P1REN = p1ren_save; 438 | 439 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 440 | return retw; 441 | } 442 | #endif 443 | 444 | #ifdef __MS430F5529 445 | uint16_t spi_transfer9(uint16_t inw) 446 | { 447 | uint8_t p3dir_save, p3out_save, p3ren_save; 448 | uint8_t p2dir_save, p2out_save, p2ren_save; 449 | uint16_t retw=0; 450 | 451 | /* Reconfigure I/O ports for bitbanging the MSB */ 452 | p3ren_save = P3REN; p3out_save = P3OUT; p3dir_save = P3DIR; 453 | p2ren_save = P2REN; p2out_save = P2OUT; p2dir_save = P2DIR; 454 | P3REN &= ~(BIT3 | BIT4); P2REN &= ~BIT7; 455 | P3OUT &= ~(BIT3 | BIT4); P2OUT &= ~BIT7; 456 | P3DIR = (P3DIR | ~(BIT3 | BIT4)) | BIT3; P2DIR |= BIT7; 457 | P3SEL &= ~(BIT3 | BIT4); P2SEL &= ~BIT7; 458 | 459 | // Perform single-bit transfer 460 | if (inw & 0x0100) 461 | P3OUT |= BIT3; 462 | P2OUT |= BIT7; 463 | if (P3IN & BIT4) 464 | retw |= 0x0100; 465 | P2OUT &= ~BIT7; 466 | 467 | // Restore port states and continue with 8-bit SPI 468 | P3SEL |= BIT3 | BIT4; P2SEL |= BIT7; 469 | P3DIR = p3dir_save; P2DIR = p2dir_save; 470 | P3OUT = p3out_save; P2OUT = p2out_save; 471 | P3REN = p3ren_save; P2REN = p2ren_save; 472 | 473 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 474 | return retw; 475 | } 476 | #endif 477 | 478 | #endif 479 | 480 | #if defined(__MSP430_HAS_USCI_B0__) && defined(SPI_DRIVER_USCI_B) 481 | void spi_init() 482 | { 483 | /* Configure ports on MSP430 device for USCI_B */ 484 | #ifdef __MSP430F5172 485 | P1SEL |= BIT3 | BIT4 | BIT5; 486 | #endif 487 | #ifdef __MSP430F5529 488 | P3SEL |= BIT0 | BIT1 | BIT2; 489 | #endif 490 | 491 | /* USCI-B specific SPI setup */ 492 | UCB0CTL1 |= UCSWRST; 493 | UCB0CTL0 = UCCKPH | UCMSB | UCMST | UCMODE_0 | UCSYNC; // SPI mode 0, master 494 | UCB0BR0 = 0x01; // SPI clocked at same speed as SMCLK 495 | UCB0BR1 = 0x00; 496 | UCB0CTL1 = UCSSEL_2; // Clock = SMCLK, clear UCSWRST and enables USCI_B module. 497 | } 498 | 499 | uint8_t spi_transfer(uint8_t inb) 500 | { 501 | UCB0TXBUF = inb; 502 | while ( !(UCB0IFG & UCRXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 503 | ; 504 | return UCB0RXBUF; 505 | } 506 | 507 | uint16_t spi_transfer16(uint16_t inw) 508 | { 509 | uint16_t retw; 510 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 511 | 512 | UCB0TXBUF = inw8[1]; 513 | while ( !(UCB0IFG & UCRXIFG) ) 514 | ; 515 | retw8[1] = UCB0RXBUF; 516 | UCB0TXBUF = inw8[0]; 517 | while ( !(UCB0IFG & UCRXIFG) ) 518 | ; 519 | retw8[0] = UCB0RXBUF; 520 | return retw; 521 | } 522 | 523 | #ifdef __MSP430F5172 524 | uint16_t spi_transfer9(uint16_t inw) 525 | { 526 | uint8_t p1dir_save, p1out_save, p1ren_save; 527 | uint16_t retw=0; 528 | 529 | /* Reconfigure I/O ports for bitbanging the MSB */ 530 | p1ren_save = P1REN; p1out_save = P1OUT; p1dir_save = P1DIR; 531 | P1REN &= ~(BIT3 | BIT4 | BIT5); 532 | P1OUT &= ~(BIT3 | BIT4 | BIT5); 533 | P1DIR = (P1DIR & ~(BIT3 | BIT4 | BIT5)) | BIT3 | BIT4; 534 | P1SEL &= ~(BIT3 | BIT4 | BIT5); 535 | 536 | // Perform single-bit transfer 537 | if (inw & 0x0100) 538 | P1OUT |= BIT4; 539 | P1OUT |= BIT3; 540 | if (P1IN & BIT5) 541 | retw |= 0x0100; 542 | P1OUT &= ~BIT3; 543 | 544 | // Restore port states and continue with 8-bit SPI 545 | P1SEL |= BIT3 | BIT4 | BIT5; 546 | P1DIR = p1dir_save; 547 | P1OUT = p1out_save; 548 | P1REN = p1ren_save; 549 | 550 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 551 | return retw; 552 | } 553 | #endif 554 | 555 | #ifdef __MSP430F5529 556 | uint16_t spi_transfer9(uint16_t inw) 557 | { 558 | uint8_t p3dir_save, p3out_save, p3ren_save; 559 | uint16_t retw=0; 560 | 561 | /* Reconfigure I/O ports for bitbanging the MSB */ 562 | p3ren_save = P3REN; p3out_save = P3OUT; p3dir_save = P3DIR; 563 | P3REN &= ~(BIT0 | BIT1 | BIT2); 564 | P3OUT &= ~(BIT0 | BIT1 | BIT2); 565 | P3DIR = (P3DIR & ~(BIT0 | BIT1 | BIT2)) | BIT0 | BIT2; 566 | P3SEL &= ~(BIT0 | BIT1 | BIT2); 567 | 568 | // Perform single-bit transfer 569 | if (inw & 0x0100) 570 | P3OUT |= BIT0; 571 | P3OUT |= BIT2; 572 | if (P3IN & BIT1) 573 | retw |= 0x0100; 574 | P3OUT &= ~BIT2; 575 | 576 | // Restore port states and continue with 8-bit SPI 577 | P3SEL |= BIT0 | BIT1 | BIT2; 578 | P3DIR = p3dir_save; 579 | P3OUT = p3out_save; 580 | P3REN = p3ren_save; 581 | 582 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 583 | return retw; 584 | } 585 | #endif 586 | 587 | #endif 588 | 589 | // Wolverine and other FRAM series chips 590 | #if defined(__MSP430_HAS_EUSCI_A0__) && (defined(SPI_DRIVER_USCI_A) || defined(SPI_DRIVER_USCI_A0)) 591 | void spi_init() 592 | { 593 | /* Configure ports on MSP430 device for USCI_A0 */ 594 | #if defined(__MSP430FR5969__) 595 | P1SEL0 &= ~BIT5; 596 | P1SEL1 |= BIT5; 597 | P2SEL0 &= ~(BIT0 | BIT1); 598 | P2SEL1 |= BIT0 | BIT1; 599 | #endif 600 | #if defined(__MSP430FR5739__) 601 | 602 | #endif 603 | #if defined(__MSP430FR4133__) 604 | P1SEL0 |= BIT0 | BIT1 | BIT2; 605 | #endif 606 | 607 | #if defined(__MSP430FR2311__) 608 | P1SEL0 |= BIT5 | BIT6 | BIT7; 609 | #endif 610 | 611 | /* USCI_A specific SPI setup */ 612 | UCA0CTLW0 |= UCSWRST; 613 | UCA0CTLW0 = UCCKPH | UCMST | UCMSB | UCSYNC | UCSSEL_2 | UCSWRST; 614 | UCA0BRW = 0x01; 615 | UCA0CTLW0 &= ~UCSWRST; 616 | } 617 | 618 | uint8_t spi_transfer(uint8_t inb) 619 | { 620 | UCA0TXBUF = inb; 621 | while ( !(UCA0IFG & UCRXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 622 | ; 623 | return UCA0RXBUF; 624 | } 625 | 626 | uint16_t spi_transfer16(uint16_t inw) 627 | { 628 | uint16_t retw; 629 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 630 | 631 | UCA0TXBUF = inw8[1]; 632 | while ( !(UCA0IFG & UCRXIFG) ) 633 | ; 634 | retw8[1] = UCA0RXBUF; 635 | UCA0TXBUF = inw8[0]; 636 | while ( !(UCA0IFG & UCRXIFG) ) 637 | ; 638 | retw8[0] = UCA0RXBUF; 639 | return retw; 640 | } 641 | 642 | #ifdef __MSP430FR5969__ 643 | uint16_t spi_transfer9(uint16_t inw) 644 | { 645 | uint8_t p1dir_save, p1out_save, p1ren_save; 646 | uint8_t p2dir_save, p2out_save, p2ren_save; 647 | uint16_t retw=0; 648 | 649 | /* Reconfigure I/O ports for bitbanging the MSB */ 650 | p1ren_save = P1REN; p1out_save = P1OUT; p1dir_save = P1DIR; 651 | p2ren_save = P2REN; p2out_save = P2OUT; p2dir_save = P2DIR; 652 | 653 | P1REN &= ~BIT5; 654 | P2REN &= ~(BIT0 | BIT1); 655 | P1OUT &= ~BIT5; 656 | P2OUT &= ~(BIT0 | BIT1); 657 | P1DIR |= BIT5; 658 | P2DIR = (P2DIR & ~(BIT0 | BIT1)) | BIT0; 659 | 660 | P1SEL1 &= ~BIT5; 661 | P2SEL1 &= ~(BIT0 | BIT1); 662 | 663 | // Perform single-bit transfer 664 | if (inw & 0x0100) 665 | P2OUT |= BIT0; 666 | P1OUT |= BIT5; 667 | if (P2IN & BIT1) 668 | retw |= 0x0100; 669 | P1OUT &= ~BIT5; 670 | 671 | // Restore port states and continue with 8-bit SPI 672 | P1SEL1 |= BIT5; 673 | P2SEL1 |= BIT0 | BIT1; 674 | 675 | P1DIR = p1dir_save; 676 | P2DIR = p2dir_save; 677 | P1OUT = p1out_save; 678 | P2OUT = p2out_save; 679 | P1REN = p1ren_save; 680 | P2REN = p2ren_save; 681 | 682 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 683 | return retw; 684 | } 685 | #endif 686 | #ifdef __MSP430FR4133__ 687 | uint16_t spi_transfer9(uint16_t inw) 688 | { 689 | uint8_t p1dir_save, p1out_save, p1ren_save; 690 | uint16_t retw=0; 691 | 692 | /* Reconfigure I/O ports for bitbanging the MSB */ 693 | p1ren_save = P1REN; p1out_save = P1OUT; p1dir_save = P1DIR; 694 | 695 | P1REN &= ~(BIT0 | BIT1 | BIT2); 696 | P1OUT &= ~(BIT0 | BIT1 | BIT2); 697 | P1DIR = (P1DIR & ~(BIT0 | BIT1 | BIT2)) | BIT0 | BIT2; 698 | P1SEL0 &= ~(BIT0 | BIT1 | BIT2); 699 | 700 | // Perform single-bit transfer 701 | if (inw & 0x0100) 702 | P1OUT |= BIT0; 703 | P1OUT |= BIT2; 704 | if (P1IN & BIT1) 705 | retw |= 0x0100; 706 | P1OUT &= ~BIT2; 707 | 708 | // Restore port states and continue with 8-bit SPI 709 | P1SEL0 |= BIT0 | BIT1 | BIT2; 710 | 711 | P1DIR = p1dir_save; 712 | P1OUT = p1out_save; 713 | P1REN = p1ren_save; 714 | 715 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 716 | return retw; 717 | } 718 | #endif 719 | #ifdef __MSP430FR2311__ 720 | uint16_t spi_transfer9(uint16_t inw) 721 | { 722 | uint8_t p1dir_save, p1out_save, p1ren_save; 723 | uint16_t retw=0; 724 | 725 | /* Reconfigure I/O ports for bitbanging the MSB */ 726 | p1ren_save = P1REN; p1out_save = P1OUT; p1dir_save = P1DIR; 727 | 728 | P1REN &= ~(BIT5 | BIT6 | BIT7); 729 | P1OUT &= ~(BIT5 | BIT6 | BIT7); 730 | P1DIR = (P1DIR & ~(BIT5 | BIT6 | BIT7)) | BIT5 | BIT7; 731 | P1SEL0 &= ~(BIT5 | BIT6 | BIT7); 732 | 733 | // Perform single-bit transfer 734 | if (inw & 0x0100) 735 | P1OUT |= BIT7; 736 | P1OUT |= BIT5; 737 | if (P1IN & BIT6) 738 | retw |= 0x0100; 739 | P1OUT &= ~BIT5; 740 | 741 | // Restore port states and continue with 8-bit SPI 742 | P1SEL0 |= BIT5 | BIT6 | BIT7; 743 | 744 | P1DIR = p1dir_save; 745 | P1OUT = p1out_save; 746 | P1REN = p1ren_save; 747 | 748 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 749 | return retw; 750 | } 751 | #endif 752 | 753 | #endif 754 | 755 | #if defined(__MSP430_HAS_EUSCI_A1__) && defined(SPI_DRIVER_USCI_A1) 756 | void spi_init() 757 | { 758 | /* Configure ports on MSP430 device for USCI_A1 */ 759 | #if defined(__MSP430FR5969__) 760 | P2SEL0 &= ~(BIT4 | BIT5 | BIT6); 761 | P2SEL1 |= BIT4 | BIT5 | BIT6; 762 | #endif 763 | #if defined(__MSP430FR5739__) 764 | 765 | #endif 766 | 767 | /* USCI_A specific SPI setup */ 768 | UCA1CTLW0 |= UCSWRST; 769 | UCA1CTLW0 = UCCKPH | UCMST | UCMSB | UCSYNC | UCSSEL_2 | UCSWRST; 770 | UCA1BRW = 0x01; 771 | UCA1CTLW0 &= ~UCSWRST; 772 | } 773 | 774 | uint8_t spi_transfer(uint8_t inb) 775 | { 776 | UCA1TXBUF = inb; 777 | while ( !(UCA1IFG & UCRXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 778 | ; 779 | return UCA1RXBUF; 780 | } 781 | 782 | uint16_t spi_transfer16(uint16_t inw) 783 | { 784 | uint16_t retw; 785 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 786 | 787 | UCA1TXBUF = inw8[1]; 788 | while ( !(UCA1IFG & UCRXIFG) ) 789 | ; 790 | retw8[1] = UCA1RXBUF; 791 | UCA1TXBUF = inw8[0]; 792 | while ( !(UCA1IFG & UCRXIFG) ) 793 | ; 794 | retw8[0] = UCA1RXBUF; 795 | return retw; 796 | } 797 | 798 | #ifdef __MSP430FR5969__ 799 | uint16_t spi_transfer9(uint16_t inw) 800 | { 801 | uint8_t p2dir_save, p2out_save, p2ren_save; 802 | uint16_t retw=0; 803 | 804 | /* Reconfigure I/O ports for bitbanging the MSB */ 805 | p2ren_save = P2REN; p2out_save = P2OUT; p2dir_save = P2DIR; 806 | 807 | P2REN &= ~(BIT4 | BIT5 | BIT6); 808 | P2OUT &= ~(BIT4 | BIT5 | BIT6); 809 | P2DIR = (P2DIR & ~(BIT4 | BIT5 | BIT6)) | BIT4 | BIT5; 810 | 811 | P2SEL1 &= ~(BIT0 | BIT1); 812 | 813 | // Perform single-bit transfer 814 | if (inw & 0x0100) 815 | P2OUT |= BIT5; 816 | P2OUT |= BIT4; 817 | if (P2IN & BIT6) 818 | retw |= 0x0100; 819 | P2OUT &= ~BIT4; 820 | 821 | // Restore port states and continue with 8-bit SPI 822 | P2SEL1 |= BIT0 | BIT1; 823 | 824 | P2DIR = p2dir_save; 825 | P2OUT = p2out_save; 826 | P2REN = p2ren_save; 827 | 828 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 829 | return retw; 830 | } 831 | #endif 832 | #endif 833 | 834 | #if defined(__MSP430_HAS_EUSCI_B0__) && (defined(SPI_DRIVER_USCI_B) || defined(SPI_DRIVER_USCI_B0)) 835 | void spi_init() 836 | { 837 | /* Configure ports on MSP430 device for USCI_B0 */ 838 | #if defined(__MSP430FR5969__) 839 | P1SEL0 &= ~(BIT6 | BIT7); 840 | P1SEL1 |= BIT6 | BIT7; 841 | P2SEL0 &= ~BIT2; 842 | P2SEL1 |= BIT2; 843 | #endif 844 | #if defined(__MSP430FR5739__) 845 | 846 | #endif 847 | #if defined(__MSP430FR4133__) 848 | P5SEL0 |= BIT1 | BIT2 | BIT3; 849 | #endif 850 | 851 | #if defined(__MSP430FR2311__) 852 | P2SEL0 |= BIT3 | BIT4 | BIT5; 853 | #endif 854 | 855 | /* USCI_B specific SPI setup */ 856 | UCB0CTLW0 |= UCSWRST; 857 | UCB0CTLW0 = UCCKPH | UCMST | UCMSB | UCSYNC | UCSSEL_2 | UCSWRST; 858 | UCB0BRW = 0x01; 859 | UCB0CTLW0 &= ~UCSWRST; 860 | } 861 | 862 | uint8_t spi_transfer(uint8_t inb) 863 | { 864 | UCB0TXBUF = inb; 865 | while ( !(UCB0IFG & UCRXIFG) ) // Wait for RXIFG indicating remote byte received via SOMI 866 | ; 867 | return UCB0RXBUF; 868 | } 869 | 870 | uint16_t spi_transfer16(uint16_t inw) 871 | { 872 | uint16_t retw; 873 | uint8_t *retw8 = (uint8_t *)&retw, *inw8 = (uint8_t *)&inw; 874 | 875 | UCB0TXBUF = inw8[1]; 876 | while ( !(UCB0IFG & UCRXIFG) ) 877 | ; 878 | retw8[1] = UCB0RXBUF; 879 | UCB0TXBUF = inw8[0]; 880 | while ( !(UCB0IFG & UCRXIFG) ) 881 | ; 882 | retw8[0] = UCB0RXBUF; 883 | return retw; 884 | } 885 | 886 | #ifdef __MSP430FR5969__ 887 | uint16_t spi_transfer9(uint16_t inw) 888 | { 889 | uint8_t p1dir_save, p1out_save, p1ren_save; 890 | uint8_t p2dir_save, p2out_save, p2ren_save; 891 | uint16_t retw=0; 892 | 893 | /* Reconfigure I/O ports for bitbanging the MSB */ 894 | p1ren_save = P1REN; p1out_save = P1OUT; p1dir_save = P1DIR; 895 | p2ren_save = P2REN; p2out_save = P2OUT; p2dir_save = P2DIR; 896 | 897 | P1REN &= ~(BIT6 | BIT7); 898 | P2REN &= ~BIT2; 899 | P1OUT &= ~(BIT6 | BIT7); 900 | P2OUT &= ~BIT2; 901 | P1DIR = (P1DIR & ~(BIT6 | BIT7)) | BIT6; 902 | P2DIR |= BIT2; 903 | 904 | P1SEL1 &= ~(BIT6 | BIT7); 905 | P2SEL1 &= ~BIT2; 906 | 907 | // Perform single-bit transfer 908 | if (inw & 0x0100) 909 | P1OUT |= BIT6; 910 | P2OUT |= BIT2; 911 | if (P1IN & BIT7) 912 | retw |= 0x0100; 913 | P2OUT &= ~BIT2; 914 | 915 | // Restore port states and continue with 8-bit SPI 916 | P1SEL1 |= BIT6 | BIT7; 917 | P2SEL1 |= BIT2; 918 | 919 | P1DIR = p1dir_save; 920 | P2DIR = p2dir_save; 921 | P1OUT = p1out_save; 922 | P2OUT = p2out_save; 923 | P1REN = p1ren_save; 924 | P2REN = p2ren_save; 925 | 926 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 927 | return retw; 928 | } 929 | #endif 930 | #ifdef __MSP430FR4133__ 931 | uint16_t spi_transfer9(uint16_t inw) 932 | { 933 | uint8_t p5dir_save, p5out_save, p5ren_save; 934 | uint16_t retw=0; 935 | 936 | /* Reconfigure I/O ports for bitbanging the MSB */ 937 | p5ren_save = P5REN; p5out_save = P5OUT; p5dir_save = P5DIR; 938 | 939 | P5REN &= ~(BIT1 | BIT2 | BIT3); 940 | P5OUT &= ~(BIT1 | BIT2 | BIT3); 941 | P5DIR = (P1DIR & ~(BIT1 | BIT2 | BIT3)) | BIT1 | BIT2; 942 | P5SEL0 &= ~(BIT1 | BIT2 | BIT3); 943 | 944 | // Perform single-bit transfer 945 | if (inw & 0x0100) 946 | P5OUT |= BIT2; 947 | P5OUT |= BIT1; 948 | if (P5IN & BIT3) 949 | retw |= 0x0100; 950 | P5OUT &= ~BIT1; 951 | 952 | // Restore port states and continue with 8-bit SPI 953 | P5SEL0 |= BIT1 | BIT2 | BIT3; 954 | 955 | P5DIR = p5dir_save; 956 | P5OUT = p5out_save; 957 | P5REN = p5ren_save; 958 | 959 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 960 | return retw; 961 | } 962 | #endif 963 | #ifdef __MSP430FR2311__ 964 | uint16_t spi_transfer9(uint16_t inw) 965 | { 966 | uint8_t p2dir_save, p2out_save, p2ren_save; 967 | uint16_t retw=0; 968 | 969 | /* Reconfigure I/O ports for bitbanging the MSB */ 970 | p2ren_save = P2REN; p2out_save = P2OUT; p2dir_save = P2DIR; 971 | 972 | P2REN &= ~(BIT3 | BIT4 | BIT5); 973 | P2OUT &= ~(BIT3 | BIT4 | BIT5); 974 | P2DIR |= BIT3 | BIT5; 975 | 976 | P2SEL0 &= ~(BIT3 | BIT4 | BIT5); 977 | 978 | // Perform single-bit transfer 979 | if (inw & 0x0100) 980 | P2OUT |= BIT5; 981 | P2OUT |= BIT3; 982 | if (P2IN & BIT4) 983 | retw |= 0x0100; 984 | P2OUT &= ~BIT3; 985 | 986 | // Restore port states and continue with 8-bit SPI 987 | P2SEL0 |= BIT3 | BIT4 | BIT5; 988 | 989 | P2DIR = p2dir_save; 990 | P2OUT = p2out_save; 991 | P2REN = p2ren_save; 992 | 993 | retw |= spi_transfer( (uint8_t)(inw & 0x00FF) ); 994 | return retw; 995 | } 996 | #endif 997 | 998 | 999 | #endif 1000 | --------------------------------------------------------------------------------