前回の続き。
STM32マイコン STM32L010F4P6 で
マイクロサーボ SG92R を動かした。
データシートを確認する
STM32マイコン STM32L010F4P6のデータシートは前回と同じなので省略。
マイクロサーボSG92Rのデータシートそのものが見つからなかったので,
マイクロサーボ9g SG-90 のデータシートを参照する。
Specifications
- Operating voltage: 3.3V(~6V)
- 20ms(50Hz) PWM Period
- 0.5 ~ 2.4ms Duty Cycle
- 4.8V(~5V) Power and Signal
とのこと。
つまりサーボコネクタの赤色線を電源の+に, 茶色線をGNDに接続して橙色線に図に書かれている信号を入力すると動く。
STM32とSG92Rサーボを接続
前回の回路にSG92Rサーボを追加した。
STM32CubeIDEでプロジェクトを開く
前回のプロジェクトを再利用する。
CubeIDEをアップデートしたので Version: 1.8.0。
なんとC++17を選択できるようになっていたので選んでみた。
(前から選べたかも知れない。)
STM32L010F4P6のタイマ設定
PWM信号はSTM32L010F4P6のGeneral-purpose timerである TIM2 を PWM mode に設定して作ることにする。
タイマ割り込みを使わないのでTIM2 global interruptはデフォルト設定。
Application note AN4013 - st.com
STM32 cross-series timer overviewより引用。
PA0をTIM2_CH1に割り当ててPWM出力ピンにする。
(このピンにSG92Rの橙色線を接続する。)
とりあえずクロックは4MHzに設定する。
TIM2の設定。
- Clock Source(クロック源): Internal Clock
- Channel1: PWM Generation CH1
- Prescaler(プリスケーラ) : 1
- Counter Period (周期) : 39999
- auto-reload preload: Enable
- Output compare preload: Enable
Prescaler (PSC - 16 bits value)
Prescaler (PSC - 16 bits value) must be between 0 and 65 535.
Counter Period (AutoReload Register - 16 bits value )
Counter Period (AutoReload Register - 16 bits value ) must be between 0 and 65 535.
タイマ周期は50Hzになるように, 以下の計算式より算出した。
$$ Update_event = \frac{TIM\_ CLK}{(PSC+1) \times (ARR+1) \times (RCR+1)} $$ より
ほしい値は各レジスタの設定値なので
右辺の分母を両辺に掛けて, Update_event を両辺から割ると
$$ (PSC+1) \times (ARR+1) \times (RCR+1) = \frac{TIM\_ CLK}{Update\_ event} $$ となって
ここに
- TIM_CLK = 4 MHz つまり $4 \times 10 ^ 6$ Hz
- Update_event = 50 Hz
- RCR(16-bit repetition counter)は無いので RCR = 0 を代入して
$$ \begin{aligned} (PSC+1) \times (ARR+1) &= \frac{4 \times 10^6}{50} \\ &= \frac{4 \times 10^2 \times 10^4}{50} \\ &= \frac{400 \times 10^4}{50} \\ &= 8 \times 10^4 \\ &= 80000 \end{aligned} $$
STM32L010F4P6マイコンの ARRレジスタは16ビットレジスタなので, そのレジスタに設定できる最大値は $2^{16} - 1 = 65535$
計算で得られた値 80000 は ARRレジスタの最大値を超える値なので設定できない。
なのでプリスケーラでクロック源から1/2にする設定(PSC=1)にする。
$$ \begin{aligned} (1+1) \times (ARR+1) & = 80000 \\ 2 \times (ARR+1) & = 80000 \\ ARR+1 &= \frac{80000}{2} \\ ARR &= \frac{80000}{2} - 1 \\ &= 39999 \end{aligned} $$
結果
Prescaler (PSC - 16 bits value) には 1を
Counter Period (AutoReload Register - 16 bits value ) には 39999を
設定する。
SG92Rを動かすプログラム
SG92Rを0°から45°づつ180°まで繰り返し動くプログラムを書いてみた。
/*
* Application.cpp
*
* Copyright 2021 Akihiro Yamamoto.
* Licensed under the Apache License, Version 2.0
* <https://spdx.org/licenses/Apache-2.0.html>
*
*/
#include <ST7032iLcd.hpp>
extern I2C_HandleTypeDef hi2c1;
extern TIM_HandleTypeDef htim2;
static ST7032iLcd i2c_lcd(hi2c1);
using PwmTimerCounts = uint16_t;
constexpr static const PwmTimerCounts PwmPeriod =
40000; // 40000 counts = 20 milliseconds
constexpr static const PwmTimerCounts ServoAngle0 =
PwmPeriod * 0.5 / 20.0; // 0.5 milliseconds / 20 milliseconds
constexpr static const PwmTimerCounts ServoAngle180 =
PwmPeriod * 2.4 / 20.0; // 2.4 milliseconds / 20 milliseconds
// degree: 0 to 180
static void moveTo(uint8_t degree) {
uint32_t count = ServoAngle0 + (ServoAngle180 - ServoAngle0) * degree / 180;
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, count);
std::string buff(50, ' ');
std::snprintf(buff.data(), buff.size(), u8"カクド 「%3d」 ド", degree);
i2c_lcd.setDdramAddress(0);
i2c_lcd.putString(buff);
}
extern "C" void application_setup() {
i2c_lcd.init();
moveTo(0);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
}
extern "C" void application_loop() {
constexpr static const uint8_t step_angles = 45;
constexpr static const uint8_t num_of_detents = 1 + 180 / step_angles;
constexpr static const uint8_t slow_down = 30;
static uint16_t i = 0;
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_4);
HAL_Delay(200);
moveTo(i / slow_down * step_angles);
i = (i + 1) % (slow_down * num_of_detents);
}
プロジェクトのビルド
Ctrl + B でビルド。
11:30:31 **** Incremental Build of configuration Debug for project hello_stm32l0 ****
make -j16 all
arm-none-eabi-g++ -o "hello_stm32l0.elf" @"objects.list" -mcpu=cortex-m0plus -T"C:\Users\aki\STM32CubeIDE\workspace_1.7.0\hello_stm32l0\STM32L010F4PX_FLASH.ld" --specs=nosys.specs -Wl,-Map="hello_stm32l0.map" -Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb -Wl,--start-group -lc -lm -lstdc++ -lsupc++ -Wl,--end-group
c:\st\stm32cubeide_1.7.0\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.9-2020-q2-update.win32_2.0.0.202105311346\tools\arm-none-eabi\bin\ld.exe: hello_stm32l0.elf section `.text' will not fit in region `FLASH'
c:\st\stm32cubeide_1.7.0\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.9-2020-q2-update.win32_2.0.0.202105311346\tools\arm-none-eabi\bin\ld.exe: region `FLASH' overflowed by 3892 bytes
collect2.exe: error: ld returned 1 exit status
make: *** [makefile:79: hello_stm32l0.elf] Error 1
"make -j16 all" terminated with exit code 2. Build might be incomplete.
11:30:32 Build Failed. 3 errors, 0 warnings. (took 348ms)
はい、エラー。
ld.exe: region `FLASH' overflowed by 3892 bytes
FLASH領域から溢れた。
あふれたのは仕方がないのでReleaseビルドにする。
11:43:25 **** Incremental Build of configuration Release for project hello_stm32l0 ****
make -j16 all
arm-none-eabi-size hello_stm32l0.elf
text data bss dec hex filename
11424 124 1752 13300 33f4 hello_stm32l0.elf
Finished building: default.size.stdout
11:43:25 Build Finished. 0 errors, 0 warnings. (took 217ms)
デバッグ関連のコードが無くなるので、収まった。
ST-Linkでプログラミング
前回と同じなので省略。
実行
サーボ角度 0 度
- サーボに供給している電圧: 5.07 V
- PWM信号周期: $19.946 \mathrm{ms} \approx 20 \mathrm{ms}$
- Duty Cycle: $499 \mathrm{\mu s} \approx 0.5 \mathrm{ms}$
- PWM信号電圧: $3.28 \mathrm{V}$
周波数と周期の関係は $f = \frac{1}{T}$ なので $\frac{1}{19.946 \times 10 ^{-3}} = 50.135 \mathrm{Hz}$
サーボ角度 45 度
- Duty Cycle: $974 \mathrm{\mu s} \approx 0.97 \mathrm{ms}$
サーボ角度 90 度
- Duty Cycle: $1.446 \mathrm{ms} \approx 1.45 \mathrm{ms}$
サーボ角度 135 度
- Duty Cycle: $1.919 \mathrm{ms} \approx 1.92 \mathrm{ms}$
サーボ角度 180 度
- Duty Cycle: $2.393 \mathrm{ms} \approx 2.4 \mathrm{ms}$
ここでもう一度マイクロサーボSG-90のデータシートを確認すると
データシートに書かれたとおりの信号を与えている。
Position “0” (1.45 ms pulse) is middle, “90” (~2.4 ms pulse) is all the way to the right, “-90” (~ 0.5 ms pulse) is all the way left.
データシートには
- 1.45 ms pulse の時に「中心」
- 2.4 ms pulse の時に「右端」
- 0.5 ms pulse の時に「左端」 となっているが, 今回は時計回りいっぱいを角度0, 反時計回りいっぱいを角度180にした。