-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathencoder.c
157 lines (136 loc) · 3.78 KB
/
encoder.c
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
#include "encoder.h"
#include <avr/interrupt.h>
#include <util/atomic.h>
#include "display.h" //Provides millis()
#include "hwprofile.h"
static const uint16_t kEncoderOKDuration = 50;
static const uint16_t kEncoderCancelDuration = 1000;
//Global Encoder Variables
static volatile int gEncoderValue = 0;
static volatile uint8_t gEncoderMin = 0;
static volatile uint8_t gEncoderMax = 0;
static volatile uint8_t gEncoderChanged = 0;
static volatile uint32_t gEncoderEnterStartTime = 0;
static volatile enum EncoderEnterState gEncoderEnterState = kEncoderEnterStateIdle;
static volatile uint8_t gEncoderLastBits = 0;
void encoder_init(void)
{
//Set pin directions
ENCODER_DIR_REG &= ~kEncoderPinMask;
//Enable Encoder Pin Change Interrupt
PCICR |= kEncoderPCINTPort;
//Set Pin Change Interrupt Mask for EncA and Enter
ENCODER_PCINT_MASK_REG |= kEncoderPCINTMask;
//Save pin states for change logic
gEncoderLastBits = ENCODER_INPUT_REG;
// Enable global interrupts
sei();
}
void encoder_set_limits(uint8_t minimum, uint8_t maximum)
{
gEncoderMin = minimum;
gEncoderMax = maximum;
//Reset value to ensure within limits
encoder_set_value(encoder_value());
}
uint8_t encoder_minimum()
{
return gEncoderMin;
}
uint8_t encoder_maximum()
{
return gEncoderMax;
}
int encoder_value(void)
{
gEncoderChanged = 0;
return gEncoderValue;
}
uint8_t encoder_changed(void)
{
return gEncoderChanged;
}
void encoder_set_value(int value)
{
value = value > gEncoderMax ? gEncoderMax : value;
value = value < gEncoderMin ? gEncoderMin : value;
gEncoderValue = value;
}
uint8_t encoder_ok(void)
{
if(gEncoderEnterState==kEncoderEnterStateOK) {
gEncoderEnterState = kEncoderEnterStateIdle;
return 1;
}
return 0;
}
uint8_t encoder_cancel(void)
{
uint32_t timestamp = millis();
uint32_t enterStart;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
enterStart = gEncoderEnterStartTime;
}
cli();
if((gEncoderEnterState==kEncoderEnterStateCancel) || ((gEncoderEnterState==kEncoderEnterStateClicked) && (enterStart + kEncoderCancelDuration < timestamp))) {
gEncoderEnterState = kEncoderEnterStateIdle;
sei();
return 1;
}
sei();
return 0;
}
uint8_t encoder_raw_enter(void)
{
return !(ENCODER_INPUT_REG & kEncoderPinE);
}
ISR(ENCODER_PCINT_VECTOR)
{
cli();
uint8_t encoderBits = ENCODER_INPUT_REG;
uint8_t encoderChangedBits = gEncoderLastBits ^ encoderBits;
//Process Encoder A Pin Rising
if ((encoderChangedBits & kEncoderPinA) && (encoderBits & kEncoderPinA)) {
if(encoderBits & kEncoderPinB) {
//EncB is high, Counter-clockwise
if (gEncoderValue > gEncoderMin)
--gEncoderValue;
} else {
//EncB is low, Clockwise
if (gEncoderValue < gEncoderMax)
++gEncoderValue;
}
gEncoderChanged = 1; //Flag value as changed
}
//Process Enter Pin Change
if (encoderChangedBits & kEncoderPinE) {
switch (gEncoderEnterState) {
case kEncoderEnterStateIdle:
//Button is pushed (ActiveLow)
if (!(encoderBits & kEncoderPinE)) {
gEncoderEnterState = kEncoderEnterStateClicked;
gEncoderEnterStartTime = millis();
}
break;
case kEncoderEnterStateClicked:
{
//Assumes interrupt must be enter release
uint16_t clickDuration = millis() - gEncoderEnterStartTime;
if(clickDuration < kEncoderOKDuration) {
gEncoderEnterState = kEncoderEnterStateIdle;
break;
}
if (clickDuration < kEncoderCancelDuration) {
gEncoderEnterState = kEncoderEnterStateOK;
break;
}
gEncoderEnterState = kEncoderEnterStateCancel;
}
default:
//Events in OK/Cancel state ignored
break;
}
}
gEncoderLastBits = encoderBits;
sei();
}