This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
en:iot-open:practical:hardware:sut:esp32:adv1_1 [2024/03/25 17:35] – [Start] pczekalski | en:iot-open:practical:hardware:sut:esp32:adv1_1 [2024/03/26 08:46] (current) – [Steps] pczekalski | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== ADV1: Using timers to execute code asynchronously ====== | ====== ADV1: Using timers to execute code asynchronously ====== | ||
- | It is advised to use timers that periodically execute a function to handle repeating tasks. Hardware timers work parallel to the CPU and consume few CPU resources. ESP32-S3 has 4 hardware timers, but each timer can execute multiple handlers. | + | It is advised to use timers that periodically execute a function to handle repeating tasks. Hardware timers work parallel to the CPU and consume few CPU resources. ESP32-S3 has 4 hardware timers, but each timer can execute multiple handlers. |
- | The idea of using the timer is to encapsulate a piece of compact code that can be run virtually asynchronously and executed is a precisely-defined time manner. In this scenario, we use a timer to update the LCD screen periodically. We choose a dummy example where Timer 1 is used to increment a value of the '' | + | The idea of using the timer is to encapsulate a piece of compact code that can be run virtually asynchronously and executed is a precisely-defined time manner. In this scenario, we use a timer to update the LCD screen periodically. We choose a dummy example where Timer 1 is used to increment a value of the '' |
Line 21: | Line 21: | ||
==== Task to be implemented ==== | ==== Task to be implemented ==== | ||
- | Present on the LCD current value of the '' | + | Present on the LCD current value of the '' |
==== Start ==== | ==== Start ==== | ||
Line 27: | Line 27: | ||
==== Steps ==== | ==== Steps ==== | ||
- | // Write some extra information if, i.e. some steps are optional; otherwise, cancel this paragraph (but do not remove the header).// | + | We used to separate tasks, but for this case, complete code is provided in chunks, including LCD handling. It presents relations on where particular parts of the code should be located when using timers and how timing relates between components. |
+ | |||
+ | <note tip>It is important, e.g. when LCD had to be set up and configured before asynchronous handlers execute writing to it, so set up order is meaningful!< | ||
=== Step 1 === | === Step 1 === | ||
- | //Describe activities done in Step 1.// | + | Include libraries: |
+ | <code c> | ||
+ | #include <Arduino.h> | ||
+ | #include < | ||
+ | </code> | ||
- | ... | + | === Step 2 === |
+ | Define LCD configuration pins (see details and explanation in the scenario: [[en: | ||
+ | <code c> | ||
+ | #define LCD_RS 2 | ||
+ | #define LCD_ENABLE 1 | ||
+ | #define LCD_D4 39 | ||
+ | #define LCD_D5 40 | ||
+ | #define LCD_D6 41 | ||
+ | #define LCD_D7 42 | ||
+ | </ | ||
- | === Step n === | + | === Step 3 === |
- | //Describe activities done in Step n.// | + | Declare variables and classes (timers): |
+ | <code c> | ||
+ | volatile byte i = 0; | ||
+ | hw_timer_t *Timer1 = NULL; | ||
+ | hw_timer_t *Timer2 = NULL; | ||
+ | static Adafruit_LiquidCrystal lcd(LCD_RS, LCD_ENABLE, LCD_D4, LCD_D5, LCD_D6, LCD_D7); | ||
+ | </code> | ||
+ | Above, we declare two separate timers: '' | ||
+ | === Step 4 === | ||
+ | Declare constants that define how frequently timers will be calling handlers: | ||
+ | <code c> | ||
+ | const int baseFrequency = 80; //MHz | ||
+ | const int interval1 = 1000000; | ||
+ | const int interval2 = 3000000; | ||
+ | </ | ||
+ | The base frequency for the timers in ESP32 is 80 MHz. Each timer counts in microseconds, | ||
+ | |||
+ | === Step 5 === | ||
+ | Declare and implement functions that are timer handles: timer calls the bound function every execution period: | ||
+ | <code c> | ||
+ | void IRAM_ATTR onTimer1() //handler for Timer1 | ||
+ | { | ||
+ | i++; | ||
+ | } | ||
+ | |||
+ | void IRAM_ATTR onTimer2() //handler for Timer2 | ||
+ | { | ||
+ | lcd.clear(); | ||
+ | lcd.setCursor(0, | ||
+ | lcd.print(i); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <note warning> | ||
+ | <note tip>If you do need to use floating point calculations in ISR or timer functions, you can use '' | ||
+ | |||
+ | <note important> | ||
+ | === Step 6 === | ||
+ | Configure timers, start LCD and enable timers. Note the correct order: LCD have to be ready when the timer calls '' | ||
+ | <code c> | ||
+ | void setup(){ | ||
+ | //Timer1 config | ||
+ | Timer1 = timerBegin(0, | ||
+ | timerAttachInterrupt(Timer1, | ||
+ | timerAlarmWrite(Timer1, | ||
+ | | ||
+ | //Timer2 config | ||
+ | Timer2 = timerBegin(1, | ||
+ | timerAttachInterrupt(Timer2, | ||
+ | timerAlarmWrite(Timer2, | ||
+ | | ||
+ | //start LCD | ||
+ | lcd.begin(16, | ||
+ | lcd.clear(); | ||
+ | | ||
+ | //start both timers | ||
+ | timerAlarmEnable(Timer1); | ||
+ | timerAlarmEnable(Timer2); | ||
+ | } | ||
+ | </ | ||
+ | In the code above, '' | ||
+ | Following, '' | ||
+ | Then we define how frequently the execution of the function above will occur: '' | ||
+ | Note that at this moment, timers are not executing the handlers yet; the last step is required: '' | ||
+ | |||
+ | |||
+ | === Step 7 === | ||
+ | This way, a main loop is empty: everything runs asynchronously, | ||
+ | <code c> | ||
+ | void loop() | ||
+ | { | ||
+ | |||
+ | } | ||
+ | </ | ||
==== Result validation ==== | ==== Result validation ==== | ||
On the LCD screen, you should see values starting from number 3, then 6, 9, 12 and so on (=3 every 3 seconds). Note, as '' | On the LCD screen, you should see values starting from number 3, then 6, 9, 12 and so on (=3 every 3 seconds). Note, as '' |