/*
D9에서 나온 PWM 신호를 받아서
PWM 신호를 읽고 OLED에 x, y 축이 있는 좌표계와
해당 PWM 듀티를 사각파 형태로 반복 표현하는 코드입니다.
*/
#include <U8glib.h>
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // I2C 인터페이스, SSD1306 128x64
const int pwmPin = 9; // PWM 신호를 출력할 핀
const int analogPin = A0; // PWM 신호를 읽을 아날로그 핀
// 25%, 50%, 75%, 100%에 해당하는 PWM 값 (0 ~ 255)
int dutyCycles[4] = {64, 128, 191, 255};
int pwmValue = 0; // 현재 PWM 값
// PWM 신호 주기 (1초 간격)
int checkInterval = 1000;
// 1초 간격 체크: 마지막 업데이트 시간과 interval 차이가 난 경우 true 반환
bool isUpdateDue(unsigned long lastUpdate, unsigned long interval)
{
return (millis() - lastUpdate >= interval);
}
// 마지막 업데이트 시간을 현재 시간으로 갱신하는 함수
void updateLastTime(unsigned long &lastUpdate)
{
lastUpdate = millis();
}
// 현재 인덱스에 해당하는 duty cycle 값을 반환하는 함수
int getDutyCycle(int index)
{
return dutyCycles[index % 4];
}
// 인덱스 값을 순환시키는 함수
int advanceIndex(int index)
{
return (index + 1) % 4;
}
// PWM 값을 하드웨어에 출력하는 함수
void outputPWM(int value)
{
analogWrite(pwmPin, value);
}
// PWM 신호를 non blocking 방식으로 갱신하는 제어 함수
void pwmSignalControl()
{
static unsigned long lastUpdate = 0; // 마지막 업데이트 시각 저장
static int index = 0;
if (isUpdateDue(lastUpdate, checkInterval))
{ // 1초 간격 확인
pwmValue = getDutyCycle(index);
outputPWM(pwmValue);
index = advanceIndex(index);
updateLastTime(lastUpdate);
}
}
// OLED에 x, y 축이 있는 좌표계를 그리고,
// 지정 영역을 여러 주기로 나누어 사각파 형태로 현재 PWM 듀티를 표현하는 함수
void drawGraph()
{
// 그래프 영역 설정
int graphX = 5; // 그래프 좌측 여백
int graphY = 10; // 그래프 상단 여백
int graphW = 118; // 그래프 너비
int graphH = 40; // 그래프 높이
// 사각파 한 주기의 길이 (픽셀 단위)
int period = 20;
// 한 주기 내 'High' 구간의 길이: pwmValue (0~255)를 period (0~20)로 매핑
int highWidth = map(pwmValue, 0, 255, 0, period);
u8g.firstPage();
do
{
// 좌표축 그리기
// y축: (graphX, graphY)부터 (graphX, graphY+graphH)
u8g.drawLine(graphX, graphY, graphX, graphY + graphH);
// x축: (graphX, graphY+graphH)부터 (graphX+graphW, graphY+graphH)
u8g.drawLine(graphX, graphY + graphH, graphX + graphW, graphY + graphH);
// 사각파형태의 그래프를 반복적으로 그리기 (한 주기씩)
int numPeriods = graphW / period;
for (int i = 0; i < numPeriods; i++)
{
int startX = graphX + i * period;
// 그릴 사각파: 왼쪽 끝에서 상승, highWidth 만큼 high 상태, 이후 하강
// 상승: low (아래, graphY+graphH)에서 high (위, graphY)로
if (highWidth > 0)
{
u8g.drawLine(startX, graphY + graphH, startX, graphY); // 상승 에지
// horizontal high 구간
u8g.drawLine(startX, graphY, startX + highWidth, graphY);
// 하강 에지: 고점에서 낮은 값으로
u8g.drawLine(startX + highWidth, graphY, startX + highWidth, graphY + graphH);
}
// 나머지 구간은 low 상태: x축과 동일한 y 위치
u8g.drawLine(startX + highWidth, graphY + graphH, startX + period, graphY + graphH);
}
// PWM 값 텍스트 표시
u8g.setFont(u8g_font_6x10);
char buf[12];
sprintf(buf, "PWM: %d", pwmValue);
u8g.drawStr(graphX, graphY - 2, buf);
} while (u8g.nextPage());
}
void setup()
{
// 시리얼 통신은 필요없으므로 제거 가능
pinMode(pwmPin, OUTPUT); // PWM 핀을 출력으로 설정
pinMode(analogPin, INPUT); // 아날로그 핀을 입력으로 설정
}
void loop()
{
pwmSignalControl();
drawGraph();
}