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 19:32] – [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 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 '' |