Contents
[개요]

Thinkpad 10 Tablet의 Docking 단자의 고장으로 Ultrabook Keyboard의 Pogo Pin에 USB Cable을 연장하는 과정을 지난 19년도에 포스팅[1]으로 다뤄보았습니다.

Fig. Thinkpad 10 Ultrabook Keyboard USB 유선으로 DIY 후 결과 사진

 

Thinkpad 10 Ultrabook Docking Keyboard는 작은 사이즈와 만족스러운 타건감 그리고 트랙패드의 존재로 굉장히 매력적인 제품인 데다가 정상 동작하는데 폐기하기에는 아까웠고 다만 지난번에 Tuning을 통해 유선으로 만들어서 사용하였지만 노트북에 USB Port를 하나 점유하는 부분이 아쉬움이 남아 무선 Bluetooth Keyboard로 개조 작업을 진행하게 되었습니다.

 

이번 DIY는 USB to Bluetooth로 변환하는 과정이 핵심이며 AI가 없던 시절에는 엄청나게 많은 시간과 노력을 들여 가능했던 DIY가 현재는 Vibe Coding 방식으로 완성도는 다소 떨어지더라도 원했던 동작이 가능한 수준까지 빠른 시일내에 만들어 볼 수 있었고 이제 그 과정을 남겨보고자 합니다.

 

- 완성된 Bluetooth Keyboard

지난번에 Function Generator를 만들었을 때처럼 우선 결론부터 이야기하면 Keyboard랑 Trackpad 모두 정상적으로 동작합니다.

Fig. Thinkpad 10 Bluetooth Keyboard DIY 후 결과 사진

 

[본문]

1. Hardware Design

1.1 Hardware 구성 설명

Fig. 구성 설명 블록도

 

상기 그림과 같이 Thinkpad Keyboard와 PC(Host)로 직접 연결되는 방식에서 중간에 별도의 Host가 Keyboad의 정보를 가져오고 이를 Bluetooth HID 로 변환하여 PC로 전송하는 기능이 추가되어야 함을 알 수 있습니다.

지금까지 사용해봤던 MCU들은 USB Device는 가능하나 Host 기능은 지원하지 않는데 이번 DIY는 USB Host 기능과 Bluetooth 기능이 요구되었고 이 기능을 모두 가지고 있는 Espressif 社의 IoT용 Microcontroller인 ESP32 Board를 알게되어 사용하게 되었습니다.

 

1.2 ESP32-S3-DevKitC-1 (1st Prototype)

앞서 설명한대로 현재 USB Host 기능과 BLE(Bluetooth) 기능을 모두 지원하는 Espressif Systems 社의 ESP32 Series를 사용하고자 합니다.

구매 진행한 개발보드는 ESP32-S3-DevKitC-1[2]이며 산업용 IoT에 특화된 제품으로 Wi-Fi, Bluetooth 5(LE) 무선 통신 기능이 내장 240Mhz로 동작하는 듀얼 코어 MCU가 적용되었다고 합니다.

Fig. ESP32-S3-DevKitC-1 구성 및 사용 Port 표기

 

기능 구현 단계에서는 동작 확인이 주목적이므로 간단하게 5V를 Keyboard와 ESP32-S3-DevKitC-1 Board에 병렬로 전원을 공급하였습니다.

1.3 Battery 용량 검토

또한 무선으로 사용해야 하므로 Battery 및 Battery 충전 Module 또한 필요하므로 1차적으로 Prototype을 만들어서 대략적인 소비전력과 Keyboard의 외관을 최대한 유지할 수 있도록 배터리의 형태 및 용량을 선정하였습니다.

번외로 nRF52840 + TinyUSB + ZMK로 구성하면 저전력으로 구성할 수 있다고 하나 최대한 적은 시간으로 동작이 가능한 DIY를 하고 싶은 상황이어서 쉬운 길을 택하게 되었습니다.

 상기 영상은 ESP32-S3 DevKitC-1과 Thinkpad Keyboard에 병렬로 연결한 후 Power Supply로 5V를 인가한 후 동작을 확인한 영상[3]이며 Power Supply에 현시된 전류를 기준으로 아래와 같은 결과가 나왔습니다.

Thinkpad 10 Keyboard
단독 동작
ESP32-S3 DevKitC-1 BLE,
Typing 동작
ESP32-S3 DevKitC-1 BLE,
Trackpad 동작
Unit
20 100 110 mA

Thinkpad Keyboard에서 기본적으로 소모하는 전류가 20mA

Bluetooth 기능을 활성화하고 실제 Keyboard 인식 및 Typing 동작시 전류는 약 100mA

Trackpad로 Cursor를 움직이고 Click하고 Two point Scroll 동작시에 최대 전류는 약 110mA

따라서 결론적으로는 Keyboard Typing 동작에는 최대 0.5Wh 가 요구되는 것을 확인했습니다. (5V 기준 약 100mA)

소비 전력이 예상했던 것보다 다소 높다고 판단되어 낮출 수 있는 방안을 생각해보았고 Default로 적용된 160Mhz Clock을 80Mhz로 낮춰보았으나 소비 전류가 줄어들진 않았습니다.

충전 Module은 TP4056 리튬폴리머 배터리 충전 모듀 3.7V 1A 1S을 사용했습니다.

Fig. 최종적용 Battery 정보(DTP951765, 1200mAh)

 

위와 같이 최종적으로 적용된 Battery는 1200mAh 입니다. (상기 Battery 사진 정보 및 구매처 : [4])

DTP951765 (Height : 9.5, Horizontal : 65, Vertical : 17[mm]) 사이즈로 Keyboard의 외관을 손상시키지 않고 적용가능한 기성품중에서 가장 큰 용량이었습니다.

따라서 최대 0.5Wh 소비 전력 기준으로 3.7V를 적용하면 135mA정도가 예상되는데 1200mAh 기준으로 8~9시간 정도 사용이 가능하다고 판단되며 실제 Battery 전압은 초기 완충시 약 4.2V에서 점점 낮아지는 비선형 특성을 가지고 있으므로 상기에 단순 계산으로 도출된 8~9시간 보다는 더 사용이 가능할 것으로 판단됩니다.

 

추가적으로 이후에 소개할 내용의 ESP32-S3 Super Mini로 변경 및 불필요한 Code 최적화 (Debugging을 위해 Serial 통신으로 전송하는 Log Data 부분 삭제) 한 후에 약 20mA 정도 감소되어 최종적으로는 기본동작이 5V / 80mA 정도로 확인되었습니다.

 

1.4 Thinkpad 10 Keyboard 내부 공간 확보(기구 측면)

Fig. Keyboard 내부 공간 확보 관련 사진

이제 동작 확인은 완료된 상태이므로 기존 Thinkpad Keyboard 안에 넣는 작업이 남았습니다.

 

우선 Thinkpad keyboard 에 적용되어있던 Wacom pen holder를 전체적으로 탈거하고 지지대 같은 부분을 전부 잘라내고 거친 부분은 줄로 갈아내서 마감했습니다.

대략적으로 가 조립을 해보았는데 문제는 ESP32-S3-DevKitC-1 Board 사이즈가 너무 커서 공간이 부족해 Housing을 뚫고 확장 작업 및 외부 돌출 형태가 될 수 밖에 없는 상황이었습니다.

 

1.5 ESP32-S3 Super Mini (2nd)

사이즈로 인해 기구부를 어떻게 해야 외관상 최소한의 수정으로 이쁘게 만들수 있을까 고민하던 찰나에 동일한 ESP32-S3 Series 중에 Super Mini 라는 Board가 있는 걸 알게되었습니다.

마침 이번 프로젝트에 필요한 Bluetooth HID와 USB Host 기능은 그대로 적용이 가능했습니다.

다만 CP2102 Driver가 없는 모델이고 Program download와 serial monitor 기능을 이용한 debugger mode로 사용하는 Port가 서로 다릅니다.

따라서 Program download 및 최종적으로 Keyboard로 연결되는 Port는 USB-C Port이고 Serial Monitor로 Debugging을 하기위해서는 UART0 Port (TX, RX) 위치에 FT232 USB to UART Converter를 이용해서 Debugging을 해야 합니다.

Fig. ESP32-S3 Super Mini 구성 및 사용 Port 설명

 

만약 Download Mode 상태에서 Debugger Mode(Serial Monitor)로 연결할 때 장치 관리자의 COM Port가 제대로 인식되지 않고 계속 Reboot이 되는거 같은 느낌이 들때는 아래와 같이 조치합니다.

 

 1. BOOT Switch 누른 상태 유지

 2. RST Switch 한 번 눌렀다 떼기

 3. BOOT Switch 떼기

 4. 위와 같이 조치한 후 Port Number 확인하는 명령어 : python -m serial.tools.list_ports

 

1.6 전체 완성 구성

Fig. 최종 Bluetooth Keyboard 완성 사진

최종 구성은 위 사진과 같으며 집에 보유중인 자재들을 최대한 활용하여 비용을 아껴보고자 DC-Jack, USB OTG까지도 사용하였습니다. ㅎ

Fig. 배터리 충전에 따른 LED 색상

2. Firmware 개발 환경 구성

- IDE : VS Code (Visual Studio Code)

- ESK : ESP-IDF

- Programming Language : Python

 

2.1 USB to UART driver install

ESP32-S3-DevKitC-1 Board를 연결하기 위해서는 CP2102 USB to UART Bridge Driver [5]설치가 필요합니다.

Fig. 장치 관리자 사진

Driver 설치가 완료되면 COM Port가 인식됩니다.

 

2.2 ESP_IDF Install Guide

다음은 VS Code에서 ESP-IDF 를 설치해야 합니다. Extensions Market에서 ESP-IDF를 검색하고 설치 진행하는데 문제는 우측 하단에 뭔가 Pop-up이 여러개 떳었는데 잘 읽어보고 승인해야 할 것이 있었습니다...

중간 과정은 생략되었으나 처음에는 ESP-IDF v6.0.1을 설치했었고 컴파일 시도 과정에서 우여곡절이 있었고 컴파일러 버전문제인지 경로 문제인지 설정 문제(json) 무엇이 문제인지를 확인하기 위해서 다양한 시도 끝에 결론적으로는 v6.0.1은 삭제했고 v5.5.4로 정착했음

  • ESP-IDF의 다른 Version을 설치하는 방법은 다음과 같음
  • VS Code에서 Ctrl + Shift + P 단축키로 명령어 팔레트 창을 띄우고 ESP-IDF : Open ESP-IDF Installation Manager를 누르면 아래와 같은 창이 Pop-Up 됨

 

여기서 New Installation을 누르면 원하는 Version의 ESP-IDF를 선택할 수 있습니다.

ESP-IDF를 설치하면 바탕화면에 아래와 같은 Icon이 생성됨

2.3 Python 기본 명령어

이번에 ESP32를 사용하면서 Python Compile 명령어를 알게된 부분이 있어 핵심만 정리하면 아래와 같습니다.

설명 명령어
(venv)를 탈출  deactivate
Python의 경로를 확인 where.exe python
재부팅 후 ESP-IDF Path 설정 C:\esp\v5.5.4\esp-idf\export.ps1
Build Target 설정 idf.py set-target esp32s3
Build idf.py build
Program Flash Download idf.py -p COM3 flash
COM Port Search idf.py monitor
Program Monitoring idf.py -p COM3 monitor
Monitoring을 멈추는 방법 Ctrl + ]
Project Folder를 Clean 하는 방법 (Build clean) idf.py fullclean

- Program Flash Download 시에는 장치 관리자에서 찾은 Device COM Port Number를 적용해야 합니다.

 

2.4 Example Code Merge로 만든 최종 Test Firmware

결과적으로는 ESP-IDF를 설치하면 Example Code를 제공하며 예를 들면 하기 경로에

HID Host 관련 예제 : C:\esp\v5.5.4\esp-idf\examples\peripherals\usb\host\hid\main

Bluetooth HID 관련 예제 : C:\esp\v5.5.4\esp-idf\examples\bluetooth\bluedroid\ble\ble_hid_device_demo\main

상기 예제들을 조합하고 Code 분석 및 방향성은 GPT를 이용해 Vibe Coding 방식으로 완성하였습니다.

ESP32_R2.6.zip
0.15MB

[결론]

현재 1주일간 노트북에 연동해서 사용해본 결과 처음에 Power Switch를 On 하고 약 2~3초 후부터 정상적으로 동작되며 Keyboard 전원은 켜둔 후 노트북만 다른 장소로 이동한 후 돌아왔을때는 Keyboard가 바로 Bluetooth 연결이 잡히지 않는 문제가 있었습니다. (개선 필요) 다시 Power Switch를 Off, On 해야 정상적으로 동작함을 확인했습니다.

 

하루 평균 12 ~ 12.5시간 정도 사용했었고 중간에 배터리 부족으로 꺼지는 현상은 없었습니다.

 

소비전력이 너무 높아 장시간 사용이 불가한 가장 큰 단점이 존재하지만 Vibe Coding을 통해 짧은 시간을 투자해서 동작 가능한 Bluetooth Keyboard를 얻어 매우 재밌는 diy 였습니다. ㅎ

 

[참고자료]

[1] : https://marasca.tistory.com/8

 

[Repair] Thinkpad 10 Ultrabook keyboard 고장 진단

1. Thinkpad 10 Ultrabook Keyboard 고장 및 A/S 판단 불가2015년 6월 28일에 Lenovo 공식 홈페이지에서 주문했었던 Thinkpad10 과 docking Keyboard가 도착하여 잘 사용하였었다. 그러다 작년 봄쯤 어느날 갑자기 keyboar

marasca.tistory.com

 

[2] :

https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-devkitc-1/index.html

 

ESP32-S3-DevKitC-1 - ESP32-S3 - — esp-dev-kits latest documentation

ESP32-S3-DevKitC-1 [中文] The ESP32-S3-DevKitC-1 is an entry-level development board equipped with ESP32-S3-WROOM-1, ESP32-S3-WROOM-1U, or ESP32-S3-WROOM-2, a general-purpose Wi-Fi + Bluetooth® Low Energy MCU module that integrates complete Wi-Fi and Bl

docs.espressif.com

[3] : https://youtube.com/shorts/13XD8b6mJaU

 

[4] :

https://www.eleparts.co.kr/goods/view?no=11235136

 

리튬폴리머 배터리 DTP951765 3.7V 1200mAh

충전지 3.7V 1200mAh 사이즈 17x65x0.95mm(T)

www.eleparts.co.kr

 

[5] :

https://www.silabs.com/software-and-tools/usb-to-uart-bridge-vcp-drivers?tab=downloads

 

CP210x USB to UART Bridge VCP Drivers - Silicon Labs

The CP210x USB to UART Bridge Virtual COM Port (VCP) drivers are required for device operation as a Virtual COM Port to facilitate host communication with CP210x products. These devices can also interface to a host using the direct access driver.

www.silabs.com

 

[개요]

MCU의 Firmware Design을 진행함에 있어 ADC 기능 구현은 필수적으로 요구되며 이를 테스트하기 위해서 Function Generator를 활용하면 테스트가 매우 용이해집니다.
하지만 취미 활동을 위해 제품으로 구매하기에는 비용이 부담스러워 저렴하게 하나 만들어봤으면 좋겠다고 생각했던 차에 Great Scott!라는 Youtube 채널에서 회로도, BOM, 제작 과정 영상까지 자료를 얻을 수 있었고 저는 그 자료를 기반으로 따라 만들어보고자 합니다. (날먹을 해보고자 합니다. ㅎ)  :D

 

- Great Soctt!이 Design한 Function Generator 관련 자료

Great Scott! Youtube Channel과 [1], Cezar Chirila 가 작성한 글 [2]에서 얻은 회로도와 BOM(Bill of Materials) 정보를 기반으로 부품 구매를 진행했었고 이후 회사 일하기 바쁘다는 핑계로 긴 시간을 손 놓고 있다가 최근 다시 의지가 생겨 제작을 시작하였습니다.

Fig. Great Scott가 배포한 회로도
Fig. Great Scott!가 제작한 Function Generator 전체 구성 사진

위 사진은 Housing 작업 전에 기능 테스트를 진행하는 단계에서 전체적인 구성을 사진으로 설명하는 부분을 발췌해왔습니다.

- 완성된 Function Generator

우선 제가 제작한 Function Generator의 형상부터 공개합니다. 부품의 배치 및 일부 부품의 Maker가 상이할 뿐 거의 동일한 형태입니다.

Fig. 제작된 Funciton Generator 상단 View
Fig. 제작된 Function Generator 하단 View
Fig. Housing을 포함한 전체 완성 사진

 

[본문]

1. Hardware Design

  1.1 Housing 작업

외함의 경우 처음부터 PCB Design 및 부품배치 많은 걸 고려해서 예쁘게 만들면 좋지만... 3D 프린터도 없고... 쉽게 가자는 생각에  튼튼하고 저렴하게 심지어 사이즈도 다양해서 선택의 폭이 넓은 Enclosure를 사용하였습니다.

Fig. 외함으로 사용 할 Enclosure
Fig. Enclosure 사이즈에 맞춘 만능 기판 타공 작업

일반적으로 Enclosure는 내부에 볼트를 체결할 수 있는 홈들이 있는데 심지어 도면도 보통 제조사에서 제공해 줍니다. 그 위치에 맞도록 만능기판을 하나 컷팅해서 수작업으로 홀 가공을 진행하였습니다.

P.S 나중에 다 만들고 나서야 타공을 확인했는데 8개 홀 중에 3개만 맞더군요. ㅋㅋ OMG

  1.2 몇년 전 진행했었던 전원부 부품 배치 작업

Fig. 전원부 부품 배치 작업 후 사진

위 사진은 처음에 Function Generator Youtube 영상을 보고 부품을 구매 후 당시에 전원부까지만 부품을 배치했었던 시점의 사진입니다. 여기까지 작업했을 때가 사진 날짜를 확인해보니 23년이더군요. 당시에 나름 열심히 머리를 굴려 전원 흐름을 고려해 부품을 배치했다고 생각했는데 음... 잘 만든건지... 우선 이어서 다시 작업을 이제야 시작하게 되었습니다. :o

  1.3 입력 전원부 연결 작업 및 전원 파형

여기서 입력 전원을 Connector로 적용해서 탈 부착이 가능하도록 만들기 위해 Enclosure에 구멍을 뚫어야 했는데 이거 이쁘게 뚫기가 좀 많이 힘들더군요..  

Fig. 입력 전원부 변압기 1, 2차측 Connector 단말 처리

입력 전원은 한국 가정용 220[Vrms], 60[Hz] 전원을 사용하였고, Fuse는 Connector안에 삽입이 가능한 구조로 2[A] Fuse로 적용했습니다. 변압기를 통해 2차 측으로 24[Vrms]가 출력됩니다. 

Fig. 전원부 구성도
Fig. 변압기 2차측 전원 파형

무부하 상태에서 변압기 2차측 24[Vrms]를 Oscilloscope로 계측해본 결과 약 26[Vrms]가 계측되었습니다.

음 Spec에는 ±12V (24[Vrms])라고 명시되어 있는데 실제로는 26[Vrms]로 2[Vrms] 정도 크게 계측 되었습니다.

Fig. 정류기 출력단 전압 파형

따라서 정류기 출력 및 평활 Capacitor 측 전압도 위와 같이 ±18[Vdc]로 높게 출력 되었습니다. 그리고 12V Regulator와 5V Regulator 출력 전압 사진은 생략했지만 이 전압들을 통해서 전체 저전압 회로에 전원이 공급됩니다.

  1.4 부품 배치 완성 및 구성 설명

Fig. 전체 부품 배치 및 각 부 구성 설명

최종적으로는 위와 같이 부품 배치가 되었습니다.

  1.5 사용한 부품 BOM

 

(BOM) Function Generator R260207.xlsx
0.02MB

 

음... BOM은 블로그에 표로 넣기가 쉽지 않은 상황이어서 파일로 대체 합니다. :o

단가, 부품명, 구매링크 등에 대한 정보가 기재되어있습니다.

  1.6 회로도

Fig. 전체 구성 회로도

실제로는 2.4 구성과 같이 Connector를 적용한 부분이 다소 있지만 위 회로도에는 Connector가 없이 직결되었다는 가정으로 그렸습니다. Kicad 연습을 해보고 싶어서 한번 시도해본 결과입니다.  

2. Software Design

아두이노를 마지막으로 사용 해본지가 벌써 15년이나 되었네요 :O 
아두이노가 무엇인지는 본 글을 읽어보시는 분이라면 아실 것이라 생각합니다. 제가 현재 참고하고있는 본 프로젝트는 아두이노를 기반으로 설계되어있고 Source Code까지 Open되어있어서 그대로 활용하여 빠른 제작 및 결과를 보고자 시작했던 것이므로 기억이 가물가물한 Arduino 사용을 위해 IDE[3]를 다운로드 및 설치 해주었습니다. :D

Arduino Source Code는 [4]에서 다운로드가 가능합니다.

  2.1 Arduino Debugging 방법

처음에는 모든 부품을 연결하고 전원 투입을 하였는데 LCD도 동작이 안되고 어디서 동작이 막힌건지 알 수가 없어서 난감한 상황이었는데 Device mart에서 제공하는 [5] 영상과 같이 Serial Monitor 기능을 이용하면 Debugging이 가능함을 알게 되었습니다. 
이 기능을 사용해서 아래와 같이 각부의 기능별로 하나하나 확인하기 위해 Digital Input 관련 코드만 추출해서 테스트한 후 I2C LCD 기능만 추출해서 테스트하면서 Step by Step으로 진행해보았습니다.

 

  2.2 Pin D4 Digital Input Test - Source Code

아래와 같이 설정하면 D4 Pin으로 입력된 신호에 따라 Serial Monitor에서 결과를 확인할 수 있습니다.
Button을 누르면 : Low (0), 때면(Ideal) : High (1) 출력

#define DEBUG
//Digital Input Pin Number : 4 Test Setting
unsigned long lastButtonPress; // Value used by button debounce
unsigned char lastButtonState;
unsigned char button;
const int buttonPin = 4;
void setup() 
{
  // put your setup code here, to run once:
  
  // Initialise pin as input with pull-up enabled and debounce variable for
  // encoder button

  pinMode(4, INPUT_PULLUP);
  lastButtonPress = millis();

#ifdef DEBUG
  Serial.begin(9600);
  while(!Serial) {}
  Serial.println("Initialize Complete");  
#endif
}

void loop()
{
  // put your main code here, to run repeatedly:
  // Check to see if the button has been pressed
  checkButton();

  #ifdef DEBUG

  #endif
}

  

// Function to debounce the button

// 0 = pressed, 1 = depressed, 2 = long press
void checkButton() 
{
  if ((millis() - lastButtonPress) > 100) 
  {
    if (digitalRead(buttonPin) != lastButtonState) 
    {
      button = digitalRead(buttonPin);
      lastButtonState = button;
      lastButtonPress = millis();
      
      Serial.print("Button Value : ");
      Serial.println(button);
    }
  }
}

 

Fig. D4 Push Button Switch Test

위와 같이 D4 Pin에 연결된 Rotary Encoder의 Button을 누르고 때면 Serial Monitor 창에서 값이 출력되어 동작 확인 유무를 직관적으로 판단이 가능합니다.

  2.3 Pin D2 & D3 Digital Input Test - Source Code

아래와 같이 설정하면 Rotary Encoder를 시계 방향, 반시계 방향으로 회전시켰을 때 Serial Monitor에서 결과를 확인할 수 있습니다. 시계 방향 회전시 : CW, 반시계 방향 회전시 : CCW

#include "Rotary.h"

#define DEBUG

//Digital Input Pin Number : 2, 3 (A, B Pulse Setting)

unsigned char menuState = 0;
Rotary encoder(2, 3);// Initialise the encoder on pins 2 and 3 (interrupt pins)
volatile unsigned char cursorPos = 0;
unsigned long frequency0 = 1000;
unsigned long frequency1 = 1000;
unsigned long frequency = frequency0;
unsigned long newFrequency = 1000;
int digitPos = 0;
volatile bool updateDisplay = false;
int freqRegister = 0; // Default FREQ register is 0
const unsigned long maxFrequency = 14000000;
unsigned char currentMode = 0;

void setup()
{
  //put your setup code here, to run once:

  // Set pins A and B from encoder as interrupts
  attachInterrupt(digitalPinToInterrupt(2), encChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(3), encChange, CHANGE);

#ifdef DEBUG
  Serial.begin(9600);
  while(!Serial) {}
  Serial.println("Initialize Complete");
#endif
}

void loop()
{
  //put your main code here, to run repeatedly:
  //Check to see if the button has been pressed

  #ifdef DEBUG

  #endif
}

unsigned char state = 0;

void encChange()
{
  // Depending in which menu state you are
  // the encoder will either change the value of a setting:
  //-+ frequency, change between FREQ0 and FREQ1 register (or -+ phase), On/Off, mode
  // or it will change the cursor position
  // unsigned char state = encoder.process();
  state = encoder.process();
  // Serial.print("State Value : ");
  // Serial.println(state);
  // Direction clockwise
  if (state == DIR_CW) 
  {
    Serial.println("State Value : CW");
    switch (menuState) 
    {
      case 1: 
      {
          if (cursorPos == 3)
            cursorPos = 0;
          else
            cursorPos++;
        } break;
      case 2: 
      {
          // Here we initialise two variables.
          // newFrequency is the value of the frequency after we increment it
          // dispDigit is the digit that we are currently modifing, and we obtain it
          // by a neat little trick, using operator % in conjunction with division
          // We then compare these variables with the maximum value for our
          // frequency, if all is good, make the change
          unsigned long newFrequency = frequency + power(10, digitPos);
          unsigned char dispDigit =
            frequency % power(10, digitPos + 1) / power(10, digitPos);
          if (newFrequency <= maxFrequency && dispDigit < 9) 
          {
            frequency += power(10, digitPos);
            updateDisplay = true;
          }
          if (freqRegister == 0) 
          {
            frequency0 = frequency;
          } else if (freqRegister == 1) 
          {
            frequency1 = frequency;
          }
        } break;
      case 4: {

          // if usePhase has been defined, changes in the encoder will vary the phase
          // value (upto 4096)
          // A better implementation would be to use increment of pi/4 or submultiples of
          // pi where 2pi = 4096
#ifdef usePhase
          unsigned long newPhase = phase + power(10, digitPos);
          unsigned char dispDigit =
            phase % power(10, digitPos + 1) / power(10, digitPos);
          if (newPhase < maxPhase && dispDigit < 9) 
          {
            phase += power(10, digitPos);
            updateDisplay = true;
          }
#endif
        } break;
      case 5: 
      {
          if (currentMode == 2)
            currentMode = 0;
          else
            currentMode++;
          updateDisplay = true;
        } break;
    }
  }
  // Direction counter clockwise
  else if (state == DIR_CCW) {
   Serial.println("State Value : CCW");
    switch (menuState) 
    {
      case 1: 
      {
          if (cursorPos == 0)
            cursorPos = 3;
          else
            cursorPos--;
        } break;
      case 2: 
      {
          unsigned long newFrequency = frequency + power(10, digitPos);
          unsigned char dispDigit =
            frequency % power(10, digitPos + 1) / power(10, digitPos);
          if (newFrequency > 0 && dispDigit > 0) 
          {
            frequency -= power(10, digitPos);
            updateDisplay = true;
          }
          if (freqRegister == 0) 
          {
            frequency0 = frequency;
          } else if (freqRegister == 1) 
          {
            frequency1 = frequency;
          }
        } break;
      case 4: 
      {
          // if usePhase has been defined, changes in the encoder will vary the phase
          // value (upto 4096)
          // A better implementation would be to use increment of pi/4 or submultiples of
          // pi where 2pi = 4096
#ifdef usePhase
          unsigned long newPhase = phase + power(10, digitPos);
          unsigned char dispDigit\ =
            phase % power(10, digitPos + 1) / power(10, digitPos);
          if (newPhase > 0 && dispDigit > 0) 
          {
            phase -= power(10, digitPos);
            updateDisplay = true;
          }
#endif
        } break;
      case 5: 
      {
          if (currentMode == 0)
            currentMode = 2;
          else
            currentMode--;
          updateDisplay = true;
        } break;
    }
  }
}
unsigned long power(int a, int b)
{
  unsigned long res;
  if (b == 0) 
  {
    res = 1;
  } else 
  {
    res = a;
    for (int i = 0; i < (b - 1); i++) 
    {
      res *= a;
    }
  }
  return res;
}

 

 

Fig. Rotary Encoder 회전방향 확인 Test

Rotary Encoder는 A, B Signal Line으로 Pulse 형태의 전압이 D2, D3 Pin으로 발생하는데 이를 이용해서 Rotary Encoder의 회전 방향을 알 수 있습니다.

정방향 회전시 Serial Monitor에서 CW 출력, 역방향 회전시 CCW가 출력됩니다.

  2.4 I2C LCD 기능 확인

다음은 LCD 동작을 확인하는 단계인데 이건 너무 간단히 해결되어 버려 당황스럽군요. :o

우선 LCD Board에 Connector로 체결하기위해 작업한 Crimp가 잘못 작업되어 전원 라인이 단선되는 문제가있어 수정했고 두번째 문제는 LCD Board 뒷편에 Backlight 밝기를 조절하는 가변 저항이 있었는데 처음에는 최대 밝기로 되어있었고 그걸로 밝기를 낮추니까 글씨가 보이더군요. -_-;;

3. AD9833 출력 파형

AD9833은 Sine Wave, Triangle Wave, Square Wave 출력이 가능합니다.

  3.1 Sine Waveform

아래는 1[kHz], 100[kHz] 출력 지령시 결과 파형

Fig. Sine Waveform

 

Sine Waveform은 위 파형과 같이 Vpp 11[V] = 7.9[Vrms] 까지 출력이 가능함을 확인했고 10[kHz] 이상부터는 Amplitude가 감쇠되는 것을 확인하였습니다. 고주파수로 갈 수록 Impedance가 커져 그런것으로 추측됩니다.

  3.2 Triangle Waveform

아래는 10[Hz], 10[kHz] 출력 지령시 결과 파형

Fig. Triangle Waveform

Triangle Waveform은 약 1[kHz] 이하에서는 완전한 삼각파였으나 1[kHz] 초과부터 삼각형 꼭짓점이 뭉그러지는 것을 확인할 수 있었습니다.

  3.3 Square Waveform

아래는 1[kHz] 출력 지령시 결과 파형

Fig. Square Waveform

Square Waveform은 1[kHz] 출력으로 설정했는데 실제로는 절반인 약 500[Hz]로 출력이 되더군요... 흠..

4. 결론

현재 제작 진행에 복붙하여 사용한 Source Code에는 약간의 Bug가 존재하더군요. 예를 들면 너무 빠르게 Rotary Encoder를 CCW로 돌렸을때 Counter가 씹히는 등의 오동작이 발생하는 경우가 있습니다.

그 외에 Menu tree가 직관적이고 쉽게 되어있기는 하지만 출력 주파수 설정시 1의 자리부터  1,000,000의 자리까지 숫자를 하나씩 설정 해줘야 하고 중간에 취소나 뒤로가기 같은 기능이 없어 편의성 측면에서는 개선 할만한 부분이 있더군요.  :D 

그리고 이건 Scope의 문제인지 아니면 Clock이 문제인지는 모르겠지만 출력 주파수의 오차 성분 위 파형과 같이 존재 했습니다.  

우선 오래전에 Function Generator를 하나 갖고 싶어서 오랫만에 납땜하면서 재밌게 제작을 완료 해보았습니다. 원래 Enclosure에 Hole을 더 가공해서 Rotary Encoder나 Variable Resistor, LCD 등을 이쁘게 배치해야 하지만 우선 동작을 한번봐서 그런지...  다른 것을 또 만들어보고 싶어 일단 함수 발생기 제작기는 여기서 일단락하고 향후 여유가 생기면 Arduino가 아닌 다른 MCU로 변경 및 LCD Menutree 및 Bug 개선 등을 해보고자 합니다. :)

 

[참고자료]

[1] : https://www.youtube.com/watch?v=Y1KE8eAC9Bk

 

[2] : https://www.allaboutcircuits.com/projects/how-to-DIY-waveform-generator-analog-devices-ad9833-ATmega328p/

 

How to Build Your Own Function Generator Using Analog Devices’ AD9833 - Projects

Tired of using your PC or mobile phone as a signal generator? It’s time to upgrade to a standalone one by making it yourself.

www.allaboutcircuits.com

 

[3] : https://www.arduino.cc/en/software/

 

https://www.arduino.cc/en/software/

 

www.arduino.cc

 

[4] : https://www.instructables.com/DIY-FunctionWaveform-Generator/

 

DIY Function/Waveform Generator

DIY Function/Waveform Generator: In this project we will have a short look at commercial function/waveform generators in order to determine what features are important for a DIY version. Afterwards I will then show you how to create a simple function gener

www.instructables.com

 

[5] : https://www.youtube.com/watch?v=gBOk1fSZUIs

 

 

[개요]

요즘은 공부할때 태블릿을 많이 사용하는 편인데 태블릿의 최대 단점은 예를 들어 10페이지에서 50페이지로 왔다 갔다 이동하고 싶은 상황에서는 많이 번거롭더군요. :(

이번 포스트는 TeX 문법이 손에 익을겸 개인적으로 자주 열어볼만한것을 메모 해보고자 합니다.

[본문]

- LaTeX Grammer Table

기능 문법 결과
제곱근 \sqrt{2} $$\sqrt{2}$$
분수 \frac{분자}{분모} $$\frac{분자}{분모}$$
윗첨자 2^{0} $$2^{0}$$
아랫첨자 2_{0} $$2_{0}$$
미분 f^{\prime}(t) $$f^{\prime}(t)$$
무한대 \infty $$\infty$$
적분 \int_{0}^{\infty}f(t) $$\int_{0}^{\infty}f(t)dt$$
덧셈과 뺄셈 \pm, \mp $$\pm, \mp$$
곱셈 (곱셈 도트) \times, \cdot $$\times, \cdot$$
동치 관계 (거의 같음) \sim, \simeq, \cong, \propto, \approx $$\sim, \simeq, \cong, \propto, \approx$$
부등호 (작거나 같음) \le, \leqq $$\le, \leqq$$
부등호 (크거나 같음) \ge, \geqq $$\ge, \geqq$$
화살표 (오른쪽) \rightarrow, \Rightarrow $$\rightarrow, \Rightarrow$$
화살표 (왼쪽) \leftarrow, \Leftarrow $$\leftarrow, \Leftarrow$$
라플라스 연산자 \mathscr{L} $$\mathscr{L}$$

- 상기 표 외 상세한 TeX 문법은 위키백과 참조[1]

- Laplace Transform Equation Table

함수이름 시간함수 $f(t), t\ge0$ 라플라스 변환 함수 $F(s)$
임펄스 함수 $$\delta(t)$$ $$1$$
계단 함수 $$U(t)$$ $$\frac{1}{s}$$
경사 함수 $$t$$, 1차 함수 $$\frac{1}{s^{2}}$$
$$t^{n}$$, 일반화된 경사함수 $$\frac{n!}{s^{n+1}}$$
지수 함수 $$e^{-at}$$ $$\frac{1}{s+a}$$
$$t\cdot e^{-at}$$ $$\frac{1}{(s+a)^{2}}$$
$$t^{n}\cdot e^{-at}$$ $$\frac{n!}{(s+a)^{n+1}}$$
삼각 함수 $$sin\omega t$$ $$\frac{\omega}{s^{2}+\omega^{2}}$$
$$cos\omega t$$ $$\frac{s}{s^{2}+\omega^{2}}$$
지수가중 삼각함수 $$e^{-at}\cdot sin\omega t$$ $$\frac{\omega}{(s+a)^{2}+\omega^{2}}$$
$$e^{-at}\cdot cos\omega t$$ $$\frac{(s+a)}{(s+a)^{2}+\omega^{2}}$$
미분 정리 $$f^{\prime}(t)$$ $$sF(s)-f(0)$$
$$f^{\prime\prime}(t)$$ $$s^{2}F(s)-sf(0)-f^{\prime}(0)$$
적분 정리 $$\int_{0}^{\infty}f(\tau)\, d\tau$$ $$\frac{1}{s}F(s)$$
$$\int_{0}^{t1}\int_{0}^{t2}f(\tau)\, d\tau$$ $$\frac{1}{s^{2}}F(s)$$

 

[Reference]

 

[1] https://ko.wikipedia.org/wiki/%EB%8F%84%EC%9B%80%EB%A7%90:TeX_%EB%AC%B8%EB%B2%95

 

+ Recent posts