<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>거북섬</title>
    <link>https://wnghks7787.tistory.com/</link>
    <description> why not change the world </description>
    <language>ko</language>
    <pubDate>Mon, 13 Jul 2026 18:31:35 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>전산거북이</managingEditor>
    <image>
      <title>거북섬</title>
      <url>https://tistory1.daumcdn.net/tistory/5520005/attach/c73dab642bb74999bcc135831022a919</url>
      <link>https://wnghks7787.tistory.com</link>
    </image>
    <item>
      <title>라즈베리파이와 STM32간 I2C 통신하기</title>
      <link>https://wnghks7787.tistory.com/58</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STM32 보드와 라즈베리파이 간 I2C 통신 방법을 기술한 문서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라즈베리파이 한 대당 여러 개의 STM32 보드를 연결해야 하므로, I2C 연결을 지향합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핀 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 5V와 GND를 연결한 상태에서, 오른쪽 핀에 SCL, SDA 핀을 연결합니다. 라즈베리파이의 SCL과 STM32의 SCL 핀이 연결되며, SDA역시 동일합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;163&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/leRT3/dJMcahSwrKp/9HoeDW16BTkBFP0RuSN6Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/leRT3/dJMcahSwrKp/9HoeDW16BTkBFP0RuSN6Yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/leRT3/dJMcahSwrKp/9HoeDW16BTkBFP0RuSN6Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FleRT3%2FdJMcahSwrKp%2F9HoeDW16BTkBFP0RuSN6Yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;163&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;163&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 생성하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공통 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STM32 CubeMX에서 프로젝트를 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpJFyg/dJMcagMYLN0/IrkDeJKYIAtlUNcqNaUXik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpJFyg/dJMcagMYLN0/IrkDeJKYIAtlUNcqNaUXik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpJFyg/dJMcagMYLN0/IrkDeJKYIAtlUNcqNaUXik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpJFyg%2FdJMcagMYLN0%2FIrkDeJKYIAtlUNcqNaUXik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;554&quot; height=&quot;572&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 PB8, 9를 각각 SCL, SDA로 설정해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, Connectivity &amp;rarr; I2C1 &amp;rarr; Mode를 Disable에서 I2C로 설정해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Parameter Settings에서 I2C Speed Mode는 Standard로, Clock Speed는 100000Hz로 잡아준 뒤, Primary slave address는 0x0a를 설정해줍니다. 이 값은 바꾸어도 괜찮지만, 반드시 설정은 해 주어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvfY2Y/dJMcaazb4cp/6mt4DxSZZPcOzgpOlSttV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvfY2Y/dJMcaazb4cp/6mt4DxSZZPcOzgpOlSttV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvfY2Y/dJMcaazb4cp/6mt4DxSZZPcOzgpOlSttV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvfY2Y%2FdJMcaazb4cp%2F6mt4DxSZZPcOzgpOlSttV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;522&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NZWR6/dJMcaasqvdf/DF9FHv9rYn34j4DgHamzok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NZWR6/dJMcaasqvdf/DF9FHv9rYn34j4DgHamzok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NZWR6/dJMcaasqvdf/DF9FHv9rYn34j4DgHamzok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNZWR6%2FdJMcaasqvdf%2FDF9FHv9rYn34j4DgHamzok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;765&quot; height=&quot;277&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPIO Settings의 GPIO Pull-up/Pull-down은 Pull-up으로, Maximum output speed는 Very High로 설정해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NVIC/DMA 중 원하는 것을 골라 설정을 해 주어야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NVIC 설정법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NVIC Settings에서 다음 event interrupt와 error interrput를 모두 enable 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzmpU0/dJMb99NIJRE/Rka2ImEdBEvswjXdFN4ui0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzmpU0/dJMb99NIJRE/Rka2ImEdBEvswjXdFN4ui0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzmpU0/dJMb99NIJRE/Rka2ImEdBEvswjXdFN4ui0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzmpU0%2FdJMb99NIJRE%2FRka2ImEdBEvswjXdFN4ui0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;765&quot; height=&quot;100&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DMA 설정법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DMA Settings에서 Add를 해 줍니다. 이후, slave를 설정할 것이므로 I2C_RX를 선택하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, Master로 사용할 것이라면 I2C_TX를 선택해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJ4zf/dJMcafHhKCd/KCL1tV2iCG7gN4ikVx4m51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJ4zf/dJMcafHhKCd/KCL1tV2iCG7gN4ikVx4m51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJ4zf/dJMcafHhKCd/KCL1tV2iCG7gN4ikVx4m51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJ4zf%2FdJMcafHhKCd%2FKCL1tV2iCG7gN4ikVx4m51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;776&quot; height=&quot;375&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 작성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code Generate를 한 뒤 다음을 추가해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 데이터를 받아야 하는 부분입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/* USER CODE BEGIN PV */
uint8_t RX_Buffer [10];        // Data to receive
volatile uint8_t rx_flag = 0;
/* USER CODE END PV */&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 receive 대기를 위한 코드입니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;  /* USER CODE BEGIN 2 */
  HAL_I2C_Slave_Receive_IT(&amp;amp;hi2c1, (uint8_t*)RX_Buffer, 1);
  HAL_Delay(100);
  /* USER CODE END 2 */&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;  while (1)
  {
    /* USER CODE END WHILE */


    /* USER CODE BEGIN 3 */
      if(rx_flag == 1)
      {
          if(RX_Buffer[0] == 0x01)
          {
              HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
          }
          else if(RX_Buffer[0] == 0x00)
          {
              HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
          }
          else
          {
              HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
          }
          HAL_I2C_Slave_Receive_IT(&amp;amp;hi2c1, (uint8_t*)RX_Buffer, 1);
      }
  }
  /* USER CODE END 3 */&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while문에서는 0x01을 받으면 LED를 키고, 0x00을 받으면 LED를 끄는것으로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외의 값을 받으면 토글하도록 만들어두었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 콜백함수는 다음과 같이 구현하였습니다. Drivers/Src/stm32f4xx_hal_i2c.c 에 weak로 작성되어있으니, 찾아서 가지고 오면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    if(hi2c-&amp;gt;Instance == I2C1)
    {
        rx_flag = 1;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버(라즈베리파이)에서 값을 보내주게 되면, stm32에서 그 값을 받아 적절히 LED를 제어합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 보내기 위해서 다음과 같이 테스트하십시오: &lt;code&gt;i2cset -y 1 0x0a 0x01&lt;/code&gt; 이는 0x0a 주소로 0x01 값을 보낸다는 의미입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 문서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wiki.st.com/stm32mcu/wiki/Getting_started_with_I2C&quot;&gt;https://wiki.st.com/stm32mcu/wiki/Getting_started_with_I2C&lt;/a&gt;&lt;/p&gt;</description>
      <category>기타</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/58</guid>
      <comments>https://wnghks7787.tistory.com/58#entry58comment</comments>
      <pubDate>Tue, 7 Jul 2026 17:04:47 +0900</pubDate>
    </item>
    <item>
      <title>[한화비전 VEDA 4기] 15주차 리뷰</title>
      <link>https://wnghks7787.tistory.com/57</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblRFec%2FdJMcahcVzMM%2FWJ076dAnFJFmpvQh2iEgek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;211&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;연구 세미나&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번 금요일에 이어 이번주 역시 연구 세미나를 진행하였습니다.&lt;br /&gt;역시 대외비라 많은 내용을 공유할 수는 없을 듯 합니다ㅜㅜㅜ&lt;br /&gt;다만, 한화비전 VEDA 아카데미에서 대여해주는 카메라의 정보 정도는 공유할 수 있을 듯 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20260705164342.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;928&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdfaai/dJMcacjmQSC/GdhGZxIWrmPc6DfyGEley0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdfaai/dJMcacjmQSC/GdhGZxIWrmPc6DfyGEley0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdfaai/dJMcacjmQSC/GdhGZxIWrmPc6DfyGEley0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdfaai%2FdJMcacjmQSC%2FGdhGZxIWrmPc6DfyGEley0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1218&quot; height=&quot;928&quot; data-filename=&quot;Pasted image 20260705164342.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;928&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이렇게 생긴 카메라 입니다. 저희 조는 PNO-A6081R을 사용하였고, 대다수의 조는 PNO-A9081R을 사용하였다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한화비전 VEDA 부트캠프에는 한화비전에서 시리얼 케이블을 사용할 수 있도록 지원하였으며, 각종 문서도 공유하여 주었습니다. 물론 내부 자료를 모두 받지는 못하지만, 꽤 많은 자료를 받을 수 있었습니다.&lt;/p&gt;
&lt;h1&gt;현장실습&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수요일에는 현장실습을 진행하였습니다.&lt;br /&gt;우선 두근두근... 최종 프로젝트 조가 발표되었습니다. 다들 5명 1팀이였는데 저희 조만 4인 1팀 이더라구요ㅜㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오후에는 한화비전 체험관을 투어하였습니다. 담당 과장님께서 체험관을 보여주셨습니다.&lt;br /&gt;처음 보는 카메라들도 많고, 인사이트도 넓힐 수 있었습니다.&lt;/p&gt;
&lt;h1&gt;최종 프로젝트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번주부터 최종프로젝트를 진행하였습니다.&lt;br /&gt;팀당 카메라는 최소 1대 이상을 지원받을 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pasted image 20260705164903.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b99ia8/dJMcahEY1Zy/qu9rJAgLnKBFVElbokhTS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b99ia8/dJMcahEY1Zy/qu9rJAgLnKBFVElbokhTS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b99ia8/dJMcahEY1Zy/qu9rJAgLnKBFVElbokhTS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb99ia8%2FdJMcahEY1Zy%2Fqu9rJAgLnKBFVElbokhTS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;Pasted image 20260705164903.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;라즈베리파이4를 서버로 이용하여 프로젝트를 진행해야 합니다.&lt;br /&gt;꽤나 해야할 것이 많은 프로젝트입니다ㅜㅜ&lt;br /&gt;우선적으로 해야 할 것은 당연히 프로젝트 주제 정하기였습니다. 이후, Jira와 Confluence를 협업 도구로 채택하여 프로젝트를 진행하게 되었습니다. 다음주 월요일까지 프로젝트 기획안을 제출해야 합니다.&lt;br /&gt;이 프로젝트 기획안이 제출된 이후에 대개 프로젝트 주제가 변경된다고는 하더라구요!!! 저희 팀도 아직 기초가 완전 탄탄하지 못한 것 같아서... 하루빨리 피드백을 받고싶습니다ㅋㅋㅋ&lt;/p&gt;
&lt;h1&gt;여담...&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 감사하게도! 이달의 TIL 챌린저에 당선되었습니다!&lt;br /&gt;그 덕분에 네이버 페이 1만원 권 받았습니다. 정말 감사합니다!!&lt;/p&gt;</description>
      <category>VEDA</category>
      <category>KDT</category>
      <category>veda</category>
      <category>글로벌소프트웨어아카데미</category>
      <category>내맘대로TIL챌린지</category>
      <category>베다</category>
      <category>임베디드</category>
      <category>임베디드SW아카데미</category>
      <category>한화비전</category>
      <category>한화비전아카데미</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/57</guid>
      <comments>https://wnghks7787.tistory.com/57#entry57comment</comments>
      <pubDate>Sun, 5 Jul 2026 16:56:26 +0900</pubDate>
    </item>
    <item>
      <title>[한화비전 VEDA 4기] 14주차 리뷰</title>
      <link>https://wnghks7787.tistory.com/56</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblRFec%2FdJMcahcVzMM%2FWJ076dAnFJFmpvQh2iEgek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;211&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;디버깅 툴&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ftrace, uftrace, strace&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ftrace는 저번주차에 이미 다룬 적 있는 커널 디버깅 툴입니다. 자세한 내용은 13주차 리뷰를 참고해주세요!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;strace&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strace 역시 디버깅 툴입니다. 무엇을 선택할 지는 본인의 자유일 듯 합니다. 다만, strace는 디버깅 정보가 없어도 어느정도 추적할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 서버
strace -e trace=network ./tcpServer

# 클라이언트
strace -e trace=network ./tcpClient&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식을 사용하면 다음과 같은 메시지를 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(24810), sin_addr=inet_addr(&quot;0.0.0.0&quot;)}, 16) = 0
listen(3, 10)                           = 0
accept(3, ^Cstrace: Process 11255 detached
 &amp;lt;detached ...&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 때문에 만약 디버깅 정보가 존재하지 않는 상태에서 디버깅을 해야한다면, strace를 사용하는 것이 괜찮은 선택이 될 듯 합니다. 여기서는 &lt;code&gt;bind()&lt;/code&gt;에서 htnos가 원하지 않는 값이 들어있습니다. 24810이라는 랜덤 값이 들어있기 때문에 이를 수정해주면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;uftrace&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 uftrace를 확인해볼까요?&lt;br /&gt;uftrace는 오픈 소스 tracing 도구입니다. 무려 한국인이 만든 프로젝트라고 합니다.&lt;br /&gt;&lt;a href=&quot;https://github.com/namhyung/uftrace&quot;&gt;https://github.com/namhyung/uftrace&lt;/a&gt;&lt;br /&gt;ftrace는 커널용 디버깅 툴인데에 비해 uftrace는 유저모드에서 사용하는 디버깅 툴입니다. 다만, 컴파일 시 &lt;code&gt;-pg&lt;/code&gt; 옵션을 넣어주어야 합니다. 프로파일링 옵션이라고 하네요.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;$ gcc ex01.c -o ex01 -pg
$ uftrace ./ex01
# DURATION     TID      FUNCTION
   0.847 us [  39158] | __monstartup();
   0.228 us [  39158] | __cxa_atexit();
            [  39158] | main() {
  35.376 us [  39158] |   open();
  11.839 us [  39158] |   write();
 167.510 us [  39158] |   close();
 216.014 us [  39158] | } /* main */

$ uftrace record ./ex01

# record 값 확인하기
$ uftrace replay

# 만약, 함수와 인자를 확인하려면
$ uftrace record -a ./ex01

# 만약, 커널 함수를 확인하려면
$ sudo uftrace record -k ./ex01&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uftrace 사용 예시입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라즈베리파이에서&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널의 function을 디버깅하기 위해서는 &lt;code&gt;System.map-&lt;/code&gt; 파일이 필요합니다만, 라즈베리파이에서는 해당 정보가 들어있지 않습니다. 이를 해결하기 위해서는 &lt;code&gt;sudo apt install linux-images-$(uname -r)-dbg&lt;/code&gt;를 설치해주면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo uftrace record -F &quot;*vfs*&quot; -K 3 --with-sym=/usr/lib/debug/boot/System.map-6.12.75+rpt-rpi-v8 ./ex01&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 설치된 파일의 위치를 잡아주면 uftrace가 정상적으로 작동하게 됩니다.&lt;/p&gt;
&lt;h1&gt;OpenCV&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학부생 시절 배운적이 있는 OpenCV입니다. 오픈 소스 라이브러리로, 학술적/상업적 용도로 무료로 사용이 가능한 강력한 툴입니다. OpenCV는 영상처리, 특징분석 등을 할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo apt update
$ sudo apt install libopencv-dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openCV의 버전과 컴파일 시 필요한 라이브러리는 다음과 같이 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;$ pkg-config --modversion opencv4
4.10.0

$ pkg-config --cflags --libs opencv4
-I/usr/include/opencv4

$ pkg-config --libs opencv4
-lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_cvv -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hdf -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_shape -lopencv_signal -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_highgui -lopencv_datasets -lopencv_text -lopencv_plot -lopencv_ml -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_wechat_qrcode -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 저걸 다 적어줄 수는 없습니다(너무 많으니까). 따라서 컴파일은 다음과 같이 하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;$ g++ src.c -o out $(pkg-config --cflags --libs opencv4)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 쓰기 불편하다면, make/cmake 등을 이용하면 됩니다.&lt;br /&gt;Cmake파일의 예시를 보여드리겠습니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;cmake_minimum_required(VERSION 3.10)
project(RaspberryPi_OpenCV_CPP)

# C++ 표준 설정
set(CMAKE_CXX_STANDARD 17)

# Opencv 패키지 찾기
find_package(OpenCV REQUIRED)

# 헤더 경로 포함
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 적어주면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;라즈베리파이에서 카메라 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 카메라는 전원 선을 분리한 상태로 연결하여야 합니다. 안그러면 모듈이 고장날 수 있습니다.&lt;br /&gt;...저도 알고싶지 않았습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ rpicam-hello  --list-cameras
Available cameras
-----------------
0 : imx219 [3280x2464 10-bit RGGB] (/base/soc/i2c0mux/i2c@1/imx219@10)
    Modes: 'SRGGB10_CSI2P' : 640x480 [206.65 fps - (1000, 752)/1280x960 crop]
                             1640x1232 [41.85 fps - (0, 0)/3280x2464 crop]
                             1920x1080 [47.57 fps - (680, 692)/1920x1080 crop]
                             3280x2464 [21.19 fps - (0, 0)/3280x2464 crop]
           'SRGGB8' : 640x480 [206.65 fps - (1000, 752)/1280x960 crop]
                      1640x1232 [83.70 fps - (0, 0)/3280x2464 crop]
                      1920x1080 [47.57 fps - (680, 692)/1920x1080 crop]
                      3280x2464 [21.19 fps - (0, 0)/3280x2464 crop]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ rpicam-hello -t 0
# 시간제한 없이 카메라를 볼 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 다음과 같이 나온다면 카메라는 성공입니다.&lt;br /&gt;이제 제대로 활용하기 위해서는 GStreamer 라는 툴이 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;패키지 설치&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ sudo apt install libglib2.0-dev libgstreamer1.0-0 libgstreamer1.0-dev

# 패키지 설치
$ sudo apt install gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav \ 
gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa libgstrtspserver-1.0-0

# 라즈베리파이 카메라용 플러그인/라이브러리 설치
$ sudo apt install libcamera-dev gstreamer1.0-libcamera
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구성 요소&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;elements: 하나하나의 객체를 의미합니다. elements들은 파이프라인으로 연결됩니다.&lt;/li&gt;
&lt;li&gt;pad: element의 입출력을 담당하는 구성 요소입니다.&lt;/li&gt;
&lt;li&gt;bins: 컨테이너로, elements를 담아냅니다.&lt;/li&gt;
&lt;li&gt;pipeline: 최상위 bin으로, 응용프로그램을 위한 버스를 제공합니다.사용법&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-bash&quot;&gt;# 특정 url의 영상 데이터 읽어오기
$ gst-launch-1.0 playbin uri=http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4
&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ gst-launch-1.0 videotestsrc pattern=0 ! autovideosink&lt;/p&gt;
&lt;h1&gt;v4l2src 디바이스가 없어서 에러가 나오는 경우&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ gst-launch-1.0 v4l2src ! videoconvert ! autovideosink&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ gst-launch-1.0 audiotestsrc ! audioresample ! autoaudiosink&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;여기서 `!` 는 파이프라인입니다. shell 에서 `|` 라고 생각하면 됩니다.
#### 라즈베리파이 카메라 송출
```bash
# 라즈베리파이 서버
$ gst-launch-1.0 libcamerasrc ! video/x-raw,colorimetry=bt709,format=NV12,width=1280,height=720,framerate=30/1 ! queue ! jpegenc ! multipartmux ! tcpserversink host=0.0.0.0 port=5000

# 플레이를 할 클라이언트
$ gst-launch-1.0 tcpclientsrc host=100.120.80.50 port=5000 ! multipartdemux ! jpegdec ! autovideosink&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;openCV에서 카메라 사용&lt;/h4&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;  6     std::string pipeline =
  7         &quot;libcamerasrc ! &quot;
  8         &quot;video/x-raw,width=640,height=480,framerate=30/1 ! &quot;
  9         &quot;videoconvert ! &quot;
 10         &quot;video/x-raw,format=BGR ! &quot;
 11         &quot;appsink&quot;;
 12
 13     cv::VideoCapture cap(pipeline, cv::CAP_GSTREAMER); // webcam&lt;/code&gt;&lt;/pre&gt;</description>
      <category>VEDA</category>
      <category>KDT</category>
      <category>veda</category>
      <category>글로벌소프트웨어아카데미</category>
      <category>내맘대로TIL챌린지</category>
      <category>베다</category>
      <category>임베디드</category>
      <category>임베디드SW아카데미</category>
      <category>한화비전</category>
      <category>한화비전아카데미</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/56</guid>
      <comments>https://wnghks7787.tistory.com/56#entry56comment</comments>
      <pubDate>Sun, 28 Jun 2026 16:44:08 +0900</pubDate>
    </item>
    <item>
      <title>[RDK 보드] config 화면 띄우기</title>
      <link>https://wnghks7787.tistory.com/55</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;RDK 시리즈는 AI 추론용 보드로, 라즈베리파이와 비슷한 생김새와 규격 덕분에 호환성이 좋다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 라즈베리파이와 다른 칩셋을 사용하며, 라즈베리파이와 다른 독자적인 규격들이 조금 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중 하나인 config 화면을 오픈하는 방법을 알아봅시다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 라즈베리파이에서&lt;/p&gt;
&lt;pre id=&quot;code_1782205571930&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo rapsi-config&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM8EEj/dJMcacKoJYL/3zwprA0SQgMCOkK4hgDBik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM8EEj/dJMcacKoJYL/3zwprA0SQgMCOkK4hgDBik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM8EEj/dJMcacKoJYL/3zwprA0SQgMCOkK4hgDBik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM8EEj%2FdJMcacKoJYL%2F3zwprA0SQgMCOkK4hgDBik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1096&quot; height=&quot;576&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. RDK 시리즈에서&lt;/p&gt;
&lt;pre id=&quot;code_1782205666102&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo srpi-config&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckIMgh/dJMcahLD6pq/dxMM4TI4XzZ3jmAptwPnQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckIMgh/dJMcahLD6pq/dxMM4TI4XzZ3jmAptwPnQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckIMgh/dJMcahLD6pq/dxMM4TI4XzZ3jmAptwPnQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckIMgh%2FdJMcahLD6pq%2FdxMM4TI4XzZ3jmAptwPnQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1092&quot; height=&quot;576&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생긴게 거의 똑같네요. 물론 모든게 똑같은건 아닙니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2PgkN/dJMb99Um0T1/s80z4VHM9BpX9UBXKix8k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2PgkN/dJMb99Um0T1/s80z4VHM9BpX9UBXKix8k0/img.png&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;395&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.0901%; margin-right: 10px;&quot; data-widthpercent=&quot;51.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2PgkN/dJMb99Um0T1/s80z4VHM9BpX9UBXKix8k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2PgkN%2FdJMb99Um0T1%2Fs80z4VHM9BpX9UBXKix8k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAAPTV/dJMb99Um0T7/3DM0ceM1Dk9ACVhQWALKDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAAPTV/dJMb99Um0T7/3DM0ceM1Dk9ACVhQWALKDK/img.png&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;426&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.7471%;&quot; data-widthpercent=&quot;48.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAAPTV/dJMb99Um0T7/3DM0ceM1Dk9ACVhQWALKDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAAPTV%2FdJMb99Um0T7%2F3DM0ceM1Dk9ACVhQWALKDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;637&quot; height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;라즈베리파이 config(좌), rdk config(우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라즈베리파이와 rdk 보드의 설정 가능한 범위가 조금 다른것을 알 수 있습니다. RDK는 Audio가 존재하지 않으며, Boot와 Auto Login이 붙어있는 등 세세한 부분이 조금 다르죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 불구하고 많은 것들을 여기서 설정할 수 있으며, 라즈베리파이와 상당히 유사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유용히 사용하세요!&lt;/p&gt;</description>
      <category>기타</category>
      <category>config</category>
      <category>RDK-X3</category>
      <category>RDK-X5</category>
      <category>라즈베리파이</category>
      <category>보드</category>
      <category>임베디드</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/55</guid>
      <comments>https://wnghks7787.tistory.com/55#entry55comment</comments>
      <pubDate>Tue, 23 Jun 2026 18:12:37 +0900</pubDate>
    </item>
    <item>
      <title>[한화비전 VEDA 4기] 13주차 리뷰</title>
      <link>https://wnghks7787.tistory.com/54</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblRFec%2FdJMcahcVzMM%2FWJ076dAnFJFmpvQh2iEgek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;211&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;부트 로더&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부트로더란, 컴퓨터가 켜졌을 때, 커널을 찾아 메모리에 올려주는 일을 합니다. 이러한 부트로더를 만들 때에는 사용할 rootfile 등을 잡아주어야 합니다. 또한, 하드웨어나 소프트웨어를 초기화해주기도 합니다.&lt;br /&gt;만약 커널이 적재되면, 부트로더는 메모리에서 내려갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부트로더는 직접 만들어줄 수도 있습니다. 보통 u-boot를 많이 이용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;u-boot&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ git clone git://git.denx.de/u-boot.git
$ cd u-boot
$ make CROSS_COMPILE=aarch64-gcc-gnu- &amp;lt;board_name&amp;gt;_defconfig # 여기서는 rpi_4_defconfig
$ make CROSS_COMPILE=aarch64-gcc-gnu- -j 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 uboot 빌드를 초기화하려면 다음 명령어를 수행하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ make CROSS_COMPILE=aarch64-gcc-gnu- distclean&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드가 완료되면 다음 파일이 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;u-boot
u-boot.bin
u-boot.lds
u-boot.lds&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;u-boot.bin&lt;/code&gt;을 라즈베리파이로 넘기면 부트로더를 올릴 수 있는데, 그러면 커널이 올라가지 않은 상태가 되기 때문에 ssh를 사용할 수 없게 됩니다.&lt;br /&gt;그럼에도 불구하고, UART 통신은 할 수 있기 때문에 이는 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dajdec/dJMcaf1jCpQ/bt9OUnU135V6ko9IjXuurK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dajdec/dJMcaf1jCpQ/bt9OUnU135V6ko9IjXuurK/img.png&quot; data-alt=&quot;USB to TTL&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dajdec/dJMcaf1jCpQ/bt9OUnU135V6ko9IjXuurK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdajdec%2FdJMcaf1jCpQ%2Fbt9OUnU135V6ko9IjXuurK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;480&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;USB to TTL&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생긴 usb-to-ttl 컨버터를 사용한다면, 직렬 통신을 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;203&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SuFcg/dJMcacp31MT/bnDQAZYRngjjQf2j0Gzsc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SuFcg/dJMcacp31MT/bnDQAZYRngjjQf2j0Gzsc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SuFcg/dJMcacp31MT/bnDQAZYRngjjQf2j0Gzsc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSuFcg%2FdJMcacp31MT%2FbnDQAZYRngjjQf2j0Gzsc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;203&quot; height=&quot;134&quot; data-origin-width=&quot;203&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;핀은 다음과 같이 연결해주면 됩니다. 다만, 라즈베리파이는 전송을 받는 입장이기 때문에, RX선을 TX에, TX선을 RX에 연결해주면 됩니다.&lt;br /&gt;이후, 라즈베리파이로 &lt;code&gt;u-boot.bin&lt;/code&gt;을 넘겨준 뒤, &lt;code&gt;/boot/firmware/&lt;/code&gt;에 해당 파일을 이동시키고, &lt;code&gt;/boot/firmware/config.txt&lt;/code&gt; 의 맨 밑에 &lt;code&gt;kernel=u-boot.bin&lt;/code&gt;을 추가해주면 됩니다. 이를 추가해주면 라즈베리파이의 기본 부트로더가 커널을 올리지 못하고 u-boot 부트로더를 올리게 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;부트로더 cmd 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부트로더의 cmd를 만들어줄 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;3588 U_BOOT_CMD(
3589     showled, 2, 1,  do_showled,
3590     &quot;print local hushshell variables&quot;,
3591     &quot;\n    - print values of all hushshell variables\n&quot;
3592     &quot;showvar name ...\n&quot;
3593     &quot;    - print value of hushshell variable 'name'&quot;
3594 );
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 부분이 있는데, 이 형식에 맞춰 함수를 만들어주면 됩니다.&lt;br /&gt;여기서는 &lt;code&gt;showled&lt;/code&gt; 라는 명령어를 입력하면 &lt;code&gt;do_showled&lt;/code&gt; 함수를 실행하라는 의미입니다.&lt;br /&gt;물론, &lt;code&gt;do_showled&lt;/code&gt; 함수는 직접 구현해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;부트로더를 이용하여 커널 부팅하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널을 부팅하기 위해서는 커널의 위치를 알아야 합니다.&lt;br /&gt;&lt;code&gt;uboot/board/raspberrypi/rpi/rpi.env&lt;/code&gt;에서 &lt;code&gt;kernel.addr&lt;/code&gt;을 찾을 수 있는데, 아마 &lt;code&gt;0x00080000&lt;/code&gt;에 존재할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;u-boot에서는 다음 명령어로 커널을 부팅할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;setenv bootargs 'console=tty1 console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw'

setenv user_mmc_boot '
mmc dev 0;
fatload mmc 0:1 ${kernel_addr_r} kernel8.img;
fatload mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb;
booti ${kernel_addr_r} - ${fdt_addr_r}'

setenv bootcmd 'run user_mmc_boot'

saveenv
printenv user_mmc_boot

reset&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 재부팅을 하게 되며, 이 이후로는 시리얼 통신이 아닌 ssh 통신을 할 수 있을 것입니다.&lt;/p&gt;
&lt;h1&gt;디바이스트리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드웨어를 구성하고 있는 요소를 arm에서 커널에게 알려주는 구조를 말합니다. 자료구조의 일종이라고 볼 수 있습니다.&lt;br /&gt;각 보드 별로 독자적으로 코드를 구성하게 되면 같은 SoC에서 파생된 보드임에도 전혀 다른 코드가 구현될 수 있습니다. 이렇게 되면 코드의 복잡도와 코드량 자체가 늘어나는 문제가 발생할 수 있습니다.&lt;br /&gt;그러나 디바이스트리를 구성하면 보드 구성이 바뀌더라도 커널 코드를 수정하지 않고도 동작할 수 있도록 만들 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;디바이스트리 구성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디바이스트리는 정해진 구성 방식이 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;/ {
    node@0 {
        a-string-property = &quot;A string&quot;;
        ...

        child_node@0 {
            first-child-property;
            ...
        };
    };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.dts&lt;/code&gt; 라는 파일로 만들어집니다. 이를 빌드하여 &lt;code&gt;.dtb&lt;/code&gt;를 만들어줍니다. 이렇게 만들어진 디바이스트리는 커널에 적재할 수 있습니다.&lt;br /&gt;만약 이미 적재된 디바이스트리 대신 다른 것을 올리고 싶다면 overlay할 수 있는데, 이는 dtoverlay 라고 하며 &lt;code&gt;.dtbo&lt;/code&gt; 확장자를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어진 장치들은 &lt;code&gt;/boot/firmware&lt;/code&gt; 에 등록할 수 있습니다. 여기 값을 등록한 뒤, &lt;code&gt;config.txt&lt;/code&gt;의 맨 밑줄에 &lt;code&gt;dtoverlay=gpioled,gpio=21,label=myled&lt;/code&gt; 라고 적는다면 dtoverlay를 사용할 수 있습니다. 물론 해당 내용은 본인의 상황에 맞게 바꾸어주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;code&gt;mydev.dts&lt;/code&gt; 라는 파일이 존재한다고 한 다음, 이 디바이스트리 소스를 사용하려면 다음과 같이 해주면 됩니다.&lt;br /&gt;먼저, &lt;code&gt;.dtbo&lt;/code&gt; 파일을 생성해주어야 합니다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ dtc -@ -I dts -O dtb -o mydev.dtbo mydev.dts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, 생성된 &lt;code&gt;.dtbo&lt;/code&gt; 파일은 &lt;code&gt;/boot/firmware/overlays&lt;/code&gt;에 위치시키고, &lt;code&gt;/boot/firmware/config.txt&lt;/code&gt;의 맨 밑줄에 다음을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;dtoverlay=mydev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 재시작을 하면 mydev가 디바이스트리로 들어가게 됩니다.&lt;/p&gt;
&lt;h1&gt;yocto&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 yocto에 대해 공부하였습니다. yocto 는 툴체인, 부트로더, 커널, 루트파일시스템을 한 번에 만들어 관리할 수 있는 툴입니다.&lt;br /&gt;yocto는 초대형 프로젝트로, 공식 문서 상 약 140기가 이상의 디스크 공간, 32기가 이상의 램이 필요하다고 합니다.&lt;br /&gt;현재 가상머신 스펙은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;6코어 cpu&lt;/li&gt;
&lt;li&gt;30287MB 메모리&lt;/li&gt;
&lt;li&gt;250GB 디스크&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 및 실행&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ubuntu에서 dependency 다운로드&lt;/h3&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;$ sudo apt-get install build-essential chrpath cpio debianutils diffstat file gawk gcc git iputils-ping libacl1 libcrypt-dev locales python3 python3-git python3-jinja2 python3-pexpect python3-pip python3-subunit socat texinfo unzip wget xz-utils zstd lz4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 먼저 필요한 패키지를 다운받아준 뒤, locale을 설정해줍니다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$ locale --all-locales | grep en_US.utf8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 locale이 지원되지 않는다고 한다면, 다음을 이용해 설정해주면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo dpkg-reconfigure locales&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;poky 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후로는 poky를 다운받습니다. poky는 레퍼런스 배포판이라고 합니다.&lt;br /&gt;&lt;a href=&quot;https://git.yoctoproject.org/poky/&quot;&gt;https://git.yoctoproject.org/poky/&lt;/a&gt;&lt;br /&gt;위 프로젝트로 들어가면 여러 버전이 나옵니다. 저는 일단 scarthgap으로 받았습니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;$ git clone -b scarthgap https://git.yoctoproject.org/poky&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, 환경 설정을 위해 스크립트를 수행해주어야 합니다.&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;$ source oe-init-build-env&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 자동으로 &lt;code&gt;build&lt;/code&gt; 폴더로 이동하게 된다. 뒤에 argument를 넣어주면 해당 폴더를 빌드 폴더로 사용하게 됩니다.&lt;br /&gt;&lt;code&gt;conf/local.conf&lt;/code&gt; 는 빌드하려는 장치의 정보가 담겨있으며, &lt;code&gt;conf/bblayer.conf&lt;/code&gt;는 메타레이어의 경로가 들어있습니다.&lt;br /&gt;만약 레이어를 추가하고 싶다면 &lt;code&gt;conf/bblayer.conf&lt;/code&gt;의 값을 변경해주면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;openembedded, meta-raspberrypi 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poky 폴더 안에 meta 폴더를 다운받아야 합니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;$ git clone -b scarthgap https://git.openembedded.org/meta-openembedded
$ git clone -b scarthgap https://git.yoctoproject.org/meta-raspberrypi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메타 데이터들을 통해 레이어를 쌓습니다. 여기서 추가되는 것들은 새로 추가되는 거고, 바뀌는 것은 바뀌게 됩니다. 이렇게 하나하나 쌓아가기 때문에 레이어라고 부르는 듯 합니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;$ vim conf/local.conf
# 이후, MACHINE ??= &quot;raspberripi4&quot; 로 수정

$ vim core/bblayers.conf
# 이후 다음을 추가해야 합니다.

/home/vboxuser/poky/meta-raspberrypi
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 설정을 마친 뒤, bitbake를 이용하여 빌드를 해주면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt; $ bitbake core-image-full-cmdline&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 yocto는 빌드 자체도 매우 오래걸리기 때문에 시간을 매우 많이(몇 시간 정도) 두고 빌드하면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;레시피&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어진 yocto에 대해서는(빌드 하기 전, 빌드파일이 생성되었을 때) &lt;code&gt;$ bitbake -c listtasks core-image-minimal&lt;/code&gt; 과 같은 방식으로 태스크 리스트를 확인할 수 있습니다.&lt;br /&gt;만약, bitbake 도중 실패하게 된다면, 어떤 작업에서 오류가 발생했는지 확인한 뒤, 해당 태스크만 clean한 뒤, 다시 빌드하는 것도 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;$ bitbake -c clean 타깃명
$ bitbake -c fetch 타깃명
$ bitbake core-image-minimal | --runall=fetch&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레시피는 다음과 같은 방식으로 만들어집니다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;  1 DESCRIPTION = &quot;A friendly program that prints Hello World!&quot;
  2 PROPERTY = &quot;optional&quot;
  3 SECTION = &quot;hello&quot;
  4
  5 LICENSE = &quot;GPLv2&quot;
  6 LIC_FILES_CHKSUM = &quot;file://${COMMON_LICENSE_DIR}/GPL2.0;md5=801f80980d171dd6425    610833a22dbe6&quot;
  7
  8 SRC_URI = &quot;file://helloworld.c&quot;
  9
 10 S = &quot;${WORKDIR}/sources&quot;
 11 UNPACKDIR = &quot;${S}&quot;
 12
 13 do_compile() {
 14     ${CC} ${CFLAGS} ${LDFLAGS} ${S}/helloworld.c -o ${B}/helloworld
 15 }
 16
 17 do_install() {
 18     install -d ${D}${bindir}
 19     install -m 0755 ${B}/helloworld ${D}${bindir}
 20 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;md5 값은 &lt;code&gt;md4sum&lt;/code&gt;을 통해 알아낼 수 있으며, src 파일을 넣어준다면 자동으로 빌드할 때 추가해줍니다.&lt;br /&gt;이렇게 추가된 함수는 해당 yocto를 통해 빌드한 이미지 파일 위에서 특별한 작업 없이도 사용할 수 있는 기본 명령어가 됩니다.&lt;/p&gt;
&lt;h1&gt;그 외...&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번주에는 기업 면접을 가느라 아쉽게도 목요일에 참여하지 못하였습니다.&lt;br /&gt;목요일은 한화비전 본사에서 취업특강과 BSP 직무설명 등을 진해하였다고 전해들었습니다. 다음 면접과는(있을지 모르겠지만) 겹치지 않으면 좋겠네요ㅜ&lt;/p&gt;</description>
      <category>VEDA</category>
      <category>KDT</category>
      <category>veda</category>
      <category>글로벌소프트웨어아카데미</category>
      <category>내맘대로TIL챌린지</category>
      <category>베다</category>
      <category>임베디드</category>
      <category>임베디드SW아카데미</category>
      <category>한화비전</category>
      <category>한화비전아카데미</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/54</guid>
      <comments>https://wnghks7787.tistory.com/54#entry54comment</comments>
      <pubDate>Mon, 22 Jun 2026 18:36:06 +0900</pubDate>
    </item>
    <item>
      <title>[한화비전 VEDA 4기] 12주차 리뷰</title>
      <link>https://wnghks7787.tistory.com/53</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblRFec%2FdJMcahcVzMM%2FWJ076dAnFJFmpvQh2iEgek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;211&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번주는 커널 프로그래밍을 공부하였습니다.&lt;/p&gt;
&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;커널이란?&quot;&gt;커널이란?&lt;/h1&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;커널은 자원을 관리하고 API를 제공하는 역할을 하는 프로그램 입니다.&lt;br /&gt;직접 뭔가를 하기보다는 응용프로그램(어플리케이션)이 잘 돌아갈 수 있도록 도와준다고 보면 될 듯 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;커널은 하나로 크게 만들어지기도 하지만, 모듈을 탑재하는 경우도 많습니다. 커널을 모듈화하면 커널을 빌드하는 시간을 줄일 수도 있고, 특정 모듈을 업데이트할 때도 커널 전체를 빌드하는 것이 아닌, 모듈만 바꿔 탑재하면 되기 때문에 유지 보수에서도 훨씬 편리하다는 장점이 존재합니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;$ cd /lib/moduels/$(uname -r)

$ cat modules.dep | more

kernel/arch/arm64/crypto/sha512-ce.ko.xz: kernel/arch/arm64/crypto/sha512-arm64.ko.xz
kernel/arch/arm64/crypto/sha3-ce.ko.xz: kernel/crypto/sha3_generic.ko.xz
kernel/arch/arm64/crypto/sm3-ce.ko.xz: kernel/crypto/sm3.ko.xz
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 우리는 현재 커널에 탑재되어있는 모듈을 확인할 수 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;xz는 압축되어있다는 것을 의미하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ko는 커널 오브젝트입니다. c언어에서 목적파일&lt;span&gt;&amp;nbsp;&lt;/span&gt;.o&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일과 비슷하다고 볼 수 있습니다.&lt;br /&gt;lsmod&lt;span&gt;&amp;nbsp;&lt;/span&gt;라는 명령어를 통해 현재 탑재된 모듈을 확인할 수도 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;커널 모듈을 올리거나 내리기 위해서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;sudo modprobe &amp;lt;모듈명&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 사용할 수 있습니다. 내릴 때는&lt;span&gt;&amp;nbsp;&lt;/span&gt;sudo modprobe -r &amp;lt;모듈명&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;입니다.&lt;br /&gt;혹은, 현재 폴더 위치에 있는 커널을 올리고 내린다면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;sudo insmod &amp;lt;모듈명&amp;gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;sudo rmmod &amp;lt;모듈명&amp;gt;을 입력하면 됩니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;모듈 프로그래밍&quot; data-ke-size=&quot;size26&quot;&gt;모듈 프로그래밍&lt;/h2&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;#include &amp;lt;linux/init.h&amp;gt;
#include &amp;lt;linux/module.h&amp;gt;

MODULE_LICENSE(&quot;GPL&quot;);

static int __init hello_init(void)
{
	printk(KERN_NOTICE &quot;Hello, World#2\n&quot;);
	return 0;
}

static void __exit hello_exit(void)
{
	printk(KERN_NOTICE &quot;Goodbye\n&quot;);
}

module_init(hello_init);
module_exit(hello_exit);
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선, 모듈 프로그래밍을 하기 위해서 사용되는 헤더는 모두 linux 에 위치합니다.&lt;br /&gt;또한,&lt;span&gt;&amp;nbsp;&lt;/span&gt;__init과&lt;span&gt;&amp;nbsp;&lt;/span&gt;__exit을 적어준 뒤,&lt;span&gt;&amp;nbsp;&lt;/span&gt;module_init(),&lt;span&gt;&amp;nbsp;&lt;/span&gt;module_exit()&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 통해 등록을 해 주어야 합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;obj-m := hello_module.o
KDIR := /usr/src/linux

default:
	make ARCH=arm64 CROSS_COMPILE=aarch64=linux-gnu- -C$(KDIR) M=$(shell pwd) modules
	
clean:
	make -C$(KDIR) M=$(shell pwd) clean
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같은 Makefile을 통해 빌드할 수 있습니다.&lt;br /&gt;이렇게 만들어진 커널 프로그램은&lt;span&gt;&amp;nbsp;&lt;/span&gt;.ko&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일로 생성되며, 이 파일만 주고받음으로써 커널 모듈을 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 함수를 통해 장치 번호를 할당받을 수 있습니다. 하드 코딩으로 장치 번호를 사용할 수도 있지만, 그렇게 하면 다른 장치와 번호가 겹칠 수 있기 때문에 지양합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;void cdev_init(struct cdev *cdev, const struct file_operations *fops);
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 함수는 캐릭터 장치를 초기화하는 함수입니다. 디바이스 드라이버를 만들 때는&lt;span&gt;&amp;nbsp;&lt;/span&gt;open(),&lt;span&gt;&amp;nbsp;&lt;/span&gt;close(),&lt;span&gt;&amp;nbsp;&lt;/span&gt;read()&lt;span&gt;&amp;nbsp;&lt;/span&gt;등을 사용할 때 어떤 일을 해야 하는지 기술할 수 있는데 이 때 만들어진 함수들은&lt;span&gt;&amp;nbsp;&lt;/span&gt;file_operations구조체에 저장하게 됩니다. 이 구조체에는 각 함수들을 넣을 수 있는 함수 포인터를 받습니다.&lt;br /&gt;cdev와 이 file_operations 를 연결하는 함수라고 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;int cdev_add(struct cdev *cdev, dev_t dev, unsigned int count);
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 위에서 할당받은 메이저/마이너 번호를 연결해줍니다.&lt;br /&gt;이렇게 하면 대략적인 커널 드라이버를 생성할 수 있게 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지는 init에 넣어 해당 모듈을 커널에 탑재할 때 일어날 수 있게 합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;void cdev_del(struct cdev *cdev);
void unregister_chrdev_region(dev_t dev, unsigned int count);
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;init에서 한 행동의 역순으로 exit에서 해 줍니다. 이것은 커널에서 모듈이 해제될 때 호출됩니다.&lt;/p&gt;</description>
      <category>VEDA</category>
      <category>KDT</category>
      <category>veda</category>
      <category>글로벌소프트웨어아카데미</category>
      <category>내맘대로TIL챌린지</category>
      <category>베다</category>
      <category>임베디드</category>
      <category>임베디드SW아카데미</category>
      <category>한화비전</category>
      <category>한화비전아카데미</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/53</guid>
      <comments>https://wnghks7787.tistory.com/53#entry53comment</comments>
      <pubDate>Sun, 14 Jun 2026 09:01:43 +0900</pubDate>
    </item>
    <item>
      <title>[디바이스마트 이벤트] RDK X3 후기</title>
      <link>https://wnghks7787.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요? 이번에는 디바이스마트에서 진행중인 RDK X3 지원 이벤트 후기에 대해 적어보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.devicemart.co.kr/page/event?seq=304&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.devicemart.co.kr/page/event?seq=304&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1781134668105&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;전자부품, 아두이노, 라즈베리파이, 공구 및 로봇까지 한곳에서 국내 최대 전자부품 전문몰 | 디&quot; data-og-description=&quot;디바이스마트&quot; data-og-host=&quot;www.devicemart.co.kr&quot; data-og-source-url=&quot;https://www.devicemart.co.kr/page/event?seq=304&quot; data-og-url=&quot;https://www.devicemart.co.kr/page/event?seq=304&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbz4mB/dJMb81f0S01/2S442r8skIusPeEjk0xwO1/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/BeWOw/dJMb82eVfyE/iry3CdhLjKBseFc0U1udD1/img.jpg?width=1200&amp;amp;height=1776&amp;amp;face=0_0_1200_1776,https://scrap.kakaocdn.net/dn/F7p4k/dJMb83SreZ2/NjYQrSzvwKm2W24WiPDoP1/img.jpg?width=900&amp;amp;height=900&amp;amp;face=0_0_900_900&quot;&gt;&lt;a href=&quot;https://www.devicemart.co.kr/page/event?seq=304&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.devicemart.co.kr/page/event?seq=304&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbz4mB/dJMb81f0S01/2S442r8skIusPeEjk0xwO1/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/BeWOw/dJMb82eVfyE/iry3CdhLjKBseFc0U1udD1/img.jpg?width=1200&amp;amp;height=1776&amp;amp;face=0_0_1200_1776,https://scrap.kakaocdn.net/dn/F7p4k/dJMb83SreZ2/NjYQrSzvwKm2W24WiPDoP1/img.jpg?width=900&amp;amp;height=900&amp;amp;face=0_0_900_900');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;전자부품, 아두이노, 라즈베리파이, 공구 및 로봇까지 한곳에서 국내 최대 전자부품 전문몰 | 디&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;디바이스마트&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.devicemart.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라즈베리파이는 조금 만져봤는데 RDK X3는 또 처음 만져보는거 같네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속 리뷰를 하려고 했는데, 충전기가 없어서 맞는 충전기도 구입하고 하다보니 조금 늦어졌습니다.ㅋㅋㅋ쿠ㅜㅜㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260611_083931637.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boA6rW/dJMcahdGdzm/tl3rEz5qF5efhhQwsHNCA1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boA6rW/dJMcahdGdzm/tl3rEz5qF5efhhQwsHNCA1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boA6rW/dJMcahdGdzm/tl3rEz5qF5efhhQwsHNCA1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboA6rW%2FdJMcahdGdzm%2Ftl3rEz5qF5efhhQwsHNCA1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot; data-filename=&quot;KakaoTalk_20260611_083931637.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품은 이렇게 박스에 담겨져 오는데, 꽤 이쁩니다. 뭔가 고급스럽네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진이 크게 찍힌거 같지만, 손바닥만합니다. 라즈베리파이와 동일한 사이즈라고 생각하심 될 듯 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dq4Lno/dJMcaiXSlNI/K3vyiZ6SdsnLWQRyY6PEPK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dq4Lno/dJMcaiXSlNI/K3vyiZ6SdsnLWQRyY6PEPK/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_20260611_083931637_01.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dq4Lno/dJMcaiXSlNI/K3vyiZ6SdsnLWQRyY6PEPK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdq4Lno%2FdJMcaiXSlNI%2FK3vyiZ6SdsnLWQRyY6PEPK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1acLD/dJMb99UdJwx/toURqSFaeuI6WnBE5EaADK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1acLD/dJMb99UdJwx/toURqSFaeuI6WnBE5EaADK/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_20260611_083931637_02.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1acLD/dJMb99UdJwx/toURqSFaeuI6WnBE5EaADK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1acLD%2FdJMb99UdJwx%2FtoURqSFaeuI6WnBE5EaADK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라즈베리파이와 규격 자체는 비슷할 듯 합니다. 아쉽게도 100% 일치하지는 않아서 제가 가지고 있던 라즈베리파이용 케이스에 담지는 못했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPIO 핀은 라즈베리파이와 완전 동일하다고 하니 참고하시면 될 듯 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 카메라 테스트를 해보고 싶었지만 현재 카메라가 없어 이는 다음으로 미루도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 간단한 커널 프로그램을 올려보면서 GPIO 작동을 확인해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 확인해야 할 것은 커널 버전과 아키텍처입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널은 리눅스 4.14.87 버전을 쓰며, aarch64 아키텍쳐를 사용중입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuGk6p/dJMcacDs10F/dVIw3YSWtnmcz9wa9w1FD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuGk6p/dJMcacDs10F/dVIw3YSWtnmcz9wa9w1FD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuGk6p/dJMcacDs10F/dVIw3YSWtnmcz9wa9w1FD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuGk6p%2FdJMcacDs10F%2FdVIw3YSWtnmcz9wa9w1FD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;258&quot; height=&quot;81&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 커널 프로그래밍을 해 보도록 합시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널 프로그래밍을 할 줄 알아야 이걸로 각종 디바이스를 제어하기 용이합니다. 나중에 카메라를 붙이는 것도 중요하지만, 카메라만 붙일건 아니니까요. GPIO 핀이 괜히 있는건 아니라고 생각합니다ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPIO 번호를 확인하기 위해서는 wiringPi를 사용하면 좋지만, 그건 라즈베리파이에서 작동하는 라이브러리라 여기서 작동할지도 모르고, 어차피 커널 프로그래밍을 하기 위해서는 어플리케이션 라이브러리는 사용할 수 없기 때문에 직접 번호를 확인해주는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히도 예제 코드가 들어있어 쉽게 확인할 수 있었습니다. 예제 코드가 있는지도 몰랐어서 삽질을 좀 했지만, 여러분들은 그러지 마세요...ㅎㅎ&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beZFJZ/dJMcacQWiAv/Ud1O1LQ1U2i7nLjNeZjXck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beZFJZ/dJMcacQWiAv/Ud1O1LQ1U2i7nLjNeZjXck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beZFJZ/dJMcacQWiAv/Ud1O1LQ1U2i7nLjNeZjXck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeZFJZ%2FdJMcacQWiAv%2FUd1O1LQ1U2i7nLjNeZjXck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;628&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시다시피 40pin_sample 이라는 폴더 안에 test_all_pins.py 가 있습니다. 코드 내부를 볼까요? 길지 않아서 코드로 보여드리겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1781223595226&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env python3

from __future__ import print_function
import sys
import time
import Hobot.GPIO as GPIO

# 获取所有可以控制的管脚 BOARD 编号
all_pins = list(GPIO.all_pin_data['BOARD'].keys())
all_pins.sort()

# 从命令行参数里面获取需要测试的管脚号序列
if len(sys.argv) &amp;gt; 1:
    all_pins = map(int, sys.argv[1:])

print(&quot;All gpio pins: &quot;, all_pins)

# 禁用警告信息
GPIO.setwarnings(False)

# 开始测试，依次拉高拉低管脚，电平保持250毫秒，键盘输入 CTRL-C 测试下一个管脚
for pin in all_pins:
    print(&quot;Testing pin %d as OUTPUT; CTRL-C to test next pin&quot; % pin)
    try:
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(pin, GPIO.OUT)
        while True:
            GPIO.output(pin, GPIO.HIGH)
            time.sleep(0.25)
            GPIO.output(pin, GPIO.LOW)
            time.sleep(0.25)
    except KeyboardInterrupt:
        pass
    finally:
        GPIO.cleanup()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중국에서 제작한 디바이스라서 그런지 한자가 많이 보이네요. 지금은 넘어가도 괜찮을 듯 합니다. 중요한건 아니니까요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 확인해보면, gpio 번호들을 쭉 매핑을 해서 어떤 위치에 있는 gpio가 몇 번인지 테스트를 하는 내용입니다. CTRL-C 을 이용하여 다음 핀으로 넘어가게 되어있다고 하네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암튼 이를 이용해서 저는 40번 핀에 연결한 것을 확인하였습니다. 아마 각각 다른 부분에 연결을 했을 거라 생각합니다. LED를 이용하여 테스트를 하였는데, 연결된 핀에 gpio 신호가 가면 LED가 깜빡깜빡 할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서!!! 반드시 확인을 해 주어야 하는게 있습니다. 저희가 단순히 어플리케이션을 만드는 게 아니라 디바이스 드라이버 모듈을 만들어 커널에 붙인다고 했잖아요? 그래서 저 번호를 그대로 사용할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfZTio/dJMcadI1n6G/S9hIvQ6ZVMYks1MrDqRvq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfZTio/dJMcadI1n6G/S9hIvQ6ZVMYks1MrDqRvq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfZTio/dJMcadI1n6G/S9hIvQ6ZVMYks1MrDqRvq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfZTio%2FdJMcadI1n6G%2FS9hIvQ6ZVMYks1MrDqRvq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;780&quot; height=&quot;578&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 명령어를 통해 볼 수 있는 내용들은 &quot;현재 사용중인 gpio&quot; 입니다. 위 명령어는 40번이 작동할 때이고, 아래는 작동을 멈추었을 때 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 보이죠? 빨간색으로 박스를 쳐놓은 gpio-108 번이 추가되었습니다. 즉, 40번 핀은 gpio-108이라는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 gpio를 이용하여 커널 드라이버를 만들 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 우선 home 디렉토리에 led_driver 라는 폴더를 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 밑에 led.c와 테스트를 위한 led_app.c를 만들 것 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1781227861403&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;linux/module.h&amp;gt;
#include &amp;lt;linux/init.h&amp;gt;
#include &amp;lt;linux/fs.h&amp;gt;
#include &amp;lt;linux/cdev.h&amp;gt;
#include &amp;lt;linux/io.h&amp;gt;
#include &amp;lt;linux/gpio.h&amp;gt;
#include &amp;lt;linux/uaccess.h&amp;gt;
#include &amp;lt;linux/device.h&amp;gt;

#define MY_MAJOR 200
#define MY_MINOR_BASE 0
#define MY_DEV_COUNT 1
#define MY_DEV_NAME &quot;my_led_dev&quot;
#define BUFFER_SIZE 1000

static dev_t dev_num;
static struct cdev led_cdev;
static struct class *led_class;
static struct device *led_device;

/* 모듈 메타데이터 설정 */
MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;sunrise&quot;);  
MODULE_DESCRIPTION(&quot;Skeleton Device Driver&quot;);

#define GPIO_LED (108)

volatile unsigned *gpio; /* I/O 접근을 위한 volatile 변수 */


static char *led_devnode(struct device *dev, umode_t *mode)
{
	if(mode)
	{
		*mode = 0666;
	}
	return NULL;
}

// open
static int led_open(struct inode *inode, struct file *file)
{
	pr_info(&quot;LED Device opened!\n&quot;);

	gpio_direction_output(GPIO_LED, 0);
	return 0;
}

// release
static int led_release(struct inode *inode, struct file *file)
{
	pr_info(&quot;LED Device closed!\n&quot;);
	return 0;
}

// write
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	char kernel_buffer[BUFFER_SIZE];
	int retval;

	if(count &amp;gt; BUFFER_SIZE - 1)
	{
		count = BUFFER_SIZE - 1;
	}

	retval = copy_from_user(kernel_buffer, buf, count);
	if(retval != 0)
	{
		pr_err(&quot;Failed to receive data from user\n&quot;);
		return -EFAULT;
	}

	kernel_buffer[count] = '\0';
	pr_info(&quot;Received from user: %s\n&quot;, kernel_buffer);

	(!strcmp(kernel_buffer, &quot;0&quot;)) ? gpio_set_value(GPIO_LED, 0) : gpio_set_value(GPIO_LED, 1);

	return count;
}

static const struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_release,
	.write = led_write,
};

/* 모듈이 커널에 로드될 때 호출되는 함수 */
static int __init led_init(void)
{
	int retval;

	pr_info(&quot;LED Module is up...\n&quot;);

	retval = alloc_chrdev_region(&amp;amp;dev_num, 0, 1, &quot;LED_Driver&quot;);

	if(retval &amp;lt; 0)
	{
		pr_err(&quot;Failed to allocate device number!\n&quot;);
		return retval;
	}
	cdev_init(&amp;amp;led_cdev, &amp;amp;led_fops);
	led_cdev.owner = THIS_MODULE;

	retval = cdev_add(&amp;amp;led_cdev, dev_num, 1);
	if(retval &amp;lt; 0)
	{
		pr_err(&quot;Failed to add cdev\n&quot;);
		unregister_chrdev_region(dev_num, 1);
		return retval;
	}

	led_class = class_create(THIS_MODULE, &quot;led_class&quot;);
	if(IS_ERR(led_class))
	{
		retval = PTR_ERR(led_device);
		goto err_del_cdev;
	}
	led_class-&amp;gt;devnode = led_devnode;

	led_device = device_create(led_class, NULL, dev_num, NULL, &quot;LED&quot;);
	if(IS_ERR(led_device))
	{
		retval = PTR_ERR(led_device);
		goto err_destroy_class;
	}

	pr_info(&quot;LED Module &amp;amp; /dev/LED setup successfully completed! Major: %d\n&quot;, MAJOR(dev_num));

	gpio_request(GPIO_LED, &quot;LED&quot;);

    return 0;

err_destroy_class:
	class_destroy(led_class);
err_del_cdev:
	cdev_del(&amp;amp;led_cdev);

	return retval;
}

/* 모듈이 커널에서 제거될 때 호출되는 함수 */
static void __exit led_exit(void)
{
	pr_info(&quot;LED Module is exiting...\n&quot;);

	device_destroy(led_class, dev_num);
	class_destroy(led_class);
	cdev_del(&amp;amp;led_cdev);
	unregister_chrdev_region(dev_num, MY_DEV_COUNT);
	gpio_free(GPIO_LED);
    pr_info(&quot;Unregistered device number\n&quot;);
}

module_init(led_init);  
module_exit(led_exit);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널 프로그래밍을 해 보신 분들은 조금 익숙하실 듯 합니다. 조금 뜯어볼까요?&lt;/p&gt;
&lt;pre id=&quot;code_1781228090339&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define GPIO_LED (108)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 얻은 gpio led의 핀 번호를 넣어줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1781227979170&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// open
static int led_open(struct inode *inode, struct file *file)
{
	pr_info(&quot;LED Device opened!\n&quot;);

	gpio_direction_output(GPIO_LED, 0);
	return 0;
}

// release
static int led_release(struct inode *inode, struct file *file)
{
	pr_info(&quot;LED Device closed!\n&quot;);
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분들은 open() 혹은 close()를 어플리케이션에서 요청을 하면 실행되는 부분입니다. 즉, 이 함수를 통해 어플리케이션에서 해당 디바이스를 오픈하면 gpio_direction_output() 이 실행되는 방식입니다. 이 부분을 통해 해당 gpio 핀을 output으로 설정해줄 수 있는 것입니다. close()를 하면 아무 작업도 하지 않고 dmesg를 통해 로그만 찍어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 write()를 했을 때 호출되는 함수입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1781228268536&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// write
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	char kernel_buffer[BUFFER_SIZE];
	int retval;

	if(count &amp;gt; BUFFER_SIZE - 1)
	{
		count = BUFFER_SIZE - 1;
	}

	retval = copy_from_user(kernel_buffer, buf, count);
	if(retval != 0)
	{
		pr_err(&quot;Failed to receive data from user\n&quot;);
		return -EFAULT;
	}

	kernel_buffer[count] = '\0';
	pr_info(&quot;Received from user: %s\n&quot;, kernel_buffer);

	(!strcmp(kernel_buffer, &quot;0&quot;)) ? gpio_set_value(GPIO_LED, 0) : gpio_set_value(GPIO_LED, 1);

	return count;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어렵게 생각할 거 없습니다. 중요한건 맨 밑에 strcmp() 한 줄입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널 버퍼에 0이 입력되면 gpio를 0으로 세팅하고, 그렇지 않으면 1로 세팅합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 모듈이 커널에 적재될 때 호출되는 함수인 init을 볼까요?&lt;/p&gt;
&lt;pre id=&quot;code_1781228364378&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 모듈이 커널에 로드될 때 호출되는 함수 */
static int __init led_init(void)
{
	int retval;

	pr_info(&quot;LED Module is up...\n&quot;);

	retval = alloc_chrdev_region(&amp;amp;dev_num, 0, 1, &quot;LED_Driver&quot;);

	if(retval &amp;lt; 0)
	{
		pr_err(&quot;Failed to allocate device number!\n&quot;);
		return retval;
	}
	cdev_init(&amp;amp;led_cdev, &amp;amp;led_fops);
	led_cdev.owner = THIS_MODULE;

	retval = cdev_add(&amp;amp;led_cdev, dev_num, 1);
	if(retval &amp;lt; 0)
	{
		pr_err(&quot;Failed to add cdev\n&quot;);
		unregister_chrdev_region(dev_num, 1);
		return retval;
	}

	led_class = class_create(THIS_MODULE, &quot;led_class&quot;);
	if(IS_ERR(led_class))
	{
		retval = PTR_ERR(led_device);
		goto err_del_cdev;
	}
	led_class-&amp;gt;devnode = led_devnode;

	led_device = device_create(led_class, NULL, dev_num, NULL, &quot;LED&quot;);
	if(IS_ERR(led_device))
	{
		retval = PTR_ERR(led_device);
		goto err_destroy_class;
	}

	pr_info(&quot;LED Module &amp;amp; /dev/LED setup successfully completed! Major: %d\n&quot;, MAJOR(dev_num));

	gpio_request(GPIO_LED, &quot;LED&quot;);

    return 0;

err_destroy_class:
	class_destroy(led_class);
err_del_cdev:
	cdev_del(&amp;amp;led_cdev);

	return retval;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 좀 기네요. 하지만 아아아아주 간단하게 분석을 해볼까요? 디바이스 드라이버를 올리기 위해서는 메이저/마이너 번호를 할당받아야 합니다. 그 부분이 바로 alloc_chrdev_region() 입니다. 이 함수를 통해 값을 할당받으면, 다음으로는 아까 만들어준 함수들과 디바이스를 연결해주는 작업을 cdev_init()을 통해 진행해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 디바이스를 cdev_add()를 통해 추가해 준 뒤, 클래스를 만들어 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 디바이스를 모두 추가했습니다. 이제 device_create()를 통해 /dev/ 에 디바이스를 추가해주고, gpio_request()로 위에서 본 함수들(gpio_direction_out() 등) 을 사용할 준비를 해 주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exit()은 init()의 역순이므로 설명하지 않겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 파일을 빌드할 makefile을 만들어줍시다.&lt;/p&gt;
&lt;pre id=&quot;code_1781226171476&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;obj-m := led.o
KDIR := /lib/modules/$(shell uname -r)/build

default:
        $(MAKE) ARCH=arm64 -C$(KDIR) M=$(shell pwd) modules

clean:
        $(MAKE) -C$(KDIR) M=$(shell pwd) clean&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 make 명령어 하나만 치면 빌드가 됩니다. 매우 간단하죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;led_app.c는 매우매우 간단합니다. 단순히 켜고 끄는 것을 보는 정도로만 할 예정이니까요.&lt;/p&gt;
&lt;pre id=&quot;code_1781230562185&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;termios.h&amp;gt;

#include &quot;sk_ioctl.h&quot;

#define BUFFER_SIZE 100

int main()
{
	int retn;
	int fd;
	char buffer[BUFFER_SIZE];

	int flag = 0;

	fd = open(&quot;/dev/LED&quot;, O_RDWR);
	printf(&quot;fd = %d\n&quot;, fd);
	if(fd &amp;lt; 0)
	{
		perror(&quot;/dev/LED error&quot;);
		exit(-1);
	}
	else
	{
		printf(&quot;LED has been detected...\n&quot;);
	}

	int choice;

	printf(&quot;1. ON\t2. OFF: &quot;);
	scanf(&quot;%d&quot;, &amp;amp;choice);

	if(choice == 1)
	{
		write(fd, &quot;1&quot;, 1);
	}
	else
	{
		write(fd, &quot;0&quot;, 1);
	}

	close(fd);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하죠? 1이면 키고 그렇지 않으면 LED를 끄는 정도로 제어할 생각입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 실행을 해 볼까요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf0vek/dJMcag6TqFV/uiN3pJMlBjTIzlWZavhmn1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf0vek/dJMcag6TqFV/uiN3pJMlBjTIzlWZavhmn1/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_20260612_111652619.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf0vek/dJMcag6TqFV/uiN3pJMlBjTIzlWZavhmn1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf0vek%2FdJMcag6TqFV%2FuiN3pJMlBjTIzlWZavhmn1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w8p9B/dJMcaaTajgk/cgnBCtK7QJxxtFVgoGJajK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w8p9B/dJMcaaTajgk/cgnBCtK7QJxxtFVgoGJajK/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;KakaoTalk_20260612_111652619_01.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w8p9B/dJMcaaTajgk/cgnBCtK7QJxxtFVgoGJajK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw8p9B%2FdJMcaaTajgk%2FcgnBCtK7QJxxtFVgoGJajK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;LED를 켠 상태(좌), 끈 상태(우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vFrIx/dJMcaftnR3z/A1kTj4C2zh9VixvsoI54kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vFrIx/dJMcaftnR3z/A1kTj4C2zh9VixvsoI54kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vFrIx/dJMcaftnR3z/A1kTj4C2zh9VixvsoI54kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvFrIx%2FdJMcaftnR3z%2FA1kTj4C2zh9VixvsoI54kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1313&quot; height=&quot;359&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시다시피 로그도 잘 작동하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 번에는 카메라를 구할 수 있으면 카메라도 제어해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격이 일단 너무 괜찮은 디바이스라고 생각합니다. GPIO 핀 번호도 큰 무리없이 다루기 좋구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 디바이스마트에서 사는 제품은 믿고 살 수 있으리라 생각이 드네요ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 특화 보드다 보니 다음번에는 CNN이나 YOLO 같은걸 좀 적용해볼 수 있으면 좋겠다는 생각이 듭니다. 한 번 공부해볼 기회가 있으면 좋겠습니다.&lt;/p&gt;</description>
      <category>기타</category>
      <category>AI보드</category>
      <category>RDK-X3</category>
      <category>디바이스마트</category>
      <category>커널프로그래밍</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/52</guid>
      <comments>https://wnghks7787.tistory.com/52#entry52comment</comments>
      <pubDate>Fri, 12 Jun 2026 11:21:54 +0900</pubDate>
    </item>
    <item>
      <title>[우당탕탕 OLED 드라이버 만들기] 3. 클래스 분리</title>
      <link>https://wnghks7787.tistory.com/51</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/df7hJ9/dJMb99TVUER/yqsbBnWpCIESlk7QfNXPK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/df7hJ9/dJMb99TVUER/yqsbBnWpCIESlk7QfNXPK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/df7hJ9/dJMb99TVUER/yqsbBnWpCIESlk7QfNXPK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdf7hJ9%2FdJMb99TVUER%2FyqsbBnWpCIESlk7QfNXPK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;457&quot; height=&quot;257&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 OLED쪽을 건들였습니다. 그동안 할 일이 많았습니다. 심화실습평가도 있었고, 뭐 그랬습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 한 작업은 엄청난 것은 아니지만, 유지보수를 위해서 반드시 필요한 작업이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssd1309_driver.cpp와 ssd1309_driver.h 파일을 만든 뒤, 지난번에 만든 함수들을 넣어주려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssd1309_driver.h의 내용을 보면서 함수가 어떻게 생겼는지 확인해봅시다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;class SSD1309Driver
{
public:
    SSD1309Driver(int fd);
    ~SSD1309Driver();

    void clearDisplay();
    void fullDisplay();

private:
    void initOLED();
    void sendCommand(uint8_t cmd);
    void sendData(uint8_t data);

    int fd;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 fd를 각 함수마다 넣어줬는데, 클래스로 분리하면서 그럴 필요가 없어졌습니다. 생성자에서 fd 값을 받아온 뒤, 계속 사용하면 되니까요. 고민중인 부분은 file descriptor를 생성자에서 생성한 뒤, 나중에 getter를 설정해서 필요하면 메인에서 값을 받아오게 할지, 지금처럼 fd는 바깥에서 생성하도록 할지 입니다. 아직까진 정해지지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;initOLED()&lt;/code&gt; 함수를 통해 그동안 쓰던 값 설정을 다 몰아주었습니다. 이제 외부에서 불러올 때나, 아니면 내부의 다른 함수들도 깔끔하게 설정할 수 있습니다.&lt;br /&gt;&lt;code&gt;sendCommand()&lt;/code&gt;와 &lt;code&gt;sendData()&lt;/code&gt;는 기존과 동일합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init에서 설정한 세팅 값은 너무 길어서 접은글로 올려두겠습니다. 보고 싶은 분들은 확인해보셔도 좋을듯 합니다. 다만, 직접 데이터시트를 보고 하나하나 확인해보는 것도 매우 좋을 것 같네요ㅎㅎ 어디까지나 제가 작성한 것은 정답은 아니니까요!&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;initOLED 확인하기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1781004530527&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void SSD1309Driver::initOLED()
{
	// display off
	sendCommand(0xAE);

	// memory address mode 설정, Horizontal addressing mode
	sendCommand(0x20);
	sendCommand(0x02);

	// GDDRAM page 시작 주소 설정 (page 0)
	sendCommand(0xB0);

	// 메모리 읽는 방향을 결정(위에서 아래로)
	sendCommand(0xC0);

	// 메모리 읽는 방향을 결정(좌에서 우로)
	sendCommand(0xA0);

	// column address의 하위 4비트를 0으로 설정
	// column address의 상위 4비트를 0으로 설정
	sendCommand(0x00);
	sendCommand(0x10);

	// display 시작 라인을 0으로 맞춤
	sendCommand(0x40);

	// Contrast 설정 최대로
	sendCommand(0x81);
	sendCommand(0xFF);

	// 흰 색을 글씨로
	sendCommand(0xA6);

	// 64줄을 사용
	sendCommand(0xA8);
	sendCommand(0x3F);

	// RAM을 기준으로 화면을 출력
	sendCommand(0xA4);

	// 얼마나 row를 밀어서 시작할지 결정: 0칸 밀어서 시작.
	sendCommand(0xD3);
	sendCommand(0x00);

	// 화면 리프레시 속도 설정(기본 설정)
	sendCommand(0xD5);
	sendCommand(0x70);

	// 픽셀 당 전기 충전 시간
	sendCommand(0xD9);
	sendCommand(0x22);

	// COM Pin 모드
	sendCommand(0xDA);
	sendCommand(0x12);

	// 전압 설정 (기본)
	sendCommand(0xD8);
	sendCommand(0x34);

	// display on
	sendCommand(0xAF);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 진짜로 값을 띄워보는 일을 해보고 싶네요... 게다가 최근에 커널에 모듈로 드라이버를 만들어서 붙이는 것을 배웠는데... 그것도 흥미로워 보였습니다. 지금 제가 만든 드라이버는 말은 드라이버지만 사실 라이브러리나 다름없거든요. 언젠간 커널까지 확장할 수 있으면 좋겠습니다.&lt;/p&gt;</description>
      <category>우당탕탕 OLED 드라이버 만들기</category>
      <category>oled</category>
      <category>드라이버</category>
      <category>라즈베리파이</category>
      <category>임베디드</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/51</guid>
      <comments>https://wnghks7787.tistory.com/51#entry51comment</comments>
      <pubDate>Tue, 9 Jun 2026 20:31:24 +0900</pubDate>
    </item>
    <item>
      <title>[한화비전 VEDA 4기] 11주차 리뷰</title>
      <link>https://wnghks7787.tistory.com/50</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blRFec/dJMcahcVzMM/WJ076dAnFJFmpvQh2iEgek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblRFec%2FdJMcahcVzMM%2FWJ076dAnFJFmpvQh2iEgek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;211&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11주차 화요일 ~ 금요일까지 심화실습평가2 가 있었습니다.&lt;br /&gt;심화 실습 평가 2에서, 라즈베리파이를 이용하여 각종 디바이스를 제어하였습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자세한 내용은 제 github에 올려두었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;github 링크:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/wnghks7787/device-management-system.git&quot;&gt;https://github.com/wnghks7787/device-management-system.git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;총 5개의 디바이스를 제어하는 서버/클라이언트 프로그램입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;pwm을 이용하여 LED 제어&lt;/li&gt;
&lt;li&gt;부저&lt;/li&gt;
&lt;li&gt;조도 센서&lt;/li&gt;
&lt;li&gt;7447 IC 칩을 이용하여 Anod FND 제어&lt;/li&gt;
&lt;li&gt;AD/DA 컨버터를 이용하여 온도 측정&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로세스는 데몬화를 하였고, 디바이스 제어는 모두 동적 라이브러리를 사용하였습니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;서버&quot; data-ke-size=&quot;size23&quot;&gt;서버&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서버는 해당 장치들에 대한 처리를 담당합니다. Daemon 프로세스로 제작하여 터미널 실행과 무관하게 실행되도록 제작합니다.&lt;br /&gt;stdin, stdout, stderr 역시 모두 정리합니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;클라이언트&quot; data-ke-size=&quot;size23&quot;&gt;클라이언트&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원하는 실행 내용을 서버에 요청합니다. 위에서 언급한 LED, 부저, 조도센서, 7segment, AD/DA컨버터를 작동하여 각각의 실행을 할 수 있습니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;개발 일정&quot; data-ke-size=&quot;size26&quot;&gt;개발 일정&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;1일차&quot; data-ke-size=&quot;size23&quot;&gt;1일차&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버-클라이언트의 TCP 통신 환경 구축 ✅ 2026-06-02
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 TCP 소켓 생성 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;클라이언트 TCP 소켓 생성 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;서버에서 무한으로 값을 받기 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;클라이언트에서 무한으로 값을 보내기 ✅ 2026-06-02&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Makefile 간단 제작 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;Thread 제어 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;Daemon 프로세스로 제어 ✅ 2026-06-02
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 디스크립터 삭제 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;시그널 핸들링 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;부모 프로세스 종료 ✅ 2026-06-02&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;README.md 관리 시작 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;Client Signal 제어 ✅ 2026-06-02&lt;/li&gt;
&lt;li&gt;개발 문서 초안 작성 ✅ 2026-06-02&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;2일차&quot; data-ke-size=&quot;size23&quot;&gt;2일차&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LED 제어(pwm) ✅ 2026-06-03&lt;/li&gt;
&lt;li&gt;조도 센서 제어 ✅ 2026-06-04&lt;/li&gt;
&lt;li&gt;7 segment 제어 ✅ 2026-06-04&lt;/li&gt;
&lt;li&gt;부저 제어 ✅ 2026-06-03&lt;/li&gt;
&lt;li&gt;Shared Library로 작성 ✅ 2026-06-03&lt;/li&gt;
&lt;li&gt;README.md 기술 ✅ 2026-06-04&lt;/li&gt;
&lt;li&gt;온도 센서 제어 ✅ 2026-06-04&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;3일차&quot; data-ke-size=&quot;size23&quot;&gt;3일차&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;README.md 마무리 ✅ 2026-06-05&lt;/li&gt;
&lt;li&gt;대몬 로그파일 만들기 ✅ 2026-06-05&lt;/li&gt;
&lt;li&gt;개발 문서 정리 ✅ 2026-06-05&lt;/li&gt;
&lt;li&gt;최종 코드 정리 ✅ 2026-06-05&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;세부 구현 내용&quot; data-ke-size=&quot;size26&quot;&gt;세부 구현 내용&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;장치 구현 사항&quot; data-ke-size=&quot;size23&quot;&gt;장치 구현 사항&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 장치는 thread를 이용하여 처리합니다. LED, 부저, 7segment는 모두 detach하여 사용하며, 조도 및 온도 센서는 join으로 값을 받아 처리하도록 되어있습니다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;LED&quot; data-ke-size=&quot;size20&quot;&gt;LED&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LED는 가장 기본적인 장치 중 하나입니다. 해당 프로젝트에서는 LED 밝기를 100%, 75%, 50%, 25%, 0%로 구분합니다.&lt;br /&gt;PWM을 이용하여 LED의 밝기를 제어할 수 있습니다.&lt;br /&gt;클라이언트에서 LED의 밝기를 제어할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;---------- LED Control Mode ----------
Select your mode:
1. LED 100% ON
2. LED 70% ON
3. LED 50% ON
4. LED 25% ON
5. LED OFF
What do you want to see:
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;부저&quot; data-ke-size=&quot;size20&quot;&gt;부저&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트에서 부저 재생을 할 수 있습니다. 단순한 beep 소리가 아닌, 음악 소리가 나오도록 제작합니다. 클라이언트에서 부저를 끌 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;---------- BUZZER Control Mode ----------
Select your mode:
1. BUZZER ON
2. BUZZER OFF
What do you want to see:
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;조도 센서&quot; data-ke-size=&quot;size20&quot;&gt;조도 센서&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트에서 조도 센서의 값을 확인할 수 있습니다. 이 때, 조도 센서는 밝기 값을 파악하여 클라이언트에 돌려줍니다.&lt;br /&gt;LED와 연계하여 만약 밝다면 LED를 끄고, 어둡다면 LED를 켤 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;---------- CDS Control Mode ----------
Select your mode:
1. Check CDS value
2. Control LED with CDS
What do you want to see:
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;7 segment&quot; data-ke-size=&quot;size20&quot;&gt;7 segment&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;디지털 숫자를 표시할 수 있는 장치입니다. 클라이언트에서 요청 시 매 초 9부터 0까지 거꾸로 숫자를 셉니다. 0에 도달하면 동시에 부저를 울립니다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;---------- FND Control Mode ----------
Select Integer Number(0~9):
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;AD/DA 컨버터&quot; data-ke-size=&quot;size20&quot;&gt;AD/DA 컨버터&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 온도를 측정하여 클라이언트에 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;What do you want to see: 5
CURRENT TEMPERATURE: 23 C
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;시그널 처리&quot; data-ke-size=&quot;size23&quot;&gt;시그널 처리&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트는 인터럽트 시그널(SIG_INT) 를 제외한 모든 시그널을 무시하도록 합니다.&lt;br /&gt;서버는 Daemon 프로세스로, 터미널 관련 시그널(SIG_HUP) 를 무시하도록 합니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;동적 라이브러리&quot; data-ke-size=&quot;size23&quot;&gt;동적 라이브러리&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동적 라이브러리의 위치를 daemon 프로세스가 되기 전에 잡아두었습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;lib&lt;span&gt;&amp;nbsp;&lt;/span&gt;폴더 안에 라이브러리를 수정하여 넣으면 서버를 다운하지 않고도 장치 변경 사항을 처리할 수 있습니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;서버 로그 확인&quot; data-ke-size=&quot;size23&quot;&gt;서버 로그 확인&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서버는 daemon 프로세스로 실행되므로 로그를 통해 실행 상태를 확인할 수 있습니다.&lt;br /&gt;journalctl -f -t devicd_control&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 통해 로그를 볼 수 있습니다.&lt;br /&gt;로그에는 클라이언트 연결 여부, 현재 클라이언트에게 입력받은 값 등을 받을 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;로그는 다음과 같이 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;Jun 05 12:26:20 jhBerry device_control[13940]: Daemon Process
Jun 05 12:26:31 jhBerry device_control[13940]: server: got connection from 100.69.83.76
Jun 05 12:26:36 jhBerry device_control[13940]: input: 42
Jun 05 12:26:36 jhBerry device_control[13940]: fnd trhead start: inputNUM=2
Jun 05 12:26:39 jhBerry device_control[13940]: input: 50
Jun 05 12:26:39 jhBerry device_control[13940]: temperature thread start
Jun 05 12:26:39 jhBerry device_control[13940]: temperature thread finish
Jun 05 12:26:39 jhBerry device_control[13940]: send temperature data
Jun 05 12:26:43 jhBerry device_control[13940]: input: 11
Jun 05 12:26:43 jhBerry device_control[13940]: led thread start
Jun 05 12:26:43 jhBerry device_control[13940]: led thread detach
Jun 05 12:26:50 jhBerry device_control[13940]: input: 31
Jun 05 12:26:50 jhBerry device_control[13940]: cds thread start
Jun 05 12:26:50 jhBerry device_control[13940]: cds: bright
Jun 05 12:26:50 jhBerry device_control[13940]: send cds data
Jun 05 12:39:13 jhBerry device_control[13940]: input: 0
Jun 05 12:39:13 jhBerry device_control[13940]: client LOGOUT
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;문제점 및 보완 사항&quot; data-ke-size=&quot;size26&quot;&gt;문제점 및 보완 사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CMake를 사용하여 제작을 완료하고 싶었지만, CMake는 기본적으로 컴파일러를 여러개 지정하는 것이 불편하여 Makefile로 제작하게 되었습니다. 추후 CMake에 대해 더 알아본 뒤, CMake로 빌드 프로세스를 구축하고자 합니다.&lt;/li&gt;
&lt;li&gt;CDS 센서를 AD/DA 컨버터가 아닌 일반 조도 센서를 사용하였습니다. 이에 따라 밝기 값을 밝다/어둡다 로만 판단할 수 있어 컨버터를 통해 바꾸어주도록 보완할 수 있습니다.&lt;/li&gt;
&lt;li&gt;부저가 실행중일 때, 다시 부저를 울리면 부저가 중복되어 실행됩니다.&lt;/li&gt;
&lt;li&gt;다중 클라이언트가 접속할 수 없습니다. 한 번에 한 클라이언트만 접속할 수 있습니다.&lt;/li&gt;
&lt;li&gt;SIG_INT를 통해 클라이언트 종료 시, 로그가 비정상적으로 나타날 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>VEDA</category>
      <category>KDT</category>
      <category>veda</category>
      <category>글로벌소프트웨어아카데미</category>
      <category>내맘대로TIL챌린지</category>
      <category>베다</category>
      <category>임베디드</category>
      <category>임베디드SW아카데미</category>
      <category>한화비전</category>
      <category>한화비전아카데미</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/50</guid>
      <comments>https://wnghks7787.tistory.com/50#entry50comment</comments>
      <pubDate>Sun, 7 Jun 2026 23:44:36 +0900</pubDate>
    </item>
    <item>
      <title>daemon 프로세스 구축 후 상대경로의 동적 라이브러리 불러오기</title>
      <link>https://wnghks7787.tistory.com/49</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;daemon 프로세스는 현재 working directory를 &lt;code&gt;/&lt;/code&gt; 로 잡아 만약 상대경로로 동적라이브러리를 불러온다면 정확한 위치를 잡아내지 못하는 단점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이를 해결하려면 어떻게 해야할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 동적 라이브러리위 위치를 절대경로로 잡으면 되지만, 여러가지 이유로 그렇게 하기 어려울 때가 있습니다. &lt;code&gt;/lib&lt;/code&gt; 나 &lt;code&gt;/usr/lib&lt;/code&gt; 등에 두면 아마 자동으로 라이브러리를 링크할 것 같지만, 상황상 그렇게 하지 못하는 경우도 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 그 해결책을 C언어를 기준으로 설명해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;우선 프로세스를 daemon으로 만들기 전에 해 주어야 한다는 것이 전제로 붙습니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;libgen.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;limits.h&amp;gt;

char lib_path[PATH_MAX];

int getLibDir()
{
    char exe_path[PATH_MAX];

    ssize_t len = readlink(&quot;/proc/self/exe&quot;, exe_path, sizeof(exe_path) - 1);

    if(len != -1)
    {
        exe_path[len] = '\0';

        char* dir = dirname(exe_path);

        // 여기에 원하는 대로 라이브러리의 위치를 지정합니다.
        sprintf(lib_path, &quot;%s/../lib/&quot;, dir);

        return 0;
    }
    else
    {
        return -1;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;readlink()&lt;/code&gt;를 통해 실제로 작동하고 있는 프로세스의 경로를 잡아줍니다.&lt;br /&gt;그 다음, &lt;code&gt;sprintf()&lt;/code&gt;로 경로 뒤에 원하는 대로 상대 경로를 지정해줄 수 있습니다. 이렇게 한다면 손쉽게 경로를 지정할 수 있습니다.&lt;br /&gt;&lt;code&gt;lib_path&lt;/code&gt; 변수에 값을 저장해 둔 뒤, 필요할 때마다 써먹을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 반드시 daemon 프로세스화 하기 전에 이 코드가 먼저 실행되어야 합니다. 그렇지 않으면 어차피 &lt;code&gt;readlink()&lt;/code&gt;를 해봤자 &lt;code&gt;/&lt;/code&gt;만 나오겠죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 데몬 프로세스에서 원하는 위치에 상대경로 라이브러리를 만드는 방법에 대해 알아보았습니다.&lt;br /&gt;질문이나 잘못된 부분이 있다면 댓글로 알려주시면 감사하겠습니다.&lt;/p&gt;</description>
      <category>Computer Science/리눅스 기초</category>
      <category>C</category>
      <category>DAEMON</category>
      <category>동적라이브러리</category>
      <category>리눅스</category>
      <category>프로세스</category>
      <author>전산거북이</author>
      <guid isPermaLink="true">https://wnghks7787.tistory.com/49</guid>
      <comments>https://wnghks7787.tistory.com/49#entry49comment</comments>
      <pubDate>Fri, 5 Jun 2026 01:30:59 +0900</pubDate>
    </item>
  </channel>
</rss>