Bir önceki yazıda Arduino Uno ve Python kullanarak DDC/CI protokolü üzerinden monitör parlaklık ve kontrast kontrolünü iki potansiyometreyle nasıl yapabileceğimizi anlatmıştım. O yazının sonunda birkaç yapılacak madde listelemiştim. Bu yazı o listedeki en kritik iki maddenin hayata geçirilmesini anlatıyor: Arduino'dan bağımsız bir MCU'ya geçiş ve uygulamanın Win32 ile yeniden yazılması.
Neden Arduino değil?
Arduino Uno güzel bir prototipleme platformu, ancak bu proje için fazla büyük ve gereksiz. Bizim ihtiyacımız olan tek şey iki ADC kanalı ve USB iletişimi. Bunun için 28 pinli bir AVR ve ayrı bir USB-Serial çevirici çipi kullanmak israf.
Daha da önemlisi, Arduino Uno USB üzerinden HID olarak görünmüyor — seri port (CDC) üzerinden
haberleşiyor. Bu da Python tarafında pyserial kütüphanesi ve COM port yönetimi
gerektiriyor. Farklı bilgisayarlarda farklı COM port numaraları atanıyor, kullanıcı bunu elle
değiştirmek zorunda kalıyor.
Hedef: cihazı taktığında ek bir kurulum veya konfigürasyon gerektirmeden çalışan, native USB HID olarak görünen, mümkün olduğunca küçük bir devre.
CH552: USB HID yetenekli minimal MCU
WCH firmasının ürettiği CH552, bu iş için biçilmiş kaftan. Enhanced 8051 çekirdeği üzerine kurulu bu MCU'nun öne çıkan özellikleri:
- Dahili USB full-speed transceiver — harici USB-Serial çeviriciye gerek yok
- 4 kanallı 8-bit ADC (P1.1, P1.4, P1.5, P3.2)
- 16KB program ROM, 1KB xRAM
- DIP20 pakette mevcut — breadboard dostu
- Adet fiyatı 0.5-1 dolar civarı
- ch55xduino ile Arduino IDE desteği
Geliştirme için WeAct Studio'nun CH552 Core Board'unu kullandım. USB konektörü, boot ve reset butonu dahil, breadboard uyumlu, tüm pinler erişilebilir.
Bağlantı şeması
Devre son derece sade:
- POT1 (Parlaklık): Orta bacak → P1.1, uçlar → GND / 3.3V
- POT2 (Kontrast): Orta bacak → P1.4, uçlar → GND / 3.3V
- Her iki pot için 10kΩ değer ideal
- USB, hem güç hem veri için kullanılıyor
USB HID descriptor: Vendor HID
Önceki Arduino versiyonunda CDC (seri port) kullanıyorduk. Bu versiyonda cihaz native USB HID olarak görünüyor. Ancak burada önemli bir detay var: standart keyboard veya mouse descriptor kullanırsak Windows cihazı gerçek bir giriş aygıtı olarak tanımlıyor ve klavye girişlerini karıştırabiliyor.
Bunun yerine Vendor Defined HID (Usage Page 0xFF00) kullandım. Bu şekilde Windows cihazı tanıyor ama sistem giriş aygıtı olarak muamele etmiyor. Device Manager'da "HID-compliant device" olarak görünüyor, herhangi bir sürücü kurulumu gerekmiyor.
CH552 ADC: Dikkat edilmesi gereken detaylar
CH552'nin ADC'sini kullanırken birkaç önemli nokta var. Standart Arduino analogRead()
fonksiyonu çalışıyor, ancak kanal seçimi biraz farklı. ch55xduino'da pin numaralandırması
port.pin formatında: P1.1 için pin 11, P1.4 için pin 14.
Öte yandan ADC çıkışı 8-bit olmasına rağmen dahili referans voltajı nedeniyle değerler 0-255 aralığının tamamını kullanmıyor. Pratikte 4-174 arası bir aralık elde ettim. Python tarafında bu değerleri 0-100 aralığına map ediyorum:
ADC_MIN = 4
ADC_MAX = 174
def map_val(raw):
val = (ADC_MAX - raw) * 100 // (ADC_MAX - ADC_MIN)
return max(0, min(100, val))
Firmware: HID veri gönderimi
USB HID üzerinden veri göndermek için ch55xduino'nun HID keyboard örneğinin kaynak dosyalarını
kullandım. Descriptor'ı Vendor HID olarak değiştirdim, veri gönderimi için mevcut
USB_EP1_send() fonksiyonunu ve HIDKey[] buffer'ını kullandım.
Her HID paketi şu şekilde:
HIDKey[0]— Parlaklık (ADC P1.1)HIDKey[1]— Kontrast (ADC P1.4)
Firmware ana döngüsü 50ms aralıklarla çalışıyor:
void loop() {
uint8_t b = adc_read_ch(0); // P1.1
uint8_t c = adc_read_ch(1); // P1.4
digitalWrite(PIN_LED, b > 128 ? HIGH : LOW); // ADC test LED'i
HIDKey[0] = b;
HIDKey[1] = c;
USB_EP1_send();
delay(50);
}
Cihazı flash'lamak için WCHISPTool kullandım. Boot moduna girmek için P3.6 butonuna basılı tutarken USB bağlamak yeterli.
Python daemon: Win32 ve dxva2
Önceki versiyonda Python GUI için tkinter, DDC/CI için monitorcontrol kütüphanesi kullanıyordum. Bu versiyonda her ikisini de değiştirdim.
OSD: tkinter yerine doğrudan Win32 GDI ile layered window. Transparan arka plan, her zaman üstte, tıklanamaz. Sağ alt köşede 2 saniye görünüp kayboluyor.
DDC/CI: monitorcontrol kütüphanesi yerine Windows'un native
dxva2.dll API'si. SetMonitorBrightness ve
SetMonitorContrast fonksiyonları doğrudan çağrılıyor. Bu yöntem hem daha hızlı
hem de monitorcontrol kütüphanesinin yaşadığı "failed to get VCP feature" hatasını tamamen
ortadan kaldırıyor.
dxva2 = ctypes.windll.dxva2
def _ddc_worker():
while True:
brightness, contrast = _ddc_queue.get()
for h in _monitor_handles:
if brightness is not None:
dxva2.SetMonitorBrightness(h, brightness)
if contrast is not None:
time.sleep(0.05)
dxva2.SetMonitorContrast(h, contrast)
DDC/CI komutları bir queue üzerinden tek bir worker thread'e gönderiliyor. Queue kapasitesi 1 — yeni komut geldiğinde işlenmemiş eski komut atılıyor. Bu sayede monitör hiçbir zaman komut yağmuruna tutulmuyor.
Sonuç
Arduino versiyonuyla karşılaştırıldığında bu versiyon birçok açıdan daha iyi:
- COM port konfigürasyonu yok — USB tak, çalış
- Ayrı USB-Serial çevirici yok — tek çip, tek USB kablo
- OSD daha responsive ve görsel olarak daha temiz
- DDC/CI iletişimi daha stabil
- Devre boyutu ve maliyeti önemli ölçüde düştü
Yapılacaklar listesinde hala birkaç madde var: ADC gürültüsü için firmware tarafında smoothing buffer, Python daemon yerine standalone exe, ve nihai hedef olarak özel PCB tasarımı. Önümüzdeki yazılarda bunları ele alacağım.
Projenin kaynak kodlarına yakında GitHub üzerinden ulaşabilirsiniz.
