diff --git a/RPi/RF24/Makefile b/RPi/RF24/Makefile new file mode 100644 index 0000000..c2ae37d --- /dev/null +++ b/RPi/RF24/Makefile @@ -0,0 +1,55 @@ +############################################################################# +# +# Makefile for librf24-bcm on Raspberry Pi +# +# License: GPL (General Public License) +# Author: Charles-Henri Hallard +# Date: 2013/03/13 +# +# Description: +# ------------ +# use make all and mak install to install the library +# You can change the install directory by editing the LIBDIR line +# +PREFIX=/usr/local + +# Library parameters +# where to put the lib +LIBDIR=$(PREFIX)/lib +# lib name +LIB=librf24-bcm +# shared library name +LIBNAME=$(LIB).so.1.0 + + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s + +# make all +# reinstall the library after each recompilation +all: librf24-bcm install + +# Make the library +librf24-bcm: RF24.o bcm2835.o + g++ -shared -Wl,-soname,$@.so.1 ${CCFLAGS} -o ${LIBNAME} $^ + +# Library parts +RF24.o: RF24.cpp + g++ -Wall -fPIC ${CCFLAGS} -c $^ + +bcm2835.o: bcm2835.c + gcc -Wall -fPIC ${CCFLAGS} -c $^ + +# clear build files +clean: + rm -rf *.o ${LIB}.* + +# Install the library to LIBPATH +install: + @echo "[Install]" + @if ( test ! -d $(PREFIX)/lib ) ; then mkdir -p $(PREFIX)/lib ; fi + #@install -m 0755 ${LIB}.a ${LIBDIR} + @install -m 0755 ${LIBNAME} ${LIBDIR} + @ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so.1 + @ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so + @ldconfig diff --git a/RPi/RF24/RF24.cpp b/RPi/RF24/RF24.cpp new file mode 100644 index 0000000..303fde0 --- /dev/null +++ b/RPi/RF24/RF24.cpp @@ -0,0 +1,1132 @@ +/* + Copyright (C) 2011 J. Coliz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + + + +03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 library + +TMRh20 2014: Updated to work with optimized RF24 and RF24 Network Arduino libs. +*/ + +#include "./RF24_config.h" +#include "./RF24.h" +#include "./nRF24L01.h" + + +/****************************************************************************/ +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size = len + 1; // Add register value to transmit buffer + + *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); + while (len--){ + *ptx++ = NOP ; // Dummy operation, just for reading + } + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + //bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) buf, size); + + status = *prx++; // status is 1st byte of receive buffer + + // decrement before to skip status byte + while ( --size ){ + *buf++ = *prx++; + } + + return status; +} + +/****************************************************************************/ +uint8_t RF24::read_register(uint8_t reg) +{ + uint8_t result; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + + *ptx++ = ( R_REGISTER | ( REGISTER_MASK & reg ) ); + *ptx++ = NOP ; // Dummy operation, just for reading + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + result = *++prx; // result is 2nd byte of receive buffer + + return result; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, uint8_t value) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + + *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); + *ptx = value ; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + status = *prx++; // status is 1st byte of receive buffer + + if (debug) + printf("write_register(%02x,%02x)\r\n",reg,value); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size = len + 1; // Add register value to transmit buffer + + *ptx++ = ( W_REGISTER | ( REGISTER_MASK & reg ) ); + while ( len-- ) + *ptx++ = *buf++; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + status = *prx; // status is 1st byte of receive buffer + + return status; +} + + +/****************************************************************************/ + +uint8_t RF24::write_payload(const void* buf, uint8_t len, const uint8_t writeType) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + const uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + size = data_len + blank_len + 1 ; // Add register value to transmit buffer + + if (debug) + printf("[Writing %u bytes %u blanks]",data_len,blank_len); + + *ptx++ = W_TX_PAYLOAD; + while ( data_len-- ) + *ptx++ = *current++; + while ( blank_len-- ) + *ptx++ = 0; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + status = *prx; // status is 1st byte of receive buffer + + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::read_payload(void* buf, uint8_t len) +{ + uint8_t status; + uint8_t * prx = spi_rxbuff; + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + + size = data_len + blank_len + 1; // Add register value to transmit buffer + + if (debug) + printf("[Reading %u bytes %u blanks]",data_len,blank_len); + + *ptx++ = R_RX_PAYLOAD; + while(size--) + *ptx++ = NOP; + + // Size has been lost during while, re affect + size = data_len + blank_len + 1; // Add register value to transmit buffer + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size); + + // 1st byte is status + status = *prx++; + + // Decrement before to skip 1st status byte + while ( --size ) + *current++ = *prx++; + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::flush_rx(void) +{ + uint8_t status; + + status = bcm2835_spi_transfer( FLUSH_RX ); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::flush_tx(void) +{ + uint8_t status; + + status = bcm2835_spi_transfer( FLUSH_TX ); + + return status; +} + +/****************************************************************************/ + +uint8_t RF24::get_status(void) +{ + return bcm2835_spi_transfer( NOP ); +} + +/****************************************************************************/ + +void RF24::print_status(uint8_t status) +{ + printf("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n", + status, + (status & _BV(RX_DR))?1:0, + (status & _BV(TX_DS))?1:0, + (status & _BV(MAX_RT))?1:0, + ((status >> RX_P_NO) & 0b111), + (status & _BV(TX_FULL))?1:0 + ); +} + +/****************************************************************************/ + +void RF24::print_observe_tx(uint8_t value) +{ + printf("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n", + value, + (value >> PLOS_CNT) & 0b1111, + (value >> ARC_CNT) & 0b1111 + ); +} + +/****************************************************************************/ + +void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen(name) < 8 ? '\t' : 0; + printf("%s\t%c =", name, extra_tab); + while (qty--) + printf(" 0x%02x",read_register(reg++)); + printf("\n"); +} + +/****************************************************************************/ + +void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) +{ + char extra_tab = strlen(name) < 8 ? '\t' : 0; + printf("%s\t%c =",name,extra_tab); + + while (qty--) + { + uint8_t buffer[5]; + read_register(reg++,buffer,sizeof buffer); + + printf(" 0x"); + uint8_t* bufptr = buffer + sizeof buffer; + while( --bufptr >= buffer ) + printf("%02x",*bufptr); + } + + printf("\r\n"); +} + + +/****************************************************************************/ + +RF24::RF24(uint8_t _cepin, uint8_t _cspin, uint32_t _spi_speed): + ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), wide_band(true), p_variant(false), + payload_size(32), dynamic_payloads_enabled(false), + pipe0_reading_address(0) +{ +} + +/****************************************************************************/ + +void RF24::setChannel(uint8_t channel) +{ + // TODO: This method could take advantage of the 'wide_band' calculation + // done in setChannel() to require certain channel spacing. + + const uint8_t max_channel = 127; + write_register(RF_CH,min(channel,max_channel)); +} + +/****************************************************************************/ + +void RF24::setPayloadSize(uint8_t size) +{ + const uint8_t max_payload_size = 32; + payload_size = min(size,max_payload_size); +} + +/****************************************************************************/ + +uint8_t RF24::getPayloadSize(void) +{ + return payload_size; +} + +/****************************************************************************/ + +static const char rf24_datarate_e_str_0[] = "1MBPS"; +static const char rf24_datarate_e_str_1[] = "2MBPS"; +static const char rf24_datarate_e_str_2[] = "250KBPS"; +static const char * const rf24_datarate_e_str_P[] = { + rf24_datarate_e_str_0, + rf24_datarate_e_str_1, + rf24_datarate_e_str_2, +}; +static const char rf24_model_e_str_0[] = "nRF24L01"; +static const char rf24_model_e_str_1[] = "nRF24L01+"; +static const char * const rf24_model_e_str_P[] = { + rf24_model_e_str_0, + rf24_model_e_str_1, +}; +static const char rf24_crclength_e_str_0[] = "Disabled"; +static const char rf24_crclength_e_str_1[] = "8 bits"; +static const char rf24_crclength_e_str_2[] = "16 bits" ; +static const char * const rf24_crclength_e_str_P[] = { + rf24_crclength_e_str_0, + rf24_crclength_e_str_1, + rf24_crclength_e_str_2, +}; +static const char rf24_pa_dbm_e_str_0[] = "PA_MIN"; +static const char rf24_pa_dbm_e_str_1[] = "PA_LOW"; +static const char rf24_pa_dbm_e_str_2[] = "PA_HIGH"; +static const char rf24_pa_dbm_e_str_3[] = "PA_MAX"; +static const char * const rf24_pa_dbm_e_str_P[] = { + rf24_pa_dbm_e_str_0, + rf24_pa_dbm_e_str_1, + rf24_pa_dbm_e_str_2, + rf24_pa_dbm_e_str_3, +}; + +static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)"; +static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)"; +static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)"; +static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven"; +static const char * const rf24_csn_e_str_P[] = { + rf24_csn_e_str_0, + rf24_csn_e_str_1, + rf24_csn_e_str_2, + rf24_csn_e_str_3, +}; + +// Display NRF24L01 details +void RF24::printDetails(void) +{ + printf("================ SPI Configuration ================\n" ); + + if (csn_pin < BCM2835_SPI_CS_NONE ) + { + printf("CSN Pin \t = %s\n",rf24_csn_e_str_P[csn_pin]); + } + else + { + printf("CSN Pin \t = Custom GPIO%d%s\n", csn_pin, + csn_pin==RPI_V2_GPIO_P1_26 ? " (CE1) Software Driven" : "" ); + } + + printf("CE Pin \t = Custom GPIO%d\n", ce_pin ); + + // SPI Bus Speed + printf("Clock Speed\t = " ); + switch (spi_speed) + { + case BCM2835_SPI_SPEED_64MHZ : printf("64 Mhz"); break ; + case BCM2835_SPI_SPEED_32MHZ : printf("32 Mhz"); break ; + case BCM2835_SPI_SPEED_16MHZ : printf("16 Mhz"); break ; + case BCM2835_SPI_SPEED_8MHZ : printf("8 Mhz"); break ; + case BCM2835_SPI_SPEED_4MHZ : printf("4 Mhz"); break ; + case BCM2835_SPI_SPEED_2MHZ : printf("2 Mhz"); break ; + case BCM2835_SPI_SPEED_1MHZ : printf("1 Mhz"); break ; + case BCM2835_SPI_SPEED_512KHZ: printf("512 KHz"); break ; + case BCM2835_SPI_SPEED_256KHZ: printf("256 KHz"); break ; + case BCM2835_SPI_SPEED_128KHZ: printf("128 KHz"); break ; + case BCM2835_SPI_SPEED_64KHZ : printf("64 KHz"); break ; + case BCM2835_SPI_SPEED_32KHZ : printf("32 KHz"); break ; + case BCM2835_SPI_SPEED_16KHZ : printf("16 KHz"); break ; + case BCM2835_SPI_SPEED_8KHZ : printf("8 KHz"); break ; + default : printf("Probably Bad !!!"); break ; + } + printf("\n"); + + printf("================ NRF Configuration ================\n" ); + + print_status(get_status()); + + print_address_register("RX_ADDR_P0-1",RX_ADDR_P0,2); + print_byte_register("RX_ADDR_P2-5",RX_ADDR_P2,4); + print_address_register("TX_ADDR",TX_ADDR); + + print_byte_register("RX_PW_P0-6",RX_PW_P0,6); + print_byte_register("EN_AA",EN_AA); + print_byte_register("EN_RXADDR",EN_RXADDR); + print_byte_register("RF_CH",RF_CH); + print_byte_register("RF_SETUP",RF_SETUP); + print_byte_register("CONFIG",CONFIG); + print_byte_register("DYNPD/FEATURE",DYNPD,2); + + printf("Data Rate\t = %s\r\n",rf24_datarate_e_str_P[getDataRate()]); + printf("Model\t\t = %s\r\n",rf24_model_e_str_P[isPVariant()]); + printf("CRC Length\t = %s\r\n",rf24_crclength_e_str_P[getCRCLength()]); + printf("PA Power\t = %s\r\n",rf24_pa_dbm_e_str_P[getPALevel()]); +} + +/****************************************************************************/ + +bool RF24::begin(void) +{ + debug = false; + //debug = true; + + // Init BCM2835 chipset for talking with us + if (!bcm2835_init()){ + return false; + } + // Initialise the CE pin of NRF24 (chip enable) + bcm2835_gpio_fsel(ce_pin, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_write(ce_pin, LOW); + + // used to drive custom I/O to trigger my logic analyser + // bcm2835_gpio_fsel(GPIO_CTRL_PIN , BCM2835_GPIO_FSEL_OUTP); + + // start the SPI library: + // Note the NRF24 wants mode 0, MSB first and default to 1 Mbps + bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); + bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); + + // Set SPI bus Speed + bcm2835_spi_setClockDivider(spi_speed); + + if(csn_pin <= BCM2835_SPI_CS_NONE){ + bcm2835_spi_chipSelect(csn_pin); + } + // This initialize the SPI bus with + // csn pin as chip select (custom or not) + bcm2835_spi_begin(csn_pin); + + // wait 100ms + delay(100); + + // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier + // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet + // sizes must never be used. See documentation for a more complete explanation. + //printf("write_register(%02X, %02X)\n", SETUP_RETR, (0b0100 << ARD) | (0b1111 << ARC)); + setRetries(5,15); + + // Determine if this is a p or non-p RF24 module and then + // reset our data rate back to default value. This works + // because a non-P variant won't allow the data rate to + // be set to 250Kbps. + if( setDataRate( RF24_250KBPS ) ) + { + p_variant = true ; + } + + // Then set the data rate to the slowest (and most reliable) speed supported by all + // hardware. + setDataRate( RF24_1MBPS ) ; + + // Initialize CRC and request 2-byte (16bit) CRC + setCRCLength( RF24_CRC_16 ) ; + + // Reset current status + // Notice reset and flush is the last thing we do + write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Set up default configuration. Callers can always change it later. + // This channel should be universally safe and not bleed over into adjacent + // spectrum. + setChannel(76); + + // Flush buffers + flush_rx(); + flush_tx(); + + powerUp(); + + // Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp ) + // PTX should use only 22uA of power + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + + return true; +} + +/****************************************************************************/ + +void RF24::startListening(void) +{ + powerUp(); + write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX)); + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Restore the pipe0 adddress, if exists + if (pipe0_reading_address) + write_register(RX_ADDR_P0, reinterpret_cast(&pipe0_reading_address), 5); + + // Flush buffers + flush_rx(); + flush_tx(); + + // Go! + bcm2835_gpio_write(ce_pin, HIGH); + + // wait for the radio to come up (130us actually only needed) + //delayMicroseconds(130); +} + +/****************************************************************************/ + +void RF24::stopListening(void) +{ + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); + flush_rx(); + write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) ); + +} + +/****************************************************************************/ + +void RF24::powerDown(void) +{ + bcm2835_gpio_write(ce_pin, LOW); + write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP)); +} + +/****************************************************************************/ + +void RF24::powerUp(void) +{ + bool up = read_register(CONFIG) & _BV(PWR_UP); + + if(! up ){ + write_register(CONFIG, ( read_register(CONFIG) | _BV(PWR_UP) )); + delay(5); + } + + +} + +/******************************************************************/ + +bool RF24::write( const void* buf, uint8_t len, const bool multicast ){ + + // Begin the write + startWrite(buf,len, multicast); + + + //Wait until complete or failed + // If this hangs, it ain't coming back, no sense in timing out + while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + + + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); + + //Max retries exceeded + if( status & _BV(MAX_RT)){ + flush_tx(); //Only going to be 1 packet int the FIFO at a time using this method, so just flush + return 0; + } + //TX OK 1 or 0 + return 1; +} + +bool RF24::write( const void* buf, uint8_t len ){ + return write(buf,len,0); +} + +/****************************************************************************/ + +//For general use, the interrupt flags are not important to clear +bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //This way the FIFO will fill up and allow blocking until packets go through + //The radio will auto-clear everything in the FIFO as long as CE remains high + + uint32_t timer = millis(); //Get the time that the payload transmission started + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or timeout + + if( get_status() & _BV(MAX_RT)){ //If MAX Retries have been reached + reUseTX(); //Set re-transmit and clear the MAX_RT interrupt flag + if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0 + } + + } + + //Start Writing + startFastWrite(buf,len,0); //Write the payload if a buffer is clear + + return 1; //Return 1 to indicate successful transmission +} + +/****************************************************************************/ + +void RF24::reUseTX(){ + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + //spiTrans( REUSE_TX_PL ); + bcm2835_spi_transfer( REUSE_TX_PL); + bcm2835_gpio_write(ce_pin, LOW); //Re-Transfer packet + bcm2835_gpio_write(ce_pin, HIGH); +} + +/****************************************************************************/ + +bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast ) +{ + //Block until the FIFO is NOT full. + //Keep track of the MAX retries and set auto-retry if seeing failures + //Return 0 so the user can control the retrys and set a timer or failure counter if required + //The radio will auto-clear everything in the FIFO as long as CE remains high + + while( ( get_status() & ( _BV(TX_FULL) ))) { //Blocking only if FIFO is full. This will loop and block until TX is successful or fail + + if( get_status() & _BV(MAX_RT)){ + //reUseTX(); //Set re-transmit + write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag + return 0; //Return 0. The previous payload has been retransmitted + //From the user perspective, if you get a 0, just keep trying to send the same payload + } + + } + //Start Writing + startFastWrite(buf,len,multicast); + + return 1; +} + +bool RF24::writeFast( const void* buf, uint8_t len ){ + return writeFast(buf,len,0); +} + +/****************************************************************************/ + +//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE high +//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay) +//Otherwise we enter Standby-II mode, which is still faster than standby mode +//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each time if sending a stream of data + +void RF24::startFastWrite( const void* buf, uint8_t len, const bool multicast){ //TMRh20 + + //write_payload( buf,len); + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + bcm2835_gpio_write(ce_pin, HIGH); + +} + +/****************************************************************************/ + +void RF24::startWrite( const void* buf, uint8_t len, const bool multicast ) +{ + + // Send the payload + write_payload( buf, len,multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD ) ; + + bcm2835_gpio_write(ce_pin, HIGH); + //bcm2835_gpio_write(ce_pin, LOW); + +} + +/****************************************************************************/ + +bool RF24::txStandBy(){ + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); //Non blocking, flush the data + return 0; + } + } + + bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode + return 1; +} + +/****************************************************************************/ + +bool RF24::txStandBy(uint32_t timeout){ + + uint32_t start = millis(); + + while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){ + if( get_status() & _BV(MAX_RT)){ + write_register(STATUS,_BV(MAX_RT) ); + bcm2835_gpio_write(ce_pin, LOW); //Set re-transmit + bcm2835_gpio_write(ce_pin, HIGH); + if(millis() - start >= timeout){ + bcm2835_gpio_write(ce_pin, LOW); + flush_tx(); + return 0; + } + } + } + bcm2835_gpio_write(ce_pin, LOW); //Set STANDBY-I mode + return 1; + +} +/****************************************************************************/ + +void RF24::maskIRQ(bool tx, bool fail, bool rx){ + + write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR ); +} + +/****************************************************************************/ + +uint8_t RF24::getDynamicPayloadSize(void) +{ + spi_txbuff[0] = R_RX_PL_WID; + spi_rxbuff[1] = 0xff; + + bcm2835_spi_transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, 2); + + if(spi_rxbuff[1] > 32) { flush_rx(); return 0; } + + return spi_rxbuff[1]; +} + +/****************************************************************************/ + +bool RF24::available(void) +{ + return available(NULL); +} + +/****************************************************************************/ + +bool RF24::available(uint8_t* pipe_num) +{ + //Check the FIFO buffer to see if data is waitng to be read + + if (!( read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){ + + + // If the caller wants the pipe number, include that + if ( pipe_num ){ + uint8_t status = get_status(); + *pipe_num = ( status >> RX_P_NO ) & 0b111; + } + return 1; + } + + return 0; +} + +/****************************************************************************/ + +void RF24::read( void* buf, uint8_t len ) +{ + // Fetch the payload + read_payload( buf, len ); + + //Clear the two possible interrupt flags with one command + write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) ); +} + +/****************************************************************************/ + +void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready) +{ + // Read the status & reset the status in one easy call + // Or is that such a good idea? + uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + + // Report to the user what happened + tx_ok = status & _BV(TX_DS); + tx_fail = status & _BV(MAX_RT); + rx_ready = status & _BV(RX_DR); +} + +/****************************************************************************/ + +void RF24::openWritingPipe(uint64_t value) +{ + // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) + // expects it LSB first too, so we're good. + + write_register(RX_ADDR_P0, reinterpret_cast(&value), 5); + write_register(TX_ADDR, reinterpret_cast(&value), 5); + + + write_register(RX_PW_P0,payload_size); +} + +/****************************************************************************/ + +static const uint8_t child_pipe[] = +{ + RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5 +}; +static const uint8_t child_payload_size[] = +{ + RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5 +}; +static const uint8_t child_pipe_enable[] = +{ + ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5 +}; + +void RF24::openReadingPipe(uint8_t child, uint64_t address) +{ + // If this is pipe 0, cache the address. This is needed because + // openWritingPipe() will overwrite the pipe 0 address, so + // startListening() will have to restore it. + if (child == 0) + pipe0_reading_address = address; + + if (child <= 6) + { + // For pipes 2-5, only write the LSB + if ( child < 2 ) + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 5); + else + write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast(&address), 1); + + write_register(pgm_read_byte(&child_payload_size[child]),payload_size); + + // Note it would be more efficient to set all of the bits for all open + // pipes at once. However, I thought it would make the calling code + // more simple to do it this way. + write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child]))); + } +} + +/****************************************************************************/ + +void RF24::toggle_features(void) +{ + bcm2835_spi_transfer( ACTIVATE ); + bcm2835_spi_transfer( 0x73 ); +} + +/****************************************************************************/ + +void RF24::enableDynamicPayloads(void) +{ + + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DPL) ); + + if (debug) + printf("FEATURE=%i\r\n",read_register(FEATURE)); + + // Enable dynamic payload on all pipes + // + // Not sure the use case of only having dynamic payload on certain + // pipes, so the library does not support it. + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); + + dynamic_payloads_enabled = true; +} + +/****************************************************************************/ + +void RF24::enableAckPayload(void) +{ + // + // enable ack payload and dynamic payload features + // + + + // So enable them and try again + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + + if (debug) + printf("FEATURE=%i\r\n",read_register(FEATURE)); + + // + // Enable dynamic payload on pipes 0 & 1 + // + + write_register(DYNPD,read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/****************************************************************************/ + +void RF24::enableDynamicAck(void){ + // + // enable dynamic ack features + // + toggle_features(); + write_register(FEATURE,read_register(FEATURE) | _BV(EN_DYN_ACK) ); + + if(debug){printf("FEATURE=%i\r\n",read_register(FEATURE));} + + +} + +/****************************************************************************/ + +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +{ + + uint8_t * ptx = spi_txbuff; + uint8_t size ; + + const uint8_t* current = reinterpret_cast(buf); + + uint8_t data_len = min(len,payload_size); + + size = data_len + 1 ; // Add register value to transmit buffer + + if (debug){ + printf("[Writing %u bytes]",data_len); + } + *ptx++ = W_ACK_PAYLOAD | ( pipe & 0b111 ); + while ( data_len-- ){ + *ptx++ = *current++; + } + bcm2835_spi_transfern( (char *) spi_txbuff, size); + +} + + +/****************************************************************************/ + +bool RF24::isAckPayloadAvailable(void) +{ + return ! read_register(FIFO_STATUS) & _BV(RX_EMPTY); +} + +/****************************************************************************/ + +bool RF24::isPVariant(void) +{ + return p_variant ; +} + +/****************************************************************************/ + +void RF24::setAutoAck(bool enable) +{ + if ( enable ) + write_register(EN_AA, 0b111111); + else + write_register(EN_AA, 0); +} + +/****************************************************************************/ + +void RF24::setAutoAck( uint8_t pipe, bool enable ) +{ + if ( pipe <= 6 ) + { + uint8_t en_aa = read_register( EN_AA ) ; + if( enable ) + { + en_aa |= _BV(pipe) ; + } + else + { + en_aa &= ~_BV(pipe) ; + } + write_register( EN_AA, en_aa ) ; + } +} + +/****************************************************************************/ + +bool RF24::testCarrier(void) +{ + return ( read_register(CD) & 1 ); +} + +/****************************************************************************/ + +bool RF24::testRPD(void) +{ + return ( read_register(RPD) & 1 ) ; +} + +/****************************************************************************/ + +void RF24::setPALevel(uint8_t level) +{ + uint8_t setup = read_register(RF_SETUP) & 0b11111000; + + if(level > 3){ // If invalid level, go to max PA + level = (RF24_PA_MAX << 1) + 1; // +1 to support the SI24R1 chip extra bit + }else{ + level = (level << 1) + 1; // Else set level as requested + } + + + write_register( RF_SETUP, setup |= level ) ; // Write it to the chip +} + +/****************************************************************************/ + +uint8_t RF24::getPALevel(void) +{ + return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1 ; +} + +/****************************************************************************/ + +bool RF24::setDataRate(rf24_datarate_e speed) +{ + bool result = false; + uint8_t setup = read_register(RF_SETUP) ; + + // HIGH and LOW '00' is 1Mbs - our default + wide_band = false ; + setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)) ; + if( speed == RF24_250KBPS ) + { + // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 + // Making it '10'. + wide_band = false ; + setup |= _BV( RF_DR_LOW ) ; + } + else + { + // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 + // Making it '01' + if ( speed == RF24_2MBPS ) + { + wide_band = true ; + setup |= _BV(RF_DR_HIGH); + } + else + { + // 1Mbs + wide_band = false ; + } + } + write_register(RF_SETUP,setup); + + // Verify our result + if ( read_register(RF_SETUP) == setup ) + { + result = true; + } + else + { + wide_band = false; + } + + return result; +} + +/****************************************************************************/ + +rf24_datarate_e RF24::getDataRate( void ) +{ + rf24_datarate_e result ; + uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + + // switch uses RAM (evil!) + // Order matters in our case below + if ( dr == _BV(RF_DR_LOW) ) + { + // '10' = 250KBPS + result = RF24_250KBPS ; + } + else if ( dr == _BV(RF_DR_HIGH) ) + { + // '01' = 2MBPS + result = RF24_2MBPS ; + } + else + { + // '00' = 1MBPS + result = RF24_1MBPS ; + } + return result ; +} + +/****************************************************************************/ + +void RF24::setCRCLength(rf24_crclength_e length) +{ + uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ; + + // switch uses RAM (evil!) + if ( length == RF24_CRC_DISABLED ) + { + // Do nothing, we turned it off above. + } + else if ( length == RF24_CRC_8 ) + { + config |= _BV(EN_CRC); + } + else + { + config |= _BV(EN_CRC); + config |= _BV( CRCO ); + } + write_register( CONFIG, config ) ; +} + +/****************************************************************************/ + +rf24_crclength_e RF24::getCRCLength(void) +{ + rf24_crclength_e result = RF24_CRC_DISABLED; + uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ; + + if ( config & _BV(EN_CRC ) ) + { + if ( config & _BV(CRCO) ) + result = RF24_CRC_16; + else + result = RF24_CRC_8; + } + + return result; +} + +/****************************************************************************/ + +void RF24::disableCRC( void ) +{ + uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ; + write_register( CONFIG, disable ) ; +} + +/****************************************************************************/ +void RF24::setRetries(uint8_t delay, uint8_t count) +{ + write_register(SETUP_RETR,(delay&0xf)< + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Modified to use the great bcm2835 library for I/O and SPI + + */ + +/** + * @file RF24.h + * + * Class declaration for RF24 and helper enums + */ + +#ifndef __RF24_H__ +#define __RF24_H__ + +#include "RF24_config.h" +#include "./bcm2835.h" + + +/** + * Power Amplifier level. + * + * For use with setPALevel() + */ +typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; + +/** + * Data rate. How fast data moves through the air. + * + * For use with setDataRate() + */ +typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +/** + * CRC Length. How big (if any) of a CRC is included. + * + * For use with setCRCLength() + */ +typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e; + +/** + * Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + */ + +class RF24 +{ +private: + uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */ + uint8_t csn_pin; /**< SPI Chip select */ + uint16_t spi_speed; /**< SPI Bus Speed */ + bool wide_band; /* 2Mbs data rate in use? */ + bool p_variant; /* False for RF24L01 and true for RF24L01P */ + uint8_t payload_size; /**< Fixed size of payloads */ + //bool ack_payload_available; /**< Whether there is an ack payload waiting */ + bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ + //uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */ + uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */ + //uint32_t spispeed; + uint8_t debug ; /* Debug flag */ + uint8_t spi_rxbuff[32+1] ; //SPI receive buffer (payload max 32 bytes) + uint8_t spi_txbuff[32+1] ; //SPI transmit buffer (payload max 32 bytes + 1 byte for the command) + +protected: + /** + * @name Low-level internal interface. + * + * Protected methods that address the chip directly. Regular users cannot + * ever call these. They are documented for completeness and for developers who + * may want to extend this class. + */ + /**@{*/ + + + /** + * Read a chunk of data in from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to put the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t read_register(uint8_t reg, uint8_t* buf, uint8_t len); + + /** + * Read single byte from a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @return Current value of register @p reg + */ + uint8_t read_register(uint8_t reg); + + /** + * Write a chunk of data to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param buf Where to get the data + * @param len How many bytes of data to transfer + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, const uint8_t* buf, uint8_t len); + + /** + * Write a single byte to a register + * + * @param reg Which register. Use constants from nRF24L01.h + * @param value The new value to write + * @return Current value of status register + */ + uint8_t write_register(uint8_t reg, uint8_t value); + + /** + * Write the transmit payload + * + * The size of data written is the fixed payload size, see getPayloadSize() + * + * @param buf Where to get the data + * @param len Number of bytes to be sent + * @return Current value of status register + */ + uint8_t write_payload(const void* buf, uint8_t len, const uint8_t writeType); + + /** + * Read the receive payload + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @param buf Where to put the data + * @param len Maximum number of bytes to read + * @return Current value of status register + */ + uint8_t read_payload(void* buf, uint8_t len); + + /** + * Empty the receive buffer + * + * @return Current value of status register + */ + uint8_t flush_rx(void); + + /** + * Empty the transmit buffer + * + * @return Current value of status register + */ + uint8_t flush_tx(void); + + /** + * Retrieve the current status of the chip + * + * @return Current value of status register + */ + uint8_t get_status(void); + + /** + * Decode and print the given status to stdout + * + * @param status Status value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_status(uint8_t status); + + /** + * Decode and print the given 'observe_tx' value to stdout + * + * @param value The observe_tx value to print + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void print_observe_tx(uint8_t value); + + /** + * Print the name and value of an 8-bit register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Print the name and value of a 40-bit address register to stdout + * + * Optionally it can print some quantity of successive + * registers on the same line. This is useful for printing a group + * of related registers on one line. + * + * @param name Name of the register + * @param reg Which register. Use constants from nRF24L01.h + * @param qty How many successive registers to print + */ + void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1); + + /** + * Turn on or off the special features of the chip + * + * The chip has certain 'features' which are only available when the 'features' + * are enabled. See the datasheet for details. + */ + void toggle_features(void); + /**@}*/ + +public: + /** + * @name Primary public interface + * + * These are the main methods you need to operate the chip + */ + /**@{*/ + + /** + * Constructor + * + * Creates a new instance of this driver. Before using, you create an instance + * and send in the unique pins that this chip is connected to. + * + * @param _cepin The pin attached to Chip Enable on the RF module + * @param _cspin The pin attached to Chip Select + */ + RF24(uint8_t _cepin, uint8_t _cspin); + RF24(uint8_t _cepin, uint8_t _cspin, uint32_t spispeed ); + + /** + * Begin operation of the chip + * + * Call this in setup(), before calling any other methods. + */ + bool begin(void); + + /** + * Start listening on the pipes opened for reading. + * + * Be sure to call openReadingPipe() first. Do not call write() while + * in this mode, without first calling stopListening(). Call + * isAvailable() to check for incoming traffic, and read() to get it. + */ + void startListening(void); + + /** + * Stop listening for incoming messages + * + * Do this before calling write(). + */ + void stopListening(void); + + /** + * Write to the open writing pipe + * + * Be sure to call openWritingPipe() first to set the destination + * of where to write to. + * + * This blocks until the message is successfully acknowledged by + * the receiver or the timeout/retransmit maxima are reached. In + * the current configuration, the max delay here is 60ms. + * + * The maximum size of data written is the fixed payload size, see + * getPayloadSize(). However, you can write less, and the remainder + * will just be filled with zeroes. + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool write( const void* buf, uint8_t len ); + + /** + * Test whether there are bytes available to be read + * + * @return True if there is a payload available, false if none is + */ + bool available(void); + + /** + * Read the payload + * + * Return the last payload received + * + * The size of data read is the fixed payload size, see getPayloadSize() + * + * @note I specifically chose 'void*' as a data type to make it easier + * for beginners to use. No casting needed. + * + * @param buf Pointer to a buffer where the data should be written + * @param len Maximum number of bytes to read into the buffer + * @return True if the payload was delivered successfully false if not + */ + void read( void* buf, uint8_t len ); + + /** + * Open a pipe for writing + * + * Only one pipe can be open at once, but you can change the pipe + * you'll listen to. Do not call this while actively listening. + * Remember to stopListening() first. + * + * Addresses are 40-bit hex values, e.g.: + * + * @code + * openWritingPipe(0xF0F0F0F0F0); + * @endcode + * + * @param address The 40-bit address of the pipe to open. This can be + * any value whatsoever, as long as you are the only one writing to it + * and only one other radio is listening to it. Coordinate these pipe + * addresses amongst nodes on the network. + */ + void openWritingPipe(uint64_t address); + + /** + * Open a pipe for reading + * + * Up to 6 pipes can be open for reading at once. Open all the + * reading pipes, and then call startListening(). + * + * @see openWritingPipe + * + * @warning Pipes 1-5 should share the first 32 bits. + * Only the least significant byte should be unique, e.g. + * @code + * openReadingPipe(1,0xF0F0F0F0AA); + * openReadingPipe(2,0xF0F0F0F066); + * @endcode + * + * @warning Pipe 0 is also used by the writing pipe. So if you open + * pipe 0 for reading, and then startListening(), it will overwrite the + * writing pipe. Ergo, do an openWritingPipe() again before write(). + * + * @todo Enforce the restriction that pipes 1-5 must share the top 32 bits + * + * @param number Which pipe# to open, 0-5. + * @param address The 40-bit address of the pipe to open. + */ + void openReadingPipe(uint8_t number, uint64_t address); + + /**@}*/ + /** + * @name Optional Configurators + * + * Methods you can use to get or set the configuration of the chip. + * None are required. Calling begin() sets up a reasonable set of + * defaults. + */ + /**@{*/ + /** + * Set the number and delay of retries upon failed submit + * + * @param delay How long to wait between each retry, in multiples of 250us, + * max is 15. 0 means 250us, 15 means 4000us. + * @param count How many retries before giving up, max 15 + */ + void setRetries(uint8_t delay, uint8_t count); + + /** + * Set RF communication channel + * + * @param channel Which RF channel to communicate on, 0-127 + */ + void setChannel(uint8_t channel); + + /** + * Set Static Payload Size + * + * This implementation uses a pre-stablished fixed payload size for all + * transmissions. If this method is never called, the driver will always + * transmit the maximum payload size (32 bytes), no matter how much + * was sent to write(). + * + * @todo Implement variable-sized payloads feature + * + * @param size The number of bytes in the payload + */ + void setPayloadSize(uint8_t size); + + /** + * Get Static Payload Size + * + * @see setPayloadSize() + * + * @return The number of bytes in the payload + */ + uint8_t getPayloadSize(void); + + /** + * Get Dynamic Payload Size + * + * For dynamic payloads, this pulls the size of the payload off + * the chip + * + * @return Payload length of last-received dynamic payload + */ + uint8_t getDynamicPayloadSize(void); + + /** + * Enable custom payloads on the acknowledge packets + * + * Ack payloads are a handy way to return data back to senders without + * manually changing the radio modes on both units. + * + * @see examples/pingpair_pl/pingpair_pl.pde + */ + void enableAckPayload(void); + + /** + * Enable dynamically-sized payloads + * + * This way you don't always have to send large packets just to send them + * once in a while. This enables dynamic payloads on ALL pipes. + * + * @see examples/pingpair_pl/pingpair_dyn.pde + */ + void enableDynamicPayloads(void); + + /** + * Determine whether the hardware is an nRF24L01+ or not. + * + * @return true if the hardware is nRF24L01+ (or compatible) and false + * if its not. + */ + bool isPVariant(void) ; + + /** + * Enable or disable auto-acknowlede packets + * + * This is enabled by default, so it's only needed if you want to turn + * it off for some reason. + * + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck(bool enable); + + /** + * Enable or disable auto-acknowlede packets on a per pipeline basis. + * + * AA is enabled by default, so it's only needed if you want to turn + * it off/on for some reason on a per pipeline basis. + * + * @param pipe Which pipeline to modify + * @param enable Whether to enable (true) or disable (false) auto-acks + */ + void setAutoAck( uint8_t pipe, bool enable ) ; + + /** + * Set Power Amplifier (PA) level to one of four levels. + * Relative mnemonics have been used to allow for future PA level + * changes. According to 6.5 of the nRF24L01+ specification sheet, + * they translate to: RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, + * RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm. + * + * @param level Desired PA level. + */ + void setPALevel( uint8_t level ) ; + + /** + * Fetches the current PA level. + * + * @return Returns a value from the rf24_pa_dbm_e enum describing + * the current PA setting. Please remember, all values represented + * by the enum mnemonics are negative dBm. See setPALevel for + * return value descriptions. + */ + uint8_t getPALevel( void ) ; + + /** + * Set the transmission data rate + * + * @warning setting RF24_250KBPS will fail for non-plus units + * + * @param speed RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps + * @return true if the change was successful + */ + bool setDataRate(rf24_datarate_e speed); + + /** + * Fetches the transmission data rate + * + * @return Returns the hardware's currently configured datarate. The value + * is one of 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS, as defined in the + * rf24_datarate_e enum. + */ + rf24_datarate_e getDataRate( void ) ; + + /** + * Set the CRC length + * + * @param length RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + void setCRCLength(rf24_crclength_e length); + + /** + * Get the CRC length + * + * @return RF24_DISABLED if disabled or RF24_CRC_8 for 8-bit or RF24_CRC_16 for 16-bit + */ + rf24_crclength_e getCRCLength(void); + + /** + * Disable CRC validation + * + */ + void disableCRC( void ) ; + + /**@}*/ + /** + * @name Advanced Operation + * + * Methods you can use to drive the chip in more advanced ways + */ + /**@{*/ + + /** + * Print a giant block of debugging information to stdout + * + * @warning Does nothing if stdout is not defined. See fdevopen in stdio.h + */ + void printDetails(void); + + /** + * Enter low-power mode + * + * To return to normal power mode, either write() some data or + * startListening, or powerUp(). + */ + void powerDown(void); + + /** + * Leave low-power mode - making radio more responsive + * + * To return to low power mode, call powerDown(). + */ + void powerUp(void) ; + +/** + * Write for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * + * Can be used with enableAckPayload() to request a response + * @see enableDynamicAck() + * @see setAutoAck() + * @see write() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0), NOACK (1) + */ + bool write( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command * + * This will not block until the 3 FIFO buffers are filled with data. + * Once the FIFOs are full, writeFast will simply wait for success or + * timeout, and return 1 or 0 respectively. From a user perspective, just + * keep trying to send the same data. The library will keep auto retrying + * the current payload using the built in functionality. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeFast is called + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(); // Returns 0 if failed. 1 if success. Blocks only until MAX_RT timeout or success. Data flushed on fail. + * + * radio.writeFast(&buf,32); // Writes 1 payload to the buffers + * txStandBy(1000); // Using extended timeouts, returns 1 if success. Retries failed payloads for 1 seconds before returning 0. + * @endcode + * + * @see txStandBy() + * @see write() + * @see writeBlocking() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @return True if the payload was delivered successfully false if not + */ + bool writeFast( const void* buf, uint8_t len ); + + /** + * WriteFast for single NOACK writes. Disables acknowledgements/autoretries for a single write. + * + * @note enableDynamicAck() must be called to enable this feature + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + */ + bool writeFast( const void* buf, uint8_t len, const bool multicast ); + + /** + * @note Optimization: New Command + * This function extends the auto-retry mechanism to any specified duration. + * It will not block until the 3 FIFO buffers are filled with data. + * If so the library will auto retry until a new payload is written + * or the user specified timeout period is reached. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * ONLY max retry interrupt flags will be cleared when writeBlocking is called + * @code + * Example (Full blocking): + * + * radio.writeBlocking(&buf,32,1000); //Wait up to 1 second to write 1 payload to the buffers + * txStandBy(1000); //Wait up to 1 second for the payload to send. Return 1 if ok, 0 if failed. + * //Blocks only until user timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @see txStandBy() + * @see write() + * @see writeFast() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param timeout User defined timeout in milliseconds. + * @return True if the payload was loaded into the buffer successfully false if not + */ + bool writeBlocking( const void* buf, uint8_t len, uint32_t timeout ); + + /** + * @note Optimization: New Command + * This function should be called as soon as transmission is finished to + * drop the radio back to STANDBY-I mode. If not issued, the radio will + * remain in STANDBY-II mode which, per the data sheet, is not a recommended + * operating mode. + * + * @note When transmitting data in rapid succession, it is still recommended by + * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is + * time enough between sends for the FIFOs to empty. + * + * Relies on built-in auto retry functionality. + * + * @code + * Example (Partial blocking): + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(); //Returns 0 if failed. 1 if success. + * //Blocks only until MAX_RT timeout or success. Data flushed on fail. + * @endcode + * @see txStandBy(unsigned long timeout) + * @return True if transmission is successful + * + */ + bool txStandBy(); + + /** + * @note Optimization: New Command + * + * This function allows extended blocking and auto-retries per a user defined timeout + * @code + * Fully Blocking Example: + * + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); + * radio.writeFast(&buf,32); //Fills the FIFO buffers up + * bool ok = txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success. + * //Blocks only until user defined timeout or success. Data flushed on fail. + * @endcode + * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); called to enable millis(). + * @param timeout Number of milliseconds to retry failed payloads + * @return True if transmission is successful + * + */ + bool txStandBy(uint32_t timeout); + + /** + * Test whether there are bytes available to be read + * + * Use this version to discover on which pipe the message + * arrived. + * + * @param[out] pipe_num Which pipe has the payload available + * @return True if there is a payload available, false if none is + */ + bool available(uint8_t* pipe_num); + +/** + * Non-blocking write to the open writing pipe used for buffered writes + * + * @note Optimization: This function now leaves the CE pin high, so the radio + * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. + * This allows the chip to be used to its full potential in TX mode. + * @warning It is important to never keep the nRF24L01 in TX mode for more than 4ms at a time. If the auto + * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the FIFO + * to clear by issuing txStandBy() or ensure appropriate time between transmissions. + * + * @see write() + * @see writeFast() + * @see startWrite() + * @see writeBlocking() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * @return True if the payload was delivered successfully false if not + */ + void startFastWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Non-blocking write to the open writing pipe + * + * Just like write(), but it returns immediately. To find out what happened + * to the send, catch the IRQ and then call whatHappened(). + * + * @note Optimization: This function again behaves as it did previously for backwards-compatibility. + * with user code. The library uses startFastWrite() internally. + * This is mainly used for single-payload transactions. + * + * @see write() + * @see writeFast() + * @see startFastWrite() + * @see whatHappened() + * + * For single noAck writes see: + * @see enableDynamicAck() + * @see setAutoAck() + * + * @param buf Pointer to the data to be sent + * @param len Number of bytes to be sent + * @param multicast Request ACK (0) or NOACK (1) + * + */ + void startWrite( const void* buf, uint8_t len, const bool multicast ); + + /** + * Optimization: New Command + * + * This function is mainly used internally to take advantage of the auto payload + * re-use functionality of the chip, but can be beneficial to users as well. + * + * The function will instruct the radio to re-use the data in the FIFO buffers, + * and instructs the radio to re-send once the timeout limit has been reached. + * Used by writeFast and writeBlocking to initiate retries when a TX failure + * occurs. Retries are automatically initiated except with the standard write(). + * This way, data is not flushed from the buffer until switching between modes. + * + * @note This is to be used AFTER auto-retry fails if wanting to resend + * using the built-in payload reuse features. + * After issuing reUseTX(), it will keep reending the same payload forever or until + * a payload is written to the FIFO, or a flush_tx command is given. + */ + void reUseTX(); + + /** + * Write an ack payload for the specified pipe + * + * The next time a message is received on @p pipe, the data in @p buf will + * be sent back in the acknowledgement. + * + * @warning According to the data sheet, only three of these can be pending + * at any time. I have not tested this. + * + * @param pipe Which pipe# (typically 1-5) will get this response. + * @param buf Pointer to data that is sent + * @param len Length of the data to send, up to 32 bytes max. Not affected + * by the static payload set by setPayloadSize(). + */ + void writeAckPayload(uint8_t pipe, const void* buf, uint8_t len); + + /** + * Enable dynamic ACKs (single write multicasting) for chosen messages + * + * @note To enable full multicasting or per-pipe multicast, use setAutoAck() + * + * @warning This MUST be called prior to attempting single write NOACK calls + * @code + * radio.enableDynamicAck(); + * radio.write(&data,32,1); // Sends a payload with no acknowledgement requested + * radio.write(&data,32,0); // Sends a payload using auto-retry/autoACK + * @endcode + */ + void enableDynamicAck(); + + /** + * Determine if an ack payload was received in the most recent call to + * write(). + * + * Call read() to retrieve the ack payload. + * + * @warning Calling this function clears the internal flag which indicates + * a payload is available. If it returns true, you must read the packet + * out as the very next interaction with the radio, or the results are + * undefined. + * + * @return True if an ack payload is available. + */ + bool isAckPayloadAvailable(void); + + /** + * Call this when you get an interrupt to find out why + * + * Tells you what caused the interrupt, and clears the state of + * interrupts. + * + * @param[out] tx_ok The send was successful (TX_DS) + * @param[out] tx_fail The send failed, too many retries (MAX_RT) + * @param[out] rx_ready There is a message waiting to be read (RX_DS) + */ + void whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready); + + /** + * Test whether there was a carrier on the line for the + * previous listening period. + * + * Useful to check for interference on the current channel. + * + * @return true if was carrier, false if not + */ + bool testCarrier(void); + + /** + * Test whether a signal (carrier or otherwise) greater than + * or equal to -64dBm is present on the channel. Valid only + * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier(). + * + * Useful to check for interference on the current channel and + * channel hopping strategies. + * + * @return true if signal => -64dBm, false if not + */ + bool testRPD(void) ; + + /** + * Test whether this is a real radio, or a mock shim for + * debugging. Setting either pin to 0xff is the way to + * indicate that this is not a real radio. + * + * @return true if this is a legitimate radio + */ + bool isValid() { return ce_pin != 0xff && csn_pin != 0xff; } + + /** + * The radio will generate interrupt signals when a transmission is complete, + * a transmission fails, or a payload is received. This allows users to mask + * those interrupts to prevent them from generating a signal on the interrupt + * pin. + * + * @code + * Mask all interrupts except the receive interrupt: + * + * radio.maskIRQ(1,1,0); + * @endcode + * + * @param tx_ok Mask transmission complete interrupts + * @param tx_fail Mask transmit failure interrupts + * @param rx_ready Mask payload received interrupts + */ + void maskIRQ(bool tx_ok,bool tx_fail,bool rx_ready); + + /**@}*/ +}; + +/** + * @example GettingStarted.pde + * + * This is an example which corresponds to my "Getting Started" blog post: + * Getting Started with nRF24L01+ on Arduino. + * + * It is an example of how to use the RF24 class. Write this sketch to two + * different nodes. Put one of the nodes into 'transmit' mode by connecting + * with the serial monitor and sending a 'T'. The ping node sends the current + * time to the pong node, which responds by sending the value back. The ping + * node can then see how long the whole cycle took. + */ + +/** + * @example nordic_fob.pde + * + * This is an example of how to use the RF24 class to receive signals from the + * Sparkfun Nordic FOB. See http://www.sparkfun.com/products/8602 . + * Thanks to Kirk Mower for providing test hardware. + */ + +/** + * @example led_remote.pde + * + * This is an example of how to use the RF24 class to control a remote + * bank of LED's using buttons on a remote control. + * + * Every time the buttons change on the remote, the entire state of + * buttons is send to the led board, which displays the state. + */ + +/** + * @example pingpair.pde + * + * This is an example of how to use the RF24 class. Write this sketch to two + * different nodes, connect the role_pin to ground on one. The ping node sends + * the current time to the pong node, which responds by sending the value back. + * The ping node can then see how long the whole cycle took. + */ + +/** + * @example pingpair_maple.pde + * + * This is an example of how to use the RF24 class on the Maple. For a more + * detailed explanation, see my blog post: + * nRF24L01+ Running on Maple + * + * It will communicate well to an Arduino-based unit as well, so it's not for only Maple-to-Maple communication. + * + * Write this sketch to two different nodes, + * connect the role_pin to ground on one. The ping node sends the current time to the pong node, + * which responds by sending the value back. The ping node can then see how long the whole cycle + * took. + */ + +/** + * @example starping.pde + * + * This sketch is a more complex example of using the RF24 library for Arduino. + * Deploy this on up to six nodes. Set one as the 'pong receiver' by tying the + * role_pin low, and the others will be 'ping transmit' units. The ping units + * unit will send out the value of millis() once a second. The pong unit will + * respond back with a copy of the value. Each ping unit can get that response + * back, and determine how long the whole cycle took. + * + * This example requires a bit more complexity to determine which unit is which. + * The pong receiver is identified by having its role_pin tied to ground. + * The ping senders are further differentiated by a byte in eeprom. + */ + +/** + * @example pingpair_pl.pde + * + * This is an example of how to do two-way communication without changing + * transmit/receive modes. Here, a payload is set to the transmitter within + * the Ack packet of each transmission. Note that the payload is set BEFORE + * the sender's message arrives. + */ + +/** + * @example pingpair_irq.pde + * + * This is an example of how to user interrupts to interact with the radio. + * It builds on the pingpair_pl example, and uses ack payloads. + */ + +/** + * @example pingpair_sleepy.pde + * + * This is an example of how to use the RF24 class to create a battery- + * efficient system. It is just like the pingpair.pde example, but the + * ping node powers down the radio and sleeps the MCU after every + * ping/pong cycle. + */ + +/** + * @example scanner.pde + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +/** + * @mainpage Driver for nRF24L01(+) 2.4GHz Wireless Transceiver + * + * @section Goals Design Goals + * + * This library is designed to be... + * @li Maximally compliant with the intended operation of the chip + * @li Easy for beginners to use + * @li Consumed with a public interface that's similiar to other Arduino standard libraries + * + * @section News News + * + * NOW COMPATIBLE WITH ARDUINO 1.0 - The 'master' branch and all examples work with both Arduino 1.0 and earlier versions. + * Please open an issue if you find any problems using it with any version of Arduino. + * + * NOW COMPATIBLE WITH MAPLE - RF24 has been tested with the + * Maple Native, + * and should work with any Maple board. See the pingpair_maple example. + * Note that only the pingpair_maple example has been tested on Maple, although + * the others can certainly be adapted. + * + * @section Useful Useful References + * + * Please refer to: + * + * @li Documentation Main Page + * @li RF24 Class Documentation + * @li Source Code + * @li Downloads Page + * @li Chip Datasheet + * + * This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or + * the SPI hardware will go into 'slave' mode. + * + * @section More More Information + * + * @subpage FAQ + * + * @section Projects Projects + * + * Stuff I have built with RF24 + * + * RF24 Getting Started - Finished Product + * + * Getting Started with nRF24L01+ on Arduino + * + * Nordic FOB and nRF24L01+ + * + * Using the Sparkfun Nordic FOB + * + * RF Duinode V3 (2V4) + * + * Low-Power Wireless Sensor Node + * + * nRF24L01+ connected to Leaf Labs Maple Native + * + * nRF24L01+ Running on Maple + */ + +#endif // __RF24_H__ +// vim:ai:cin:sts=2 sw=2 ft=cpp + diff --git a/RPi/RF24/RF24_config.h b/RPi/RF24/RF24_config.h new file mode 100644 index 0000000..eef0704 --- /dev/null +++ b/RPi/RF24/RF24_config.h @@ -0,0 +1,34 @@ + +/* + Copyright (C) 2011 J. Coliz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Modified to use the great bcm2835 library for I/O and SPI + + */ + +#ifndef __RF24_CONFIG_H__ +#define __RF24_CONFIG_H__ + +#include +#include +#include +#include +#include +#include +#include "bcm2835.h" + +// GCC a Arduino Missing +#define max(a,b) (a>b?a:b) +#define min(a,b) (a +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "bcm2835.h" + +// This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11) +// You can do some safe, non-destructive testing on any platform with: +// gcc bcm2835.c -D BCM2835_TEST +// ./a.out +//#define BCM2835_TEST + +// Uncommenting this define compiles alternative I2C code for the version 1 RPi +// The P1 header I2C pins are connected to SDA0 and SCL0 on V1. +// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. +// #define I2C_V1 + +// Pointers to the hardware register bases +volatile uint32_t *bcm2835_gpio = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_pwm = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_clk = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_pads = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_spi0 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_bsc0 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_bsc1 = (volatile uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_st = (volatile uint32_t *)MAP_FAILED; + + +// This variable allows us to test on hardware other than RPi. +// It prevents access to the kernel memory, and does not do any peripheral access +// Instead it prints out what it _would_ do if debug were 0 +static uint8_t debug = 0; + +// I2C The time needed to transmit one byte. In microseconds. +static int i2c_byte_wait_us = 0; + +// SPI Custom Chip Select Pin +static int spi_custom_cs = 0; + +// Time for millis() +static unsigned long long epoch ; + +// +// Low level register access functions +// + +void bcm2835_set_debug(uint8_t d) +{ + debug = d; +} + +// safe read from peripheral +uint32_t bcm2835_peri_read(volatile uint32_t* paddr) +{ + if (debug) + { + printf("bcm2835_peri_read paddr %08X\n", (unsigned) paddr); + return 0; + } + else + { + // Make sure we dont return the _last_ read which might get lost + // if subsequent code changes to a different peripheral + uint32_t ret = *paddr; + *paddr; // Read without assigneing to an unused variable + return ret; + } +} + +// read from peripheral without the read barrier +uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr) +{ + if (debug) + { + printf("bcm2835_peri_read_nb paddr %08X\n", (unsigned) paddr); + return 0; + } + else + { + return *paddr; + } +} + +// safe write to peripheral +void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value) +{ + if (debug) + { + printf("bcm2835_peri_write paddr %08X, value %08X\n", (unsigned) paddr, value); + } + else + { + // Make sure we don't rely on the first write, which may get + // lost if the previous access was to a different peripheral. + *paddr = value; + *paddr = value; + } +} + +// write to peripheral without the write barrier +void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value) +{ + if (debug) + { + printf("bcm2835_peri_write_nb paddr %08X, value %08X\n", + (unsigned) paddr, value); + } + else + { + *paddr = value; + } +} + +// Set/clear only the bits in value covered by the mask +void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask) +{ + uint32_t v = bcm2835_peri_read(paddr); + v = (v & ~mask) | (value & mask); + bcm2835_peri_write(paddr, v); +} + +// +// Low level convenience functions +// + +// Function select +// pin is a BCM2835 GPIO pin number NOT RPi pin number +// There are 6 control registers, each control the functions of a block +// of 10 pins. +// Each control register has 10 sets of 3 bits per GPIO pin: +// +// 000 = GPIO Pin X is an input +// 001 = GPIO Pin X is an output +// 100 = GPIO Pin X takes alternate function 0 +// 101 = GPIO Pin X takes alternate function 1 +// 110 = GPIO Pin X takes alternate function 2 +// 111 = GPIO Pin X takes alternate function 3 +// 011 = GPIO Pin X takes alternate function 4 +// 010 = GPIO Pin X takes alternate function 5 +// +// So the 3 bits for port X are: +// X / 10 + ((X % 10) * 3) +void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode) +{ + // Function selects are 10 pins per 32 bit word, 3 bits per pin + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10); + uint8_t shift = (pin % 10) * 3; + uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift; + uint32_t value = mode << shift; + bcm2835_peri_set_bits(paddr, value, mask); +} + +// Set output pin +void bcm2835_gpio_set(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, 1 << shift); +} + +// Clear output pin +void bcm2835_gpio_clr(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, 1 << shift); +} + +// Set all output pins in the mask +void bcm2835_gpio_set_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4; + bcm2835_peri_write(paddr, mask); +} + +// Clear all output pins in the mask +void bcm2835_gpio_clr_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4; + bcm2835_peri_write(paddr, mask); +} + +// Read input pin +uint8_t bcm2835_gpio_lev(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEV0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = bcm2835_peri_read(paddr); + return (value & (1 << shift)) ? HIGH : LOW; +} + +// See if an event detection bit is set +// Sigh cant support interrupts yet +uint8_t bcm2835_gpio_eds(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = bcm2835_peri_read(paddr); + return (value & (1 << shift)) ? HIGH : LOW; +} + +// Write a 1 to clear the bit in EDS +void bcm2835_gpio_set_eds(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_write(paddr, value); +} + +// Rising edge detect enable +void bcm2835_gpio_ren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_ren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Falling edge detect enable +void bcm2835_gpio_fen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_fen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// High detect enable +void bcm2835_gpio_hen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_hen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Low detect enable +void bcm2835_gpio_len(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_len(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Async rising edge detect enable +void bcm2835_gpio_aren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_aren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Async falling edge detect enable +void bcm2835_gpio_afen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_afen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +// Set pullup/down +void bcm2835_gpio_pud(uint8_t pud) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; + bcm2835_peri_write(paddr, pud); +} + +// Pullup/down clock +// Clocks the value of pud into the GPIO pin +void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUDCLK0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, (on ? 1 : 0) << shift); +} + +// Read GPIO pad behaviour for groups of GPIOs +uint32_t bcm2835_gpio_pad(uint8_t group) +{ + volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2; + return bcm2835_peri_read(paddr); +} + +// Set GPIO pad behaviour for groups of GPIOs +// powerup value for al pads is +// BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA +void bcm2835_gpio_set_pad(uint8_t group, uint32_t control) +{ + volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2; + bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD); +} + +// Some convenient arduino-like functions +// milliseconds +void bcm2835_delay(unsigned int millis) +{ + struct timespec sleeper; + + sleeper.tv_sec = (time_t)(millis / 1000); + sleeper.tv_nsec = (long)(millis % 1000) * 1000000; + nanosleep(&sleeper, NULL); +} + +// microseconds +void bcm2835_delayMicroseconds(uint64_t micros) +{ + struct timespec t1; + uint64_t start; + + // Calling nanosleep() takes at least 100-200 us, so use it for + // long waits and use a busy wait on the System Timer for the rest. + start = bcm2835_st_read(); + + if (micros > 450) + { + t1.tv_sec = 0; + t1.tv_nsec = 1000 * (long)(micros - 200); + nanosleep(&t1, NULL); + } + + bcm2835_st_delay(start, micros); +} + +// This function is added in order to simulate arduino millis() function +unsigned int bcm2835_millis(void) +{ +struct timeval now; +unsigned long long ms; + +gettimeofday(&now, NULL); + +ms = (now.tv_sec * 1000000 + now.tv_usec) / 1000 ; + +return ((uint32_t) (ms - epoch )); +} + +// +// Higher level convenience functions +// + +// Set the state of an output +void bcm2835_gpio_write(uint8_t pin, uint8_t on) +{ + if (on) + bcm2835_gpio_set(pin); + else + bcm2835_gpio_clr(pin); +} + +// Set the state of a all 32 outputs in the mask to on or off +void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on) +{ + if (on) + bcm2835_gpio_set_multi(mask); + else + bcm2835_gpio_clr_multi(mask); +} + +// Set the state of a all 32 outputs in the mask to the values in value +void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask) +{ + bcm2835_gpio_set_multi(value & mask); + bcm2835_gpio_clr_multi((~value) & mask); +} + +// Set the pullup/down resistor for a pin +// +// The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on +// the respective GPIO pins. These registers must be used in conjunction with the GPPUD +// register to effect GPIO Pull-up/down changes. The following sequence of events is +// required: +// 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither +// to remove the current Pull-up/down) +// 2. Wait 150 cycles ? this provides the required set-up time for the control signal +// 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to +// modify ? NOTE only the pads which receive a clock will be modified, all others will +// retain their previous state. +// 4. Wait 150 cycles ? this provides the required hold time for the control signal +// 5. Write to GPPUD to remove the control signal +// 6. Write to GPPUDCLK0/1 to remove the clock +// +// RPi has P1-03 and P1-05 with 1k8 pullup resistor +void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud) +{ + bcm2835_gpio_pud(pud); + delayMicroseconds(10); + bcm2835_gpio_pudclk(pin, 1); + delayMicroseconds(10); + bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF); + bcm2835_gpio_pudclk(pin, 0); +} + +void bcm2835_spi_begin(uint8_t cs) +{ + /* + // Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 + + + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK + + // Set the SPI CS register to the some sensible defaults + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + bcm2835_peri_write(paddr, 0); // All 0s + + // Clear TX and RX fifos + bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); + */ + + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + // Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them + // except if we need custom Chip Select Pin + // printf("bcm2835_spi_begin -> spi_custom_cs = %d \n",cs ); + + // Do we need custom chip select control or + // drive CE1 manually (because CE1 does not work with hardware) + if ( cs > BCM2835_SPI_CS_NONE || cs == BCM2835_SPI_CS1 ) + { + // indicate we will use a custom GPIO port + spi_custom_cs = cs ; + + // ok hard CE1 not working, drive it manually + if (cs == BCM2835_SPI_CS1 ) + { + // Dirty Hack CE1 in now custom Chip Select GPIO 26 + // the real CE1 pin + spi_custom_cs = RPI_GPIO_P1_26 ; + + bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_write(spi_custom_cs, HIGH); + } + + // Mask in we use custom CS (not sure it has a real effect) + bcm2835_peri_set_bits(paddr, BCM2835_SPI_CS_NONE, BCM2835_SPI0_CS_CS); + } + // Ok hardware driving of chip select + else + { + // Just in case + spi_custom_cs = 0 ; + + // Mask in the CS bits of CS + bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); + } + + // Now we can drive the I/O as asked + if (spi_custom_cs == 0) + { + // Not custom CS, so hardware driven + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0 + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1 + } + else + { + // so set custom CS as output, High level by default + bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_OUTP); // Custom GPIO + bcm2835_gpio_write(spi_custom_cs, HIGH); + } + + // Classic pin, hardware driven + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK + + // Set the SPI CS register to the some sensible defaults + bcm2835_peri_write(paddr, 0); // All 0s + + // Clear TX and RX fifos + bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); +} + +void bcm2835_spi_end(void) +{ + // Set all the SPI0 pins back to input + if (spi_custom_cs == 0) + { + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 + } + else + { + bcm2835_gpio_fsel(spi_custom_cs, BCM2835_GPIO_FSEL_INPT); // Custom GPIO + } + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK + + /* + // Set all the SPI0 pins back to input + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1 + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0 + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK +*/ + + +} + +// Drive Custom chip select pin +void bcm2835_spi_setChipSelect(uint8_t level) +{ + // Do this only if we are using custom ChipSelect I/O + if ( spi_custom_cs > BCM2835_SPI_CS_NONE ) + bcm2835_gpio_write(spi_custom_cs, level); +} + + + void bcm2835_spi_setBitOrder(uint8_t order) +{ + // BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one suported by SPI0 +} + +// defaults to 0, which means a divider of 65536. +// The divisor must be a power of 2. Odd numbers +// rounded down. The maximum SPI clock rate is +// of the APB clock +void bcm2835_spi_setClockDivider(uint16_t divider) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CLK/4; + bcm2835_peri_write(paddr, divider); +} + +void bcm2835_spi_setDataMode(uint8_t mode) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + // Mask in the CPO and CPHA bits of CS + bcm2835_peri_set_bits(paddr, mode << 2, BCM2835_SPI0_CS_CPOL | BCM2835_SPI0_CS_CPHA); +} + +// Writes (and reads) a single byte to SPI +uint8_t bcm2835_spi_transfer(uint8_t value) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + + // Custom chip select LOW + if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(LOW); + } + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + // Clear TX and RX fifos + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); + + // Set TA = 1 + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); + + // Maybe wait for TXD + while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)){ + delayMicroseconds(10); + } + + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, value); + + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)){ + delayMicroseconds(10); + } + + // Read any byte that was sent back by the slave while we sere sending to it + uint32_t ret = bcm2835_peri_read_nb(fifo); + + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); + + // Custom chip select HIGH + if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(HIGH); + } + return ret; +} + +// Writes (and reads) an number of bytes to SPI +void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + uint32_t TXCnt=0; + uint32_t RXCnt=0; + + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + + // Clear TX and RX fifos + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); + + // Set TA = 1 + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); + + // Use the FIFO's to reduce the interbyte times + while((TXCnt < len)||(RXCnt < len)) + { + // TX fifo not full, so add some more bytes + while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))&&(TXCnt < len )) + { + delayMicroseconds(10); + bcm2835_peri_write_nb(fifo, tbuf[TXCnt]); + TXCnt++; + } + //Rx fifo not empty, so get the next received bytes + while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len )) + { + delayMicroseconds(10); + rbuf[RXCnt] = bcm2835_peri_read_nb(fifo); + RXCnt++; + } + } + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)){ + delayMicroseconds(10); + } + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); +} + +// Writes an number of bytes to SPI +void bcm2835_spi_writenb(char* tbuf, uint32_t len) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + + if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(LOW); + } + // This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + + // Clear TX and RX fifos + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); + + // Set TA = 1 + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); + + uint32_t i; + for (i = 0; i < len; i++) + { + // Maybe wait for TXD + while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) + ; + + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, tbuf[i]); + + // Read from FIFO to prevent stalling + while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) + (void) bcm2835_peri_read_nb(fifo); + } + + // Wait for DONE to be set + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) { + while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) + (void) bcm2835_peri_read_nb(fifo); + }; + + // Set TA = 0, and also set the barrier + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); + + if(spi_custom_cs > BCM2835_SPI_CS_NONE){ + bcm2835_spi_setChipSelect(HIGH); + } +} + +// Writes (and reads) an number of bytes to SPI +// Read bytes are copied over onto the transmit buffer +void bcm2835_spi_transfern(char* buf, uint32_t len) +{ + bcm2835_spi_transfernb(buf, buf, len); +} + +void bcm2835_spi_chipSelect(uint8_t cs) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + // Mask in the CS bits of CS + bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); +} + +void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + uint8_t shift = 21 + cs; + // Mask in the appropriate CSPOLn bit + bcm2835_peri_set_bits(paddr, active << shift, 1 << shift); +} + +void bcm2835_i2c_begin(void) +{ +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; + // Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them + bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA + bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; + // Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL +#endif + + // Read the clock divider register + uint16_t cdiv = bcm2835_peri_read(paddr); + // Calculate time for transmitting one byte + // 1000000 = micros seconds in a second + // 9 = Clocks per byte : 8 bits + ACK + i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9; +} + +void bcm2835_i2c_end(void) +{ +#ifdef I2C_V1 + // Set all the I2C/BSC0 pins back to input + bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA + bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL +#else + // Set all the I2C/BSC1 pins back to input + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL +#endif +} + +void bcm2835_i2c_setSlaveAddress(uint8_t addr) +{ + // Set I2C Device Address +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4; +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4; +#endif + bcm2835_peri_write(paddr, addr); +} + +// defaults to 0x5dc, should result in a 166.666 kHz I2C clock frequency. +// The divisor must be a power of 2. Odd numbers +// rounded down. +void bcm2835_i2c_setClockDivider(uint16_t divider) +{ +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; +#endif + bcm2835_peri_write(paddr, divider); + // Calculate time for transmitting one byte + // 1000000 = micros seconds in a second + // 9 = Clocks per byte : 8 bits + ACK + i2c_byte_wait_us = ((float)divider / BCM2835_CORE_CLK_HZ) * 1000000 * 9; +} + +// set I2C clock divider by means of a baudrate number +void bcm2835_i2c_set_baudrate(uint32_t baudrate) +{ + uint32_t divider; + // use 0xFFFE mask to limit a max value and round down any odd number + divider = (BCM2835_CORE_CLK_HZ / baudrate) & 0xFFFE; + bcm2835_i2c_setClockDivider( (uint16_t)divider ); +} + +// Writes an number of bytes to I2C +uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + // Clear Status + bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); + // Set Data Length + bcm2835_peri_write_nb(dlen, len); + // pre populate FIFO with max buffer + while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) + { + bcm2835_peri_write_nb(fifo, buf[i]); + i++; + remaining--; + } + + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // Transfer is over when BCM2835_BSC_S_DONE + while(!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE )) + { + while ( remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TXD )) + { + // Write to FIFO, no barrier + bcm2835_peri_write_nb(fifo, buf[i]); + i++; + remaining--; + } + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read an number of bytes from I2C +uint8_t bcm2835_i2c_read(char* buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + // Clear Status + bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); + // Set Data Length + bcm2835_peri_write_nb(dlen, len); + // Start read + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ); + + // wait for transfer to complete + while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) + { + // we must empty the FIFO as it is populated and not use any delay + while (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is received + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read an number of bytes from I2C sending a repeated start after writing +// the required register. Only works if your device supports this mode +uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + // Clear Status + bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); + // Set Data Length + bcm2835_peri_write_nb(dlen, 1); + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN); + bcm2835_peri_write_nb(fifo, regaddr[0]); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // poll for transfer has started + while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) + { + // Linux may cause us to miss entire transfer stage + if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) + break; + } + + // Send a repeated start with read bit set in address + bcm2835_peri_write_nb(dlen, len); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); + + // Wait for write to complete and first byte back. + bcm2835_delayMicroseconds(i2c_byte_wait_us * 3); + + // wait for transfer to complete + while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) + { + // we must empty the FIFO as it is populated and not use any delay + while (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Sending an arbitrary number of bytes before issuing a repeated start +// (with no prior stop) and reading a response. Some devices require this behavior. +uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = cmds_len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + // Clear FIFO + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + + // Clear Status + bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); + + // Set Data Length + bcm2835_peri_write_nb(dlen, cmds_len); + + // pre populate FIFO with max buffer + while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) + { + bcm2835_peri_write_nb(fifo, cmds[i]); + i++; + remaining--; + } + + // Enable device and start transfer + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + // poll for transfer has started (way to do repeated start, from BCM2835 datasheet) + while ( !( bcm2835_peri_read_nb(status) & BCM2835_BSC_S_TA ) ) + { + // Linux may cause us to miss entire transfer stage + if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) + break; + } + + remaining = buf_len; + i = 0; + + // Send a repeated start with read bit set in address + bcm2835_peri_write_nb(dlen, buf_len); + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); + + // Wait for write to complete and first byte back. + bcm2835_delayMicroseconds(i2c_byte_wait_us * (cmds_len + 1)); + + // wait for transfer to complete + while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) + { + // we must empty the FIFO as it is populated and not use any delay + while (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + // transfer has finished - grab any remaining stuff in FIFO + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + // Read from FIFO, no barrier + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + // Received a NACK + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + // Received Clock Stretch Timeout + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + // Not all data is sent + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +// Read the System Timer Counter (64-bits) +uint64_t bcm2835_st_read(void) +{ + volatile uint32_t* paddr; + uint64_t st; + paddr = bcm2835_st + BCM2835_ST_CHI/4; + st = bcm2835_peri_read(paddr); + st <<= 32; + paddr = bcm2835_st + BCM2835_ST_CLO/4; + st += bcm2835_peri_read(paddr); + return st; +} + +// Delays for the specified number of microseconds with offset +void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros) +{ + uint64_t compare = offset_micros + micros; + + while(bcm2835_st_read() < compare) + ; +} + +// PWM + +void bcm2835_pwm_set_clock(uint32_t divisor) +{ + // From Gerts code + divisor &= 0xfff; + // Stop PWM clock + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01); + bcm2835_delay(110); // Prevents clock going slow + // Wait for the clock to be not busy + while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) + bcm2835_delay(1); + // set the clock divider and enable PWM clock + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12)); + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); // Source=osc and enable +} + +void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled) +{ + uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL); + + if (channel == 0) + { + if (markspace) + control |= BCM2835_PWM0_MS_MODE; + else + control &= ~BCM2835_PWM0_MS_MODE; + if (enabled) + control |= BCM2835_PWM0_ENABLE; + else + control &= ~BCM2835_PWM0_ENABLE; + } + else if (channel == 1) + { + if (markspace) + control |= BCM2835_PWM1_MS_MODE; + else + control &= ~BCM2835_PWM1_MS_MODE; + if (enabled) + control |= BCM2835_PWM1_ENABLE; + else + control &= ~BCM2835_PWM1_ENABLE; + } + + // If you use the barrier here, wierd things happen, and the commands dont work + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control); + // bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE); + +} + +void bcm2835_pwm_set_range(uint8_t channel, uint32_t range) +{ + if (channel == 0) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range); + else if (channel == 1) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range); +} + +void bcm2835_pwm_set_data(uint8_t channel, uint32_t data) +{ + if (channel == 0) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data); + else if (channel == 1) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data); +} + +// Allocate page-aligned memory. +void *malloc_aligned(size_t size) +{ + void *mem; + errno = posix_memalign(&mem, BCM2835_PAGE_SIZE, size); + return (errno ? NULL : mem); +} + +// Map 'size' bytes starting at 'off' in file 'fd' to memory. +// Return mapped address on success, MAP_FAILED otherwise. +// On error print message. +static void *mapmem(const char *msg, size_t size, int fd, off_t off) +{ + void *map = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, off); + if (MAP_FAILED == map) + fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno)); + return map; +} + +static void unmapmem(void **pmem, size_t size) +{ + if (*pmem == MAP_FAILED) return; + munmap(*pmem, size); + *pmem = MAP_FAILED; +} + +// Initialise this library. +int bcm2835_init(void) +{ + struct timeval tv ; + + if (debug) + { + bcm2835_pads = (uint32_t*)BCM2835_GPIO_PADS; + bcm2835_clk = (uint32_t*)BCM2835_CLOCK_BASE; + bcm2835_gpio = (uint32_t*)BCM2835_GPIO_BASE; + bcm2835_pwm = (uint32_t*)BCM2835_GPIO_PWM; + bcm2835_spi0 = (uint32_t*)BCM2835_SPI0_BASE; + bcm2835_bsc0 = (uint32_t*)BCM2835_BSC0_BASE; + bcm2835_bsc1 = (uint32_t*)BCM2835_BSC1_BASE; + bcm2835_st = (uint32_t*)BCM2835_ST_BASE; + return 1; // Success + } + int memfd = -1; + int ok = 0; + // Open the master /dev/memory device + if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) + { + fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", + strerror(errno)) ; + goto exit; + } + + // GPIO: + bcm2835_gpio = (volatile uint32_t *)mapmem("gpio", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_BASE); + if (bcm2835_gpio == MAP_FAILED) goto exit; + + // PWM + bcm2835_pwm = (volatile uint32_t *)mapmem("pwm", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PWM); + if (bcm2835_pwm == MAP_FAILED) goto exit; + + // Clock control (needed for PWM) + bcm2835_clk = (volatile uint32_t *)mapmem("clk", BCM2835_BLOCK_SIZE, memfd, BCM2835_CLOCK_BASE); + if (bcm2835_clk == MAP_FAILED) goto exit; + + bcm2835_pads = (volatile uint32_t *)mapmem("pads", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_PADS); + if (bcm2835_pads == MAP_FAILED) goto exit; + + bcm2835_spi0 = (volatile uint32_t *)mapmem("spi0", BCM2835_BLOCK_SIZE, memfd, BCM2835_SPI0_BASE); + if (bcm2835_spi0 == MAP_FAILED) goto exit; + + // I2C + bcm2835_bsc0 = (volatile uint32_t *)mapmem("bsc0", BCM2835_BLOCK_SIZE, memfd, BCM2835_BSC0_BASE); + if (bcm2835_bsc0 == MAP_FAILED) goto exit; + + bcm2835_bsc1 = (volatile uint32_t *)mapmem("bsc1", BCM2835_BLOCK_SIZE, memfd, BCM2835_BSC1_BASE); + if (bcm2835_bsc1 == MAP_FAILED) goto exit; + + // ST + bcm2835_st = (volatile uint32_t *)mapmem("st", BCM2835_BLOCK_SIZE, memfd, BCM2835_ST_BASE); + if (bcm2835_st == MAP_FAILED) goto exit; + + ok = 1; + +exit: + if (memfd >= 0) + close(memfd); + + if (!ok) + bcm2835_close(); + + gettimeofday (&tv, NULL) ; + epoch = (tv.tv_sec * 1000000 + tv.tv_usec) / 1000 ; + + return ok; +} + +// Close this library and deallocate everything +int bcm2835_close(void) +{ + if (debug) return 1; // Success + unmapmem((void**) &bcm2835_gpio, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_pwm, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_clk, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_spi0, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_bsc0, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_bsc1, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_st, BCM2835_BLOCK_SIZE); + unmapmem((void**) &bcm2835_pads, BCM2835_BLOCK_SIZE); + return 1; // Success +} + +#ifdef BCM2835_TEST +// this is a simple test program that prints out what it will do rather than +// actually doing it +int main(int argc, char **argv) +{ + // Be non-destructive + bcm2835_set_debug(1); + + if (!bcm2835_init()) + return 1; + + // Configure some GPIO pins fo some testing + // Set RPI pin P1-11 to be an output + bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP); + // Set RPI pin P1-15 to be an input + bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT); + // with a pullup + bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP); + // And a low detect enable + bcm2835_gpio_len(RPI_GPIO_P1_15); + // and input hysteresis disabled on GPIOs 0 to 27 + bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA); + +#if 1 + // Blink + while (1) + { + // Turn it on + bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH); + + // wait a bit + bcm2835_delay(500); + + // turn it off + bcm2835_gpio_write(RPI_GPIO_P1_11, LOW); + + // wait a bit + bcm2835_delay(500); + } +#endif + +#if 0 + // Read input + while (1) + { + // Read some data + uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15); + printf("read from pin 15: %d\n", value); + + // wait a bit + bcm2835_delay(500); + } +#endif + +#if 0 + // Look for a low event detection + // eds will be set whenever pin 15 goes low + while (1) + { + if (bcm2835_gpio_eds(RPI_GPIO_P1_15)) + { + // Now clear the eds flag by setting it to 1 + bcm2835_gpio_set_eds(RPI_GPIO_P1_15); + printf("low event detect for pin 15\n"); + } + + // wait a bit + bcm2835_delay(500); + } +#endif + + if (!bcm2835_close()) + return 1; + + return 0; +} +#endif + + + diff --git a/RPi/RF24/bcm2835.h b/RPi/RF24/bcm2835.h new file mode 100644 index 0000000..ab47838 --- /dev/null +++ b/RPi/RF24/bcm2835.h @@ -0,0 +1,1353 @@ +// bcm2835.h +// +// C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi +// +// Author: Mike McCauley +// Copyright (C) 2011-2013 Mike McCauley +// $Id: bcm2835.h,v 1.13 2013/12/06 22:24:52 mikem Exp mikem $ +// +//****************************************/ +// TMRh20 2014 - Merge updated lib from here: http://www.airspayce.com/mikem/bcm2835/group__spi.html#ga2fa186568605c21e9166a19b1d82ea95 +// with lib from here: https://github.com/farconada/RF24Network/blob/master/bcm2835.c +// Uses ONLY hardware CS pins +// Any CE pin can be used +// For use with optimized RPi RF24 and RF24 Network libs - see github.com/TMRh20 +//****************************************/ +// +/// \mainpage C library for Broadcom BCM 2835 as used in Raspberry Pi +/// +/// This is a C library for Raspberry Pi (RPi). It provides access to +/// GPIO and other IO functions on the Broadcom BCM 2835 chip, +/// allowing access to the GPIO pins on the +/// 26 pin IDE plug on the RPi board so you can control and interface with various external devices. +/// +/// It provides functions for reading digital inputs and setting digital outputs, using SPI and I2C, +/// and for accessing the system timers. +/// Pin event detection is supported by polling (interrupts are not supported). +/// +/// It is C++ compatible, and installs as a header file and non-shared library on +/// any Linux-based distro (but clearly is no use except on Raspberry Pi or another board with +/// BCM 2835). +/// +/// The version of the package that this documentation refers to can be downloaded +/// from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.36.tar.gz +/// You can find the latest version at http://www.airspayce.com/mikem/bcm2835 +/// +/// Several example programs are provided. +/// +/// Based on data in http://elinux.org/RPi_Low-level_peripherals and +/// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +/// and http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 +/// +/// You can also find online help and discussion at http://groups.google.com/group/bcm2835 +/// Please use that group for all questions and discussions on this topic. +/// Do not contact the author directly, unless it is to discuss commercial licensing. +/// +/// Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian +/// and Occidentalisv01 +/// CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() +/// are used and the pin is pulled LOW +/// it can cause temporary hangs on 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian +/// and Occidentalisv01. +/// Reason for this is not yet determined, but we suspect that an interrupt handler is +/// hitting a hard loop on those OSs. +/// If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with +/// bcm2835_gpio_clr_len() and friends after use. +/// +/// \par Installation +/// +/// This library consists of a single non-shared library and header file, which will be +/// installed in the usual places by make install +/// +/// \code +/// # download the latest version of the library, say bcm2835-1.xx.tar.gz, then: +/// tar zxvf bcm2835-1.xx.tar.gz +/// cd bcm2835-1.xx +/// ./configure +/// make +/// sudo make check +/// sudo make install +/// \endcode +/// +/// \par Physical Addresses +/// +/// The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() +/// are low level peripheral register access functions. They are designed to use +/// physical addresses as described in section 1.2.3 ARM physical addresses +/// of the BCM2835 ARM Peripherals manual. +/// Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus +/// addresses for peripherals are set up to map onto the peripheral bus address range starting at +/// 0x7E000000. Thus a peripheral advertised in the manual at bus address 0x7Ennnnnn is available at +/// physical address 0x20nnnnnn. +/// +/// The base address of the various peripheral registers are available with the following +/// externals: +/// bcm2835_gpio +/// bcm2835_pwm +/// bcm2835_clk +/// bcm2835_pads +/// bcm2835_spio0 +/// bcm2835_st +/// bcm2835_bsc0 +/// bcm2835_bsc1 +/// +/// \par Pin Numbering +/// +/// The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying +/// BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs +/// +/// RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835, +/// as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the +/// IDE header. +/// +/// RPi Version 2 also has a P5 connector with 4 GPIO pins, 5V, 3.3V and Gnd. +/// +/// The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ +/// the RPi pin number. There are symbolic definitions for each of the available pins +/// that you should use for convenience. See \ref RPiGPIOPin. +/// +/// \par SPI Pins +/// +/// The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, +/// allowing you to send and received data by SPI (Serial Peripheral Interface). +/// For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus +/// +/// When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their +/// default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able +/// to control the state of the SPI pins through the usual bcm2835_spi_gpio_write(). +/// When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be +/// configured and controled with the usual bcm2835_gpio_* calls. +/// +/// The Raspberry Pi GPIO pins used for SPI are: +/// +/// - P1-19 (MOSI) +/// - P1-21 (MISO) +/// - P1-23 (CLK) +/// - P1-24 (CE0) +/// - P1-26 (CE1) +/// +/// \par I2C Pins +/// +/// The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface, +/// allowing you to send and received data by I2C ("eye-squared cee"; generically referred to as "two-wire interface") . +/// For more information about I?C, see http://en.wikipedia.org/wiki/I%C2%B2C +/// +/// The Raspberry Pi V2 GPIO pins used for I2C are: +/// +/// - P1-03 (SDA) +/// - P1-05 (SLC) +/// +/// \par PWM +/// +/// The BCM2835 supports hardware PWM on a limited subset of GPIO pins. This bcm2835 library provides +/// functions for configuring and controlling PWM output on these pins. +/// +/// The BCM2835 contains 2 independent PWM channels (0 and 1), each of which be connnected to a limited subset of +/// GPIO pins. The following GPIO pins may be connected to the following PWM channels (from section 9.5): +/// \code +/// GPIO PIN RPi pin PWM Channel ALT FUN +/// 12 0 0 +/// 13 1 0 +/// 18 1-12 0 5 +/// 19 1 5 +/// 40 0 0 +/// 41 1 0 +/// 45 1 0 +/// 52 0 1 +/// 53 1 1 +/// \endcode +/// In order for a GPIO pin to emit output from its PWM channel, it must be set to the Alt Function given above. +/// Note carefully that current versions of the Raspberry Pi only expose one of these pins (GPIO 18 = RPi Pin 1-12) +/// on the IO headers, and therefore this is the only IO pin on the RPi that can be used for PWM. +/// Further it must be set to ALT FUN 5 to get PWM output. +/// +/// Both PWM channels are driven by the same PWM clock, whose clock dvider can be varied using +/// bcm2835_pwm_set_clock(). Each channel can be separately enabled with bcm2835_pwm_set_mode(). +/// The average output of the PWM channel is determined by the ratio of DATA/RANGE for that channel. +/// Use bcm2835_pwm_set_range() to set the range and bcm2835_pwm_set_data() to set the data in that ratio +/// +/// Each PWM channel can run in either Balanced or Mark-Space mode. In Balanced mode, the hardware +/// sends a combination of clock pulses that results in an overall DATA pulses per RANGE pulses. +/// In Mark-Space mode, the hardware sets the output HIGH for DATA clock pulses wide, followed by +/// LOW for RANGE-DATA clock pulses. +/// +/// The PWM clock can be set to control the PWM pulse widths. The PWM clock is derived from +/// a 19.2MHz clock. You can set any divider, but some common ones are provided by the BCM2835_PWM_CLOCK_DIVIDER_* +/// values of \ref bcm2835PWMClockDivider. +/// +/// For example, say you wanted to drive a DC motor with PWM at about 1kHz, +/// and control the speed in 1/1024 increments from +/// 0/1024 (stopped) through to 1024/1024 (full on). In that case you might set the +/// clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be +/// 1.2MHz/1024 = 1171.875Hz. +/// +/// \par Real Time performance constraints +/// +/// The bcm2835 is a library for user programs (i.e. they run in 'userland'). +/// Such programs are not part of the kernel and are usually +/// subject to paging and swapping by the kernel while it does other things besides running your program. +/// This means that you should not expect to get real-time performance or +/// real-time timing constraints from such programs. In particular, there is no guarantee that the +/// bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. +/// In fact, depending on other activity on the host, IO etc, you might get significantly longer delay times +/// than the one you asked for. So please dont expect to get exactly the time delay you request. +/// +/// Arjan reports that you can prevent swapping on Linux with the following code fragment: +/// +/// \code +/// struct sched_param sp; +/// memset(&sp, 0, sizeof(sp)); +/// sp.sched_priority = sched_get_priority_max(SCHED_FIFO); +/// sched_setscheduler(0, SCHED_FIFO, &sp); +/// mlockall(MCL_CURRENT | MCL_FUTURE); +/// \endcode +/// +/// \par Bindings to other languages +/// +/// mikem has made Perl bindings available at CPAN: +/// http://search.cpan.org/~mikem/Device-BCM2835-1.9/lib/Device/BCM2835.pm +/// Matthew Baker has kindly made Python bindings available at: +/// https://github.com/mubeta06/py-libbcm2835 +/// Gary Marks has created a Serial Peripheral Interface (SPI) command-line utility +/// for Raspberry Pi, based on the bcm2835 library. The +/// utility, spincl, is licensed under Open Source GNU GPLv3 by iP Solutions (http://ipsolutionscorp.com), as a +/// free download with source included: http://ipsolutionscorp.com/raspberry-pi-spi-utility/ +/// +/// \par Open Source Licensing GPL V2 +/// +/// This is the appropriate option if you want to share the source code of your +/// application with everyone you distribute it to, and you also want to give them +/// the right to share who uses it. If you wish to use this software under Open +/// Source Licensing, you must contribute all your source code to the open source +/// community in accordance with the GPL Version 2 when your application is +/// distributed. See http://www.gnu.org/copyleft/gpl.html and COPYING +/// +/// \par Acknowledgements +/// +/// Some of this code has been inspired by Dom and Gert. +/// The I2C code has been inspired by Alan Barr. +/// +/// \par Revision History +/// +/// \version 1.0 Initial release +/// \version 1.1 Minor bug fixes +/// \version 1.2 Added support for SPI +/// \version 1.3 Added bcm2835_spi_transfern() +/// \version 1.4 Fixed a problem that prevented SPI CE1 being used. Reported by David Robinson. +/// \version 1.5 Added bcm2835_close() to deinit the library. Suggested by C?sar Ortiz +/// \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01 +/// Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen() +/// bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now +/// changes only the pin specified. Other pins that were already previously +/// enabled stay enabled. +/// Added bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen() +/// bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() +/// to clear the enable for individual pins, suggested by Andreas Sundstrom. +/// \version 1.7 Added bcm2835_spi_transfernb to support different buffers for read and write. +/// \version 1.8 Improvements to read barrier, as suggested by maddin. +/// \version 1.9 Improvements contributed by mikew: +/// I noticed that it was mallocing memory for the mmaps on /dev/mem. +/// It's not necessary to do that, you can just mmap the file directly, +/// so I've removed the mallocs (and frees). +/// I've also modified delayMicroseconds() to use nanosleep() for long waits, +/// and a busy wait on a high resolution timer for the rest. This is because +/// I've found that calling nanosleep() takes at least 100-200 us. +/// You need to link using '-lrt' using this version. +/// I've added some unsigned casts to the debug prints to silence compiler +/// warnings I was getting, fixed some typos, and changed the value of +/// BCM2835_PAD_HYSTERESIS_ENABLED to 0x08 as per Gert van Loo's doc at +/// http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 +/// Also added a define for the passwrd value that Gert says is needed to +/// change pad control settings. +/// \version 1.10 Changed the names of the delay functions to bcm2835_delay() +/// and bcm2835_delayMicroseconds() to prevent collisions with wiringPi. +/// Macros to map delay()-> bcm2835_delay() and +/// Macros to map delayMicroseconds()-> bcm2835_delayMicroseconds(), which +/// can be disabled by defining BCM2835_NO_DELAY_COMPATIBILITY +/// \version 1.11 Fixed incorrect link to download file +/// \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping) +/// \version 1.13 New GPIO pin definitions for RPi version 2 plug P5 +/// Hardware base pointers are now available (after initialisation) externally as bcm2835_gpio +/// bcm2835_pwm bcm2835_clk bcm2835_pads bcm2835_spi0. +/// \version 1.14 Now compiles even if CLOCK_MONOTONIC_RAW is not available, uses CLOCK_MONOTONIC instead. +/// Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. +/// Reported by Ben Simpson. +/// \version 1.15 Added bcm2835_close() to end of examples as suggested by Mark Wolfe. +/// \version 1.16 Added bcm2835_gpio_set_multi, bcm2835_gpio_clr_multi and bcm2835_gpio_write_multi +/// to allow a mask of pins to be set all at once. Requested by Sebastian Loncar. +/// \version 1.17 Added bcm2835_gpio_write_mask. Requested by Sebastian Loncar. +/// \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: +/// now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. +/// No need to link with -lrt now. Contributed by Arjan van Vught. +/// \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. +/// Reported by olly. +/// \version 1.20 Patch from Mark Dootson to close /dev/mem after access to the peripherals has been granted. +/// \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. +/// Patch courtesy Jeremy Mortis. +/// \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set +/// falling edge events. Reported by Mark Dootson. +/// \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. +/// Improvements to bcm2835_i2c_read and bcm2835_i2c_write functions +/// to fix ocasional reads not completing. Patched by Mark Dootson. +/// \version 1.24 Mark Dootson p[atched a problem with his previously submitted code +/// under high load from other processes. +/// \version 1.25 Updated author and distribution location details to airspayce.com +/// \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. +/// Reported by Hartmut Henkel. +/// \version 1.27 bcm2835_gpio_set_pad() no longer needs BCM2835_PAD_PASSWRD: it is +/// now automatically included. +/// Added suport for PWM mode with bcm2835_pwm_* functions. +/// \version 1.28 Fixed a problem where bcm2835_spi_writenb() would have problems with transfers of more than +/// 64 bytes dues to read buffer filling. Patched by Peter Wรผrtz. +/// \version 1.29 Further fix to SPI from Peter Wรผrtz. +/// \version 1.30 10 microsecond delays from bcm2835_spi_transfer and bcm2835_spi_transfern for +/// significant performance improvements, Patch by Alan Watson. +/// \version 1.31 Fix a GCC warning about dummy variable, patched by Alan Watson. Thanks. +/// \version 1.32 Added option I2C_V1 definition to compile for version 1 RPi. +/// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. +/// Contributed by Malcolm Wiles based on work by Arvi Govindaraj. +/// \version 1.33 Added command line utilities i2c and gpio to examples. Contributed by Shahrooz Shahparnia. +/// \version 1.34 Added bcm2835_i2c_write_read_rs() which writes an arbitrary number of bytes, +/// sends a repeat start, and reads from the device. Contributed by Eduardo Steinhorst. +/// \version 1.35 Fix build errors when compiled under Qt. Also performance improvements with SPI transfers. Contributed by Udo Klaas. +/// \version 1.36 Make automake's test runner detect that we're skipping tests when not root, the second +/// one makes us skip the test when using fakeroot (as used when building +/// Debian packages). Contributed by Guido Gรผnther. +/// +/// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS + + + +// Defines for BCM2835 +#ifndef BCM2835_H +#define BCM2835_H + +#include + +/// \defgroup constants Constants for passing to and from library functions +/// The values here are designed to be passed to various functions in the bcm2835 library. +/// @{ + + +/// This means pin HIGH, true, 3.3volts on a pin. +#define HIGH 0x1 +/// This means pin LOW, false, 0volts on a pin. +#define LOW 0x0 + +/// Speed of the core clock core_clk +#define BCM2835_CORE_CLK_HZ 250000000 ///< 250 MHz + +// Physical addresses for various peripheral register sets +/// Base Physical Address of the BCM 2835 peripheral registers +#define BCM2835_PERI_BASE 0x20000000 +/// Base Physical Address of the System Timer registers +#define BCM2835_ST_BASE (BCM2835_PERI_BASE + 0x3000) +/// Base Physical Address of the Pads registers +#define BCM2835_GPIO_PADS (BCM2835_PERI_BASE + 0x100000) +/// Base Physical Address of the Clock/timer registers +#define BCM2835_CLOCK_BASE (BCM2835_PERI_BASE + 0x101000) +/// Base Physical Address of the GPIO registers +#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000) +/// Base Physical Address of the SPI0 registers +#define BCM2835_SPI0_BASE (BCM2835_PERI_BASE + 0x204000) +/// Base Physical Address of the BSC0 registers +#define BCM2835_BSC0_BASE (BCM2835_PERI_BASE + 0x205000) +/// Base Physical Address of the PWM registers +#define BCM2835_GPIO_PWM (BCM2835_PERI_BASE + 0x20C000) + /// Base Physical Address of the BSC1 registers +#define BCM2835_BSC1_BASE (BCM2835_PERI_BASE + 0x804000) + + +/// Base of the ST (System Timer) registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_st; + +/// Base of the GPIO registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_gpio; + +/// Base of the PWM registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_pwm; + +/// Base of the CLK registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_clk; + +/// Base of the PADS registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_pads; + +/// Base of the SPI0 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_spi0; + +/// Base of the BSC0 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_bsc0; + +/// Base of the BSC1 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_bsc1; + +/// Size of memory page on RPi +#define BCM2835_PAGE_SIZE (4*1024) +/// Size of memory block on RPi +#define BCM2835_BLOCK_SIZE (4*1024) + + +// Defines for GPIO +// The BCM2835 has 54 GPIO pins. +// BCM2835 data sheet, Page 90 onwards. +/// GPIO register offsets from BCM2835_GPIO_BASE. Offsets into the GPIO Peripheral block in bytes per 6.1 Register View +#define BCM2835_GPFSEL0 0x0000 ///< GPIO Function Select 0 +#define BCM2835_GPFSEL1 0x0004 ///< GPIO Function Select 1 +#define BCM2835_GPFSEL2 0x0008 ///< GPIO Function Select 2 +#define BCM2835_GPFSEL3 0x000c ///< GPIO Function Select 3 +#define BCM2835_GPFSEL4 0x0010 ///< GPIO Function Select 4 +#define BCM2835_GPFSEL5 0x0014 ///< GPIO Function Select 5 +#define BCM2835_GPSET0 0x001c ///< GPIO Pin Output Set 0 +#define BCM2835_GPSET1 0x0020 ///< GPIO Pin Output Set 1 +#define BCM2835_GPCLR0 0x0028 ///< GPIO Pin Output Clear 0 +#define BCM2835_GPCLR1 0x002c ///< GPIO Pin Output Clear 1 +#define BCM2835_GPLEV0 0x0034 ///< GPIO Pin Level 0 +#define BCM2835_GPLEV1 0x0038 ///< GPIO Pin Level 1 +#define BCM2835_GPEDS0 0x0040 ///< GPIO Pin Event Detect Status 0 +#define BCM2835_GPEDS1 0x0044 ///< GPIO Pin Event Detect Status 1 +#define BCM2835_GPREN0 0x004c ///< GPIO Pin Rising Edge Detect Enable 0 +#define BCM2835_GPREN1 0x0050 ///< GPIO Pin Rising Edge Detect Enable 1 +#define BCM2835_GPFEN0 0x0058 ///< GPIO Pin Falling Edge Detect Enable 0 +#define BCM2835_GPFEN1 0x005c ///< GPIO Pin Falling Edge Detect Enable 1 +#define BCM2835_GPHEN0 0x0064 ///< GPIO Pin High Detect Enable 0 +#define BCM2835_GPHEN1 0x0068 ///< GPIO Pin High Detect Enable 1 +#define BCM2835_GPLEN0 0x0070 ///< GPIO Pin Low Detect Enable 0 +#define BCM2835_GPLEN1 0x0074 ///< GPIO Pin Low Detect Enable 1 +#define BCM2835_GPAREN0 0x007c ///< GPIO Pin Async. Rising Edge Detect 0 +#define BCM2835_GPAREN1 0x0080 ///< GPIO Pin Async. Rising Edge Detect 1 +#define BCM2835_GPAFEN0 0x0088 ///< GPIO Pin Async. Falling Edge Detect 0 +#define BCM2835_GPAFEN1 0x008c ///< GPIO Pin Async. Falling Edge Detect 1 +#define BCM2835_GPPUD 0x0094 ///< GPIO Pin Pull-up/down Enable +#define BCM2835_GPPUDCLK0 0x0098 ///< GPIO Pin Pull-up/down Enable Clock 0 +#define BCM2835_GPPUDCLK1 0x009c ///< GPIO Pin Pull-up/down Enable Clock 1 + +/// \brief bcm2835PortFunction +/// Port function select modes for bcm2835_gpio_fsel() +typedef enum +{ + BCM2835_GPIO_FSEL_INPT = 0b000, ///< Input + BCM2835_GPIO_FSEL_OUTP = 0b001, ///< Output + BCM2835_GPIO_FSEL_ALT0 = 0b100, ///< Alternate function 0 + BCM2835_GPIO_FSEL_ALT1 = 0b101, ///< Alternate function 1 + BCM2835_GPIO_FSEL_ALT2 = 0b110, ///< Alternate function 2 + BCM2835_GPIO_FSEL_ALT3 = 0b111, ///< Alternate function 3 + BCM2835_GPIO_FSEL_ALT4 = 0b011, ///< Alternate function 4 + BCM2835_GPIO_FSEL_ALT5 = 0b010, ///< Alternate function 5 + BCM2835_GPIO_FSEL_MASK = 0b111 ///< Function select bits mask +} bcm2835FunctionSelect; + +/// \brief bcm2835PUDControl +/// Pullup/Pulldown defines for bcm2835_gpio_pud() +typedef enum +{ + BCM2835_GPIO_PUD_OFF = 0b00, ///< Off ? disable pull-up/down + BCM2835_GPIO_PUD_DOWN = 0b01, ///< Enable Pull Down control + BCM2835_GPIO_PUD_UP = 0b10 ///< Enable Pull Up control +} bcm2835PUDControl; + +/// Pad control register offsets from BCM2835_GPIO_PADS +#define BCM2835_PADS_GPIO_0_27 0x002c ///< Pad control register for pads 0 to 27 +#define BCM2835_PADS_GPIO_28_45 0x0030 ///< Pad control register for pads 28 to 45 +#define BCM2835_PADS_GPIO_46_53 0x0034 ///< Pad control register for pads 46 to 53 + +/// Pad Control masks +#define BCM2835_PAD_PASSWRD (0x5A << 24) ///< Password to enable setting pad mask +#define BCM2835_PAD_SLEW_RATE_UNLIMITED 0x10 ///< Slew rate unlimited +#define BCM2835_PAD_HYSTERESIS_ENABLED 0x08 ///< Hysteresis enabled +#define BCM2835_PAD_DRIVE_2mA 0x00 ///< 2mA drive current +#define BCM2835_PAD_DRIVE_4mA 0x01 ///< 4mA drive current +#define BCM2835_PAD_DRIVE_6mA 0x02 ///< 6mA drive current +#define BCM2835_PAD_DRIVE_8mA 0x03 ///< 8mA drive current +#define BCM2835_PAD_DRIVE_10mA 0x04 ///< 10mA drive current +#define BCM2835_PAD_DRIVE_12mA 0x05 ///< 12mA drive current +#define BCM2835_PAD_DRIVE_14mA 0x06 ///< 14mA drive current +#define BCM2835_PAD_DRIVE_16mA 0x07 ///< 16mA drive current + +/// \brief bcm2835PadGroup +/// Pad group specification for bcm2835_gpio_pad() +typedef enum +{ + BCM2835_PAD_GROUP_GPIO_0_27 = 0, ///< Pad group for GPIO pads 0 to 27 + BCM2835_PAD_GROUP_GPIO_28_45 = 1, ///< Pad group for GPIO pads 28 to 45 + BCM2835_PAD_GROUP_GPIO_46_53 = 2 ///< Pad group for GPIO pads 46 to 53 +} bcm2835PadGroup; + +/// \brief GPIO Pin Numbers +/// +/// Here we define Raspberry Pin GPIO pins on P1 in terms of the underlying BCM GPIO pin numbers. +/// These can be passed as a pin number to any function requiring a pin. +/// Not all pins on the RPi 26 bin IDE plug are connected to GPIO pins +/// and some can adopt an alternate function. +/// RPi version 2 has some slightly different pinouts, and these are values RPI_V2_*. +/// At bootup, pins 8 and 10 are set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively +/// When SPI0 is in use (ie after bcm2835_spi_begin()), pins 19, 21, 23, 24, 26 are dedicated to SPI +/// and cant be controlled independently +typedef enum +{ + RPI_GPIO_P1_03 = 0, ///< Version 1, Pin P1-03 + RPI_GPIO_P1_05 = 1, ///< Version 1, Pin P1-05 + RPI_GPIO_P1_07 = 4, ///< Version 1, Pin P1-07 + RPI_GPIO_P1_08 = 14, ///< Version 1, Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_GPIO_P1_10 = 15, ///< Version 1, Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_GPIO_P1_11 = 17, ///< Version 1, Pin P1-11 + RPI_GPIO_P1_12 = 18, ///< Version 1, Pin P1-12, can be PWM channel 0 in ALT FUN 5 + RPI_GPIO_P1_13 = 21, ///< Version 1, Pin P1-13 + RPI_GPIO_P1_15 = 22, ///< Version 1, Pin P1-15 + RPI_GPIO_P1_16 = 23, ///< Version 1, Pin P1-16 + RPI_GPIO_P1_18 = 24, ///< Version 1, Pin P1-18 + RPI_GPIO_P1_19 = 10, ///< Version 1, Pin P1-19, MOSI when SPI0 in use + RPI_GPIO_P1_21 = 9, ///< Version 1, Pin P1-21, MISO when SPI0 in use + RPI_GPIO_P1_22 = 25, ///< Version 1, Pin P1-22 + RPI_GPIO_P1_23 = 11, ///< Version 1, Pin P1-23, CLK when SPI0 in use + RPI_GPIO_P1_24 = 8, ///< Version 1, Pin P1-24, CE0 when SPI0 in use + RPI_GPIO_P1_26 = 7, ///< Version 1, Pin P1-26, CE1 when SPI0 in use + + // RPi Version 2 + RPI_V2_GPIO_P1_03 = 2, ///< Version 2, Pin P1-03 + RPI_V2_GPIO_P1_05 = 3, ///< Version 2, Pin P1-05 + RPI_V2_GPIO_P1_07 = 4, ///< Version 2, Pin P1-07 + RPI_V2_GPIO_P1_08 = 14, ///< Version 2, Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_V2_GPIO_P1_10 = 15, ///< Version 2, Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_V2_GPIO_P1_11 = 17, ///< Version 2, Pin P1-11 + RPI_V2_GPIO_P1_12 = 18, ///< Version 2, Pin P1-12, can be PWM channel 0 in ALT FUN 5 + RPI_V2_GPIO_P1_13 = 27, ///< Version 2, Pin P1-13 + RPI_V2_GPIO_P1_15 = 22, ///< Version 2, Pin P1-15 + RPI_V2_GPIO_P1_16 = 23, ///< Version 2, Pin P1-16 + RPI_V2_GPIO_P1_18 = 24, ///< Version 2, Pin P1-18 + RPI_V2_GPIO_P1_19 = 10, ///< Version 2, Pin P1-19, MOSI when SPI0 in use + RPI_V2_GPIO_P1_21 = 9, ///< Version 2, Pin P1-21, MISO when SPI0 in use + RPI_V2_GPIO_P1_22 = 25, ///< Version 2, Pin P1-22 + RPI_V2_GPIO_P1_23 = 11, ///< Version 2, Pin P1-23, CLK when SPI0 in use + RPI_V2_GPIO_P1_24 = 8, ///< Version 2, Pin P1-24, CE0 when SPI0 in use + RPI_V2_GPIO_P1_26 = 7, ///< Version 2, Pin P1-26, CE1 when SPI0 in use + + // RPi Version 2, new plug P5 + RPI_V2_GPIO_P5_03 = 28, ///< Version 2, Pin P5-03 + RPI_V2_GPIO_P5_04 = 29, ///< Version 2, Pin P5-04 + RPI_V2_GPIO_P5_05 = 30, ///< Version 2, Pin P5-05 + RPI_V2_GPIO_P5_06 = 31, ///< Version 2, Pin P5-06 + +} RPiGPIOPin; + +// Defines for SPI +// GPIO register offsets from BCM2835_SPI0_BASE. +// Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map +#define BCM2835_SPI0_CS 0x0000 ///< SPI Master Control and Status +#define BCM2835_SPI0_FIFO 0x0004 ///< SPI Master TX and RX FIFOs +#define BCM2835_SPI0_CLK 0x0008 ///< SPI Master Clock Divider +#define BCM2835_SPI0_DLEN 0x000c ///< SPI Master Data Length +#define BCM2835_SPI0_LTOH 0x0010 ///< SPI LOSSI mode TOH +#define BCM2835_SPI0_DC 0x0014 ///< SPI DMA DREQ Controls + +// Register masks for SPI0_CS +#define BCM2835_SPI0_CS_LEN_LONG 0x02000000 ///< Enable Long data word in Lossi mode if DMA_LEN is set +#define BCM2835_SPI0_CS_DMA_LEN 0x01000000 ///< Enable DMA mode in Lossi mode +#define BCM2835_SPI0_CS_CSPOL2 0x00800000 ///< Chip Select 2 Polarity +#define BCM2835_SPI0_CS_CSPOL1 0x00400000 ///< Chip Select 1 Polarity +#define BCM2835_SPI0_CS_CSPOL0 0x00200000 ///< Chip Select 0 Polarity +#define BCM2835_SPI0_CS_RXF 0x00100000 ///< RXF - RX FIFO Full +#define BCM2835_SPI0_CS_RXR 0x00080000 ///< RXR RX FIFO needs Reading ( full) +#define BCM2835_SPI0_CS_TXD 0x00040000 ///< TXD TX FIFO can accept Data +#define BCM2835_SPI0_CS_RXD 0x00020000 ///< RXD RX FIFO contains Data +#define BCM2835_SPI0_CS_DONE 0x00010000 ///< Done transfer Done +#define BCM2835_SPI0_CS_TE_EN 0x00008000 ///< Unused +#define BCM2835_SPI0_CS_LMONO 0x00004000 ///< Unused +#define BCM2835_SPI0_CS_LEN 0x00002000 ///< LEN LoSSI enable +#define BCM2835_SPI0_CS_REN 0x00001000 ///< REN Read Enable +#define BCM2835_SPI0_CS_ADCS 0x00000800 ///< ADCS Automatically Deassert Chip Select +#define BCM2835_SPI0_CS_INTR 0x00000400 ///< INTR Interrupt on RXR +#define BCM2835_SPI0_CS_INTD 0x00000200 ///< INTD Interrupt on Done +#define BCM2835_SPI0_CS_DMAEN 0x00000100 ///< DMAEN DMA Enable +#define BCM2835_SPI0_CS_TA 0x00000080 ///< Transfer Active +#define BCM2835_SPI0_CS_CSPOL 0x00000040 ///< Chip Select Polarity +#define BCM2835_SPI0_CS_CLEAR 0x00000030 ///< Clear FIFO Clear RX and TX +#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 ///< Clear FIFO Clear RX +#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 ///< Clear FIFO Clear TX +#define BCM2835_SPI0_CS_CPOL 0x00000008 ///< Clock Polarity +#define BCM2835_SPI0_CS_CPHA 0x00000004 ///< Clock Phase +#define BCM2835_SPI0_CS_CS 0x00000003 ///< Chip Select + +/// \brief bcm2835SPIBitOrder SPI Bit order +/// Specifies the SPI data bit ordering for bcm2835_spi_setBitOrder() +typedef enum +{ + BCM2835_SPI_BIT_ORDER_LSBFIRST = 0, ///< LSB First + BCM2835_SPI_BIT_ORDER_MSBFIRST = 1 ///< MSB First +}bcm2835SPIBitOrder; + +/// \brief SPI Data mode +/// Specify the SPI data mode to be passed to bcm2835_spi_setDataMode() +typedef enum +{ + BCM2835_SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0 + BCM2835_SPI_MODE1 = 1, ///< CPOL = 0, CPHA = 1 + BCM2835_SPI_MODE2 = 2, ///< CPOL = 1, CPHA = 0 + BCM2835_SPI_MODE3 = 3, ///< CPOL = 1, CPHA = 1 +}bcm2835SPIMode; + +/// \brief bcm2835SPIChipSelect +/// Specify the SPI chip select pin(s) +typedef enum +{ + // Can also use non-standard pins with this lib + BCM2835_SPI_CS0 = 0, ///< Chip Select 0 + BCM2835_SPI_CS1 = 1, ///< Chip Select 1 + BCM2835_SPI_CS2 = 2, ///< Chip Select 2 (ie pins CS1 and CS2 are asserted) + BCM2835_SPI_CS_NONE = 3, ///< No CS, control it yourself + + // Only GPIO > 3 can be used (to not interfere with the previous value just above ) + // Lucky we have plenty of theese pins + BCM2835_SPI_CS_GPIO4 = RPI_V2_GPIO_P1_07, /// BCM GPIO 4 + BCM2835_SPI_CS_GPIO17 = RPI_V2_GPIO_P1_11, /// BCM GPIO 17 + BCM2835_SPI_CS_GPIO18 = RPI_V2_GPIO_P1_12, /// BCM GPIO 18 + BCM2835_SPI_CS_GPIO22 = RPI_V2_GPIO_P1_15, /// BCM GPIO 22 + BCM2835_SPI_CS_GPIO23 = RPI_V2_GPIO_P1_16, /// BCM GPIO 23 + BCM2835_SPI_CS_GPIO24 = RPI_V2_GPIO_P1_18, /// BCM GPIO 24 + BCM2835_SPI_CS_GPIO25 = RPI_V2_GPIO_P1_22, /// BCM GPIO 25 + BCM2835_SPI_CS_GPIO28 = RPI_V2_GPIO_P5_03, /// BCM GPIO 28 + BCM2835_SPI_CS_GPIO29 = RPI_V2_GPIO_P5_04, /// BCM GPIO 29 + BCM2835_SPI_CS_GPIO30 = RPI_V2_GPIO_P5_05, /// BCM GPIO 30 + BCM2835_SPI_CS_GPIO31 = RPI_V2_GPIO_P5_06, /// BCM GPIO 31 + +} bcm2835SPIChipSelect; + +/// \brief bcm2835SPIClockDivider +/// Specifies the divider used to generate the SPI clock from the system clock. +/// Figures below give the divider, clock period and clock frequency. +/// Clock divided is based on nominal base clock rate of 250MHz +/// It is reported that (contrary to the documentation) any even divider may used. +/// The frequencies shown for each divider have been confirmed by measurement +typedef enum +{ + BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, ///< 65536 = 262.144us = 3.814697260kHz + BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, ///< 32768 = 131.072us = 7.629394531kHz + BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, ///< 16384 = 65.536us = 15.25878906kHz + BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, ///< 8192 = 32.768us = 30/51757813kHz + BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, ///< 4096 = 16.384us = 61.03515625kHz + BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, ///< 2048 = 8.192us = 122.0703125kHz + BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, ///< 1024 = 4.096us = 244.140625kHz + BCM2835_SPI_CLOCK_DIVIDER_512 = 512, ///< 512 = 2.048us = 488.28125kHz + BCM2835_SPI_CLOCK_DIVIDER_256 = 256, ///< 256 = 1.024us = 976.5625MHz + BCM2835_SPI_CLOCK_DIVIDER_128 = 128, ///< 128 = 512ns = = 1.953125MHz + BCM2835_SPI_CLOCK_DIVIDER_64 = 64, ///< 64 = 256ns = 3.90625MHz + BCM2835_SPI_CLOCK_DIVIDER_32 = 32, ///< 32 = 128ns = 7.8125MHz + BCM2835_SPI_CLOCK_DIVIDER_16 = 16, ///< 16 = 64ns = 15.625MHz + BCM2835_SPI_CLOCK_DIVIDER_8 = 8, ///< 8 = 32ns = 31.25MHz + BCM2835_SPI_CLOCK_DIVIDER_4 = 4, ///< 4 = 16ns = 62.5MHz + BCM2835_SPI_CLOCK_DIVIDER_2 = 2, ///< 2 = 8ns = 125MHz, fastest you can get + BCM2835_SPI_CLOCK_DIVIDER_1 = 1, ///< 1 = 262.144us = 3.814697260kHz, same as 0/65536 +} bcm2835SPIClockDivider; + +/// \brief bcm2835SPISpeed +/// Specifies the divider used to generate the SPI clock from the system clock. +/// Figures below give the clock speed instead of clock divider. +#define BCM2835_SPI_SPEED_64MHZ BCM2835_SPI_CLOCK_DIVIDER_4 +#define BCM2835_SPI_SPEED_32MHZ BCM2835_SPI_CLOCK_DIVIDER_8 +#define BCM2835_SPI_SPEED_16MHZ BCM2835_SPI_CLOCK_DIVIDER_16 +#define BCM2835_SPI_SPEED_8MHZ BCM2835_SPI_CLOCK_DIVIDER_32 +#define BCM2835_SPI_SPEED_4MHZ BCM2835_SPI_CLOCK_DIVIDER_64 +#define BCM2835_SPI_SPEED_2MHZ BCM2835_SPI_CLOCK_DIVIDER_128 +#define BCM2835_SPI_SPEED_1MHZ BCM2835_SPI_CLOCK_DIVIDER_256 +#define BCM2835_SPI_SPEED_512KHZ BCM2835_SPI_CLOCK_DIVIDER_512 +#define BCM2835_SPI_SPEED_256KHZ BCM2835_SPI_CLOCK_DIVIDER_1024 +#define BCM2835_SPI_SPEED_128KHZ BCM2835_SPI_CLOCK_DIVIDER_2048 +#define BCM2835_SPI_SPEED_64KHZ BCM2835_SPI_CLOCK_DIVIDER_4096 +#define BCM2835_SPI_SPEED_32KHZ BCM2835_SPI_CLOCK_DIVIDER_8192 +#define BCM2835_SPI_SPEED_16KHZ BCM2835_SPI_CLOCK_DIVIDER_16384 +#define BCM2835_SPI_SPEED_8KHZ BCM2835_SPI_CLOCK_DIVIDER_32768 + +// Defines for I2C +// GPIO register offsets from BCM2835_BSC*_BASE. +// Offsets into the BSC Peripheral block in bytes per 3.1 BSC Register Map +#define BCM2835_BSC_C 0x0000 ///< BSC Master Control +#define BCM2835_BSC_S 0x0004 ///< BSC Master Status +#define BCM2835_BSC_DLEN 0x0008 ///< BSC Master Data Length +#define BCM2835_BSC_A 0x000c ///< BSC Master Slave Address +#define BCM2835_BSC_FIFO 0x0010 ///< BSC Master Data FIFO +#define BCM2835_BSC_DIV 0x0014 ///< BSC Master Clock Divider +#define BCM2835_BSC_DEL 0x0018 ///< BSC Master Data Delay +#define BCM2835_BSC_CLKT 0x001c ///< BSC Master Clock Stretch Timeout + +// Register masks for BSC_C +#define BCM2835_BSC_C_I2CEN 0x00008000 ///< I2C Enable, 0 = disabled, 1 = enabled +#define BCM2835_BSC_C_INTR 0x00000400 ///< Interrupt on RX +#define BCM2835_BSC_C_INTT 0x00000200 ///< Interrupt on TX +#define BCM2835_BSC_C_INTD 0x00000100 ///< Interrupt on DONE +#define BCM2835_BSC_C_ST 0x00000080 ///< Start transfer, 1 = Start a new transfer +#define BCM2835_BSC_C_CLEAR_1 0x00000020 ///< Clear FIFO Clear +#define BCM2835_BSC_C_CLEAR_2 0x00000010 ///< Clear FIFO Clear +#define BCM2835_BSC_C_READ 0x00000001 ///< Read transfer + +// Register masks for BSC_S +#define BCM2835_BSC_S_CLKT 0x00000200 ///< Clock stretch timeout +#define BCM2835_BSC_S_ERR 0x00000100 ///< ACK error +#define BCM2835_BSC_S_RXF 0x00000080 ///< RXF FIFO full, 0 = FIFO is not full, 1 = FIFO is full +#define BCM2835_BSC_S_TXE 0x00000040 ///< TXE FIFO full, 0 = FIFO is not full, 1 = FIFO is full +#define BCM2835_BSC_S_RXD 0x00000020 ///< RXD FIFO contains data +#define BCM2835_BSC_S_TXD 0x00000010 ///< TXD FIFO can accept data +#define BCM2835_BSC_S_RXR 0x00000008 ///< RXR FIFO needs reading (full) +#define BCM2835_BSC_S_TXW 0x00000004 ///< TXW FIFO needs writing (full) +#define BCM2835_BSC_S_DONE 0x00000002 ///< Transfer DONE +#define BCM2835_BSC_S_TA 0x00000001 ///< Transfer Active + +#define BCM2835_BSC_FIFO_SIZE 16 ///< BSC FIFO size + +/// \brief bcm2835I2CClockDivider +/// Specifies the divider used to generate the I2C clock from the system clock. +/// Clock divided is based on nominal base clock rate of 250MHz +typedef enum +{ + BCM2835_I2C_CLOCK_DIVIDER_2500 = 2500, ///< 2500 = 10us = 100 kHz + BCM2835_I2C_CLOCK_DIVIDER_626 = 626, ///< 622 = 2.504us = 399.3610 kHz + BCM2835_I2C_CLOCK_DIVIDER_150 = 150, ///< 150 = 60ns = 1.666 MHz (default at reset) + BCM2835_I2C_CLOCK_DIVIDER_148 = 148, ///< 148 = 59ns = 1.689 MHz +} bcm2835I2CClockDivider; + +/// \brief bcm2835I2CReasonCodes +/// Specifies the reason codes for the bcm2835_i2c_write and bcm2835_i2c_read functions. +typedef enum +{ + BCM2835_I2C_REASON_OK = 0x00, ///< Success + BCM2835_I2C_REASON_ERROR_NACK = 0x01, ///< Received a NACK + BCM2835_I2C_REASON_ERROR_CLKT = 0x02, ///< Received Clock Stretch Timeout + BCM2835_I2C_REASON_ERROR_DATA = 0x04, ///< Not all data is sent / received +} bcm2835I2CReasonCodes; + +// Defines for ST +// GPIO register offsets from BCM2835_ST_BASE. +// Offsets into the ST Peripheral block in bytes per 12.1 System Timer Registers +// The System Timer peripheral provides four 32-bit timer channels and a single 64-bit free running counter. +// BCM2835_ST_CLO is the System Timer Counter Lower bits register. +// The system timer free-running counter lower register is a read-only register that returns the current value +// of the lower 32-bits of the free running counter. +// BCM2835_ST_CHI is the System Timer Counter Upper bits register. +// The system timer free-running counter upper register is a read-only register that returns the current value +// of the upper 32-bits of the free running counter. +#define BCM2835_ST_CS 0x0000 ///< System Timer Control/Status +#define BCM2835_ST_CLO 0x0004 ///< System Timer Counter Lower 32 bits +#define BCM2835_ST_CHI 0x0008 ///< System Timer Counter Upper 32 bits + +/// @} + + +// Defines for PWM, word offsets (ie 4 byte multiples) +#define BCM2835_PWM_CONTROL 0 +#define BCM2835_PWM_STATUS 1 +#define BCM2835_PWM_DMAC 2 +#define BCM2835_PWM0_RANGE 4 +#define BCM2835_PWM0_DATA 5 +#define BCM2835_PWM_FIF1 6 +#define BCM2835_PWM1_RANGE 8 +#define BCM2835_PWM1_DATA 9 + +// Defines for PWM Clock, word offsets (ie 4 byte multiples) +#define BCM2835_PWMCLK_CNTL 40 +#define BCM2835_PWMCLK_DIV 41 +#define BCM2835_PWM_PASSWRD (0x5A << 24) ///< Password to enable setting PWM clock + +#define BCM2835_PWM1_MS_MODE 0x8000 ///< Run in Mark/Space mode +#define BCM2835_PWM1_USEFIFO 0x2000 ///< Data from FIFO +#define BCM2835_PWM1_REVPOLAR 0x1000 ///< Reverse polarity +#define BCM2835_PWM1_OFFSTATE 0x0800 ///< Ouput Off state +#define BCM2835_PWM1_REPEATFF 0x0400 ///< Repeat last value if FIFO empty +#define BCM2835_PWM1_SERIAL 0x0200 ///< Run in serial mode +#define BCM2835_PWM1_ENABLE 0x0100 ///< Channel Enable + +#define BCM2835_PWM0_MS_MODE 0x0080 ///< Run in Mark/Space mode +#define BCM2835_PWM_CLEAR_FIFO 0x0040 ///< Clear FIFO +#define BCM2835_PWM0_USEFIFO 0x0020 ///< Data from FIFO +#define BCM2835_PWM0_REVPOLAR 0x0010 ///< Reverse polarity +#define BCM2835_PWM0_OFFSTATE 0x0008 ///< Ouput Off state +#define BCM2835_PWM0_REPEATFF 0x0004 ///< Repeat last value if FIFO empty +#define BCM2835_PWM0_SERIAL 0x0002 ///< Run in serial mode +#define BCM2835_PWM0_ENABLE 0x0001 ///< Channel Enable + + +/// \brief bcm2835PWMClockDivider +/// Specifies the divider used to generate the PWM clock from the system clock. +/// Figures below give the divider, clock period and clock frequency. +/// Clock divided is based on nominal PWM base clock rate of 19.2MHz +/// The frequencies shown for each divider have been confirmed by measurement +typedef enum +{ + BCM2835_PWM_CLOCK_DIVIDER_32768 = 32768, ///< 32768 = 585Hz + BCM2835_PWM_CLOCK_DIVIDER_16384 = 16384, ///< 16384 = 1171.8Hz + BCM2835_PWM_CLOCK_DIVIDER_8192 = 8192, ///< 8192 = 2.34375kHz + BCM2835_PWM_CLOCK_DIVIDER_4096 = 4096, ///< 4096 = 4.6875kHz + BCM2835_PWM_CLOCK_DIVIDER_2048 = 2048, ///< 2048 = 9.375kHz + BCM2835_PWM_CLOCK_DIVIDER_1024 = 1024, ///< 1024 = 18.75kHz + BCM2835_PWM_CLOCK_DIVIDER_512 = 512, ///< 512 = 37.5kHz + BCM2835_PWM_CLOCK_DIVIDER_256 = 256, ///< 256 = 75kHz + BCM2835_PWM_CLOCK_DIVIDER_128 = 128, ///< 128 = 150kHz + BCM2835_PWM_CLOCK_DIVIDER_64 = 64, ///< 64 = 300kHz + BCM2835_PWM_CLOCK_DIVIDER_32 = 32, ///< 32 = 600.0kHz + BCM2835_PWM_CLOCK_DIVIDER_16 = 16, ///< 16 = 1.2MHz + BCM2835_PWM_CLOCK_DIVIDER_8 = 8, ///< 8 = 2.4MHz + BCM2835_PWM_CLOCK_DIVIDER_4 = 4, ///< 4 = 4.8MHz + BCM2835_PWM_CLOCK_DIVIDER_2 = 2, ///< 2 = 9.6MHz, fastest you can get + BCM2835_PWM_CLOCK_DIVIDER_1 = 1, ///< 1 = 4.6875kHz, same as divider 4096 +} bcm2835PWMClockDivider; + +// Historical name compatibility +#ifndef BCM2835_NO_DELAY_COMPATIBILITY +#define delay(x) bcm2835_delay(x) +#define delayMicroseconds(x) bcm2835_delayMicroseconds(x) +#define millis() bcm2835_millis() +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + /// \defgroup init Library initialisation and management + /// These functions allow you to intialise and control the bcm2835 library + /// @{ + + /// Initialise the library by opening /dev/mem and getting pointers to the + /// internal memory for BCM 2835 device registers. You must call this (successfully) + /// before calling any other + /// functions in this library (except bcm2835_set_debug). + /// If bcm2835_init() fails by returning 0, + /// calling any other function may result in crashes or other failures. + /// Prints messages to stderr in case of errors. + /// \return 1 if successful else 0 + extern int bcm2835_init(void); + + /// Close the library, deallocating any allocated memory and closing /dev/mem + /// \return 1 if successful else 0 + extern int bcm2835_close(void); + + /// Sets the debug level of the library. + /// A value of 1 prevents mapping to /dev/mem, and makes the library print out + /// what it would do, rather than accessing the GPIO registers. + /// A value of 0, the default, causes normal operation. + /// Call this before calling bcm2835_init(); + /// \param[in] debug The new debug level. 1 means debug + extern void bcm2835_set_debug(uint8_t debug); + + /// @} // end of init + + /// \defgroup lowlevel Low level register access + /// These functions provide low level register access, and should not generally + /// need to be used + /// + /// @{ + + /// Reads 32 bit value from a peripheral address + /// The read is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read(volatile uint32_t* paddr); + + + /// Reads 32 bit value from a peripheral address without the read barrier + /// You should only use this when your code has previously called bcm2835_peri_read() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr); + + + /// Writes 32 bit value from a peripheral address + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value); + + /// Writes 32 bit value from a peripheral address without the write barrier + /// You should only use this when your code has previously called bcm2835_peri_write() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); + + /// Alters a number of bits in a 32 peripheral regsiter. + /// It reads the current valu and then alters the bits deines as 1 in mask, + /// according to the bit value in value. + /// All other bits that are 0 in the mask are unaffected. + /// Use this to alter a subset of the bits in a register. + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write, masked in by mask. + /// \param[in] mask Bitmask that defines the bits that will be altered in the register. + /// \sa Physical Addresses + extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask); + /// @} // end of lowlevel + + /// \defgroup gpio GPIO register access + /// These functions allow you to control the GPIO interface. You can set the + /// function of each GPIO pin, read the input state and set the output state. + /// @{ + + /// Sets the Function Select register for the given pin, which configures + /// the pin as Input, Output or one of the 6 alternate functions. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect + extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode); + + /// Sets the specified pin output to + /// HIGH. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_set(uint8_t pin); + + /// Sets the specified pin output to + /// LOW. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_clr(uint8_t pin); + + /// Sets any of the first 32 GPIO output pins specified in the mask to + /// HIGH. + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \sa bcm2835_gpio_write_multi() + extern void bcm2835_gpio_set_multi(uint32_t mask); + + /// Sets any of the first 32 GPIO output pins specified in the mask to + /// LOW. + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \sa bcm2835_gpio_write_multi() + extern void bcm2835_gpio_clr_multi(uint32_t mask); + + /// Reads the current level on the specified + /// pin and returns either HIGH or LOW. Works whether or not the pin + /// is an input or an output. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return the current level either HIGH or LOW + extern uint8_t bcm2835_gpio_lev(uint8_t pin); + + /// Event Detect Status. + /// Tests whether the specified pin has detected a level or edge + /// as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), + /// bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen(). + /// Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin); + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return HIGH if the event detect status for the given pin is true. + extern uint8_t bcm2835_gpio_eds(uint8_t pin); + + /// Sets the Event Detect Status register for a given pin to 1, + /// which has the effect of clearing the flag. Use this afer seeing + /// an Event Detect Status on the pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_set_eds(uint8_t pin); + + /// Enable Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a ?011? pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_ren(uint8_t pin); + + /// Disable Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_ren(uint8_t pin); + + /// Enable Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a ?100? pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_fen(uint8_t pin); + + /// Disable Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_fen(uint8_t pin); + + /// Enable High Detect Enable for the specified pin. + /// When a HIGH level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_hen(uint8_t pin); + + /// Disable High Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_hen(uint8_t pin); + + /// Enable Low Detect Enable for the specified pin. + /// When a LOW level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_len(uint8_t pin); + + /// Disable Low Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_len(uint8_t pin); + + /// Enable Asynchronous Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// rising edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_aren(uint8_t pin); + + /// Disable Asynchronous Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_aren(uint8_t pin); + + /// Enable Asynchronous Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// falling edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_afen(uint8_t pin); + + /// Disable Asynchronous Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_afen(uint8_t pin); + + /// Sets the Pull-up/down register for the given pin. This is + /// used with bcm2835_gpio_pudclk() to set the Pull-up/down resistor for the given pin. + /// However, it is usually more convenient to use bcm2835_gpio_set_pud(). + /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + /// \sa bcm2835_gpio_set_pud() + extern void bcm2835_gpio_pud(uint8_t pud); + + /// Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] on HIGH to clock the value from bcm2835_gpio_pud() into the pin. + /// LOW to remove the clock. + /// \sa bcm2835_gpio_set_pud() + extern void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on); + + /// Reads and returns the Pad Control for the given GPIO group. + /// \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* + /// \return Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup + extern uint32_t bcm2835_gpio_pad(uint8_t group); + + /// Sets the Pad Control for the given GPIO group. + /// \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* + /// \param[in] control Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup. Note + /// that it is not necessary to include BCM2835_PAD_PASSWRD in the mask as this + /// is automatically included. + extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control); + + /// Delays for the specified number of milliseconds. + /// Uses nanosleep(), and therefore does not use CPU until the time is up. + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + /// If the interval specified in req is not an exact multiple of the granularity + /// underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// \param[in] millis Delay in milliseconds + extern void bcm2835_delay (unsigned int millis); + + /// Delays for the specified number of microseconds. + /// Uses a combination of nanosleep() and a busy wait loop on the BCM2835 system timers, + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + /// If the interval specified in req is not an exact multiple of the granularity + /// underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// For times less than about 450 microseconds, uses a busy wait on the System Timer. + /// It is reported that a delay of 0 microseconds on RaspberryPi will in fact + /// result in a delay of about 80 microseconds. Your mileage may vary. + /// \param[in] micros Delay in microseconds + extern void bcm2835_delayMicroseconds (uint64_t micros); + + /// Indicate the number of milliseconds since startup of PI + /// This function is like the Arduino millis function + /// \return Number of milliseconds + extern unsigned int bcm2835_millis(void); + + /// Sets the output state of the specified pin + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write(uint8_t pin, uint8_t on); + + /// Sets any of the first 32 GPIO output pins specified in the mask to the state given by on + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on); + + /// Sets the first 32 GPIO output pins specified in the mask to the value given by value + /// \param[in] value values required for each bit masked in by mask, eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask); + + /// Sets the Pull-up/down mode for the specified pin. This is more convenient than + /// clocking the mode in with bcm2835_gpio_pud() and bcm2835_gpio_pudclk(). + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); + + /// @} + + /// \defgroup spi SPI access + /// These functions let you use SPI0 (Serial Peripheral Interface) to + /// interface with an external SPI device. + /// @{ + + /// Start SPI operations. + /// Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// to alternate function ALT0, which enables those pins for SPI interface. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// by default PI hardware driven using CE0 + /// You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to + /// their default functions + /// \sa bcm2835_spi_end() + extern void bcm2835_spi_begin(uint8_t cs); + + /// End SPI operations. + /// SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// are returned to their default INPUT behaviour. + extern void bcm2835_spi_end(void); + + /// Sets the SPI custom Chip Select pin to correct level + /// Defaults to + /// \param[in] level The desired level, LOW or HIGH, + extern void bcm2835_spi_setChipSelect(uint8_t level); + + /// Sets the SPI bit order + /// NOTE: has no effect. Not supported by SPI0. + /// Defaults to + /// \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, + /// see \ref bcm2835SPIBitOrder + extern void bcm2835_spi_setBitOrder(uint8_t order); + + /// Sets the SPI clock divider and therefore the + /// SPI clock speed. + /// \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, + /// see \ref bcm2835SPIClockDivider + extern void bcm2835_spi_setClockDivider(uint16_t divider); + + /// Sets the SPI data mode + /// Sets the clock polariy and phase + /// \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, + /// see \ref bcm2835SPIMode + extern void bcm2835_spi_setDataMode(uint8_t mode); + + /// Sets the chip select pin(s) + /// When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the + /// transfer. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + /// or one of One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + extern void bcm2835_spi_chipSelect(uint8_t cs); + + /// Sets the chip select pin polarity for a given pin + /// When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) + /// will be asserted to the + /// value given by active. When transfers are not happening, the chip select pin(s) + /// return to the complement (inactive) value. + /// \param[in] cs The chip select pin to affect + /// \param[in] active Whether the chip select pin is to be active HIGH + extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active); + + /// Transfers one byte to and from the currently selected SPI slave. + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// during the transfer. + /// Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. + /// Returns the read data byte from the slave. + /// Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + /// \param[in] value The 8 bit data byte to write to MOSI + /// \return The 8 bit byte simultaneously read from MISO + /// \sa bcm2835_spi_transfern() + extern uint8_t bcm2835_spi_transfer(uint8_t value); + + /// Transfers any number of bytes to and from the currently selected SPI slave. + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// during the transfer. + /// Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO. + /// The data read read from the slave is placed into rbuf. rbuf must be at least len bytes long + /// Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + /// \param[in] tbuf Buffer of bytes to send. + /// \param[out] rbuf Received bytes will by put in this buffer + /// \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send/received + /// \sa bcm2835_spi_transfer() + extern void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len); + + /// Transfers any number of bytes to and from the currently selected SPI slave + /// using bcm2835_spi_transfernb. + /// The returned data from the slave replaces the transmitted data in the buffer. + /// \param[in,out] buf Buffer of bytes to send. Received bytes will replace the contents + /// \param[in] len Number of bytes int eh buffer, and the number of bytes to send/received + /// \sa bcm2835_spi_transfer() + extern void bcm2835_spi_transfern(char* buf, uint32_t len); + + /// Transfers any number of bytes to the currently selected SPI slave. + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// during the transfer. + /// \param[in] buf Buffer of bytes to send. + /// \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send + extern void bcm2835_spi_writenb(char* buf, uint32_t len); + + /// @} + + /// \defgroup i2c I2C access + /// These functions let you use I2C (The Broadcom Serial Control bus with the Philips + /// I2C bus/interface version 2.1 January 2000.) to interface with an external I2C device. + /// @{ + + /// Start I2C operations. + /// Forces RPi I2C pins P1-03 (SDA) and P1-05 (SCL) + /// to alternate function ALT0, which enables those pins for I2C interface. + /// You should call bcm2835_i2c_end() when all I2C functions are complete to return the pins to + /// their default functions + /// \sa bcm2835_i2c_end() + extern void bcm2835_i2c_begin(void); + + /// End I2C operations. + /// I2C pins P1-03 (SDA) and P1-05 (SCL) + /// are returned to their default INPUT behaviour. + extern void bcm2835_i2c_end(void); + + /// Sets the I2C slave address. + /// \param[in] addr The I2C slave address. + extern void bcm2835_i2c_setSlaveAddress(uint8_t addr); + + /// Sets the I2C clock divider and therefore the I2C clock speed. + /// \param[in] divider The desired I2C clock divider, one of BCM2835_I2C_CLOCK_DIVIDER_*, + /// see \ref bcm2835I2CClockDivider + extern void bcm2835_i2c_setClockDivider(uint16_t divider); + + /// Sets the I2C clock divider by converting the baudrate parameter to + /// the equivalent I2C clock divider. ( see \sa bcm2835_i2c_setClockDivider) + /// For the I2C standard 100khz you would set baudrate to 100000 + /// The use of baudrate corresponds to its use in the I2C kernel device + /// driver. (Of course, bcm2835 has nothing to do with the kernel driver) + extern void bcm2835_i2c_set_baudrate(uint32_t baudrate); + + /// Transfers any number of bytes to the currently selected I2C slave. + /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) + /// \param[in] buf Buffer of bytes to send. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to send. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_write(const char * buf, uint32_t len); + + /// Transfers any number of bytes from the currently selected I2C slave. + /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_read(char* buf, uint32_t len); + + /// Allows reading from I2C slaves that require a repeated start (without any prior stop) + /// to read after the required slave register has been set. For example, the popular + /// MPL3115A2 pressure and temperature sensor. Note that your device must support or + /// require this mode. If your device does not require this mode then the standard + /// combined: + /// \sa bcm2835_i2c_write + /// \sa bcm2835_i2c_read + /// are a better choice. + /// Will read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + /// \param[in] regaddr Buffer containing the slave register you wish to read from. + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len); + + /// Allows sending an arbitrary number of bytes to I2C slaves before issuing a repeated + /// start (with no prior stop) and reading a response. + /// Necessary for devices that require such behavior, such as the MLX90620. + /// Will write to and read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + /// \param[in] cmds Buffer containing the bytes to send before the repeated start condition. + /// \param[in] cmds_len Number of bytes to send from cmds buffer + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] buf_len Number of bytes to receive in the buf buffer. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len); + + /// @} + + /// \defgroup st System Timer access + /// Allows access to and delays using the System Timer Counter. + /// @{ + + /// Read the System Timer Counter register. + /// \return the value read from the System Timer Counter Lower 32 bits register + extern uint64_t bcm2835_st_read(void); + + /// Delays for the specified number of microseconds with offset. + /// \param[in] offset_micros Offset in microseconds + /// \param[in] micros Delay in microseconds + extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros); + + /// @} + + /// \defgroup pwm Pulse Width Modulation + /// Allows control of 2 independent PWM channels. A limited subset of GPIO pins + /// can be connected to one of these 2 channels, allowing PWM control of GPIO pins. + /// You have to set the desired pin into a particular Alt Fun to PWM output. See the PWM + /// documentation on the Main Page. + /// @{ + + /// Sets the PWM clock divisor, + /// to control the basic PWM pulse widths. + /// \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common + /// values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider + extern void bcm2835_pwm_set_clock(uint32_t divisor); + + /// Sets the mode of the given PWM channel, + /// allowing you to control the PWM mode and enable/disable that channel + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode. + /// \param[in] enabled Set true to enable this channel and produce PWM pulses. + extern void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled); + + /// Sets the maximum range of the PWM output. + /// The data value can vary between 0 and this range to control PWM output + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] range The maximum value permitted for DATA. + extern void bcm2835_pwm_set_range(uint8_t channel, uint32_t range); + + /// Sets the PWM pulse ratio to emit to DATA/RANGE, + /// where RANGE is set by bcm2835_pwm_set_range(). + /// \param[in] channel The PWM channel. 0 or 1. + /// \param[in] data Controls the PWM output ratio as a fraction of the range. + /// Can vary from 0 to RANGE. + extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data); + + /// @} +#ifdef __cplusplus +} +#endif + +#endif // BCM2835_H + +/// @example blink.c +/// Blinks RPi GPIO pin 11 on and off + +/// @example input.c +/// Reads the state of an RPi input pin + +/// @example event.c +/// Shows how to use event detection on an input pin + +/// @example spi.c +/// Shows how to use SPI interface to transfer a byte to and from an SPI device + +/// @example spin.c +/// Shows how to use SPI interface to transfer a number of bytes to and from an SPI device + +/// @example pwm.c +/// Shows how to use PWM to control GPIO pins + +/// @example i2c.c +/// Command line utility for executing i2c commands with the +/// Broadcom bcm2835. Contributed by Shahrooz Shahparnia. + +/// example gpio.c +/// Command line utility for executing gpio commands with the +/// Broadcom bcm2835. Contributed by Shahrooz Shahparnia. diff --git a/RPi/RF24/examples/Makefile b/RPi/RF24/examples/Makefile new file mode 100644 index 0000000..73da217 --- /dev/null +++ b/RPi/RF24/examples/Makefile @@ -0,0 +1,40 @@ +############################################################################# +# +# Makefile for librf24 examples on Raspberry Pi +# +# License: GPL (General Public License) +# Author: gnulnulf +# Date: 2013/02/07 (version 1.0) +# +# Description: +# ------------ +# use make all and make install to install the examples +# You can change the install directory by editing the prefix line +# +prefix := /usr/local + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s +#CCFLAGS= + +# define all programs +#PROGRAMS = scanner pingtest gettingstarted +PROGRAMS = gettingstarted gettingstarted_call_response transfer pingpair_dyn +SOURCES = ${PROGRAMS:=.cpp} + +all: ${PROGRAMS} + +${PROGRAMS}: ${SOURCES} + g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm $@.cpp -o $@ + +clean: + rm -rf $(PROGRAMS) + +install: all + test -d $(prefix) || mkdir $(prefix) + test -d $(prefix)/bin || mkdir $(prefix)/bin + for prog in $(PROGRAMS); do \ + install -m 0755 $$prog $(prefix)/bin; \ + done + +.PHONY: install diff --git a/RPi/RF24/examples/extra/Makefile b/RPi/RF24/examples/extra/Makefile new file mode 100644 index 0000000..c313b5b --- /dev/null +++ b/RPi/RF24/examples/extra/Makefile @@ -0,0 +1,40 @@ +############################################################################# +# +# Makefile for librf24 examples on Raspberry Pi +# +# License: GPL (General Public License) +# Author: gnulnulf +# Date: 2013/02/07 (version 1.0) +# +# Description: +# ------------ +# use make all and make install to install the examples +# You can change the install directory by editing the prefix line +# +prefix := /usr/local + +# The recommended compiler flags for the Raspberry Pi +CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s +#CCFLAGS= + +# define all programs +PROGRAMS = rpi-hub scanner + +SOURCES = ${PROGRAMS:=.cpp} + +all: ${PROGRAMS} + +${PROGRAMS}: ${SOURCES} + g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm $@.cpp -o $@ + +clean: + rm -rf $(PROGRAMS) + +install: all + test -d $(prefix) || mkdir $(prefix) + test -d $(prefix)/bin || mkdir $(prefix)/bin + for prog in $(PROGRAMS); do \ + install -m 0755 $$prog $(prefix)/bin; \ + done + +.PHONY: install diff --git a/RPi/RF24/examples/extra/rpi-hub.cpp b/RPi/RF24/examples/extra/rpi-hub.cpp new file mode 100644 index 0000000..4785148 --- /dev/null +++ b/RPi/RF24/examples/extra/rpi-hub.cpp @@ -0,0 +1,134 @@ +/* + * + * Filename : rpi-hub.cpp + * + * This program makes the RPi as a hub listening to all six pipes from the remote sensor nodes ( usually Arduino ) + * and will return the packet back to the sensor on pipe0 so that the sender can calculate the round trip delays + * when the payload matches. + * + * I encounter that at times, it also receive from pipe7 ( or pipe0 ) with content of FFFFFFFFF that I will not sent + * back to the sender + * + * Refer to RF24/examples/rpi_hub_arduino/ for the corresponding Arduino sketches to work with this code. + * + * + * CE is not used and CSN is GPIO25 (not pinout) + * + * Refer to RPi docs for GPIO numbers + * + * Author : Stanley Seow + * e-mail : stanleyseow@gmail.com + * date : 6th Mar 2013 + * + * 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + * Modified to use with Arduipi board http://hallard.me/arduipi + * Changed to use modified bcm2835 and RF24 library + * + * + */ + +#include +#include +#include "../RF24.h" + +using namespace std; + +// Radio pipe addresses for the 2 nodes to communicate. +// First pipe is for writing, 2nd, 3rd, 4th, 5th & 6th is for reading... +const uint64_t pipes[6] = + { 0xF0F0F0F0D2LL, 0xF0F0F0F0E1LL, + 0xF0F0F0F0E2LL, 0xF0F0F0F0E3LL, + 0xF0F0F0F0F1, 0xF0F0F0F0F2 + }; + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + +int main(int argc, char** argv) +{ + uint8_t len; + + // Refer to RF24.h or nRF24L01 DS for settings + radio.begin(); + radio.enableDynamicPayloads(); + radio.setAutoAck(1); + radio.setRetries(15,15); + radio.setDataRate(RF24_1MBPS); + radio.setPALevel(RF24_PA_MAX); + radio.setChannel(76); + radio.setCRCLength(RF24_CRC_16); + + // Open 6 pipes for readings ( 5 plus pipe0, also can be used for reading ) + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + radio.openReadingPipe(2,pipes[2]); + radio.openReadingPipe(3,pipes[3]); + radio.openReadingPipe(4,pipes[4]); + radio.openReadingPipe(5,pipes[5]); + + // + // Start listening + // + radio.startListening(); + + // + // Dump the configuration of the rf unit for debugging + // + radio.printDetails(); + + printf("Output below : \n"); + delay(1); + + while(1) + { + char receivePayload[32]; + uint8_t pipe = 1; + + // Start listening + radio.startListening(); + + while ( radio.available(&pipe) ) + { + len = radio.getDynamicPayloadSize(); + radio.read( receivePayload, len ); + + // Display it on screen + printf("Recv: size=%i payload=%s pipe=%i",len,receivePayload,pipe); + + // Send back payload to sender + radio.stopListening(); + + // if pipe is 7, do not send it back + if ( pipe != 7 ) + { + radio.write(receivePayload,len); + receivePayload[len]=0; + printf("\t Send: size=%i payload=%s pipe:%i\n",len,receivePayload,pipe); + } + else + { + printf("\n"); + } + + pipe++; + + // reset pipe to 0 + if ( pipe > 6 ) + pipe = 0; + } + + delayMicroseconds(20); + } + + return 0; +} + diff --git a/RPi/RF24/examples/extra/scanner.cpp b/RPi/RF24/examples/extra/scanner.cpp new file mode 100644 index 0000000..1390495 --- /dev/null +++ b/RPi/RF24/examples/extra/scanner.cpp @@ -0,0 +1,147 @@ +/* + Copyright (C) 2011 J. Coliz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 and RF24 library + + */ + +/** + * Channel scanner + * + * Example to detect interference on the various channels available. + * This is a good diagnostic tool to check whether you're picking a + * good channel for your application. + * + * Inspired by cpixip. + * See http://arduino.cc/forum/index.php/topic,54795.0.html + */ + +#include +#include +#include "../RF24.h" + +using namespace std; + +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + +// +// Channel info +// +//const uint8_t num_channels = 128; +const uint8_t num_channels = 120; +uint8_t values[num_channels]; + + +const int num_reps = 100; +int reset_array=0; + + +int main(int argc, char** argv) +{ + // + // Print preamble + // + + //Serial.begin(57600); + //printf_begin(); + printf("RF24/examples/scanner/\n"); + + // + // Setup and configure rf radio + // + radio.begin(); + + radio.setAutoAck(false); + + // Get into standby mode + radio.startListening(); + radio.stopListening(); + + radio.printDetails(); + + // Print out header, high then low digit + int i = 0; + + while ( i < num_channels ) + { + printf("%x",i>>4); + ++i; + } + printf("\n"); + + i = 0; + while ( i < num_channels ) + { + printf("%x",i&0xf); + ++i; + } + printf("\n"); + + // forever loop + while(1) + { + if ( reset_array == 1 ) + { + // Clear measurement values + memset(values,0,sizeof(values)); + printf("\n"); + } + + // Scan all channels num_reps times + int i = num_channels; + while (i--) + { + // Select this channel + radio.setChannel(i); + + // Listen for a little + radio.startListening(); + delayMicroseconds(128); + //radio.stopListening(); + + // Did we get a carrier? + if ( radio.testCarrier() ) + ++values[i]; + if ( values[i] == 0xf ) + { + reset_array = 2; + } + radio.stopListening(); + } + + // Print out channel measurements, clamped to a single hex digit + i = 0; + while ( i < num_channels ) + { + printf("%x",min(0xf,(values[i]&0xf))); + ++i; + } + + printf("\n"); + } + + return 0; +} + +// vim:ai:cin:sts=2 sw=2 ft=cpp diff --git a/RPi/RF24/examples/gettingstarted.cpp b/RPi/RF24/examples/gettingstarted.cpp new file mode 100644 index 0000000..d4aeaa3 --- /dev/null +++ b/RPi/RF24/examples/gettingstarted.cpp @@ -0,0 +1,188 @@ +/* + Copyright (C) 2011 J. Coliz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + + 03/17/2013 : Charles-Henri Hallard (http://hallard.me) + Modified to use with Arduipi board http://hallard.me/arduipi + Changed to use modified bcm2835 and RF24 library +TMRh20 2014 - Updated to work with optimized RF24 Arduino library + + */ + +/** + * Example RF Radio Ping Pair + * + * This is an example of how to use the RF24 class on RPi, communicating to an Arduino running + * the GettingStarted sketch. + */ + +#include +#include +#include +#include +#include "./RF24.h" + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); + + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; + +int main(int argc, char** argv){ + + + // Print preamble: + printf("RF24/examples/pingtest/\n"); + + // Setup and configure rf radio + radio.begin(); + + // optionally, increase the delay between retries & # of retries + radio.setRetries(15,15); + + // Dump the configuration of the rf unit for debugging + radio.printDetails(); + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit) \n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + + if ( role == role_ping_out ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); + + } + + // forever loop + while (1) + { + if (role == role_ping_out) + { + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + + printf("Now sending...\n"); + unsigned long time = millis(); + + bool ok = radio.write( &time, sizeof(unsigned long) ); + + if (!ok){ + printf("failed.\n"); + } + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout (250ms) + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) { + // by bcatalin ยป Thu Feb 14, 2013 11:26 am + //delay(5); //add a small delay to let radio.available to check payload + if (millis() - started_waiting_at > 200 ) + timeout = true; + } + + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n"); + } + else + { + // Grab the response, compare, and send to debugging spew + unsigned long got_time; + radio.read( &got_time, sizeof(unsigned long) ); + + // Spew it + printf("Got response %lu, round-trip delay: %lu\n",got_time,millis()-got_time); + } + + // Try again 1s later + // delay(1000); + sleep(1); + + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + //printf("Check available...\n"); + //delay(3); + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + unsigned long got_time; + + + // Fetch the payload, and see if this was the last one. + radio.read( &got_time, sizeof(unsigned long) ); + + radio.stopListening(); + //delay(1); + // Seem to need a delay, or the RPi is too quick + radio.write( &got_time, sizeof(unsigned long) ); + + //delay(1); + // Now, resume listening so we catch the next packets. + radio.startListening(); + //delay(1); + // Spew it + printf("Got payload(%d) %lu...\n",sizeof(unsigned long), got_time); + } + //delay(5); + } + } // forever loop + + return 0; +} + diff --git a/RPi/RF24/examples/gettingstarted_call_response.cpp b/RPi/RF24/examples/gettingstarted_call_response.cpp new file mode 100644 index 0000000..4444191 --- /dev/null +++ b/RPi/RF24/examples/gettingstarted_call_response.cpp @@ -0,0 +1,138 @@ +/* +TMRh20 2014 - Updated to work with optimized RF24 Arduino library +*/ + + +/** + * Example for efficient call-response using ack-payloads + * + * This example continues to make use of all the normal functionality of the radios including + * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well. + * This allows very fast call-response communication, with the responding radio never having to + * switch out of Primary Receiver mode to send back a payload, but having the option to switch to + * primary transmitter if wanting to initiate communication instead of respond to a commmunication. + */ + +#include +#include +#include +#include +#include "./RF24.h" + +using namespace std; + +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); + + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0, role = 0; + +uint8_t counter = 1; // A single byte to keep track of the data being sent back and forth + + +int main(int argc, char** argv){ + + + printf("RF24/examples/gettingstarted_call_response\n"); + + radio.begin(); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.enableAckPayload(); // Allow optional ack payloads + radio.setRetries(1,15); // Smallest time between retries, max no. of retries + radio.setPayloadSize(1); // Here we are sending 1-byte payloads to test the call-response speed + //radio.powerUp(); + radio.printDetails(); // Dump the configuration of the rf unit for debugging + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit)\n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + // This simple sketch opens two pipes for these two nodes to communicate + // back and forth. + + if ( role == role_ping_out ) { + radio.openWritingPipe(addresses[0]); + radio.openReadingPipe(1,addresses[1]); + }else{ + radio.openWritingPipe(addresses[1]); + radio.openReadingPipe(1,addresses[0]); + radio.startListening(); + } + +// forever loop +while (1){ + + +/****************** Ping Out Role ***************************/ + + if (role == role_ping_out){ // Radio is in ping mode + + uint8_t gotByte; // Initialize a variable for the incoming response + + radio.stopListening(); // First, stop listening so we can talk. + printf("Now sending %d as payload. ",counter); // Use a simple byte counter as payload + unsigned long time = millis(); // Record the current microsecond count + + if ( radio.write(&counter,1) ){ // Send the counter variable to the other radio + if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank + printf("Got blank response. round-trip delay: %lu ms\n\r",millis()-time); + }else{ + while(radio.available() ){ // If an ack with payload was received + radio.read( &gotByte, 1 ); // Read it, and display the response time + printf("Got response %d, round-trip delay: %lu ms\n\r",gotByte,millis()-time); + counter++; // Increment the counter variable + } + } + + }else{ printf("Sending failed.\n\r"); } // If no ack response, sending failed + + sleep(1); // Try again later + } + +/****************** Pong Back Role ***************************/ + + if ( role == role_pong_back ) { + uint8_t pipeNo, gotByte; // Declare variables for the pipe and the byte received + while( radio.available(&pipeNo)){ // Read all available payloads + radio.read( &gotByte, 1 ); + // Since this is a call-response. Respond directly with an ack payload. + // Ack payloads are much more efficient than switching to transmit mode to respond to a call + radio.writeAckPayload(pipeNo,&gotByte, 1 ); // This can be commented out to send empty payloads. + printf("Sent response %d \n\r", gotByte); + } + } + +} //while 1 +} //main + + diff --git a/RPi/RF24/examples/pingpair_dyn.cpp b/RPi/RF24/examples/pingpair_dyn.cpp new file mode 100644 index 0000000..b7e4d70 --- /dev/null +++ b/RPi/RF24/examples/pingpair_dyn.cpp @@ -0,0 +1,181 @@ +/* + TMRh20 2014 - Optimized RF24 Library Fork +*/ + +/** + * Example using Dynamic Payloads + * + * This is an example of how to use payloads of a varying (dynamic) size. + */ + +#include +#include +#include +#include +#include "./RF24.h" + + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; + +const int min_payload_size = 4; +const int max_payload_size = 32; +const int payload_size_increments_by = 1; +int next_payload_size = min_payload_size; + +char receive_payload[max_payload_size+1]; // +1 to allow room for a terminating NULL char + +int main(int argc, char** argv){ + + + // Print preamble: + printf("RF24/examples/pingpair_dyn/\n"); + + // Setup and configure rf radio + radio.begin(); + radio.enableDynamicPayloads(); + radio.setRetries(5,15); + radio.printDetails(); + + +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit) \n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + + if ( role == role_ping_out ) { + radio.openWritingPipe(pipes[0]); + radio.openReadingPipe(1,pipes[1]); + } else { + radio.openWritingPipe(pipes[1]); + radio.openReadingPipe(1,pipes[0]); + radio.startListening(); + } + + +// forever loop + while (1) + { + +if (role == role_ping_out) + { + // The payload will always be the same, what will change is how much of it we send. + static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012"; + + // First, stop listening so we can talk. + radio.stopListening(); + + // Take the time, and send it. This will block until complete + printf("Now sending length %i...",next_payload_size); + radio.write( send_payload, next_payload_size ); + + // Now, continue listening + radio.startListening(); + + // Wait here until we get a response, or timeout + unsigned long started_waiting_at = millis(); + bool timeout = false; + while ( ! radio.available() && ! timeout ) + if (millis() - started_waiting_at > 500 ) + timeout = true; + + // Describe the results + if ( timeout ) + { + printf("Failed, response timed out.\n\r"); + } + else + { + // Grab the response, compare, and send to debugging spew + uint8_t len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got response size=%i value=%s\n\r",len,receive_payload); + } + + // Update size for next time. + next_payload_size += payload_size_increments_by; + if ( next_payload_size > max_payload_size ) + next_payload_size = min_payload_size; + + // Try again 1s later + delay(100); + } + + // + // Pong back role. Receive each packet, dump it out, and send it back + // + + if ( role == role_pong_back ) + { + // if there is data ready + if ( radio.available() ) + { + // Dump the payloads until we've gotten everything + uint8_t len; + + while (radio.available()) + { + // Fetch the payload, and see if this was the last one. + len = radio.getDynamicPayloadSize(); + radio.read( receive_payload, len ); + + // Put a zero at the end for easy printing + receive_payload[len] = 0; + + // Spew it + printf("Got payload size=%i value=%s\n\r",len,receive_payload); + } + + // First, stop listening so we can talk + radio.stopListening(); + + // Send the final one back. + radio.write( receive_payload, len ); + printf("Sent response.\n\r"); + + // Now, resume listening so we catch the next packets. + radio.startListening(); + } + } +} +} + + diff --git a/RPi/RF24/examples/transfer.cpp b/RPi/RF24/examples/transfer.cpp new file mode 100644 index 0000000..c4e4ecb --- /dev/null +++ b/RPi/RF24/examples/transfer.cpp @@ -0,0 +1,148 @@ +/* +TMRh20 2014 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ + +/** General Data Transfer Rate Test + * This example demonstrates basic data transfer functionality with the + updated library. This example will display the transfer rates acheived using + the slower form of high-speed transfer using blocking-writes. + */ + +#include +#include +#include +#include +#include "./RF24.h" + + +using namespace std; +// +// Hardware configuration +// + +// CE Pin, CSN Pin, SPI Speed + +// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz +//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ); + +// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz +//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); + +// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz +RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_26, BCM2835_SPI_SPEED_8MHZ); + + + +// Radio pipe addresses for the 2 nodes to communicate. +const uint64_t addresses[2] = { 0xABCDABCD71LL, 0x544d52687CLL }; + +bool role_ping_out = 1, role_pong_back = 0; +bool role = 0; +uint8_t data[32]; +unsigned long startTime, stopTime, counter, rxTimer=0; + +int main(int argc, char** argv){ + + + // Print preamble: + + printf("RF24/examples/Transfer/\n"); + + radio.begin(); // Setup and configure rf radio + radio.setChannel(1); + radio.setPALevel(RF24_PA_MAX); + radio.setDataRate(RF24_2MBPS); + radio.setAutoAck(1); // Ensure autoACK is enabled + radio.setRetries(2,15); // Optionally, increase the delay between retries & # of retries + radio.setCRCLength(RF24_CRC_8); + radio.printDetails(); +/********* Role chooser ***********/ + + printf("\n ************ Role Setup ***********\n"); + string input = ""; + char myChar = {0}; + cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit)\n>"; + getline(cin,input); + + if(input.length() == 1) { + myChar = input[0]; + if(myChar == '0'){ + cout << "Role: Pong Back, awaiting transmission " << endl << endl; + }else{ cout << "Role: Ping Out, starting transmission " << endl << endl; + role = role_ping_out; + } + } +/***********************************/ + + if ( role == role_ping_out ) { + radio.openWritingPipe(addresses[0]); + radio.openReadingPipe(1,addresses[1]); + } else { + radio.openWritingPipe(addresses[1]); + radio.openReadingPipe(1,addresses[0]); + radio.startListening(); + } + + + for(int i=0; i<32; i++){ + data[i] = rand() % 255; //Load the buffer with random data + } + + // forever loop + while (1){ + + if (role == role_ping_out){ + sleep(2); + printf("Initiating Basic Data Transfer\n\r"); + + long int cycles = 10000; //Change this to a higher or lower number. + + startTime = millis(); + + for(int i=0; i 1000){ + rxTimer = millis(); + printf("Rate: "); + float numBytes = counter*32; + printf("%.2f KB/s \n\r",numBytes/1000); + printf("Payload Count: %lu \n\r", counter); + counter = 0; + } + } + +} // loop +} // main + + + + + + diff --git a/RPi/RF24/nRF24L01.h b/RPi/RF24/nRF24L01.h new file mode 100644 index 0000000..ba83534 --- /dev/null +++ b/RPi/RF24/nRF24L01.h @@ -0,0 +1,127 @@ +/* + Copyright (c) 2007 Stefan Engelke + Portions Copyright (C) 2011 Greg Copeland + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* Memory Map */ +#define CONFIG 0x00 +#define EN_AA 0x01 +#define EN_RXADDR 0x02 +#define SETUP_AW 0x03 +#define SETUP_RETR 0x04 +#define RF_CH 0x05 +#define RF_SETUP 0x06 +#define STATUS 0x07 +#define OBSERVE_TX 0x08 +#define CD 0x09 +#define RX_ADDR_P0 0x0A +#define RX_ADDR_P1 0x0B +#define RX_ADDR_P2 0x0C +#define RX_ADDR_P3 0x0D +#define RX_ADDR_P4 0x0E +#define RX_ADDR_P5 0x0F +#define TX_ADDR 0x10 +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define FIFO_STATUS 0x17 +#define DYNPD 0x1C +#define FEATURE 0x1D + +/* Bit Mnemonics */ +#define MASK_RX_DR 6 +#define MASK_TX_DS 5 +#define MASK_MAX_RT 4 +#define EN_CRC 3 +#define CRCO 2 +#define PWR_UP 1 +#define PRIM_RX 0 +#define ENAA_P5 5 +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW 0 +#define ARD 4 +#define ARC 0 +#define PLL_LOCK 4 +#define RF_DR 3 +#define RF_PWR 6 +#define RX_DR 6 +#define TX_DS 5 +#define MAX_RT 4 +#define RX_P_NO 1 +#define TX_FULL 0 +#define PLOS_CNT 4 +#define ARC_CNT 0 +#define TX_REUSE 6 +#define FIFO_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 +#define EN_DPL 2 +#define EN_ACK_PAY 1 +#define EN_DYN_ACK 0 + +/* Instruction Mnemonics */ +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define NOP 0xFF + +/* Non-P omissions */ +#define LNA_HCURR 0 + +/* P model memory Map */ +#define RPD 0x09 +#define W_TX_PAYLOAD_NO_ACK 0xB0 + +/* P model bit Mnemonics */ +#define RF_DR_LOW 5 +#define RF_DR_HIGH 3 +#define RF_PWR_LOW 1 +#define RF_PWR_HIGH 2 diff --git a/RPi/RF24/readme.md b/RPi/RF24/readme.md new file mode 100644 index 0000000..9c4ce74 --- /dev/null +++ b/RPi/RF24/readme.md @@ -0,0 +1,108 @@ + +Optimized Raspberry Pi RF24 and RF24 Network Libraries +============================================= + General Documentation: http://tmrh20.github.io + + Library functions are mostly the same. + See the included examples for RPi specific usage + +##Raspberry Pi - PreConfig + + +####Possible pre-configuration: + If SPI is not already enabled, load it on boot: +``` + sudo raspi-config +``` +A. Update the tool via the menu as required +B. Select Advanced and enable the SPI kernel module +C. Update other software and libraries: +``` + sudo apt-get update + sudo apt-get upgrade +``` +RPi - RF24 Quick-Start +====================== +A. Make a directory to contain the RF24 and possibly RF24Network lib and enter it: +``` + mkdir ~/rf24libs + cd ~/rf24libs +``` +B. Clone the RF24 Repo +``` + git clone https://github.com/tmrh20/RF24.git rtemp +``` +C. Copy the RPi library folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24 ./ + rm -r rtemp + cd RF24 +``` +D. Build the library, and run an example file: +``` + sudo make + cd examples + sudo make + sudo ./gettingstarted +``` + +RPi - RF24Network Quick-Start +============================= +A. Enter the same directory that contains the RF24 library folder +``` + cd ~/rf24libs +``` +B. Clone the RF24Network Repo +``` + git clone https://github.com/tmrh20/RF24Network.git ntemp +``` +C. Copy the RF24Network folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24Network ./ + rm -r ntemp + cd RF24Network +``` +D. Build the library +``` + sudo make + cd examples + sudo make + sudo ./helloworld_rx OR sudo ./helloworld_tx +``` + +Connection Info +=============== + +Using pin 15/GPIO 22 for CE, pin 24/GPIO8 (CE0) for CSN + +**Constructor:** +``` +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); +``` + +**Pins:** + + |NRF24L01 | RPI | P1 Connector | + |----- |----------- | -------------| + |VCC: | rpi-3v3 | (17) | + |GND: | rpi-gnd | (25) | + |CE: | rpi-gpio22 | (15) | + |CSN: | rpi-gpio8 | (24) | + |SCK: | rpi-sckl | (23) | + |MOSI: | rpi-mosi | (19) | + |MISO: | rpi-miso | (21) | + + +**************** + + +Based on the arduino lib from J. Coliz . +the library was berryfied by Purinda Gunasekara . +then forked from github stanleyseow/RF24 to https://github.com/jscrane/RF24-rpi +Network lib also based on https://github.com/farconada/RF24Network + +Currently optimized and aligned with Arduino fork of libraries by TMRh20: +https://github.com/tmrh20/RF24/RPi and https://github.com/tmrh20/RF24Network/RPi +Documentation: http://tmrh20.github.io + + diff --git a/RPi/readme.md b/RPi/readme.md new file mode 100644 index 0000000..9c4ce74 --- /dev/null +++ b/RPi/readme.md @@ -0,0 +1,108 @@ + +Optimized Raspberry Pi RF24 and RF24 Network Libraries +============================================= + General Documentation: http://tmrh20.github.io + + Library functions are mostly the same. + See the included examples for RPi specific usage + +##Raspberry Pi - PreConfig + + +####Possible pre-configuration: + If SPI is not already enabled, load it on boot: +``` + sudo raspi-config +``` +A. Update the tool via the menu as required +B. Select Advanced and enable the SPI kernel module +C. Update other software and libraries: +``` + sudo apt-get update + sudo apt-get upgrade +``` +RPi - RF24 Quick-Start +====================== +A. Make a directory to contain the RF24 and possibly RF24Network lib and enter it: +``` + mkdir ~/rf24libs + cd ~/rf24libs +``` +B. Clone the RF24 Repo +``` + git clone https://github.com/tmrh20/RF24.git rtemp +``` +C. Copy the RPi library folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24 ./ + rm -r rtemp + cd RF24 +``` +D. Build the library, and run an example file: +``` + sudo make + cd examples + sudo make + sudo ./gettingstarted +``` + +RPi - RF24Network Quick-Start +============================= +A. Enter the same directory that contains the RF24 library folder +``` + cd ~/rf24libs +``` +B. Clone the RF24Network Repo +``` + git clone https://github.com/tmrh20/RF24Network.git ntemp +``` +C. Copy the RF24Network folder to the current directory, and delete the rest +``` + mv rtemp/RPi/RF24Network ./ + rm -r ntemp + cd RF24Network +``` +D. Build the library +``` + sudo make + cd examples + sudo make + sudo ./helloworld_rx OR sudo ./helloworld_tx +``` + +Connection Info +=============== + +Using pin 15/GPIO 22 for CE, pin 24/GPIO8 (CE0) for CSN + +**Constructor:** +``` +RF24 radio(RPI_V2_GPIO_P1_15,BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ); +``` + +**Pins:** + + |NRF24L01 | RPI | P1 Connector | + |----- |----------- | -------------| + |VCC: | rpi-3v3 | (17) | + |GND: | rpi-gnd | (25) | + |CE: | rpi-gpio22 | (15) | + |CSN: | rpi-gpio8 | (24) | + |SCK: | rpi-sckl | (23) | + |MOSI: | rpi-mosi | (19) | + |MISO: | rpi-miso | (21) | + + +**************** + + +Based on the arduino lib from J. Coliz . +the library was berryfied by Purinda Gunasekara . +then forked from github stanleyseow/RF24 to https://github.com/jscrane/RF24-rpi +Network lib also based on https://github.com/farconada/RF24Network + +Currently optimized and aligned with Arduino fork of libraries by TMRh20: +https://github.com/tmrh20/RF24/RPi and https://github.com/tmrh20/RF24Network/RPi +Documentation: http://tmrh20.github.io + +