-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEspSoftSerialRx.cpp
205 lines (170 loc) · 5.17 KB
/
EspSoftSerialRx.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/* EspSoftSerial receiver
Scott Day 2015
github.com/scottwday/EspSoftSerial
Uses a pin change interrupt and the tick timer as a uart receiver
*/
#include <arduino.h>
#include "EspSoftSerialRx.h"
//Make sure static numInstances gets initialised to zero.
byte EspSoftSerialRx::_numInstances = 0;
//Static pointers to instances, allows interrupts to be forwarded
EspSoftSerialRx* EspSoftSerialRx::_instances[MAX_ESPSOFTSERIAL_INSTANCES];
void EspSoftSerialRx::reset()
{
_lastChangeTicks = 0;
_errorState = 0;
_bitCounter = 0;
_bitBuffer = 0;
_buffer.reset();
}
void EspSoftSerialRx::setEnabled( bool enabled)
{
if (enabled)
{
switch (_instanceId)
{
case 0: attachInterrupt(_rxPin, onRxPinChange0, CHANGE); break;
case 1: attachInterrupt(_rxPin, onRxPinChange1, CHANGE); break;
case 2: attachInterrupt(_rxPin, onRxPinChange2, CHANGE); break;
case 3: attachInterrupt(_rxPin, onRxPinChange3, CHANGE); break;
}
}
else
{
detachInterrupt(_rxPin);
}
}
// Supply the baud rate and the pin you're interested in
void EspSoftSerialRx::begin(const unsigned long baud, const byte rxPin)
{
_rxPin = rxPin;
_halfBitRate = (ESP8266_CLOCK/2) / baud;
// pinMode(rxPin, INPUT_PULLUP);
pinMode(rxPin, INPUT);
//You can't register an interrupt on a class member, so these shims pass on the call
if (_numInstances < MAX_ESPSOFTSERIAL_INSTANCES)
{
_instances[_numInstances] = this;
_instanceId = _numInstances;
switch(_numInstances)
{
case 0: attachInterrupt(rxPin, onRxPinChange0, CHANGE); break;
case 1: attachInterrupt(rxPin, onRxPinChange1, CHANGE); break;
case 2: attachInterrupt(rxPin, onRxPinChange2, CHANGE); break;
case 3: attachInterrupt(rxPin, onRxPinChange3, CHANGE); break;
}
++_numInstances;
}
}
//These shim the interrupt call into a class instance
// It's inefficient but it shields the cruft from the user
void EspSoftSerialRx::onRxPinChange0() { _instances[0]->onRxPinChange(); }
void EspSoftSerialRx::onRxPinChange1() { _instances[1]->onRxPinChange(); }
void EspSoftSerialRx::onRxPinChange2() { _instances[2]->onRxPinChange(); }
void EspSoftSerialRx::onRxPinChange3() { _instances[3]->onRxPinChange(); }
void EspSoftSerialRx::service()
{
if (_inInterrupt)
return;
//Complete bytes with high MSB and Protect against rollover
if (_lastChangeTicks)
{
unsigned long delta = (ESP.getCycleCount() - _lastChangeTicks) & 0x7FFFFFFF;
//Has it been less than 16 bits worth of time since the last edge?
if (delta > (_halfBitRate<<5))
{
//Is there an incomplete byte that we need to finish now that it's been a while since the last edge?
// Complete the current byte with 1's
if ((_bitCounter) && (_bitCounter <= 10))
{
addBits(11 - _bitCounter, 0);
}
_lastChangeTicks = 0;
}
}
}
bool EspSoftSerialRx::read(byte& c)
{
return _buffer.read(c);
}
//Returns the time difference saturated to 127 bits
byte EspSoftSerialRx::getNumBitPeriodsSinceLastChange(unsigned long ticks)
{
//Get how long since last change in cpu ticks
// If it's zero then it's been a long time
unsigned long delta = 0xFFFFFFFF;
if (_lastChangeTicks)
delta = (ticks - _lastChangeTicks) & 0x7FFFFFFF;
//Has it been less than 16 bits worth of time since the last edge?
// 16 bits is the max we care about and we don't want to overflow.
//If so, round up the number of half bits / 2 to get the number of bits
// otherwise leave it as 0x7F to indicate a long time
byte numBits = 0x7F;
if (delta < (_halfBitRate<<5)) // <==> delta / _halfBitRate < 32
numBits = ((byte)(delta / _halfBitRate) + 1) >> 1;
return numBits;
}
//Add bits to _bitBuffer and increment _bitCounter up till 10 bits
inline void EspSoftSerialRx::addBits(byte numBits, byte value)
{
//Add bits, LSB first, we'll eventually have 10 bits
byte i;
for (i=0; i<numBits && _bitCounter <= 10; i++)
{
_bitBuffer = _bitBuffer >> 1;
if (!value) _bitBuffer |= 0x200;
++_bitCounter;
}
//The previous byte in the buffer has finished once 10 bit times are up
if ((!_errorState) && (_bitCounter > 10))
{
unsigned short controlBits = _bitBuffer & 0xFE01;
if (controlBits == 0x200)
{
char b = (char)((_bitBuffer>>1)&0xFF);
_buffer.write(b);
//Serial.print(b);
}
else
{
_errorState = SOFTSERIAL_ERROR_STOPBIT;
}
_bitCounter = 0;
_bitBuffer = 0;
}
}
//Interrupt handler
void EspSoftSerialRx::onRxPinChange()
{
_inInterrupt = true;
byte value = digitalRead(_rxPin);
unsigned long ticks = ESP.getCycleCount();
byte numBits = getNumBitPeriodsSinceLastChange(ticks);
//Add bits to the buffer
addBits(numBits, value);
//The longest possible run of equal bits is 9
if (numBits > 9)
{
if (value == 0)
{
//A falling edge after a long time means we're coming out of an idle condition
_errorState = 0;
_bitCounter = 0;
}
else
{
//The line shouldn't ever be held down for longer than 9 bits
_errorState = SOFTSERIAL_ERROR_LONGLOW;
}
}
//Check for beginning of start bit
if ((_bitCounter == 0) && (value == 0))
{
_bitCounter = 1;
_bitBuffer = 0;
}
//Update timer, be careful of zero: it's reserved to mean 'a long time ago'
_lastChangeTicks = ticks;
if (_lastChangeTicks == 0) _lastChangeTicks = 1;
_inInterrupt = false;
}