-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshift_reg_74595.h
244 lines (227 loc) · 8.1 KB
/
shift_reg_74595.h
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// shift_reg_74595.h
// Data output on TTL chip 74HC595
// port_data_mosi: connection to 74HC595 Pin 14 [DS]
// port_clock_sck: connection to 74HC595 Pin 11 [SHCP]
// port_latch: connection to 74HC595 Pin 12 [STCP]
// 74HC595
// +--v--+
// Q1|1 16| Vcc
// Q2|2 15| Q0
// Q3|3 14| DS <-- Data
// Q4|4 13| OEi
// Q5|5 12| ST_CP <-- Latch
// Q6|6 11| SH_CP <-- Clock
// Q7|7 10| MRi
// Gnd|8 9| Q7'
// +-----+
#pragma once
#include "scheduler.h"
#include "mup_util.h"
namespace ustd {
class ShiftReg {
/*! Instantiate a 74HC595 shift register mupplet.
*
* This mupplet uses three IOs to fill a 74HC595 shift register of 8 output
* bits with data. It can either use the hardware SPI or 3 arbitrary IOs.
* Through cascading shift registers, a large number of outputs can be
* driven.
*/
public:
String SHIFTREG74595_VERSION = "0.1.0";
Scheduler *pSched;
int tID;
String name;
uint8_t port_data_mosi;
uint8_t port_clock_sck;
uint8_t port_latch;
uint8_t cur_data;
bool useSPI;
unsigned long bitPulseTimer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned long bitPulseDelta[8] = {0, 0, 0, 0, 0, 0, 0, 0};
ShiftReg(String name, uint8_t port_data_mosi, uint8_t port_clock_sck, uint8_t port_latch,
bool useSPI = true)
: name(name), port_data_mosi(port_data_mosi), port_clock_sck(port_clock_sck),
port_latch(port_latch), useSPI(useSPI) {
/*! Create 74HC595 mupplet
*
* This either use hardware SPI or 3 GPIOs. Functionality is equivalent.
*
* @param name Name of this mupplet.
* @param port_data_mosi MOSI hardware pin for SPI mode or any GPIO for
* non-SPI mode.
* @param port_clock_sck SCK hardware pin for SPI mode or any GPIO for
* non-SPI mode.
* @param port_latch A GPIO used to latch the outputs during
* programming.
* @param useSPI true: hardware SPI mode, false: GPIO bitbang mode.
*/
}
~ShiftReg() {
}
void begin(Scheduler *_pSched, unsigned long scheduleIntervalUsec = 50000) {
/*! Start mupplet
*
* @param pSched Pointer to scheduler object
* @param scheduleIntervalUsec Period for pulse-timer scheduling,
* default 50ms
*/
pSched = _pSched;
digitalWrite(port_latch, HIGH);
pinMode(port_latch, OUTPUT);
if (useSPI) {
// SPI.pins(mosi, miso, clk, hwcs);
SPI.begin();
} else {
digitalWrite(port_data_mosi,
HIGH); // first writing, then setting output-mode
digitalWrite(port_clock_sck, HIGH); // generates safe start state
pinMode(port_data_mosi, OUTPUT);
pinMode(port_clock_sck, OUTPUT);
}
writeShiftReg(0);
auto ft = [=]() { this->loop(); };
tID = pSched->add(ft, name, scheduleIntervalUsec);
auto fnall = [=](String topic, String msg, String originator) {
this->subsMsg(topic, msg, originator);
};
pSched->subscribe(tID, name + "/shiftreg/#", fnall);
}
private:
void writeShiftReg(uint8_t data, uint8_t mask = 0xff) {
uint8_t abs_data = data;
if (mask != 0xff) {
abs_data = (cur_data & (!mask)) | (data & mask);
}
cur_data = abs_data;
digitalWrite(port_latch,
LOW); // Pulse latch to put data on output ports
delayMicroseconds(2);
if (useSPI)
SPI.transfer(abs_data);
else
shiftOut(port_data_mosi, port_clock_sck, MSBFIRST, abs_data);
delayMicroseconds(2);
digitalWrite(port_latch, HIGH);
}
public:
void set(uint8_t data, uint8_t mask = 0xff) {
/*! Output to 74HC595
*
* Write complete byte or masked part to 74HC595 outputs and send MQTT
* update message.
*
* @param data data byte for output.
* @param mask masks that shows which bits should be changed to content
* of data. If a bit in mask is zero, then the old content of the shift
* register is maintained.
*/
writeShiftReg(data, mask);
char buf[32];
sprintf(buf, "%d", cur_data);
pSched->publish(name + "/shiftreg", buf);
}
void setBit(uint8_t bit, bool val) {
/*! Change a single bit of 74HC595
*
* Set or reset a bit in 74HC595 shift register.
*
* @param bit bit to change.
* @param val true: set bit to 1, false: set bit to zero.
*/
uint8_t cw = 1 << bit;
if (val) {
set(cw, cw);
} else {
set(0, cw);
}
}
void pulseBit(uint8_t bit, unsigned long ms = 1000) {
/*! Pulse a single bit of 74HC595 shift register for duration of ms
* milliseconds.
*
* Set a bit in 74HC595 shift register for a time perios of ms
* milliseconds (one-time).
*
* @param bit bit to pulse.
* @param ms high signal duration in milliseconds. Note: default timer
* for this mupplet is 50ms, which therefore is the minimum resolution
* for a pulse. Change parameter scheduleIntervalUsec of the begin()
* method to increase the resolution. (Shorter schedule intervals allow
* for shorter pulses.)
*/
if (bit >= 0 and bit < 8) {
setBit(bit, true);
bitPulseTimer[bit] = millis();
bitPulseDelta[bit] = ms;
}
}
private:
void loop() {
for (uint8_t bit = 0; bit < 8; bit++) {
if (bitPulseTimer[bit]) {
if (timeDiff(bitPulseTimer[bit], millis()) > bitPulseDelta[bit]) {
bitPulseTimer[bit] = 0;
bitPulseDelta[bit] = 0;
setBit(bit, false);
}
}
}
}
void subsMsg(String topic, String msg, String originator) {
char msgbuf[128];
memset(msgbuf, 0, 128);
strncpy(msgbuf, msg.c_str(), 127);
// TODO: allow other data-formats than decimal
if (topic == name + "/shiftreg/set/all") {
uint8_t mask = 0xff;
char *p = strchr(msgbuf, ',');
if (p != nullptr) {
*p = 0;
++p;
mask = atoi(p);
}
uint8_t data = atoi(msgbuf);
set(data, mask);
} else {
String wct = name + "/shiftreg/set/*"; // 8Bits: /shiftreg/set/[0..7]
int bit;
if (pSched->mqttmatch(topic, wct)) {
if (topic.length() >= wct.length()) {
const char *p = topic.c_str();
bit = atoi(&p[name.length() + strlen("/i2cpwm/")]);
if (bit >= 0 and bit <= 7) {
double level = parseUnitLevel(msg);
if (level == 0.0)
setBit(bit, false);
else
setBit(bit, true);
}
}
}
}
String wct = name + "/shiftreg/pulse/*"; // 8Bits: /shiftreg/set/[0..7]
int bit;
if (pSched->mqttmatch(topic, wct)) {
if (topic.length() >= wct.length()) {
const char *p = topic.c_str();
bit = atoi(&p[name.length() + strlen("/i2cpwm/")]);
if (bit >= 0 and bit <= 7) {
unsigned long ms = 1000;
char *p = strchr(msgbuf, ',');
if (p != nullptr) {
*p = 0;
++p;
ms = atol(p);
}
double level = parseUnitLevel(msgbuf);
if (level == 0.0)
setBit(bit, false);
else {
pulseBit(bit, ms);
}
}
}
}
};
}; // ShiftReg
} // namespace ustd