DCモーターを可変速度制御してみた。
使うもの
実験回路をブレッドボードに作る
前回までのソフトウェアを再利用するので、今回は実験回路を先に作る。
AE-TB67H450回路図
TB67H450FNGは、PWMチョッパ方式のDCブラシモータドライバです。
TB67H450データシート
実験回路図
FA-130RAは電圧範囲1.5V~3VだけどTB67H450のモーター電源電圧下限が4.5Vなので電源は単3ニッケル水素充電池4本直列にする。
モーターの定格電圧より供給電圧が高いけど、モータードライバーの電流制限を1A位にしておけば(モーターストールしていなければ)大丈夫だろうと。
おもに モータのブラシで発生するノイズ
で制御回路の動作不調を引き起こすので、
それを抑えるためにモーターの端子間とケース間はセラミックコンデンサーで高周波を短絡させておく。
VREFには電源電圧の半分を与えるので、TB67H450データシートにある式で制限電流を計算すると
$$
I_{out} = 0.1 \times \frac{2.5}{0.22} \approx 1.1 \mathrm{A}
$$
この程度ならモーター焼損しないだろうと。
実験回路
これはIN1,IN2ともにHを入力してショートブレーキをかけている状態。
この状態で
- IN1をH, IN2をLの信号を入力するとモーターが正転
- IN1をL, IN2をHの信号を入力するとモーターが逆転
- IN1をH, IN2をHの信号を入力するとショートブレーキ
になることを確認しておく。
e2 studioで新規プロジェクトを作る。
新規 -> Renesas C/C++ Project -> Renesas RL78 から
Renesas CC-RL C/C++ Executable Project を選択して
ターゲット・デバイス: R5F10Y47 を選択して
Use 周辺コード作成にチェックを入れてプロジェクトを作る。
これはそのまま確定する。
クロックは基盤に20MHzセラロックが載っているが、今回はオンチップオシレータ20MHzを選択する。
周辺機能のシリアル・インターフェースIICAの転送モードはシングルマスタ
設定はそのままでいい
PWMのためにタイマーチャネル0を多重PWM出力(マスタ)、タイマーチャネル1を多重PWM出力(スレーブ)、タイマーチャネル2を多重PWM出力(スレーブ)にする。
チャネル0の周期は1msつまりPWM周波数1,000Hz
チャネル1とチャネル2のデューティは共に0%
ロータリーエンコーダ読み取りとディレイのために12ビットインターバルタイマーを使用する。
ロータリーエンコーダを接続するポートP00, P01を入力に設定して内蔵プルアップにチェックを入れる。
保存して「コードを生成する」をクリックする。
端子配置図を確認する。
- VSS(7番ピン)をGND
- VDD(8番ピン)を5V
- TOOL0(2番ピン)を書き込み器
- RESET#(3番ピン)を書き込み器
- SCLA0(15番ピン)を液晶モジュールのSCL
- SDAA0(16番ピン)を液晶モジュールのSDA
- TO02(14番ピン)をTB67H450モータードライバーのIN2
- TO01(13番ピン)をTB67H450モータードライバーのIN1
- P00(9番ピン)をロータリーエンコーダのB相出力
- P01(10番ピン)をロータリーエンコーダのA相出力
に接続する。
(P00/P01はマイコン内臓プルアップをSCL/SDAは液晶モジュールの変換基盤に載っているプルアップを利用した)
プロジェクト設定
プロジェクト設定は 前前前回と同じ。
GitHubリポジトリ
https://github.com/ak1211/R5F10Y47_dcmotor
コード
詳細はGitHubリポジトリ で確認してください。
/src/cg_src/r_cg_main.c
r_cg_main.cコードの一部を貼っておきます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| static void set_speed(int8_t speed) {
if (speed < -100 || 100 < speed) {
return;
}
// PWM出力の周期
uint32_t pwm_period = (TDR00H << 8 | TDR00L) + 1;
//
uint16_t forward = 0;
uint16_t backward = 0;
if (speed < 0) {
forward = 0;
backward = pwm_period * (uint32_t) (-speed) / 100;
} else if (speed > 0) {
forward = pwm_period * (uint32_t) speed / 100;
backward = 0;
}
TDR01H = (forward >> 8) & 0xFF;
TDR01L = forward & 0xFF;
TDR02H = (backward >> 8) & 0xFF;
TDR02L = backward & 0xFF;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| void main(void)
{
R_MAIN_UserInit();
/* Start user code. Do not edit comment generated here */
// 100ms待つ
delay(100);
// LCDの初期化
AQM1602A_init();
// 1行目
AQM1602A_puts("RL78 DC motor");
//
gROTATION_COUNTER = 0;
while (1U) {
// 2行目
char message[17];
strcpy(&message[0], "ON duty = ");
to_string(&message[8], gROTATION_COUNTER);
strncat(message, " % ", 5);
//
AQM1602A_send_command(0x80 | 0x40); // アドレス設定
AQM1602A_puts(message);
//
set_speed(gROTATION_COUNTER);
//
delay(100);
}
/* End user code. Do not edit comment generated here */
}
|
1
2
3
4
5
6
7
8
| static void R_MAIN_UserInit(void)
{
/* Start user code. Do not edit comment generated here */
R_IT_Start();
R_TAU0_Channel0_Start();
EI();
/* End user code. Do not edit comment generated here */
}
|
Renesas Flash Programmerで書き込む
実行
IN1/IN2(TB67H450)の入力
チャネル CH1(黄色): IN1 / CH2(緑色): IN2
マイナスのデューティは逆転と読み替えてください。
- オンデューティ10%
- オンデューティ25%
- オンデューティ50%
- オンデューティ75%
- オンデューティ100%
- オンデューティ-10%
- オンデューティ-25%
- オンデューティ-50%
- オンデューティ-75%
- オンデューティ-100%
電流波形
PWMのOFF期間中モーター電流を電源に回生しているので、トルクがよわよわ。
PWM出力によるブラシ付DCモーターの駆動:PWM駆動時の電流回生方法
- CH1(黄色): IN1の信号(IN2はLow固定)
- CH2(緑色): 回路図の抵抗RSの電圧 ≒ モーターと電源間の電流
ON時に上昇したモーター電流がOFF時に逆向きの電流になって電源に回生されている。
SlowDecay方式
トルクが弱いので /src/cg_src/r_cg_main.c をSlowDecayになるように変更する。
OFF時にモーター端子間に電流を流すことで、モーター電流をできるだけ維持するように制御する方式。
具体的にはIN1/IN2のロジックを反転する。
PWM出力によるブラシ付DCモーターの駆動:PWM駆動時の電流回生方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
| static void set_speed_fast_decay(int8_t speed) {
if (speed < -100 || 100 < speed) {
return;
}
// PWM出力の周期
uint32_t pwm_period = (TDR00H << 8 | TDR00L) + 1;
if (speed > 0) {
//
// 正転
// IN1: PWM(Active High)
// IN2: Low
//
// タイマー出力論理(TO01/TO02共にアクティブハイ)
TOL0 &= (uint8_t)~0x06;
// TO01デューティ
uint32_t timer = pwm_period * (uint32_t)speed / 100;
TDR01H = (timer >> 8) & 0xFF;
TDR01L = timer & 0xFF;
// TO02デューティ
TDR02H = TDR02L = 0; // 出力Low固定
} else if (speed < 0) {
speed = -speed;
//
// 逆転
// IN1: Low
// IN2: PWM(Active High)
//
// タイマー出力論理(TO01/TO02共にアクティブハイ)
TOL0 &= (uint8_t)~0x06;
// TO01デューティ
TDR01H = TDR01L = 0; // 出力Low固定
// TO02デューティ
uint32_t timer = pwm_period * (uint32_t)speed / 100;
TDR02H = (timer >> 8) & 0xFF;
TDR02L = timer & 0xFF;
} else {
// タイマー出力論理(TO01/TO02共にアクティブハイ)
TOL0 &= (uint8_t)~0x06;
// TO01デューティ
TDR01H = TDR01L = 0;
// TO02デューティ
TDR02H = TDR02L = 0;
}
}
static void set_speed_slow_decay(int8_t speed) {
if (speed < -100 || 100 < speed) {
return;
}
// PWM出力の周期
uint32_t pwm_period = (TDR00H << 8 | TDR00L) + 1;
if (speed > 0) {
//
// 正転
// IN1: High
// IN2: PWM(Active Low)
//
// タイマー出力論理(TO01/TO02共にアクティブロー)
TOL0 |= (uint8_t)0x06;
// TO01デューティ
TDR01H = TDR01L = 0; // 出力High固定
// TO02デューティ
uint32_t timer = pwm_period * (uint32_t)speed / 100;
TDR02H = (timer >> 8) & 0xFF;
TDR02L = timer & 0xFF;
} else if (speed < 0) {
speed = -speed;
//
// 逆転
// IN1: PWM(Active Low)
// IN2: High
//
// タイマー出力論理(TO01/TO02共にアクティブロー)
TOL0 |= (uint8_t)0x06;
// TO01デューティ
uint32_t timer = pwm_period * (uint32_t)speed / 100;
TDR01H = (timer >> 8) & 0xFF;
TDR01L = timer & 0xFF;
// TO02デューティ
TDR02H = TDR02L = 0; // 出力High固定
} else {
// タイマー出力論理(TO01/TO02共にアクティブハイ)
TOL0 &= (uint8_t)~0x06;
// TO01デューティ
TDR01H = TDR01L = 0;
// TO02デューティ
TDR02H = TDR02L = 0;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| void main(void)
{
R_MAIN_UserInit();
/* Start user code. Do not edit comment generated here */
// 100ms待つ
delay(100);
// LCDの初期化
AQM1602A_init();
//
gROTATION_COUNTER = 0;
while (1U) {
// 2行目
char message[17];
strcpy(&message[0], "ON duty = ");
to_string(&message[8], gROTATION_COUNTER);
strncat(message, " % ", 5);
//
AQM1602A_send_command(0x80 | 0x40); // アドレス設定
AQM1602A_puts(message);
// ポート2でfast decay / slow decay を切り替える
if(P0_bit.no2) {
set_speed_slow_decay(gROTATION_COUNTER);
// 1行目
AQM1602A_send_command(0x80); // アドレス設定
AQM1602A_puts("RL78 DC motor(S)");
} else {
set_speed_fast_decay(gROTATION_COUNTER);
// 1行目
AQM1602A_send_command(0x80); // アドレス設定
AQM1602A_puts("RL78 DC motor(F)");
}
//
delay(100);
}
/* End user code. Do not edit comment generated here */
}
|
実行
明らかにトルクが増えた。
IN1/IN2の出力波形(Slow Decay)
チャネル CH1(黄色): IN1 / CH2(緑色): IN2
マイナスのデューティは逆転と読み替えてください。
- オンデューティ10%
- オンデューティ25%
- オンデューティ50%
- オンデューティ75%
- オンデューティ100%
- オンデューティ-10%
- オンデューティ-25%
- オンデューティ-50%
- オンデューティ-75%
- オンデューティ-100%
電流波形(Slow Decay)
- CH1(黄色): IN2の信号(IN1はHigh固定)
- CH2(緑色): 回路図の抵抗RSの電圧 ≒ モーターと電源間の電流
OFF期間中に抵抗RSを通じて電源に流れる電流は無い。
ON期間中に増加したモーター電流をOFF期間中にモータ端子間に回生する制御によって、常にモーター電流が流れ続ける状態を作り、そのことでトルクが増えるということ。