LedC libraries

Hi

one of the reasons that it took me a long time to switch to the Arduino IDE albeit with an ESP32
was the old style C programming.

This has been largely remedied and for instance I can happily write:

ledcAttachPin(X_PWM_OUT, X_PWM_CHANNEL);
ledcSetup(X_PWM_CHANNEL, X_PWM_FREQ, PWM_RESOLUTION);

I cannot find an equivalent command though to either stop the output (Frequency still seems to
have a very frequency) or a ledcDETACH command. (I am assuming I just can set the pin to be an input). (Somewhat crude...)

But that is not the question:

As soon as I dig deeper into the libraries I immediately find something like

  uint32_t ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel)  (ESP32 documentation)

or

esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level);
or

esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel);

(GitHub).

Nowhere can I find where the above commands ledcAttachPin or ledcSetup are defined.

Thanks a lot for any light you can shed on that.

Nowhere can I find where the above commands ledcAttachPin or ledcSetup are defined.

You find them on Github:

double      ledcSetup(uint8_t channel, double freq, uint8_t resolution_bits);
void        ledcWrite(uint8_t channel, uint32_t duty);
double      ledcWriteTone(uint8_t channel, double freq);
double      ledcWriteNote(uint8_t channel, note_t note, uint8_t octave);
uint32_t    ledcRead(uint8_t channel);
double      ledcReadFreq(uint8_t channel);
void        ledcAttachPin(uint8_t pin, uint8_t channel);
void        ledcDetachPin(uint8_t pin);

The functions are defined in: arduino-esp32/esp32-hal-ledc.c at master · espressif/arduino-esp32 · GitHub

Thank you so much!

I hope the LedcDetachPin will turn off the output.

I noticed that with ledcSetup(0,0,8) for instance the pin still oscillates with about 4 Hz.

Thanks again.

I am just about to study the ESP32 core/framework/environment. In the very first example I have noticed this ledcWrite() command that was not defined anywhere (from the perspective of clean and sound coding practices). If this command is hidden it is a subject to change and all our codes may just stop working one day when those hidden files are changed by a third party.

Well, all this introduction is to support this more than logical question from NOISCA. He is right and all these answers are more than ignorant.
I am quite disappointed with contributors who use these forums to offend others rather than understand them and help them. All questions should be respected, because people who ask them may not be quite educated or knowledgeable. But those who dare to answer should/must be the knowledgeable ones. The person who pointed to a github location (definition of ledcAttachPin(), ledcSetup() etc..) clearly needs to learn ArduinoIDE platform or any other platform for that matter. Any other explanation is offending Noiska on purpose.

So, guys, ARDUINO & all the supported modules is an interesting, FREE and useful platform for both hobbyists and design engineers. Lets all behave properly. Disrespect for other people comes from a limited knowledge and lack of self-respect.

2 Likes

pdozet:
.. this more than logical question from NOISCA. ..The person who pointed to a github location ..

you mean noiasca? anyway I literally signed up just to say that disrespect comes from being up your own backside, and you might want to consider professional help for your neurosis. And reread this thread to see what you wrote in context, where to me I see a question answered twice and then some random spanner flies in to spill his handbag over it, and tries to demand other people act according to his rules based on his already provably mistaken assumptions.
(if your wondering, I have all day)

How is it possible to include the functions ledcAttachPin or ledcSetup in a sketch without a #include for the esp32-hal-ledc.h library in the sketch? Are there other ESP32 libraries that also may be used in sketches without #include ing them? Thanks for helping an old noob.

You might find this interesting.

Thank you for replying. However, I didn't see the connection between your link and my question. I still don't understand why is it possible to include the functions ledcAttachPin or ledcSetup in a sketch without a #include for the esp32-hal-ledc.h library in the sketch,

From Arduino.h:

#include "esp32-hal.h"

From esp32-hal.h:

#include "esp32-hal-ledc.h"

The LEDC API is a poor choice for PWM for motors over the MCPWM API.

The LEDC API uses a ESP32 hardware timer, the MCPWM has its own timers. If not assigned, as in the ESP32SERVO library, the decision as to which hardware timer to use is made by the OS. The auto assign and configure of a hardware timer can conflict with using other hardware timers as it will be an unknown as to which hardware timer will be used and your assigned timer may be taken over by another process. Also not a really big deal, the LEDC uses clock ticks as a torque value over using us resulting in position accuracy. The MCPWM uses us. The LEDC API uses CPU clock cycles to do PWM. The MCPWM is a hardware module that, once programmed, does not require CPU time to maintain PWM outputs.

gfvalvo:
From Arduino.h:

#include "esp32-hal.h"

From esp32-hal.h:

#include "esp32-hal-ledc.h"

Thanks. That helps a lot. Can you help me understand how Arduino.h is included in skectches written in the Arduino IDE for the ESP32? It doesn't show in the code.

It's all part of the SFM (Sheer Fu**ing Magic) that is Arduino. I wouldn't spend too much time fretting about it. The only thing you really need to know is that if you start writing .cpp files then you must #include <Arduino.h> explicitly (either in the .cpp for via a .h header file).

gfvalvo:
It's all part of the SFM (Sheer Fu**ing Magic) that is Arduino. I wouldn't spend too much time fretting about it. The only thing you really need to know is that if you start writing .cpp files then you must #include <Arduino.h> explicitly (either in the .cpp for via a .h header file).

I do not include the Arduino.h. I try my best to avoid using Arduino.h, when coding for the ESP32. It is not necessary to do and can result in operational slow downs and introduces errors.

For instance, using Analogread with an ESP32 is quite inaccurate over using the ESP32 AD API and printing with log_x is faster then printing with Serial.print.

An .h file without Arduino.h and wrote in the Arduino IDE.

#include <driver/spi_master.h>
#include "sdkconfig.h"
#include "esp_system.h"
 uint8_t GetLowBits();
 uint16_t GetHighBits();
 void fReadSPIdata16bits( spi_device_handle_t &h, int address );
 int fWriteSPIdata8bits( spi_device_handle_t &h, int address, int sendData );
 int fInitializeSPI_Devices( spi_device_handle_t &h, int csPin);
int fInitializeSPI_Channel( int spiCLK, int spiMOSI, int spiMISO, spi_host_device_t SPI_Host, bool EnableDMA);
int fWriteSPIdata32bits( spi_device_handle_t &h, int _sendData0, int _sendData1, int _sendData2, int _sendData3 );
int fReadSPIdataXbits( spi_device_handle_t &h, int _readaddress, int *rxbuf, int rxlen );
int fWriteSPIdata8bits2( spi_device_handle_t &h, int _sendData );

The cpp without Arduino.h

#include "ESP32_SPI_API.h"
/////////////////////////////
///////////////////////////
uint8_t txData[2] = { };
int8_t rxData[25] = { };
uint8_t low;
uint8_t high;
//////
//////////////////////////////////
uint8_t GetLowBits()
{
  return low;
}
uint16_t GetHighBits()
{
  return high;
}
////////////////////////////////////////
int fInitializeSPI_Channel( int spiCLK, int spiMOSI, int spiMISO, spi_host_device_t SPI_Host, bool EnableDMA)
{
  esp_err_t intError;
  spi_bus_config_t bus_config = { };
  bus_config.sclk_io_num = spiCLK; // CLK
  bus_config.mosi_io_num = spiMOSI; // MOSI
  bus_config.miso_io_num = spiMISO; // MISO
  bus_config.quadwp_io_num = -1; // Not used
  bus_config.quadhd_io_num = -1; // Not used
  intError = spi_bus_initialize( HSPI_HOST, &bus_config, EnableDMA) ;
  return intError;
}
//////
int fInitializeSPI_Devices( spi_device_handle_t &h, int csPin)
{
  esp_err_t intError;
  spi_device_interface_config_t dev_config = { };  // initializes all field to 0
  dev_config.address_bits     = 0;
  dev_config.command_bits     = 0;
  dev_config.dummy_bits       = 0;
  dev_config.mode             = 3 ;
  dev_config.duty_cycle_pos   = 0;
  dev_config.cs_ena_posttrans = 0;
  dev_config.cs_ena_pretrans  = 0;
  dev_config.clock_speed_hz   = 5000000;
  dev_config.spics_io_num     = csPin;
  dev_config.flags            = 0;
  dev_config.queue_size       = 1;
  dev_config.pre_cb           = NULL;
  dev_config.post_cb          = NULL;
  spi_bus_add_device(HSPI_HOST, &dev_config, &h);
  return intError;
  // return h;
} // void fInitializeSPI_Devices()
///////////////////////////////////////////////////////////////
void fReadSPIdata16bits( spi_device_handle_t &h, int _address )
{
  uint8_t address = _address;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 3); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 8 * 2 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = rxData;
  txData[0] = address | 0x80;
  intError = spi_device_transmit( h, &trans_desc);
  low = rxData[0]; high = rxData[1];
} // void fSendSPI( uint8_t count, uint8_t address, uint8_t DataToSend)
////
int fWriteSPIdata8bits( spi_device_handle_t &h, int _address, int _sendData )
{
  uint8_t address =  _address;
  uint8_t sendData = _sendData;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 2); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 0 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = NULL;
  txData[0] = address  & 0x7F;
  txData[1] = sendData;
  intError = spi_device_transmit( h, &trans_desc);
  return intError;
} // void fWriteSPIdata8bits(  spi_device_handle_t &h, uint8_t address, uint8_t sendData )
//
int fWriteSPIdata8bits2( spi_device_handle_t &h, int _sendData )
{
  rxData[0] = 0x00;
  // uint8_t address =  _address;
  uint8_t sendData = _sendData;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 2); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 8 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = rxData;
  txData[0] = sendData;
  // txData[0] = address;
  // txData[1] = sendData;
  intError = spi_device_transmit( h, &trans_desc);
  if ( intError == 0 )
  {
    return rxData[0];
  }
  else
  {
  return intError;  
  }
} // void fWriteSPIdata8bits(  spi_device_handle_t &h, uint8_t address, uint8_t sendData )
////
int fWriteSPIdata32bits( spi_device_handle_t &h, int _sendData0, int _sendData1, int _sendData2, int _sendData3 )
{
  // uint8_t address =  _address;
  // uint8_t sendData = _sendData;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 4); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 0 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = NULL;
  txData[0] = (uint8_t)_sendData0; // command bits
  txData[1] = (uint8_t)_sendData1; // lower bits
  txData[2] = (uint8_t)_sendData2; // higher bits
  txData[3] = (uint8_t)_sendData3; // address
  intError = spi_device_transmit( h, &trans_desc);
  return intError;
} // void fWriteSPIdata8bits(  spi_device_handle_t &h, uint8_t address, uint8_t sendData )
////
int fReadSPIdataXbits( spi_device_handle_t &h, int _readaddress, int *rxbuf, int rxlen )
{
  uint8_t address = _readaddress;
  int8_t rxBuf[rxlen] = {0};
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = ( (8 * 1) + (8 * rxlen)); // total data bits
  trans_desc.tx_buffer = txData ;
  trans_desc.rxlength = 8 * rxlen ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = rxBuf;
  txData[0] = address;
  // txData[1] = 0x00;
  intError = spi_device_transmit( h, &trans_desc);
  for (int i = 0; i < rxlen; i++)
  {
    rxbuf[i] = rxBuf[i];
  }
 return intError;
} // int fReadSPIdataXbits( spi_device_handle_t &h, int _readaddress, int *rxbuf, int rxlen )

Its been working for over a year without Arduino.h.

Regardless, if you compile a main .ino file using Arduino IDE, you're going to get Arduino.h.

gfvalvo:
Regardless, if you compile a main .ino file using Arduino IDE, you're going to get Arduino.h.

Then it does not NEED to be included in another .h file, as its already being included. Cool.

ledc vs MCPWM
Found an issue with channel selection ... so opened an issue at ESP-IDF closed

Most likely found that doing the configuration, as the example shows, does not work see below for configuration use under the Arduino IDE:

my code in setup to use the MCPWM

 mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_NUM_4 ); // Azimuth
  mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_NUM_12 ); // Altitude servo
  //
  mcpwm_config_t pwm_config = {};
  pwm_config.frequency = 50;    //frequency = 50Hz, i.e. for every servo motor time period should be 20ms
  pwm_config.cmpr_a = 0;    //duty cycle of PWMxA = 0
  pwm_config.cmpr_b = 0;    //duty cycle of PWMxb = 0
  pwm_config.counter_mode = MCPWM_UP_COUNTER;
  pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
 mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);    //Configure PWM0A timer 0 with above settings
  mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config);    //Configure PWM0A timer 1 with above settings
  //  ////
  fMoveAltitude( 1525 );
  fMoveAzimuth( 1500 );

Here is the move functions

void fMoveAltitude( int MoveTo )
{
  mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, MoveTo);
  vTaskDelay( 12 );
}
//////
void fMoveAzimuth( int MoveTo )
{
  mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A, MoveTo);
  vTaskDelay( 12 );
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.