回答:
短い答え:Raspberry PiでPWMを確実に読み取ることはできません。
PWMの読み取りにはマイクロ秒の精度が必要です(非常に遅いPWMを読み取っている場合を除く)。これは、カーネルモジュールをいじらないと、ユーザーランドソフトウェアのRaspberry Piでは利用できません。
PWMをキャプチャする最も簡単な方法は、シリアルまたはI 2 C出力を備えた安価な(<0.5ドル)マイクロコントローラーを入手し、それをRaspberry Piに接続して、マイクロコントローラーから実際の値を読み取ることです。これは非常に確実に機能し、非常に正確です。
私はpiGpio Cライブラリを使用して、かなり正確なパルス幅測定を行うことができます。 http://abyz.me.uk/rpi/pigpio/index.html
このライブラリを使用すると、gpioピンのエッジ遷移でトリガーされるコールバック関数をインストールし、各遷移のマイクロ秒レベルのタイムスタンプを取得できます。マイクロ秒の精度でこれを当てにできるとは思わないでください。ただし、私のテストでは、精度は少なくとも+/- 10usであり、おそらくそれより優れていることが示唆されています。
レベルの変更を自分でgpioにポーリングするビジーループを実行するよりもはるかに優れています。
これは興味深い質問であり、Google検索は明確な解決策を提供していないとあなたが正解している質問です。(私がGoogleが私の教育/課題について知りたいすべてのことを数秒で答えることができた日を逃しています。)
PWMの原理を理解していると思います。したがって、私はそれについては触れません。ただし、理論上は、通常のデジタル入力ピンのPWM値を巧妙なコーディングで読み取ることができると思います。
私はこれを自分で試したことはありませんが、ピンが高い時間と低い時間を測定し(PWMの読み取り値を与える)、センサーのサプライヤーが提供する数式を使用できるはずですこれを実際の読み値に変換します。
この方法は、超音波モジュールからパルス長を読み取り、それを距離に変換する必要がある同様の問題で私に役立ちます。私が想定できる問題は、信頼できる測定値を確実にすることです!
あなたがそれが役立つと思い、超音波モジュールに使用したコードを確認したい場合は、そのように言ってください。帰宅したらコピーします。
私はコードのコピーを開始しましたが、何らかの理由で、ウェブサイトは一度に小さなセクションしかコピーできません(そして私はガレージからpiを取り出すのが面倒なので)ここへのリンクです。モジュールを近接センサーとして使用することに関連しているため、下部にあるほとんどの機能は無視してください。http://pibot.webnode.com/products/ultrasonic-range-sensor/
長い答え:実際にできます!(まあ私たちの友人の抵抗器とコンデンサーから少し助けて)
PWM出力をアナログ電圧レベル(DAC)に変換し、ラズベリーパイのADCピンで読み取ることができます。
必要なのは、4k7の抵抗と0.1uFのコンデンサです。
この回路のシミュレーション – CircuitLabを使用して作成された回路図
上記のシンプルなRCローパスフィルターは、PWM信号をデューティサイクルに比例する電圧に変換します。これは、ラズベリーパイでアナログ値として読み取ることができます。
私の答えはピンからではありませんが、サウンドカードオシロスコープに基づくものを使用して、パルス入力を読み取ることができます。
人々は何年もデスクトップPCでサウンドカードを使用してオシロスコープを作成しています。最新の内蔵サウンドカードを使用すると、10kHzまで使用可能な結果が得られるようです。Raspberry Pi USB接続のサウンドカードを使用すると、最大周波数が低くなる可能性があります。
Linux用のサウンドカードオシロスコーププロジェクトの例を以下に示します。http://www.yann.com/en/diy-turn-your-gnulinux-computer-into-a-free-oscilloscope-29/09/2010.html
私が書いたこのPythonスクリプトは、RCレシーバーのPWM信号を読み取るのに適しています。すでに指摘したように、高周波PWM信号は明らかに機能しません。
RCレシーバーの10本の信号出力ピンをRaspberry GPIOピンに直接接続しました。レシーバは、RPIからの+ 5VおよびGNDピンによって給電されます。
他の多くのことを行うため、スクリプトを簡略化しました。間違いや残り物を見つけた場合は、お知らせください
import RPi.GPIO as GPIO
import time
import numpy as np
inPINS = [2,3,4,14,15,18,17,27,22,23]
smoothingWindowLength=4
def getTimex():
return time.time()
GPIO.setup(inPINS, GPIO.IN)
upTimes = [[0] for i in range(len(inPINS))]
downTimes = [[0] for i in range(len(inPINS))]
deltaTimes = [[0] for i in range(len(inPINS))]
def my_callback1(channel):
i = inPINS.index(channel)
v = GPIO.input(inPINS[i])
#GPIO.output(outPINS[0], v) # mirror input state to output state directly (forward servo value only) - don't set PWM then for this pin
if (v==0):
downTimes[i].append(getTimex())
if len(downTimes[i])>smoothingWindowLength: del downTimes[i][0]
else:
upTimes[i].append(getTimex())
if len(upTimes[i])>smoothingWindowLength: del upTimes[i][0]
deltaTimes[i].append( (downTimes[i][-1]-upTimes[i][-2])/(upTimes[i][-1]-downTimes[i][-1]) )
if len(deltaTimes[i])>smoothingWindowLength: del deltaTimes[i][0]
GPIO.add_event_detect(inPINS[0], GPIO.BOTH, callback=my_callback1)
GPIO.add_event_detect(inPINS[1], GPIO.BOTH, callback=my_callback1)
try:
while True:
ovl = deltaTimes[0][-smoothingWindowLength:] # output first pin PWM
ov = sorted(ovl)[len(ovl) // 2] #ov = np.mean(ovl)
print ov
time.sleep(0.1)
except KeyboardInterrupt:
GPIO.cleanup()
Raspberry PiのPWM入力を、pigpio Cライブラリを使用して読み取ることは非常に可能で、比較的簡単です。優れたパフォーマンスが必要な場合は、PythonではなくCを使用することをお勧めします。以下の短いサンプルコードを提供しました。一部の人が言うところとは対照的に、これは優れたタイミングパフォーマンスと非常に低いジッターを備えています。読み取り値はRPi 3 Bで一貫して5 us以内であり、5 usほどの短いパルスを測定できます。提供されたコードは概念実証のみであり、パルスの欠如(0%/ 100%デューティサイクル)または72分ごとに発生する「ティック」ラップアラウンドを適切に処理しないことに注意してください。プログラムはユーザーモードで問題なく実行されますが、タイミンググリッチへの抵抗を最大にするには、プログラムを次のような負のniceレベルで実行します。sudo nice -n -20 ./program
http://abyz.me.uk/rpi/pigpio/pdif2.htmlにあるpigpioのドキュメントを参照してください。
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include "pigpiod_if2.h"
static uint32_t rise_tick = 0; // Pulse rise time tick value
static uint32_t pulse_width = 0; // Last measured pulse width (us)
// Callback function for measuring PWM input
void pwm_cbfunc(int pi, unsigned user_gpio, unsigned level, uint32_t tick) {
if (level == 1) { // rising edge
rise_tick = tick;
}
else if (level == 0) { // falling edge
pulse_width = tick - rise_tick; // TODO: Handle 72 min wrap-around
}
}
int main(int argc, char **argv)
{
const unsigned int pwm_in = 23; // GPIO Pin # for PWM in, change as reqd
int pi = pigpio_start(0, 0);
if (pi < 0) {
fprintf(stderr, "pigpio initialization failed (%d)\n", pi);
return pi;
}
// Set up callback for PWM input
callback(pi, pwm_in, EITHER_EDGE, pwm_cbfunc);
while (true) {
printf("PWM pulse width: %u\n", pulse_width);
usleep(500000);
}
}
高精度の簡単なソリューション:
ArduinoをiicスレーブまたはUARTデバイスとして使用すると、問題なく動作するようです。マイクロコントローラーは、pulseInメソッドを介して情報を読み取ることができます。