前回で
コード生成の準備が整ったのでコード生成でPWMを設定してみます。
関連:
・
コード生成(準備編)・
コード生成(AD編)・
コード生成(UART編)・I2Cスレーブ(
現象、
対策)
今回は
10Y47でPWMを3ポート分出力する設定を行います。
RL78G10の場合、PWMのマスタとスレーブを設定します。
マスタ:
繰り返し周期などの全体の設定
スレーブ:実際に出力するデューティー比の設定
となっている様です。
マスタは周期等の管理を行うだけでPWMの波形は出力されません。
PWMの周波数を独立して設定する場合は
PWM出力(マスタ)PWM出力(スレーブ)の組み合わせを使います。
10Y47の場合、PWM出力(スレーブ)は最大2ポートになります。
なるべくPWM出力のポート数を稼ぎたい場合は
多重PWM出力(マスタ)、多重PWM出力(スレーブ)の組み合わせを使います。
10Y47の場合、多重PWM出力(スレーブ)は最大3ポートになります。
出力端子の設定
なるべくPWM出力のポート数を稼ぎたいので、多重PWMとして設定します。
コード生成→周辺機能→タイマ・アレイ・ユニット→一般設定のタブから
多重PWM出力(マスタ)、多重PWM出力(スレーブ)×3に設定します。
周期などが標準状態に設定されるので、好みに合わせて設定を変更します。
チャンネル0がマスターなので、チャンネル0のタブから各種設定を行います。
PWM周期1kHz、初期状態のデューティー比を25%、50%、75%に設定してみます。
PWM周期・チャンネル0(マスタ):1kHz→1ms
デューティー・チャンネル1(スレーブ):25%
・チャンネル2(スレーブ):50%
・チャンネル3(スレーブ):75%
コード生成を実行すればPWMの基本設定ができます。
生成されたコードの実行
ここで生成されたコードを見てみます。
ファイル→コード生成→
r_cg_tau.c PWMのコード生成により3つの関数が作成されました。
void
R_TAU0_Create(void)
PWMの初期化関数です
SH等で作成したinit_pwm()に相当します。
void
R_TAU0_Channel0_Start(void)
PWMの処理開始関数です。
void
R_TAU0_Channel0_Stop(void)
PWMの処理開始停止です。
ハードウェアマニュアルを見ると
R_TAU0_Create→
R_TAU0_Channel0_Startの順で実行するように書いてありますが、
R_TAU0_Createは
rg_cg_systemint.c内
R_Systeminitで実行されるように自動的に
設定されます。
また、
R_Systeminitは
hdwinitで実行され、
hdwinitもどこかで自動実行されるようです。
という事で、R_TAU0_Channel0_Startを実行すれば1kHzで
チャンネル1→TO01→P04 デューティー比25%
チャンネル2→TO02→P05 デューティー比50%
チャンネル3→TO03→P07 デューティー比75%
が出力されるようになります。
自作メイン関数でR_TAU0_Channel0_Startを実行させます。
<設定>
#include "iodefine.h"
//端子の基本設定読み込み#include "r_cg_macrodriver.h"
//uint16_tとか読み込み#include "r_cg_wdt.h"
//ウォッチドッグタイマー#include "r_cg_tau.h" //PWM設定void user_main(void){
R_TAU0_Channel0_Start(); //PWM開始 while(1){
R_WDT_Restart();
//ウォッチドッグタイマーリセット }
}
ビルドして書込んで(
E1,
シリアル)実行し、TO01からデューティー比25%でPWMが出力されれば
ひとまず成功です。
デューティー比の変更
デューティー比固定では使えないので、デューティー比を変更してみます。
コード生成機能がしてくれるのはここまでです。
ここから先はハードウェアマニュアル等を確認しながらの作業が必要です。
ルネサスのページからRL78/G10の資料をダウンロードしておきます。
https://www.renesas.com/jp/ja/products/microcontrollers-microprocessors/rl78/rl78g1x/rl78g10.html
※ダウンロードには登録(無料)が必要です。
r_cg_tau.cの
R_TAU0_Create内で初期設定されているので見てみます。
PWMの周期
TDR00H = _
4E_TAU_TDR00H_VALUE;
TDR00L = _
1F_TAU_TDR00L_VALUE;
に対して、PWM1のON時間
TDR01H = _
13_TAU_TDR01H_VALUE;
TDR01L = _
88_TAU_TDR01L_VALUE;
でデューティー比が決まります。
周期
0x4E1F(19999)に対して
0x1388(5000)でデューティー比が25%に設定されています。
従って、TDR0*HおよびTDR0*Lを変更すればデューティー比を変更できます。
これに基づいて、チャンネル1のデューティー比を変更してみます。
<設定>
#include "iodefine.h"
//端子の基本設定読み込み#include "r_cg_macrodriver.h"
//uint16_tとか読み込み#include "r_cg_wdt.h"
//ウォッチドッグタイマー
#include "r_cg_tau.h"
//PWM設定void user_main(
void){
unsigned short duty1;
//デューティー比 0-1000 unsigned long pwm_out1;
//出力 unsigned short pwm_max=
TDR00H*0x100+
TDR00L;
R_TAU0_Channel0_Start();
//PWM開始 while(1){
duty1=700;
//デューティー比を設定(70%) pwm_out1=(long)duty1*pwm_max/1000;
//設定値を計算 pwm_out1=(pwm_out1>pwm_max)?pwm_max:pwm_out1;
//設定上限 TDR01H=(
unsigned char)(pwm_out1/0x100);
//上位8bitを格納 TDR01L=(
unsigned char)(pwm_out1&0x00FF);
//下位8bitを格納 R_WDT_Restart();
//ウォッチドッグタイマーリセット }
}
これでデューティー比を変更可能になりました。
チャンネル2、チャンネル3を設定する場合は同様の方法で
チャンネル2:TDR02H、TDR02L
チャンネル3:TDR03H、TDR03L
へ設定値を書き込みます。
あとは必要に応じて関数化して使いましょう。
<関数化の例>(2020/08/15変数型式修正)
#include "iodefine.h"
//端子の基本設定読み込み#include "r_cg_macrodriver.h"
//uint16_tとか読み込み#include "r_cg_tau.h"
//PWM設定void set_pwm(
int port,
short duty){
unsigned short pwm_out;
//設定値 unsigned short pwm_max=
TDR00H*0x100+
TDR00L;
//最大値 volatile unsigned char *pwm_H;
//H格納先(ポインタ) volatile unsigned char *pwm_L;
//L格納先(ポインタ) switch(port){
case 1:
pwm_H=&
TDR01H;
//格納先を指定 pwm_L=&
TDR01L;
//格納先を指定 break;
case 2:
pwm_H=&
TDR02H;
//格納先を指定 pwm_L=&
TDR02L;
//格納先を指定 break;
case 3:
pwm_H=&
TDR03H;
//格納先を指定 pwm_L=&
TDR03L;
//格納先を指定 break;
default:
//該当なし return;
}
pwm_out=(
unsigned short)((
long)duty*pwm_max/1000);
//設定値を計算 pwm_out=(pwm_out>pwm_max)?pwm_max:pwm_out;
//設定上限 *pwm_H=(
unsigned char)(pwm_out/0x100);
//上位8bitを格納 *pwm_L=(
unsigned char)(pwm_out&0x00FF);
//下位8bitを格納}
<実行例>#include "iodefine.h"
//端子の基本設定読み込み#include "r_cg_macrodriver.h"
//uint16_tとか読み込み#include "r_cg_wdt.h"
//ウォッチドッグタイマー
void user_main(
void){
unsigned short duty1,duty2,duty3;
//デューティー比 0-1000 R_TAU0_Channel0_Start();
//PWM開始 while(1){
duty1=250;
//デューティー比を設定 duty2=500;
//デューティー比を設定 duty3=750;
//デューティー比を設定 set_pwm(1,duty1);
//TO01へPWM出力 set_pwm(2,duty2);
//TO02へPWM出力 set_pwm(3,duty3);
//TO03へPWM出力 R_WDT_Restart();
//ウォッチドッグタイマーリセット }
}
タイマー機能
SHおよびRXはPWMと
タイマーが独立した機能になっていました。
RL78(G10)はPWMもタイマーもTAU(タイマ・アレイ・ユニット)へ集約されています。
で、PWMを優先して使用すると純粋なタイマーへ割り振るカウンターが残りません。
しかし、タイマーが無いとそれはそれで困ります。
そこで、チャンネル0のカウント機能をタイマー機能として使用します。
PWMの周期設定で周期を1msに設定した場合、1msごとにチャンネル0の割り込み
が発生します。コード生成を利用するとr_cg_tau_user.cの
r_tau0_channel0_interruptとして割込み機能が生成されています。
r_tau0_channel0_interruptへ1msごとに実行したい処理を記述しておきます。
今まで通り_systimerを作成し、システム時刻として利用します。
とりあえず内容はr_cg_tau_user.cへ記述していきます。
<設定>(2020/08/15誤記修正)
/********************************************************************************************Global variables and functions*********************************************************************************************//* Start user code for global. Do not edit comment generated here */static unsigned long _systime=0;/* End user code. Do not edit comment generated here */・
・
・
static void __near r_tau0_channel0_interrupt(
void)
{
/* Start user code. Do not edit comment generated here */ _systime++; //1msを前提とした場合 /* End user code. Do not edit comment generated here */}
・
・
・
/* Start user code for adding. Do not edit comment generated here */unsigned long getsystime(void){ return _systime;}/* End user code. Do not edit comment generated here */プロトタイプ宣言はとりあえず
r_cg_tau.hに書いておきます。
/* Start user code for function. Do not edit comment generated here */unsigned long getsystime(void);/* End user code. Do not edit comment generated here */これでシステム時刻を利用できます。
とりあえずr_cg_tau_user.cへ記述しましたが、作成した関数を他のプロジェクトでも
流用したい場合は自動生成されるr_cg_tau_user.cを移植するのは危険なので、各種
自作関数は
自作のソースファイルで管理し、
実行したい処理をまとめた自作関数を
自動生成された割込み関数で実行するのが良いと思います。
周期を
20ms等に設定した場合は割り込みの発生が20msなのでそのままでは
1msの分解能
を得る事ができませんが、内部カウント値
TCR00H,
TCR00Lの情報を利用すれば1ms程度
の分解能を得ることもできます。