반응형
ESP8266에서 Arduino 에서 로터리 엔코더로 PWM Duty를 조절하고, 해당 상태를 SSD1306에서 표시하도록 하자.
이 글에다가 핀을 변경하고 PWM 기능을 추가했다.
결선도
NewNode V3 핀 이름 |
RotaryEncoder 핀 이름 |
SSD1306 핀 이름 |
PWM 채널 |
VIN (5V) | 5V | ||
GND | GND | ||
D4 | KEY | ||
D6 | S1 | ||
D7 | S2 | ||
3V | VDD | ||
GND | GND | ||
D2 | SDA | ||
D3 | SCK | ||
D5 | Ch1 다이얼로 조절하는 채널 |
||
D8 | Ch2 알아서 변함 |
||
D3 | Ch3 알아서 변함 |
구현 결과
코드 구현
Arduino에서 PWM는 소프트웨어로 구현한다고 한다. 내부 타이머에서 counter를 확인해서 Faling을 결정하는 것이 아닌 인터럽트 타이머로 처리된다고 한다. [1]
그리고 PWM 신호가 되는 핀을 찾아야되서 문제가 있는데, 일단은 업로드 후에 부팅이 되고, PWM 기능이 동작하는 핀을 할당하면 위에 결선도 처럼 연결된다.
main.ino
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
245
246
247
248
249
250
251
252
253
254
255
|
/************************************/
// Include Headers
/************************************/
#include "oled.h"
#define GPIO_S1 8
#define GPIO_S2 9
#define GPIO_S3 10
#define GPIO_D0 16 // PWM
#define GPIO_D3 0 // PWM
#define GPIO_D4 2
#define GPIO_D5 14
#define GPIO_D6 12
#define GPIO_D7 13 // PWM
#define GPIO_D8 15 // PWM
void setRotaryRight();
void setRotaryLeft();
void setRotaryNeutral();
void updateRotary();
// Simple Scheduler
unsigned long last_loop_time = micros();
unsigned long now_loop_time = micros();
unsigned long size_loop_time = 100*1000;
unsigned long delta_loop_time = 0;
/************************************/
// Display Settings
/************************************/
OLED* monitor = new OLED();
/************************************/
// Rotary Button
/************************************/
// Rotary Encoder Inputs
#define BUTTON_KEY GPIO_D4
#define BUTTON_S1 GPIO_D6
#define BUTTON_S2 GPIO_D7
#define PWM1 GPIO_D5
#define PWM2 GPIO_D8
#define PWM3 GPIO_D3
volatile int rotary_pulse_s1 = 0;
volatile int rotary_pulse_s2 = 0;
volatile unsigned long rotary_last_pulse_s1 = 0;
volatile unsigned long rotary_last_pulse_s2 = 0;
volatile int rotary_state = 0;
volatile int rotary_state_prev = 0;
volatile int rotary_state_s1 = 0;
volatile int rotary_state_s2 = 0;
volatile int rotary_counter = 0;
volatile int rotary_right = false;
volatile int rotary_left = false;
volatile int rotary_event = true;
volatile int button_counter = 0;
volatile int button_clicked = false;
volatile int did_button_clicked = false;
volatile unsigned long button_last_press = 0;
void setup() {
int system_on = true;
/************************************/
// Display Initialization
/************************************/
int monitor_on = monitor->setup_display();
system_on = system_on & monitor_on;
sprintf(monitor->msg[2], "1. Display is ON");
monitor->print();
/************************************/
// PWM
/************************************/
pinMode(PWM1, OUTPUT);
pinMode(PWM2, OUTPUT);
pinMode(PWM3, OUTPUT);
for (int a = 0; a <= 10; a++){
analogWrite(PWM1, a*100);
analogWrite(PWM2, a*100);
analogWrite(PWM3, a*100);
delay(100);
}
for (int a = 10; a > 0; a--){
analogWrite(PWM1, a*100);
analogWrite(PWM2, a*100);
analogWrite(PWM3, a*100);
delay(100);
}
sprintf(monitor->msg[3], "2. PWM is ON");
monitor->print();
/************************************/
// Rotary Button Settings
/************************************/
// Set encoder pins as inputs
sprintf(monitor->msg[5], "4. Intialize Buttons");
monitor->print();
pinMode(BUTTON_S1, INPUT);
pinMode(BUTTON_S2, INPUT);
pinMode(BUTTON_KEY, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_S1), isr_rotary_s1, FALLING);
attachInterrupt(digitalPinToInterrupt(BUTTON_S2), isr_rotary_s2, FALLING);
attachInterrupt(digitalPinToInterrupt(BUTTON_KEY), isr_button_click, RISING);
sprintf(monitor->msg[5], "4. Buttons are ON");
monitor->print();
}
int a = 0;
void loop() {
/************************************/
// Display
/************************************/
sprintf(monitor->msg[2], "Direction L %d / R %d", rotary_left, rotary_right);
if (did_button_clicked == true){
sprintf(monitor->msg[4],"Button click!");
did_button_clicked = false;
}
else
sprintf(monitor->msg[4],"Button released!");
if(rotary_event == true){
rotary_event = false;
setRotaryNeutral();
}
sprintf(monitor->msg[0], "button_cnt %4d", button_counter);
sprintf(monitor->msg[1], "rotary_cnt: %4d", rotary_counter);
sprintf(monitor->msg[5], "S1 %d S2 %d = %d",digitalRead(BUTTON_S1),
digitalRead(BUTTON_S2),
2*digitalRead(BUTTON_S1) + digitalRead(BUTTON_S2));
monitor->print();
/************************************/
// PWM
/************************************/
a = (a + 1)%10;
int ch1 = (rotary_counter + 50) * 10 % 1000;
analogWrite(PWM1, ch1);
analogWrite(PWM2, a*100);
analogWrite(PWM3, a*100);
/************************************/
// Simple Scheduler
/************************************/
while(1){
now_loop_time = micros();
// Clock Overflow Case, each time after about 70 mins
if(now_loop_time < last_loop_time){
last_loop_time = 0;
break;
}
// Normal Case
else{
delta_loop_time = now_loop_time - last_loop_time;
if(delta_loop_time > size_loop_time){
last_loop_time = last_loop_time + size_loop_time;
break;
}
}
}
}
//3201 3201
// 0 -> 4 (2(0+1)+2) -> 10 (2*(4+1)+0) -> 23 (2*(10+1)+1)
void setRotaryRight(){
rotary_counter++;
rotary_left = true;
rotary_right = false;
rotary_event = true;
}
// 3102 3102
// 0 -> 3 (2*(0+1)+1) -> 8 (2*(3+1)+0) -> 20 (2*(8+1)+2)
void setRotaryLeft(){
rotary_counter--;
rotary_left = false;
rotary_right = true;
rotary_event = true;
}
void setRotaryNeutral(){
rotary_event = false;
rotary_left = false;
rotary_right = false;
}
void updateRotary(){
rotary_state_s1 = digitalRead(BUTTON_S1);
rotary_state_s2 = digitalRead(BUTTON_S2);
rotary_state = 2 * rotary_state_s1 + rotary_state_s2;
if(rotary_state_prev == 3 && rotary_state == 2){
setRotaryRight();
}
if(rotary_state_prev == 0 && rotary_state == 1){
setRotaryRight();
}
if(rotary_state_prev == 1 && rotary_state == 0){
setRotaryLeft();
}
if(rotary_state_prev == 2 && rotary_state == 3){
setRotaryLeft();
}
rotary_state_prev = rotary_state;
}
/************************************/
// Interrupt Service Routines
/************************************/
// CW (+, R) : S1 true, S2 Rising
// CCW (-, L) : S1 Rising, S2 true
IRAM_ATTR void isr_rotary_s1(){
// prevent being clicked over twice at once
if(rotary_last_pulse_s1 - millis() < 30)
rotary_pulse_s1 = false;
else{
rotary_pulse_s1 = true;
}
rotary_last_pulse_s1 = millis();
// processing rotary action
if(rotary_pulse_s1 == true){
rotary_pulse_s1 = false;
updateRotary();
}
}
IRAM_ATTR void isr_rotary_s2(){
// prevent being clicked over twice at once
if(rotary_last_pulse_s2 - millis() < 30)
rotary_pulse_s2 = false;
else{
rotary_pulse_s2 = true;
}
rotary_last_pulse_s2 = millis();
// processing rotary action
if(rotary_pulse_s2 == true){
rotary_pulse_s2 = false;
updateRotary();
}
}
// Button Interrupt Routine
IRAM_ATTR void isr_button_click(){
// prevent being clicked over twice at once
if(button_last_press - millis() < 500)
button_clicked = false;
else
button_clicked = true;
button_last_press = millis();
// processing button action
// throw main loop the fact being clicked
if(button_clicked == true){
button_clicked = false;
did_button_clicked = true;
button_counter++;
}
}
|
cs |
Reference
[1] COMPASS, "ESP8266 ADC와 PWM 사용하기", https://blog.naver.com/compass1111/221249842314
** EOF **
728x90
'SW > Arduino' 카테고리의 다른 글
[Arduino] RotaryEncoder & SSD1306을 이용해서 기능 구현하기 (0) | 2024.01.21 |
---|---|
[Arduino] ESP8266-12F - GPS & OLED & 로터리 버튼 & MPU6500 (0) | 2023.01.01 |
[Arduino] 실행 성능 계측 방법, millis()와 micros() 활용하기 (0) | 2023.01.01 |
[Arduino] ESP8266-12F - GPS & Display (1) | 2022.12.31 |
[Arduino] ESP8266-12F - Rotary Button & Display (0) | 2022.12.30 |