-
Notifications
You must be signed in to change notification settings - Fork 13
/
lib_rotary.c
130 lines (110 loc) · 3.43 KB
/
lib_rotary.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
/*
* File name: lib_rotary.c
* Date first: 12/29/2017
* Date last: 06/24/2018
*
* Description: STM8 Library for ALPS rotary encoder
*
* Author: Richard Hodges
*
* Copyright (C) 2017, 2018 Richard Hodges. All rights reserved.
* Permission is hereby granted for any use.
*
******************************************************************************
*
* Pinout:
*
* PA1: encoder A
* PA2: encoder B
*/
#include "stm8s_header.h"
#include "lib_rotary.h"
#define PIN_MASK (2 | 4)
static char val_cur;
static char val_max;
static char direction;
static char pins_last;
static char roll_up, roll_down;
/******************************************************************************
*
* Setup
* in: number of steps
*/
void alps_init(char steps, char option)
{
PA_DDR &= ~(PIN_MASK); /* PA1, PA2 inputs */
PA_CR2 &= ~(PIN_MASK); /* no interrupts */
PA_CR1 |= (PIN_MASK); /* weak pullups */
val_cur = 0;
val_max = steps;
direction = 0;
pins_last = PA_IDR & PIN_MASK;
/* assume ALPS_LIMITS option */
roll_up = steps - 1; /* new value when val_cur hits maximum */
roll_down = 1; /* value (adjusted ) when val_cur goes < 0 */
if (option == ALPS_ROLLOVER) {
roll_up = 0;
roll_down = steps;
}
}
/******************************************************************************
*
* Get current position (relative from setup)
*/
char alps_value(void)
{
return val_cur;
}
/******************************************************************************
*
* Poll switch (recommended: 1 to 4 milliseconds
*/
void alps_poll(void)
{
char pins_cur;
char diff;
pins_cur = PA_IDR & PIN_MASK;
if ((pins_cur == 0) || /* are both pins zero? */
(pins_cur == PIN_MASK)) { /* both set? */
if (pins_cur == pins_last)
return;
pins_last = pins_cur;
if (direction) {
val_cur++;
if (val_cur == val_max)
val_cur = roll_up;
return;
}
if (val_cur == 0)
val_cur = roll_down;
val_cur--;
return;
}
diff = pins_cur ^ pins_last;
direction = 0; /* counter-clockwise */
if (diff == 4) /* pin B changed */
direction = 1; /* clockwise */
}
/******************************************************************************
The ALPS rotary encoder switch has 30 detent positions, each one spaced 12
degrees apart. The three (encoder) pins are A (left), Common, B (right).
Pins A and B should be pulled up to Vcc; 10K resistors are recommended to
keep current under 1mA. Here, the input pins have internal pull-ups of
about 40K.
Both A and B are stable at detent position, and both A and B will have
the same output. When the encoder is rotated clockwise, B will change
first, then A. For counterclockwise, A will change first, then B.
Instead of using Interrupt On Change, poll at a rate often enough to
catch the fastest transitions. One full revolution in half a second
might be a good starting point, which gives 60 pin changes per second,
or one every 16 milliseconds. Oversampling by 4 would give one poll
every 4 milliseconds, which is probably already a timer interrupt.
0. Read pins A and B, save starting status.
Polling procedure:
1. Are pins A and B the same?
NO: Which changed from saved status? Set direction flag. Exit.
YES: Continue to 2.
2. Are A and B equal to last saved status?
YES: Encoder at same detent position. Ignore. Exit.
NO: Save new status. Eval direction flag and indicate rotation.
*/