Skip to content

Instantly share code, notes, and snippets.

@nagubal
Last active March 14, 2016 18:39
Show Gist options
  • Select an option

  • Save nagubal/f23dad55b46c0d887a0f to your computer and use it in GitHub Desktop.

Select an option

Save nagubal/f23dad55b46c0d887a0f to your computer and use it in GitHub Desktop.
A 2.3" 4 digits 7-segment arduino clock, using the DE-DP003 from Sure Electronics, and temperature reading using a DS18B20 sensor - Uses old libraries
/**
* USE_GITHUB_USERNAME=nagubal
*
* 2.3" 4 digits 7-segment clock, using the DE-DP003 from Sure Electronics
* http://www.sure-electronics.net/mcu,display/DE-DP003.pdf
*
* The DE-DP003 is a common anode 7-segment LED display.
* The LED driver chips are composed of 4pcs of 74HC595 and 5pcs of ULN2003.
* Data should be clocked in from CLK_IN and DATA_IN in J1, and DIMM_IN pin should be pull to low to enable display.
* PWM signal can be applied on the DIMM_IN pin to control brightness.
*/
#include <EEPROM.h>
#include <Streaming.h>
#include <Wire.h>
#define ENCODER_USE_INTERRUPTS 1
// http://www.pjrc.com/teensy/td_libs_Encoder.html
#include "Encoder.h"
//https://github.com/akafugu/ds_rtc_lib
#include <WireRtcLib.h>
// http://www.pjrc.com/teensy/td_libs_OneWire.html
#include <OneWire.h>
// http://www.milesburton.com/?title=Dallas_Temperature_Control_Library
#include "DallasTemperature.h"
#include "pinConstants.h"
// the DisplayMode enum declaration
#include "displayMode.h"
/**
* La résolution du DS18B20; http://datasheets.maxim-ic.com/en/ds/DS18B20.pdf
*/
#define DS18B20_RESOLUTION 11
/**
* the debounce time, increase if the output flickers
*/
#define DEBOUNCE 5
/**
* Default brightness
*/
#define DEFAULT_BRIGHTNESS 7
#define CHRONODOT_ID 0x68
/**
* The current display mode
*/
DisplayMode displayMode = CLOCK_TEMP_MODE;
SettingsMode settingsMode = SETTINGS_OFF;
WireRtcLib::tm* settingsTime;
/**
* segments to be switched on for digits on 7-segments display
*/
byte segments[] = {
B11111100,
B01100000,
B11011010,
B11110010,
B01100110,
B10110110,
B10111110,
B11100000,
B11111110,
B11110110
};
static unsigned int brightness_pwm;
static unsigned int previousBrightness;
boolean brightnessHasChanged = false;
/**
* used by the 1Hz interrupt
*/
volatile boolean displayNow = false;
/**
* the prevEncoderButton encoderButton from the input pin
*/
static uint8_t prevEncoderButton = 16;
unsigned long millisEncButton = 0; // the last time the output pin was toggled
// Change these two numbers to the pins connected to your encoder.
// Best Performance: both pins have interrupt capability
// Good Performance: only the first pin has interrupt capability
// Low Performance: neither pin has interrupt capability
Encoder myEnc(ENC_PORT3, ENC_PORT6);
long oldPosition = -999;
/**
* RTC based on the DS1307/DS3231SN chip connected via I2C and the Wire library
*/
WireRtcLib rtc;
/**
* Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
*/
OneWire oneWire(ONE_WIRE_BUS);
/**
* Pass our oneWire reference to Dallas Temperature.
*/
DallasTemperature sensors(&oneWire);
void setup() {
brightness_pwm = DEFAULT_BRIGHTNESS;
previousBrightness = DEFAULT_BRIGHTNESS;
pinMode(DATA_PIN, OUTPUT);
pinMode(CLK_PIN, OUTPUT);
pinMode(DIMM_PIN, OUTPUT);
digitalWrite(CLK_PIN, LOW);
clearDisplay();
// handles the push button on the rotary encoder
DDRB &= ~(1 << PB4); // set PB4 as an input
PORTB = (1 << PB4); // Turn on the internal pull-up resistor for PB4
// set PIN_TEMP_ONLY & PIN_CLOCK_ONLY & SQW_INTERRUPT_PIN as an input
//DDRD &= ~( (1 << PIN_TEMP_ONLY) | (1 << PIN_CLOCK_ONLY) | (1 << SQW_INTERRUPT_PIN) );
DDRD = DDRD | B00000000;
//pinMode(PIN_TEMP_ONLY,INPUT);
//pinMode(PIN_CLOCK_ONLY,INPUT);
//pinMode(SQW_INTERRUPT_PIN,INPUT);
// Turn on the internal pull-up resistor for PIN_CLOCK_ONLY & PIN_TEMP_ONLY & SQW_INTERRUPT_PIN
//PORTD = ((1 << PIN_CLOCK_ONLY) | (1 << PIN_TEMP_ONLY) | (1 << SQW_INTERRUPT_PIN));
PORTD = PORTD | B00110100;
// gestion du potentiomètre de luminosité
pinMode(BRIGHTNESS_POT_PIN, INPUT);
Serial.begin(57600);
// set power pin for DS18B20 to output
pinMode(ONE_WIRE_POWER, OUTPUT);
//DDRC |= (1 << ONE_WIRE_POWER);
// Initialisation du DS1307
Wire.begin();
rtc.begin();
sensors.begin();
if(sensors.getDeviceCount() > 0) {
sensors.setResolution(DS18B20_RESOLUTION);
}
// enable SQW, 1Hz
enableSQW();
// register interrupt function to 1Hz line
attachInterrupt(SQW_INTERRUPT_PIN - 2, oneHzInterruptHandler, FALLING); // 1 = digital pin 3, 0 = digital pin 2
WireRtcLib::tm* now = rtc.getTime();
//Serial << "date: " << now->wday << " " << now->mday << "/" << now->mon << "/" << now->year << endl;
Serial << "arduino up and running..." << endl;
}
byte enableSQW(void) {
Wire.beginTransmission(CHRONODOT_ID);
Wire.write(0x0E); // control register
Wire.endTransmission();
Wire.requestFrom(CHRONODOT_ID, 1);
byte ctrl;
if(Wire.available()) {
ctrl = Wire.read();
}
Wire.beginTransmission(CHRONODOT_ID);
Wire.write(0x0E); // control register
Wire.write(ctrl & B11100011); // all bits 0;
Wire.endTransmission();
//Serial << "ctrl: " << ctrl << endl;
return ctrl;
}
void oneHzInterruptHandler(void) {
//Serial.println(" TOP");
// Affichage de l'heure toutes les minutes, sauf si la luminosité vient d'être modifiée via l'encodeur
if(!brightnessHasChanged) {
displayNow = true;
}
brightnessHasChanged = false;
}
/**
* Désactive l'affichage pour mise à jour
*/
void disableDisplay() {
digitalWrite(DIMM_PIN, HIGH);
}
/**
* Active l'affichage suite à mise à jour
*/
void enableDisplay() {
analogWrite(DIMM_PIN, brightness_pwm * 16);
}
/**
* Efface l'affichage: toutes LEDs éteintes
*/
void clearDisplay() {
disableDisplay();
for (int i = 0; i < 4; ++i)
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,B00000000);
enableDisplay();
}
void displayTime(WireRtcLib::tm* time) {
byte digitvalue, bitfield;
disableDisplay();
if(settingsMode != SET_SECONDS_MODE) {
// segment minutes basses
digitvalue = time->min % 10;
bitfield = segments[digitvalue];
if(displayMode == SET_CLOCK_MODE) {
bitfield |= 1;
}
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
// segment minutes hautes
digitvalue = time->min / 10;
bitfield = segments[digitvalue];
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
// segment heures basses
digitvalue = time->hour % 10;
bitfield = segments[digitvalue];
// on fait clignoter le décimal point
if(displayMode == SET_CLOCK_MODE) {
bitfield |= 1;
}
else {
bitfield |= time->sec & 1;
}
//Serial.println(time->sec & 1);
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
// segment heures hautes
digitvalue = time->hour / 10;
// si l'heure est comprise entre 1h et 9h, on n'affiche pas le 0.
bitfield = digitvalue ? segments[digitvalue] : 0;
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
}
else {
// segment minutes basses
digitvalue = time->sec % 10;
bitfield = segments[digitvalue];
if(displayMode == SET_CLOCK_MODE) {
bitfield |= 1;
}
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
// segment minutes hautes
digitvalue = time->sec / 10;
bitfield = segments[digitvalue];
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,B00000000);
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,B00000000);
}
// Les données sont envoyées, on les affiche...
enableDisplay();
}
void displayTemp() {
float temp = getTemp();
unsigned int digits [3];
// partie entière
double iValue;
// partie décimale
double fValue = modf(temp,&iValue);
unsigned int integerValue = (unsigned int) iValue;
// On tient le dernier digit à afficher
unsigned int fractionValue = (unsigned int) (round(fValue * 10));
digits[2] = fractionValue;
//Serial << "temp: " << temp << " => " << "integerValue: " << integerValue << " , fractionValue : " << fractionValue << endl;
if(integerValue >= 10 ) {
digits[1] = integerValue % 10;
digits[0] = integerValue / 10;
}
else {
digits[0] = 0;
digits[1] = integerValue;
}
//Serial << digits[0] << digits[1] << "." << digits[2] << endl;
// on se prépare à envoyer les données
disableDisplay();
// le signe degré...
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,B11000110);
byte bitfield = segments[digits[2]];
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
bitfield = segments[digits[1]];
bitfield |= 1;
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
bitfield = digits[0] ? segments[digits[0]] : 0;
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
// Les données sont envoyées, on les affiche...
enableDisplay();
}
float getTemp() {
// turn DS18B20 sensor on, set HIGH
//PORTC |= (1 << ONE_WIRE_POWER);
digitalWrite(ONE_WIRE_POWER, HIGH);
//Serial << "Requesting temperature from DS18B20... ";
sensors.requestTemperatures();
float temp = sensors.getTempCByIndex(0);
// turn DS18B20 off, set LOW
//PORTC &= ~(1 << ONE_WIRE_POWER);
digitalWrite(ONE_WIRE_POWER, LOW);
//Serial << temp << endl;
return temp;
}
DisplayMode getCurrentDisplayMode () {
//uint8_t switchPin = (PIND & ((1<<PIN_CLOCK_ONLY) | (1<<PIN_TEMP_ONLY)));
//Serial << "switchPin: " << switchPin << endl;
//return SET_CLOCK_MODE;
switch ((PIND & ((1<<PIN_CLOCK_ONLY) | (1<<PIN_TEMP_ONLY)))) {
case 0x10:
return TEMPERATURE_MODE;
case 0x20:
return CLOCK_MODE;
/*case 0x30:
return CLOCK_TEMP_MODE;
break;*/
default:
return CLOCK_TEMP_MODE;
}
}
boolean setBrightness() {
// let's read the encoder position to set the brightness
int encoderValue = readEncoder();
if (encoderValue != 0) {
if (previousBrightness == 15 && encoderValue == -1)
brightness_pwm = 15;
else if (previousBrightness == 0 && encoderValue == 1)
brightness_pwm = 0;
else
brightness_pwm = previousBrightness - encoderValue;
// display the selected brightness level: 1 - lowest, 16 highest
unsigned int level = 16 - brightness_pwm;
//Serial << "level: " << level << endl;
unsigned int digits [3];
if(level >= 10 ) {
digits[1] = level % 10;
digits[0] = level / 10;
}
else {
digits[0] = 0;
digits[1] = level;
}
// on se prépare à envoyer les données
disableDisplay();
byte bitfield = segments[digits[1]];
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
bitfield = segments[digits[0]];
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,bitfield);
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,B00000000);
shiftOut(DATA_PIN,CLK_PIN,LSBFIRST,B00000000);
// Les données sont envoyées, on les affiche...
enableDisplay();
previousBrightness = brightness_pwm;
brightnessHasChanged = true;
}
return brightnessHasChanged;
}
void handleButtonStateChanged() {
uint8_t encoderButton = PINB & (1 << PB4); // reads pin 4 of port B, i.e. D12 which is the rotary encoder switch
unsigned long currentMillis = millis();
if ((unsigned long)(currentMillis - millisEncButton) >= DEBOUNCE) {
// We're not in the debounce period.
if(encoderButton != prevEncoderButton) {
//Serial << "handleButtonStateChanged" << endl;
// Button state changed
// Start debounce period after processing the button
millisEncButton = millis();
prevEncoderButton = encoderButton;
// Do the processing
if (encoderButton) {
//Serial << "handleButtonStateChanged" << endl;
switch (displayMode) {
case SET_CLOCK_MODE :
if(settingsMode == SET_HOURS_MODE) {
settingsMode = SET_MINUTES_MODE;
}
else if (settingsMode == SET_MINUTES_MODE) {
settingsMode = SET_SECONDS_MODE;
displayTime(settingsTime);
}
else if (settingsMode == SET_SECONDS_MODE) {
// let's set the time on the chronodot
//Serial << "setting time: " << settingsTime->hour << ":" << settingsTime->min << ":" << settingsTime->sec << endl;
rtc.setTime(settingsTime);
settingsMode = SETTINGS_OFF;
displayMode = CLOCK_MODE;
}
break;
default:
displayMode = SET_CLOCK_MODE;
settingsMode = SET_HOURS_MODE;
settingsTime = rtc.getTime();
displayTime(settingsTime);
break;
}
}
/*
else {
// Do the low stuff. We don't care about that one as
// we're interested just in the button being pressed
}
*/
}
}
}
long readEncoder() {
long retValue = 0;
long newPosition = myEnc.read();
if (newPosition != oldPosition) {
if (newPosition < oldPosition) {
retValue += 1;
oldPosition = newPosition;
}
else if(newPosition >= oldPosition) {
retValue -= 1;
//value = value == 60 ? 0 : value;
oldPosition = newPosition;
}
Serial << "readEncoder: " << retValue << endl;
}
return retValue;
}
void loop() {
handleButtonStateChanged();
if(displayMode != SET_CLOCK_MODE) {
DisplayMode currentDisplayMode = getCurrentDisplayMode();
setBrightness();
if(!brightnessHasChanged) {
if (displayMode != currentDisplayMode) {
// the display mode has changed! let's reset the timer...
displayNow = true;
displayMode = currentDisplayMode;
//Serial << " =======> display mode has changed: " << displayMode << endl;
}
if(displayNow) {
WireRtcLib::tm* now = rtc.getTime();
switch (displayMode) {
case TEMPERATURE_MODE:
displayTemp();
break;
case CLOCK_MODE:
displayTime(now);
break;
case CLOCK_TEMP_MODE:
uint8_t seconds = now->sec;
if(seconds <= 5 || seconds >= 30 && seconds <= 35) {
displayTemp();
}
else {
displayTime(now);
}
}
displayNow = false;
}
} // if(!brightnessHasChanged)
}
else {
// SET_CLOCK_MODE
//Serial << "SET_CLOCK_MODE : " << settingsMode << endl;
int encoderValue = readEncoder();
if (encoderValue != 0) {
switch (settingsMode) {
case SET_HOURS_MODE:
if (settingsTime->hour == 0 && encoderValue == -1)
settingsTime->hour = 23;
else if (settingsTime->hour == 23 && encoderValue == 1)
settingsTime->hour = 0;
else
settingsTime->hour += encoderValue;
break;
case SET_MINUTES_MODE:
if (settingsTime->min == 0 && encoderValue == -1)
settingsTime->min = 59;
else if (settingsTime->min == 59 && encoderValue == 1)
settingsTime->min = 0;
else
settingsTime->min += encoderValue;
break;
case SET_SECONDS_MODE:
if (settingsTime->sec == 0 && encoderValue == -1)
settingsTime->sec = 59;
else if (settingsTime->sec == 59 && encoderValue == 1)
settingsTime->sec = 0;
else
settingsTime->sec += encoderValue;
break;
}
displayTime(settingsTime);
}
}
}
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
#include "DallasTemperature.h"
extern "C" {
#include "Arduino.h"
}
DallasTemperature::DallasTemperature(OneWire* _oneWire)
#if REQUIRESALARMS
: _AlarmHandler(&defaultAlarmHandler)
#endif
{
_wire = _oneWire;
devices = 0;
parasite = false;
bitResolution = 9;
waitForConversion = true;
}
// initialize the bus
void DallasTemperature::begin(void)
{
DeviceAddress deviceAddress;
_wire->reset_search();
devices = 0; // Reset the number of devices when we enumerate wire devices
while (_wire->search(deviceAddress))
{
if (validAddress(deviceAddress))
{
if (!parasite && readPowerSupply(deviceAddress)) parasite = true;
ScratchPad scratchPad;
readScratchPad(deviceAddress, scratchPad);
bitResolution = max(bitResolution, getResolution(deviceAddress));
devices++;
}
}
}
// returns the number of devices found on the bus
uint8_t DallasTemperature::getDeviceCount(void)
{
return devices;
}
// returns true if address is valid
bool DallasTemperature::validAddress(uint8_t* deviceAddress)
{
return (_wire->crc8(deviceAddress, 7) == deviceAddress[7]);
}
// finds an address at a given index on the bus
// returns true if the device was found
bool DallasTemperature::getAddress(uint8_t* deviceAddress, uint8_t index)
{
uint8_t depth = 0;
_wire->reset_search();
while (depth <= index && _wire->search(deviceAddress))
{
if (depth == index && validAddress(deviceAddress)) return true;
depth++;
}
return false;
}
// attempt to determine if the device at the given address is connected to the bus
bool DallasTemperature::isConnected(uint8_t* deviceAddress)
{
ScratchPad scratchPad;
return isConnected(deviceAddress, scratchPad);
}
// attempt to determine if the device at the given address is connected to the bus
// also allows for updating the read scratchpad
bool DallasTemperature::isConnected(uint8_t* deviceAddress, uint8_t* scratchPad)
{
readScratchPad(deviceAddress, scratchPad);
return (_wire->crc8(scratchPad, 8) == scratchPad[SCRATCHPAD_CRC]);
}
// read device's scratch pad
void DallasTemperature::readScratchPad(uint8_t* deviceAddress, uint8_t* scratchPad)
{
// send the command
_wire->reset();
_wire->select(deviceAddress);
_wire->write(READSCRATCH);
// TODO => collect all comments & use simple loop
// byte 0: temperature LSB
// byte 1: temperature MSB
// byte 2: high alarm temp
// byte 3: low alarm temp
// byte 4: DS18S20: store for crc
// DS18B20 & DS1822: configuration register
// byte 5: internal use & crc
// byte 6: DS18S20: COUNT_REMAIN
// DS18B20 & DS1822: store for crc
// byte 7: DS18S20: COUNT_PER_C
// DS18B20 & DS1822: store for crc
// byte 8: SCRATCHPAD_CRC
//
// for(int i=0; i<9; i++)
// {
// scratchPad[i] = _wire->read();
// }
// read the response
// byte 0: temperature LSB
scratchPad[TEMP_LSB] = _wire->read();
// byte 1: temperature MSB
scratchPad[TEMP_MSB] = _wire->read();
// byte 2: high alarm temp
scratchPad[HIGH_ALARM_TEMP] = _wire->read();
// byte 3: low alarm temp
scratchPad[LOW_ALARM_TEMP] = _wire->read();
// byte 4:
// DS18S20: store for crc
// DS18B20 & DS1822: configuration register
scratchPad[CONFIGURATION] = _wire->read();
// byte 5:
// internal use & crc
scratchPad[INTERNAL_BYTE] = _wire->read();
// byte 6:
// DS18S20: COUNT_REMAIN
// DS18B20 & DS1822: store for crc
scratchPad[COUNT_REMAIN] = _wire->read();
// byte 7:
// DS18S20: COUNT_PER_C
// DS18B20 & DS1822: store for crc
scratchPad[COUNT_PER_C] = _wire->read();
// byte 8:
// SCTRACHPAD_CRC
scratchPad[SCRATCHPAD_CRC] = _wire->read();
_wire->reset();
}
// writes device's scratch pad
void DallasTemperature::writeScratchPad(uint8_t* deviceAddress, const uint8_t* scratchPad)
{
_wire->reset();
_wire->select(deviceAddress);
_wire->write(WRITESCRATCH);
_wire->write(scratchPad[HIGH_ALARM_TEMP]); // high alarm temp
_wire->write(scratchPad[LOW_ALARM_TEMP]); // low alarm temp
// DS18S20 does not use the configuration register
if (deviceAddress[0] != DS18S20MODEL) _wire->write(scratchPad[CONFIGURATION]); // configuration
_wire->reset();
// save the newly written values to eeprom
_wire->write(COPYSCRATCH, parasite);
if (parasite) delay(10); // 10ms delay
_wire->reset();
}
// reads the device's power requirements
bool DallasTemperature::readPowerSupply(uint8_t* deviceAddress)
{
bool ret = false;
_wire->reset();
_wire->select(deviceAddress);
_wire->write(READPOWERSUPPLY);
if (_wire->read_bit() == 0) ret = true;
_wire->reset();
return ret;
}
// set resolution of all devices to 9, 10, 11, or 12 bits
// if new resolution is out of range, it is constrained.
void DallasTemperature::setResolution(uint8_t newResolution)
{
bitResolution = constrain(newResolution, 9, 12);
DeviceAddress deviceAddress;
for (int i=0; i<devices; i++)
{
getAddress(deviceAddress, i);
setResolution(deviceAddress, bitResolution);
}
}
// set resolution of a device to 9, 10, 11, or 12 bits
// if new resolution is out of range, 9 bits is used.
bool DallasTemperature::setResolution(uint8_t* deviceAddress, uint8_t newResolution)
{
ScratchPad scratchPad;
if (isConnected(deviceAddress, scratchPad))
{
// DS18S20 has a fixed 9-bit resolution
if (deviceAddress[0] != DS18S20MODEL)
{
switch (newResolution)
{
case 12:
scratchPad[CONFIGURATION] = TEMP_12_BIT;
break;
case 11:
scratchPad[CONFIGURATION] = TEMP_11_BIT;
break;
case 10:
scratchPad[CONFIGURATION] = TEMP_10_BIT;
break;
case 9:
default:
scratchPad[CONFIGURATION] = TEMP_9_BIT;
break;
}
writeScratchPad(deviceAddress, scratchPad);
}
return true; // new value set
}
return false;
}
// returns the global resolution
uint8_t DallasTemperature::getResolution()
{
return bitResolution;
}
// returns the current resolution of the device, 9-12
// returns 0 if device not found
uint8_t DallasTemperature::getResolution(uint8_t* deviceAddress)
{
if (deviceAddress[0] == DS18S20MODEL) return 9; // this model has a fixed resolution
ScratchPad scratchPad;
if (isConnected(deviceAddress, scratchPad))
{
switch (scratchPad[CONFIGURATION])
{
case TEMP_12_BIT:
return 12;
break; // not needed ?
case TEMP_11_BIT:
return 11;
break;
case TEMP_10_BIT:
return 10;
break;
case TEMP_9_BIT:
return 9;
break;
}
}
return 0;
}
// sets the value of the waitForConversion flag
// TRUE : function requestTemperature() etc returns when conversion is ready
// FALSE: function requestTemperature() etc returns immediately (USE WITH CARE!!)
// (1) programmer has to check if the needed delay has passed
// (2) but the application can do meaningfull things in that time
void DallasTemperature::setWaitForConversion(bool flag)
{
waitForConversion = flag;
}
// gets the value of the waitForConversion flag
bool DallasTemperature::getWaitForConversion()
{
return waitForConversion;
}
// sends command for all devices on the bus to perform a temperature conversion
void DallasTemperature::requestTemperatures()
{
_wire->reset();
_wire->skip();
_wire->write(STARTCONVO, parasite);
// ASYNC mode?
if (false == waitForConversion) return;
switch (bitResolution)
{
case 9:
delay(94);
break;
case 10:
delay(188);
break;
case 11:
delay(375);
break;
case 12:
default:
delay(750);
break;
}
return;
}
// sends command for one device to perform a temperature by address
// returns FALSE if device is disconnected
// returns TRUE otherwise
bool DallasTemperature::requestTemperaturesByAddress(uint8_t* deviceAddress)
{
// check device
ScratchPad scratchPad;
if (false == isConnected(deviceAddress, scratchPad)) return false;
_wire->reset();
_wire->select(deviceAddress);
_wire->write(STARTCONVO, parasite);
// ASYNC mode?
if (false == waitForConversion) return true;
if (deviceAddress[0] == DS18S20MODEL)
{
delay(750); // max value found in datasheet
return true;
}
// other models
switch(scratchPad[CONFIGURATION])
{
case TEMP_9_BIT:
delay(94);
break;
case TEMP_10_BIT:
delay(188);
break;
case TEMP_11_BIT:
delay(375);
break;
case TEMP_12_BIT:
default:
delay(750);
break;
}
return true;
}
// sends command for one device to perform a temp conversion by index
bool DallasTemperature::requestTemperaturesByIndex(uint8_t deviceIndex)
{
DeviceAddress deviceAddress;
getAddress(deviceAddress, deviceIndex);
return requestTemperaturesByAddress(deviceAddress);
}
// Fetch temperature for device index
float DallasTemperature::getTempCByIndex(uint8_t deviceIndex)
{
DeviceAddress deviceAddress;
getAddress(deviceAddress, deviceIndex);
return getTempC((uint8_t*)deviceAddress);
}
// Fetch temperature for device index
float DallasTemperature::getTempFByIndex(uint8_t deviceIndex)
{
return toFahrenheit(getTempCByIndex(deviceIndex));
}
// reads scratchpad and returns the temperature in degrees C
float DallasTemperature::calculateTemperature(uint8_t* deviceAddress, uint8_t* scratchPad)
{
int16_t rawTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB];
switch (deviceAddress[0])
{
case DS18B20MODEL:
case DS1822MODEL:
switch (scratchPad[CONFIGURATION])
{
case TEMP_12_BIT:
return (float)rawTemperature * 0.0625;
break;
case TEMP_11_BIT:
return (float)(rawTemperature >> 1) * 0.125;
break;
case TEMP_10_BIT:
return (float)(rawTemperature >> 2) * 0.25;
break;
case TEMP_9_BIT:
return (float)(rawTemperature >> 3) * 0.5;
break;
}
break;
case DS18S20MODEL:
/*
Resolutions greater than 9 bits can be calculated using the data from
the temperature, COUNT REMAIN and COUNT PER �C registers in the
scratchpad. Note that the COUNT PER �C register is hard-wired to 16
(10h). After reading the scratchpad, the TEMP_READ value is obtained
by truncating the 0.5�C bit (bit 0) from the temperature data. The
extended resolution temperature can then be calculated using the
following equation:
COUNT_PER_C - COUNT_REMAIN
TEMPERATURE = TEMP_READ - 0.25 + --------------------------
COUNT_PER_C
*/
// Good spot. Thanks Nic Johns for your contribution
return (float)(rawTemperature >> 1) - 0.25 +((float)(scratchPad[COUNT_PER_C] - scratchPad[COUNT_REMAIN]) / (float)scratchPad[COUNT_PER_C] );
break;
}
}
// returns temperature in degrees C or DEVICE_DISCONNECTED if the
// device's scratch pad cannot be read successfully.
// the numeric value of DEVICE_DISCONNECTED is defined in
// DallasTemperature.h. It is a large negative number outside the
// operating range of the device
float DallasTemperature::getTempC(uint8_t* deviceAddress)
{
// TODO: Multiple devices (up to 64) on the same bus may take
// some time to negotiate a response
// What happens in case of collision?
ScratchPad scratchPad;
if (isConnected(deviceAddress, scratchPad)) return calculateTemperature(deviceAddress, scratchPad);
return DEVICE_DISCONNECTED;
}
// returns temperature in degrees F
// TODO: - when getTempC returns DEVICE_DISCONNECTED
// -127 gets converted to -196.6 F
float DallasTemperature::getTempF(uint8_t* deviceAddress)
{
return toFahrenheit(getTempC(deviceAddress));
}
// returns true if the bus requires parasite power
bool DallasTemperature::isParasitePowerMode(void)
{
return parasite;
}
#if REQUIRESALARMS
/*
ALARMS:
TH and TL Register Format
BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0
S 2^6 2^5 2^4 2^3 2^2 2^1 2^0
Only bits 11 through 4 of the temperature register are used
in the TH and TL comparison since TH and TL are 8-bit
registers. If the measured temperature is lower than or equal
to TL or higher than or equal to TH, an alarm condition exists
and an alarm flag is set inside the DS18B20. This flag is
updated after every temperature measurement; therefore, if the
alarm condition goes away, the flag will be turned off after
the next temperature conversion.
*/
// sets the high alarm temperature for a device in degrees celsius
// accepts a float, but the alarm resolution will ignore anything
// after a decimal point. valid range is -55C - 125C
void DallasTemperature::setHighAlarmTemp(uint8_t* deviceAddress, char celsius)
{
// make sure the alarm temperature is within the device's range
if (celsius > 125) celsius = 125;
else if (celsius < -55) celsius = -55;
ScratchPad scratchPad;
if (isConnected(deviceAddress, scratchPad))
{
scratchPad[HIGH_ALARM_TEMP] = (uint8_t)celsius;
writeScratchPad(deviceAddress, scratchPad);
}
}
// sets the low alarm temperature for a device in degreed celsius
// accepts a float, but the alarm resolution will ignore anything
// after a decimal point. valid range is -55C - 125C
void DallasTemperature::setLowAlarmTemp(uint8_t* deviceAddress, char celsius)
{
// make sure the alarm temperature is within the device's range
if (celsius > 125) celsius = 125;
else if (celsius < -55) celsius = -55;
ScratchPad scratchPad;
if (isConnected(deviceAddress, scratchPad))
{
scratchPad[LOW_ALARM_TEMP] = (uint8_t)celsius;
writeScratchPad(deviceAddress, scratchPad);
}
}
// returns a char with the current high alarm temperature or
// DEVICE_DISCONNECTED for an address
char DallasTemperature::getHighAlarmTemp(uint8_t* deviceAddress)
{
ScratchPad scratchPad;
if (isConnected(deviceAddress, scratchPad)) return (char)scratchPad[HIGH_ALARM_TEMP];
return DEVICE_DISCONNECTED;
}
// returns a char with the current low alarm temperature or
// DEVICE_DISCONNECTED for an address
char DallasTemperature::getLowAlarmTemp(uint8_t* deviceAddress)
{
ScratchPad scratchPad;
if (isConnected(deviceAddress, scratchPad)) return (char)scratchPad[LOW_ALARM_TEMP];
return DEVICE_DISCONNECTED;
}
// resets internal variables used for the alarm search
void DallasTemperature::resetAlarmSearch()
{
alarmSearchJunction = -1;
alarmSearchExhausted = 0;
for(uint8_t i = 0; i < 7; i++)
alarmSearchAddress[i] = 0;
}
// This is a modified version of the OneWire::search method.
//
// Also added the OneWire search fix documented here:
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295
//
// Perform an alarm search. If this function returns a '1' then it has
// enumerated the next device and you may retrieve the ROM from the
// OneWire::address variable. If there are no devices, no further
// devices, or something horrible happens in the middle of the
// enumeration then a 0 is returned. If a new device is found then
// its address is copied to newAddr. Use
// DallasTemperature::resetAlarmSearch() to start over.
bool DallasTemperature::alarmSearch(uint8_t* newAddr)
{
uint8_t i;
char lastJunction = -1;
uint8_t done = 1;
if (alarmSearchExhausted) return false;
if (!_wire->reset()) return false;
// send the alarm search command
_wire->write(0xEC, 0);
for(i = 0; i < 64; i++)
{
uint8_t a = _wire->read_bit( );
uint8_t nota = _wire->read_bit( );
uint8_t ibyte = i / 8;
uint8_t ibit = 1 << (i & 7);
// I don't think this should happen, this means nothing responded, but maybe if
// something vanishes during the search it will come up.
if (a && nota) return false;
if (!a && !nota)
{
if (i == alarmSearchJunction)
{
// this is our time to decide differently, we went zero last time, go one.
a = 1;
alarmSearchJunction = lastJunction;
}
else if (i < alarmSearchJunction)
{
// take whatever we took last time, look in address
if (alarmSearchAddress[ibyte] & ibit) a = 1;
else
{
// Only 0s count as pending junctions, we've already exhasuted the 0 side of 1s
a = 0;
done = 0;
lastJunction = i;
}
}
else
{
// we are blazing new tree, take the 0
a = 0;
alarmSearchJunction = i;
done = 0;
}
// OneWire search fix
// See: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295
}
if (a) alarmSearchAddress[ibyte] |= ibit;
else alarmSearchAddress[ibyte] &= ~ibit;
_wire->write_bit(a);
}
if (done) alarmSearchExhausted = 1;
for (i = 0; i < 8; i++) newAddr[i] = alarmSearchAddress[i];
return true;
}
// returns true if device address has an alarm condition
// TODO: can this be done with only TEMP_MSB REGISTER (faster)
// if ((char) scratchPad[TEMP_MSB] <= (char) scratchPad[LOW_ALARM_TEMP]) return true;
// if ((char) scratchPad[TEMP_MSB] >= (char) scratchPad[HIGH_ALARM_TEMP]) return true;
bool DallasTemperature::hasAlarm(uint8_t* deviceAddress)
{
ScratchPad scratchPad;
if (isConnected(deviceAddress, scratchPad))
{
float temp = calculateTemperature(deviceAddress, scratchPad);
// check low alarm
if ((char)temp <= (char)scratchPad[LOW_ALARM_TEMP]) return true;
// check high alarm
if ((char)temp >= (char)scratchPad[HIGH_ALARM_TEMP]) return true;
}
// no alarm
return false;
}
// returns true if any device is reporting an alarm condition on the bus
bool DallasTemperature::hasAlarm(void)
{
DeviceAddress deviceAddress;
resetAlarmSearch();
return alarmSearch(deviceAddress);
}
// runs the alarm handler for all devices returned by alarmSearch()
void DallasTemperature::processAlarms(void)
{
resetAlarmSearch();
DeviceAddress alarmAddr;
while (alarmSearch(alarmAddr))
{
if (validAddress(alarmAddr))
_AlarmHandler(alarmAddr);
}
}
// sets the alarm handler
void DallasTemperature::setAlarmHandler(AlarmHandler *handler)
{
_AlarmHandler = handler;
}
// The default alarm handler
void DallasTemperature::defaultAlarmHandler(uint8_t* deviceAddress)
{
}
#endif
// Convert float celsius to fahrenheit
float DallasTemperature::toFahrenheit(float celsius)
{
return (celsius * 1.8) + 32;
}
// Convert float fahrenheit to celsius
float DallasTemperature::toCelsius(float fahrenheit)
{
return (fahrenheit - 32) / 1.8;
}
#if REQUIRESNEW
// MnetCS - Allocates memory for DallasTemperature. Allows us to instance a new object
void* DallasTemperature::operator new(unsigned int size) // Implicit NSS obj size
{
void * p; // void pointer
p = malloc(size); // Allocate memory
memset((DallasTemperature*)p,0,size); // Initalise memory
//!!! CANT EXPLICITLY CALL CONSTRUCTOR - workaround by using an init() methodR - workaround by using an init() method
return (DallasTemperature*) p; // Cast blank region to NSS pointer
}
// MnetCS 2009 - Unallocates the memory used by this instance
void DallasTemperature::operator delete(void* p)
{
DallasTemperature* pNss = (DallasTemperature*) p; // Cast to NSS pointer
pNss->~DallasTemperature(); // Destruct the object
free(p); // Free the memory
}
#endif
#ifndef DallasTemperature_h
#define DallasTemperature_h
#define DALLASTEMPLIBVERSION "3.7.0"
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// set to true to include code for new and delete operators
#ifndef REQUIRESNEW
#define REQUIRESNEW false
#endif
// set to true to include code implementing alarm search functions
#ifndef REQUIRESALARMS
#define REQUIRESALARMS true
#endif
#include <inttypes.h>
#include <OneWire.h>
// Model IDs
#define DS18S20MODEL 0x10
#define DS18B20MODEL 0x28
#define DS1822MODEL 0x22
// OneWire commands
#define STARTCONVO 0x44 // Tells device to take a temperature reading and put it on the scratchpad
#define COPYSCRATCH 0x48 // Copy EEPROM
#define READSCRATCH 0xBE // Read EEPROM
#define WRITESCRATCH 0x4E // Write to EEPROM
#define RECALLSCRATCH 0xB8 // Reload from last known
#define READPOWERSUPPLY 0xB4 // Determine if device needs parasite power
#define ALARMSEARCH 0xEC // Query bus for devices with an alarm condition
// Scratchpad locations
#define TEMP_LSB 0
#define TEMP_MSB 1
#define HIGH_ALARM_TEMP 2
#define LOW_ALARM_TEMP 3
#define CONFIGURATION 4
#define INTERNAL_BYTE 5
#define COUNT_REMAIN 6
#define COUNT_PER_C 7
#define SCRATCHPAD_CRC 8
// Device resolution
#define TEMP_9_BIT 0x1F // 9 bit
#define TEMP_10_BIT 0x3F // 10 bit
#define TEMP_11_BIT 0x5F // 11 bit
#define TEMP_12_BIT 0x7F // 12 bit
// Error Codes
#define DEVICE_DISCONNECTED -127
typedef uint8_t DeviceAddress[8];
class DallasTemperature
{
public:
DallasTemperature(OneWire*);
// initalize bus
void begin(void);
// returns the number of devices found on the bus
uint8_t getDeviceCount(void);
// returns true if address is valid
bool validAddress(uint8_t*);
// finds an address at a given index on the bus
bool getAddress(uint8_t*, const uint8_t);
// attempt to determine if the device at the given address is connected to the bus
bool isConnected(uint8_t*);
// attempt to determine if the device at the given address is connected to the bus
// also allows for updating the read scratchpad
bool isConnected(uint8_t*, uint8_t*);
// read device's scratchpad
void readScratchPad(uint8_t*, uint8_t*);
// write device's scratchpad
void writeScratchPad(uint8_t*, const uint8_t*);
// read device's power requirements
bool readPowerSupply(uint8_t*);
// get global resolution
uint8_t getResolution();
// set global resolution to 9, 10, 11, or 12 bits
void setResolution(uint8_t);
// returns the device resolution, 9-12
uint8_t getResolution(uint8_t*);
// set resolution of a device to 9, 10, 11, or 12 bits
bool setResolution(uint8_t*, uint8_t);
// sets/gets the waitForConversion flag
void setWaitForConversion(bool);
bool getWaitForConversion(void);
// sends command for all devices on the bus to perform a temperature conversion
void requestTemperatures(void);
// sends command for one device to perform a temperature conversion by address
bool requestTemperaturesByAddress(uint8_t*);
// sends command for one device to perform a temperature conversion by index
bool requestTemperaturesByIndex(uint8_t);
// returns temperature in degrees C
float getTempC(uint8_t*);
// returns temperature in degrees F
float getTempF(uint8_t*);
// Get temperature for device index (slow)
float getTempCByIndex(uint8_t);
// Get temperature for device index (slow)
float getTempFByIndex(uint8_t);
// returns true if the bus requires parasite power
bool isParasitePowerMode(void);
#if REQUIRESALARMS
typedef void AlarmHandler(uint8_t*);
// sets the high alarm temperature for a device
// accepts a char. valid range is -55C - 125C
void setHighAlarmTemp(uint8_t*, const char);
// sets the low alarm temperature for a device
// accepts a char. valid range is -55C - 125C
void setLowAlarmTemp(uint8_t*, const char);
// returns a signed char with the current high alarm temperature for a device
// in the range -55C - 125C
char getHighAlarmTemp(uint8_t*);
// returns a signed char with the current low alarm temperature for a device
// in the range -55C - 125C
char getLowAlarmTemp(uint8_t*);
// resets internal variables used for the alarm search
void resetAlarmSearch(void);
// search the wire for devices with active alarms
bool alarmSearch(uint8_t*);
// returns true if ia specific device has an alarm
bool hasAlarm(uint8_t*);
// returns true if any device is reporting an alarm on the bus
bool hasAlarm(void);
// runs the alarm handler for all devices returned by alarmSearch()
void processAlarms(void);
// sets the alarm handler
void setAlarmHandler(AlarmHandler *);
// The default alarm handler
static void defaultAlarmHandler(uint8_t*);
#endif
// convert from celcius to farenheit
static float toFahrenheit(const float);
// convert from farenheit to celsius
static float toCelsius(const float);
#if REQUIRESNEW
// initalize memory area
void* operator new (unsigned int);
// delete memory reference
void operator delete(void*);
#endif
private:
typedef uint8_t ScratchPad[9];
// parasite power on or off
bool parasite;
// used to determine the delay amount needed to allow for the
// temperature conversion to take place
int bitResolution;
// used to requestTemperature with or without delay
bool waitForConversion;
// count of devices on the bus
uint8_t devices;
// Take a pointer to one wire instance
OneWire* _wire;
// reads scratchpad and returns the temperature in degrees C
float calculateTemperature(uint8_t*, uint8_t*);
#if REQUIRESALARMS
// required for alarmSearch
uint8_t alarmSearchAddress[8];
char alarmSearchJunction;
uint8_t alarmSearchExhausted;
// the alarm handler function pointer
AlarmHandler *_AlarmHandler;
#endif
};
#endif
enum DisplayMode {
CLOCK_MODE = 0, // display clock only
TEMPERATURE_MODE = 1, // display temperature only
CLOCK_TEMP_MODE = 2, // display clock and temperature every 25 seconds for 5 seconds
SET_CLOCK_MODE = 3
};
enum SettingsMode {
SETTINGS_OFF = 0,
SET_HOURS_MODE = 1,
SET_MINUTES_MODE = 2,
SET_SECONDS_MODE = 3
};
#include "Encoder.h"
// Yes, all the code is in the header file, to provide the user
// configure options with #define (before they include it), and
// to facilitate some crafty optimizations!
Encoder_internal_state_t * Encoder::interruptArgs[];
/* Encoder Library, for measuring quadrature encoded signals
* http://www.pjrc.com/teensy/td_libs_Encoder.html
* Copyright (c) 2011 PJRC.COM, LLC - Paul Stoffregen <[email protected]>
*
* Version 1.0
*
* 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.
*/
#ifndef Encoder_h_
#define Encoder_h_
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#elif defined(WIRING)
#include "Wiring.h"
#else
#include "WProgram.h"
#include "pins_arduino.h"
#endif
#include "util/direct_pin_read.h"
#if defined(ENCODER_USE_INTERRUPTS) || !defined(ENCODER_DO_NOT_USE_INTERRUPTS)
#define ENCODER_USE_INTERRUPTS
#define ENCODER_ARGLIST_SIZE CORE_NUM_INTERRUPT
#include "util/interrupt_pins.h"
#ifdef ENCODER_OPTIMIZE_INTERRUPTS
#include "util/interrupt_config.h"
#endif
#else
#define ENCODER_ARGLIST_SIZE 0
#endif
// All the data needed by interrupts is consolidated into this ugly struct
// to facilitate assembly language optimizing of the speed critical update.
// The assembly code uses auto-incrementing addressing modes, so the struct
// must remain in exactly this order.
typedef struct {
volatile IO_REG_TYPE * pin1_register;
volatile IO_REG_TYPE * pin2_register;
IO_REG_TYPE pin1_bitmask;
IO_REG_TYPE pin2_bitmask;
uint8_t state;
int32_t position;
} Encoder_internal_state_t;
class Encoder
{
public:
Encoder(uint8_t pin1, uint8_t pin2) {
#ifdef INPUT_PULLUP
pinMode(pin1, INPUT_PULLUP);
pinMode(pin2, INPUT_PULLUP);
#else
pinMode(pin1, INPUT);
digitalWrite(pin1, HIGH);
pinMode(pin2, INPUT);
digitalWrite(pin2, HIGH);
#endif
encoder.pin1_register = PIN_TO_BASEREG(pin1);
encoder.pin1_bitmask = PIN_TO_BITMASK(pin1);
encoder.pin2_register = PIN_TO_BASEREG(pin2);
encoder.pin2_bitmask = PIN_TO_BITMASK(pin2);
encoder.position = 0;
// allow time for a passive R-C filter to charge
// through the pullup resistors, before reading
// the initial state
delayMicroseconds(2000);
uint8_t s = 0;
if (DIRECT_PIN_READ(encoder.pin1_register, encoder.pin1_bitmask)) s |= 1;
if (DIRECT_PIN_READ(encoder.pin2_register, encoder.pin2_bitmask)) s |= 2;
encoder.state = s;
#ifdef ENCODER_USE_INTERRUPTS
interrupts_in_use = 0;
switch (pin1) {
#ifdef CORE_INT0_PIN
case CORE_INT0_PIN:
interruptArgs[0] = &encoder;
attachInterrupt(0, isr0, CHANGE);
break;
#endif
#ifdef CORE_INT1_PIN
case CORE_INT1_PIN:
interruptArgs[1] = &encoder;
attachInterrupt(1, isr1, CHANGE);
break;
#endif
#ifdef CORE_INT2_PIN
case CORE_INT2_PIN:
interruptArgs[2] = &encoder;
attachInterrupt(2, isr2, CHANGE);
break;
#endif
#ifdef CORE_INT3_PIN
case CORE_INT3_PIN:
interruptArgs[3] = &encoder;
attachInterrupt(3, isr3, CHANGE);
break;
#endif
#ifdef CORE_INT4_PIN
case CORE_INT4_PIN:
interruptArgs[4] = &encoder;
attachInterrupt(4, isr4, CHANGE);
break;
#endif
#ifdef CORE_INT5_PIN
case CORE_INT5_PIN:
interruptArgs[5] = &encoder;
attachInterrupt(5, isr5, CHANGE);
break;
#endif
#ifdef CORE_INT6_PIN
case CORE_INT6_PIN:
interruptArgs[6] = &encoder;
attachInterrupt(6, isr6, CHANGE);
break;
#endif
#ifdef CORE_INT7_PIN
case CORE_INT7_PIN:
interruptArgs[7] = &encoder;
attachInterrupt(7, isr7, CHANGE);
break;
#endif
default:
return;
}
interrupts_in_use++;
switch (pin2) {
#ifdef CORE_INT0_PIN
case CORE_INT0_PIN:
interruptArgs[0] = &encoder;
attachInterrupt(0, isr0, CHANGE);
break;
#endif
#ifdef CORE_INT1_PIN
case CORE_INT1_PIN:
interruptArgs[1] = &encoder;
attachInterrupt(1, isr1, CHANGE);
break;
#endif
#ifdef CORE_INT2_PIN
case CORE_INT2_PIN:
interruptArgs[2] = &encoder;
attachInterrupt(2, isr2, CHANGE);
break;
#endif
#ifdef CORE_INT3_PIN
case CORE_INT3_PIN:
interruptArgs[3] = &encoder;
attachInterrupt(3, isr3, CHANGE);
break;
#endif
#ifdef CORE_INT4_PIN
case CORE_INT4_PIN:
interruptArgs[4] = &encoder;
attachInterrupt(4, isr4, CHANGE);
break;
#endif
#ifdef CORE_INT5_PIN
case CORE_INT5_PIN:
interruptArgs[5] = &encoder;
attachInterrupt(5, isr5, CHANGE);
break;
#endif
#ifdef CORE_INT6_PIN
case CORE_INT6_PIN:
interruptArgs[6] = &encoder;
attachInterrupt(6, isr6, CHANGE);
break;
#endif
#ifdef CORE_INT7_PIN
case CORE_INT7_PIN:
interruptArgs[7] = &encoder;
attachInterrupt(7, isr7, CHANGE);
break;
default:
return;
#endif
}
interrupts_in_use++;
#endif
//update_finishup(); // to force linker to include the code
}
#ifdef ENCODER_USE_INTERRUPTS
inline int32_t read() {
if (interrupts_in_use < 2) {
noInterrupts();
update(&encoder);
} else {
noInterrupts();
}
int32_t ret = encoder.position;
interrupts();
return ret;
}
inline void write(int32_t p) {
noInterrupts();
encoder.position = p;
interrupts();
}
#else
inline int32_t read() {
update(&encoder);
return encoder.position;
}
inline void write(int32_t p) {
encoder.position = p;
}
#endif
private:
Encoder_internal_state_t encoder;
#ifdef ENCODER_USE_INTERRUPTS
uint8_t interrupts_in_use;
#endif
public:
static Encoder_internal_state_t * interruptArgs[ENCODER_ARGLIST_SIZE];
// _______ _______
// Pin1 ______| |_______| |______ Pin1
// negative <--- _______ _______ __ --> positive
// Pin2 __| |_______| |_______| Pin2
// new new old old
// pin2 pin1 pin2 pin1 Result
// ---- ---- ---- ---- ------
// 0 0 0 0 no movement
// 0 0 0 1 +1
// 0 0 1 0 -1
// 0 0 1 1 +2 (assume pin1 edges only)
// 0 1 0 0 -1
// 0 1 0 1 no movement
// 0 1 1 0 -2 (assume pin1 edges only)
// 0 1 1 1 +1
// 1 0 0 0 +1
// 1 0 0 1 -2 (assume pin1 edges only)
// 1 0 1 0 no movement
// 1 0 1 1 -1
// 1 1 0 0 +2 (assume pin1 edges only)
// 1 1 0 1 -1
// 1 1 1 0 +1
// 1 1 1 1 no movement
/*
// Simple, easy-to-read "documentation" version :-)
//
void update(void) {
uint8_t s = state & 3;
if (digitalRead(pin1)) s |= 4;
if (digitalRead(pin2)) s |= 8;
switch (s) {
case 0: case 5: case 10: case 15:
break;
case 1: case 7: case 8: case 14:
position++; break;
case 2: case 4: case 11: case 13:
position--; break;
case 3: case 12:
position += 2; break;
default:
position -= 2; break;
}
state = (s >> 2);
}
*/
static void update(Encoder_internal_state_t *arg) {
#if defined(__AVR__)
// The compiler believes this is just 1 line of code, so
// it will inline this function into each interrupt
// handler. That's a tiny bit faster, but grows the code.
// Especially when used with ENCODER_OPTIMIZE_INTERRUPTS,
// the inline nature allows the ISR prologue and epilogue
// to only save/restore necessary registers, for very nice
// speed increase.
asm volatile (
"ld r30, X+" "\n\t"
"ld r31, X+" "\n\t"
"ld r24, Z" "\n\t" // r24 = pin1 input
"ld r30, X+" "\n\t"
"ld r31, X+" "\n\t"
"ld r25, Z" "\n\t" // r25 = pin2 input
"ld r30, X+" "\n\t" // r30 = pin1 mask
"ld r31, X+" "\n\t" // r31 = pin2 mask
"ld r22, X" "\n\t" // r22 = state
"andi r22, 3" "\n\t"
"and r24, r30" "\n\t"
"breq L%=1" "\n\t" // if (pin1)
"ori r22, 4" "\n\t" // state |= 4
"L%=1:" "and r25, r31" "\n\t"
"breq L%=2" "\n\t" // if (pin2)
"ori r22, 8" "\n\t" // state |= 8
"L%=2:" "ldi r30, lo8(pm(L%=table))" "\n\t"
"ldi r31, hi8(pm(L%=table))" "\n\t"
// "L%=2:" "ldi r30, lo8(pm(Ltable))" "\n\t"
// "ldi r31, hi8(pm(Ltable))" "\n\t"
"add r30, r22" "\n\t"
"adc r31, __zero_reg__" "\n\t"
"asr r22" "\n\t"
"asr r22" "\n\t"
"st X+, r22" "\n\t" // store new state
"ld r22, X+" "\n\t"
"ld r23, X+" "\n\t"
"ld r24, X+" "\n\t"
"ld r25, X+" "\n\t"
"ijmp" "\n\t" // jumps to update_finishup()
// TODO move this table to another static function,
// so it doesn't get needlessly duplicated. Easier
// said than done, due to linker issues and inlining
"L%=table:" "\n\t"
"rjmp L%=end" "\n\t" // 0
"rjmp L%=plus1" "\n\t" // 1
"rjmp L%=minus1" "\n\t" // 2
"rjmp L%=plus2" "\n\t" // 3
"rjmp L%=minus1" "\n\t" // 4
"rjmp L%=end" "\n\t" // 5
"rjmp L%=minus2" "\n\t" // 6
"rjmp L%=plus1" "\n\t" // 7
"rjmp L%=plus1" "\n\t" // 8
"rjmp L%=minus2" "\n\t" // 9
"rjmp L%=end" "\n\t" // 10
"rjmp L%=minus1" "\n\t" // 11
"rjmp L%=plus2" "\n\t" // 12
"rjmp L%=minus1" "\n\t" // 13
"rjmp L%=plus1" "\n\t" // 14
"rjmp L%=end" "\n\t" // 15
"L%=minus2:" "\n\t"
"subi r22, 2" "\n\t"
"sbci r23, 0" "\n\t"
"sbci r24, 0" "\n\t"
"sbci r25, 0" "\n\t"
"rjmp L%=store" "\n\t"
"L%=minus1:" "\n\t"
"subi r22, 1" "\n\t"
"sbci r23, 0" "\n\t"
"sbci r24, 0" "\n\t"
"sbci r25, 0" "\n\t"
"rjmp L%=store" "\n\t"
"L%=plus2:" "\n\t"
"subi r22, 254" "\n\t"
"rjmp L%=z" "\n\t"
"L%=plus1:" "\n\t"
"subi r22, 255" "\n\t"
"L%=z:" "sbci r23, 255" "\n\t"
"sbci r24, 255" "\n\t"
"sbci r25, 255" "\n\t"
"L%=store:" "\n\t"
"st -X, r25" "\n\t"
"st -X, r24" "\n\t"
"st -X, r23" "\n\t"
"st -X, r22" "\n\t"
"L%=end:" "\n"
: : "x" (arg) : "r22", "r23", "r24", "r25", "r30", "r31");
#else
uint8_t p1val = DIRECT_PIN_READ(arg->pin1_register, arg->pin1_bitmask);
uint8_t p2val = DIRECT_PIN_READ(arg->pin2_register, arg->pin2_bitmask);
uint8_t state = arg->state & 3;
if (p1val) state |= 4;
if (p2val) state |= 8;
arg->state = (state >> 2);
switch (state) {
case 1: case 7: case 8: case 14:
arg->position++;
return;
case 2: case 4: case 11: case 13:
arg->position--;
return;
case 3: case 12:
arg->position += 2;
return;
case 6: case 9:
arg->position += 2;
return;
}
#endif
}
/*
#if defined(__AVR__)
// TODO: this must be a no inline function
// even noinline does not seem to solve difficult
// problems with this. Oh well, it was only meant
// to shrink code size - there's no performance
// improvement in this, only code size reduction.
__attribute__((noinline)) void update_finishup(void) {
asm volatile (
"ldi r30, lo8(pm(Ltable))" "\n\t"
"ldi r31, hi8(pm(Ltable))" "\n\t"
"Ltable:" "\n\t"
"rjmp L%=end" "\n\t" // 0
"rjmp L%=plus1" "\n\t" // 1
"rjmp L%=minus1" "\n\t" // 2
"rjmp L%=plus2" "\n\t" // 3
"rjmp L%=minus1" "\n\t" // 4
"rjmp L%=end" "\n\t" // 5
"rjmp L%=minus2" "\n\t" // 6
"rjmp L%=plus1" "\n\t" // 7
"rjmp L%=plus1" "\n\t" // 8
"rjmp L%=minus2" "\n\t" // 9
"rjmp L%=end" "\n\t" // 10
"rjmp L%=minus1" "\n\t" // 11
"rjmp L%=plus2" "\n\t" // 12
"rjmp L%=minus1" "\n\t" // 13
"rjmp L%=plus1" "\n\t" // 14
"rjmp L%=end" "\n\t" // 15
"L%=minus2:" "\n\t"
"subi r22, 2" "\n\t"
"sbci r23, 0" "\n\t"
"sbci r24, 0" "\n\t"
"sbci r25, 0" "\n\t"
"rjmp L%=store" "\n\t"
"L%=minus1:" "\n\t"
"subi r22, 1" "\n\t"
"sbci r23, 0" "\n\t"
"sbci r24, 0" "\n\t"
"sbci r25, 0" "\n\t"
"rjmp L%=store" "\n\t"
"L%=plus2:" "\n\t"
"subi r22, 254" "\n\t"
"rjmp L%=z" "\n\t"
"L%=plus1:" "\n\t"
"subi r22, 255" "\n\t"
"L%=z:" "sbci r23, 255" "\n\t"
"sbci r24, 255" "\n\t"
"sbci r25, 255" "\n\t"
"L%=store:" "\n\t"
"st -X, r25" "\n\t"
"st -X, r24" "\n\t"
"st -X, r23" "\n\t"
"st -X, r22" "\n\t"
"L%=end:" "\n"
: : : "r22", "r23", "r24", "r25", "r30", "r31");
}
#endif
*/
#if defined(ENCODER_USE_INTERRUPTS) && !defined(ENCODER_OPTIMIZE_INTERRUPTS)
#ifdef CORE_INT0_PIN
static void isr0(void) { update(interruptArgs[0]); }
#endif
#ifdef CORE_INT1_PIN
static void isr1(void) { update(interruptArgs[1]); }
#endif
#ifdef CORE_INT2_PIN
static void isr2(void) { update(interruptArgs[2]); }
#endif
#ifdef CORE_INT3_PIN
static void isr3(void) { update(interruptArgs[3]); }
#endif
#ifdef CORE_INT4_PIN
static void isr4(void) { update(interruptArgs[4]); }
#endif
#ifdef CORE_INT5_PIN
static void isr5(void) { update(interruptArgs[5]); }
#endif
#ifdef CORE_INT6_PIN
static void isr6(void) { update(interruptArgs[6]); }
#endif
#ifdef CORE_INT7_PIN
static void isr7(void) { update(interruptArgs[7]); }
#endif
#endif
};
#if defined(ENCODER_USE_INTERRUPTS) && defined(ENCODER_OPTIMIZE_INTERRUPTS)
#if defined(__AVR__)
#if defined(INT0_vect) && CORE_NUM_INTERRUPT > 0
ISR(INT0_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(0)]); }
#endif
#if defined(INT1_vect) && CORE_NUM_INTERRUPT > 1
ISR(INT1_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(1)]); }
#endif
#if defined(INT2_vect) && CORE_NUM_INTERRUPT > 2
ISR(INT2_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(2)]); }
#endif
#if defined(INT3_vect) && CORE_NUM_INTERRUPT > 3
ISR(INT3_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(3)]); }
#endif
#if defined(INT4_vect) && CORE_NUM_INTERRUPT > 4
ISR(INT4_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(4)]); }
#endif
#if defined(INT5_vect) && CORE_NUM_INTERRUPT > 5
ISR(INT5_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(5)]); }
#endif
#if defined(INT6_vect) && CORE_NUM_INTERRUPT > 6
ISR(INT6_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(6)]); }
#endif
#if defined(INT7_vect) && CORE_NUM_INTERRUPT > 7
ISR(INT7_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(7)]); }
#endif
#endif // AVR
#endif // ENCODER_OPTIMIZE_INTERRUPTS
#endif
/**
* The DATA_IN pin
*/
#define DATA_PIN 8
/**
* The DIMM_IN pin should be pulled to LOW to enable display,
* also used for brightness with PWM (using analogWrite())
*/
#define DIMM_PIN 9
//11
/**
* The CLK_IN pin
*/
#define CLK_PIN 10
/**
* DS18B20 data wire is plugged into port 7 on the Arduino
*/
#define ONE_WIRE_BUS 7
#define ENC_PORT3 3
#define ENC_PORT6 6
/**
* DS18B20 Power pin is connected on pin A3
*/
#define ONE_WIRE_POWER A3
/**
* Brightness potientiometer pin is connected on pin A1
*/
#define BRIGHTNESS_POT_PIN A1
#define SQW_INTERRUPT_PIN 2
#define PIN_CLOCK_ONLY 4
#define PIN_TEMP_ONLY 5
#define PB4 4 //D12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment