This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:iot-open:introductiontoembeddedprogramming2:cppfundamentals:hardwarespecific [2023/07/13 13:02] – pczekalski | en:iot-open:introductiontoembeddedprogramming2:cppfundamentals:hardwarespecific [2025/10/06 20:00] (current) – [Interrupts] pczekalski | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Hardware-specific extensions in programming ====== | ||
| + | {{: | ||
| + | Some generic programming techniques and patterns mentioned above require adaptation for different hardware platforms. It may occur whenever hardware-related aspects are in charge, e.g., accessing GPIOs, ADC conversion, timers, interrupts, multitasking (task scheduling and management), | ||
| + | It is common for hardware vendors to provide rich examples, either in the form of documentation and downloadable samples (e.g. STM) or via Github (Espressif), | ||
| + | ==== Analog input ==== | ||
| + | Some MCUs use specific setups. Analogue input may work out of the box. Still, low-level control usually brings better results and higher flexibility (e.g. instead of changing the input voltage to reflect the whole measurement range, you can regulate internal amplification and sensitivity. | ||
| + | |||
| + | ** A special note on analogue inputs in ESP32 **\\ | ||
| + | Please note implementation varies even between the ESP32 chips family, and not all chips provide all of the functions, so it is essential to refer to the technical documentation ((https:// | ||
| + | |||
| + | ESP32 has 15 channels exposed (18 total) of the up to 12-bit resolution ADCs. Reading the raw data (12-bit resolution is the default, 8 samples per measure as default) using the '' | ||
| + | Technically, | ||
| + | Just execute '' | ||
| + | Several useful functions are here (not limited to): | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | <note important> | ||
| + | |||
| + | ==== Analog output ==== | ||
| + | PWM frequently controls analogue-style, | ||
| + | PWM uses pulses to change the adequate power delivered to the actuator. | ||
| + | It applies to motors, LEDs, bulbs, heaters and indirectly to the servos (but that works another way). | ||
| + | |||
| + | ** A special note on ESP32 MCUs **\\ | ||
| + | The classical '' | ||
| + | ESP32 has up to sixteen (0 to 15) PWM channels (controllers) that can be freely bound to any of the regular GPIOs.\\ The exact number of PWM channels depends on the family member of the ESP chips, e.g. ESP32-S2 and S3 series have only 8 independent PWM channels while ESP32-C3 has only 6. In the Arduino software framework for ESP32, it is referred to as '' | ||
| + | |||
| + | To use PWM in ESP32, one must perform the following steps: | ||
| + | * configure GPIO pin as '' | ||
| + | * initiate PWM controller by fixing PWM frequency and resolution, | ||
| + | * bind the controller to the GPIO pin, | ||
| + | * write to the controller (not to the PIN!) providing a duty cycle related to the resolution selected above - every call persistently sets the PWM duty cycle until the next call to the function setting duty cycle. | ||
| + | |||
| + | More information and detailed references can be found in the technical documentation for the ESP32 chips family ((https:// | ||
| + | |||
| + | Sample code controlling an LED on GPIO 26 with 5kHz frequency and 8-bit resolution is presented below: | ||
| + | <code c> | ||
| + | #include " | ||
| + | |||
| + | ... | ||
| + | |||
| + | #define RGBLED_R 26 | ||
| + | #define PWM1_Ch | ||
| + | #define PWM_Res | ||
| + | #define PWM_Freq | ||
| + | |||
| + | ... | ||
| + | |||
| + | ledcSetup(PWM1_Ch, | ||
| + | // | ||
| + | ledcAttachPin(RGBLED_R, | ||
| + | //Bind a PWM channel to the GPIO | ||
| + | ledcWrite(PWM1_Ch, | ||
| + | //Full on: control via the PWM channel, not via the GPIO | ||
| + | ... | ||
| + | </ | ||
| + | |||
| + | <note tip>You can bind one PWM channel to many GPIOs to control them synchronously.</ | ||
| + | |||
| + | This technique can be easily adapted to control, e.g. standard and digital servos. PWM signal specification to control servos is presented in the chapter [[en: | ||
| + | |||
| + | ==== Interrupts ==== | ||
| + | Arduino boards used to have a limited set of GPIOs to trigger interrupts. In other MCUs, it is a rule of thumb that almost all GPIOs (but those used, e.g. for external SPI flash) can trigger an interrupt; thus, there is much higher flexibility in, e.g., the use of user interface devices such as buttons. | ||
| + | |||
| + | ** A special note on ESP8266 and ESP32 **\\ | ||
| + | Suppose the interrupt routine (function handler) uses any variables or accesses flash memory. In that case, it is necessary to use some form of tagging for the ISR function due to the specific, low-level memory management. A use of '' | ||
| + | <code c> | ||
| + | void IRAM_ATTR ButtonIRS() { //IRS function | ||
| + | button_toggle =!button_toggle; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | <note important> | ||
| + | |||
| + | <note warning> | ||
| + | |||
| + | ==== Timers ==== | ||
| + | The number of hardware timers, their features, and specific configuration is per MCU. Even single MCU families have different numbers of timers, e.g., in the case of the STM32 chips, the ESP32, and many others. Those differences, | ||
| + | |||
| + | ** A special note on ESP32 MCUs **\\ | ||
| + | The number of hardware timers varies between family members. Most ESP32s have 4, but ESP32-C3 has only two ((https:// | ||
| + | Special techniques using the critical section, muxes and semaphores are needed when more than one routine writes to the shared variable between processes (usually main code and an interrupt handler). However, It is unnecessary in the scenario where the interrupt handler writes to the variable and some other code (e.g. in the '' | ||
| + | In this example, the base clock for the timer in the ESP32 chip is 80MHz, and the timer ('' | ||
| + | '' | ||
| + | <code c> | ||
| + | #include " | ||
| + | |||
| + | #define LED_GPIO 0 //RED LED on GPIO 0 - vendor-specific | ||
| + | #define PRESCALLER 80 // | ||
| + | #define COUNTER 2000000 | ||
| + | |||
| + | volatile bool LEDOn = false; | ||
| + | hw_timer_t *tHBT = NULL; //Heart Beat Timer | ||
| + | |||
| + | void IRAM_ATTR onHBT(){ | ||
| + | LEDOn = !LEDOn; | ||
| + | // | ||
| + | } | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(9600); | ||
| + | pinMode(LED_GPIO, | ||
| + | |||
| + | tHBT = timerBegin(0, | ||
| + | // | ||
| + | //Most ESP32s (but ESP32-C3) have 4 timers (0-3), | ||
| + | //and ESP32-C3 has only two (0-1). | ||
| + | if (tHBT==NULL) //Check timer is created OK, NULL otherwise | ||
| + | { | ||
| + | Serial.println(" | ||
| + | delay(1000); | ||
| + | ESP.restart(); | ||
| + | } | ||
| + | timerAttachInterrupt(tHBT, | ||
| + | //Attach interrupt to the timer | ||
| + | timerAlarmWrite(tHBT, | ||
| + | //Configure to run every 2s (2000000us) and repeat forever | ||
| + | timerAlarmEnable(tHBT); | ||
| + | | ||
| + | } | ||
| + | //Loop function only reads LEDOn value and updates GPIO accordingly | ||
| + | void loop() { | ||
| + | digitalWrite(LED_GPIO, | ||
| + | } | ||
| + | </ | ||
| + | Timers can also be used to implement a Watchdog. Regarding the example above, it is usually a " | ||