RL78/G10ファミリR5F10Y47ASPでマイクロサーボSG92Rを動かしてみた。

以前に STM32L010F4P6 マイコンで SG92R を動かしたが、同じことを R5F10Y47ASP マイコンでやってみる。

使うもの

マイクロサーボのデータシートを確認する

マイクロサーボ SG92R のデータシートそのものが見つからなかったので,
マイクロサーボ9g SG-90 のデータシートを参照する。

SG90 のデータシート

SG90 のデータシート

Specifications

とのこと。

つまりサーボコネクタの赤色線を電源の+に, 茶色線を GND に接続して橙色線に図に書かれている信号を入力すると動く。

e2 studio で新規プロジェクトを作る。

回路を作るのは後回しにして、e2 studio を起動して新規プロジェクトを作る。

新規 -> Renesas C/C++ Project -> Renesas RL78 から
Renesas CC-RL C/C++ Executable Project を選択して
ターゲット・デバイス: R5F10Y47 を選択して
Use 周辺コード作成にチェックを入れてプロジェクトを作る。

サーボの位置0度はデーターシートに書いてある通りオンデューティ 1.45ms で周期は 20ms だから1.4520×100=7.25%\frac{1.45}{20} \times 100 = 7.25 \%

保存して「コードを生成する」をクリックする。

端子配置図

に接続する。
(P00/P01 はマイコン内臓プルアップを SCL/SDA は液晶モジュールの変換基盤に載っているプルアップを利用した)

あと
サーボの VCC 入力(赤色)は 5V
サーボの GND 入力(茶色)は GND
ロータリーエンコーダの C 相(common)は GND

image01

プロジェクト設定

プロジェクト設定は 前前回と同じ。

GitHub リポジトリ

https://github.com/ak1211/R5F10Y47_servo

コード

詳細はGitHub リポジトリ で確認してください。

/src/cg_src/r_cg_userdefine.h

C
/* Start user code for function. Do not edit comment generated here */
#include <stdint.h>
#include <stdbool.h>
extern volatile bool gMILLISECONDS_TIMER_EVENT_OCCURRED_FLAG;
extern volatile int8_t gROTATION_COUNTER;
/* End user code. Do not edit comment generated here */
クリックして展開し、詳細を表示

タイマユニット /src/cg_src/r_cg_tau_user.c

C
/* Start user code for include. Do not edit comment generated here */
#include <stdbool.h>
/* End user code. Do not edit comment generated here */
クリックして展開し、詳細を表示

ロータリーエンコーダ読み取り部分はchan さんのサイトを参考にした

C
/* Start user code for global. Do not edit comment generated here */
volatile bool gMILLISECONDS_TIMER_EVENT_OCCURRED_FLAG = false;
//
#define INITIAL_TIMER_EVENT_FLAGS (0xFFU)
volatile static uint8_t gTIMER_EVENT_FLAGS = INITIAL_TIMER_EVENT_FLAGS;
//
#define MIN_ROTATION_COUNT  (-90)
#define MAX_ROTATION_COUNT  (90)
volatile int8_t gROTATION_COUNTER = 0;
//
static inline void countup_rotation_counter(void) {
    if (gROTATION_COUNTER < MAX_ROTATION_COUNT) {
        gROTATION_COUNTER++;
    }
}
//
static inline void countdown_rotation_counter(void) {
    if (gROTATION_COUNTER > MIN_ROTATION_COUNT) {
        gROTATION_COUNTER--;
    }
}
#define ENCORDER_B_BIT  (P0_bit.no0)
#define ENCORDER_A_BIT  (P0_bit.no1)
#define ENCORDER_AB_BIT  (P0 & 3)
volatile static uint8_t gCAPTURED_ENCORDER = 0U;
//
static void sample_encoder(void) {
    static const int8_t dir[] = { 0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1,
            1, 0 };
    static int8_t i;
    int8_t n;

    i = (i << 2) + ENCORDER_AB_BIT;
    n = dir[i & 15];
    if (n < 0) {
        countdown_rotation_counter();
    } else if (n > 0) {
        countup_rotation_counter();
    }
}

static void capture_encorder(void) {
    gCAPTURED_ENCORDER = (gCAPTURED_ENCORDER << 2) | ENCORDER_AB_BIT;
    gCAPTURED_ENCORDER &= 15;
    switch (gCAPTURED_ENCORDER) {
    case 0x7:
        countup_rotation_counter();
        break;
    case 0xd:
        countdown_rotation_counter();
        break;
    default:
        break;
    }
}
/* End user code. Do not edit comment generated here */
クリックして展開し、詳細を表示
C
static void __near r_tau0_channel2_interrupt(void) {
    /* Start user code. Do not edit comment generated here */
    sample_encoder();
    // タイマー割り込みを8分周する
    gTIMER_EVENT_FLAGS <<= 1;
    if (gTIMER_EVENT_FLAGS == 0U) {
        gMILLISECONDS_TIMER_EVENT_OCCURRED_FLAG = true;
        gTIMER_EVENT_FLAGS = INITIAL_TIMER_EVENT_FLAGS;
    }
    /* End user code. Do not edit comment generated here */
}
クリックして展開し、詳細を表示

/src/cg_src/r_cg_main.c

C
/* Start user code for include. Do not edit comment generated here */
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
/* End user code. Do not edit comment generated here */
クリックして展開し、詳細を表示
C
/* Start user code for global. Do not edit comment generated here */
// I2C LCD (AE-AQM1602A) のターゲット(スレーブ)アドレス
#define AQM1602A_I2C_TARGET_ADDRESS (0x3e)

// I2C LCD (AE-AQM1602A) 用送信バッファ
#define I2C_SEND_BUFFER_SIZE (2U)
static uint8_t gI2C_SEND_BUFFER[I2C_SEND_BUFFER_SIZE];

// 1ミリ秒 × multiply 遅延
void delay_milliseconds(uint16_t multiply) {
	while (multiply-- > 0U) {
		gMILLISECONDS_TIMER_EVENT_OCCURRED_FLAG = false;
		while (!gMILLISECONDS_TIMER_EVENT_OCCURRED_FLAG) {
			HALT();
		}
		R_WDT_Restart();
	}
}

// ミリ秒delay
static inline void delay(uint16_t milliseconds) {
	delay_milliseconds(milliseconds);
}

// I2C LCD (AE-AQM1602A)にコマンドを送信する
void AQM1602A_send_command(const uint8_t command_byte) {
	gI2C_SEND_BUFFER[0] = 0x00;
	gI2C_SEND_BUFFER[1] = command_byte;
	R_IICA0_Master_Send(AQM1602A_I2C_TARGET_ADDRESS << 1, gI2C_SEND_BUFFER,
	I2C_SEND_BUFFER_SIZE, 128U);
	delay(10);
}

// I2C LCD (AE-AQM1602A)に1文字送信する
void AQM1602A_send_data(const uint8_t byte) {
	gI2C_SEND_BUFFER[0] = 0x40;
	gI2C_SEND_BUFFER[1] = byte;
	R_IICA0_Master_Send(AQM1602A_I2C_TARGET_ADDRESS << 1, gI2C_SEND_BUFFER,
	I2C_SEND_BUFFER_SIZE, 128U);
	delay(1);
}

// I2C LCD (AE-AQM1602A)に文字列を送信する
void AQM1602A_puts(const char *p) {
	while (*p) {
		AQM1602A_send_data(*p++);
	}
}

// I2C LCD (AE-AQM1602A) クリアディスプレイ
void AQM1602A_clear_display(void) {
	AQM1602A_send_command(0x01);    // clear display
	delay(10);
}

// I2C LCD (AE-AQM1602A) ホーム(左上)に移動する
void AQM1602A_return_home(void) {
	AQM1602A_send_command(0x02);    // return home
	delay(10);
}

// I2C LCD (AE-AQM1602A)の初期化
void AQM1602A_init(void) {
	AQM1602A_send_command(0x38);    // function set
	delay(20);
	AQM1602A_send_command(0x39);    // function set
	delay(20);
	AQM1602A_send_command(0x14);    // internal osc frequency
	delay(20);
	AQM1602A_send_command(0x7A);    // contrast lower
	delay(20);
	AQM1602A_send_command(0x54);    // contrast higher / icon / power
	delay(20);
	AQM1602A_send_command(0x6C);    // follower control
	delay(20);
	//
	AQM1602A_send_command(0x38);    // function set
	delay(20);
	AQM1602A_send_command(0x01);    // clear display
	delay(20);
	AQM1602A_send_command(0x0C);    // display on
	delay(20);
}

// 値を文字列にする
void to_string(char *dst, int16_t value) {
	if (value < 0) {
		*dst++ = '-';
		value = -value;
	} else {
		*dst++ = ' ';
	}
	uint8_t xs[5];
	int8_t idx = 0;
	do {
		xs[idx++] = '0' + value % 10;
		value = value / 10;
	} while (idx < sizeof(xs) && value != 0);
	while (--idx >= 0) {
		*dst++ = xs[idx];
	}
	*dst = '\0';
}

static void set_servo_angle(int8_t degrees) {
	if (degrees < -90 || 90 < degrees) {
		return;
	}
	// PWM出力の周期
	uint16_t pwm_period = (TDR00H << 8 | TDR00L) + 1;
	//
	// SG-90 / SG-92R
	// center: 1.45ms (on duty) / 20ms(period) * 100 = 7.25%
	// -90 deg: 0.5ms (on duty) / 20ms(period) * 100 = 2.5%
	// 90 deg: 2.4ms (on duty) / 20ms(period) * 100 = 12.0%
	//
	const float center = 1.45f / 20.0f;
	float on_duty;
	if (degrees == 0) {
		on_duty = center;
	} else {
		on_duty = center + ((2.4f - 0.5f) / 20.0f) * (float) degrees / 180.0f;
	}
	//
	uint16_t tdr = pwm_period * on_duty;
	TDR01H = tdr >> 8;
	TDR01L = tdr & 0xFF;
}
/* End user code. Do not edit comment generated here */
クリックして展開し、詳細を表示
C
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 R5F10Y47ASP");
	//
	gROTATION_COUNTER = 0;
	while (1U) {
		// 2行目
		char message[17];
		strcpy(&message[0], "Angle = ");
		to_string(&message[8], gROTATION_COUNTER);
		strncat(message, " deg ", 5);
		//
		AQM1602A_send_command(0x80 | 0x40); // アドレス設定
		AQM1602A_puts(message);
		//
		set_servo_angle(gROTATION_COUNTER);
		delay(100);
	}
	/* End user code. Do not edit comment generated here */
}
クリックして展開し、詳細を表示
C
static void R_MAIN_UserInit(void) {
	/* Start user code. Do not edit comment generated here */
	R_TAU0_Channel0_Start();
	R_TAU0_Channel2_Start();
	EI();
	/* End user code. Do not edit comment generated here */
}
クリックして展開し、詳細を表示

Renesas Flash Programmer で書き込む

rfp01

実行

当たり前だけどデータシート通りですね。

サーボ角度-90 度

サーボ角度-45 度

サーボ角度 0 度(center)

サーボ角度 45 度

サーボ角度 90 度

著作権表示

著者: Akihiro Yamamoto

リンク: https://ak1211.com/posts/7739/

ライセンス: CC BY-NC-SA 4.0

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Please attribute the source, use non-commercially, and maintain the same license.

コメント

検索を開始

キーワードを入力して記事を検索

↑↓
ESC
⌘K ショートカット