みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

Yocto Project 3.0(Zeus)でラズベリーパイ4を動かす

はじめに

技適が通り、やっと国内で流通し始めたラズベリーパイ4が手に入ったので、Yocto Projectで動かしてみる。 発熱がすごいらしいのでファン付きのケースも同時に購入した。

構築手順

ソース取得

依存関係がいつの間にか改善されている。最小限の構成であればmeta-openembeddedは不要となったようだ。

$ mkdir -p rpi-zeus/layers
$ cd rpi-zeus/layers
$ git clone git://git.yoctoproject.org/poky.git -b zeus
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b zeus
$ cd ../

環境変数設定

$ source layers/poky/oe-init-build-env build

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

こちらもmeta-raspberrypiのみの追加で行ける。

$ bitbake-layers add-layer ../layers/meta-raspberrypi

local.confの修正

MACHINEをraspberrypi4に設定し、UARTを有効化する。

MACHINE = "raspberrypi4"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# connman
IMAGE_INSTALL_append = " connman \
                 connman-client \
"

ビルド

core-image-baseをビルドする。

$ bitbake core-image-base

書き込み

ddやEtcherで書き込む

ddの場合は次のようにする。

$ sudo dd if=tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.rpi-sdimg of=/dev/sdX bs=100M 

/dev/sdXはSDのデバイスに適宜読み替え。

実行

zeusが起動した。

# cat /etc/os-release
ID="poky"
NAME="Poky (Yocto Project Reference Distro)"
VERSION="3.0.1 (zeus)"
VERSION_ID="3.0.1"
PRETTY_NAME="Poky (Yocto Project Reference Distro) 3.0.1 (zeus)"

メモリもきちんと4G見えているようだ。

# free
              total        used        free      shared  buff/cache   available
Mem:        3999780       86908     3871760        8796       41112     3849068
Swap:             0           0           0

きちんと動かしてないけど、USB3.0バイスも認識している様子。

~# lsusb
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 0480:0200 Toshiba America Inc External Disk
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

# lsusb -t
/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
    |__ Port 1: Dev 2, If 0, Class=Mass Storage, Driver=usb-storage, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M

USB3.0のHDDを接続したらきちんとBus 02の下にHDDが入った。

まとめ

大体問題なく動作しているようだ。

ベアメタルのHiFIve1 Rev.BでHello world

はじめに

前回ベアメタルでLチカは動いたので、今度はシリアルを動かす。

ホストPCの環境はUbuntu 18.04

UARTのポート

HiFive1 Rev.BをPCに接続すると/dev/ttyACM0/dev/ttyACM1が見えるようになる。 この内/dev/ttyACM0デバッグコンソールとして使える。

f:id:mickey_happygolucky:20191114074445p:plain

この図のSegger J-Link OBのUART 0FE310のUART0に接続されている。そのためFE310のUART0の内容がttyACM0でも見ることができる。

筆者環境ではSegger J-Link OBを実装しているMK22Fは意外と調子が悪いので、UARTのボーレートなどのパラメータが正しくてもminicomにゴミしか出ないことが多い。

なので、MK22Fに入るまでのUART0が直接見れるJ2コネクタのD0/D1にUSB-シリアル変換ケーブルを接続するほうが確実性は高い。

UART1

意外とわかりづらいのがFE310のUART1。

Segger J-Link OBのUARTには接続されていないのでttyACM1とは無関係。

f:id:mickey_happygolucky:20191114074453p:plain

GPIO_18がUART1_RX、GPIO_23がUART1_TXに割り当てられている。

回路図によるとArduino互換ヘッダのD2(DIG2)がGPIO_18(UART1_RX)、D7(DIG7)がGPIO_23(UART1_TX)に割り当たっている。

f:id:mickey_happygolucky:20191114074502p:plain

だがしかし。筆者の手元のボードではUART1に出力するとD2から波形が出る。

マニュアルなのか配線なのかよくわからないが、とりあえずTXとRXは逆転していそう。 今回はHello worldということでTXしか使用しないので実際のRXがどうなっているかは未確認。

リンカスクリプト

linker.ldは変更なし。

アセンブリ

utils.Sレジスタを読み出すためのget32を追加。

    .global start
start:
    lui sp, 0x80004
    jal main
    j .
    
    .global dummy
dummy:
    ret

    .global put32
put32:
    sw x11,(x10)
    ret

    .global get32
get32:
    lw x10, (x10)
    ret

プログラム

プログラムはすべてmain.cに記述。

定義部

マクロとグローバル変数の定義

void put32(unsigned int, unsigned int);
unsigned int get32(unsigned int);

#define GPIOBASE         0x10012000
#define GPIO_OUTPUT_EN     (GPIOBASE+0x08)
#define GPIO_PORT      (GPIOBASE+0x0c)
#define GPIO_IOF_EN        (GPIOBASE+0x38)
#define GPIO_IOF_SEL   (GPIOBASE+0x3C)

#define IOF_UART0_RX   (1<<16)
#define IOF_UART0_TX   (1<<17)
#define IOF_UART1_RX   (1<<18)
#define IOF_UART1_TX   (1<<23)

#define UART0BASE      0x10013000
#define UART1BASE      0x10023000

#define UART_TXDATA        (0x00)
#define UART_TXCTRL        (0x08)
#define UART_DIV       (0x18)

#define TXCTRL_TXEN        (1)
#define TXCTRL_NSTOP   (1<<1)

#define HFCLK          (16000000) //HF clock is 16MHz
#define LFCLK          (32768)    //LF clock is 32KHz

unsigned int uart = 0;

UART初期化

後からUART0/1両対応にしたので不細工。

int setup_uart(int port, int baudrate)
{
    unsigned int iof;
    switch (port) {
    case 0:
        uart = UART0BASE;
        iof = IOF_UART0_RX|IOF_UART0_TX;
        break;
    case 1:
        uart = UART1BASE;
        iof = IOF_UART1_RX|IOF_UART1_TX;
        break;
    default:
        return -1;
    }

    unsigned int ra;
    ra = get32(GPIO_IOF_SEL);
    ra &= ~iof;
    put32(GPIO_IOF_SEL, ra);

    ra = get32(GPIO_IOF_EN);
    ra |= iof;
    put32(GPIO_IOF_EN, ra);

    unsigned div = HFCLK/baudrate-1;
    put32(uart+UART_DIV, div);

    put32(uart+UART_TXCTRL, TXCTRL_NSTOP|TXCTRL_TXEN);
    return 0;
}

シリアル出力

TX queueがFullかであれば待ってTXDATAへ書き込み。

void putc(char c)
{
    /* wait if queue is full. */
    while (1) {
        if ((get32(uart+UART_TXDATA) & 0x80000000) == 0)
            break;
    }
    put32(uart+UART_TXDATA, c);
}

void puts(const char* s)
{
    do {
        putc(*s++);
    } while (*s != '\0');
}

1文字ずつputcを呼ぶのはだるいので文字列対応。

こんにちわ

int main(int argc, char *argv[])
{
    setup_uart(0, 115200);
    puts("hello world !!\r\n");
    return 0;
}

Makefile

Makefileは少し改良した

PROGRAM=hello
RISCVGNU = riscv32-unknown-elf

AOPS = -march=rv32imac
COPS = -march=rv32imac -Wall -O0 -g  -nostdlib -nostartfiles -ffreestanding 
#COPS = -march=rv32imac -Wall -O2  -nostdlib -nostartfiles -ffreestanding

all : $(PROGRAM).hex

clean :
    rm -f *.o
    rm -f *.elf
    rm -f *.hex
    rm -f *.list
    rm -f *~

utils.o : utils.S
    $(RISCVGNU)-as $(AOPS) utils.S -o utils.o

main.o : main.c
    $(RISCVGNU)-gcc $(COPS) -c main.c -o main.o

$(PROGRAM).hex : linker.ld utils.o main.o 
    $(RISCVGNU)-ld utils.o main.o -T linker.ld -o $(PROGRAM).elf
    $(RISCVGNU)-objdump -D $(PROGRAM).elf > $(PROGRAM).list
    $(RISCVGNU)-objcopy $(PROGRAM).elf -O ihex $(PROGRAM).hex

flash : $(PROGRAM).hex
    JLinkExe -device FE310 -speed 1000 -if JTAG -jtagconf -1,-1 -autoconnect 1 -CommanderScript "upload.jlink"

gdb : $(PROGRAM).hex
    JLinkGDBServer -device FE310 -endian little -speed 1000 &
    riscv32-unknown-elf-gdb $(PROGRAM).elf

プログラム名の変数かとgdbの追加。

これによってmake gdbデバッグを開始できる。

J-Link OBの調子によってgdbの開始に失敗することがある。

実行

make flashで書き込み。

minicomでUART0を開いておくと、次のように表示される。

Bench Clock Reset Complete

ATE0-->ATE0
OK
AT+BLEINIT=0-->OK
AT+CWMODE=0-->OK

hello world !!

hello world !!の前にブートローダによっていくつか表示がある。

main関数で呼び出しているsetup_uartの第1引数を1にするとUART1に変更することができる。

まとめ

UARTを使用してHello worldを作成した。

ttyACM0はUART0の入出力をUSBケーブルで見れるようになるが、若干不安定。

UART1はUSBのttyACM1とは無関係。J2のD2/D7でアクセス可能だが、TX/RXが逆転しているように見える。

HiFive1 Rev.Bはまだハードがこなれていない感じ。

参照

図は下記からの抜粋

ラズベリーパイ3でYocto Project 3.0(Zeus)を試す

はじめに

Yocto Project 3.0(Zeus)がリリースされていたのでラズベリーパイ3で動かす。

構築手順

ソース取得

$ mkdir -p rpi-zeus/layers
$ cd rpi-zeus/layers
$ git clone git://git.yoctoproject.org/poky.git -b zeus
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b zeus
$ git clone git://git.openembedded.org/meta-openembedded -b zeus
$ cd ../

環境変数設定

$ source layers/poky/oe-init-build-env build

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

ツールの説明

今回のバージョンからbitbake以外のツールに関する説明が表示されるようになった。

Other commonly useful commands are:
 - 'devtool' and 'recipetool' handle common recipe tasks
 - 'bitbake-layers' handles common layer tasks
 - 'oe-pkgdata-util' handles common target package tasks

oe-pkgdata-utilははじめて見る。

パッケージのバージョンやファイルの一覧などを見ることができるっぽい。

レイヤ追加

$ bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-python
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
$ bitbake-layers add-layer ../layers/meta-raspberrypi

local.confの修正

MACHINEをraspberrypi3に設定し、UARTを有効化する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# connman
IMAGE_INSTALL_append = " connman \
                 connman-client \
"

ビルド

core-image-baseをビルドする。

$ bitbake core-image-base

書き込み

ddやEtcherで書き込む

ddの場合は次のようにする。

$ sudo dd if=tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.rpi-sdimg of=/dev/sdX bs=100M 

/dev/sdXはSDのデバイスに適宜読み替え。

実行

zeusが起動した。

# cat /etc/os-release
ID="poky"
NAME="Poky (Yocto Project Reference Distro)"
VERSION="3.0 (zeus)"
VERSION_ID="3.0"
PRETTY_NAME="Poky (Yocto Project Reference Distro) 3.0 (zeus)"

まとめ

zeusも問題なく立ち上がった。

ベアメタルでHiFIve1 Rev.Bを動かす

はじめに

PlatformIOでHiFive1 Rev.Bを動かすで下記のように書いたので ベアメタルで動かしてみる。

標準のライブラリやヘッダは使っていないので、リンカスクリプトとスタートアップを書けば、 ベアメタルでもこのまま動くはず。

リンカスクリプト

リンカスクリプトlinker.ldとして次の内容で作成する。

MEMORY
{
    rom : ORIGIN = 0x20010000, LENGTH = 0x6a120
    ram : ORIGIN = 0x80000000, LENGTH = 0x4000
}

ENTRY(start)

SECTIONS
{
    .text : {*(.text*)} > rom
    .rodata : {*(.rodata*)} > rom
    .bss : {*(.bss*)} > ram
}

スタートアップ

utils.Sにスタートアップのコードを追加する。

    .global start
start:
    lui sp, 0x80004
    jal main
    j .
    
    .global dummy
dummy:
    ret

    .global put32
put32:
    sw x11,(x10)
    ret

Makefile

Makefileを次の内容で作成する。

RISCVGNU = riscv32-unknown-elf

AOPS = -march=rv32imac
COPS = -march=rv32imac -Wall -O0 -g  -nostdlib -nostartfiles -ffreestanding 
#COPS = -march=rv32imac -Wall -O2  -nostdlib -nostartfiles -ffreestanding

all : blinker.hex

clean :
    rm -f *.o
    rm -f *.elf
    rm -f *.hex
    rm -f *.list
    rm -f *~

utils.o : utils.S
    $(RISCVGNU)-as $(AOPS) utils.S -o utils.o

led.o : led.c
    $(RISCVGNU)-gcc $(COPS) -c led.c -o led.o

blinker.hex : linker.ld utils.o led.o 
    $(RISCVGNU)-ld utils.o led.o -T linker.ld -o blinker.elf
    $(RISCVGNU)-objdump -D blinker.elf > blinker.list
    $(RISCVGNU)-objcopy blinker.elf -O ihex blinker.hex

flash : blinker.hex
    JLinkExe -device FE310 -speed 1000 -if JTAG -jtagconf -1,-1 -autoconnect 1 -CommanderScript "upload.jlink"

Cプログラム

led.cの内容はそのままなので省略。

JLinkコマンドファイル

ターゲットへの書き込みにupload.jlinkとして次の内容でファイルを作成する。

h
loadfile ./blinker.hex
r
q

ツールチェイン

ツールチェインはRISC-Vのベアメタル入門(自分用)を参照。

J-Linkのツール

SEGGERのサイトからパッケージをダウンロードする。

PlatformIOのものを使う場合には.bashrcに次のように追加してPATHを通す。

export PATH="${HOME}/.platformio/packages/tool-jlink:${PATH}"

書き込み

Makefileに次の部分を仕込んだのでmakeコマンドで書き込むことができる。

flash : blinker.hex
    JLinkExe -device FE310 -speed 1000 -if JTAG -jtagconf -1,-1 -autoconnect 1 -CommanderScript "upload.jlink"

書き込みにはJLinkExeを使用するのでJ-Linkのツールが必要となる。

次のようにしてプログラムを書き込む。

$ make flash

まとめ

ベアメタルでもLチカができた。

PlatformIOでHiFive1 Rev.Bをデバッグする

はじめに

前回はPlatformIOでLチカの実行までしたので、デバッグ方法調べる。

pio debug

特にplatform.iniを書き換えたりする必要もなく次のコマンドでデバッグを開始できる。

$ pio debug --interface=gdb
Warning! Please install `99-platformio-udev.rules`. 
Mode details: https://docs.platformio.org/en/latest/faq.html#platformio-udev-rules

SEGGER J-Link GDB Server V6.52 Command Line Version

JLinkARM.dll V6.52 (DLL compiled Sep 27 2019 17:53:31)

Command line: -singlerun -if JTAG -select USB -speed 1000 -jtagconf -1,-1 -device FE310 -port 2331
-----GDB Server start settings-----
GDBInit file:                  none
GDB Server Listening port:     2331
SWO raw output listening port: 2332
Terminal I/O port:             2333
Accept remote connection:      yes
Generate logfile:              off
Verify download:               off
Init regs on start:            off
Silent mode:                   off
Single run mode:               on
Target connection timeout:     0 ms
------J-Link related settings------
J-Link Host interface:         USB
J-Link script:                 none
J-Link settings file:          none
------Target related settings------
Target device:                 FE310
Target interface:              JTAG
Target interface speed:        1000kHz
Target endian:                 little

Connecting to J-Link...
Reading symbols from /home/mickey/work/pio/hifive1_revb/.pio/build/hifive1-revb/firmware.elf...
(gdb) J-Link is connected.
Firmware: J-Link OB-K22-SiFive compiled Aug 22 2019 14:12:56
Hardware: V1.00
S/N: 979001361
Checking target voltage...
Target voltage: 3.30 V
Listening on TCP/IP port 2331
Connecting to target...
J-Link found 1 JTAG device, Total IRLen = 5
JTAG ID: 0x20000913 (RISC-V)
Connected to target
Waiting for GDB connection...

ここまで表示されたあと、Enterを押すとgdbのプロンプトが表示される。

次のコマンドでターゲットに接続すればデバッグが可能になる。

(gdb) target remote :2331
(gdb) mon reset
(gdb) load

たとえば、前回のプログラムであれば次のような感じ。

(gdb) l
18      put32(GPIO_OUTPUT_EN, LED_RGB);
19      put32(GPIO_OUT_XOR, LED_RGB);
20      put32(GPIO_PORT, LED_RGB);
21  
22      while (1) {
23          put32(GPIO_PORT, LED_RGB);
24          for (rx = 0; rx < 2000000; rx++) dummy(rx);
25          put32(GPIO_PORT, 0);
26          for (rx = 0; rx < 2000000; rx++) dummy(rx);
27      }

ブレークポイントを貼る。

(gdb) b 23

実行状態にする。

(gdb) c

main.cの23行目の部分でブレークすればOK。

99-platformio-udev.rulesの警告

実行時に下記の警告が表示されることがあるが、問題なくターゲットと接続できている場合は無視して良い。

Warning! Please install `99-platformio-udev.rules`. 
Mode details: https://docs.platformio.org/en/latest/faq.html#platformio-udev-rules

ターゲットとうまく接続できない場合はudevのルールをインストールすると良い。 詳細はメッセージにあるURLに書いてあるとおり。

$ curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules
$ sudo service udev restart

まとめ

PlatformIOではデバッグが簡単に開始できる。

PlatformIOでHiFive1 Rev.Bを動かす

はじめに

HiFive Rev.Bを入手したのでまずはPlatformIOでLチカする。

プロジェクトの作成

次のコマンドを実行し、PlatformIOのプロジェクトを作成する。

$ platformio init --board hifive1-revb

無印とはブートアドレスが異なるので、ボードもhifive1-revbを使用する。

Lチカのコード

基本的なコードはここのblinker01を参考にしている。

レジスタアクセス

レジスタにアクセスするためにマクロを作成しておく。

utils.Sを次の内容で作成

    .global dummy
dummy:
    ret

    .global put32
put32:
    sw x11,(x10)
    ret

led.cを次の内容で作成する。

void put32(unsigned int, unsigned int);
void dummy (unsigned int);

#define GPIOBASE         0x10012000
#define GPIO_OUTPUT_EN     (GPIOBASE+0x08)
#define GPIO_PORT      (GPIOBASE+0x0c)
#define GPIO_OUT_XOR   (GPIOBASE+0x40)

#define LED_RED (1<<22)
#define LED_BLUE (1<<21)
#define LED_GREEN (1<<19)
#define LED_RGB (LED_RED|LED_GREEN|LED_BLUE)

int main(int argc, char *argv[])
{
    unsigned int rx;

    put32(GPIO_OUTPUT_EN, LED_RGB);
    put32(GPIO_OUT_XOR, LED_RGB);
    put32(GPIO_PORT, LED_RGB);

    while (1) {
        put32(GPIO_PORT, LED_RGB);
        for (rx = 0; rx < 2000000; rx++) dummy(rx);
        put32(GPIO_PORT, 0);
        for (rx = 0; rx < 2000000; rx++) dummy(rx);
    }
    return 0;
}

標準のライブラリやヘッダは使っていないので、リンカスクリプトとスタートアップを書けば、 ベアメタルでもこのまま動くはず。

ビルド

次のコマンドでビルド

$ platformio run

これは次のように短く書ける

$ pio run

書き込み

次のコマンドで書き込み

$ platformio run --target upload

次のように短く書ける

$ pio run -t upload

ラズベリーパイ3の(ソフトウェア)PWMで回転サーボを動かす

はじめに

秋月電子でも入手可能な回転サーボ FS90R を入手したので動かしてみる。

環境はwarrior(Yocto 2.7)でmeta-raspberrypiを使用する。

環境構築

ここの「環境構築」を参照。

local.confに下記の内容を追加する。

IMAGE_INSTALL_append = " rpi-gpio"

core-image-baseを作成する。

$ bitbake core-image-base

ddやEtcherなどでマイクロSDに書き込む。

配線

FS90R側の信号は次のようになる。

信号
オレンジ Signal(PWM)
+(V)
-(GND)

駆動電圧は最低4.8Vなので、5V入れてやれば動く。 制御用の電圧は3.3Vでも問題ない様子。

ラズベリーパイの5VのピンはUSBからの電流を分配しているだけっぽいので、 手っ取り早くやるなら、+のところはラズベリーパイの5Vに接続してしまっても問題ないと思う。 ただし、本体の電源そのものなので、サーボを回すと高頻度で次のように電源低下を検出ようになる。

[ 1747.351869] Under-voltage detected! (0x00050005) 

それが気になる場合は、別系統で電源を引っ張ってきてモーターに入れると良い。

筆者は、USB-シリアル変換ケーブルのPCのUSBから出ている5Vをモーターに入れた。 (ラズベリーパイにコンセントの電源をひっぱているので、普段この5Vは余っている)

プログラム

PWMを制御したいので手っ取り早くpythonで書く。GPIOの制御にはRPi.GPIOを使用する。 RPi.GPIOはソフトウェアでPWMをシミュレートすることができ、普通のGPIOピンでもPWMすることができる。

ただし、ハードウェアPWMと比べると制度が低いので、正確なパルス幅が必要な場合は注意が必要となる。

Duty比

パルス幅のレンジは700〜2300usで、1500usで停止。これを境に小さいと時計回り、大きいと半時計回りとなる。

動作 パルス幅(us)
停止 1500
時計回り 1500未満
半時計回り 1501以上

RPi.GPIOのPWMではベースとなる周波数とDuty比でパルス幅を指定する。

例えば50Hzの場合、1回の振幅に20msかかるので、1500usのパルス幅を指定したい場合次のように計算する。

1500us = 1500us/(20ms*1000)*100 = 7.5%

停止させておくために設定するDutyは7.5%ということになる。

次のプログラムは制御信号にGPIO21を使用して次の動作を行う。

  • 2秒間停止
  • 2秒間時計回りで回転
  • 2秒間半時計回りで回転
import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

gp_out = 21
GPIO.setup(gp_out, GPIO.OUT)
servo = GPIO.PWM(gp_out, 50)
servo.start(7.5)
time.sleep(2)
servo.ChangeDutyCycle(5)
time.sleep(2)
servo.ChangeDutyCycle(10)
time.sleep(2)
servo.stop()
GPIO.cleanup() 

しかし、実際に動かしてみるとピッタリと停止しないことがわかる。 これは先述の通りソフトウェアPWMでありパルス幅が正確ではないことが原因とななる。

なので、7.5の時点でどちらに回転しているかを見て値を微調整する。 筆者の環境では7.1でピッタリと停止した。

まとめ

サーボの電源は本体と別系統が無難。

ソフトウェアPWMでも、値を微調整すれば回転サーボの制御は可能。 ただし正確に制御したい場合はハードウェアPWMのほうが確実。