-
Notifications
You must be signed in to change notification settings - Fork 0
/
smart_port.hpp
147 lines (114 loc) · 2.61 KB
/
smart_port.hpp
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
#pragma once
#include "arduino_gpio.hpp"
// TODO: make a class?
// TODO: data pins default state?
// TODO: turn off pull-ups when switching data pins to input?
// TODO: ignore first frame end (it might be missing starting bits)
//#define SMART_PORT_USE_INTERRUPT
namespace smart_port_details
{
enum : uint8_t
{
input_enable_pin = 5, // active low
output_load_pin = 4, // active low
accio_pin = 3, // active low
accio2_pin = 2, // active low
};
#if defined(SMART_PORT_USE_INTERRUPT)
volatile bool frame_end = false;
}
// possibly doable without interrupt?
ISR (INT0_vect)
{
using namespace smart_port_details;
// seems to be fine up to 500us
//_delay_us(300);
digital_write(accio_pin, true);
frame_end = true;
#endif
}
class smart_port
{
private:
uint8_t data_pins[8] =
{
6, 7, 8, 9, 10, 11, 12, 13
};
//}
public:
void init()
{
using namespace smart_port_details;
// input enable (active low)
digital_write(input_enable_pin, true);
enable_output(input_enable_pin, true);
// output load (active low)
digital_write(output_load_pin, true);
enable_output(output_load_pin, true);
// accio output (active low)
digital_write(accio_pin, false);
enable_output(accio_pin, true);
// probably default
digital_write(accio2_pin, false);
enable_output(accio2_pin, false);
#if defined(SMART_PORT_USE_INTERRUPT)
// enable interrupts (should this be elsewhere?)
sei();
// enable INT0 falling edge
MCUCR = (1 << ISC01) | (1 << ISC00);
EIMSK = (1 << INT0);
#endif
}
uint8_t read()
{
using namespace smart_port_details;
#if defined(SMART_PORT_USE_INTERRUPT)
// wait for interrupt
while (!frame_end)
{}
frame_end = false;
#else
// busy wait on pin state
while (digital_read(accio2_pin))
{}
// seems to be fine up to 500us
//_delay_us(300);
digital_write(accio_pin, true);
#endif
// read byte
digital_write(input_enable_pin, false);
// seems to need at least 350us
// or just wait for accio2 to go high I think
// TODO: do which?
#if 1
_delay_us(500);
#else
while (!digital_read(accio2_pin))
{}
#endif
uint8_t byte = 0;
for (uint8_t i = 0; i != 8; ++i)
{
enable_output(data_pins[i], false);
byte |= digital_read(data_pins[i]) << i;
}
digital_write(input_enable_pin, true);
return byte;
}
void write(uint8_t const byte)
{
using namespace smart_port_details;
// prepare output byte
for (uint8_t i = 0; i != 8; ++i)
{
enable_output(data_pins[i], true);
digital_write(data_pins[i], byte & (1 << i));
}
digital_write(output_load_pin, false);
digital_write(output_load_pin, true);
// seems to be fine up to 7ms
//_delay_ms(4);
// accio low
digital_write(accio_pin, false);
}
};