Commit 9e717722 authored by akatran's avatar akatran

Merge branch 'master' of https://github.com/TMRh20/RF24

parents a9255c80 a1d346a9
......@@ -66,7 +66,8 @@ CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=$(ARCH) -mtune=arm1176jzf-s
else
DRIVER_DIR=$(ARCH_DIR)/BBB
OBJECTS+=gpio.o compatibility.o
OBJECTS+=gpio.o compatibility.o interrupt.o
SHARED_LINKER_FLAGS+=-pthread
endif
......
......@@ -290,10 +290,12 @@ uint8_t RF24::read_payload(void* buf, uint8_t data_len)
status = *prx++; // 1st byte is status
if (data_len > 0) {
while ( --data_len ) // Decrement before to skip 1st status byte
*current++ = *prx++;
*current = *prx;
}
endTransaction();
#else
......@@ -553,13 +555,13 @@ void RF24::printDetails(void)
print_byte_register(PSTR("EN_RXADDR"),EN_RXADDR);
print_byte_register(PSTR("RF_CH\t"),RF_CH);
print_byte_register(PSTR("RF_SETUP"),RF_SETUP);
print_byte_register(PSTR("CONFIG\t"),CONFIG);
print_byte_register(PSTR("CONFIG\t"),NRF_CONFIG);
print_byte_register(PSTR("DYNPD/FEATURE"),DYNPD,2);
printf_P(PSTR("Data Rate\t = "PRIPSTR"\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()]));
printf_P(PSTR("Model\t\t = "PRIPSTR"\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()]));
printf_P(PSTR("CRC Length\t = "PRIPSTR"\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()]));
printf_P(PSTR("PA Power\t = "PRIPSTR"\r\n"), pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()]));
printf_P(PSTR("Data Rate\t = " PRIPSTR "\r\n"),pgm_read_word(&rf24_datarate_e_str_P[getDataRate()]));
printf_P(PSTR("Model\t\t = " PRIPSTR "\r\n"),pgm_read_word(&rf24_model_e_str_P[isPVariant()]));
printf_P(PSTR("CRC Length\t = " PRIPSTR "\r\n"),pgm_read_word(&rf24_crclength_e_str_P[getCRCLength()]));
printf_P(PSTR("PA Power\t = " PRIPSTR "\r\n"), pgm_read_word(&rf24_pa_dbm_e_str_P[getPALevel()]));
}
......@@ -573,8 +575,6 @@ bool RF24::begin(void)
#if defined (RF24_LINUX)
SPI();
#if defined (MRAA)
GPIO();
gpio.begin(ce_pin,csn_pin);
......@@ -632,8 +632,8 @@ bool RF24::begin(void)
// WARNING: Delay is based on P-variant whereby non-P *may* require different timing.
delay( 5 ) ;
// Reset CONFIG and enable 16-bit CRC.
write_register( CONFIG, 0b00001100 ) ;
// Reset NRF_CONFIG and enable 16-bit CRC.
write_register( NRF_CONFIG, 0b00001100 ) ;
// 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
......@@ -649,8 +649,8 @@ bool RF24::begin(void)
{
p_variant = true ;
}
/*setup = read_register(RF_SETUP);
if( setup == 0b00001110 ) // register default for nRF24L01P
setup = read_register(RF_SETUP);
/*if( setup == 0b00001110 ) // register default for nRF24L01P
{
p_variant = true ;
}*/
......@@ -684,7 +684,7 @@ bool RF24::begin(void)
// 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) );
write_register(NRF_CONFIG, ( read_register(NRF_CONFIG) ) & ~_BV(PRIM_RX) );
// if setup is 0 or ff then there was no response from module
return ( setup != 0 && setup != 0xff );
......@@ -697,7 +697,7 @@ void RF24::startListening(void)
#if !defined (RF24_TINY) && ! defined(LITTLEWIRE)
powerUp();
#endif
write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX));
write_register(NRF_CONFIG, read_register(NRF_CONFIG) | _BV(PRIM_RX));
write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
ce(HIGH);
// Restore the pipe0 adddress, if exists
......@@ -734,7 +734,7 @@ void RF24::stopListening(void)
flush_tx();
}
//flush_rx();
write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) );
write_register(NRF_CONFIG, ( read_register(NRF_CONFIG) ) & ~_BV(PRIM_RX) );
#if defined (RF24_TINY) || defined (LITTLEWIRE)
// for 3 pins solution TX mode is only left with additonal powerDown/powerUp cycle
......@@ -754,7 +754,7 @@ void RF24::stopListening(void)
void RF24::powerDown(void)
{
ce(LOW); // Guarantee CE is low on powerDown
write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP));
write_register(NRF_CONFIG,read_register(NRF_CONFIG) & ~_BV(PWR_UP));
}
/****************************************************************************/
......@@ -762,11 +762,11 @@ void RF24::powerDown(void)
//Power up now. Radio will not power down unless instructed by MCU for config changes etc.
void RF24::powerUp(void)
{
uint8_t cfg = read_register(CONFIG);
uint8_t cfg = read_register(NRF_CONFIG);
// if not powered up then power up and wait for the radio to initialize
if (!(cfg & _BV(PWR_UP))){
write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP));
write_register(NRF_CONFIG, cfg | _BV(PWR_UP));
// For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by mode.
// There must be a delay of Tpd2stby (see Table 16.) after the nRF24L01+ leaves power down mode before
......@@ -803,7 +803,7 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast )
while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) {
#if defined (FAILURE_HANDLING) || defined (RF24_LINUX)
if(millis() - timer > 85){
if(millis() - timer > 95){
errNotify();
#if defined (FAILURE_HANDLING)
return 0;
......@@ -849,7 +849,7 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout )
if(millis() - timer > timeout){ return 0; } //If this payload has exceeded the user-defined timeout, exit and return 0
}
#if defined (FAILURE_HANDLING) || defined (RF24_LINUX)
if(millis() - timer > (timeout+85) ){
if(millis() - timer > (timeout+95) ){
errNotify();
#if defined (FAILURE_HANDLING)
return 0;
......@@ -896,7 +896,7 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast )
//From the user perspective, if you get a 0, just keep trying to send the same payload
}
#if defined (FAILURE_HANDLING) || defined (RF24_LINUX)
if(millis() - timer > 85 ){
if(millis() - timer > 95 ){
errNotify();
#if defined (FAILURE_HANDLING)
return 0;
......@@ -970,7 +970,7 @@ bool RF24::txStandBy(){
return 0;
}
#if defined (FAILURE_HANDLING) || defined (RF24_LINUX)
if( millis() - timeout > 85){
if( millis() - timeout > 95){
errNotify();
#if defined (FAILURE_HANDLING)
return 0;
......@@ -1003,7 +1003,7 @@ bool RF24::txStandBy(uint32_t timeout, bool startTx){
}
}
#if defined (FAILURE_HANDLING) || defined (RF24_LINUX)
if( millis() - start > (timeout+85)){
if( millis() - start > (timeout+95)){
errNotify();
#if defined (FAILURE_HANDLING)
return 0;
......@@ -1022,7 +1022,12 @@ bool RF24::txStandBy(uint32_t timeout, bool startTx){
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 config = read_register(NRF_CONFIG);
/* clear the interrupt flags */
config &= ~(1 << MASK_MAX_RT | 1 << MASK_TX_DS | 1 << MASK_RX_DR);
/* set the specified interrupt flags */
config |= fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR;
write_register(NRF_CONFIG, config);
}
/****************************************************************************/
......@@ -1474,7 +1479,7 @@ rf24_datarate_e RF24::getDataRate( void )
void RF24::setCRCLength(rf24_crclength_e length)
{
uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ;
uint8_t config = read_register(NRF_CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ;
// switch uses RAM (evil!)
if ( length == RF24_CRC_DISABLED )
......@@ -1490,7 +1495,7 @@ void RF24::setCRCLength(rf24_crclength_e length)
config |= _BV(EN_CRC);
config |= _BV( CRCO );
}
write_register( CONFIG, config ) ;
write_register( NRF_CONFIG, config ) ;
}
/****************************************************************************/
......@@ -1499,7 +1504,7 @@ rf24_crclength_e RF24::getCRCLength(void)
{
rf24_crclength_e result = RF24_CRC_DISABLED;
uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ;
uint8_t config = read_register(NRF_CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ;
uint8_t AA = read_register(EN_AA);
if ( config & _BV(EN_CRC ) || AA)
......@@ -1517,8 +1522,8 @@ rf24_crclength_e RF24::getCRCLength(void)
void RF24::disableCRC( void )
{
uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ;
write_register( CONFIG, disable ) ;
uint8_t disable = read_register(NRF_CONFIG) & ~_BV(EN_CRC) ;
write_register( NRF_CONFIG, disable ) ;
}
/****************************************************************************/
......@@ -1550,6 +1555,13 @@ void RF24::setRetries(uint8_t delay, uint8_t count)
# define DO 15 // PB6
# define USCK 16 // PB7
# define SS 13 // PB4
#elif defined(__AVR_ATtiny861__)
// these depend on the core used (check pins_arduino.h)
// tested with google-code core
# define DI 9 // PB0
# define DO 8 // PB1
# define USCK 7 // PB2
# define SS 6 // PB3
#endif
#if defined(RF24_TINY)
......
......@@ -38,8 +38,7 @@
#include "utility/includes.h"
//ATTiny
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__)
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__) || defined(__AVR_ATtiny861__)
#define RF24_TINY
#include "utility/ATTiny/RF24_arch_config.h"
......
......@@ -56,8 +56,8 @@ if (role == 1) {
Serial.println(F("Now sending"));
unsigned long time = micros(); // Take the time, and send it. This will block until complete
if (!radio.write( &time, sizeof(unsigned long) )){
unsigned long start_time = micros(); // Take the time, and send it. This will block until complete
if (!radio.write( &start_time, sizeof(unsigned long) )){
Serial.println(F("failed"));
}
......@@ -78,15 +78,15 @@ if (role == 1) {
}else{
unsigned long got_time; // Grab the response, compare, and send to debugging spew
radio.read( &got_time, sizeof(unsigned long) );
unsigned long time = micros();
unsigned long end_time = micros();
// Spew it
Serial.print(F("Sent "));
Serial.print(time);
Serial.print(start_time);
Serial.print(F(", Got response "));
Serial.print(got_time);
Serial.print(F(", Round-trip delay "));
Serial.print(time-got_time);
Serial.print(end_time-start_time);
Serial.println(F(" microseconds"));
}
......
name=RF24
version=1.1.5
version=1.1.6
author=TMRh20
maintainer=TMRh20
sentence=A library for NRF24L01(+) communication.
......
......@@ -24,7 +24,7 @@
*/
/* Memory Map */
#define CONFIG 0x00
#define NRF_CONFIG 0x00
#define EN_AA 0x01
#define EN_RXADDR 0x02
#define SETUP_AW 0x03
......
......@@ -10,6 +10,8 @@
*/
#include "gpio.h"
#include <stdlib.h>
#include <unistd.h>
GPIO::GPIO() {
}
......@@ -24,12 +26,24 @@ void GPIO::open(int port, int DDR)
fprintf(f, "%d\n", port);
fclose(f);
int counter = 0;
char file[128];
sprintf(file, "/sys/class/gpio/gpio%d/direction", port);
f = fopen(file, "w");
if (DDR == 0) fprintf(f, "in\n");
while( ( f = fopen(file,"w")) == NULL ){ //Wait 10 seconds for the file to be accessible if not open on first attempt
sleep(1);
counter++;
if(counter > 10){
perror("Could not open /sys/class/gpio/gpio%d/direction");
exit(0);
}
}
if (DDR == 0)
fprintf(f, "in\n");
else fprintf(f, "out\n");
fclose(f);
}
void GPIO::close(int port)
......
......@@ -4,5 +4,6 @@
#define RF24_BBB
#include "BBB/RF24_arch_config.h"
#include "BBB/interrupt.h"
#endif
\ No newline at end of file
/*
Interrupts functions extruded from wiringPi library by Oitzu.
wiringPi Copyright (c) 2012 Gordon Henderson
https://projects.drogon.net/raspberry-pi/wiringpi
wiringPi is free software: GNU Lesser General Public License
see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <sys/stat.h>
#include "interrupt.h"
#include <pthread.h>
//#define delay(x) bcm2835_delay(x)
static pthread_mutex_t pinMutex = PTHREAD_MUTEX_INITIALIZER ;
static volatile int pinPass = -1 ;
pthread_t threadId [64];
// sysFds:
// Map a file descriptor from the /sys/class/gpio/gpioX/value
static int sysFds [64] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
} ;
// ISR Data
static void (*isrFunctions [64])(void) ;
int waitForInterrupt (int pin, int mS)
{
int fd, x ;
uint8_t c ;
struct pollfd polls ;
if ((fd = sysFds [pin]) == -1)
return -2 ;
// Setup poll structure
polls.fd = fd ;
polls.events = POLLPRI ; // Urgent data!
// Wait for it ...
x = poll (&polls, 1, mS) ;
// Do a dummy read to clear the interrupt
// A one character read appars to be enough.
// Followed by a seek to reset it.
(void)read (fd, &c, 1) ;
lseek (fd, 0, SEEK_SET) ;
return x ;
}
int piHiPri (const int pri)
{
struct sched_param sched ;
memset (&sched, 0, sizeof(sched)) ;
if (pri > sched_get_priority_max (SCHED_RR))
sched.sched_priority = sched_get_priority_max (SCHED_RR) ;
else
sched.sched_priority = pri ;
return sched_setscheduler (0, SCHED_RR, &sched) ;
}
void *interruptHandler (void *arg)
{
int myPin ;
(void)piHiPri (55) ; // Only effective if we run as root
myPin = pinPass ;
pinPass = -1 ;
for (;;)
if (waitForInterrupt (myPin, -1) > 0){
pthread_mutex_lock (&pinMutex) ;
isrFunctions [myPin] () ;
pthread_mutex_unlock (&pinMutex) ;
pthread_testcancel(); //Cancel at this point if we have an cancellation request.
}
return NULL ;
}
int attachInterrupt (int pin, int mode, void (*function)(void))
{
const char *modeS ;
char fName [64] ;
char pinS [8] ;
pid_t pid ;
int count, i ;
char c ;
int bcmGpioPin ;
bcmGpioPin = pin ;
if (mode != INT_EDGE_SETUP)
{
/**/ if (mode == INT_EDGE_FALLING)
modeS = "falling" ;
else if (mode == INT_EDGE_RISING)
modeS = "rising" ;
else
modeS = "both" ;
sprintf (pinS, "%d", bcmGpioPin) ;
if ((pid = fork ()) < 0) // Fail
return printf("wiringPiISR: fork failed: %s\n", strerror (errno)) ;
if (pid == 0) // Child, exec
{
/**/ if (access ("/usr/local/bin/gpio", X_OK) == 0)
{
execl ("/usr/local/bin/gpio", "gpio", "edge", pinS, modeS, (char *)NULL) ;
return printf ("wiringPiISR: execl failed: %s\n", strerror (errno)) ;
}
else if (access ("/usr/bin/gpio", X_OK) == 0)
{
execl ("/usr/bin/gpio", "gpio", "edge", pinS, modeS, (char *)NULL) ;
return printf ("wiringPiISR: execl failed: %s\n", strerror (errno)) ;
}
else
return printf ("wiringPiISR: Can't find gpio program\n") ;
}
else // Parent, wait
wait (NULL) ;
}
if (sysFds [bcmGpioPin] == -1)
{
sprintf (fName, "/sys/class/gpio/gpio%d/value",bcmGpioPin);
if ((sysFds [bcmGpioPin] = open (fName, O_RDWR)) < 0)
return printf ("wiringPiISR: unable to open %s: %s\n", fName, strerror (errno)) ;
}
ioctl (sysFds [bcmGpioPin], FIONREAD, &count) ;
for (i = 0 ; i < count ; ++i)
read (sysFds [bcmGpioPin], &c, 1) ;
isrFunctions [pin] = function ;
pthread_mutex_lock (&pinMutex) ;
pinPass = pin ;
pthread_create (&threadId[bcmGpioPin], NULL, interruptHandler, NULL) ;
while (pinPass != -1)
delay (1) ;
pthread_mutex_unlock (&pinMutex) ;
return 0 ;
}
int detachInterrupt (int pin)
{
char pinS [8];
const char *modeS = "none";
pid_t pid ;
if (!pthread_cancel(threadId[pin])) //Cancel the thread
{
return 0;
}
if (!close(sysFds[pin])) //Close filehandle
{
return 0;
}
/* Set wiringPi to 'none' interrupt mode */
sprintf (pinS, "%d", pin) ;
if ((pid = fork ()) < 0) // Fail
return printf("wiringPiISR: fork failed: %s\n", strerror (errno)) ;
if (pid == 0) // Child, exec
{
/**/ if (access ("/usr/local/bin/gpio", X_OK) == 0)
{
execl ("/usr/local/bin/gpio", "gpio", "edge", pinS, modeS, (char *)NULL) ;
return printf ("wiringPiISR: execl failed: %s\n", strerror (errno)) ;
}
else if (access ("/usr/bin/gpio", X_OK) == 0)
{
execl ("/usr/bin/gpio", "gpio", "edge", pinS, modeS, (char *)NULL) ;
return printf ("wiringPiISR: execl failed: %s\n", strerror (errno)) ;
}
else
return printf ("wiringPiISR: Can't find gpio program\n") ;
}
else // Parent, wait
wait (NULL) ;
return 1;
}
void rfNoInterrupts(){
pthread_mutex_lock (&pinMutex) ;
}
void rfInterrupts(){
pthread_mutex_unlock (&pinMutex) ;
}
\ No newline at end of file
/*
Interrupts functions extruded from wiringPi library by Oitzu.
wiringPi Copyright (c) 2012 Gordon Henderson
https://projects.drogon.net/raspberry-pi/wiringpi
wiringPi is free software: GNU Lesser General Public License
see <http://www.gnu.org/licenses/>
*/
#include "RF24_arch_config.h"
#define INT_EDGE_SETUP 0
#define INT_EDGE_FALLING 1
#define INT_EDGE_RISING 2
#define INT_EDGE_BOTH 3
/*
* interruptHandler:
* This is a thread and gets started to wait for the interrupt we're
* hoping to catch. It will call the user-function when the interrupt
* fires.
*********************************************************************************
*/
void *interruptHandler (void *arg);
#ifdef __cplusplus
extern "C" {
#endif
/*
* waitForInterrupt:
* Pi Specific.
* Wait for Interrupt on a GPIO pin.
* This is actually done via the /sys/class/gpio interface regardless of
* the wiringPi access mode in-use. Maybe sometime it might get a better
* way for a bit more efficiency.
*********************************************************************************
*/
extern int waitForInterrupt (int pin, int mS);
/*
* piHiPri:
* Attempt to set a high priority schedulling for the running program
*********************************************************************************
*/
extern int piHiPri (const int pri);
/*
* attachInterrupt (Original: wiringPiISR):
* Pi Specific.
* Take the details and create an interrupt handler that will do a call-
* back to the user supplied function.
*********************************************************************************
*/
extern int attachInterrupt (int pin, int mode, void (*function)(void));
/*
* detachInterrupt:
* Pi Specific detachInterrupt.
* Will cancel the interrupt thread, close the filehandle and
* setting wiringPi back to 'none' mode.
*********************************************************************************
*/
extern int detachInterrupt (int pin);
extern void rfNoInterrupts();
extern void rfInterrupts();
#ifdef __cplusplus
}
#endif
\ No newline at end of file
......@@ -10,8 +10,10 @@
#include "spi.h"
SPI::SPI() {
#include <pthread.h>
static pthread_mutex_t spiMutex;
SPI::SPI():fd(-1) {
}
void SPI::begin(int busNo){
......@@ -48,16 +50,15 @@ void SPI::init()
{
int ret;
if(this->fd > 0) {
close(this->fd);
}
if (this->fd < 0) // check whether spi is already open
{
this->fd = open(this->device.c_str(), O_RDWR);
if (this->fd < 0)
{
perror("can't open device");
abort();
}
}
/*
......@@ -113,6 +114,7 @@ void SPI::init()
uint8_t SPI::transfer(uint8_t tx_)
{
pthread_mutex_lock (&spiMutex);
int ret;
uint8_t tx[1] = {tx_};
uint8_t rx[1];
......@@ -124,10 +126,10 @@ uint8_t SPI::transfer(uint8_t tx_)
tr.len = 1,//ARRAY_SIZE(tx),
tr.delay_usecs = 0,
tr.cs_change=1,
tr.speed_hz = this->speed,
tr.bits_per_word = this->bits,
};
tr.speed_hz = this->speed,
//Note: On RPi, for some reason I started getting 'bad message' errors, and changing the struct as below fixed it, until an update...??
//
/* // One byte is transfered at once
......@@ -146,10 +148,12 @@ uint8_t SPI::transfer(uint8_t tx_)
ret = ioctl(this->fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
{
pthread_mutex_unlock (&spiMutex);
perror("can't send spi message");
abort();
}
pthread_mutex_unlock (&spiMutex);
return rx[0];
}
......@@ -157,6 +161,7 @@ uint8_t SPI::transfer(uint8_t tx_)
void SPI::transfernb(char* tbuf, char* rbuf, uint32_t len)
{
pthread_mutex_lock (&spiMutex);
int ret;
this->init();
struct spi_ioc_transfer tr = {
......@@ -165,9 +170,9 @@ void SPI::transfernb(char* tbuf, char* rbuf, uint32_t len)
tr.len = len,//ARRAY_SIZE(tx),
tr.cs_change=1,
tr.delay_usecs = 0,
tr.speed_hz = this->speed,
tr.bits_per_word = this->bits,
};
tr.speed_hz = this->speed,
//Note: On RPi, for some reason I started getting 'bad message' errors, and changing the struct as below fixed it, until an update...??
// One byte is transfered at once
......@@ -187,10 +192,11 @@ void SPI::transfernb(char* tbuf, char* rbuf, uint32_t len)
ret = ioctl(this->fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
{
pthread_mutex_unlock (&spiMutex);
perror("can't send spi message");
abort();
}
pthread_mutex_unlock (&spiMutex);
//return rx[0];
}
......@@ -201,6 +207,7 @@ void SPI::transfern(char* buf, uint32_t len)
SPI::~SPI() {
if (!(this->fd < 0))
close(this->fd);
}
......@@ -18,6 +18,7 @@
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#define BCK2835_LIBRARY_BUILD
#include "bcm2835.h"
......@@ -271,6 +272,13 @@ uint8_t bcm2835_gpio_eds(uint8_t pin)
return (value & (1 << shift)) ? HIGH : LOW;
}
uint32_t bcm2835_gpio_eds_multi(uint32_t mask)
{
volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4;
uint32_t value = bcm2835_peri_read(paddr);
return (value & mask);
}
/* Write a 1 to clear the bit in EDS */
void bcm2835_gpio_set_eds(uint8_t pin)
{
......@@ -280,6 +288,12 @@ void bcm2835_gpio_set_eds(uint8_t pin)
bcm2835_peri_write(paddr, value);
}
void bcm2835_gpio_set_eds_multi(uint32_t mask)
{
volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4;
bcm2835_peri_write(paddr, mask);
}
/* Rising edge detect enable */
void bcm2835_gpio_ren(uint8_t pin)
{
......@@ -396,16 +410,22 @@ void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on)
/* Read GPIO pad behaviour for groups of GPIOs */
uint32_t bcm2835_gpio_pad(uint8_t group)
{
if (bcm2835_pads == MAP_FAILED)
return 0;
volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group;
return bcm2835_peri_read(paddr);
}
/* Set GPIO pad behaviour for groups of GPIOs
// powerup value for al pads is
// powerup value for all 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)
{
if (bcm2835_pads == MAP_FAILED)
return;
volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group;
bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD);
}
......@@ -520,10 +540,13 @@ void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud)
bcm2835_gpio_pudclk(pin, 0);
}
void bcm2835_spi_begin(void)
int bcm2835_spi_begin(void)
{
volatile uint32_t* paddr;
if (bcm2835_spi0 == MAP_FAILED)
return 0; /* bcm2835_init() failed, or not root */
/* 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 */
......@@ -537,6 +560,8 @@ void bcm2835_spi_begin(void)
/* Clear TX and RX fifos */
bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR);
return 1; // OK
}
void bcm2835_spi_end(void)
......@@ -551,7 +576,7 @@ void bcm2835_spi_end(void)
void bcm2835_spi_setBitOrder(uint8_t __attribute__((unused)) order)
{
/* BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one suported by SPI0 */
/* BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one supported by SPI0 */
}
/* defaults to 0, which means a divider of 65536.
......@@ -718,10 +743,14 @@ void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active)
bcm2835_peri_set_bits(paddr, active << shift, 1 << shift);
}
void bcm2835_i2c_begin(void)
int bcm2835_i2c_begin(void)
{
uint16_t cdiv;
if ( bcm2835_bsc0 == MAP_FAILED
|| bcm2835_bsc1 == MAP_FAILED)
return 0; /* bcm2835_init() failed, or not root */
#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 */
......@@ -741,6 +770,8 @@ void bcm2835_i2c_begin(void)
// 9 = Clocks per byte : 8 bits + ACK
*/
i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9;
return 1;
}
void bcm2835_i2c_end(void)
......@@ -1177,6 +1208,10 @@ void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros)
void bcm2835_pwm_set_clock(uint32_t divisor)
{
if ( bcm2835_clk == MAP_FAILED
|| bcm2835_pwm == MAP_FAILED)
return; /* bcm2835_init() failed or not root */
/* From Gerts code */
divisor &= 0xfff;
/* Stop PWM clock */
......@@ -1192,6 +1227,10 @@ void bcm2835_pwm_set_clock(uint32_t divisor)
void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled)
{
if ( bcm2835_clk == MAP_FAILED
|| bcm2835_pwm == MAP_FAILED)
return; /* bcm2835_init() failed or not root */
uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL);
if (channel == 0)
......@@ -1225,6 +1264,10 @@ void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled)
void bcm2835_pwm_set_range(uint8_t channel, uint32_t range)
{
if ( bcm2835_clk == MAP_FAILED
|| bcm2835_pwm == MAP_FAILED)
return; /* bcm2835_init() failed or not root */
if (channel == 0)
bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range);
else if (channel == 1)
......@@ -1233,6 +1276,10 @@ void bcm2835_pwm_set_range(uint8_t channel, uint32_t range)
void bcm2835_pwm_set_data(uint8_t channel, uint32_t data)
{
if ( bcm2835_clk == MAP_FAILED
|| bcm2835_pwm == MAP_FAILED)
return; /* bcm2835_init() failed or not root */
if (channel == 0)
bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data);
else if (channel == 1)
......@@ -1304,10 +1351,16 @@ int bcm2835_init(void)
}
/* else we are prob on RPi 1 with BCM2835, and use the hardwired defaults */
/* Now get ready to map the peripherals block */
/* Now get ready to map the peripherals block
* If we are not root, try for the new /dev/gpiomem interface and accept
* the fact that we can only access GPIO
* else try for the /dev/mem interface and get access to everything
*/
memfd = -1;
ok = 0;
/* Open the master /dev/memory device */
if (geteuid() == 0)
{
/* Open the master /dev/mem device */
if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0)
{
fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n",
......@@ -1333,6 +1386,25 @@ int bcm2835_init(void)
bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4;
ok = 1;
}
else
{
/* Not root, try /dev/gpiomem */
/* Open the master /dev/mem device */
if ((memfd = open("/dev/gpiomem", O_RDWR | O_SYNC) ) < 0)
{
fprintf(stderr, "bcm2835_init: Unable to open /dev/gpiomem: %s\n",
strerror(errno)) ;
goto exit;
}
/* Base of the peripherals block is mapped to VM */
bcm2835_peripherals_base = 0;
bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, (uint32_t)bcm2835_peripherals_base);
if (bcm2835_peripherals == MAP_FAILED) goto exit;
bcm2835_gpio = bcm2835_peripherals;
ok = 1;
}
exit:
if (memfd >= 0)
......
......@@ -23,7 +23,7 @@
BCM 2835).
The version of the package that this documentation refers to can be downloaded
from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.48.tar.gz
from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.50.tar.gz
You can find the latest version at http://www.airspayce.com/mikem/bcm2835
Several example programs are provided.
......@@ -38,7 +38,7 @@
Before asking a question or reporting a bug, please read http://www.catb.org/esr/faqs/smart-questions.html
Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian
and Occidentalisv01
and Occidentalisv01, 2016-02-09 Raspbian Jessie.
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
......@@ -48,6 +48,24 @@
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 Running as root
Prior to the release of Raspbian Jessie in Feb 2016, access to any
peripheral device via /dev/mem on the RPi required the process to
run as root. Raspbian Jessie permits non-root users to access the
GPIO peripheral (only) via /dev/gpiomem, and this library supports
that limited mode of operation.
If the library runs with effective UID of 0 (ie root), then
bcm2835_init() will attempt to open /dev/mem, and, if successful, it
will permit use of all peripherals and library functions.
If the library runs with any other effective UID (ie not root), then
bcm2835_init() will attempt to open /dev/gpiomem, and, if
successful, will only permit GPIO operations. In particular,
bcm2835_spi_begin() and bcm2835_i2c_begin() will return false and all
other non-gpio operations may fail silently or crash.
\par Installation
This library consists of a single non-shared library and header file, which will be
......@@ -421,6 +439,15 @@
Added patch from Eckhardt Ulrich that fixed problems that could cause hanging with bcm2835_i2c_read_register_rs
and others.
\version 1.49 2016-01-05
Added patch from Jonathan Perkin with new functions bcm2835_gpio_eds_multi() and bcm2835_gpio_set_eds_multi().
\version 1.50 2016-02-28
Added support for running as non-root, permitting access to GPIO only. Functions
bcm2835_spi_begin() and bcm2835_i2c_begin() will now return 0 if not running as root
(which prevents access to the SPI and I2C peripherals, amongst others).
Testing on Raspbian Jessie.
\author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS
*/
......@@ -431,7 +458,7 @@
#include <stdint.h>
#define BCM2835_VERSION 10048 /* Version 1.48 */
#define BCM2835_VERSION 10050 /* Version 1.50 */
/* RPi 2 is ARM v7, and has DMB instruction for memory barriers.
Older RPis are ARM v6 and don't, so a coprocessor instruction must be used instead.
......@@ -503,7 +530,7 @@ extern uint32_t bcm2835_peripherals_size;
extern uint32_t *bcm2835_peripherals;
/*! Base of the ST (System Timer) registers.
Available after bcm2835_init has been called
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_st;
......@@ -513,32 +540,32 @@ extern volatile uint32_t *bcm2835_st;
extern volatile uint32_t *bcm2835_gpio;
/*! Base of the PWM registers.
Available after bcm2835_init has been called
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_pwm;
/*! Base of the CLK registers.
Available after bcm2835_init has been called
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_clk;
/*! Base of the PADS registers.
Available after bcm2835_init has been called
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_pads;
/*! Base of the SPI0 registers.
Available after bcm2835_init has been called
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_spi0;
/*! Base of the BSC0 registers.
Available after bcm2835_init has been called
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_bsc0;
/*! Base of the BSC1 registers.
Available after bcm2835_init has been called
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_bsc1;
......@@ -552,7 +579,7 @@ typedef enum
BCM2835_REGBASE_PWM = 3, /*!< Base of the PWM registers. */
BCM2835_REGBASE_CLK = 4, /*!< Base of the CLK registers. */
BCM2835_REGBASE_PADS = 5, /*!< Base of the PADS registers. */
BCM2835_REGBASE_SPI0 = 6, /*! Base of the SPI0 registers. */
BCM2835_REGBASE_SPI0 = 6, /*!< Base of the SPI0 registers. */
BCM2835_REGBASE_BSC0 = 7, /*!< Base of the BSC0 registers. */
BCM2835_REGBASE_BSC1 = 8 /*!< Base of the BSC1 registers. */
} bcm2835RegisterBase;
......@@ -826,7 +853,7 @@ typedef enum
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
The frequencies shown for each divider have been confirmed by measurement.
*/
typedef enum
{
......@@ -1015,12 +1042,16 @@ extern "C" {
@{
*/
/*! Initialise the library by opening /dev/mem and getting pointers to the
/*! Initialise the library by opening /dev/mem (if you are root)
or /dev/gpiomem (if you are not)
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.
If bcm2835_init() succeeds but you are not running as root, then only gpio operations
are permitted, and calling any other functions may result in crashes or other failures. .
Prints messages to stderr in case of errors.
\return 1 if successful else 0
*/
......@@ -1186,6 +1217,13 @@ extern "C" {
*/
extern uint8_t bcm2835_gpio_eds(uint8_t pin);
/*! Same as bcm2835_gpio_eds() but checks if any of the pins specified in
the mask have detected a level or edge.
\param[in] mask Mask of pins to check. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05)
\return Mask of pins HIGH if the event detect status for the given pin is true.
*/
extern uint32_t bcm2835_gpio_eds_multi(uint32_t mask);
/*! 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.
......@@ -1193,6 +1231,12 @@ extern "C" {
*/
extern void bcm2835_gpio_set_eds(uint8_t pin);
/*! Same as bcm2835_gpio_set_eds() but clears the flag for any pin which
is set in the mask.
\param[in] mask Mask of pins to clear. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05)
*/
extern void bcm2835_gpio_set_eds_multi(uint32_t mask);
/*! 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
......@@ -1370,10 +1414,11 @@ extern "C" {
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.
You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to
their default functions
their default functions.
\sa bcm2835_spi_end()
\return 1 if successful, 0 otherwise (perhaps because you are not running as root)
*/
extern void bcm2835_spi_begin(void);
extern int bcm2835_spi_begin(void);
/*! End SPI operations.
SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1)
......@@ -1476,9 +1521,10 @@ extern "C" {
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
\return 1 if successful, 0 otherwise (perhaps because you are not running as root)
\sa bcm2835_i2c_end()
*/
extern void bcm2835_i2c_begin(void);
extern int bcm2835_i2c_begin(void);
/*! End I2C operations.
I2C pins P1-03 (SDA) and P1-05 (SCL)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment