Skip to content

RMT - Add capability to set transmitter End Of Transmission/idle state #9235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
supersebbo opened this issue Feb 11, 2024 · 5 comments · Fixed by #9238
Closed
1 task done

RMT - Add capability to set transmitter End Of Transmission/idle state #9235

supersebbo opened this issue Feb 11, 2024 · 5 comments · Fixed by #9238
Assignees
Labels
Status: To be implemented Selected for Development Type: Feature request Feature request for Arduino ESP32
Milestone

Comments

@supersebbo
Copy link

Related area

ESP32 RMT

Hardware specification

ESP32

Is your feature request related to a problem?

It is not possible to set the RMT Transmitter EOT/idle state from the Arduino implementation, making it impossible to drive a bus that requires a logic high idle state.

Describe the solution you'd like

Add a hook to set the EOT state on the RMT init.

The function is available in the Espressif ESP-IDF Implementation as a flag on the RMT TX Config uint32_t eot_level
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html?#_CPPv421rmt_transmit_config_t

Describe alternatives you've considered

No response

Additional context

No response

I have checked existing list of Feature requests and the Contribution Guide

  • I confirm I have checked existing list of Feature requests and Contribution Guide.
@supersebbo supersebbo added the Type: Feature request Feature request for Arduino ESP32 label Feb 11, 2024
@SuGlider
Copy link
Collaborator

@supersebbo - This could the the new API for such the requested RMT feature - let me know if it sound good.

/**
     Sets the End of Transmission level to be set the <pin> when the RMT transmission ends.
     This function affects how rmtWrite(), rmtWriteAsync() or rmtWriteLooping() will set the pin after writing the data.
     The default EOT level is LOW, in case this function isn't used before RMT Writing.
     This level can be set for each RMT pin and can change between writings to the same pin.

     <EOT_Level> shall be Zero (LOW) or non-zero (HIGH) value.
     It only affects the transmission process, therefore, it doesn't affect any IDLE LEVEL before starting the RMT transmission. 
     The pre-transmission idle level can be set manually calling, for instance, digitalWrite(pin, Level).
*/
void rmtSetEOT(int pin, uint8_t EOT_Level);

Example:

#define MY_RMT_PIN 2
void setup() {
    Serial.begin(115200);
    if (!rmtInit(MY_RMT_PIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) {
        Serial.println("init sender failed\n");
    }
    Serial.println("real tick set to: 100ns");
    
    // sets the End of Transmission Level to HIGH, after writing to the pin. DEFAULT is LOW.
    rmtSetEOT(MY_RMT_PIN, HIGH);
    
    // Send the data and wait until it is done - set EOT level to HIGH
    rmtWrite(MY_RMT_PIN, led_data, NR_OF_ALL_BITS, RMT_WAIT_FOR_EVER);

}

@SuGlider
Copy link
Collaborator

This is an example that sets the initial RMT state and then also sets the EOT state:

#define BLINK_GPIO 2
#define EOT_INITIAL_STATE_TIME_MS 1000

// BLINK_GPIO shall start at RMT_EOT (HIGH or LOW) as initial state for EOT_INITIAL_STATE_TIME_MS, 
// BLINK: 1 second ON, 1 second OFF and then return/stay to RMT_EOT level at the end.
#define RMT_EOT HIGH

// RMT is at 400KHz with a 2.5us tick
// This RMT data sends a 0.5Hz pulse with 1s High and 1s Low signal
rmt_data_t blink_1s_rmt_data[] = {
  // 400,000 x 2.5us = 1 second ON
  {25000, 1, 25000, 1,},
  {25000, 1, 25000, 1,},
  {25000, 1, 25000, 1,},
  {25000, 1, 25000, 1,},
  {25000, 1, 25000, 1,},
  {25000, 1, 25000, 1,},
  {25000, 1, 25000, 1,},
  {25000, 1, 25000, 1,},
  // 400,000 x 2.5us = 1 second OFF
  {25000, 0, 25000, 0,},
  {25000, 0, 25000, 0,},
  {25000, 0, 25000, 0,},
  {25000, 0, 25000, 0,},
  {25000, 0, 25000, 0,},
  {25000, 0, 25000, 0,},
  {25000, 0, 25000, 0,},
  {25000, 0, 25000, 0,},
  // Looping mode needs a Zero ending data to mark the EOF
  {0, 0, 0, 0}
};

void setup() {
  Serial.begin(115200);
  Serial.println("Starting Blink testing...");
  Serial.flush();
  
  // 1 RMT Block has 64 RMT_SYMBOLS (ESP32|ESP32S2) or 48 RMT_SYMBOLS (ESP32C3|ESP32S3)
  if (!rmtInit(BLINK_GPIO, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 400000)) { //2.5us tick
    Serial.println("===> rmtInit Error!");
  }
   
  // sets the End of Transmission Level to HIGH, after writing to the pin. DEFAULT is LOW.
  rmtSetEOT(BLINK_GPIO, RMT_EOT);
  // set initial RMT state by writing a single RMT data
  rmt_data_t initStateSetup_rmt_data[] = { {1, RMT_EOT, 0, 0} };  
  rmtWrite(BLINK_GPIO, initStateSetup_rmt_data, RMT_SYMBOLS_OF(initStateSetup_rmt_data), RMT_WAIT_FOR_EVER);
  Serial.printf("\nLED GPIO%d start in the inital level %s\n", BLINK_GPIO, RMT_EOT == LOW ? "LOW" : "HIGH");
  delay(EOT_INITIAL_STATE_TIME_MS); // set initial state of the LED is set by RMT_EOT.
   
  // Send the data and wait until it is done - set EOT level to HIGH
  Serial.printf("\nLED GPIO%d Blinks 1 second HIGH - 1 second LOW.\n", BLINK_GPIO);
  if (!rmtWrite(BLINK_GPIO, blink_1s_rmt_data, RMT_SYMBOLS_OF(blink_1s_rmt_data) - 2, RMT_WAIT_FOR_EVER)) {
    Serial.println("===> rmtWrite Blink 1s Error!");
  }
  Serial.printf("\nLED GPIO%d goes to the EOT level %s\n", BLINK_GPIO, RMT_EOT == LOW ? "LOW" : "HIGH");
}

void loop(){}

@SuGlider SuGlider moved this from In Progress to In Review in Arduino ESP32 Core Project Roadmap Feb 12, 2024
@SuGlider SuGlider added the Status: To be implemented Selected for Development label Feb 13, 2024
@supersebbo
Copy link
Author

This would be perfect, thanks so much for the quick turn-around.

@supersebbo
Copy link
Author

While you're in the code could you also add an API to set the RMT clock source to REF_TICK (1MHz) instead of DEFAULT APB?

As RMT driven busses are typically slow, using the APB clock results in a huge clock divider to reach kHz speeds, this on it's own is fine but it causes problems when using the Min RX period settings as the register for this value is only 8bit, as described here espressif/esp-idf#11262 (comment)

@SuGlider
Copy link
Collaborator

While you're in the code could you also add an API to set the RMT clock source to REF_TICK (1MHz) instead of DEFAULT APB?

As RMT driven busses are typically slow, using the APB clock results in a huge clock divider to reach kHz speeds, this on it's own is fine but it causes problems when using the Min RX period settings as the register for this value is only 8bit, as described here espressif/esp-idf#11262 (comment)

I'll work on an API that will allow the user to set the source clock too.
This is part of another Task which will add Power Management feature based on automatic Dynamic Frequency Scaling (DFS).
The proposal is that the user will be able to set the Clock Source for each peripheral and also set it in a Global way whenever necessary in order to increase DFS applicability.

This should be ready for Arduino 3.0.0 final release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: To be implemented Selected for Development Type: Feature request Feature request for Arduino ESP32
Projects
Development

Successfully merging a pull request may close this issue.

2 participants