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:39] – [Steps] 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 29: | Line 29: | ||
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. | 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!</ | + | <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 === | ||
Include libraries: | Include libraries: | ||
Line 37: | Line 37: | ||
</ | </ | ||
+ | === Step 2 === | ||
+ | Define LCD configuration pins (see details and explanation in the scenario: [[en: | ||
+ | <code c> | ||
#define LCD_RS 2 | #define LCD_RS 2 | ||
#define LCD_ENABLE 1 | #define LCD_ENABLE 1 | ||
Line 43: | Line 46: | ||
#define LCD_D6 41 | #define LCD_D6 41 | ||
#define LCD_D7 42 | #define LCD_D7 42 | ||
+ | </ | ||
+ | === Step 3 === | ||
+ | Declare variables and classes (timers): | ||
+ | <code c> | ||
volatile byte i = 0; | volatile byte i = 0; | ||
hw_timer_t *Timer1 = NULL; | hw_timer_t *Timer1 = NULL; | ||
hw_timer_t *Timer2 = NULL; | hw_timer_t *Timer2 = NULL; | ||
+ | static Adafruit_LiquidCrystal lcd(LCD_RS, LCD_ENABLE, LCD_D4, LCD_D5, LCD_D6, LCD_D7); | ||
+ | </ | ||
+ | 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 baseFrequency = 80; //MHz | ||
const int interval1 = 1000000; | const int interval1 = 1000000; | ||
const int interval2 = 3000000; | const int interval2 = 3000000; | ||
+ | </ | ||
+ | The base frequency for the timers in ESP32 is 80 MHz. Each timer counts in microseconds, | ||
- | static Adafruit_LiquidCrystal lcd(LCD_RS, LCD_ENABLE, LCD_D4, LCD_D5, LCD_D6, LCD_D7); | + | === Step 5 === |
- | + | Declare and implement functions that are timer handles: timer calls the bound function every execution period: | |
- | void IRAM_ATTR onTimer1() | + | <code c> |
+ | void IRAM_ATTR onTimer1() | ||
{ | { | ||
i++; | i++; | ||
} | } | ||
- | void IRAM_ATTR onTimer2() | + | void IRAM_ATTR onTimer2() |
{ | { | ||
lcd.clear(); | lcd.clear(); | ||
Line 64: | Line 81: | ||
lcd.print(i); | 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(){ | void setup(){ | ||
//Timer1 config | //Timer1 config | ||
Line 69: | Line 95: | ||
timerAttachInterrupt(Timer1, | timerAttachInterrupt(Timer1, | ||
timerAlarmWrite(Timer1, | timerAlarmWrite(Timer1, | ||
- | timerAlarmEnable(Timer1); | ||
| | ||
//Timer2 config | //Timer2 config | ||
Line 75: | Line 100: | ||
timerAttachInterrupt(Timer2, | timerAttachInterrupt(Timer2, | ||
timerAlarmWrite(Timer2, | timerAlarmWrite(Timer2, | ||
- | timerAlarmEnable(Timer2); | ||
| | ||
//start LCD | //start LCD | ||
lcd.begin(16, | lcd.begin(16, | ||
lcd.clear(); | lcd.clear(); | ||
+ | | ||
//start both timers | //start both timers | ||
timerAlarmEnable(Timer1); | timerAlarmEnable(Timer1); | ||
timerAlarmEnable(Timer2); | 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() | void loop() | ||
{ | { | ||
} | } | ||
- | + | </code> | |
- | === Step 2 === | + | |
- | + | ||
- | ... | + | |
- | + | ||
- | === Step n === | + | |
- | //Describe activities done in Step n.// | + | |
==== 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 '' |