반응형

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
알아서 변함

 

구현 결과

로터리를 돌리면 불빛의 밝기가 조절되는데, 디스플레이의 rotary_cnt으로 정도를 표시하고 -50~50 범위에서 움직일 때 밝기가 조절된다.


 

코드 구현

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

+ Recent posts