Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3359d0ef47 | ||
|
47ac8c0e91 | ||
|
bc5212e502 | ||
|
8633326518 | ||
|
4aedff4fa8 | ||
|
b21452d98d | ||
|
d698f10a08 | ||
|
1a0eda0d59 | ||
|
54917a0b83 | ||
|
b61587aa7b | ||
|
0867eccc3a | ||
|
33215645db | ||
|
b1fbe102c9 | ||
|
efdcb0d2ba | ||
|
42711b30d6 | ||
|
89732cfc6a | ||
|
5b3c090601 |
63
.gitattributes
vendored
63
.gitattributes
vendored
@ -1,63 +0,0 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,6 +0,0 @@
|
||||
/.vs
|
||||
/Debug
|
||||
/*.opensdf
|
||||
/*.sdf
|
||||
/*.opendb
|
||||
/*.suo
|
112
.vscode/c_cpp_properties.json
vendored
Normal file
112
.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Mac",
|
||||
"includePath": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"/opt/arduino-1.8.5/hardware/arduino/avr/cores/arduino",
|
||||
"/opt/arduino-1.8.5/hardware/tools/avr/avr/include",
|
||||
"/opt/arduino-1.8.5/hardware/arduino/avr/variants/standard",
|
||||
"${workspaceRoot}",
|
||||
"${workspaceRoot}/src"
|
||||
],
|
||||
"defines": [
|
||||
"ARDUINO=160",
|
||||
"__AVR__",
|
||||
"UBRRH",
|
||||
"__AVR_ATmega328P__",
|
||||
"VS_INTELLISENSE"
|
||||
],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"macFrameworkPath": [
|
||||
"/System/Library/Frameworks",
|
||||
"/Library/Frameworks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"/usr/include/c++/4.9",
|
||||
"/usr/include/x86_64-linux-gnu/c++/4.9",
|
||||
"/usr/include/c++/4.9/backward",
|
||||
"/usr/lib/gcc/x86_64-linux-gnu/4.9/include",
|
||||
"/usr/local/include",
|
||||
"/usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed",
|
||||
"/usr/include/x86_64-linux-gnu",
|
||||
"/usr/include",
|
||||
"/opt/arduino-1.8.5/hardware/arduino/avr/cores/arduino",
|
||||
"/opt/arduino-1.8.5/hardware/tools/avr/avr/include",
|
||||
"/opt/arduino-1.8.5/hardware/arduino/avr/variants/standard",
|
||||
"${workspaceRoot}",
|
||||
"${workspaceRoot}/src"
|
||||
],
|
||||
"defines": [
|
||||
"ARDUINO=160",
|
||||
"__AVR__",
|
||||
"UBRRH",
|
||||
"__AVR_ATmega328P__",
|
||||
"VS_INTELLISENSE"
|
||||
],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/usr/include/c++/4.9",
|
||||
"/usr/include/x86_64-linux-gnu/c++/4.9",
|
||||
"/usr/include/c++/4.9/backward",
|
||||
"/usr/lib/gcc/x86_64-linux-gnu/4.9/include",
|
||||
"/usr/local/include",
|
||||
"/usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed",
|
||||
"/usr/include/x86_64-linux-gnu",
|
||||
"/usr/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++14"
|
||||
},
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include",
|
||||
"/opt/arduino-1.8.5/hardware/arduino/avr/cores/arduino",
|
||||
"/opt/arduino-1.8.5/hardware/tools/avr/avr/include",
|
||||
"/opt/arduino-1.8.5/hardware/arduino/avr/variants/standard",
|
||||
"${workspaceRoot}",
|
||||
"${workspaceRoot}/src"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE",
|
||||
"ARDUINO=160",
|
||||
"__AVR__",
|
||||
"UBRRH",
|
||||
"__AVR_ATmega328P__",
|
||||
"VS_INTELLISENSE"
|
||||
],
|
||||
"intelliSenseMode": "msvc-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 3
|
||||
}
|
187
FakeD2413.ino
Normal file
187
FakeD2413.ino
Normal file
@ -0,0 +1,187 @@
|
||||
/* Ian Fleet 2018
|
||||
|
||||
All files, software, schematics and designs are provided as-is with no warranty.
|
||||
All files, software, schematics and designs are for experimental/hobby use.
|
||||
Under no circumstances should any part be used for critical systems where safety,
|
||||
life or property depends upon it. You are responsible for all use.
|
||||
You are free to use, modify, derive or otherwise extend for your own purposes
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// This example emulates a DS2413 device on an Arduino UNO or ATTINY85
|
||||
// note : physical DS2413 devices found in 2018 are often clones with
|
||||
// a device code different to the Maxim datasheet
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "OneWireSlave.h"
|
||||
#include "comptime.h"
|
||||
|
||||
// This is the pin that will be used for one-wire data
|
||||
// On Arduino Uno, you can use pin 2 or pin 3
|
||||
|
||||
Pin oneWireData(2); // PB2 only attiny85 pin with rising/falling interrupts
|
||||
|
||||
//Pin led(0);
|
||||
|
||||
|
||||
// This sample emulates a DS2413 device , so we start by defining the available commands
|
||||
|
||||
const byte DS2413_FAMILY_ID = 0x3A; // Maxim DS2413 device code
|
||||
const byte CLONE_FAMILY_ID = 0x85; // clone device code
|
||||
const byte DS2413_ACCESS_READ = 0xF5;
|
||||
const byte DS2413_ACCESS_WRITE = 0x5A;
|
||||
const byte DS2413_ACK_SUCCESS = 0xAA;
|
||||
|
||||
// generate unique id
|
||||
const byte owROM[7] = { DS2413_FAMILY_ID, SERIAL_NUMBER};
|
||||
// will be calculated in begin:---------------^^^^
|
||||
|
||||
// or use fixed id - make sure it doesn't conflict with another device
|
||||
//const byte owROM[7] = { 0x3A, 0x00, 0x55, 0xAA, 0x00, 0x11, 0x22 };
|
||||
|
||||
|
||||
#define PIOA 3 // (pin 3)
|
||||
#define PIOB 4 // (pin 4)
|
||||
|
||||
uint8_t latch = 0;
|
||||
uint8_t statusbyte1 = 0;
|
||||
uint8_t statusbyte2 = 0;
|
||||
|
||||
enum DeviceState
|
||||
{
|
||||
DS_WaitingReset,
|
||||
DS_WaitingCommand,
|
||||
DS_WaitingStatus1,
|
||||
DS_WaitingStatus2,
|
||||
};
|
||||
volatile DeviceState state = DS_WaitingReset;
|
||||
|
||||
// scratchpad
|
||||
volatile byte scratchpad[2];
|
||||
volatile byte response[2];
|
||||
|
||||
|
||||
// This function will be called each time the OneWire library has an event to notify (reset, error, byte received)
|
||||
void owReceive(OneWireSlave::ReceiveEvent evt, byte data);
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
void setup()
|
||||
{
|
||||
// led.outputMode();
|
||||
// led.writeLow();
|
||||
// OSCCAL = 85;
|
||||
// Setup the OneWire library
|
||||
OWSlave.setReceiveCallback(&owReceive);
|
||||
OWSlave.begin(owROM, oneWireData.getPinNumber());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
void loop()
|
||||
{
|
||||
delay(10);
|
||||
|
||||
cli();//disable interrupts
|
||||
// Be sure to not block interrupts for too long, OneWire timing is very tight for some operations. 1 or 2 microseconds (yes, microseconds, not milliseconds) can be too much depending on your master controller, but then it's equally unlikely that you block exactly at the moment where it matters.
|
||||
// This can be mitigated by using error checking and retry in your high-level communication protocol. A good thing to do anyway.
|
||||
|
||||
sei();//enable interrupts
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
static uint8_t getstatus() {
|
||||
uint8_t c = 0;
|
||||
if (latch & 0x01) c |= 0x02;
|
||||
if (digitalRead(PIOA)) c |= 0x01;
|
||||
if (latch & 0x02) c |= 0x08;
|
||||
if (digitalRead(PIOB)) c |= 0x04;
|
||||
uint8_t x = (~c) << 4;
|
||||
return x + c;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
static void port(int PIO, bool stat) {
|
||||
if (stat) {
|
||||
digitalWrite(PIO, HIGH);
|
||||
pinMode(PIO, INPUT);
|
||||
} else {
|
||||
pinMode(PIO, OUTPUT);
|
||||
digitalWrite(PIO, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
static void set(uint8_t val) {
|
||||
latch = val;
|
||||
port(PIOA, latch & 1);
|
||||
port(PIOB, latch & 2);
|
||||
//TODO copy latch to EEPROM
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
void owReceive(OneWireSlave::ReceiveEvent evt, byte data)
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case OneWireSlave::RE_Byte:
|
||||
switch (state)
|
||||
{
|
||||
case DS_WaitingCommand:
|
||||
switch (data)
|
||||
{
|
||||
case DS2413_ACCESS_WRITE:
|
||||
state = DS_WaitingStatus1;
|
||||
break;
|
||||
|
||||
case DS2413_ACCESS_READ:
|
||||
state = DS_WaitingReset;
|
||||
scratchpad[0] = getstatus();
|
||||
OWSlave.beginWrite((const byte*)scratchpad, 1, 0);
|
||||
|
||||
break;
|
||||
|
||||
//case :
|
||||
|
||||
// break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case DS_WaitingStatus1:
|
||||
statusbyte1 = data;
|
||||
state = DS_WaitingStatus2;
|
||||
break;
|
||||
|
||||
case DS_WaitingStatus2:
|
||||
statusbyte2 = data;
|
||||
if (statusbyte1 != ~statusbyte2) {
|
||||
set(statusbyte1);
|
||||
response[0] = DS2413_ACK_SUCCESS;
|
||||
} else {
|
||||
response[0] = 0x11; // mark error - real DS2413 does not do this
|
||||
}
|
||||
response[1] = getstatus();
|
||||
OWSlave.beginWrite((const byte*)response, 2, 0);
|
||||
state = DS_WaitingCommand;
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case OneWireSlave::RE_Reset:
|
||||
state = DS_WaitingCommand;
|
||||
break;
|
||||
|
||||
case OneWireSlave::RE_Error:
|
||||
state = DS_WaitingReset;
|
||||
break;
|
||||
}
|
||||
}
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Youen Toupin, aka neuoy
|
||||
Copyright (c) 2018 Youen Toupin, aka neuoy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
87
LowLevel.h
87
LowLevel.h
@ -4,7 +4,10 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef VS_INTELLISENSE
|
||||
#define __attribute__()
|
||||
#define __attribute__(...)
|
||||
#define digitalPinToPort(pin) 0
|
||||
#define digitalPinToBitMask(pin) 0
|
||||
#define portInputRegister(arg1) 0
|
||||
#endif
|
||||
|
||||
#if ARDUINO >= 100
|
||||
@ -26,20 +29,38 @@
|
||||
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask))
|
||||
|
||||
#if defined (__AVR_ATtiny85__)
|
||||
#define CLEARINTERRUPT GIFR |= (1 << INTF0)
|
||||
#include "UserTimer.h" //ATtiny-support based on TinyCore1 Arduino-core for ATtiny at http://github.com/Coding-Badly/TinyCore1.git
|
||||
|
||||
/* Note : The attiny85 clock speed = 16mhz (fuses L 0xF1, H 0xDF. E oxFF
|
||||
OSCCAL VALUE must also be calibrated to 16mhz
|
||||
|
||||
*/
|
||||
#define CLEARINTERRUPT GIFR |= (1 << INTF0) | (1<<PCIF);
|
||||
#define USERTIMER_COMPA_vect TIMER1_COMPA_vect
|
||||
|
||||
__attribute__((always_inline)) static inline void UserTimer_Init( void )
|
||||
{
|
||||
UserTimer_SetToPowerup();
|
||||
UserTimer_SetWaveformGenerationMode(UserTimer_(CTC_OCR));
|
||||
TCCR1 = 0; //stop the timer
|
||||
TCNT1 = 0;
|
||||
//GTCCR |= (1<<PSR1); //reset the prescaler
|
||||
TIMSK = 0; // clear timer interrupts enable
|
||||
}
|
||||
__attribute__((always_inline)) static inline void UserTimer_Run(short skipTicks)
|
||||
__attribute__((always_inline)) static inline void UserTimer_Run(int skipTicks)
|
||||
{
|
||||
UserTimer_SetCount(0);
|
||||
UserTimer_SetOutputCompareMatchAndClear(skipTicks);
|
||||
UserTimer_ClockSelect(UserTimer_(Prescale_Value_64));
|
||||
|
||||
TCNT1 = 0; //zero the timer
|
||||
GTCCR |= (1 << PSR1); //reset the prescaler
|
||||
OCR1A = skipTicks; //set the compare value
|
||||
TCCR1 |= (1 << CTC1) | (0 << CS13) | (1 << CS12) | (1 << CS11) | (0 << CS10);//32 prescaler
|
||||
//TCCR1 |= (1 << CTC1) | (0 << CS13) | (1 << CS12) | (0 << CS11) | (0 << CS10);//8 prescaler
|
||||
TIMSK |= (1 << OCIE1A); //interrupt on Compare Match A
|
||||
}
|
||||
#define UserTimer_Stop() UserTimer_ClockSelect(UserTimer_(Stopped))
|
||||
|
||||
__attribute__((always_inline)) static inline void UserTimer_Stop()
|
||||
{
|
||||
TIMSK = 0; //&= ~(1 << OCIE1A);// clear timer interrupt enable
|
||||
TCCR1 = 0;
|
||||
}
|
||||
|
||||
|
||||
#elif defined (__AVR_ATmega328P__)
|
||||
#define CLEARINTERRUPT EIFR |= (1 << INTF0)
|
||||
@ -49,8 +70,7 @@ __attribute__((always_inline)) static inline void UserTimer_Init( void )
|
||||
{
|
||||
TCCR1A = 0;
|
||||
TCCR1B = 0;
|
||||
// enable timer compare interrupt
|
||||
TIMSK1 |= (1 << OCIE1A);
|
||||
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
|
||||
}
|
||||
__attribute__((always_inline)) static inline void UserTimer_Run(short skipTicks)
|
||||
{
|
||||
@ -109,19 +129,20 @@ __attribute__((always_inline)) static inline void UserTimer_Run(short skipTicks)
|
||||
#error "Please define I/O register types here"
|
||||
#endif
|
||||
|
||||
|
||||
class Pin
|
||||
{
|
||||
private:
|
||||
private:
|
||||
volatile IO_REG_TYPE *reg_;
|
||||
IO_REG_TYPE mask_;
|
||||
byte interruptNumber_;
|
||||
byte pinNumber_;
|
||||
|
||||
public:
|
||||
public:
|
||||
Pin()
|
||||
: mask_(0)
|
||||
, reg_(0)
|
||||
, interruptNumber_((byte)-1)
|
||||
, interruptNumber_((byte) - 1)
|
||||
, pinNumber_(255)
|
||||
{ }
|
||||
|
||||
@ -135,26 +156,44 @@ public:
|
||||
{
|
||||
case 2: interruptNumber_ = 0; break;
|
||||
case 3: interruptNumber_ = 1; break;
|
||||
default: interruptNumber_ = (byte)-1;
|
||||
default: interruptNumber_ = (byte) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline byte getPinNumber() { return pinNumber_; }
|
||||
inline byte getPinNumber() {
|
||||
return pinNumber_;
|
||||
}
|
||||
|
||||
inline void inputMode() { DIRECT_MODE_INPUT(reg_, mask_); }
|
||||
inline void outputMode() { DIRECT_MODE_OUTPUT(reg_, mask_); }
|
||||
inline void inputMode() {
|
||||
DIRECT_MODE_INPUT(reg_, mask_);
|
||||
}
|
||||
inline void outputMode() {
|
||||
DIRECT_MODE_OUTPUT(reg_, mask_);
|
||||
}
|
||||
|
||||
inline bool read() { return DIRECT_READ(reg_, mask_) == 1; }
|
||||
inline void writeLow() { DIRECT_WRITE_LOW(reg_, mask_); }
|
||||
inline void writeHigh() { DIRECT_WRITE_HIGH(reg_, mask_); }
|
||||
inline void write(bool value) { if (value) writeHigh(); else writeLow(); }
|
||||
inline bool read() {
|
||||
return DIRECT_READ(reg_, mask_) == 1;
|
||||
}
|
||||
inline void writeLow() {
|
||||
DIRECT_WRITE_LOW(reg_, mask_);
|
||||
}
|
||||
inline void writeHigh() {
|
||||
DIRECT_WRITE_HIGH(reg_, mask_);
|
||||
}
|
||||
inline void write(bool value) {
|
||||
if (value) writeHigh();
|
||||
else writeLow();
|
||||
}
|
||||
|
||||
inline void attachInterrupt(void (*handler)(), int mode)
|
||||
{
|
||||
CLEARINTERRUPT; // clear any pending interrupt (we want to call the handler only for interrupts happening after it is attached)
|
||||
::attachInterrupt(interruptNumber_, handler, mode);
|
||||
}
|
||||
inline void detachInterrupt() { ::detachInterrupt(interruptNumber_); }
|
||||
|
||||
inline void detachInterrupt() {
|
||||
::detachInterrupt(interruptNumber_);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,32 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2013
|
||||
VisualStudioVersion = 12.0.31101.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OneWireIO", "OneWireIO.vcxproj", "{3B500971-1570-460F-81C3-22AC3B7764B9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|Mixed Platforms = Debug|Mixed Platforms
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|Mixed Platforms = Release|Mixed Platforms
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{3B500971-1570-460F-81C3-22AC3B7764B9}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -1,107 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{3B500971-1570-460F-81C3-22AC3B7764B9}</ProjectGuid>
|
||||
<RootNamespace>OneWireIO</RootNamespace>
|
||||
<ProjectName>OneWireIO</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>D:\Outils\Arduino\hardware\arduino\avr\cores\arduino;D:\Outils\Arduino\hardware\tools\avr\avr\include;C:\Program Files %28x86%29\Arduino\hardware\arduino\avr\cores\arduino;C:\Program Files %28x86%29\Arduino\hardware\tools\avr\avr\include</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);ARDUINO=160;__AVR__;UBRRH;__AVR_ATmega328P__;VS_INTELLISENSE</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>if exist $(ProjectDir)$(IntDir)$(ProjectName).ino.elf (
|
||||
echo Elf generated
|
||||
) else (
|
||||
echo no output
|
||||
exit /B 1
|
||||
)</Command>
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent />
|
||||
<CustomBuild>
|
||||
<Command>
|
||||
</Command>
|
||||
</CustomBuild>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="OneWireIO.ino">
|
||||
<FileType>Document</FileType>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">if exist $(ProjectDir)$(IntDir)$(ProjectName).ino.elf del $(ProjectDir)$(IntDir)$(ProjectName).ino.elf
|
||||
"C:\Program Files (x86)\Arduino\arduino.exe" --upload --pref build.path=$(ProjectDir)$(Configuration) %(FullPath) | more
|
||||
</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">always_build_$(ProjectConfiguration).dummy</Outputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Building and uploading sketch...</Message>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="OneWireSlave.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="LowLevel.h" />
|
||||
<ClInclude Include="OneWireSlave.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="OneWireSlave.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="LowLevel.h" />
|
||||
<ClInclude Include="OneWireSlave.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="OneWireIO.ino" />
|
||||
</ItemGroup>
|
||||
</Project>
|
101
OneWireSlave.cpp
101
OneWireSlave.cpp
@ -11,19 +11,40 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
const unsigned long ResetMinDuration = 480;
|
||||
const unsigned long ResetMaxDuration = 900;
|
||||
#if defined (__AVR_ATtiny85__)
|
||||
|
||||
const unsigned long PresenceWaitDuration = 15;
|
||||
const unsigned long PresenceDuration = 200;
|
||||
const unsigned long ResetMinDuration = 480; // min 480us / 484us measured
|
||||
const unsigned long ResetMaxDuration = 640; //
|
||||
|
||||
const unsigned long ReadBitSamplingTime = 25;
|
||||
const unsigned long PresenceWaitDuration = 35; // spec 15us to 60us / 40us measured
|
||||
|
||||
const unsigned long SendBitDuration = 35;
|
||||
const unsigned long PresenceDuration = 150; // spec 60us to 240us / 148us measured
|
||||
|
||||
const byte ReceiveCommand = (byte)-1;
|
||||
const unsigned long ReadBitSamplingTime = 15; // spec > 15us to 60us / 31us measured
|
||||
|
||||
void(*timerEvent)() = 0;
|
||||
// SendBitDuration varies the bus release time to indicate a "1"
|
||||
// measured total send bit duration is 63us versus 60us in the spec
|
||||
const unsigned long SendBitDuration = 20; // bus release time spec 15us / measured 19us
|
||||
|
||||
|
||||
#elif defined (__AVR_ATmega328P__)
|
||||
|
||||
const unsigned long ResetMinDuration = 480;
|
||||
const unsigned long ResetMaxDuration = 900;
|
||||
|
||||
const unsigned long PresenceWaitDuration = 15;
|
||||
|
||||
const unsigned long PresenceDuration = 200;
|
||||
|
||||
const unsigned long ReadBitSamplingTime = 25;
|
||||
|
||||
const unsigned long SendBitDuration = 35;
|
||||
|
||||
#endif
|
||||
|
||||
const byte ReceiveCommand = (byte) - 1;
|
||||
|
||||
void(*timerEvent)() = 0;
|
||||
}
|
||||
|
||||
OneWireSlave OWSlave;
|
||||
@ -32,8 +53,8 @@ byte OneWireSlave::rom_[8];
|
||||
byte OneWireSlave::scratchpad_[8];
|
||||
Pin OneWireSlave::pin_;
|
||||
|
||||
unsigned long OneWireSlave::resetStart_;
|
||||
unsigned long OneWireSlave::lastReset_;
|
||||
unsigned long OneWireSlave::resetStart_ = 0;;
|
||||
unsigned long OneWireSlave::lastReset_ = 0;
|
||||
|
||||
void(*OneWireSlave::receiveBitCallback_)(bool bit, bool error);
|
||||
void(*OneWireSlave::bitSentCallback_)(bool error);
|
||||
@ -79,7 +100,7 @@ ISR(USERTIMER_COMPA_vect) // timer1 interrupt
|
||||
void OneWireSlave::begin(const byte* rom, byte pinNumber)
|
||||
{
|
||||
pin_ = Pin(pinNumber);
|
||||
resetStart_ = (unsigned long)-1;
|
||||
resetStart_ = (unsigned long) - 1;
|
||||
lastReset_ = 0;
|
||||
|
||||
memcpy(rom_, rom, 7);
|
||||
@ -143,7 +164,7 @@ void OneWireSlave::beginWrite(const byte* bytes, short numBytes, void(*complete)
|
||||
|
||||
void OneWireSlave::endWrite_(bool error, bool resetInterrupts)
|
||||
{
|
||||
if(resetInterrupts)
|
||||
if (resetInterrupts)
|
||||
beginWaitReset_();
|
||||
|
||||
if (sendingClientBytes_)
|
||||
@ -231,16 +252,33 @@ byte OneWireSlave::crc8(const byte* data, short numBytes)
|
||||
return crc;
|
||||
}
|
||||
|
||||
#if defined (__AVR_ATtiny85__)
|
||||
|
||||
void OneWireSlave::setTimerEvent_(short delayMicroSeconds, void(*handler)())
|
||||
{
|
||||
delayMicroSeconds -= 8; // remove overhead (tuned on attiny85, values 0 - 10 work ok)
|
||||
|
||||
short skipTicks = delayMicroSeconds / 2; // 16mhz clock, prescaler = 32
|
||||
if (skipTicks < 1) skipTicks = 1;
|
||||
timerEvent = handler;
|
||||
UserTimer_Run(skipTicks);
|
||||
}
|
||||
|
||||
#elif defined (__AVR_ATmega328P__)
|
||||
|
||||
void OneWireSlave::setTimerEvent_(short delayMicroSeconds, void(*handler)())
|
||||
{
|
||||
delayMicroSeconds -= 10; // remove overhead (tuned on Arduino Uno)
|
||||
|
||||
// prescaler 64
|
||||
short skipTicks = (delayMicroSeconds - 3) / 4; // round the micro seconds delay to a number of ticks to skip (4us per tick, so 4us must skip 0 tick, 8us must skip 1 tick, etc.)
|
||||
if (skipTicks < 1) skipTicks = 1;
|
||||
timerEvent = handler;
|
||||
UserTimer_Run(skipTicks);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void OneWireSlave::disableTimer_()
|
||||
{
|
||||
UserTimer_Stop();
|
||||
@ -289,7 +327,7 @@ void OneWireSlave::beginResetDetectionSendZero_()
|
||||
void OneWireSlave::cancelResetDetection_()
|
||||
{
|
||||
disableTimer_();
|
||||
resetStart_ = (unsigned long)-1;
|
||||
resetStart_ = (unsigned long) - 1;
|
||||
}
|
||||
|
||||
void OneWireSlave::resetCheck_()
|
||||
@ -386,9 +424,11 @@ void OneWireSlave::beginWaitReset_()
|
||||
disableTimer_();
|
||||
pin_.inputMode();
|
||||
pin_.attachInterrupt(&OneWireSlave::waitReset_, CHANGE);
|
||||
resetStart_ = (unsigned int)-1;
|
||||
//resetStart_ = (unsigned int) - 1;
|
||||
resetStart_ = (unsigned long) - 1;
|
||||
}
|
||||
|
||||
|
||||
void OneWireSlave::waitReset_()
|
||||
{
|
||||
onEnterInterrupt_();
|
||||
@ -396,22 +436,28 @@ void OneWireSlave::waitReset_()
|
||||
unsigned long now = micros();
|
||||
if (state)
|
||||
{
|
||||
if (resetStart_ == (unsigned int)-1)
|
||||
if (resetStart_ == (unsigned long) - 1)
|
||||
{
|
||||
onLeaveInterrupt_();
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long resetDuration = now - resetStart_;
|
||||
resetStart_ = (unsigned int)-1;
|
||||
static unsigned long resetDuration = now - resetStart_;
|
||||
//resetStart_ = (unsigned int) - 1;
|
||||
resetStart_ = (unsigned long) - 1;
|
||||
if (resetDuration >= ResetMinDuration)
|
||||
{
|
||||
if (resetDuration > ResetMaxDuration)
|
||||
{
|
||||
ERROR("Reset too long");
|
||||
onLeaveInterrupt_();
|
||||
return;
|
||||
}
|
||||
|
||||
// The following test has been removed because of a bug which causes the value of
|
||||
// resetduration to exceed ResetMaxDuration intermittently. This happens
|
||||
// only on the attiny platform - possibly due to the implementation of
|
||||
// micros() in the attiny core
|
||||
|
||||
// if (resetDuration > ResetMaxDuration)
|
||||
// {
|
||||
// ERROR("Reset too long");
|
||||
// onLeaveInterrupt_();
|
||||
// return;
|
||||
// }
|
||||
|
||||
lastReset_ = now;
|
||||
pin_.detachInterrupt();
|
||||
@ -420,6 +466,9 @@ void OneWireSlave::waitReset_()
|
||||
endWrite_(true, false);
|
||||
if (clientReceiveCallback_ != 0)
|
||||
clientReceiveCallback_(RE_Reset, 0);
|
||||
} else
|
||||
{
|
||||
//ERROR("Reset too short");
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -614,7 +663,7 @@ void OneWireSlave::searchRomOnBitReceived_(bool bit, bool error)
|
||||
}
|
||||
}
|
||||
|
||||
void OneWireSlave::beginWriteBytes_(const byte* data, short numBytes, void(*complete)(bool error))
|
||||
void OneWireSlave::beginWriteBytes_(const byte * data, short numBytes, void(*complete)(bool error))
|
||||
{
|
||||
sendBuffer_ = data;
|
||||
bufferLength_ = numBytes;
|
||||
@ -661,7 +710,7 @@ void OneWireSlave::bitSent_(bool error)
|
||||
beginSendBit_(bit, &OneWireSlave::bitSent_);
|
||||
}
|
||||
|
||||
void OneWireSlave::beginReceiveBytes_(byte* buffer, short numBytes, void(*complete)(bool error))
|
||||
void OneWireSlave::beginReceiveBytes_(byte * buffer, short numBytes, void(*complete)(bool error))
|
||||
{
|
||||
recvBuffer_ = buffer;
|
||||
bufferLength_ = numBytes;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
class OneWireSlave
|
||||
{
|
||||
public:
|
||||
public:
|
||||
enum ReceiveEvent
|
||||
{
|
||||
RE_Reset, //!< The master has sent a general reset
|
||||
@ -21,13 +21,19 @@ public:
|
||||
void end();
|
||||
|
||||
//! Sets (or replaces) a function to be called when something is received. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next byte.
|
||||
void setReceiveCallback(void(*callback)(ReceiveEvent evt, byte data)) { clientReceiveCallback_ = callback; }
|
||||
void setReceiveCallback(void(*callback)(ReceiveEvent evt, byte data)) {
|
||||
clientReceiveCallback_ = callback;
|
||||
}
|
||||
|
||||
//! Sets (or replaces) a function to be called when a bit is received. The byte reception callback is called after that if the received bit was the last of a byte. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next bit.
|
||||
void setReceiveBitCallback(void(*callback)(bool bit)) { clientReceiveBitCallback_ = callback; }
|
||||
void setReceiveBitCallback(void(*callback)(bool bit)) {
|
||||
clientReceiveBitCallback_ = callback;
|
||||
}
|
||||
|
||||
//! Sets (or replaces) a function to be called when the library has a message to log, if the functionality is enabled in OneWireSlave.cpp. This is for debugging purposes.
|
||||
void setLogCallback(void(*callback)(const char* message)) { logCallback_ = callback; }
|
||||
void setLogCallback(void(*callback)(const char* message)) {
|
||||
logCallback_ = callback;
|
||||
}
|
||||
|
||||
//! Writes the specified bytes synchronously. This function blocks until the write operation has finished. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs.
|
||||
bool write(const byte* bytes, short numBytes);
|
||||
@ -44,11 +50,12 @@ public:
|
||||
//! Cancels any pending write operation, started by writeBit or write. If this function is called before the master asked for a bit, then nothing is sent to the master.
|
||||
void stopWrite();
|
||||
|
||||
//! Sets the alarmed state, that is used when the master makes a conditional search of alarmed devices.
|
||||
void alarmed(bool value);
|
||||
|
||||
static byte crc8(const byte* data, short numBytes);
|
||||
|
||||
private:
|
||||
private:
|
||||
static void setTimerEvent_(short delayMicroSeconds, void(*handler)());
|
||||
static void disableTimer_();
|
||||
|
||||
@ -99,7 +106,7 @@ private:
|
||||
static void endSendBitZero_();
|
||||
static void resetCheck_();
|
||||
|
||||
private:
|
||||
private:
|
||||
static byte rom_[8];
|
||||
static byte scratchpad_[8];
|
||||
static Pin pin_;
|
||||
|
20
README.md
20
README.md
@ -1,22 +1,8 @@
|
||||
# OneWireArduinoSlave
|
||||
An arduino library to communicate using the Dallas one-wire protocol, where the Arduino takes the role of a slave. Entirely implemented using interrupts, you can perform other tasks while communication is handled in background.
|
||||
|
||||
## 1-wire introduction
|
||||
1-wire allows communication over long distances (100m and more, see Dallas documentation for details) with a single wire (plus a ground wire). You can put as much devices as you want on the same wire (they communicate one at a time). 1-wire also allows to send power over the data wire (parasitic power), but, though I haven't tried, I don't believe it would work with an Arduino. You'll need a separate 5V power source, which, if it comes next to your data wire, means you need 3 wires (5V, data, and ground). You'll also need a master controller, for example the USB adapter DS9490R, to connect to a computer, that will control communication with all 1-wire devices.
|
||||
Notes
|
||||
|
||||
## How to use this library
|
||||
This library allows you to emulate existing 1-wire devices with an Arduino, or to create your own protocol. All low-level details are handled by the library, such as reset detection, ROM matching, byte sending and receiving. Look at the demo sketch to see an example.
|
||||
Added support for the attiny85 platform at 16mhz
|
||||
|
||||
There are only 3 important functions:
|
||||
- `void setReceiveCallback(void(*callback)(ReceiveEvent evt, byte data))` sets the function that will be called back when an event occurs, such as when a byte is received
|
||||
- `void begin(byte* rom, byte pinNumber)` starts the library, which will respond to the provided ROM (must be unique on your network) on the specified Arduino pin. The ROM is cloned by the library, so you can discard your buffer immediately if you want.
|
||||
- `void write(byte* bytes, short numBytes, void(*complete)(bool error))` starts writing one or more bytes, and will call the provided callback (optional) when it's done. The buffer you provide here must stay available until the end of the write operation, which happens in background. Do not use local variables.
|
||||
|
||||
## Notes about the interrupt-based implementation
|
||||
Since the library is implemented using interrupts, none of its functions will block: you can continue execute your code immediately.
|
||||
|
||||
This also means callbacks are called from interrupt handlers, so you must make them very short to not block further communication.
|
||||
|
||||
You must also be careful when you explicitely block interrupts, as the 1-wire protocol has very tight timings, especially when writing bytes (which also happens when searching for device ROMs): a delay of 3 microseconds (yes, microseconds, not milliseconds) can be enough for some (quite intolerant) masters to miss a bit.
|
||||
|
||||
But if your code only blocks interrupts for reasonably short time, the probability to block exactly at the bad moment is low, so you can easily mitigate the issue by adding CRC checks in your high-level communication protocol, and retrying when an error is detected. This is an important thing to do anyway because 1-wire does not natively perform any error checking (excepted for ROM operations which already contain a CRC byte). Standard 1-wire devices also include CRC checks in their specific protocols.
|
||||
Included a OneWireSlave implementation of a DS2413 (FakeDS2413) for both the ATMEGA328 and ATTINY85 platforms
|
||||
|
21
comptime.h
Normal file
21
comptime.h
Normal file
@ -0,0 +1,21 @@
|
||||
// copied - source unknown
|
||||
|
||||
#define YY (((__DATE__[9]-'0')*16 + __DATE__[10])-'0')
|
||||
#define MMM (\
|
||||
__DATE__ [2] == '?' ? 1 \
|
||||
: __DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? 1 : 6) \
|
||||
: __DATE__ [2] == 'b' ? 2 \
|
||||
: __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? 3 : 4) \
|
||||
: __DATE__ [2] == 'y' ? 5 \
|
||||
: __DATE__ [2] == 'l' ? 7 \
|
||||
: __DATE__ [2] == 'g' ? 8 \
|
||||
: __DATE__ [2] == 'p' ? 9 \
|
||||
: __DATE__ [2] == 't' ? 16 \
|
||||
: __DATE__ [2] == 'v' ? 17 \
|
||||
: 18)
|
||||
#define DD ( __DATE__[4] == '?' ? 1 : ((__DATE__[4] == ' ' ? 0 : ((__DATE__[4] - '0') * 16)) + __DATE__[5] - '0'))
|
||||
#define TT (((__TIME__[0] - '0') * 16) + __TIME__[1] - '0')
|
||||
#define MM (((__TIME__[3] - '0') * 16) + __TIME__[4] - '0')
|
||||
#define SS (((__TIME__[6] - '0') * 16) + __TIME__[7] - '0')
|
||||
|
||||
#define SERIAL_NUMBER YY, MMM, DD, TT, MM, SS
|
@ -1,5 +1,4 @@
|
||||
#include "Arduino.h"
|
||||
#include "LowLevel.h"
|
||||
#include "OneWireSlave.h"
|
||||
|
||||
// This is the pin that will be used for one-wire data (depending on your arduino model, you are limited to a few choices, because some pins don't have complete interrupt support)
|
16
examples/FakeDS18B20/README.md
Normal file
16
examples/FakeDS18B20/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
This is a **DS18B20** emulator, i.e. it turns an Arduino Uno into a device roughly equivalent to an actual DS18B20 temperature sensor. Excepted in this sample it doesn't actually sense temperature (because the Arduino lacks the hardware for this), it just returns 42 as if that was the expected answer.
|
||||
|
||||
To make it work, you will need to connect your Arduino Uno to a 1-wire master (for example an USB adapter on a computer).
|
||||
|
||||
You can then connect:
|
||||
|
||||
- a GND pin of your Uno to your 1-wire network ground.
|
||||
- the pin "2" of your Uno to the data line of your 1-wire network. You may use "3" as well but need to change the code.
|
||||
|
||||
You will see 28.000000000002 probe that always return 42 as temperature :
|
||||
|
||||
$ cat 28.000000000002/temperature
|
||||
42
|
||||
Conversion timing is emulated as well.
|
||||
|
||||
If you don't use an Arduino Uno, you may need to adjust which pin you connect as the data line (it needs to have hardware interrupts), and make the corresponding change in the example source code. A few microcontrollers are compatible with the library, check the full list in [the documentation](../../extras/documentation.md).
|
83
extras/documentation.md
Normal file
83
extras/documentation.md
Normal file
@ -0,0 +1,83 @@
|
||||
# OneWireSlave library documentation
|
||||
|
||||
This library allows you to emulate existing 1-wire devices with an Arduino, or to create your own protocol. All low-level details are handled by the library, such as reset detection, ROM matching, byte sending and receiving. Look at the demo sketch to see an example.
|
||||
|
||||
## Compatible boards
|
||||
|
||||
If you have tested the library on another board, please send a pull-request (or just tell me which board) to update this list.
|
||||
|
||||
### ATmega328
|
||||
|
||||
- Arduino Uno (tested by [neuoy](https://github.com/neuoy))
|
||||
|
||||
## Getting started
|
||||
|
||||
See example "FakeDS18B20", and the associated [documentation](examples/FakeDS18B20/README.md), for a working use case of the library.
|
||||
|
||||
## Library reference
|
||||
|
||||
### setReceiveCallback
|
||||
|
||||
`void setReceiveCallback(void(*callback)(ReceiveEvent evt, byte data))`
|
||||
|
||||
Sets the function that will be called back when an event occurs, such as when a byte is received.
|
||||
|
||||
### begin
|
||||
|
||||
`void begin(byte* rom, byte pinNumber)`
|
||||
|
||||
Starts the library, which will respond to the provided ROM (must be unique on your network) on the specified Arduino pin. The ROM is cloned by the library, so you can discard your buffer immediately if you want.
|
||||
|
||||
### write
|
||||
`bool write(const byte* bytes, short numBytes)`
|
||||
|
||||
Writes the specified bytes synchronously. This function blocks until the write operation has finished. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs.
|
||||
|
||||
### beginWrite
|
||||
`void beginWrite(const byte* bytes, short numBytes, void(*complete)(bool error))`
|
||||
|
||||
Starts sending the specified bytes. They will be sent in the background, and the buffer must remain valid and unchanged until the write operation has finished or is cancelled. The optional callback is used to notify when the bytes are sent, or if an error occured. Callbacks are executed from interrupts and should be as short as possible. If `bytes` is null or `numBytes` is 0, nothing is sent, which is equivalent to calling `stopWrite`. In any case, calling the write function will cancel the previous write operation if it didn't complete yet.
|
||||
|
||||
### setReceiveBitCallback
|
||||
`void setReceiveBitCallback(void(*callback)(bool bit))`
|
||||
|
||||
Sets (or replaces) a function to be called when a bit is received. The byte reception callback is called after that if the received bit was the last of a byte. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next bit.
|
||||
|
||||
### writeBit
|
||||
`bool writeBit(bool value)`
|
||||
|
||||
Writes a single bit synchronously. This function blocks until the bit is sent. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs.
|
||||
|
||||
### beginWriteBit
|
||||
`void beginWriteBit(bool value, bool repeat = false, void(*bitSent)(bool error) = 0)`
|
||||
|
||||
Sets a bit that will be sent next time the master asks for one. Optionnaly, the repeat parameter can be set to true to continue sending the same bit each time. In both cases, the send operation can be canceled by calling `stopWrite`.
|
||||
|
||||
### stopWrite
|
||||
`void stopWrite()`
|
||||
|
||||
Cancels any pending write operation, started by writeBit or write. If this function is called before the master asked for a bit, then nothing is sent to the master.
|
||||
|
||||
### alarmed
|
||||
`void alarmed(bool value)`
|
||||
|
||||
Sets the alarmed state, that is used when the master makes a conditional search of alarmed devices.
|
||||
|
||||
### setLogCallback(void(*callback)(const char* message))
|
||||
`void setLogCallback(void(*callback)(const char* message))`
|
||||
|
||||
Sets (or replaces) a function to be called when the library has a message to log, if the functionality is enabled in OneWireSlave.cpp (uncomment line `#define ERROR_MESSAGES`). This is for debugging purposes.
|
||||
|
||||
### end
|
||||
`void end()`
|
||||
|
||||
Stops all 1-wire activities, which frees hardware resources for other purposes.
|
||||
|
||||
## Notes about the interrupt-based implementation
|
||||
Since the library is implemented using interrupts, none of its functions will block: you can continue execute your code immediately.
|
||||
|
||||
This also means callbacks are called from interrupt handlers, so you must make them very short to not block further communication.
|
||||
|
||||
You must also be careful when you explicitely block interrupts, as the 1-wire protocol has very tight timings, especially when writing bytes (which also happens when searching for device ROMs): a delay of 3 microseconds (yes, microseconds, not milliseconds) can be enough for some (quite intolerant) masters to miss a bit.
|
||||
|
||||
But if your code only blocks interrupts for reasonably short time, the probability to block exactly at the bad moment is low, so you can easily mitigate the issue by adding CRC checks in your high-level communication protocol, and retrying when an error is detected. This is an important thing to do anyway because 1-wire does not natively perform any error checking (excepted for ROM operations which already contain a CRC byte). Standard 1-wire devices also include CRC checks in their specific protocols.
|
34
keywords.txt
Normal file
34
keywords.txt
Normal file
@ -0,0 +1,34 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For OneWireSlave
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
ReceiveEvent KEYWORD1
|
||||
OneWireSlave KEYWORD1
|
||||
Pin KEYWORD1
|
||||
OWSlave KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
setReceiveCallback KEYWORD2
|
||||
setReceiveBitCallback KEYWORD2
|
||||
write KEYWORD2
|
||||
writeBit KEYWORD2
|
||||
stopWrite KEYWORD2
|
||||
alarmed KEYWORD2
|
||||
crc8 KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
RE_Reset LITERAL1
|
||||
RE_Byte LITERAL1
|
||||
RE_Error LITERAL1
|
9
library.properties
Normal file
9
library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=OneWireSlave
|
||||
version=0.1
|
||||
author=Youen Toupin, aka neuoy
|
||||
maintainer=Youen Toupin, aka neuoy
|
||||
sentence=Communicate using the Dallas one-wire protocol, where the Arduino takes the role of a slave.
|
||||
paragraph=This library allows you to emulate existing 1-wire devices with an Arduino, or to create your own protocol. All low-level details are handled by the library, such as reset detection, ROM matching, byte sending and receiving.
|
||||
category=Signal Input/Output
|
||||
url=https://github.com/neuoy/OneWireArduinoSlave
|
||||
architectures=*
|
715
src/OneWireSlave.cpp
Normal file
715
src/OneWireSlave.cpp
Normal file
@ -0,0 +1,715 @@
|
||||
#include "OneWireSlave.h"
|
||||
|
||||
// uncomment this line to enable sending messages along with errors (but takes more program memory)
|
||||
//#define ERROR_MESSAGES
|
||||
|
||||
#ifdef ERROR_MESSAGES
|
||||
#define ERROR(msg) error_(msg)
|
||||
#else
|
||||
#define ERROR(msg) error_(0)
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
const unsigned long ResetMinDuration = 480;
|
||||
const unsigned long ResetMaxDuration = 900;
|
||||
|
||||
const unsigned long PresenceWaitDuration = 15;
|
||||
const unsigned long PresenceDuration = 200;
|
||||
|
||||
const unsigned long ReadBitSamplingTime = 25;
|
||||
|
||||
const unsigned long SendBitDuration = 35;
|
||||
|
||||
const byte ReceiveCommand = (byte)-1;
|
||||
|
||||
void(*timerEvent)() = 0;
|
||||
}
|
||||
|
||||
OneWireSlave OWSlave;
|
||||
|
||||
byte OneWireSlave::rom_[8];
|
||||
byte OneWireSlave::scratchpad_[8];
|
||||
Pin OneWireSlave::pin_;
|
||||
|
||||
unsigned long OneWireSlave::resetStart_;
|
||||
unsigned long OneWireSlave::lastReset_;
|
||||
|
||||
void(*OneWireSlave::receiveBitCallback_)(bool bit, bool error);
|
||||
void(*OneWireSlave::bitSentCallback_)(bool error);
|
||||
void(*OneWireSlave::clientReceiveCallback_)(ReceiveEvent evt, byte data);
|
||||
void(*OneWireSlave::clientReceiveBitCallback_)(bool bit);
|
||||
|
||||
byte OneWireSlave::receivingByte_;
|
||||
|
||||
byte OneWireSlave::searchRomBytePos_;
|
||||
byte OneWireSlave::searchRomBitPos_;
|
||||
bool OneWireSlave::searchRomInverse_;
|
||||
bool OneWireSlave::resumeCommandFlag_;
|
||||
bool OneWireSlave::alarmedFlag_;
|
||||
|
||||
const byte* OneWireSlave::sendBuffer_;
|
||||
byte* OneWireSlave::recvBuffer_;
|
||||
short OneWireSlave::bufferLength_;
|
||||
byte OneWireSlave::bufferBitPos_;
|
||||
short OneWireSlave::bufferPos_;
|
||||
void(*OneWireSlave::receiveBytesCallback_)(bool error);
|
||||
void(*OneWireSlave::sendBytesCallback_)(bool error);
|
||||
|
||||
volatile bool OneWireSlave::waitingSynchronousWriteToComplete_;
|
||||
volatile bool OneWireSlave::synchronousWriteError_;
|
||||
|
||||
bool OneWireSlave::sendingClientBytes_;
|
||||
|
||||
bool OneWireSlave::singleBit_;
|
||||
bool OneWireSlave::singleBitRepeat_;
|
||||
void(*OneWireSlave::singleBitSentCallback_)(bool error);
|
||||
|
||||
void(*OneWireSlave::logCallback_)(const char* message);
|
||||
|
||||
|
||||
ISR(USERTIMER_COMPA_vect) // timer1 interrupt
|
||||
{
|
||||
UserTimer_Stop(); // disable clock
|
||||
void(*event)() = timerEvent;
|
||||
timerEvent = 0;
|
||||
event();
|
||||
}
|
||||
|
||||
void OneWireSlave::begin(const byte* rom, byte pinNumber)
|
||||
{
|
||||
pin_ = Pin(pinNumber);
|
||||
resetStart_ = (unsigned long)-1;
|
||||
lastReset_ = 0;
|
||||
|
||||
memcpy(rom_, rom, 7);
|
||||
rom_[7] = crc8(rom_, 7);
|
||||
|
||||
resumeCommandFlag_ = false;
|
||||
alarmedFlag_ = false;
|
||||
|
||||
clientReceiveBitCallback_ = 0;
|
||||
sendingClientBytes_ = false;
|
||||
|
||||
// log("Enabling 1-wire library")
|
||||
|
||||
cli(); // disable interrupts
|
||||
pin_.inputMode();
|
||||
pin_.writeLow(); // make sure the internal pull-up resistor is disabled
|
||||
|
||||
// prepare hardware timer
|
||||
UserTimer_Init();
|
||||
|
||||
// start 1-wire activity
|
||||
beginWaitReset_();
|
||||
sei(); // enable interrupts
|
||||
}
|
||||
|
||||
void OneWireSlave::end()
|
||||
{
|
||||
// log("Disabling 1-wire library");
|
||||
|
||||
cli();
|
||||
disableTimer_();
|
||||
pin_.detachInterrupt();
|
||||
releaseBus_();
|
||||
sei();
|
||||
}
|
||||
|
||||
bool OneWireSlave::write(const byte* bytes, short numBytes)
|
||||
{
|
||||
// TODO: put the arduino to sleep between interrupts to save power?
|
||||
waitingSynchronousWriteToComplete_ = true;
|
||||
beginWrite(bytes, numBytes, &OneWireSlave::onSynchronousWriteComplete_);
|
||||
while (waitingSynchronousWriteToComplete_)
|
||||
delay(1);
|
||||
return !synchronousWriteError_;
|
||||
}
|
||||
|
||||
void OneWireSlave::onSynchronousWriteComplete_(bool error)
|
||||
{
|
||||
synchronousWriteError_ = error;
|
||||
waitingSynchronousWriteToComplete_ = false;
|
||||
}
|
||||
|
||||
void OneWireSlave::beginWrite(const byte* bytes, short numBytes, void(*complete)(bool error))
|
||||
{
|
||||
cli();
|
||||
endWrite_(true);
|
||||
sendingClientBytes_ = true;
|
||||
beginWriteBytes_(bytes, numBytes, complete == 0 ? noOpCallback_ : complete);
|
||||
sei();
|
||||
}
|
||||
|
||||
void OneWireSlave::endWrite_(bool error, bool resetInterrupts)
|
||||
{
|
||||
if(resetInterrupts)
|
||||
beginWaitReset_();
|
||||
|
||||
if (sendingClientBytes_)
|
||||
{
|
||||
sendingClientBytes_ = false;
|
||||
if (sendBytesCallback_ != 0)
|
||||
{
|
||||
void(*callback)(bool error) = sendBytesCallback_;
|
||||
sendBytesCallback_ = noOpCallback_;
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
else if (singleBitSentCallback_ != 0)
|
||||
{
|
||||
void(*callback)(bool) = singleBitSentCallback_;
|
||||
singleBitSentCallback_ = 0;
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
|
||||
bool OneWireSlave::writeBit(bool value)
|
||||
{
|
||||
// TODO: put the arduino to sleep between interrupts to save power?
|
||||
waitingSynchronousWriteToComplete_ = true;
|
||||
beginWriteBit(value, false, &OneWireSlave::onSynchronousWriteComplete_);
|
||||
while (waitingSynchronousWriteToComplete_)
|
||||
delay(1);
|
||||
return !synchronousWriteError_;
|
||||
}
|
||||
|
||||
void OneWireSlave::beginWriteBit(bool value, bool repeat, void(*bitSent)(bool))
|
||||
{
|
||||
cli();
|
||||
endWrite_(true);
|
||||
|
||||
singleBit_ = value;
|
||||
singleBitRepeat_ = repeat;
|
||||
singleBitSentCallback_ = bitSent;
|
||||
beginSendBit_(value, &OneWireSlave::onSingleBitSent_);
|
||||
sei();
|
||||
}
|
||||
|
||||
void OneWireSlave::onSingleBitSent_(bool error)
|
||||
{
|
||||
if (!error && singleBitRepeat_)
|
||||
{
|
||||
beginSendBit_(singleBit_, &OneWireSlave::onSingleBitSent_);
|
||||
}
|
||||
else
|
||||
{
|
||||
beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_);
|
||||
}
|
||||
|
||||
if (singleBitSentCallback_ != 0)
|
||||
{
|
||||
void(*callback)(bool) = singleBitSentCallback_;
|
||||
singleBitSentCallback_ = 0;
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
|
||||
void OneWireSlave::stopWrite()
|
||||
{
|
||||
beginWrite(0, 0, 0);
|
||||
}
|
||||
|
||||
void OneWireSlave::alarmed(bool value)
|
||||
{
|
||||
alarmedFlag_ = value;
|
||||
}
|
||||
|
||||
byte OneWireSlave::crc8(const byte* data, short numBytes)
|
||||
{
|
||||
byte crc = 0;
|
||||
|
||||
while (numBytes--) {
|
||||
byte inbyte = *data++;
|
||||
for (byte i = 8; i; i--) {
|
||||
byte mix = (crc ^ inbyte) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix) crc ^= 0x8C;
|
||||
inbyte >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void OneWireSlave::setTimerEvent_(short delayMicroSeconds, void(*handler)())
|
||||
{
|
||||
delayMicroSeconds -= 10; // remove overhead (tuned on Arduino Uno)
|
||||
|
||||
short skipTicks = (delayMicroSeconds - 3) / 4; // round the micro seconds delay to a number of ticks to skip (4us per tick, so 4us must skip 0 tick, 8us must skip 1 tick, etc.)
|
||||
if (skipTicks < 1) skipTicks = 1;
|
||||
timerEvent = handler;
|
||||
UserTimer_Run(skipTicks);
|
||||
}
|
||||
|
||||
void OneWireSlave::disableTimer_()
|
||||
{
|
||||
UserTimer_Stop();
|
||||
}
|
||||
|
||||
void OneWireSlave::onEnterInterrupt_()
|
||||
{
|
||||
}
|
||||
|
||||
void OneWireSlave::onLeaveInterrupt_()
|
||||
{
|
||||
}
|
||||
|
||||
void OneWireSlave::error_(const char* message)
|
||||
{
|
||||
if (logCallback_ != 0)
|
||||
logCallback_(message);
|
||||
endWrite_(true);
|
||||
if (clientReceiveCallback_ != 0)
|
||||
clientReceiveCallback_(RE_Error, 0);
|
||||
}
|
||||
|
||||
void OneWireSlave::pullLow_()
|
||||
{
|
||||
pin_.outputMode();
|
||||
pin_.writeLow();
|
||||
}
|
||||
|
||||
void OneWireSlave::releaseBus_()
|
||||
{
|
||||
pin_.inputMode();
|
||||
}
|
||||
|
||||
void OneWireSlave::beginResetDetection_()
|
||||
{
|
||||
setTimerEvent_(ResetMinDuration - 50, &OneWireSlave::resetCheck_);
|
||||
resetStart_ = micros() - 50;
|
||||
}
|
||||
|
||||
void OneWireSlave::beginResetDetectionSendZero_()
|
||||
{
|
||||
setTimerEvent_(ResetMinDuration - SendBitDuration - 50, &OneWireSlave::resetCheck_);
|
||||
resetStart_ = micros() - SendBitDuration - 50;
|
||||
}
|
||||
|
||||
void OneWireSlave::cancelResetDetection_()
|
||||
{
|
||||
disableTimer_();
|
||||
resetStart_ = (unsigned long)-1;
|
||||
}
|
||||
|
||||
void OneWireSlave::resetCheck_()
|
||||
{
|
||||
onEnterInterrupt_();
|
||||
if (!pin_.read())
|
||||
{
|
||||
pin_.attachInterrupt(&OneWireSlave::waitReset_, CHANGE);
|
||||
// log("Reset detected during another operation");
|
||||
}
|
||||
onLeaveInterrupt_();
|
||||
}
|
||||
|
||||
void OneWireSlave::beginReceiveBit_(void(*completeCallback)(bool bit, bool error))
|
||||
{
|
||||
receiveBitCallback_ = completeCallback;
|
||||
pin_.attachInterrupt(&OneWireSlave::receive_, FALLING);
|
||||
}
|
||||
|
||||
void OneWireSlave::receive_()
|
||||
{
|
||||
onEnterInterrupt_();
|
||||
|
||||
pin_.detachInterrupt();
|
||||
setTimerEvent_(ReadBitSamplingTime, &OneWireSlave::readBit_);
|
||||
|
||||
onLeaveInterrupt_();
|
||||
}
|
||||
|
||||
void OneWireSlave::readBit_()
|
||||
{
|
||||
onEnterInterrupt_();
|
||||
|
||||
bool bit = pin_.read();
|
||||
if (bit)
|
||||
cancelResetDetection_();
|
||||
else
|
||||
beginResetDetection_();
|
||||
receiveBitCallback_(bit, false);
|
||||
//dbgOutput.writeLow();
|
||||
//dbgOutput.writeHigh();
|
||||
|
||||
onLeaveInterrupt_();
|
||||
}
|
||||
|
||||
void OneWireSlave::beginSendBit_(bool bit, void(*completeCallback)(bool error))
|
||||
{
|
||||
bitSentCallback_ = completeCallback;
|
||||
if (bit)
|
||||
{
|
||||
pin_.attachInterrupt(&OneWireSlave::sendBitOne_, FALLING);
|
||||
}
|
||||
else
|
||||
{
|
||||
pin_.attachInterrupt(&OneWireSlave::sendBitZero_, FALLING);
|
||||
}
|
||||
}
|
||||
|
||||
void OneWireSlave::sendBitOne_()
|
||||
{
|
||||
onEnterInterrupt_();
|
||||
|
||||
beginResetDetection_();
|
||||
bitSentCallback_(false);
|
||||
|
||||
onLeaveInterrupt_();
|
||||
}
|
||||
|
||||
void OneWireSlave::sendBitZero_()
|
||||
{
|
||||
pullLow_(); // this must be executed first because the timing is very tight with some master devices
|
||||
|
||||
onEnterInterrupt_();
|
||||
|
||||
pin_.detachInterrupt();
|
||||
setTimerEvent_(SendBitDuration, &OneWireSlave::endSendBitZero_);
|
||||
|
||||
onLeaveInterrupt_();
|
||||
}
|
||||
|
||||
void OneWireSlave::endSendBitZero_()
|
||||
{
|
||||
onEnterInterrupt_();
|
||||
|
||||
releaseBus_();
|
||||
beginResetDetectionSendZero_();
|
||||
bitSentCallback_(false);
|
||||
|
||||
onLeaveInterrupt_();
|
||||
}
|
||||
|
||||
void OneWireSlave::beginWaitReset_()
|
||||
{
|
||||
disableTimer_();
|
||||
pin_.inputMode();
|
||||
pin_.attachInterrupt(&OneWireSlave::waitReset_, CHANGE);
|
||||
resetStart_ = (unsigned int)-1;
|
||||
}
|
||||
|
||||
void OneWireSlave::waitReset_()
|
||||
{
|
||||
onEnterInterrupt_();
|
||||
bool state = pin_.read();
|
||||
unsigned long now = micros();
|
||||
if (state)
|
||||
{
|
||||
if (resetStart_ == (unsigned int)-1)
|
||||
{
|
||||
onLeaveInterrupt_();
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long resetDuration = now - resetStart_;
|
||||
resetStart_ = (unsigned int)-1;
|
||||
if (resetDuration >= ResetMinDuration)
|
||||
{
|
||||
if (resetDuration > ResetMaxDuration)
|
||||
{
|
||||
ERROR("Reset too long");
|
||||
onLeaveInterrupt_();
|
||||
return;
|
||||
}
|
||||
|
||||
lastReset_ = now;
|
||||
pin_.detachInterrupt();
|
||||
unsigned long alreadyElapsedTime = micros() - now;
|
||||
setTimerEvent_(alreadyElapsedTime < PresenceWaitDuration ? PresenceWaitDuration - alreadyElapsedTime : 0, &OneWireSlave::beginPresence_);
|
||||
endWrite_(true, false);
|
||||
if (clientReceiveCallback_ != 0)
|
||||
clientReceiveCallback_(RE_Reset, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resetStart_ = now;
|
||||
}
|
||||
onLeaveInterrupt_();
|
||||
}
|
||||
|
||||
void OneWireSlave::beginPresence_()
|
||||
{
|
||||
pullLow_();
|
||||
setTimerEvent_(PresenceDuration, &OneWireSlave::endPresence_);
|
||||
}
|
||||
|
||||
void OneWireSlave::endPresence_()
|
||||
{
|
||||
releaseBus_();
|
||||
|
||||
beginWaitCommand_();
|
||||
}
|
||||
|
||||
void OneWireSlave::beginWaitCommand_()
|
||||
{
|
||||
bufferPos_ = ReceiveCommand;
|
||||
beginReceive_();
|
||||
}
|
||||
|
||||
void OneWireSlave::beginReceive_()
|
||||
{
|
||||
receivingByte_ = 0;
|
||||
bufferBitPos_ = 0;
|
||||
beginReceiveBit_(&OneWireSlave::onBitReceived_);
|
||||
}
|
||||
|
||||
void OneWireSlave::onBitReceived_(bool bit, bool error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
ERROR("Invalid bit");
|
||||
if (bufferPos_ >= 0)
|
||||
receiveBytesCallback_(true);
|
||||
return;
|
||||
}
|
||||
|
||||
receivingByte_ |= ((bit ? 1 : 0) << bufferBitPos_);
|
||||
++bufferBitPos_;
|
||||
|
||||
if (clientReceiveBitCallback_ != 0 && bufferPos_ != ReceiveCommand)
|
||||
clientReceiveBitCallback_(bit);
|
||||
|
||||
if (bufferBitPos_ == 8)
|
||||
{
|
||||
// log("received byte", (long)receivingByte_);
|
||||
|
||||
if (bufferPos_ == ReceiveCommand)
|
||||
{
|
||||
bufferPos_ = 0;
|
||||
switch (receivingByte_)
|
||||
{
|
||||
case 0xF0: // SEARCH ROM
|
||||
resumeCommandFlag_ = false;
|
||||
beginSearchRom_();
|
||||
return;
|
||||
case 0xEC: // CONDITIONAL SEARCH ROM
|
||||
resumeCommandFlag_ = false;
|
||||
if (alarmedFlag_)
|
||||
{
|
||||
beginSearchRom_();
|
||||
}
|
||||
else
|
||||
{
|
||||
beginWaitReset_();
|
||||
}
|
||||
return;
|
||||
case 0x33: // READ ROM
|
||||
resumeCommandFlag_ = false;
|
||||
beginWriteBytes_(rom_, 8, &OneWireSlave::noOpCallback_);
|
||||
return;
|
||||
case 0x55: // MATCH ROM
|
||||
resumeCommandFlag_ = false;
|
||||
beginReceiveBytes_(scratchpad_, 8, &OneWireSlave::matchRomBytesReceived_);
|
||||
return;
|
||||
case 0xCC: // SKIP ROM
|
||||
resumeCommandFlag_ = false;
|
||||
beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_);
|
||||
return;
|
||||
case 0xA5: // RESUME
|
||||
if (resumeCommandFlag_)
|
||||
{
|
||||
beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_);
|
||||
}
|
||||
else
|
||||
{
|
||||
beginWaitReset_();
|
||||
}
|
||||
return;
|
||||
default:
|
||||
ERROR("Unknown command");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
recvBuffer_[bufferPos_++] = receivingByte_;
|
||||
receivingByte_ = 0;
|
||||
bufferBitPos_ = 0;
|
||||
if (bufferPos_ == bufferLength_)
|
||||
{
|
||||
beginWaitReset_();
|
||||
receiveBytesCallback_(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beginReceiveBit_(&OneWireSlave::onBitReceived_);
|
||||
}
|
||||
|
||||
void OneWireSlave::beginSearchRom_()
|
||||
{
|
||||
searchRomBytePos_ = 0;
|
||||
searchRomBitPos_ = 0;
|
||||
searchRomInverse_ = false;
|
||||
|
||||
beginSearchRomSendBit_();
|
||||
}
|
||||
|
||||
void OneWireSlave::beginSearchRomSendBit_()
|
||||
{
|
||||
byte currentByte = rom_[searchRomBytePos_];
|
||||
bool currentBit = bitRead(currentByte, searchRomBitPos_);
|
||||
bool bitToSend = searchRomInverse_ ? !currentBit : currentBit;
|
||||
|
||||
beginSendBit_(bitToSend, &OneWireSlave::continueSearchRom_);
|
||||
}
|
||||
|
||||
void OneWireSlave::continueSearchRom_(bool error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
ERROR("Failed to send bit");
|
||||
return;
|
||||
}
|
||||
|
||||
searchRomInverse_ = !searchRomInverse_;
|
||||
if (searchRomInverse_)
|
||||
{
|
||||
beginSearchRomSendBit_();
|
||||
}
|
||||
else
|
||||
{
|
||||
beginReceiveBit_(&OneWireSlave::searchRomOnBitReceived_);
|
||||
}
|
||||
}
|
||||
|
||||
void OneWireSlave::searchRomOnBitReceived_(bool bit, bool error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
ERROR("Bit read error during ROM search");
|
||||
return;
|
||||
}
|
||||
|
||||
byte currentByte = rom_[searchRomBytePos_];
|
||||
bool currentBit = bitRead(currentByte, searchRomBitPos_);
|
||||
|
||||
if (bit == currentBit)
|
||||
{
|
||||
++searchRomBitPos_;
|
||||
if (searchRomBitPos_ == 8)
|
||||
{
|
||||
searchRomBitPos_ = 0;
|
||||
++searchRomBytePos_;
|
||||
}
|
||||
|
||||
if (searchRomBytePos_ == 8)
|
||||
{
|
||||
// log("ROM sent entirely");
|
||||
|
||||
beginWaitReset_();
|
||||
}
|
||||
else
|
||||
{
|
||||
beginSearchRomSendBit_();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// log("Leaving ROM search");
|
||||
beginWaitReset_();
|
||||
}
|
||||
}
|
||||
|
||||
void OneWireSlave::beginWriteBytes_(const byte* data, short numBytes, void(*complete)(bool error))
|
||||
{
|
||||
sendBuffer_ = data;
|
||||
bufferLength_ = numBytes;
|
||||
bufferPos_ = 0;
|
||||
bufferBitPos_ = 0;
|
||||
sendBytesCallback_ = complete;
|
||||
|
||||
if (sendBuffer_ != 0 && bufferLength_ > 0)
|
||||
{
|
||||
bool bit = bitRead(sendBuffer_[0], 0);
|
||||
beginSendBit_(bit, &OneWireSlave::bitSent_);
|
||||
}
|
||||
else
|
||||
{
|
||||
endWrite_(true);
|
||||
beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_);
|
||||
}
|
||||
}
|
||||
|
||||
void OneWireSlave::bitSent_(bool error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
ERROR("error sending a bit");
|
||||
sendBytesCallback_(true);
|
||||
return;
|
||||
}
|
||||
|
||||
++bufferBitPos_;
|
||||
if (bufferBitPos_ == 8)
|
||||
{
|
||||
bufferBitPos_ = 0;
|
||||
++bufferPos_;
|
||||
}
|
||||
|
||||
if (bufferPos_ == bufferLength_)
|
||||
{
|
||||
endWrite_(false);
|
||||
sendBytesCallback_(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool bit = bitRead(sendBuffer_[bufferPos_], bufferBitPos_);
|
||||
beginSendBit_(bit, &OneWireSlave::bitSent_);
|
||||
}
|
||||
|
||||
void OneWireSlave::beginReceiveBytes_(byte* buffer, short numBytes, void(*complete)(bool error))
|
||||
{
|
||||
recvBuffer_ = buffer;
|
||||
bufferLength_ = numBytes;
|
||||
bufferPos_ = 0;
|
||||
receiveBytesCallback_ = complete;
|
||||
beginReceive_();
|
||||
}
|
||||
|
||||
void OneWireSlave::noOpCallback_(bool error)
|
||||
{
|
||||
if (error)
|
||||
ERROR("error during an internal 1-wire operation");
|
||||
}
|
||||
|
||||
void OneWireSlave::matchRomBytesReceived_(bool error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
resumeCommandFlag_ = false;
|
||||
ERROR("error receiving match rom bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp(rom_, scratchpad_, 8) == 0)
|
||||
{
|
||||
// log("ROM matched");
|
||||
resumeCommandFlag_ = true;
|
||||
beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_);
|
||||
}
|
||||
else
|
||||
{
|
||||
// log("ROM not matched");
|
||||
resumeCommandFlag_ = false;
|
||||
beginWaitReset_();
|
||||
}
|
||||
}
|
||||
|
||||
void OneWireSlave::notifyClientByteReceived_(bool error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
if (clientReceiveCallback_ != 0)
|
||||
clientReceiveCallback_(RE_Error, 0);
|
||||
ERROR("error receiving custom bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
beginReceiveBytes_(scratchpad_, 1, &OneWireSlave::notifyClientByteReceived_);
|
||||
if (clientReceiveCallback_ != 0)
|
||||
clientReceiveCallback_(RE_Byte, scratchpad_[0]);
|
||||
}
|
148
src/OneWireSlave.h
Normal file
148
src/OneWireSlave.h
Normal file
@ -0,0 +1,148 @@
|
||||
#ifndef _OneWireSlave_h_
|
||||
#define _OneWireSlave_h_
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "utility/LowLevel.h"
|
||||
|
||||
class OneWireSlave
|
||||
{
|
||||
public:
|
||||
enum ReceiveEvent
|
||||
{
|
||||
RE_Reset, //!< The master has sent a general reset
|
||||
RE_Byte, //!< The master just sent a byte of data
|
||||
RE_Error //!< A communication error happened (such as a timeout) ; the library will stop all 1-wire activities until the next reset
|
||||
};
|
||||
|
||||
//! Starts listening for the 1-wire master, on the specified pin, as a virtual slave device identified by the specified ROM (7 bytes, starting from the family code, CRC will be computed internally). Reset, Presence, SearchRom and MatchRom are handled automatically. The library will use the external interrupt on the specified pin (note that this is usually not possible with all pins, depending on the board), as well as one hardware timer. Blocking interrupts (either by disabling them explicitely with sei/cli, or by spending time in another interrupt) can lead to malfunction of the library, due to tight timing for some 1-wire operations.
|
||||
void begin(const byte* rom, byte pinNumber);
|
||||
|
||||
//! Stops all 1-wire activities, which frees hardware resources for other purposes.
|
||||
void end();
|
||||
|
||||
//! Sets (or replaces) a function to be called when something is received. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next byte.
|
||||
void setReceiveCallback(void(*callback)(ReceiveEvent evt, byte data)) { clientReceiveCallback_ = callback; }
|
||||
|
||||
//! Sets (or replaces) a function to be called when a bit is received. The byte reception callback is called after that if the received bit was the last of a byte. The callback is executed from interrupts and should be as short as possible. Failure to return quickly can prevent the library from correctly reading the next bit.
|
||||
void setReceiveBitCallback(void(*callback)(bool bit)) { clientReceiveBitCallback_ = callback; }
|
||||
|
||||
//! Sets (or replaces) a function to be called when the library has a message to log, if the functionality is enabled in OneWireSlave.cpp. This is for debugging purposes.
|
||||
void setLogCallback(void(*callback)(const char* message)) { logCallback_ = callback; }
|
||||
|
||||
//! Writes the specified bytes synchronously. This function blocks until the write operation has finished. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs.
|
||||
bool write(const byte* bytes, short numBytes);
|
||||
|
||||
//! Starts sending the specified bytes. They will be sent in the background, and the buffer must remain valid and unchanged until the write operation has finished or is cancelled. The optional callback is used to notify when the bytes are sent, or if an error occured. Callbacks are executed from interrupts and should be as short as possible. If bytes is null or numBytes is 0, nothing is sent, which is equivalent to calling stopWrite. In any case, calling the write function will cancel the previous write operation if it didn't complete yet.
|
||||
void beginWrite(const byte* bytes, short numBytes, void(*complete)(bool error));
|
||||
|
||||
//! Writes a single bit synchronously. This function blocks until the bit is sent. Do not call from an interrupt handler! Returns true in case of success, false if an error occurs.
|
||||
bool writeBit(bool value);
|
||||
|
||||
//! Sets a bit that will be sent next time the master asks for one. Optionnaly, the repeat parameter can be set to true to continue sending the same bit each time. In both cases, the send operation can be canceled by calling stopWrite.
|
||||
void beginWriteBit(bool value, bool repeat = false, void(*bitSent)(bool error) = 0);
|
||||
|
||||
//! Cancels any pending write operation, started by writeBit or write. If this function is called before the master asked for a bit, then nothing is sent to the master.
|
||||
void stopWrite();
|
||||
|
||||
//! Sets the alarmed state, that is used when the master makes a conditional search of alarmed devices.
|
||||
void alarmed(bool value);
|
||||
|
||||
static byte crc8(const byte* data, short numBytes);
|
||||
|
||||
private:
|
||||
static void setTimerEvent_(short delayMicroSeconds, void(*handler)());
|
||||
static void disableTimer_();
|
||||
|
||||
static void onEnterInterrupt_();
|
||||
static void onLeaveInterrupt_();
|
||||
|
||||
static void error_(const char* message);
|
||||
|
||||
static void pullLow_();
|
||||
static void releaseBus_();
|
||||
|
||||
static void beginReceiveBit_(void(*completeCallback)(bool bit, bool error));
|
||||
static void beginSendBit_(bool bit, void(*completeCallback)(bool error));
|
||||
|
||||
static void beginResetDetection_();
|
||||
static void beginResetDetectionSendZero_();
|
||||
static void cancelResetDetection_();
|
||||
|
||||
static void beginWaitReset_();
|
||||
static void beginWaitCommand_();
|
||||
static void beginReceive_();
|
||||
static void onBitReceived_(bool bit, bool error);
|
||||
|
||||
static void beginSearchRom_();
|
||||
static void beginSearchRomSendBit_();
|
||||
static void continueSearchRom_(bool error);
|
||||
static void searchRomOnBitReceived_(bool bit, bool error);
|
||||
|
||||
static void beginWriteBytes_(const byte* data, short numBytes, void(*complete)(bool error));
|
||||
static void beginReceiveBytes_(byte* buffer, short numBytes, void(*complete)(bool error));
|
||||
|
||||
static void endWrite_(bool error, bool resetInterrupts = true);
|
||||
|
||||
static void onSynchronousWriteComplete_(bool error);
|
||||
static void noOpCallback_(bool error);
|
||||
static void matchRomBytesReceived_(bool error);
|
||||
static void notifyClientByteReceived_(bool error);
|
||||
static void bitSent_(bool error);
|
||||
|
||||
// interrupt handlers
|
||||
static void waitReset_();
|
||||
static void beginPresence_();
|
||||
static void endPresence_();
|
||||
static void receive_();
|
||||
static void readBit_();
|
||||
static void sendBitOne_();
|
||||
static void sendBitZero_();
|
||||
static void endSendBitZero_();
|
||||
static void resetCheck_();
|
||||
|
||||
private:
|
||||
static byte rom_[8];
|
||||
static byte scratchpad_[8];
|
||||
static Pin pin_;
|
||||
|
||||
static unsigned long resetStart_;
|
||||
static unsigned long lastReset_;
|
||||
|
||||
static void(*receiveBitCallback_)(bool bit, bool error);
|
||||
static void(*bitSentCallback_)(bool error);
|
||||
|
||||
static byte receivingByte_;
|
||||
|
||||
static byte searchRomBytePos_;
|
||||
static byte searchRomBitPos_;
|
||||
static bool searchRomInverse_;
|
||||
static bool resumeCommandFlag_;
|
||||
static bool alarmedFlag_;
|
||||
|
||||
static const byte* sendBuffer_;
|
||||
static byte* recvBuffer_;
|
||||
static short bufferLength_;
|
||||
static short bufferPos_;
|
||||
static byte bufferBitPos_;
|
||||
static void(*receiveBytesCallback_)(bool error);
|
||||
static void(*sendBytesCallback_)(bool error);
|
||||
|
||||
static volatile bool waitingSynchronousWriteToComplete_;
|
||||
static volatile bool synchronousWriteError_;
|
||||
|
||||
static bool sendingClientBytes_;
|
||||
|
||||
static bool singleBit_;
|
||||
static bool singleBitRepeat_;
|
||||
static void (*singleBitSentCallback_)(bool error);
|
||||
static void onSingleBitSent_(bool error);
|
||||
|
||||
static void(*clientReceiveCallback_)(ReceiveEvent evt, byte data);
|
||||
static void(*clientReceiveBitCallback_)(bool bit);
|
||||
|
||||
static void(*logCallback_)(const char* message);
|
||||
};
|
||||
|
||||
extern OneWireSlave OWSlave;
|
||||
|
||||
#endif
|
163
src/utility/LowLevel.h
Normal file
163
src/utility/LowLevel.h
Normal file
@ -0,0 +1,163 @@
|
||||
#ifndef _LowLevel_h
|
||||
#define _LowLevel_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef VS_INTELLISENSE
|
||||
#define __attribute__(...)
|
||||
#define digitalPinToPort(pin) 0
|
||||
#define digitalPinToBitMask(pin) 0
|
||||
#define portInputRegister(arg1) 0
|
||||
#endif
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h" // for delayMicroseconds, digitalPinToBitMask, etc
|
||||
#else
|
||||
#include "WProgram.h" // for delayMicroseconds
|
||||
#include "pins_arduino.h" // for digitalPinToBitMask, etc
|
||||
#endif
|
||||
|
||||
#if defined(__AVR__)
|
||||
#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin)))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define IO_REG_TYPE uint8_t
|
||||
#define IO_REG_ASM asm("r30")
|
||||
#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)
|
||||
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask))
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask))
|
||||
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask))
|
||||
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask))
|
||||
|
||||
#if defined (__AVR_ATtiny85__)
|
||||
#define CLEARINTERRUPT GIFR |= (1 << INTF0)
|
||||
#include "UserTimer.h" //ATtiny-support based on TinyCore1 Arduino-core for ATtiny at http://github.com/Coding-Badly/TinyCore1.git
|
||||
__attribute__((always_inline)) static inline void UserTimer_Init( void )
|
||||
{
|
||||
UserTimer_SetToPowerup();
|
||||
UserTimer_SetWaveformGenerationMode(UserTimer_(CTC_OCR));
|
||||
}
|
||||
__attribute__((always_inline)) static inline void UserTimer_Run(short skipTicks)
|
||||
{
|
||||
UserTimer_SetCount(0);
|
||||
UserTimer_SetOutputCompareMatchAndClear(skipTicks);
|
||||
UserTimer_ClockSelect(UserTimer_(Prescale_Value_64));
|
||||
}
|
||||
#define UserTimer_Stop() UserTimer_ClockSelect(UserTimer_(Stopped))
|
||||
|
||||
#elif defined (__AVR_ATmega328P__)
|
||||
#define CLEARINTERRUPT EIFR |= (1 << INTF0)
|
||||
#define USERTIMER_COMPA_vect TIMER1_COMPA_vect
|
||||
|
||||
__attribute__((always_inline)) static inline void UserTimer_Init( void )
|
||||
{
|
||||
TCCR1A = 0;
|
||||
TCCR1B = 0;
|
||||
// enable timer compare interrupt
|
||||
TIMSK1 |= (1 << OCIE1A);
|
||||
}
|
||||
__attribute__((always_inline)) static inline void UserTimer_Run(short skipTicks)
|
||||
{
|
||||
TCNT1 = 0;
|
||||
OCR1A = skipTicks;
|
||||
// turn on CTC mode with 64 prescaler
|
||||
TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
|
||||
}
|
||||
#define UserTimer_Stop() TCCR1B = 0
|
||||
#endif
|
||||
|
||||
#elif defined(__MK20DX128__) || defined(__MK20DX256__)
|
||||
#define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
|
||||
#define PIN_TO_BITMASK(pin) (1)
|
||||
#define IO_REG_TYPE uint8_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) (*((base)+512))
|
||||
#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0)
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1)
|
||||
#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1)
|
||||
#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1)
|
||||
|
||||
#elif defined(__SAM3X8E__)
|
||||
// Arduino 1.5.1 may have a bug in delayMicroseconds() on Arduino Due.
|
||||
// http://arduino.cc/forum/index.php/topic,141030.msg1076268.html#msg1076268
|
||||
// If you have trouble with OneWire on Arduino Due, please check the
|
||||
// status of delayMicroseconds() before reporting a bug in OneWire!
|
||||
#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0)
|
||||
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask))
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask))
|
||||
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask))
|
||||
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask))
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM
|
||||
#endif
|
||||
#ifndef pgm_read_byte
|
||||
#define pgm_read_byte(addr) (*(const uint8_t *)(addr))
|
||||
#endif
|
||||
|
||||
#elif defined(__PIC32MX__)
|
||||
#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin)))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define IO_REG_ASM
|
||||
#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10
|
||||
#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08
|
||||
#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04
|
||||
#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24
|
||||
#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28
|
||||
|
||||
#else
|
||||
#error "Please define I/O register types here"
|
||||
#endif
|
||||
|
||||
class Pin
|
||||
{
|
||||
private:
|
||||
volatile IO_REG_TYPE *reg_;
|
||||
IO_REG_TYPE mask_;
|
||||
byte interruptNumber_;
|
||||
byte pinNumber_;
|
||||
|
||||
public:
|
||||
Pin()
|
||||
: mask_(0)
|
||||
, reg_(0)
|
||||
, interruptNumber_((byte)-1)
|
||||
, pinNumber_(255)
|
||||
{ }
|
||||
|
||||
Pin(uint8_t pin)
|
||||
{
|
||||
pinNumber_ = pin;
|
||||
mask_ = PIN_TO_BITMASK(pin);
|
||||
reg_ = PIN_TO_BASEREG(pin);
|
||||
|
||||
switch (pin)
|
||||
{
|
||||
case 2: interruptNumber_ = 0; break;
|
||||
case 3: interruptNumber_ = 1; break;
|
||||
default: interruptNumber_ = (byte)-1;
|
||||
}
|
||||
}
|
||||
|
||||
inline byte getPinNumber() { return pinNumber_; }
|
||||
|
||||
inline void inputMode() { DIRECT_MODE_INPUT(reg_, mask_); }
|
||||
inline void outputMode() { DIRECT_MODE_OUTPUT(reg_, mask_); }
|
||||
|
||||
inline bool read() { return DIRECT_READ(reg_, mask_) == 1; }
|
||||
inline void writeLow() { DIRECT_WRITE_LOW(reg_, mask_); }
|
||||
inline void writeHigh() { DIRECT_WRITE_HIGH(reg_, mask_); }
|
||||
inline void write(bool value) { if (value) writeHigh(); else writeLow(); }
|
||||
|
||||
inline void attachInterrupt(void (*handler)(), int mode)
|
||||
{
|
||||
CLEARINTERRUPT; // clear any pending interrupt (we want to call the handler only for interrupts happening after it is attached)
|
||||
::attachInterrupt(interruptNumber_, handler, mode);
|
||||
}
|
||||
inline void detachInterrupt() { ::detachInterrupt(interruptNumber_); }
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user