차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판이전 판
comfilepi:piezo_buzzer:index [2026/07/13 07:58] mfranklincomfilepi:piezo_buzzer:index [2026/07/13 08:13] (현재) mfranklin
줄 1: 줄 1:
-``` 
 ====== 전자기식 부저 제어 ====== ====== 전자기식 부저 제어 ======
  
-모든 ComfilePi 패널 PC에는 전자기식 부저가 내장되어 있습니다. 기본 설정에서는 화면을 터치할 때 부저음이 울립니다.+모든 ComfilePi 패널 PC에는 전자기식 부저가 내장되어 있습니다. 기본적으로 화면을 터치하면 부저음이 울립니다.
  
 ===== 터치 부저음 활성화/비활성화 ===== ===== 터치 부저음 활성화/비활성화 =====
줄 25: 줄 24:
 CPi-G/J 패널 PC에는 SoC의 PWM 중 하나에 연결된 전기기계식 부저가 장착되어 있습니다. CPi-G/J 패널 PC에는 SoC의 PWM 중 하나에 연결된 전기기계식 부저가 장착되어 있습니다.
  
-특정 주파수와 재생 시간으로 부저음을 출력하려면 [[https://manpages.debian.org/trixie/beep/beep.1.en.html|beep]] 프로그램을 사용하십시오. 예: ''beep -f 2000 -l 100''+특정 주파수와 지속 시간으로 부저음을 출력하려면 [[https://manpages.debian.org/trixie/beep/beep.1.en.html|beep]] 프로그램을 사용하십시오. 예: ''beep -f 2000 -l 100''
  
 ==== CPi-A/B/C/S ==== ==== CPi-A/B/C/S ====
줄 39: 줄 38:
  
 ===== 전자기식 부저 프로그래밍 ===== ===== 전자기식 부저 프로그래밍 =====
 +
  
 ==== CPi-G/J ==== ==== CPi-G/J ====
줄 120: 줄 120:
         }         }
     }     }
 +
     // Linux 입력 이벤트 상수     // Linux 입력 이벤트 상수
     const ushort EV_SYN = 0x00;     const ushort EV_SYN = 0x00;
줄 149: 줄 149:
     }     }
  
-    // 반복적인 리플렉션성 비용을 방지하기 위해 한 번만 계산+    // 반복적인 계산 비용을 방지하기 위해 한 번만 계산
     static readonly int InputEventSize = Marshal.SizeOf<InputEvent>();     static readonly int InputEventSize = Marshal.SizeOf<InputEvent>();
  
     static void SendTone(FileStream fs, int hz)     static void SendTone(FileStream fs, int hz)
     {     {
-        // hz > 0: 지정된 주파수의 소리 출력+        // hz > 0: 지정된 주파수로 소리 출력
         // hz == 0: 소리 출력 중지         // hz == 0: 소리 출력 중지
         WriteEvent(fs, EV_SND, SND_TONE, hz);         WriteEvent(fs, EV_SND, SND_TONE, hz);
줄 178: 줄 178:
         MemoryMarshal.Write(buffer, in ev);         MemoryMarshal.Write(buffer, in ev);
  
-        // 추가 할당 없이 기록+        // 추가 메모리 할당 없이 기록
         fs.Write(buffer);         fs.Write(buffer);
     }     }
 } }
  
 +</code>
 +
 +=== Python 예제 ===
 +
 +<code Python>
 +#!/usr/bin/env python3
 +
 +import ctypes
 +import math
 +import os
 +import signal
 +import sys
 +import time
 +
 +
 +EVENT_PATH = "/dev/input/by-path/platform-buzzer-event"
 +
 +# Linux 입력 이벤트 상수
 +EV_SYN = 0x00
 +EV_SND = 0x12
 +
 +SYN_REPORT = 0x00
 +SND_TONE = 0x02
 +
 +exiting = False
 +
 +
 +# struct timeval {
 +#     long tv_sec;
 +#     long tv_usec;
 +# };
 +class TimeVal(ctypes.Structure):
 +    _fields_ = [
 +        ("tv_sec", ctypes.c_long),
 +        ("tv_usec", ctypes.c_long),
 +    ]
 +
 +
 +# struct input_event {
 +#     struct timeval time;
 +#     __u16 type;
 +#     __u16 code;
 +#     __s32 value;
 +# };
 +class InputEvent(ctypes.Structure):
 +    _fields_ = [
 +        ("time", TimeVal),
 +        ("type", ctypes.c_uint16),
 +        ("code", ctypes.c_uint16),
 +        ("value", ctypes.c_int32),
 +    ]
 +
 +
 +def handle_sigint(signum, frame):
 +    global exiting
 +
 +    exiting = True
 +    print("\nCtrl+C가 입력되었습니다. 사이렌을 중지합니다...")
 +
 +
 +def write_all(fd: int, data: bytes) -> None:
 +    """부분 쓰기 작업을 처리하면서 전체 버퍼를 기록합니다."""
 +    view = memoryview(data)
 +
 +    while view:
 +        written = os.write(fd, view)
 +
 +        if written == 0:
 +            raise OSError("부저 장치에 데이터를 기록할 수 없습니다.")
 +
 +        view = view[written:]
 +
 +
 +def write_event(fd: int, event_type: int, code: int, value: int) -> None:
 +    event = InputEvent()
 +
 +    # 타임스탬프가 0이어도 무방하며, 커널이 타이밍을 처리합니다.
 +    event.time.tv_sec = 0
 +    event.time.tv_usec = 0
 +    event.type = event_type
 +    event.code = code
 +    event.value = value
 +
 +    write_all(fd, bytes(event))
 +
 +
 +def send_tone(fd: int, hz: int) -> None:
 +    # hz > 0: 요청한 주파수로 소리 출력
 +    # hz == 0: 소리 출력 중지
 +    write_event(fd, EV_SND, SND_TONE, hz)
 +    write_event(fd, EV_SYN, SYN_REPORT, 0)
 +
 +
 +def main() -> int:
 +    global exiting
 +
 +    signal.signal(signal.SIGINT, handle_sigint)
 +
 +    print("사이렌 발생기가 실행 중입니다. 중지하려면 Ctrl+C를 누르십시오.")
 +
 +    elapsed = 0.0
 +    step = 0.02  # 50Hz 갱신 주기: 20밀리초
 +    fd = None
 +
 +    try:
 +        fd = os.open(EVENT_PATH, os.O_WRONLY)
 +
 +        while not exiting:
 +            # 2초마다 500Hz에서 1500Hz까지 상승한 후 다시 하강합니다.
 +            cycle_time = elapsed % 2.0
 +
 +            if cycle_time < 1.0:
 +                frequency = 500.0 + cycle_time * 1000.0
 +            else:
 +                frequency = 1500.0 - (cycle_time - 1.0) * 1000.0
 +
 +            # 정확히 .5인 값에서 Python의 round() 동작은 C#과 다릅니다.
 +            # 여기에서는 일반적으로 주파수가 20Hz 단위로 변하므로
 +            # 이 차이는 중요하지 않습니다.
 +            hz = math.floor(frequency + 0.5)
 +
 +            send_tone(fd, hz)
 +
 +            time.sleep(step)
 +            elapsed += step
 +
 +        return 0
 +
 +    except OSError as error:
 +        print(error, file=sys.stderr)
 +        return 1
 +
 +    finally:
 +        if fd is not None:
 +            try:
 +                send_tone(fd, 0)
 +            except OSError as error:
 +                print(f"부저음을 중지할 수 없습니다: {error}", file=sys.stderr)
 +            finally:
 +                os.close(fd)
 +
 +        print("사이렌이 중지되었습니다.")
 +
 +
 +if __name__ == "__main__":
 +    raise SystemExit(main())
 </code> </code>
  
 ==== CPi-A/B/C/S ==== ==== CPi-A/B/C/S ====
 +
  
 전자기식 부저는 CPi-A/B/S 패널 PC에서는 GPIO 핀 30에 연결되어 있으며, CPi-C 패널 PC에서는 GPIO 핀 27에 연결되어 있습니다. [[https://abyz.co.uk/rpi/pigpio/|pigpio 라이브러리]]를 사용하여 제어할 수 있습니다. 전자기식 부저는 CPi-A/B/S 패널 PC에서는 GPIO 핀 30에 연결되어 있으며, CPi-C 패널 PC에서는 GPIO 핀 27에 연결되어 있습니다. [[https://abyz.co.uk/rpi/pigpio/|pigpio 라이브러리]]를 사용하여 제어할 수 있습니다.
  
 다음 예제는 프로그래밍 언어를 사용하여 부저음을 출력하는 방법을 보여줍니다. 다음 예제는 프로그래밍 언어를 사용하여 부저음을 출력하는 방법을 보여줍니다.
 +
 +=== C# 예제 ===
 +
 +<code C#>
 +using System;
 +using System.Runtime.InteropServices;
 +using System.Threading;
 +
 +internal static class Program
 +{
 +    private const uint Pin = 30; // CPi-A/B/S
 +    // private const uint Pin = 27; // CPi-C
 +
 +    private static void Main()
 +    {
 +        // NULL을 전달하면 기본 호스트와 포트를 사용합니다.
 +        int pi = Pigpio.pigpio_start(IntPtr.Zero, IntPtr.Zero);
 +
 +        if (pi < 0)
 +        {
 +            Console.Error.WriteLine(
 +                $"pigpio 데몬에 연결할 수 없습니다. 오류: {pi}");
 +            Environment.ExitCode = 1;
 +            return;
 +        }
 +
 +        try
 +        {
 +            // pigpio가 사용 가능한 가장 가까운 PWM 주파수를 선택합니다.
 +            int actualFrequency = Pigpio.set_PWM_frequency(pi, Pin, 2700);
 +
 +            if (actualFrequency < 0)
 +                throw new InvalidOperationException(
 +                    $"PWM 주파수를 설정할 수 없습니다. 오류: {actualFrequency}");
 +
 +            Console.WriteLine(
 +                $"요청한 주파수: 2700Hz, 실제 주파수: {actualFrequency}Hz");
 +
 +            // 128/255는 약 50% 듀티비입니다.
 +            CheckResult(
 +                Pigpio.set_PWM_dutycycle(pi, Pin, 128),
 +                "부저음을 시작할 수 없습니다.");
 +
 +            // 100밀리초 동안 부저음을 출력합니다.
 +            Thread.Sleep(100);
 +        }
 +        finally
 +        {
 +            // 연결을 종료하기 전에 항상 부저음을 중지합니다.
 +            int result = Pigpio.set_PWM_dutycycle(pi, Pin, 0);
 +
 +            if (result < 0)
 +                Console.Error.WriteLine(
 +                    $"부저음을 중지할 수 없습니다. 오류: {result}");
 +
 +            Pigpio.pigpio_stop(pi);
 +        }
 +    }
 +
 +    private static void CheckResult(int result, string message)
 +    {
 +        if (result < 0)
 +            throw new InvalidOperationException($"{message} 오류: {result}");
 +    }
 +}
 +
 +internal static class Pigpio
 +{
 +    private const string LibraryName = "libpigpiod_if2.so.1";
 +
 +    [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
 +    internal static extern int pigpio_start(
 +        IntPtr address,
 +        IntPtr port);
 +
 +    [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
 +    internal static extern void pigpio_stop(int pi);
 +
 +    [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
 +    internal static extern int set_PWM_frequency(
 +        int pi,
 +        uint gpio,
 +        uint frequency);
 +
 +    [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
 +    internal static extern int set_PWM_dutycycle(
 +        int pi,
 +        uint gpio,
 +        uint dutycycle);
 +}
 +</code>
 +
  
 === Python 예제 === === Python 예제 ===
줄 205: 줄 444:
  
 # 샘플링 속도의 제한으로 인해 실제로는 2700Hz를 # 샘플링 속도의 제한으로 인해 실제로는 2700Hz를
-# 정확히 출력할 수 없지만, 가능한 가장 가까운 주파수로 설정됩니다.+# 정확하게 출력할 수 없지만, 가능한 가장 가까운 주파수를 사용합니다.
 pi.set_PWM_frequency(PIN, 2700) pi.set_PWM_frequency(PIN, 2700)
  
줄 222: 줄 461:
  
 [[comfilepi:index|ComfilePi - 산업용 Raspberry Pi 패널 PC]] [[comfilepi:index|ComfilePi - 산업용 Raspberry Pi 패널 PC]]
-```