차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
| 양쪽 이전 판이전 판 | |||
| comfilepi:piezo_buzzer:index [2026/07/13 07:58] – mfranklin | comfilepi: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 중 하나에 연결된 전기기계식 부저가 장착되어 있습니다. | ||
| - | 특정 주파수와 | + | 특정 주파수와 |
| ==== 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< | static readonly int InputEventSize = Marshal.SizeOf< | ||
| static void SendTone(FileStream fs, int hz) | static void SendTone(FileStream fs, int hz) | ||
| { | { | ||
| - | // hz > 0: 지정된 주파수의 소리 출력 | + | // hz > 0: 지정된 주파수로 소리 출력 |
| // hz == 0: 소리 출력 중지 | // hz == 0: 소리 출력 중지 | ||
| WriteEvent(fs, | WriteEvent(fs, | ||
| 줄 178: | 줄 178: | ||
| MemoryMarshal.Write(buffer, | MemoryMarshal.Write(buffer, | ||
| - | // 추가 할당 없이 기록 | + | // 추가 |
| fs.Write(buffer); | fs.Write(buffer); | ||
| } | } | ||
| } | } | ||
| + | </ | ||
| + | |||
| + | === Python 예제 === | ||
| + | |||
| + | <code Python> | ||
| + | # | ||
| + | |||
| + | import ctypes | ||
| + | import math | ||
| + | import os | ||
| + | import signal | ||
| + | import sys | ||
| + | import time | ||
| + | |||
| + | |||
| + | EVENT_PATH = "/ | ||
| + | |||
| + | # 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_ = [ | ||
| + | (" | ||
| + | (" | ||
| + | ] | ||
| + | |||
| + | |||
| + | # struct input_event { | ||
| + | # | ||
| + | # __u16 type; | ||
| + | # __u16 code; | ||
| + | # __s32 value; | ||
| + | # }; | ||
| + | class InputEvent(ctypes.Structure): | ||
| + | _fields_ = [ | ||
| + | (" | ||
| + | (" | ||
| + | (" | ||
| + | (" | ||
| + | ] | ||
| + | |||
| + | |||
| + | def handle_sigint(signum, | ||
| + | global exiting | ||
| + | |||
| + | exiting = True | ||
| + | print(" | ||
| + | |||
| + | |||
| + | def write_all(fd: | ||
| + | """ | ||
| + | view = memoryview(data) | ||
| + | |||
| + | while view: | ||
| + | written = os.write(fd, | ||
| + | |||
| + | if written == 0: | ||
| + | raise OSError(" | ||
| + | |||
| + | view = view[written: | ||
| + | |||
| + | |||
| + | def write_event(fd: | ||
| + | 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, | ||
| + | |||
| + | |||
| + | def send_tone(fd: | ||
| + | # hz > 0: 요청한 주파수로 소리 출력 | ||
| + | # hz == 0: 소리 출력 중지 | ||
| + | write_event(fd, | ||
| + | write_event(fd, | ||
| + | |||
| + | |||
| + | def main() -> int: | ||
| + | global exiting | ||
| + | |||
| + | signal.signal(signal.SIGINT, | ||
| + | |||
| + | print(" | ||
| + | |||
| + | elapsed = 0.0 | ||
| + | step = 0.02 # 50Hz 갱신 주기: 20밀리초 | ||
| + | fd = None | ||
| + | |||
| + | try: | ||
| + | fd = os.open(EVENT_PATH, | ||
| + | |||
| + | 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, | ||
| + | |||
| + | time.sleep(step) | ||
| + | elapsed += step | ||
| + | |||
| + | return 0 | ||
| + | |||
| + | except OSError as error: | ||
| + | print(error, | ||
| + | return 1 | ||
| + | |||
| + | finally: | ||
| + | if fd is not None: | ||
| + | try: | ||
| + | send_tone(fd, | ||
| + | except OSError as error: | ||
| + | print(f" | ||
| + | finally: | ||
| + | os.close(fd) | ||
| + | |||
| + | print(" | ||
| + | |||
| + | |||
| + | if __name__ == " | ||
| + | raise SystemExit(main()) | ||
| </ | </ | ||
| ==== CPi-A/B/C/S ==== | ==== CPi-A/B/C/S ==== | ||
| + | |||
| 전자기식 부저는 CPi-A/B/S 패널 PC에서는 GPIO 핀 30에 연결되어 있으며, CPi-C 패널 PC에서는 GPIO 핀 27에 연결되어 있습니다. [[https:// | 전자기식 부저는 CPi-A/B/S 패널 PC에서는 GPIO 핀 30에 연결되어 있으며, CPi-C 패널 PC에서는 GPIO 핀 27에 연결되어 있습니다. [[https:// | ||
| 다음 예제는 프로그래밍 언어를 사용하여 부저음을 출력하는 방법을 보여줍니다. | 다음 예제는 프로그래밍 언어를 사용하여 부저음을 출력하는 방법을 보여줍니다. | ||
| + | |||
| + | === 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, | ||
| + | |||
| + | if (pi < 0) | ||
| + | { | ||
| + | Console.Error.WriteLine( | ||
| + | $" | ||
| + | Environment.ExitCode = 1; | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | try | ||
| + | { | ||
| + | // pigpio가 사용 가능한 가장 가까운 PWM 주파수를 선택합니다. | ||
| + | int actualFrequency = Pigpio.set_PWM_frequency(pi, | ||
| + | |||
| + | if (actualFrequency < 0) | ||
| + | throw new InvalidOperationException( | ||
| + | $"PWM 주파수를 설정할 수 없습니다. 오류: {actualFrequency}" | ||
| + | |||
| + | Console.WriteLine( | ||
| + | $" | ||
| + | |||
| + | // 128/255는 약 50% 듀티비입니다. | ||
| + | CheckResult( | ||
| + | Pigpio.set_PWM_dutycycle(pi, | ||
| + | " | ||
| + | |||
| + | // 100밀리초 동안 부저음을 출력합니다. | ||
| + | Thread.Sleep(100); | ||
| + | } | ||
| + | finally | ||
| + | { | ||
| + | // 연결을 종료하기 전에 항상 부저음을 중지합니다. | ||
| + | int result = Pigpio.set_PWM_dutycycle(pi, | ||
| + | |||
| + | if (result < 0) | ||
| + | Console.Error.WriteLine( | ||
| + | $" | ||
| + | |||
| + | Pigpio.pigpio_stop(pi); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | private static void CheckResult(int result, string message) | ||
| + | { | ||
| + | if (result < 0) | ||
| + | throw new InvalidOperationException($" | ||
| + | } | ||
| + | } | ||
| + | |||
| + | internal static class Pigpio | ||
| + | { | ||
| + | private const string LibraryName = " | ||
| + | |||
| + | [DllImport(LibraryName, | ||
| + | internal static extern int pigpio_start( | ||
| + | IntPtr address, | ||
| + | IntPtr port); | ||
| + | |||
| + | [DllImport(LibraryName, | ||
| + | internal static extern void pigpio_stop(int pi); | ||
| + | |||
| + | [DllImport(LibraryName, | ||
| + | internal static extern int set_PWM_frequency( | ||
| + | int pi, | ||
| + | uint gpio, | ||
| + | uint frequency); | ||
| + | |||
| + | [DllImport(LibraryName, | ||
| + | internal static extern int set_PWM_dutycycle( | ||
| + | int pi, | ||
| + | uint gpio, | ||
| + | uint dutycycle); | ||
| + | } | ||
| + | </ | ||
| + | |||
| === Python 예제 === | === Python 예제 === | ||
| 줄 205: | 줄 444: | ||
| # 샘플링 속도의 제한으로 인해 실제로는 2700Hz를 | # 샘플링 속도의 제한으로 인해 실제로는 2700Hz를 | ||
| - | # 정확히 출력할 수 없지만, 가능한 가장 가까운 주파수로 설정됩니다. | + | # 정확하게 |
| pi.set_PWM_frequency(PIN, | pi.set_PWM_frequency(PIN, | ||
| 줄 222: | 줄 461: | ||
| [[comfilepi: | [[comfilepi: | ||
| - | ``` | ||
