Skip to content

Commit

Permalink
Merge pull request #34 from EnviroDIY/develop
Browse files Browse the repository at this point in the history
Support for SAMD boards
  • Loading branch information
SRGDamia1 authored May 16, 2017
2 parents e0fc52f + df5c19c commit cf2cbdf
Show file tree
Hide file tree
Showing 12 changed files with 341 additions and 232 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ install:
- pio update

script:
- platformio ci --lib="." --board=mayfly
- platformio ci --lib="." --board=mayfly --board=feather32u4 --board=adafruit_feather_m0_usb
134 changes: 134 additions & 0 deletions OverviewOfInterrupts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Overview of Interrupts

## Some vocabulary:
Registers: PCMSK0, PCMSK1, PCMSK2, PCMSK3: registers that enable or disable pin-change interrupts on individual pins

**PCICR** : a register where the three least significant bits enable or disable pin change interrupts on a range of pins, i.e. {0,0,0,0,0,PCIE2,PCIE1,PCIE0}, where PCIE2 maps to PCMSK2, PCIE1 maps to PCMSK1, and PCIE0 maps to PCMSK0.

**noInterrupts()** globally disables interrupts (of all types)

**interrupts()** globally enables interrupts (of all types), but they will only occur if the requisite registers are set (e.g. PCMSK and PCICR).

## What is an interrupt?
An interrupt is a signal that causes the microcontroller to halt execution of the program, and perform a subroutine known as an interrupt handler or Interrupt Service Routine (ISR). After the ISR, program execution continues. This allows the microcontroller to efficiently handle a time-sensitive function such as receiving a burst of data on one of its pins, by not forcing the microcontroller to wait for the data. It can perform other tasks until the interrupt is called.

Physically, interrupts take the form of signals on the pins of the microcontroller. To make the library generic, the dataPin responds to pin change interrupts which are available on all pins of the Arduino Uno, and a majority of pins on other variants. Pin change interrupts trigger an ISR on any change of state detected for a specified pin.

Obviously, interrupts are not always desirable, so they can be controlled by manipulating registers. Registers are small 1-byte (8-bit) stores of memory directly accessible by processor. There is one register PCICR, which can enable and disable pin change interrupts for a range of pins at a single time.

There are also three registers that store the state (enabled/disabled) of the
pin change interrupts: PCMSK0, PCMSK1, and PCMSK2. Each bit stores a 1
(enabled) or 0 (disabled).

For example, PCMSK0 holds an 8 bits which represent:
PCMSK0 {PCINT7, PCINT6, PCINT5, PCINT4, PCINT3, PCINT2, PCINT1, PCINT0}

On an Arduino Uno, these map to:
PCMSK0 {XTAL2, XTAL1, Pin 13, Pin 12, Pin 11, Pin 10, Pin 9, Pin 8}

## Enabling an interrupt.

Initially, no interrupts are enabled, so PCMSK0 looks like: {00000000}. If we were to use pin 9 as the data pin, we would set the bit in the pin 9 position to 1, like so: {00000010}.

To accomplish this, we can make use of some predefined macros.

One macro "digitalPinToPCMSK" is defined in "pins_arduino.h" which allows us to quickly get the proper register (PCMSK0, PCMSK1, or PCMSK2) given the number of the Arduino pin. So when we write: digitalPinToPCMSK(9), the address of PCMSK0 is returned. We can use the dereferencing operator '*' to get the value of the address.

That is, *digitalPinToPCMSK(9) returns: {00000000}.

Another macro , "digitalPinToPCMSKbit" is also defined in "pins_arduino.h" and returns the position of the bit of interest within the PCMSK of interest.

So when we write: digitalPinToPCMSKbit(9), the value returned is 1, because pin 9 is represented by the "1st bit" within PCMSK0, which has a zero-based index. That is: PCMSK { 7th bit, 6th bit, 5th bit, 4th bit, 3rd bit, 2nd bit, 1st bit, 0th bit }

The leftward bit shift operator "<<" can then used to create a "mask". Masks are data that are used during bitwise operations to manipulate (enable / disable) one or more individual bits simultaneously.

The syntax for the operator is (variable << number_of_bits).
Some examples:

byte a = 5; // binary: a = 00000101

byte b = a << 4; // binary: b = 01010000

So when we write: (1<<digitalPinToPCMSKbit(9)), we get: {00000010}.

Or equivalently: (1<<1), we get: {00000010}.

To use the mask to set the bit of interest we use the bitwise or operator '|'. We will use the compact '|=' notation which does the operation and then stores the result back into the left hand side.


So the operation:

*digitalPinToPCMSK(_dataPin) |= (1<<digitalPinToPCMSKbit(9));

Accomplishes:

(1<<digitalPinToPCMSKbit(9)) {00000010}

PCMSK0 | {00000000}

-------------

{00000010}


We must also enable the global control for the interrupt. This is done in a similar fashion:

*digitalPinToPCICR(_dataPin) |= (1<<digitalPinToPCICRbit(_dataPin));


Now let's assume that part of your Arduino sketch outside of SDI-12 had set a pin change interrupt on pin 13. Pin 9 and pin 13 are on the same PCMSK in the case of the Arduino Uno.

This time before we set the bit for pin nine,

*digitalPinToPCMSK(9) returns: {00100000}.

So now:

(1<<digitalPinToPCMSKbit(9)) {00000010}

PCMSK0 | {00100000}

-------------

{00100010}


By using a bitmask and bitwise operation, we have successfully enabled pin 9 without effecting the state of pin 13.


## Disabling an interrupt.

When the we would like to put the SDI-12 object in the DISABLED state, (e.g. the destructor is called), we need to make sure the bit corresponding to the data pin is unset.

Let us consider again the case of where an interrupt has been enabled on pin 13: {00100010}. We want to be sure not to disturb this interrupt when disabling the interrupt on pin 9.

We will make use of similar macros, but this time we will use an inverted bit mask and the AND operation. The "&=" operator is equivalent to a bitwise AND operation between the PCMSK register of interest and the previous result, which is then stored back into the PCMSK of interest. The "*" is the dereferencing operator, which is equivalently translated to "value pointed by", and allows us to store the result back into the proper PCMSK.

Again 1<<digitalPinToPCMSKbit(9) returns {00000010}

The inversion symbol ~ modifies the result to {11111101}

So to finish our example:

~(1<<digitalPinToPCMSKbit(9)) {11111101}

PCMSK0 & {00100010}

-------------

{00100000}


So only the interrupt on pin 13 remains set. As a matter of book keeping, if we unset the last bit in the PCMSK, we ought to also unset the respective bit in the PCICR.
!(*digitalPinToPCMSK(9)
will evaluate TRUE if PCMSK {00000000}
will evaluate FALSE if PCMSK != {00000000}

In this case, pin 13 is set, so the expression would be FALSE. If we go back to the original case without pin 13, the expression after disabling pin 9 would evaluate to TRUE.

Therefore if we evaluate to TRUE, we should tidy up:
```cpp
if(!*digitalPinToPCMSK(_dataPin)){
*digitalPinToPCICR(_dataPin) &= ~(1<<digitalPinToPCICRbit(_dataPin));
}
```
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ Arduino library for SDI-12 communications to a wide variety of environmental sen

This project is part of the [EnviroDIY](http://envirodiy.org/) vision to create an open source hardware and software stack to deliver near real time environmental data from wireless sensor networks, such as the Arduino-compatible [EnviroDIY™ Mayfly Data Logger](http://envirodiy.org/mayfly/).

Note that this library will conflict with SoftwareSerial, the Sodaq PCInt library, and any other library that monopolize all pin change interrupt vectors. To help in using this in combination with those libraries, there is a version (SDI12_PCINT3) which is identical, except in that it only uses PCINT3 and ignores 0-2. Get that version from the "Mayfly" branch.
The library has been tested with an Arduino Uno (AtMega328p), EnviroDIY Mayfly (AtMega1284p), Adafruit Feather 32u4 (AtMega32u4, identical to Arduino Leonardo), and an Adafruit Feather M0 (SAMD21G18, identical to Arduino Zero). Not all pins are usable on all boards. These pins will work on those processors:

**AtMega328p / Arduino Uno:** All pins
**Arduino Mega or Mega 2560:** 10, 11, 12, 13, 14, 15, 50, 51, 52, 53, A8 (62), A9 (63), A10 (64), A11 (65), A12 (66), A13 (67), A14 (68), A15 (69)
**AtMega32u4 / Arduino Leonardo:** 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI)
**AtMega1284p / EnviroDIY Mayfly:** All pins
**SAMD21G18 / Arduino Zero:** All pins (except 4 on the zero)

Note that this library will, by nature, conflict with SoftwareSerial and any other library that monopolize all pin change interrupt vectors for all AVR boards. If you would like to use a different pin change interrupt library, uncomment the line ```#define SDI12_EXTERNAL_PCINT``` in SDI12.h and recompile the library. Then, in your own code call SDI12::handleInterrupt() as the interrupt for the SDI12 pin using the other interrupt library. Example j shows doing this using GreyGnome's [EnableInterrupt](https://github.com/GreyGnome/EnableInterrupt) library.

This library also shares some of the other limitations of SoftwareSerial, that is, all interrupts are disabled during transmission and it might be sensitive to interrupt use by other libraries. Because SDI-12 operates at a very slow baud rate (only 1200 baud), the disabling of interrupts during transmission could possibly be for more time than you might expect. For that reason, please don't try to send and receive data through the SDI-12 library while transmitting other serial data or looking for other pin change interrupts.

## Getting Started

Expand Down
9 changes: 6 additions & 3 deletions examples/b_address_change/b_address_change.ino
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ void setup(){
Serial.begin(9600);
mySDI12.begin();

// First write the output value, and only then set the output mode.
digitalWrite(POWERPIN, HIGH);
pinMode(POWERPIN, OUTPUT);
// Power the sensors;
#if POWERPIN > 0
pinMode(POWERPIN, OUTPUT);
digitalWrite(POWERPIN, HIGH);
delay(200);
#endif
}

void loop(){
Expand Down
12 changes: 7 additions & 5 deletions examples/c_check_all_addresses/c_check_all_addresses.ino
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
#include "SDI12.h"

#define POWERPIN -1 // change to the proper pin (or -1 if not switching power)
#define FirstPin 2 // change to lowest pin number on your board
#define LastPin 23 // change to highest pin number on your board
#define FirstPin 5 // change to lowest pin number on your board
#define LastPin 24 // change to highest pin number on your board

// gets identification information from a sensor, and prints it to the serial port
// expects a character between '0'-'9', 'a'-'z', or 'A'-'Z'.
Expand Down Expand Up @@ -130,9 +130,11 @@ void setup(){
Serial.println("//\n// Start Search for SDI-12 Devices \n// -----------------------");

// Power the sensors;
pinMode(POWERPIN, OUTPUT);
digitalWrite(POWERPIN, HIGH);
delay(1000);
#if POWERPIN > 0
pinMode(POWERPIN, OUTPUT);
digitalWrite(POWERPIN, HIGH);
delay(200);
#endif

for (uint8_t pin = FirstPin; pin < LastPin; pin++)
{
Expand Down
18 changes: 14 additions & 4 deletions examples/d_simple_logger/d_simple_logger.ino
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
#include "SDI12.h"

#define POWERPIN -1 // change to the proper pin (or -1)
#define DATAPIN 9 // change to the proper pin
#define DATAPIN 6 // change to the proper pin

SDI12 mySDI12(DATAPIN);

Expand Down Expand Up @@ -110,6 +110,7 @@ void printInfo(char i){
void printBufferToScreen(){
String buffer = "";
mySDI12.read(); // consume address
mySDI12.read(); // consume address
while(mySDI12.available()){
char c = mySDI12.read();
if(c == '+' || c == '-'){
Expand Down Expand Up @@ -231,10 +232,14 @@ boolean setVacant(byte i){

void setup(){
Serial.begin(57600);
while(!Serial);

pinMode(DATAPIN, INPUT);
pinMode(POWERPIN, OUTPUT);
digitalWrite(POWERPIN, HIGH);
// Power the sensors;
#if POWERPIN > 0
pinMode(POWERPIN, OUTPUT);
digitalWrite(POWERPIN, HIGH);
delay(200);
#endif

mySDI12.begin();
delay(500); // allow things to settle
Expand All @@ -258,6 +263,8 @@ void setup(){
for(byte i = 0; i < 62; i++){
if(isTaken(i)){
found = true;
Serial.print("Found address: ");
Serial.println(i);
break;
}
}
Expand All @@ -279,6 +286,7 @@ void loop(){
Serial.print(millis()/1000);
Serial.print(",");
printInfo(i);
Serial.print("\t\t,");
takeMeasurement(i);
Serial.println();
}
Expand All @@ -288,6 +296,7 @@ void loop(){
Serial.print(millis()/1000);
Serial.print(",");
printInfo(i);
Serial.print("\t\t,");
takeMeasurement(i);
Serial.println();
}
Expand All @@ -297,6 +306,7 @@ void loop(){
Serial.print(millis()/1000);
Serial.print(",");
printInfo(i);
Serial.print("\t\t,");
takeMeasurement(i);
Serial.println();
};
Expand Down
13 changes: 8 additions & 5 deletions examples/j_external_pcint_library/j_external_pcint_library.ino
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
#include <EnableInterrupt.h>
#include <SDI12.h>

#define POWERPIN 22 // change to the proper pin (or -1)
#define DATAPIN 7 // change to the proper pin
#define POWERPIN -1 // change to the proper pin (or -1)
#define DATAPIN 9 // change to the proper pin

SDI12 mySDI12(DATAPIN);

Expand Down Expand Up @@ -207,9 +207,12 @@ boolean setVacant(byte i){
void setup(){
Serial.begin(57600);

pinMode(DATAPIN, INPUT);
pinMode(POWERPIN, OUTPUT);
digitalWrite(POWERPIN, HIGH);
// Power the sensors;
#if POWERPIN > 0
pinMode(POWERPIN, OUTPUT);
digitalWrite(POWERPIN, HIGH);
delay(200);
#endif

mySDI12.begin();
delay(500); // allow things to settle
Expand Down
9 changes: 6 additions & 3 deletions keywords.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

# Syntax Coloring Map for SDI12 when
# using the Arduino IDE.
# Syntax Coloring Map for SDI12 when
# using the Arduino IDE.

### Classes (KEYWORD1)

Expand All @@ -11,11 +11,14 @@ SDI12 KEYWORD1
begin KEYWORD2
end KEYWORD2
forceHold KEYWORD2
forceListen KEYWORD2
sendCommand KEYWORD2
sendResponse KEYWORD2
available KEYWORD2
peek KEYWORD2
read KEYWORD2
clearBuffer KEYWORD2
flush KEYWORD2
setActive KEYWORD2
isActive KEYWORD2

handleInterrupt KEYWORD2
30 changes: 30 additions & 0 deletions library.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "Arduino-SDI-12",
"version": "1.0.1",
"keywords": "SDI-12, sdi12, communication, bus, sensor, Decagon",
"description": "Arduino library for SDI-12 communications to a wide variety of environmental sensors.",
"repository":
{
"type": "git",
"url": "https://github.com/EnviroDIY/Arduino-SDI-12.git"
},
"authors":
[
{
"name": "Kevin M. Smith",
"email": "[email protected]"
},
{
"name": "Shannon Hicks",
"email": "[email protected]"
},
{
"name": "Sara Damiano",
"email": "[email protected]",
"maintainer": true
}
],
"license": "BSD-3-Clause",
"frameworks": "arduino",
"platforms": "atmelavr, atmelsam"
}
4 changes: 2 additions & 2 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name=Arduino-SDI-12
version=0.38.0
author=Kevin M. Smith <[email protected]>, Shannon Hicks <[email protected]>, Jeff Horsburgh <[email protected]>, Kenny Fryar-Ludwig <[email protected]>
version=1.0.1
author=Kevin M. Smith <[email protected]>, Shannon Hicks <[email protected]>
maintainer=Sara Damiano <[email protected]>
sentence=Arduino library for SDI-12 communications to a wide variety of environmental sensors.
paragraph=This library provides a general software solution, without requiring any additional hardware.
Expand Down
Loading

0 comments on commit cf2cbdf

Please sign in to comment.