https://github.com/espressif/esp-idf/bl ... ple_main.c
Since I'm using the ESP32, the RMT module does not support the parameter:
Code: Select all
rmt_transmit_config_t tx_config = {
.loop_count = 5000 // ERROR on ESP32
}1) Run the stepper motor for N steps (user-defined);
2) Know when the rotation is complete (programmatically, without a physical encoder);
3) Do this without blocking code execution using rmt_tx_wait_all_done.
Question: How do I properly implement the constant-speed rotation phase on the ESP32 so that a callback is triggered after the movement completes (acceleration + constant speed + deceleration)?
Current issue: In my current code, only the case without a constant-speed phase works correctly. Using a loop is not suitable because I would need to add a counter in the callback to track how many steps have been taken. This seems cumbersome and inconvenient.
Initialization RMT:
Code: Select all
#define STEP_MOTOR_RESOLUTION_HZ 1000000
#define SAMPLE_POINTS_ACDEC 300
#define LOW_FREQ_HZ 50
#define MAIN_FREQ_HZ 1000
#define SYMBOL_DURATION ((uint32_t)(STEP_MOTOR_RESOLUTION_HZ / MAIN_FREQ_HZ / 2))
#define BLOCK_SIZE 4000
rmt_channel_handle_t motor_chan = NULL;
rmt_encoder_handle_t accel_motor_encoder = NULL;
rmt_encoder_handle_t decel_motor_encoder = NULL;
rmt_encoder_handle_t uniform_motor_encoder = NULL;
rmt_encoder_handle_t copy_encoder = NULL;
rmt_copy_encoder_config_t copy_encoder_config_t = {};
static stepper_motor_curve_encoder_config_t decel_encoder_config = {
.resolution = STEP_MOTOR_RESOLUTION_HZ,
.sample_points = SAMPLE_POINTS_ACDEC,
.start_freq_hz = MAIN_FREQ_HZ,
.end_freq_hz = LOW_FREQ_HZ,
};
static stepper_motor_uniform_encoder_config_t uniform_encoder_config = {
.resolution = STEP_MOTOR_RESOLUTION_HZ,
};
static stepper_motor_curve_encoder_config_t accel_encoder_config = {
.resolution = STEP_MOTOR_RESOLUTION_HZ,
.sample_points = SAMPLE_POINTS_ACDEC,
.start_freq_hz = LOW_FREQ_HZ,
.end_freq_hz = MAIN_FREQ_HZ,
};
ESP_LOGI(TAG_RMT, "Create RMT TX channel");
rmt_tx_channel_config_t tx_chan_config = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.gpio_num = STEP_MOTOR,
.mem_block_symbols = 64,
.resolution_hz = STEP_MOTOR_RESOLUTION_HZ,
.trans_queue_depth = 10,
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &motor_chan));
ESP_LOGI(TAG_RMT, "Create motor encoders");
ESP_ERROR_CHECK(rmt_new_copy_encoder(©_encoder_config_t, ©_encoder));
ESP_ERROR_CHECK(rmt_new_stepper_motor_curve_encoder(&accel_encoder_config, &accel_motor_encoder));
ESP_ERROR_CHECK(rmt_new_stepper_motor_uniform_encoder(&uniform_encoder_config, &uniform_motor_encoder));
ESP_ERROR_CHECK(rmt_new_stepper_motor_curve_encoder(&decel_encoder_config, &decel_motor_encoder));
ESP_LOGI(TAG_RMT, "Enable RMT channel");
ESP_ERROR_CHECK(rmt_enable(motor_chan));
xRotationDone = xSemaphoreCreateBinary();
Code: Select all
rmt_tx_event_callbacks_t cbs = {
.on_trans_done = tx_done_callback
};
ESP_ERROR_CHECK(rmt_tx_register_event_callbacks(motor_chan, &cbs, NULL));
bool tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (xRotationDone)
{
xSemaphoreGiveFromISR(xRotationDone, &xHigherPriorityTaskWoken);
}
return xHigherPriorityTaskWoken == pdTRUE;
}
Code: Select all
bool rmt_rotate_degree(float degrees, int rotation_dir)
{
if ((motor_chan == NULL) || (accel_motor_encoder == NULL) || (decel_motor_encoder == NULL) || (uniform_motor_encoder == NULL))
{
ESP_LOGE("STEPPER", "Motor not initialized!");
return false;
}
if (degrees < 0.1f)
{
ESP_LOGW("STEPPER", "Rotation angle too small: %.2f degrees", degrees);
return false;
}
gpio_set_level(DIR_MOTOR, rotation_dir);
uint32_t total_steps = (uint32_t)(degrees * STEPS_PER_DEGREE);
rmt_transmit_config_t tx_config = {
.loop_count = 0,
.flags = {
.eot_level = 1
}};
if (total_steps <= (SAMPLE_POINTS_ACDEC * 2))
{
uint32_t half_steps = total_steps / 2;
uint32_t remain_steps = total_steps - half_steps;
ESP_LOGI("STEPPER", "Rotating %.2f degrees. Direction %s. Summary %lu steps: accel steps %lu, decel steps %lu", degrees, rotation_dir == DIR_CLOCKWISE ? "CLOCKWISE" : "COUNTERCLOCKWISE", total_steps, half_steps, remain_steps);
ESP_ERROR_CHECK(rmt_transmit(motor_chan, accel_motor_encoder, &half_steps, sizeof(uint32_t), &tx_config)); /// - разгон;
ESP_ERROR_CHECK(rmt_transmit(motor_chan, decel_motor_encoder, &remain_steps, sizeof(uint32_t), &tx_config)); /// - торможение;
}
else
{
rmt_symbol_word_t *uniform_block = NULL;
uint32_t uniform_steps = total_steps - (SAMPLE_POINTS_ACDEC * 2);
ESP_LOGI(TAG_RMT, "SYMBOL_DURATION = %lu, expected = %lu", SYMBOL_DURATION, STEP_MOTOR_RESOLUTION_HZ / MAIN_FREQ_HZ / 2);
ESP_LOGI("STEPPER", "Rotating %.2f degrees. Direction %s. Summary %lu steps: accel steps %lu, uniform steps %lu, decel steps %lu", degrees, rotation_dir == DIR_CLOCKWISE ? "CLOCKWISE" : "COUNTERCLOCKWISE", total_steps, SAMPLE_POINTS_ACDEC, uniform_steps, SAMPLE_POINTS_ACDEC);
uniform_block = malloc((BLOCK_SIZE * 2 + 1) * sizeof(rmt_symbol_word_t));
if (uniform_block == NULL)
{
ESP_LOGE("STEPPER", "Failed to allocate memory for block!");
return;
}
if (uniform_block)
{
for (uint32_t i = 0; i < BLOCK_SIZE; i++)
{
uniform_block[i * 2].level0 = 0;
uniform_block[i * 2].duration0 = SYMBOL_DURATION;
uniform_block[i * 2].level1 = 1;
uniform_block[i * 2].duration1 = SYMBOL_DURATION;
}
}
uniform_block[BLOCK_SIZE * 2].level0 = 0;
uniform_block[BLOCK_SIZE * 2].duration0 = 0;
uniform_block[BLOCK_SIZE * 2].level1 = 0;
uniform_block[BLOCK_SIZE * 2].duration1 = 0;
ESP_ERROR_CHECK(rmt_transmit(motor_chan, accel_motor_encoder,
&accel_encoder_config.sample_points,
sizeof(accel_encoder_config.sample_points),
&tx_config));
for (uint32_t remaining = uniform_steps; remaining > 0; remaining -= BLOCK_SIZE)
{
uint32_t this_block = (remaining < BLOCK_SIZE) ? remaining : BLOCK_SIZE;
rmt_transmit(motor_chan, copy_encoder,
uniform_block,
(this_block * 2 + 1) * sizeof(rmt_symbol_word_t),
&tx_config);
}
ESP_ERROR_CHECK(rmt_transmit(motor_chan, decel_motor_encoder,
&decel_encoder_config.sample_points,
sizeof(decel_encoder_config.sample_points),
&tx_config));
}
return true;
}
Code: Select all
rmt_rotate_degree(2, DIR_CLOCKWISE);
xSemaphoreTake(xRotationDone, portMAX_DELAY);