PWM
PWMとは
こんな波形です。

パルス(Pulse)の周期は一定です
duty(ONの時間 / 周期)の幅(Width)を変える
変調(Modulation)です

Duty=0%でグランドレベルの定電圧です。
Duty=100%でVCCの定電圧です。
Duty=50%で矩形波です。

PWMは調光やモーター制御などで使います。昔、サウンドカードなどなかった時代、PC9801の電源投入時ピポと音を出すICを制御して音声を出したことがあります。 しかし、サウンドカードが普及するとこの技術はあまり使われなくなりました。
PWMの活躍場面は、サーボモータの制御ではないでしょうか?オシロスコープの波形は実際制御している波形です。
サーボモーターはGWS PICO STDです。
数年前は、数十ヘルツの周期のPWMはラズベリーパイでは困難と思われていたようですが、今は簡単に制御できます。 ただし使用する使用するライブラリによって簡単だったり面倒だったりします。おススメは
pigpio
です。これを使ったアプリです。もちろんGUIです。

このサーボの仕様書が見つからなかったです。周期は20msec (周波数なら50Hz)は分かったので動作確認した結果を反映しました。dutyは2%から12%までが有効範囲です。
pigpioを使うには一度デーモンを起動する必要があります。
sudo pigpiod

起動のたびにこれをやるのが面倒な人は、
sudo nano /etc/rc.local
exit 0
の前に
pigpiod
を挿入してください。
それではソースです。

# -*- coding: utf-8 -*-
import tkinter  as tk
import pigpio

def getDuty(percent):
    return int(1000000 * (percent / 100))

#スケールが動いたときのイベントハンドラー
def get_siler():
    pi.hardware_PWM(PWM_PIN, 50, getDuty(val.get()))
    print(val.get())


if __name__ == "__main__":
    PWM_PIN = 18
    
    root = tk.Tk()
    root.title('PWM control')
    root.geometry("260x100")

    #ラベルの設定
    lblduty = tk.Label(
        root, relief=tk.RIDGE, bd=2, text='duty %')
    lblduty.grid(row=0, column=0)

    # スケールの設定
    val = tk.DoubleVar()
    val.set(7)
    sc = tk.Scale(
        root,
        variable=val,
        orient=tk.HORIZONTAL,
        length=250,
        from_=2,
        to=13,
        relief= tk.FLAT,
        resolution=0.2,
        tickinterval=2,
        command=lambda e: get_siler())
    sc.grid(row=1, column=0, sticky=(tk.N, tk.E, tk.S, tk.W))
    
    #GPIOの設定
    pi = pigpio.pi()
    pi.set_mode(PWM_PIN, pigpio.OUTPUT)
    #50Hz(20msec)
    pi.hardware_PWM(PWM_PIN, 50, getDuty(val.get()))

    root.mainloop()
主役は、

    #50Hz(20msec)
    pi.hardware_PWM(PWM_PIN, 50, getDuty(val.get()))
PWMの出力はこれだけです。
また、スケールは0.2%ごとに増減できます。
こんな短いコードでサーボを動かすことができました。

PWM非実装のポートでPWMができる

pigpioはPWM非実装のポートでPWMが使えます。

    pi.set_mode(PWM_PIN, pigpio.OUTPUT)
    #周波数をセットする50Hz(20msec)
    pi.set_PWM_frequency(PWM_PIN, 50)
    #100%=PWM_RANGE  1%=PWM_RANGE / 100
    #PWM_RANGEの範囲 25~40000
    pi.set_PWM_range(PWM_PIN, PWM_RANGE)
    #PWMのdutyをセット
    pi.set_PWM_dutycycle(PWM_PIN, getDuty(7))
コメントに書いてある通りです。
hardware_PWMと違うのは100%に対するrangeをセットするところです。
PWMの回路を実装したポートに比べ オシロスコープでの波形と実際の動作は・・・・・・
同じです!
これなら複数のサーボを使えそうですね。全部のソースです。

# -*- coding: utf-8 -*-
import tkinter  as tk
import pigpio

def getDuty(percent):
    return int(percent * PWM_RANGE  / 100)

#スケールが動いたときのイベントハンドラー
def get_siler():
    pi.set_PWM_dutycycle(PWM_PIN, getDuty(val.get()))
    print(val.get())


if __name__ == "__main__":
    PWM_PIN = 20
    PWM_RANGE = 10000
    
    root = tk.Tk()
    root.title('PWM control')
    root.geometry("260x100")

    #ラベルの設定
    lblduty = tk.Label(
        root, relief=tk.RIDGE, bd=2, text='duty %')
    lblduty.grid(row=0, column=0)

    # スケールの設定
    val = tk.DoubleVar()
    val.set(7)
    sc = tk.Scale(
        root,
        variable=val,
        orient=tk.HORIZONTAL,
        length=250,
        from_=2,
        to=13,
        relief= tk.FLAT,
        resolution=0.2,
        tickinterval=2,
        command=lambda e: get_siler())
    sc.grid(row=1, column=0, sticky=(tk.N, tk.E, tk.S, tk.W))
    
    #GPIOの設定
    pi = pigpio.pi()
    pi.set_mode(PWM_PIN, pigpio.OUTPUT)
    #周波数をセットする50Hz(20msec)
    pi.set_PWM_frequency(PWM_PIN, 50)
    #100%=PWM_RANGE  1%=PWM_RANGE / 100
    #PWM_RANGEの範囲 25~40000
    pi.set_PWM_range(PWM_PIN, PWM_RANGE)
    #PWMのdutyをセット
    pi.set_PWM_dutycycle(PWM_PIN, getDuty(7))

    root.mainloop()