forked from iqaudio/tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIQ_rot.c
208 lines (158 loc) · 5.99 KB
/
IQ_rot.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
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
// Rotary Encoder Sample app - IQ_rot.c
// Makes use of WIRINGPI Library
// USES Raspberry Pi GPIO 23 and 24.
// Adjusts ALSA volume (based on Left channel value) up or down to correspond with rotary encoder direction
// Assumes IQAUDIO.COM Pi-DAC volume range -103dB to 0dB
//
// G.Garrity Aug 30th 2015 IQaudIO.com
//
// Compile with gcc IQ_rot.c -oIQ_rot -lwiringPi -lasound
//
// Make sure you have the most upto date WiringPi installed on the Pi to be used.
#include <stdio.h>
#include <wiringPi.h>
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#include <stdbool.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
/*
Rotary encoder connections:
Encoder A - gpio 23 (IQAUDIO.COM PI-DAC 23)
Encoder B - gpio 24 (IQAUDIO.COM PI-DAC 24)
Encoder Common - Pi ground (IQAUDIO.COM PI-DAC GRD)
*/
// Define DEBUG_PRINT TRUE for output
#define DEBUG_PRINT 0 // 1 debug messages, 0 none
// Define the Raspberry Pi IO Pins being used
#define ENCODER_A 4 // GPIO 23
#define ENCODER_B 5 // GPIO 24
#define TRUE 1
#define FALSE 0
static volatile int encoderPos;
static volatile int lastEncoded;
static volatile int encoded;
static volatile int inCriticalSection = FALSE;
/* forward declaration */
void encoderPulse();
int main(int argc, char * argv[])
{
int pos = 125;
long min, max;
long gpiodelay_value = 250; // was 50
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
const char *card = "default";
// Previous linux driver's mixer name
// const char *selem_name = "Playback Digital";
// const char *selem_name = "PCM";
const char *selem_name = "Digital"; // Linux 4.1.6-v7+ #810
int x, mute_state;
long i, currentVolume;
printf("IQaudIO.com Pi-DAC Volume Control support Rotary Encoder) v1.5 Aug 30th 2015\n\n");
wiringPiSetup ();
/* pull up is needed as encoder common is grounded */
pinMode (ENCODER_A, INPUT);
pullUpDnControl (ENCODER_A, PUD_UP);
pinMode (ENCODER_B, INPUT);
pullUpDnControl (ENCODER_B, PUD_UP);
encoderPos = pos;
// Setup ALSA access
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
if (DEBUG_PRINT) printf("Returned card VOLUME range - min: %ld, max: %ld\n", min, max);
// Minimum given is mute, we need the first real value
min++;
// Get current volume
if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, ¤tVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf("Current ALSA volume LEFT: %ld\n", currentVolume);
if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_RIGHT, ¤tVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf("Current ALSA volume RIGHT: %ld\n", currentVolume);
/* monitor encoder level changes */
wiringPiISR (ENCODER_A, INT_EDGE_BOTH, &encoderPulse);
wiringPiISR (ENCODER_B, INT_EDGE_BOTH, &encoderPulse);
// Now sit and spin waiting for GPIO pins to go active...
while (1)
{
if (encoderPos != pos)
{
// get current volume
if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, ¤tVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf(" Current ALSA volume: %ld\n", currentVolume);
// Adjust for MUTE
if (currentVolume < min)
{
currentVolume = 0;
if (DEBUG_PRINT) printf(" Current ALSA volume set to min: %ld\n", currentVolume);
}
// What way did the encoder go?
if (encoderPos > pos)
{
pos = encoderPos;
currentVolume = currentVolume + 10;
// Adjust for MAX volume
if (currentVolume > max) currentVolume = max;
if (DEBUG_PRINT) printf("Volume UP %d - %ld", pos, currentVolume);
}
else if (encoderPos < pos)
{
pos = encoderPos;
currentVolume = currentVolume - 10;
// Adjust for MUTE
if (currentVolume < min) currentVolume = 0;
if (DEBUG_PRINT) printf("Volume DOWN %d - %ld", pos, currentVolume);
}
if (x = snd_mixer_selem_set_playback_volume_all(elem, currentVolume))
{
printf(" ERROR %d %s\n", x, snd_strerror(x));
} else if (DEBUG_PRINT) printf(" Volume successfully set to %ld using ALSA!\n", currentVolume);
}
// Check x times per second, MAY NEED TO ADJUST THS FREQUENCY FOR SOME ENCODERS */
delay(gpiodelay_value); /* check pos x times per second */
}
// We never get here but should close the sockets etc. on exit.
snd_mixer_close(handle);
}
// Called whenever there is GPIO activity on the defined pins.
void encoderPulse()
{
/*
+---------+ +---------+ 0
| | | |
A | | | |
| | | |
+---------+ +---------+ +----- 1
+---------+ +---------+ 0
| | | |
B | | | |
| | | |
----+ +---------+ +---------+ 1
*/
if (inCriticalSection==TRUE) return;
inCriticalSection = TRUE;
int MSB = digitalRead(ENCODER_A);
int LSB = digitalRead(ENCODER_B);
int encoded = (MSB << 1) | LSB;
int sum = (lastEncoded << 2) | encoded;
if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderPos++;
else if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderPos--;
lastEncoded = encoded;
inCriticalSection = FALSE;
}