サーボを動作させてみる
サーボを動作させてみる
SG-90小型サーボをESP32に接続してみました。
回路図
SG-90くらいの小型サーボならESP32 DEVKITの3.3Vに接続すれば動きます。回路図もご覧の通り簡単です。繋げるだけ。
SG-90小型サーボについて
20msのパルス信号で動作します。
0.5ms~2.5msの範囲のON時間でサーボが180°稼働します。
実際には調整できるように180°より可動範囲は大きいですがそこはトリムなりパルス幅の最小・最大値を制限するなりして調整します。
サーボから伸びている配線の色が説明とちょっと違ってるみたい。
(赤とかないんですけど)
色 | 説明 |
---|---|
茶色 | GND |
オレンジ | 電源 |
黄色 | 信号線 |
目の錯覚かな、写真で見ると説明書通りにオレンジが赤、黄色がオレンジに見える。
プログラム
main.cを以下のようにします。
PWM用の初期設定が面倒・・・。
#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "nvs_flash.h"
#include "driver/mcpwm_prelude.h"
#include "driver/gpio.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_log.h"
#define TAG "SERVO"
// PWM用パラメータ
#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000 // 1MHz, 1us per tick
#define SERVO_TIMEBASE_PERIOD 20000 // 20000 tick, 20ms
// PWM用
int groupID;
mcpwm_timer_handle_t timer;
mcpwm_oper_handle_t oper;
mcpwm_cmpr_handle_t comparator;
mcpwm_gen_handle_t generator;
// ボリューム用
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_chan_cfg_t adc1_config = {
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_12, // 12bit = 0xFFF = 4,095 0~4,095に変換されます。
};
void init() {
ESP_LOGI(TAG, "Init(S)");
groupID = 0;
// AIN2初期化 PWMパルス信号
timer = NULL;
mcpwm_timer_config_t timer_config = {
.group_id = groupID,
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
.resolution_hz = SERVO_TIMEBASE_RESOLUTION_HZ,
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
.period_ticks = SERVO_TIMEBASE_PERIOD,
};
esp_err_t ret = mcpwm_new_timer(&timer_config, &timer);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_new_timer error %d", ret);
return;
}
ESP_LOGI(TAG, "init(2)");
oper = NULL;
mcpwm_operator_config_t operator_config = {
.group_id = groupID,
};
ret = mcpwm_new_operator(&operator_config, &oper);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_new_operator error %d", ret);
return;
}
ret = mcpwm_operator_connect_timer(oper, timer);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_operator_connect_timer error %d", ret);
return;
}
ESP_LOGI(TAG, "init(3)");
comparator = NULL;
mcpwm_comparator_config_t comparator_config = {
.flags.update_cmp_on_tez = true,
};
ret = mcpwm_new_comparator(oper, &comparator_config, &comparator);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_new_comparator error %d", ret);
return;
}
ESP_LOGI(TAG, "init(4)");
generator = NULL;
mcpwm_generator_config_t generator_config = {
.gen_gpio_num = CONFIG_SERVO_PIN,
};
ret = mcpwm_new_generator(oper, &generator_config, &generator);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_new_generator error %d", ret);
return;
}
if (generator == NULL) {
ESP_LOGE(TAG, "mcpwm_new_generator error");
}
ESP_LOGI(TAG, "init(5)");
ret = mcpwm_comparator_set_compare_value(comparator, 0);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_comparator_set_compare_value error %d", ret);
return;
}
ESP_LOGI(TAG, "init(6)");
ret = mcpwm_generator_set_action_on_timer_event(generator,
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_generator_set_actions_on_timer_event error %d", ret);
return;
}
ESP_LOGI(TAG, "init(7)");
ret = mcpwm_generator_set_action_on_compare_event(generator,
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator, MCPWM_GEN_ACTION_LOW));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_generator_set_actions_on_compare_event error %d", ret);
return;
}
ESP_LOGI(TAG, "init(8)");
ret = mcpwm_timer_enable(timer);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_timer_enable error %d", ret);
return;
}
ESP_LOGI(TAG, "init(9)");
ret = mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_timer_start_stop error %d", ret);
return;
}
// ボリューム用ADコンバータ初期化
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT_2,
.ulp_mode = ADC_ULP_MODE_DISABLE,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL_0, &adc1_config));
ESP_LOGI(TAG, "Init(E)");
}
void app_main(void)
{
nvs_flash_init(); // Flash初期化 (お約束のようなもの)
init();
int maxval = (1 << 12) - 1;
while(1) {
int val;
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC_CHANNEL_0, &val));
// サーボのパルスは20ms(20000)の内0.5~2.5ms(500~2500)の間で-90°~90°の回転になります。0.5~2.5msはサーボ個体によって微調整が必要になるかも?
// 以下は手持ちのサーボで丁度180°になるように設定してます。
const int min_pulse = 608;
const int max_pulse = 2352;
int servo = (int)((double)val / (double)maxval * (max_pulse - min_pulse) + min_pulse);
// int bitwidth = (int)adc1_config.bitwidth;
// double d = val * 3.3 / (pow(2, bitwidth) - 1);
// ESP_LOGI(TAG, "volume = %d(%lfv), servo=%d", val, d, servo);
// サーボ
esp_err_t ret;
ret = mcpwm_comparator_set_compare_value(comparator, servo);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "mcpwm_comparator_set_compare_value error %d", ret);
}
// 10ms待機
vTaskDelay(pdMS_TO_TICKS(10));
}
}
複数個接続する場合はもっと容量の大きなレギュレータを使わないとダメかな?
DEVKITだと2個くらいなら動かせました。3個以上はためしてない。
コメント
コメントを投稿