<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>junhk9_9.log</title>
        <link>https://velog.io/</link>
        <description>임베디드 개발자</description>
        <lastBuildDate>Mon, 06 Apr 2026 00:57:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>junhk9_9.log</title>
            <url>https://velog.velcdn.com/images/junhk9_9/profile/d473d51f-cb50-4fd5-83d5-09426a581c5d/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. junhk9_9.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/junhk9_9" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[I2C]]></title>
            <link>https://velog.io/@junhk9_9/I2C</link>
            <guid>https://velog.io/@junhk9_9/I2C</guid>
            <pubDate>Mon, 06 Apr 2026 00:57:06 GMT</pubDate>
            <description><![CDATA[<h2 id="1-i2c-통신의-특징">1. I2C 통신의 특징</h2>
<p>필립스(NXP)에서 개발한 <strong>동기식(Synchronous)</strong>, <strong>반이중(Half-Duplex)</strong> 방식의 직렬 통신입니다.</p>
<ul>
<li><strong>2개의 라인</strong>: <ul>
<li><strong>SDA (Serial Data)</strong>: 데이터가 오가는 양방향 라인.</li>
<li><strong>SCL (Serial Clock)</strong>: 통신 타이밍을 맞추는 클럭 라인.</li>
</ul>
</li>
<li><strong>주소 지정 방식</strong>: CS(Chip Select) 핀 대신 각 슬레이브의 <strong>고유 7비트 주소</strong>를 사용하여 통신 대상을 정합니다.</li>
<li><strong>멀티 마스터/멀티 슬레이브</strong>: 한 버스에 여러 마스터와 슬레이브가 존재할 수 있습니다.</li>
<li><strong>Open-Drain</strong>: 두 라인 모두 Pull-Up 저항이 필수적, 장치가 통신하지 않을 때는 High, 통신 시에만 Low로 당김.</li>
</ul>
<hr>
<h2 id="2-하드웨어의-핵심-open-drain--pull-up">2. 하드웨어의 핵심: Open-Drain &amp; Pull-up</h2>
<p>I2C를 논할 때 가장 중요한 전기적 특성입니다.</p>
<p>[Image of I2C bus circuit diagram showing open-drain configuration and pull-up resistors]</p>
<ul>
<li><strong>Open-Drain</strong>: I2C 핀은 내부적으로 회로를 &#39;Low(0V)&#39;로 만들 순 있지만, &#39;High&#39;로 밀어 올리지는 못합니다.</li>
<li><strong>Pull-up 저항</strong>: 라인이 아무 상태도 아닐 때 &#39;High&#39; 상태를 유지하기 위해 외부에 저항(보통 4.7kΩ)을 달아줍니다. <ul>
<li><em>Tip: 통신 에러가 잦다면 풀업 저항값이 적절한지 확인해야 합니다.</em></li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-통신-시퀀스-data-protocol">3. 통신 시퀀스 (Data Protocol)</h2>
<p>I2C 통신은 정해진 규칙(프로토콜)에 따라 진행됩니다.</p>
<ol>
<li><strong>Start Condition</strong>: SCL이 High일 때 SDA가 High → Low로 변하며 시작을 알립니다.</li>
<li><strong>Address Frame (7-bit)</strong>: 통신할 슬레이브 주소를 보냅니다.</li>
<li><strong>Read/Write Bit</strong>: 8번째 비트로 읽기(1)인지 쓰기(0)인지 결정합니다.</li>
<li><strong>ACK/NACK</strong>: 9번째 클럭에서 수신 측이 SDA를 Low로 내려 &#39;잘 받았다(ACK)&#39;는 신호를 줍니다.</li>
<li><strong>Data Frame</strong>: 실제 데이터를 8비트씩 주고받으며, 매 바이트마다 ACK를 확인합니다.</li>
<li><strong>Stop Condition</strong>: SCL이 High일 때 SDA가 Low → High로 변하며 종료합니다.</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[SPI]]></title>
            <link>https://velog.io/@junhk9_9/SPI</link>
            <guid>https://velog.io/@junhk9_9/SPI</guid>
            <pubDate>Mon, 06 Apr 2026 00:42:16 GMT</pubDate>
            <description><![CDATA[<h2 id="1-spi-통신이란">1. SPI 통신이란?</h2>
<p>SPI는 모토로라에서 제안한 <strong>동기식(Synchronous)</strong>, <strong>전이중(Full-Duplex)(데이터 송신과 수신이 동시에 이루어짐)</strong> 방식의 직렬 통신입니다. 주로 MCU와 센서, 메모리(Flash), 디스플레이 간의 고속 데이터 전송에 사용됩니다. I2C에 비해 통신 속도가 매우 빠르다. 1:N 통신이 가능하다=하나의 마스터에 여러 개의 슬레이브를 연결할 수 있다.</p>
<h3 id="핀-구성-4-wire">핀 구성 (4-Wire)</h3>
<ul>
<li><strong>SCLK (Serial Clock):</strong> 통신 속도를 결정하는 클럭 (Master가 생성).</li>
<li><strong>MOSI (Master Out Slave In):</strong> 마스터에서 슬레이브로 데이터 전송.</li>
<li><strong>MISO (Master In Slave Out):</strong> 슬레이브에서 마스터로 데이터 전송.</li>
<li><strong>CS/SS (Slave Select):</strong> 통신할 슬레이브를 선택 (보통 Active Low).</li>
</ul>
<hr>
<h2 id="2-핵심-파라미터-cpol과-cpha">2. 핵심 파라미터: CPOL과 CPHA</h2>
<p>SPI를 처음 연결할 때 가장 많이 하는 실수가 바로 <strong>SPI Mode</strong> 설정입니다. 데이터시트의 Timing Diagram을 보고 아래 4가지 모드 중 하나를 선택해야 합니다.</p>
<table>
<thead>
<tr>
<th align="left">Mode</th>
<th align="center">CPOL (Polarity)</th>
<th align="center">CPHA (Phase)</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>0</strong></td>
<td align="center">0</td>
<td align="center">0</td>
<td align="left">Idle 시 클럭 Low, 첫 번째 엣지에서 샘플링</td>
</tr>
<tr>
<td align="left"><strong>1</strong></td>
<td align="center">0</td>
<td align="center">1</td>
<td align="left">Idle 시 클럭 Low, 두 번째 엣지에서 샘플링</td>
</tr>
<tr>
<td align="left"><strong>2</strong></td>
<td align="center">1</td>
<td align="center">0</td>
<td align="left">Idle 시 클럭 High, 첫 번째 엣지에서 샘플링</td>
</tr>
<tr>
<td align="left"><strong>3</strong></td>
<td align="center">1</td>
<td align="center">1</td>
<td align="left">Idle 시 클럭 High, 두 번째 엣지에서 샘플링</td>
</tr>
</tbody></table>
<hr>
<h2 id="3-atmega328p-spi-주요-레지스터-정리">3. ATmega328P SPI 주요 레지스터 정리</h2>
<p>AVR 아키텍처에서 SPI를 제어하려면 크게 3가지 레지스터(<code>SPCR</code>, <code>SPSR</code>, <code>SPDR</code>)만 이해하면 됩니다.</p>
<h3 id="1-spcr-spi-control-register">1) SPCR (SPI Control Register)</h3>
<p>SPI의 동작 모드와 설정을 담당하는 가장 중요한 레지스터입니다.</p>
<table>
<thead>
<tr>
<th align="left">Bit</th>
<th align="left">Name</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>7</strong></td>
<td align="left"><strong>SPIE</strong></td>
<td align="left"><strong>SPI Interrupt Enable</strong>: 1로 설정 시 전송 완료 인터뷰트 활성화</td>
</tr>
<tr>
<td align="left"><strong>6</strong></td>
<td align="left"><strong>SPE</strong></td>
<td align="left"><strong>SPI Enable</strong>: 1로 설정 시 SPI 모듈 활성화</td>
</tr>
<tr>
<td align="left"><strong>5</strong></td>
<td align="left"><strong>DORD</strong></td>
<td align="left"><strong>Data Order</strong>: 1이면 LSB First, 0이면 MSB First</td>
</tr>
<tr>
<td align="left"><strong>4</strong></td>
<td align="left"><strong>MSTR</strong></td>
<td align="left"><strong>Master/Slave Select</strong>: 1이면 Master 모드, 0이면 Slave 모드</td>
</tr>
<tr>
<td align="left"><strong>3</strong></td>
<td align="left"><strong>CPOL</strong></td>
<td align="left"><strong>Clock Polarity</strong>: 1이면 Idle 시 High, 0이면 Low</td>
</tr>
<tr>
<td align="left"><strong>2</strong></td>
<td align="left"><strong>CPHA</strong></td>
<td align="left"><strong>Clock Phase</strong>: 데이터 샘플링 시점 결정 (Mode 설정)</td>
</tr>
<tr>
<td align="left"><strong>1:0</strong></td>
<td align="left"><strong>SPR1:0</strong></td>
<td align="left"><strong>SPI Clock Rate</strong>: 분주비 설정 (SCK 속도 결정)</td>
</tr>
</tbody></table>
<h3 id="2-spsr-spi-status-register">2) SPSR (SPI Status Register)</h3>
<p>현재 통신 상태를 확인하거나 속도를 배속하는 데 사용합니다.</p>
<ul>
<li><strong>Bit 7 - SPIF (SPI Interrupt Flag):</strong> 데이터 전송이 완료되면 1이 됩니다. <code>SPDR</code> 레지스터를 읽거나 인터럽트가 실행되면 자동으로 0이 됩니다.</li>
<li><strong>Bit 6 - WCOL (Write Collision Flag):</strong> 전송 중에 <code>SPDR</code>에 데이터를 쓰려고 하면 발생합니다.</li>
<li><strong>Bit 0 - SPI2X (Double SPI Speed Bit):</strong> 1로 설정하면 SPI 속도가 2배 빨라집니다.</li>
</ul>
<h3 id="3-spdr-spi-data-register">3) SPDR (SPI Data Register)</h3>
<p>실제 데이터를 주고받는 통로입니다.</p>
<ul>
<li><strong>쓰기:</strong> 이 레지스터에 값을 넣으면 즉시 전송이 시작됩니다 (Master 모드일 때).</li>
<li><strong>읽기:</strong> 전송이 완료된 후 이 레지스터를 읽으면 수신된 데이터를 얻을 수 있습니다.</li>
</ul>
<hr>
<h2 id="4-표준-spi-통신-시퀀스-standard-sequence">4. 표준 SPI 통신 시퀀스 (Standard Sequence)</h2>
<p>SPI 통신이 시작되고 끝날 때까지 하드웨어 내부에서 일어나는 논리적 순서입니다. 개발자는 이 타이밍을 이해해야 정확한 드라이버 코드를 작성할 수 있습니다.</p>
<h3 id="단계별-동작-과정">단계별 동작 과정</h3>
<ol>
<li><p><strong>Slave 선택 (SS/CS Assert)</strong></p>
<ul>
<li>Master가 통신하고자 하는 특정 Slave의 <strong>SS(Slave Select) 핀을 Low(0)</strong>로 떨어뜨립니다.</li>
<li>이때부터 해당 Slave는 활성화되어 Master의 클럭에 반응할 준비를 합니다.</li>
</ul>
</li>
<li><p><strong>클럭 생성 (SCLK Generation)</strong></p>
<ul>
<li>Master가 설정된 보드레이트(Baud Rate)에 맞춰 <strong>SCLK 신호를 출력</strong>하기 시작합니다.</li>
<li>CPOL 설정에 따라 클럭의 시작 레벨(High or Low)이 결정됩니다.</li>
</ul>
</li>
<li><p><strong>데이터 교환 (Data Shift-out/in)</strong></p>
<ul>
<li><strong>MOSI:</strong> Master는 출력할 비트를 MOSI 라인에 싣습니다.</li>
<li><strong>MISO:</strong> Slave는 응답 비트를 MISO 라인에 싣습니다.</li>
<li>CPHA 설정에 따라 클럭의 첫 번째 또는 두 번째 엣지에서 데이터를 <strong>샘플링(읽기)</strong>합니다.</li>
<li>보통 8비트(1바이트) 단위로 이 과정이 8번 반복됩니다.</li>
</ul>
</li>
<li><p><strong>전송 완료 플래그 (Interrupt Flag)</strong></p>
<ul>
<li>8비트 전송이 완료되면 MCU 내부의 <strong>SPIF(SPI Interrupt Flag)</strong>가 1로 세팅됩니다.</li>
<li>인터럽트가 설정되어 있다면 ISR(Interrupt Service Routine)이 실행됩니다.</li>
</ul>
</li>
<li><p><strong>Slave 해제 (SS/CS De-assert)</strong></p>
<ul>
<li>데이터 전송이 모두 끝나면 Master는 <strong>SS 핀을 다시 High(1)</strong>로 올립니다.</li>
<li>Slave는 비활성 상태로 돌아가며 MISO 라인을 High-Z(플로팅) 상태로 만듭니다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="⚠️-시퀀스-제어-시-주의사항">⚠️ 시퀀스 제어 시 주의사항</h3>
<ul>
<li><strong>Dummy Byte:</strong> SPI는 송신과 수신이 동시에 일어납니다. 슬레이브로부터 데이터를 읽기만 하고 싶을 때도, 마스터는 반드시 의미 없는 데이터(0xFF 등)를 <strong>송신</strong>하여 클럭을 생성해주어야 합니다.</li>
<li><strong>CS 딜레이:</strong> 아주 빠른 MCU를 사용할 경우, CS를 Low로 내린 직후 너무 빨리 데이터를 보내면 슬레이브가 준비되지 않아 첫 비트가 깨질 수 있습니다. 필요시 아주 짧은 지연 시간(Software Delay)을 주어야 합니다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[SG90+DHT11]]></title>
            <link>https://velog.io/@junhk9_9/SG90DHT11</link>
            <guid>https://velog.io/@junhk9_9/SG90DHT11</guid>
            <pubDate>Thu, 02 Apr 2026 07:32:48 GMT</pubDate>
            <description><![CDATA[<p>습도가 20초과이면 서보모터가 열림
미만이면 닫힘 상태로 작동하고, 시리얼 모니터에 현재 창문이 열렸는지와 온/습도 출력 프로그램.
9번(SG90),2번(DHT11)핀 사용</p>
<h3 id="arduino">Arduino</h3>
<pre><code>#include &lt;Servo.h&gt;
#include &lt;DHT.h&gt;

// [핀 설정]
#define DHTPIN 2       // DHT11 데이터 핀
#define DHTTYPE DHT11  // 센서 타입 설정
#define SERVO_PIN 9    // 서보 모터 핀

// [객체 생성]
DHT dht(DHTPIN, DHTTYPE);
Servo myServo;

// [상태 관리 변수]
unsigned long lastSensorRead = 0;
const unsigned long interval = 2000; // 2초 간격

void setup() {
  Serial.begin(9600);
  dht.begin();
  myServo.attach(SERVO_PIN);

  myServo.write(0); // 초기 상태: 닫힘
  Serial.println(&quot;--- Arduino Intelligent Window System Start ---&quot;);
}

void loop() {
  // [논블로킹 타이머] millis()를 사용해 2초마다 실행
  unsigned long currentMillis = millis();

  if (currentMillis - lastSensorRead &gt;= interval) {
    lastSensorRead = currentMillis;

    // 데이터 읽기
    float hum = dht.readHumidity();
    float temp = dht.readTemperature();

    // 센서 오류 체크
    if (isnan(hum) || isnan(temp)) {
      Serial.println(&quot;Failed to read from DHT sensor!&quot;);
      return;
    }

    // 시리얼 모니터 출력
    Serial.print(&quot;Temp: &quot;); Serial.print(temp);
    Serial.print(&quot;C, Hum: &quot;); Serial.print(hum);
    Serial.print(&quot;% | Window: &quot;);

    // --- 히스테리시스 제어 로직 ---

    if (hum &gt; 20) {
      myServo.write(90); // 90도로 이동
      Serial.println(&quot;OPEN&quot;);
    }

    else if (hum &lt; 20) {
      myServo.write(0); // 0도로 이동
      Serial.println(&quot;CLOSED&quot;);
    }
    // 이전 각도 유지
    else {
      Serial.println(&quot;KEEPING STATE&quot;);
    }
  }

  // 루프가 멈추지 않으므로 여기에 다른 실시간 코드 추가 가능!
}</code></pre><h3 id="avr">AVR</h3>
<pre><code>#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include &lt;avr/io.h&gt;
#include &lt;avr/interrupt.h&gt;
#include &lt;util/delay.h&gt;
#include &quot;UART0.h&quot;  // 기존에 사용하던 UART 라이브러리 포함

// [핀 정의]
#define DHT11_PIN     PD2    // DHT11 데이터 (디지털 2번)
#define SERVO_PIN     PB1    // SG90 신호 (디지털 9번)

// [서보 각도 설정]
#define WINDOW_OPEN   3000   // 90도 (약 1.5ms)
#define WINDOW_CLOSE  1000   // 0도 (약 0.5ms)

volatile uint32_t ms_ticks = 0;

// --- [1] Timer 0: 1ms 시스템 틱 (논블로킹 시간 측정) ---
void Timer0_Init(void) {
    TCCR0A = (1 &lt;&lt; WGM01);              // CTC 모드
    TCCR0B = (1 &lt;&lt; CS01) | (1 &lt;&lt; CS00); // 분주비 64
    OCR0A = 249;                        
    TIMSK0 |= (1 &lt;&lt; OCIE0A);            
}

// --- [2] Timer 1: 서보 하드웨어 PWM ---
void Timer1_Init(void) {
    DDRB |= (1 &lt;&lt; SERVO_PIN);           
    TCCR1A = (1 &lt;&lt; COM1A1) | (1 &lt;&lt; WGM11);
    TCCR1B = (1 &lt;&lt; WGM13) | (1 &lt;&lt; WGM12) | (1 &lt;&lt; CS11); 
    ICR1 = 39999;                       
    OCR1A = WINDOW_CLOSE;               
}

// --- [3] DHT11: 직접 제어 함수 ---
uint8_t DHT11_read_byte(void) {
    uint8_t result = 0;
    for (int i = 0; i &lt; 8; i++) {
        while (!(PIND &amp; (1 &lt;&lt; DHT11_PIN))); 
        _delay_us(30);                      
        if (PIND &amp; (1 &lt;&lt; DHT11_PIN)) result |= (1 &lt;&lt; (7 - i));
        while (PIND &amp; (1 &lt;&lt; DHT11_PIN));    
    }
    return result;
}

int8_t DHT11_get_data(uint8_t *h, uint8_t *t) {
    uint8_t data[5];
    DDRD |= (1 &lt;&lt; DHT11_PIN);
    PORTD &amp;= ~(1 &lt;&lt; DHT11_PIN);
    _delay_ms(18);
    PORTD |= (1 &lt;&lt; DHT11_PIN);
    _delay_us(30);
    DDRD &amp;= ~(1 &lt;&lt; DHT11_PIN); 

    _delay_us(40);
    if (PIND &amp; (1 &lt;&lt; DHT11_PIN)) return -1;
    _delay_us(80);
    if (!(PIND &amp; (1 &lt;&lt; DHT11_PIN))) return -1;
    _delay_us(80);

    for (int i = 0; i &lt; 5; i++) data[i] = DHT11_read_byte();

    if (data[4] == (data[0] + data[1] + data[2] + data[3])) {
        *h = data[0]; 
        *t = data[2]; 
        return 0;
    }
    return -2;
}

ISR(TIMER0_COMPA_vect) {
    ms_ticks++;
}

// --- [4] 메인 루프 ---
int main(void) {
    Timer0_Init();
    Timer1_Init();
    UART0_init(); // UART 초기화 (9600bps 가정)
    sei(); 

    uint8_t hum = 0, temp = 0;
    uint32_t last_sensor_read = 0;

    UART0_print_string(&quot;--- Intelligent Ventilation System Start ---\r\n&quot;);

    while (1) {
        // [논블로킹] 2초마다 센서 감시 및 시리얼 출력
        if (ms_ticks - last_sensor_read &gt;= 2000) {
            last_sensor_read = ms_ticks;

            if (DHT11_get_data(&amp;hum, &amp;temp) == 0) {
                // 1. 시리얼 모니터로 현재 상태 전송
                UART0_print_string(&quot;Temp: &quot;);
                UART0_print_1_byte_number(temp);
                UART0_print_string(&quot;C, Hum: &quot;);
                UART0_print_1_byte_number(hum);
                UART0_print_string(&quot;% | Window: &quot;);

                // 2. 히스테리시스 제어 로직 및 창문 상태 출력
                if ( hum &gt; 20) {
                    OCR1A = WINDOW_OPEN;
                    UART0_print_string(&quot;OPEN\r\n&quot;);
                }
                else if (hum &lt; 20) {
                    OCR1A = WINDOW_CLOSE;
                    UART0_print_string(&quot;CLOSED\r\n&quot;);
                }
                else {
                    UART0_print_string(&quot;KEEPING STATE\r\n&quot;);
                }
            } else {
                UART0_print_string(&quot;Sensor Read Error!\r\n&quot;);
            }
        }

        // 메인 루프는 계속 회전하므로 다른 실시간 작업 추가 가능
    }
}</code></pre><pre><code>#include &quot;UART0.h&quot;


void UART0_init(void)
{
    UBRR0H = 0x00;
    UBRR0L = 207;           // 9,600 보율 설정
    UCSR0A |= _BV(U2X0);    // 2배속 모드
    UCSR0C = 0x06;          // 비동기, 8비트 데이터, 패리티 없음, 1비트 정지 비트
    UCSR0B |= _BV(RXEN0);   // 수신 가능
    UCSR0B |= _BV(TXEN0);   // 송신 가능
}

void UART0_transmit(char data)
{
    while(!(UCSR0A &amp; (1&lt;&lt;UDRE0))); // 송신 대기
    UDR0 = data;
}

unsigned char UART0_receive(void)
{
    while(!(UCSR0A &amp; (1&lt;&lt;RXC0))); // 수신 대기
    return UDR0;
}

void UART0_print_string(char *str)
{
    for(int i = 0; str[i]; i++)
        UART0_transmit(str[i]);
}

void UART0_print_1_byte_number(uint8_t n)
{
    char numString[4] = &quot;0&quot;;
    int i, index = 0;
    if(n &gt; 0) {
        for(i = 0; n != 0; i++) {
            numString[i] = n % 10 + &#39;0&#39;;
            n = n / 10;
        }
        numString[i] = &#39;\0&#39;;
        index = i - 1;
    }
    for(i = index; i &gt;= 0; i--)
        UART0_transmit(numString[i]);
}

</code></pre><pre><code>#ifndef UART0_H_
#define UART0_H_

#include &lt;avr/io.h&gt;

void UART0_init(void);
void UART0_transmit(char data);
unsigned char UART1_receive(void);
void UART0_print_string(char *str);
void UART0_print_1_byte_number(uint8_t n);


#endif /* UART0_H_ */</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[PWM]]></title>
            <link>https://velog.io/@junhk9_9/PWM</link>
            <guid>https://velog.io/@junhk9_9/PWM</guid>
            <pubDate>Thu, 02 Apr 2026 02:39:01 GMT</pubDate>
            <description><![CDATA[<p>8bit FastPWM mode 예시 코드</p>
<pre><code>#ifndef F_CPU
#define F_CPU 16000000UL 
#endif

#include &lt;avr/io.h&gt;
#include &lt;util/delay.h&gt;
#include &quot;UART0.h&quot; // 제공해주신 헤더 포함

void Timer0_FastPWM_Init(void) {
    DDRD |= (1 &lt;&lt; DDD6); //6번핀 출력 설정
    TCCR0A = (1 &lt;&lt; COM0A1) | (1 &lt;&lt; WGM01) | (1 &lt;&lt; WGM00); // 비반전 모드, Fast PWM 모드
    TCCR0B = (1 &lt;&lt; CS02); // 분주비 256으로 설정
    OCR0A = 0; // 처음 LED밝기=0
}

int main(void) {
    Timer0_FastPWM_Init();
    UART0_init();

    int16_t brightness = 0;
    int8_t fadeAmount = 1;

    while (1) {
        OCR0A = (uint8_t)brightness; // PWM 듀티비 업데이트

        // 🔥 10단계마다 한 번씩만 시리얼 출력 (나머지 연산 % 활용)
        if (brightness % 10 == 0) {
            UART0_print_string(&quot;Brightness: &quot;);
            UART0_print_1_byte_number((uint8_t)brightness);
            UART0_print_string(&quot;\r\n&quot;);
        }

        brightness += fadeAmount;
        if (brightness &lt;= 0 || brightness &gt;= 255) { // 0이나 255까지 가면 반대로
            fadeAmount = -fadeAmount;

            // 방향 전환 시에는 확실히 표시
            UART0_print_string(fadeAmount &gt; 0 ? &quot;&gt;&gt; UP\r\n&quot; : &quot;&lt;&lt; DOWN\r\n&quot;);
        }

        _delay_ms(10); 
    }
}</code></pre><p>uart는 기존 코드들을 사용했고, serial monitor에 너무 빠르게 찍혀 자꾸 먹통이 됐다.
따라서 10단위로 출력되도록 해줬다.
9,10번 핀은 timer1번에 해당된다. timer0은 6번(PD6)핀을 사용해야 한다.</p>
<h3 id="timer1번-실습-코드">timer1번 실습 코드</h3>
<pre><code>#ifndef F_CPU
#define F_CPU 16000000UL // 시스템 클럭 정의 (16MHz)
#endif

#include &lt;avr/io.h&gt;
#include &lt;util/delay.h&gt;

// 1. Timer1 PWM 초기화 함수
void Timer1_PWM_Breathing_Init(void) {
    // 1-1. 출력 핀 설정: PB1 (OC1A)
    DDRB |= (1 &lt;&lt; PB1); 

    // 1-2. TCCR1A 레지스터 설정
    // - COM1A1 = 1: 비교 일치 시 OC1A 핀을 LOW로, BOTTOM 도달 시 HIGH로 (비반전 모드)
    // - WGM11 = 1, WGM10 = 1: 위상 교정 PWM 모드 (10비트 해상도, TOP = 0x03FF)
    TCCR1A = (1 &lt;&lt; COM1A1) | (1 &lt;&lt; WGM11) | (1 &lt;&lt; WGM10);

    // 1-3. TCCR1B 레지스터 설정
    // - WGM13 = 0, WGM12 = 0: 위상 교정 PWM 모드 (10비트) 유지
    // - CS11 = 1, CS10 = 1: 프리스케일러 64 설정 (122Hz flicker-free 주파수)
    TCCR1B = (1 &lt;&lt; CS11) | (1 &lt;&lt; CS10);

    // 1-4. 초기 듀티비 설정: 0% (OCR1A = 0)
    OCR1A = 0;
}

// 2. 메인 함수
int main(void) {
    Timer1_PWM_Breathing_Init(); // PWM 및 핀 초기화

    uint16_t brightness = 0;     // 현재 밝기 값 (OCR1A 대입용, 0~1023)
    int8_t fadeAmount = 1;       // 밝기 변화량 및 방향 (양수: 밝아짐, 음수: 어두워짐)

    while (1) {
        // 2-1. OCR1A 레지스터 업데이트 (듀티비 제어)
        OCR1A = brightness;

        // 2-2. 다음 루프를 위한 밝기 값 계산
        brightness += fadeAmount;

        // 2-3. 밝기 경계값 처리 및 방향 반전
        // 10비트 해상도이므로 TOP 값은 1023 (0x03FF)
        if (brightness &lt;= 0 || brightness &gt;= 1023) {
            fadeAmount = -fadeAmount; // 방향 전환

            // 극단적인 값에서의 오버플로/언더플로 방지 및 부드러운 전환을 위한 보정
            if(brightness &gt; 1023) brightness = 1023;
            if(brightness &lt; 0)    brightness = 0;
        }

        // 2-4. 숨쉬기 속도 조절을 위한 미세 지연 (블로킹 방식)
        // 이 지연 값에 따라 숨쉬기 주기가 결정됨 (너무 크면 끊겨 보임)
        _delay_ms(3);
    }
    return 0; // 이 줄에 도달하지 않음
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[8bit/16bit timer]]></title>
            <link>https://velog.io/@junhk9_9/8bit16bit-timer</link>
            <guid>https://velog.io/@junhk9_9/8bit16bit-timer</guid>
            <pubDate>Thu, 02 Apr 2026 01:21:14 GMT</pubDate>
            <description><![CDATA[<h2 id="🚀-avr-타이머카운터-모드-총정리-8비트-vs-16비트">🚀 AVR 타이머/카운터 모드 총정리 (8비트 vs 16비트)</h2>
<p>디지털 시스템 설계나 마이크로컨트롤러(MCU) 공부 시 핵심인 타이머/카운터의 주요 동작 모드를 정리했습니다.</p>
<hr>
<h3 id="1-8비트-타이머카운터-timer-0-2">1. 8비트 타이머/카운터 (Timer 0, 2)</h3>
<p>8비트 타이머는 구조가 단순하여 빠르고 반복적인 작업에 유리합니다. (0~255 카운트)</p>
<table>
<thead>
<tr>
<th align="left">모드명</th>
<th align="left">동작 특징</th>
<th align="left">주요 용도</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Normal (일반)</strong></td>
<td align="left">0에서 255(MAX)까지 증가 후 오버플로 발생 시 다시 0부터 시작.</td>
<td align="left">단순 시간 지연, 이벤트 카운팅</td>
</tr>
<tr>
<td align="left"><strong>CTC (Clear Timer on Compare)</strong></td>
<td align="left">0에서 사용자가 설정한 <code>OCRn</code> 값까지 증가 후 즉시 0으로 초기화.</td>
<td align="left"><strong>정확한 주파수 생성</strong>, 사각파 출력, 부저 음계 생성</td>
</tr>
<tr>
<td align="left"><strong>Fast PWM (고속 PWM)</strong></td>
<td align="left">0~255까지 <strong>단방향</strong> 카운팅. <code>OCRn</code>과 비교하여 핀 출력 상태 변화.</td>
<td align="left">LED 밝기 제어, DC 모터 속도 제어 (고속 응답)</td>
</tr>
<tr>
<td align="left"><strong>Phase Correct PWM</strong></td>
<td align="left">0~255까지 올라갔다 다시 0으로 내려오는 <strong>양방향</strong> 카운팅.</td>
<td align="left">정밀 모터 제어, 전력 변환 (상보적 대칭 파형)</td>
</tr>
</tbody></table>
<hr>
<h3 id="2-16비트-타이머카운터-timer-1-3">2. 16비트 타이머/카운터 (Timer 1, 3)</h3>
<p>16비트는 카운트 범위가 넓고(0~65535), 정밀도가 매우 높습니다.</p>
<ul>
<li><strong>Normal / CTC 모드:</strong> 8비트와 원리는 같으나 훨씬 긴 시간을 잴 수 있습니다.</li>
<li><strong>Fast PWM 모드:</strong> <code>ICR1</code> 레지스터를 TOP(천장)으로 설정 가능하여, 주기를 자유롭게 조절하면서 여러 개의 PWM 채널을 독립적으로 사용하기 좋습니다.</li>
<li><strong>Input Capture (입력 캡처):</strong> 외부 신호가 들어온 &#39;정확한 시점&#39;을 기록하는 16비트만의 특수 기능입니다. (초음파 센서, RPM 측정 등에 필수)</li>
</ul>
<hr>
<h3 id="3-핵심-개념-비교-8-bit-vs-16-bit">3. 핵심 개념 비교 (8-bit vs 16-bit)</h3>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">8-bit Timer</th>
<th align="left">16-bit Timer</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>카운트 범위</strong></td>
<td align="left">$0 \sim 255$</td>
<td align="left">$0 \sim 65,535$</td>
</tr>
<tr>
<td align="left"><strong>분해능(Resolution)</strong></td>
<td align="left">보통 (거친 제어)</td>
<td align="left"><strong>매우 높음 (섬세한 제어)</strong></td>
</tr>
<tr>
<td align="left"><strong>주요 활용</strong></td>
<td align="left">단순 주기 인터럽트, 비동기 모드(ASSR)</td>
<td align="left"><strong>서보 모터 제어</strong>, 정밀 시간 측정</td>
</tr>
</tbody></table>
<hr>
<h3 id="💡-보너스-꼭-알아야-할-관련-용어">💡 보너스: 꼭 알아야 할 관련 용어</h3>
<ul>
<li><strong>ASSR (Asynchronous Status Register):</strong> 내부 클록 대신 외부 32.768kHz 크리스탈을 써서 정확한 <strong>RTC(실시간 시계)</strong>를 구현할 때 설정하는 레지스터입니다.</li>
<li><strong>Glitch (글리치):</strong> 신호 전달 경로의 시간 차로 인해 발생하는 의도치 않은 아주 짧은 노이즈 신호입니다.</li>
<li><strong>Timing Jitter (타이밍 지터):</strong> 신호의 타이밍이 원래 위치에서 미세하게 흔들리는 현상으로, 데이터 에러의 원인이 됩니다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Timer/Counter]]></title>
            <link>https://velog.io/@junhk9_9/TimerCounter</link>
            <guid>https://velog.io/@junhk9_9/TimerCounter</guid>
            <pubDate>Wed, 01 Apr 2026 06:03:18 GMT</pubDate>
            <description><![CDATA[<h3 id="timer">Timer</h3>
<pre><code>클럭 신호를 셈.
TCNTn: 실제로 숫자가 올라가는 계수기, 8bit timer=0~255, 16bit=0~65535
TCCRn: 타이머의 동작 모드와 분주비를 설정하는 제어 레지스터
OCRn: TCNT값과 비교할 기준값을 저장, 일치하면 인터럽트 발생
TIMSK: 오버플로 및 비교 일치 인터럽트 사용여부 결정
TIFR: 인터럽트 조건이 만족되었을 때, 해당 비트가 1로 세팅되는 상태 레지스터</code></pre><h3 id="counter">Counter</h3>
<pre><code>Event(신호)를 셈.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[DHT11]]></title>
            <link>https://velog.io/@junhk9_9/DHT11</link>
            <guid>https://velog.io/@junhk9_9/DHT11</guid>
            <pubDate>Wed, 01 Apr 2026 04:17:41 GMT</pubDate>
            <description><![CDATA[<pre><code>#include &quot;DHT.h&quot;

#define DHTPIN 2     // Digital pin connected to the DHT sensor

// Uncomment whatever type you&#39;re using!
//#define DHTTYPE DHT11   // DHT 11
#define DHTTYPE DHT11   // DHT 11  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  Serial.println(F(&quot;DHTxx test!&quot;));

  dht.begin();
}

void loop() {
  // Wait a few seconds between measurements.
  delay(2000);


  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds &#39;old&#39; (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  float f = dht.readTemperature(true);

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println(F(&quot;Failed to read from DHT sensor!&quot;));
    return;
  }

  // Compute heat index in Fahrenheit (the default)
  float hif = dht.computeHeatIndex(f, h);
  // Compute heat index in Celsius (isFahreheit = false)
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print(F(&quot;Humidity: &quot;));
  Serial.print(h);
  Serial.print(F(&quot;%  Temperature: &quot;));
  Serial.print(t);
  Serial.print(F(&quot;°C &quot;));
  Serial.print(f);
  Serial.print(F(&quot;°F  Heat index: &quot;));
  Serial.print(hic);
  Serial.print(F(&quot;°C &quot;));
  Serial.print(hif);
  Serial.println(F(&quot;°F&quot;));
}</code></pre><p>아두이노 우노와 연결해서 시리얼 모니터에 온/습도 측정</p>
<h3 id="avr변환-코드">AVR변환 코드</h3>
<pre><code>#ifndef F_CPU
#define F_CPU 16000000UL // 16MHz 클럭 설정
#endif

#include &lt;avr/io.h&gt;
#include &lt;util/delay.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &quot;UART0.h&quot; //기존에 했던 헤더파일, uart통신 관련 선언이 들어있음 UART0.c와 함께 사용
// --- 설정: DHT11 연결 핀 (아두이노 D2 = PORTD 2) ---
#define DHT_PIN     PD2
#define DHT_DDR     DDRD
#define DHT_PORT    PORTD
#define DHT_PIN_REG PIND

uint8_t bits[5]; // 습도 정수, 습도 소수, 온도 정수, 온도 소수, 체크섬 저장


// [4] DHT11 데이터 수신 로직
int8_t dht11_read() {
    uint8_t j = 0, i = 0;
    memset(bits, 0, sizeof(bits));

    // 1. Start Signal
    DHT_DDR |= (1 &lt;&lt; DHT_PIN);    // 출력 모드
    DHT_PORT &amp;= ~(1 &lt;&lt; DHT_PIN);  // LOW로 내림
    _delay_ms(18);                // 최소 18ms 유지
    DHT_PORT |= (1 &lt;&lt; DHT_PIN);   // HIGH로 올림
    _delay_us(40);                // 20~40us 대기
    DHT_DDR &amp;= ~(1 &lt;&lt; DHT_PIN);   // 입력 모드로 전환 (Pull-up에 의해 HIGH 유지)

    // 2. Sensor Response 확인
    _delay_us(40);
    if (DHT_PIN_REG &amp; (1 &lt;&lt; DHT_PIN)) return -1; // Response LOW가 안 오면 에러
    _delay_us(80);
    if (!(DHT_PIN_REG &amp; (1 &lt;&lt; DHT_PIN))) return -1; // Response HIGH가 안 오면 에러
    _delay_us(80);

    // 3. 40비트 데이터 읽기
    for (j = 0; j &lt; 5; j++) {
        for (i = 0; i &lt; 8; i++) {
            while (!(DHT_PIN_REG &amp; (1 &lt;&lt; DHT_PIN))); // HIGH가 될 때까지 대기
            _delay_us(30); // 30us 대기 후 핀 상태 확인

            if (DHT_PIN_REG &amp; (1 &lt;&lt; DHT_PIN)) {
                bits[j] |= (1 &lt;&lt; (7 - i)); // 30us 후에도 HIGH면 비트 &#39;1&#39;
            }
            while (DHT_PIN_REG &amp; (1 &lt;&lt; DHT_PIN)); // 다시 LOW가 될 때까지 대기
        }
    }

    // 4. 체크섬 확인 (바이트 0+1+2+3 == 바이트 4)
    if (bits[0] + bits[1] + bits[2] + bits[3] == bits[4]) return 0;
    return -2; // 체크섬 오류
}

int main(void) {
    char buf[64];
    UART0_init();
    UART0_print_string(&quot;--- DHT11 AVR System Start ---\r\n&quot;);

    while (1) {
        int8_t result = dht11_read();

        if (result == 0) {
            // 성공 시 출력 (정수 부분만 사용해도 DHT11은 충분합니다)
            sprintf(buf, &quot;Hum: %d.%d%%  Temp: %d.%dC\r\n&quot;, 
                    bits[0], bits[1], bits[2], bits[3]);
            UART0_print_string(buf);
        } else if (result == -1) {
            UART0_print_string(&quot;Sensor No Response!\r\n&quot;);
        } else {
            UART0_print_string(&quot;Checksum Error!\r\n&quot;);
        }

        _delay_ms(2000); // 센서 안정화를 위해 2초 대기
    }
}</code></pre><h3 id="uart0c">UART0.c</h3>
<pre><code>#include &quot;UART0.h&quot;


void UART0_init(void)
{
    UBRR0H = 0x00;
    UBRR0L = 207;           // 9,600 보율 설정
    UCSR0A |= _BV(U2X0);    // 2배속 모드
    UCSR0C = 0x06;          // 비동기, 8비트 데이터, 패리티 없음, 1비트 정지 비트
    UCSR0B |= _BV(RXEN0);   // 수신 가능
    UCSR0B |= _BV(TXEN0);   // 송신 가능
}

void UART0_transmit(char data)
{
    while(!(UCSR0A &amp; (1&lt;&lt;UDRE0))); // 송신 대기
    UDR0 = data;
}

unsigned char UART0_receive(void)
{
    while(!(UCSR0A &amp; (1&lt;&lt;RXC0))); // 수신 대기
    return UDR0;
}

void UART0_print_string(char *str)
{
    for(int i = 0; str[i]; i++)
        UART0_transmit(str[i]);
}

void UART0_print_1_byte_number(uint8_t n)
{
    char numString[4] = &quot;0&quot;;
    int i, index = 0;
    if(n &gt; 0) {
        for(i = 0; n != 0; i++) {
            numString[i] = n % 10 + &#39;0&#39;;
            n = n / 10;
        }
        numString[i] = &#39;\0&#39;;
        index = i - 1;
    }
    for(i = index; i &gt;= 0; i--)
        UART0_transmit(numString[i]);
}

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Interrupt]]></title>
            <link>https://velog.io/@junhk9_9/Interrupt</link>
            <guid>https://velog.io/@junhk9_9/Interrupt</guid>
            <pubDate>Tue, 31 Mar 2026 02:54:11 GMT</pubDate>
            <description><![CDATA[<p>Event가 발생했을때, 처리하고 복귀하는 매커니즘.
인터럽트 Vector 주소로 이동하여 코드를 실행한다.
ISR(인터럽트 서비스 루틴)</p>
<h3 id="vector-table">Vector Table</h3>
<p>메모리의 맨 앞부분에 주소가 고정되어 있다.
주소가 낮을수록 우선순위가 높다.
Reset&gt;Int0&gt;Int1&gt;...
리셋포함 총 35개</p>
<p>요청-주 프로그램 중단-상태저장-ISR실행-상태복원-복귀</p>
<h3 id="c언어-구현">c언어 구현</h3>
<p>#include &lt;avr/interrupt.h&gt; 헤더파일 필요
ISR(벡터이름){} 형식임.
전역 인터럽트 활성화 sei()
반대는 cli()</p>
<pre><code>#include &lt;avr/io.h&gt;
#include &lt;avr/interrupt.h&gt;

#define LED_PIN PB5 // 아두이노 우노 내장 LED (D13)
#define BTN_PIN PD2 // 외부 인터럽트 0 (D2)

volatile int state = 0;            // 현재 LED의 상태
void int0_init(void) {
    // 1. PD2 핀을 입력으로 설정 및 내부 풀업 저항 활성화
    DDRD &amp;= ~(1 &lt;&lt; BTN_PIN);
    PORTD |= (1 &lt;&lt; BTN_PIN);

    // 2. 인터럽트 감지 조건 설정 (EICRA 레지스터)
    // ISC01 = 1, ISC00 = 0 : 하강 에지(Falling Edge) 감지
    // 스위치를 눌러 전압이 GND로 떨어지는 순간을 포착함
    EICRA |= (1 &lt;&lt; ISC01); // 1번 비트를 1로
    EICRA &amp;= ~(1 &lt;&lt; ISC00); // 0번 비트를 0으로

    // 3. INT0 외부 인터럽트 허용 (EIMSK 레지스터)
    EIMSK |= (1 &lt;&lt; INT0);

    // 4. 전역 인터럽트 활성화
    sei(); 
}

// INT0 외부 인터럽트 서비스 루틴(ISR)
ISR(INT0_vect) {
    // LED 상태 반전 (Toggle)
    PORTB ^= (1 &lt;&lt; LED_PIN); 
}

int main(void) {
    // LED 핀을 출력으로 설정
    DDRB |= (1 &lt;&lt; LED_PIN);

    // 인터럽트 초기화
    int0_init();

    while(1) {
        // 메인 루프는 비워둠. 
        // CPU는 다른 작업을 하다가 버튼이 눌리면 즉각 ISR을 실행함.
    }
    return 0;
}</code></pre><p>평소엔 while문(아무것도 동작 안하는 무한루프)를 돌다가, ISR로 이동하면 LED상태를 토글시킨다. </p>
<h3 id="다중-인터럽트-처리">다중 인터럽트 처리</h3>
<p>우선순위부터 처리하고, 나머지는 대기(pending상태임)
기존 코드에서, 버튼 디바운싱 부분 추가. Timer0이용(8bit)</p>
<pre><code>#include &lt;avr/io.h&gt;
#include &lt;avr/interrupt.h&gt;

#define LED_PIN PB5 // 아두이노 내장 LED (D13)
#define BTN_PIN PD2 // 외부 인터럽트 0 (D2)
volatile int debounce_count = 0; // 전역 변수 추가
void init_hardware(void) {
    // 1. 입출력 핀 설정 (D2 풀업 저항 활성화)
    DDRB |= (1 &lt;&lt; LED_PIN);
    DDRD &amp;= ~(1 &lt;&lt; BTN_PIN);
    PORTD |= (1 &lt;&lt; BTN_PIN);

    // 2. INT0 외부 인터럽트 설정 (하강 에지)
    EICRA |= (1 &lt;&lt; ISC01);
    EICRA &amp;= ~(1 &lt;&lt; ISC00);
    EIMSK |= (1 &lt;&lt; INT0); 

    // 3. Timer0 초기화 (아직 타이머는 가동하지 않음)
    TCCR0A = 0x00; // 일반(Normal) 모드
    TCCR0B = 0x00; // 타이머 정지 상태

    sei(); // 전역 인터럽트 활성화
}

// [핵심 1] INT0 외부 인터럽트 (버튼이 눌리는 순간 1회만 실행됨)
ISR(INT0_vect) {
    // 1. 바운싱으로 인한 중복 실행을 막기 위해 INT0 감지 임시 차단
    EIMSK &amp;= ~(1 &lt;&lt; INT0);

    // 2. 실제 원하는 작업 수행
    PORTB ^= (1 &lt;&lt; LED_PIN); 

    // 3. 디바운싱 타이머(Timer0) 가동 설정
    TCNT0 = 0;              // 타이머 카운트 0으로 초기화
    TIFR0 |= (1 &lt;&lt; TOV0);   // 이전 오버플로우 플래그 강제 지움
    TIMSK0 |= (1 &lt;&lt; TOIE0); // Timer0 오버플로우 인터럽트 켜기

    // 프리스케일러 1024 적용하여 타이머 시작 
    // (16MHz / 1024 = 15625Hz. 256번 카운트 시 약 16.38ms 경과)
    TCCR0B = (1 &lt;&lt; CS02) | (1 &lt;&lt; CS00);
}

// [핵심 2] Timer0 오버플로우 인터럽트 (약 16.38ms 후 스위치 안정화 시점)
ISR(TIMER0_OVF_vect) {
    // 1. 타이머 정지 및 인터럽트 끄기 (1회용 지연 역할 완료)
    TCCR0B = 0x00; 
    TIMSK0 &amp;= ~(1 &lt;&lt; TOIE0);

    // 2. 대기하는 16ms 동안 발생했던 노이즈(바운싱) 인터럽트 플래그 강제 삭제
    EIFR |= (1 &lt;&lt; INTF0);

    // 3. INT0 외부 인터럽트 다시 켜기 (새로운 버튼 클릭 받을 준비 완료)
    EIMSK |= (1 &lt;&lt; INT0);
}

int main(void) {
    init_hardware();

    while(1) {
        // 메인 루프는 완전히 비어 있습니다. 
        // 딜레이 없이 100%의 CPU 성능을 다른 기능(UART, 센서 읽기 등)에 쏟을 수 있습니다.
    }
    return 0;
}</code></pre><p>delay는 함수는 가급적 지양하는 것이 좋음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SWAR(SIMD Within A Register)]]></title>
            <link>https://velog.io/@junhk9_9/SWARSIMD-Within-A-Register</link>
            <guid>https://velog.io/@junhk9_9/SWARSIMD-Within-A-Register</guid>
            <pubDate>Tue, 31 Mar 2026 01:16:24 GMT</pubDate>
            <description><![CDATA[<p>분할정복, 비트 마스킹을 활용하여 레지스터 내부에서 병렬 덧셈을 수행함.
브랜치리스(Branchless) 알고리즘이다.</p>
<pre><code>uint32_t popcount(uint32_t n) {
    n = n - ((n &gt;&gt; 1) &amp; 0x55555555);                   // Step 1: 2비트 그룹 합산
    n = (n &amp; 0x33333333) + ((n &gt;&gt; 2) &amp; 0x33333333);    // Step 2: 4비트 그룹 합산
    n = (n + (n &gt;&gt; 4)) &amp; 0x0F0F0F0F;                   // Step 3: 8비트 그룹 합산
    return (n * 0x01010101) &gt;&gt; 24;                     // Step 4: 최종 누적 및 시프트
}</code></pre><h3 id="step1-2bit-합산">Step1 (2bit 합산)</h3>
<p>n = n - ((n &gt;&gt; 1) &amp; 0x55555555) =&gt; 0101
1011 -&gt; (1+0) (1+1) -&gt; 01 10 
1의 개수를 저장하게 되는데, n=11(10진수)라면, n=00001011, 비트연산 후(00000101)
n에서 빼면, 00000110이 된다.</p>
<h3 id="step24bit">Step2(4bit)</h3>
<p>n=00000110이므로, 왼쪽 비트연산부터 하면, 00000010, 오른쪽은 00000001
더하면 즉, 00000010+00000001=00000011</p>
<h3 id="step38bit">Step3(8bit)</h3>
<p>4bit끼리 더함</p>
<h3 id="step4final">Step4(final)</h3>
<p>총 32bit로 합친다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ATmega128 시작]]></title>
            <link>https://velog.io/@junhk9_9/ATmega128</link>
            <guid>https://velog.io/@junhk9_9/ATmega128</guid>
            <pubDate>Fri, 27 Mar 2026 05:22:07 GMT</pubDate>
            <description><![CDATA[<h2 id="fuse-bits">Fuse Bits</h2>
<p>칩의 동작 환경(동작 주파수, 리셋) 등을 결정하는 3byte 비휘발성 메모리.
설정시 High, Low, Extended퓨즈로 나뉘고, 잘못설정하면 망가짐.</p>
<h3 id="low-fuse">Low Fuse</h3>
<p>시스템 클록, 분주비, 기동시간(Start-up time)등 설정</p>
<h3 id="high-fuse">High Fuse</h3>
<p>온-칩 디버깅(JTAG) 사용 여부, 부트 사이즈, 워치도그 타이머 상시 가동 여부 설정</p>
<h3 id="extended-fuse">Extended Fuse</h3>
<p>M103 호환모드 설정 등 추가적인 확장 시스템 옵션 제어</p>
<h3 id="program-test">Program Test</h3>
<p>Bring-up: LED점멸(Blink) 프로그램을 작성하고 보드에 다운로드하여 테스트 하는 과정. c로치면 hello world같은 느낌</p>
<h3 id="floating-state">Floating State</h3>
<p>입력 핀에 전압이 확정되지 않아 0인지 1인지 알 수 없는 불안정한 상태.
버튼을 누르지 않았음에도 누른 것으로 오인식할 수 있음.
저항 사용으로 해결.</p>
<h3 id="pull-up-resistor">Pull-up Resistor</h3>
<p>저항을 전원(VCC)쪽에 연결, 스위치는 GND
스위치OFF=High
스위치ON=LOW</p>
<h3 id="pull-down-resistor">PULL-Down Resistor</h3>
<p>저항을 GND, 스위치는 VCC
스위치OFF=LOW
스위치ON=High</p>
<h3 id="회로">회로</h3>
<p>Drive 용어(전기를 준다)가 나오면 트랜지스터가 들어감</p>
<h3 id="pull-up">Pull-up</h3>
<pre><code>#include &lt;avr/io.h&gt;

int main(void) {
    // LED 설정 (출력)
    DDRB |= (1 &lt;&lt; PB5) | (1 &lt;&lt; PB4); 

    // 버튼 설정 (입력)
    DDRD &amp;= ~((1 &lt;&lt; PD7) | (1 &lt;&lt; PD6)); 

    // 주석 해제 시 내부 풀업, 주석 처리 시 외부 풀업용
    // PORTD |= (1 &lt;&lt; PD7) | (1 &lt;&lt; PD6); 

    while (1) {
        // 7번 핀 확인: 결과가 0일 때(눌렸을 때) 참이 됨
        if (!(PIND &amp; (1 &lt;&lt; PD7))) { 
            PORTB |= (1 &lt;&lt; PB5);  
        } else {
            PORTB &amp;= ~(1 &lt;&lt; PB5);
        }

        // 6번 핀 확인
        if (!(PIND &amp; (1 &lt;&lt; PD6))) { 
            PORTB |= (1 &lt;&lt; PB4);  
        } else {
            PORTB &amp;= ~(1 &lt;&lt; PB4);
        }
    }
    return 0;
}</code></pre><h3 id="pull-down">Pull-Down</h3>
<pre><code>#include &lt;avr/io.h&gt;

int main(void) {
    // [설정 1] LED 방향 설정 (출력)
    // PB5(13번)와 PB4(12번)를 1로 만듭니다.
    DDRB |= (1 &lt;&lt; PB5) | (1 &lt;&lt; PB4); 

    // [설정 2] 버튼 방향 설정 (입력)
    // PD7(7번)과 PD6(6번)을 0으로 만듭니다.
    DDRD &amp;= ~((1 &lt;&lt; PD7) | (1 &lt;&lt; PD6)); 


    while (1) {
        // --- 7번 버튼(PD7) -&gt; 13번 LED(PB5) ---
        // 외부 풀다운이므로 누르면 1(High)이 됩니다.
        if (PIND &amp; (1 &lt;&lt; PD7)) { 
            PORTB |= (1 &lt;&lt; PB5);   // 누르면 켠다
        } else {
            PORTB &amp;= ~(1 &lt;&lt; PB5);  // 떼면 끈다
        }

        // --- 6번 버튼(PD6) -&gt; 12번 LED(PB4) ---
        if (PIND &amp; (1 &lt;&lt; PD6)) { 
            PORTB |= (1 &lt;&lt; PB4);   // 누르면 켠다
        } else {
            PORTB &amp;= ~(1 &lt;&lt; PB4);  // 떼면 끈다
        }
    }

    return 0;
}</code></pre><p>내부 PULL-DOWN 기능은 없다고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Arduino 이론]]></title>
            <link>https://velog.io/@junhk9_9/Arduino-%EC%9D%B4%EB%A1%A0</link>
            <guid>https://velog.io/@junhk9_9/Arduino-%EC%9D%B4%EB%A1%A0</guid>
            <pubDate>Thu, 26 Mar 2026 07:31:29 GMT</pubDate>
            <description><![CDATA[<p>레지스터: 명령어, 연산, 계산등을 임시 저장
각 레지스터는 주소를 가지지만, 헤더파일등 정의된 이름을 사용하여 c언어 변수처럼 사용가능하다.</p>
<p>Arduino vs. Atmega(AVR)
아두이노: 라이브러리 중심
AVR: 레지스터 중심</p>
<h3 id="uart-통신-해보기">UART 통신 해보기</h3>
<pre><code>#define F_CPU 16000000L      // CPU 클럭 속도 설정 (16MHz)
#define BIT_DELAY_US 104     // 9600bps 기준 한 비트가 유지되는 시간 (1초/9600 = 약 104us)
#define MY_RX PD2            // 소프트웨어 수신(RX) 핀을 디지털 2번(PORTD 2)으로 지정
#define MY_TX PD3            // 소프트웨어 송신(TX) 핀을 디지털 3번(PORTD 3)으로 지정

#include &lt;avr/io.h&gt;          // AVR 입출력 레지스터 정의 헤더
#include &lt;util/delay.h&gt;      // _delay_us, _delay_ms 함수 사용 헤더
#include &lt;stdio.h&gt;           // printf, scanf 등 표준 입출력 함수 사용 헤더

// 함수 원형 선언
void UART0_init(void);
int UART0_transmit(char data, FILE *stream);
int UART0_receive(FILE *stream);

// printf 출력을 위한 스트림 설정 (출력 방향을 UART0_transmit 함수로 연결)
FILE OUTPUT = FDEV_SETUP_STREAM(UART0_transmit, NULL, _FDEV_SETUP_WRITE);

// scanf 입력을 위한 스트림 설정 (입력 방향을 UART0_receive 함수로 연결)
FILE INPUT = FDEV_SETUP_STREAM(NULL, UART0_receive, _FDEV_SETUP_READ);

// 하드웨어 및 소프트웨어 포트 초기화 설정
void UART0_init(void)
{
    // --- 하드웨어 UART0 설정 (PC 통신용) ---
    UBRR0H = 0x00; 
    UBRR0L = 207;            // 16MHz, 2배속 모드에서 9600bps 맞추는 값
    UCSR0A |= _BV(U2X0);     // 2배속 모드 활성화 (오차 감소)
    UCSR0C |= 0x06;          // 데이터 8비트, 정지 1비트 설정 (비동기)
    UCSR0B |= _BV(RXEN0) | _BV(TXEN0); // 하드웨어 송수신 회로 켜기

    // --- 소프트웨어 UART 설정 (상대 보드 통신용) ---
    DDRD &amp;= ~(1 &lt;&lt; MY_RX);    // PD2(2번 핀)를 입력으로 설정 (상대방 데이터 받기)
    DDRD |= (1 &lt;&lt; MY_TX);     // PD3(3번 핀)를 출력으로 설정 (상대방에게 데이터 보내기)
    PORTD |= (1 &lt;&lt; MY_TX);    // UART 통신 대기 상태는 항상 High(5V) 유지
}

// PC(시리얼 모니터)로 한 글자 보내는 함수 (printf 내부 동작)
int UART0_transmit(char data, FILE *stream)
{
    if (data == &#39;\n&#39;) UART0_transmit(&#39;\r&#39;, stream); // 줄바꿈(\n) 시 커서를 맨 앞으로(\r)

    while( !(UCSR0A &amp; (1 &lt;&lt; UDRE0)) ); // 하드웨어 송신 버퍼가 빌 때까지 대기
    UDR0 = data;                       // 버퍼에 데이터 전달 (자동 전송)

    return 0;
}

// PC(시리얼 모니터)로부터 한 글자 받는 함수 (scanf 내부 동작)
int UART0_receive(FILE *stream)
{
    while( !(UCSR0A &amp; (1 &lt;&lt; RXC0)) );  // 데이터가 완전히 들어올 때까지 대기
    return UDR0;                       // 수신 버퍼에서 데이터 읽어서 반환
}

// 상대방 보드(3번 핀)로 데이터 전송 (직접 신호 만들기)
void my_uart_tx(char data){
    // 1. Start Bit: 신호선을 Low(0V)로 떨어뜨려 전송 시작을 알림
    PORTD &amp;= ~(1 &lt;&lt; MY_TX);
    _delay_us(BIT_DELAY_US);

    // 2. Data Bits: 8비트 데이터를 한 비트씩 전송 (낮은 자리수부터)
    for (int i = 0; i &lt; 8; i++) {
        if (data &amp; (1 &lt;&lt; i))
            PORTD |= (1 &lt;&lt; MY_TX);  // 비트가 1이면 High
        else
            PORTD &amp;= ~(1 &lt;&lt; MY_TX); // 비트가 0이면 Low

        _delay_us(BIT_DELAY_US);    // 9600bps 속도에 맞춰 대기
    }

    // 3. Stop Bit: 신호선을 다시 High로 올려 전송 종료를 알림
    PORTD |= (1 &lt;&lt; MY_TX);
    _delay_us(500); // 다음 전송 전까지 충분한 안정 시간 확보
}

// 상대방 보드(2번 핀)로부터 데이터 수신 (직접 신호 읽기)
char my_uart_rx()
{
    char data = 0;

    // 데이터 비트의 중앙 지점에서 값을 읽기 위해 0.5비트 시간만큼 대기 (샘플링 정확도 향상)
    _delay_us(BIT_DELAY_US/2);

    for (int i = 0; i &lt; 8; i++) {
        _delay_us(BIT_DELAY_US);    // 다음 비트 위치로 이동
        if (PIND &amp; (1 &lt;&lt; MY_RX))    // 현재 핀 상태가 High(1)인지 확인
            data |= (1 &lt;&lt; i);       // 비트 저장
    }

    // 정지 비트가 끝날 때까지 대기하여 수신 완료
    _delay_us(BIT_DELAY_US);

    return data;
}

int main(void)
{
    UART0_init();         // 초기화 실행
    stdout = &amp;OUTPUT;     // printf가 사용할 통로 연결
    stdin = &amp;INPUT;       // scanf가 사용할 통로 연결

    char c, c_in;

    while (1)
    {
        // --- 1. [상대 보드 -&gt; 내 PC] 데이터 전달 ---
        // 2번 핀(RX)이 Low가 되면 상대방이 데이터를 보내기 시작한 것 (Start Bit 감지)
        if (!(PIND &amp; (1 &lt;&lt; MY_RX))) { 
            _delay_us(BIT_DELAY_US); // Start Bit가 지나가길 기다림
            c = my_uart_rx();        // 소프트웨어 방식으로 데이터 읽기
            printf(&quot;%c&quot;, c);         // 읽은 데이터를 PC 시리얼 모니터에 출력
        }

        // --- 2. [내 PC -&gt; 상대 보드] 데이터 전달 ---
        // 하드웨어 UART로 PC에서 데이터가 들어왔는지 확인
        if (UCSR0A &amp; (1 &lt;&lt; RXC0)) {
            scanf(&quot;%c&quot;, &amp;c_in);      // PC에서 친 글자 읽기
            my_uart_tx(c_in);        // 소프트웨어 방식으로 상대 보드에 전송
        }
    }

    return 0;
}</code></pre><p>기존 코드는 pc1과 아두이노1, pc2와 아두이노2 각각 통신하는 걸 2,3번 핀으로 설정하였고, 아두이노1과 2를 연결하는 코드가 빠졌다.
따라서, 위의 코드는 2번 핀이 Start Bit가 되는 순간을 검사하게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ARDUINO 시작]]></title>
            <link>https://velog.io/@junhk9_9/ARDUINO</link>
            <guid>https://velog.io/@junhk9_9/ARDUINO</guid>
            <pubDate>Tue, 24 Mar 2026 07:11:20 GMT</pubDate>
            <description><![CDATA[<p>m    밀리 (milli)        $10^{-3}$    1mm (미리미터)
μ    마이크로 (micro)  $10^{-6}$    머리카락 굵기 정도
n    나노 (nano)        $10^{-9}$    DNA 분자 크기 수준
p    피코 (pico)        $10^{-12}$    원자 크기 수준
LED 속에 커다란게 -임
PNP트랜지스터: 전기를 주면 꺼짐, 뺏으면 켜짐.
NPN트랜지스터: 전기를 주면(High) 켜짐, 뺏으면 꺼짐.
RISC: 코드가 길은만큼 전력소모 감소.
CISC: 코드가 짧은만큼 많은 기능을 함-&gt; 전력소모 증가
ROM: 전원이 꺼져도 저장, 저장할 때
RAM: 전원이 꺼지면 날라감, 변수나 계산할 때
EEPROM(flash rom): ROM의 종류, RAM의 장점도 가져옴. 데이터 변경 가능.
<strong>FET(Field Effect Transistor)</strong></p>
<ul>
<li>&#39;전압&#39;으로 &#39;전류&#39;를 제어
Capacitor: 축전기
Diode: 전기가 한쪽 방향으로만 흐름</li>
</ul>
<p><strong>platfromio</strong>
.elf or .hex 파일이있으면 아두이노에 구울 수 있음.
volatile키워드를 써서 레지스터 주소에 접근할 수 있음.</p>
<pre><code>#include &lt;Arduino.h&gt; // PlatformIO에서는 이 헤더가 필수입니다.

void setup() {
  // DDRB 레지스터 주소 (0x24) 직접 접근 -&gt; 5번 비트 출력 설정
  *((volatile unsigned char *)0x24) |= 0x20; //2진수로 0010 0000

}

void loop() {
  // PORTB 레지스터 (0x25) -&gt; LED ON
  *((volatile unsigned char *)0x25) |= 0x20;

  delay(1000);

  // PORTB 레지스터 (0x25) -&gt; LED OFF
  *((volatile unsigned char *)0x25) &amp;= ~0x20;

  delay(1000);
}</code></pre><p>다른 방식으로 LED 켜보기(C)</p>
<pre><code>#include &lt;Arduino.h&gt;

// 1. LED 상태를 보관할 구조체 정의
typedef struct {
    uint8_t pin;               // 핀 번호
    unsigned long onTime;      // 켜져 있을 시간 (ms)
    unsigned long offTime;     // 꺼져 있을 시간 (ms)
    uint8_t state;             // 현재 상태 (HIGH/LOW)
    unsigned long prevMillis;  // 마지막으로 상태가 바뀐 시간
} Flasher;

// 2. 구조체 초기화 함수 (C++의 생성자 역할)
void Flasher_Init(Flasher* f, uint8_t pin, unsigned long on, unsigned long off) {
    f-&gt;pin = pin;
    f-&gt;onTime = on;
    f-&gt;offTime = off;
    f-&gt;state = LOW;
    f-&gt;prevMillis = 0;

    pinMode(f-&gt;pin, OUTPUT);
}

// 3. 상태 업데이트 함수 (구조체 포인터를 받아 상태 변경)
void Flasher_Update(Flasher* f, unsigned long currentMillis) {
    // 켜져 있고, 끄는 시간이 되었을 때
    if ((f-&gt;state == HIGH) &amp;&amp; (currentMillis - f-&gt;prevMillis &gt;= f-&gt;onTime)) {
        f-&gt;state = LOW;
        f-&gt;prevMillis = currentMillis;
        digitalWrite(f-&gt;pin, f-&gt;state);
    }
    // 꺼져 있고, 켜는 시간이 되었을 때
    else if ((f-&gt;state == LOW) &amp;&amp; (currentMillis - f-&gt;prevMillis &gt;= f-&gt;offTime)) {
        f-&gt;state = HIGH;
        f-&gt;prevMillis = currentMillis;
        digitalWrite(f-&gt;pin, f-&gt;state);
    }
}

// 구조체 변수(인스턴스) 선언
Flasher led1;
Flasher led2;
Flasher led3;

void setup() {
    // 각 LED 초기화 (주소 전달 연산자 &#39;&amp;&#39; 사용)
    Flasher_Init(&amp;led1, 13, 1000, 1000);   // 13번 핀
    Flasher_Init(&amp;led2, 12, 500, 500);   // 12번 핀
    Flasher_Init(&amp;led3, 11, 100, 100);    // 11번 핀
}

void loop() {
    // 성능 최적화: loop 한 번당 millis()는 한 번만 호출하여 전달
    unsigned long currentMillis = millis(); 

    // 각 LED 상태 업데이트
    Flasher_Update(&amp;led1, currentMillis);
    Flasher_Update(&amp;led2, currentMillis);
    Flasher_Update(&amp;led3, currentMillis);

    // 이 위치에 지연 없이 다른 작업 추가 가능
}</code></pre><p>C++</p>
<pre><code>#include &lt;Arduino.h&gt;

// LED 상태를 관리하는 클래스 정의
class Flasher {
  private:
    uint8_t ledPin;               // 제어할 핀 번호
    unsigned long OnTime;         // 켜져 있을 시간 (ms)
    unsigned long OffTime;        // 꺼져 있을 시간 (ms)

    int ledState;                 // 현재 LED 상태 (HIGH/LOW)
    unsigned long previousMillis; // 상태가 마지막으로 바뀐 시간

  public:
    // 생성자: 핀 번호 및 주기 설정
    Flasher(int pin, unsigned long on, unsigned long off) {
        ledPin = pin;
        OnTime = on;
        OffTime = off;

        ledState = LOW;
        previousMillis = 0;

        pinMode(ledPin, OUTPUT);
    }

    // 상태 업데이트 함수 (loop에서 지속 호출)
    void update() {
        // 현재 시간 확인
        unsigned long currentMillis = millis(); 

        // LED가 켜져 있고, 설정한 켜짐 시간(OnTime)이 지났을 때
        if ((ledState == HIGH) &amp;&amp; (currentMillis - previousMillis &gt;= OnTime)) {
            ledState = LOW;                  // 상태를 LOW로 변경
            previousMillis = currentMillis;  // 현재 시간을 과거 시간으로 저장
            digitalWrite(ledPin, ledState);  // 실제 핀 제어
        }
        // LED가 꺼져 있고, 설정한 꺼짐 시간(OffTime)이 지났을 때
        else if ((ledState == LOW) &amp;&amp; (currentMillis - previousMillis &gt;= OffTime)) {
            ledState = HIGH;                 // 상태를 HIGH로 변경
            previousMillis = currentMillis;  // 현재 시간을 과거 시간으로 저장
            digitalWrite(ledPin, ledState);  // 실제 핀 제어
        }
    }
};

// 독립적인 주기를 가진 객체 생성
// led1: 13번 핀, 100ms 켜지고 400ms 꺼짐 (빠르게 깜빡임)
Flasher led1(13, 100, 400);

// led2: 12번 핀, 500ms 켜지고 500ms 꺼짐 (느리게 깜빡임)
Flasher led2(12, 500, 500);

// led3: 11번 핀, 50ms 켜지고 950ms 꺼짐 (1초에 한 번 짧게 깜빡임)
Flasher led3(11, 50, 950);

void setup() {
    // 핀 초기화는 객체 생성 시 수행되므로 생략 가능
}

void loop() {
    // 1. 각 LED의 상태 업데이트
    // 내부적으로 millis()를 검사하여 시간이 된 객체만 상태를 반전시킴
    led1.update();
    led2.update();
    led3.update();

    // 2. 다른 작업 추가
    // delay()가 없으므로 센서 읽기, 통신 등의 작업을 자유롭게 배치 가능
}</code></pre><p>TaskScheduler라이브러리 이용</p>
<pre><code>#include &lt;Arduino.h&gt;
#include &lt;TaskScheduler.h&gt;

Scheduler runner;

// 태스크 콜백 함수 선언
void blinkLED1();
void blinkLED2();

// 태스크 객체 생성 (주기ms, 반복횟수, 콜백함수, 스케줄러포인터, 자동시작)
Task task1(1000, TASK_FOREVER, &amp;blinkLED1, &amp;runner, true); // 1초 주기
Task task2(300, TASK_FOREVER, &amp;blinkLED2, &amp;runner, true);  // 0.3초 주기

void blinkLED1() {
    digitalWrite(13, !digitalRead(13)); // 내장 LED 토글
}

void blinkLED2() {
    digitalWrite(12, !digitalRead(12)); // 외부 LED 토글
}

void setup() {
    pinMode(13, OUTPUT);
    pinMode(12, OUTPUT);
    runner.startNow(); // 스케줄러 시작
}

void loop() {
    runner.execute(); // 스케줄러 실행 루프 (delay 사용 금지)
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[git]]></title>
            <link>https://velog.io/@junhk9_9/git</link>
            <guid>https://velog.io/@junhk9_9/git</guid>
            <pubDate>Wed, 18 Mar 2026 07:29:45 GMT</pubDate>
            <description><![CDATA[<h3 id="github-desktop말고-리눅스-환경에서-사용해보자">github desktop말고 리눅스 환경에서 사용해보자</h3>
<p>git init: git저장소로 설정 .git파일 생성된다.
git status: git의 현재 상태 확인
git add: 파일을 git에 추가함
git commit -m: 메시지를 추가하여 커밋함. -m &quot;ㅎㅇ&quot;
git restore: 마지막 commit상태로 복구시킴
git branch: 브랜치 생성
git switch: 브랜치 이동할 때
git merge: merge 시킬 때 
git remote remove: 연결 끊기
git remote add: 연결하기
git fetch: fetch할때 사용
git pull: fetch와 merge된 파일을 땡겨옴
git checkout: swithc와 비슷하나 충돌 위험 있음, 최근엔 사용하지 않는 편</p>
<h3 id="commit-전에-유저-정보를-입력해줘야함">commit 전에 유저 정보를 입력해줘야함</h3>
<p>git config --global user.email &quot;<a href="mailto:you@example.com">you@example.com</a>&quot;
git config --global user.name &quot;Your Name&quot;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수 포인터, void 포인터]]></title>
            <link>https://velog.io/@junhk9_9/%ED%95%A8%EC%88%98-%ED%8F%AC%EC%9D%B8%ED%84%B0-void-%ED%8F%AC%EC%9D%B8%ED%84%B0</link>
            <guid>https://velog.io/@junhk9_9/%ED%95%A8%EC%88%98-%ED%8F%AC%EC%9D%B8%ED%84%B0-void-%ED%8F%AC%EC%9D%B8%ED%84%B0</guid>
            <pubDate>Wed, 18 Mar 2026 04:45:16 GMT</pubDate>
            <description><![CDATA[<h3 id="함수-포인터">함수 포인터</h3>
<pre><code>#include &lt;stdio.h&gt;

int sum(int, int);         // 함수 선언

int main(void)
{
    int (*fp)(int, int);   // 함수 포인터 선언
    int res;               // 반환값 저장할 변수

    fp = sum;              // 함수명을 함수 포인터에 저장
    res = fp(10, 20);      // 함수 포인터로 함수 호출
    printf(&quot;result : %d\n&quot;, res);   // 반환값 출력

    return 0;
}

int sum(int a, int b)      // 함수 정의
{
    return (a + b);
}</code></pre><h3 id="void포인터">void포인터</h3>
<pre><code>#include &lt;stdio.h&gt;

int main(void)
{
    int a = 10;                           // int형 변수
    double b = 3.5;                       // double형 변수
    void* vp;                             // void 포인터

    vp = &amp;a;                              // int형 변수의 주소 저장
    printf(&quot;a : %d\n&quot;, *(int*)vp);

    vp = &amp;b;                              // double형 변수의 주소 저장
    printf(&quot;b : %.1lf\n&quot;, *(double*)vp);
//printf(&quot;b : %.1lf\n&quot;, *vp);           // 잘못된 표현(data type을 변경해야 한다!)
    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[C언어 복습]]></title>
            <link>https://velog.io/@junhk9_9/C%EC%96%B8%EC%96%B4-%EB%B3%B5%EC%8A%B5-9jxk6fct</link>
            <guid>https://velog.io/@junhk9_9/C%EC%96%B8%EC%96%B4-%EB%B3%B5%EC%8A%B5-9jxk6fct</guid>
            <pubDate>Tue, 17 Mar 2026 07:18:58 GMT</pubDate>
            <description><![CDATA[<h3 id="비트-연산-관련-예제">비트 연산 관련 예제</h3>
<pre><code>#define _CRT_SECURE_NO_WARNINGS
#include &lt;stdio.h&gt;
#include &lt;time.h&gt;
#include &lt;string.h&gt;
#define SIZE 7
int main(void)
{
    unsigned int num;
    int pos = 0;
    char cmd[SIZE];
    printf(&quot;Register:&quot;);
    scanf(&quot;%x&quot;, &amp;num);
    printf(&quot;Position:&quot;);
    scanf(&quot;%d&quot;, &amp;pos);
    printf(&quot;Command:&quot;);
    scanf(&quot;%s&quot;, cmd);
    if (strcmp(cmd, &quot;SET&quot;) == 0) {
        num |= (1 &lt;&lt; pos);
    }
    else if (strcmp(cmd, &quot;CLEAR&quot;) == 0) {
        num &amp;= ~(1 &lt;&lt; pos);
    }
    else if (strcmp(cmd, &quot;TOGGLE&quot;) == 0) {
        num ^= (1 &lt;&lt; pos);
    }
    else if (strcmp(cmd, &quot;CHECK&quot;) == 0) {
        int bit = (num &gt;&gt; pos) &amp; 1;
        printf(&quot;%d번 비트의 값은 %d입니다.\n&quot;, pos, bit);
    }

    printf(&quot;Result:0x%08X\n&quot;, num);
    printf(&quot;Binary:&quot;);
    for (int i = 31; i &gt;= 0; i--) {
        int bit = (num &gt;&gt; i) &amp; 1;
        printf(&quot;%d&quot;, bit);
        if (i % 4 == 0) {
            printf(&quot; &quot;);
        }
    }
    return 0;

}</code></pre><p>1를 pos만큼 왼쪽으로 밀면(1&lt;&lt;pos) 정확히 pos자리만 1인 비트가 생성된다. 이 생성된 mask를 가지고서 비트 연산을 하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[UART(Universal Asynchronous Receiver/Transmitter)]]></title>
            <link>https://velog.io/@junhk9_9/UARTUniversal-Asynchronous-ReceiverTransmitter</link>
            <guid>https://velog.io/@junhk9_9/UARTUniversal-Asynchronous-ReceiverTransmitter</guid>
            <pubDate>Wed, 04 Mar 2026 07:49:37 GMT</pubDate>
            <description><![CDATA[<h3 id="통신">통신</h3>
<h4 id="동기-vs-비동기">동기 vs 비동기</h4>
<p>동기:clk 신호에 맞춰서
비동기:clk 신호에 무관, 별도의 부호비트에 맞춰서 통신 하는것</p>
<h2 id="uart">UART</h2>
<p>비동기 직렬 통신 방식, MCU,FPGA,센서 등에서 널리 사용되는 통신 방법
직렬 통신:data를 1bit씩 순차적으로 전송
송신과 수신 기능을 동시에 처리 가능
단점:비교적 느림, 송수신 속도 정확하게 맞춰야 함, 다중 노드 통신 어려움
clk신호가 없기 때문에, 아래 순서대로 구성된다
Start Bit: 통신의 시작을 알림(항상 Low,1bit)
Data Bits:실제 전송하려는 데이터(보통 5~8bit)
Parity Bit:오류 검증을 위한 패리티 값을 생성하여 송신하고, 수신쪽에서 오류 판단
Stop Bit:데이터 전송 종료를 알림(항상 High,1bit)</p>
<p>Baud Rate:통신에서 1초에 전송하는 심볼의 수
bps(bits per second):1초에 몇개의 데이터 비트가 지나가는지</p>
<p>TX(Transmit):송신
RX(Receive):수신</p>
<p>9600bps에 char 문자 1개를 몇개까지 보낼수 있는가?
9600/10=960개
char=1byte=8bit
startbit=1bit
endbit=1bit</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[02.27]]></title>
            <link>https://velog.io/@junhk9_9/02.27</link>
            <guid>https://velog.io/@junhk9_9/02.27</guid>
            <pubDate>Fri, 27 Feb 2026 06:25:57 GMT</pubDate>
            <description><![CDATA[<h3 id="piezo-buzzer-pwm-control">Piezo Buzzer PWM control</h3>
<p>피에조 효과 또는 압전 효과를 이용하여 소리를 만들어 내는 소자
압전효과: 압력을 주면 전기적 신호가 발생, 전기적 신호를 받아 변형이 오는 효과</p>
<h3 id="setup-time-vs-hold-time">Setup Time VS. Hold Time</h3>
<p><strong>Setup Time</strong>: Rising or Falling Edge가 발생하기 직전에, data가 미리 도착해서 안정적으로 유지되어야 하는 최소 시간
<strong>원인</strong>: Clock 주기가 너무 빠른경우, 조합논리 회로가 너무 먼 경우
<strong>violation 해결방법</strong>: 조합논리 간소화
주파수를 낮추면 되지만, 성능이 떨어진다. 내부 RTL수정이 불가할 때 사용
문제가 되는 Path에 D-FF를 추가, 현업에선 보통 Pipeline을 삽입한다고 함
<strong>즉, 경로를 빠르게 만들면 된다.</strong></p>
<p><strong>Hold Time</strong>: 엣지가 발생한 직후에, data가 변하지 않고 안정적으로 유지되어야 하는 최소 시간
<strong>Violation 해결방법</strong>: buffer나 combi logic(조합회로)를 중간에 넣어준다
<strong>즉, 경로를 느리게 만들면 된다.(버퍼를 달면 된다.)</strong></p>
<h3 id="metastablility">Metastablility</h3>
<p>setuptime or holdtime을 어겼을 때, 발생하는 오류 상태
FF가 0인지 1인지 판별하지 못한다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[02.26]]></title>
            <link>https://velog.io/@junhk9_9/02.26</link>
            <guid>https://velog.io/@junhk9_9/02.26</guid>
            <pubDate>Thu, 26 Feb 2026 08:24:02 GMT</pubDate>
            <description><![CDATA[<h3 id="pcmpulse-code-modulation">PCM(Pulse Code Modulation)</h3>
<p>-아날로그 신호를 디지털 데이터로 변환시킴
 표본화(sampling)=아날로그 신호를 일정한 시간 간격으로 쪼갬
 양자화(Quantization)= 각 시점의 신호 크기를 반올림함
 부호화(Encoding)=0과1의 이진 코드로 변환
 표본화-양자화-부호화 순서대로 진행</p>
<h3 id="pwmpulse-width-modulation">PWM(Pulse Width Modulation)</h3>
<p> -디지털 환경에서, 1이 켜져 있는 시간의 비율(폭)을 조절하여 아날로그적인 효과를 내는 제어 방식
 신호가 켜져 있는 시간의 비율=Duty Cycle
 %가 높을 수록 HIGH값이 늘어남</p>
<h4 id="pwm-실습">PWM 실습</h4>
<pre><code>`timescale1ns /1ps
module top(
    inputclk,
    inputreset,
    inputincrease_duty_btn,
    inputdecrease_duty_btn,
    input[1:0] motor_direction,
    outputPWM_OUT,
    outputPWM_OUT_LED,
    output[1:0] in1_in2,
    output[3:0] an,
    output[7:0] seg
wirew_clean_inc_btn;
wirew_clean_dec_btn;
wire[3:0] w_DUTY_CYCLE;
wirew_tick;
 tick_generator u_tick_generator(
    
.clk(clk),
.reset(reset),
.tick(w_tick)
);
 debouncer u_increase_duty_btn(
.clk(clk),
.reset(reset),
.noisy_btn(increase_duty_btn),  
.clean_btn(w_clean_inc_btn)
);
 debouncer u_decrease_duty_btn(
.clk(clk),
.reset(reset),
.noisy_btn(decrease_duty_btn),  
.clean_btn(w_clean_dec_btn)
);
 pwm_duty_control u_pwm_duty_control(
.clk(clk),
.reset(reset),
.duty_inc(w_clean_inc_btn),
.duty_dec(w_clean_dec_btn),
.DUTY_CYCLE(w_DUTY_CYCLE), //FND에 출력 0~9
.PWM_OUT(PWM_OUT),
.PWM_OUT_LED(PWM_OUT_LED)
 fnd_contorller u_fnd_contorller(
.clk(clk),
.reset(reset),
.tick(w_tick),
.in_data(w_DUTY_CYCLE),
.motor_dir(motor_direction),
        
.an(an),
.seg(seg)
);
assignin1_in2=motor_direction;
endmodule</code></pre><p>btn_debouncer</p>
<pre><code>`timescale1ns /1ps
module btn_debouncer(
    inputclk,
    inputreset,
    input[2:0] btn,   // 3개의 버튼 입력: btn[2:0] → 각각 btnL, btnC, btnR
    output[2:0] debounced_btn
);
    debouncer U_debouncer_btnL(
.clk(clk),
.reset(reset),
.noisy_btn(btn[0]),
.clean_btn(debounced_btn[0])
    debouncer U_debouncer_btnC(
.clk(clk),
.reset(reset),
.noisy_btn(btn[1]),
.clean_btn(debounced_btn[1])
    debouncer U_debouncer_btnR(
.clk(clk),
.reset(reset),
.noisy_btn(btn[2]),
.clean_btn(debounced_btn[2])
 //   assign led = debounced_btn;   // button을 누를때 마다 led가 동작 되도록 한다.
endmodule
// 아래에 있는 코드는 버튼 디바운서 이다. 
// 이는 버튼 3개(btn[2:0])의 입력에서 발생할 수 있는 
// 노이즈(채터링)을 제거해, 안정적인 버튼 입력(debounced_btn[2:0])으로 만들어준다.
// 파라미터는 보통 클럭 카운트 제한값으로 사용되어,
// 얼마나 오랫동안 입력이 안정적인지를 판단하기 위한 시간 지연 설정이다.
// 예: 100MHz 클럭이라면, 999,999는 약 10ms의 디바운싱 지연을 의미할 수 있다.
module debouncer#(parameterDEBOUNCE_LIMIT =20&#39;d999_999) (
    inputclk,
    inputreset,
    inputnoisy_btn,  // raw noisy button input
    output regclean_btn
);
    reg[19:0] count;
    regbtn_state=0;
    always@(posedgeclk or posedgereset) begin
        if(reset) begin   // active-high reset
count &lt;=0;
btn_state &lt;=0;
clean_btn &lt;=0;
        end else if(noisy_btn ==btn_state) begin  // 버튼 상태가 이전과 동일할 경우 (안정됨)
count &lt;=0;
        end else begin
            if(count &lt;DEBOUNCE_LIMIT)  // 버튼 상태가 바뀌었지만 아직 안정되지 않은 경우
count &lt;=count +1;
            else begin  // 상태가 충분히 오랫동안 유지됨(10ms)
btn_state &lt;=noisy_btn;
clean_btn &lt;=noisy_btn;
count &lt;=0;  // 리셋하면 다음 변경을 다시 감지할 수 있음
            end
        end
    end
endmodule
pwm_duty_control
`timescale1ns /1ps
// 100Mhz/10 -&gt; 10Mhz의 주파수를 만듦.
// 100Mhz의 10% 조절 해상도를 가질 수 있는 최대 주파수
// 0~9까지 총 10번의 클럭을 세고
module pwm_duty_control(
    inputclk,
    inputreset,
    inputduty_inc,
    inputduty_dec,
    output[3:0] DUTY_CYCLE, //FND에 출력 0~9
    outputPWM_OUT,
    outputPWM_OUT_LED
reg[3:0] r_DUTY_CYCLE=4&#39;d5;
reg[3:0] r_counter_PWM;
// edge 검출 register
regr_prev_duty_inc,r_prev_duty_dec;
wirew_duty_inc=(duty_inc &amp;&amp;!r_prev_duty_inc); //rising edge 검출
wirew_duty_dec=(duty_dec&amp;&amp;!r_prev_duty_dec);
//duty cycle 제어 btnU,btnD
always@(posedgeclk, posedgereset) begin
    if(reset) begin
r_DUTY_CYCLE&lt;=4&#39;d5; //50% duty cycle
    end else begin
r_prev_duty_inc &lt;=duty_inc; //이전 상태 저장
r_prev_duty_dec&lt;=duty_dec;  
        if(w_duty_inc &amp;&amp;r_DUTY_CYCLE&lt;4&#39;d9)
r_DUTY_CYCLE&lt;=r_DUTY_CYCLE+1;
        if(w_duty_dec &amp;&amp;r_DUTY_CYCLE&gt;4&#39;d1)
r_DUTY_CYCLE&lt;=r_DUTY_CYCLE-1;
    end
end
// 10MHz PWM 신호 생성(0~9)
always@(posedgeclk, posedgereset) begin
    if(reset) begin
r_counter_PWM &lt;=0;
    end else begin
        if(r_counter_PWM &gt;=4&#39;d9)
r_counter_PWM&lt;=0;
            elser_counter_PWM&lt;=r_counter_PWM+1;
    end        
    end
    assignPWM_OUT=(reset)? 1&#39;b0: (r_counter_PWM&lt;r_DUTY_CYCLE) ? 1&#39;b1: 1&#39;b0;
    assignPWM_OUT_LED=PWM_OUT;
    assignDUTY_CYCLE=r_DUTY_CYCLE;
endmodule</code></pre><p>pwm_duty_control</p>
<pre><code>`timescale1ns /1ps
// 100Mhz/10 -&gt; 10Mhz의 주파수를 만듦.
// 100Mhz의 10% 조절 해상도를 가질 수 있는 최대 주파수
// 0~9까지 총 10번의 클럭을 세고
module pwm_duty_control(
    inputclk,
    inputreset,
    inputduty_inc,
    inputduty_dec,
    output[3:0] DUTY_CYCLE, //FND에 출력 0~9
    outputPWM_OUT,
    outputPWM_OUT_LED
reg[3:0] r_DUTY_CYCLE=4&#39;d5;
reg[3:0] r_counter_PWM;
// edge 검출 register
regr_prev_duty_inc,r_prev_duty_dec;
wirew_duty_inc=(duty_inc &amp;&amp;!r_prev_duty_inc); //rising edge 검출
wirew_duty_dec=(duty_dec&amp;&amp;!r_prev_duty_dec);
//duty cycle 제어 btnU,btnD
always@(posedgeclk, posedgereset) begin
    if(reset) begin
r_DUTY_CYCLE&lt;=4&#39;d5; //50% duty cycle
    end else begin
r_prev_duty_inc &lt;=duty_inc; //이전 상태 저장
r_prev_duty_dec&lt;=duty_dec;  
        if(w_duty_inc &amp;&amp;r_DUTY_CYCLE&lt;4&#39;d9)
r_DUTY_CYCLE&lt;=r_DUTY_CYCLE+1;
        if(w_duty_dec &amp;&amp;r_DUTY_CYCLE&gt;4&#39;d1)
r_DUTY_CYCLE&lt;=r_DUTY_CYCLE-1;
    end
end
// 10MHz PWM 신호 생성(0~9)
always@(posedgeclk, posedgereset) begin
    if(reset) begin
r_counter_PWM &lt;=0;
    end else begin
        if(r_counter_PWM &gt;=4&#39;d9)
r_counter_PWM&lt;=0;
            elser_counter_PWM&lt;=r_counter_PWM+1;
    end        
    end
    assignPWM_OUT=(reset)? 1&#39;b0: (r_counter_PWM&lt;r_DUTY_CYCLE) ? 1&#39;b1: 1&#39;b0;
    assignPWM_OUT_LED=PWM_OUT;
    assignDUTY_CYCLE=r_DUTY_CYCLE;
endmodule</code></pre><p>tick_generator</p>
<pre><code>module tick_generator(
    
    inputclk,
    inputreset,
    output regtick
);
    parameterINPUT_FREQUENCY =100_000_000; // 100MHz
    parameterTICK_Hz =1000; // 1kHz
    parameterTICK_COUNT =INPUT_FREQUENCY /TICK_Hz; // 100,000
    reg[$clog2(TICK_COUNT)-1:0] r_tick_counter =0;
    always@(posedgeclk or posedgereset) begin
        // 초기화
        if(reset) begin
tick &lt;=0;
r_tick_counter &lt;=0;
        end
        // 카운팅 완료
        else if(r_tick_counter ==TICK_COUNT-1) begin
r_tick_counter &lt;=0;
tick &lt;=1&#39;b1;          // 1클럭 폭 펄스
        end
        // r_counter 증가
        else begin
r_tick_counter &lt;=r_tick_counter +1;
tick &lt;=1&#39;b0;
        end
    end
endmodule</code></pre><p>btnU를 누르면 DUTY CYCLE이 커지고, btnD를 누르면 DUTY CYCLE이 작아진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Shift Register]]></title>
            <link>https://velog.io/@junhk9_9/Shift-Register</link>
            <guid>https://velog.io/@junhk9_9/Shift-Register</guid>
            <pubDate>Wed, 25 Feb 2026 08:33:15 GMT</pubDate>
            <description><![CDATA[<p>D Flip-Flop: D(Data), Clk(clock) 두 입력을 갖는 플립플롭, 출력은 보통 Q</p>
<h3 id="shift-register로-패턴-찾기">Shift Register로 패턴 찾기</h3>
<pre><code>`timescale 1ns / 1ps

// 1010111
module shift_register(
    input clk,
    input reset,
    input in,
    output  out
    );

reg [6:0] sr7;
always @(posedge clk, posedge reset) begin
    if(reset) begin
        sr7&lt;=7&#39;b0000000;
    end else
        sr7&lt;={sr7[5:0],in}; // shift register

end 

assign out=(sr7==7&#39;b1010111) ? 1 : 0;
endmodule
</code></pre><p>옆으로 미는 연산임</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[FSM으로 패턴 찾기]]></title>
            <link>https://velog.io/@junhk9_9/%EC%9E%84%EB%B2%A0%EB%94%94%EB%93%9C-%EA%B8%B0%EC%B4%88-%EC%A7%80%EC%8B%9D</link>
            <guid>https://velog.io/@junhk9_9/%EC%9E%84%EB%B2%A0%EB%94%94%EB%93%9C-%EA%B8%B0%EC%B4%88-%EC%A7%80%EC%8B%9D</guid>
            <pubDate>Tue, 24 Feb 2026 08:05:57 GMT</pubDate>
            <description><![CDATA[<h3 id="저항-색띠-읽는법-알아두기">저항 색띠 읽는법 알아두기!</h3>
<p>다이오드: 전류를 한쪽 방향으로만 흐르도록 제어함
다리 긴게 +임
유리속에 커다란게 -임</p>
<p>애노드/캐소드 (Anode/Cathode) </p>
<p>조합회로: 값 저장 x, clock 사용x, 입력이 바로 출력에 반영된다.
순차회로: 값 저장 O, clock 사용</p>
<p>FSM(Finite State Machine)
    유한한 상태 천이 머신, 현재 상태에 따라서 다음상태가 결정된다.
    현재 상태에 따라 출력이 결정된다.
    조합회로=무조건 다음 상태만 결정하는 회로
    <strong>State Register</strong>
    클럭의 엣지를 검출하여, 현재 상태로 업데이트하고 유지함
    State Transition Diagram-원은 상태, 선은 입력
   Moore Machine: 출력은 상태 레지스터의 값만 보고 만들어짐, 외부 입력은 연결 x, 안정,느림
   Mealy Machine: 출력은 현재 상태,입력 모두에 의해 결정된다, 노이즈(글리치)에 취약, 빠름
  FSM으로 패턴 찾기
  ex) 101011이 들어온 경우
  <img src="https://velog.velcdn.com/images/junhk9_9/post/92429c7c-4ff2-414a-bfbb-d6b77a69ec20/image.png" alt="">
  0110 패턴 찾기 코드</p>
<pre><code>`timescale 1ns / 1ps

module fsm_pattern(
    input clk,
    input reset,
    input in,
    output reg out
    );
parameter start=3&#39;d0,st1=3&#39;d1,st2=3&#39;d2,st3=3&#39;d3,st4=3&#39;d4;
reg [2:0] cur_state=start;
reg [2:0] next_state;

always@(*) begin

case(cur_state)
    start: next_state=in ? start : st1;
    st1: next_state=in ? st2 : st1;
    st2: next_state=in ? st3 : st1;
    st3: next_state=in ? start : st4;
    st4: next_state=in ? st2 : st1;

    default: next_state=start;
endcase
end


always@(posedge clk, posedge reset) begin
   if(reset)
     cur_state&lt;=start;
     else 
     cur_state&lt;=next_state;
end

always@(*) begin
    out=1&#39;b0;
    case(cur_state)
    st3: begin
            if (in==1&#39;b0) out=1&#39;b1;
            else out=1&#39;b0;
    end
    default: out=1&#39;b0;
endcase

end





endmodule
</code></pre><pre><code>`timescale 1ns / 1ps

module tb_fsm_pattern();
reg clk;
reg reset;
reg in;
wire out;
fsm_pattern u_fsm_pattern(
    .clk(clk),
    .reset(reset),
    .in(in),
    .out(out)
);
always  #5 clk=~clk;
initial begin
$monitor(&quot;time=%t, state=%b, in=%b, out=%b&quot;, $time, fsm_pattern.cur_state,in,out);
end

initial begin
clk=0;
reset=1;
in=0;
#100 reset=0;
@(posedge clk) in=0;
@(posedge clk) in=1;
@(posedge clk) in=0;
@(posedge clk) in=1;
@(posedge clk) in=1;
@(posedge clk) in=0;
@(posedge clk) in=0;
@(posedge clk) in=1;
@(posedge clk) in=0;
@(posedge clk) in=0;
@(posedge clk) in=1;
@(posedge clk) in=1;
@(posedge clk) in=0;
@(posedge clk) in=1;
@(posedge clk) in=1;
@(posedge clk) in=0;
#20;
$display(&quot;Finished&quot;);
$finish;
end



endmodule
</code></pre>]]></description>
        </item>
    </channel>
</rss>