├── .gitignore ├── LICENSE ├── README.md ├── alertled └── alertled.ino ├── basic_tasks └── basic_tasks.ino ├── basicshell └── basicshell.ino ├── countsem └── countsem.ino ├── critical └── critical.ino ├── debounce └── debounce.ino ├── debounceisr └── debounceisr.ino ├── delayuntil └── delayuntil.ino ├── eventgr └── eventgr.ino ├── evsync └── evsync.ino ├── freqctr-m5 └── freqctr-m5.ino ├── freqctr-ttgo └── freqctr-ttgo.ino ├── freqctr └── freqctr.ino ├── gatekeeper └── gatekeeper.ino ├── hcsr04 └── hcsr04.ino ├── i2cscan └── i2cscan.ino ├── mailbox └── mailbox.ino ├── mutex └── mutex.ino ├── pcf8563 └── pcf8563.ino ├── press └── press.ino ├── press2 └── press2.ino ├── qset └── qset.ino ├── stubs └── stubs.ino ├── task_delete └── task_delete.ino ├── task_suspend └── task_suspend.ino ├── task_yield └── task_yield.ino ├── taskcreate └── taskcreate.ino ├── taskcreate2 └── taskcreate2.ino ├── tasklocal └── tasklocal.ino ├── tasknfy1 └── tasknfy1.ino ├── tasknfy2 └── tasknfy2.ino ├── tasknfy3 └── tasknfy3.ino ├── tasknfy4 └── tasknfy4.ino ├── tasknfy5 └── tasknfy5.ino ├── ticks └── ticks.ino ├── watchdog1 └── watchdog1.ino ├── watchdog2 └── watchdog2.ino └── worms1 └── worms1.ino /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Warren Gay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FreeRTOS_for_ESP32 2 | FreeRTOS for ESP32 3 | 4 | This repo contains the code used for the book: https://www.elektor.com/freertos-for-esp32-arduino-e-book 5 | -------------------------------------------------------------------------------- /alertled/alertled.ino: -------------------------------------------------------------------------------- 1 | // alertled.ino 2 | // MIT License (see file LICENSE) 3 | 4 | // LED is active high 5 | #define GPIO_LED 12 6 | 7 | // 8 | // AlertLED class to drive LED 9 | // 10 | class AlertLED { 11 | TimerHandle_t thandle = nullptr; 12 | volatile bool state; 13 | volatile unsigned count; 14 | unsigned period_ms; 15 | int gpio; 16 | 17 | void reset(bool s); 18 | 19 | public: 20 | AlertLED(int gpio,unsigned period_ms=1000); 21 | void alert(); 22 | void cancel(); 23 | 24 | static void callback(TimerHandle_t th); 25 | }; 26 | 27 | // 28 | // Constructor: 29 | // gpio GPIO pin to drive LED on 30 | // period_ms Overall period in ms 31 | // 32 | AlertLED::AlertLED(int gpio,unsigned period_ms) { 33 | this->gpio = gpio; 34 | this->period_ms = period_ms; 35 | pinMode(this->gpio,OUTPUT); 36 | digitalWrite(this->gpio,LOW); 37 | } 38 | 39 | // 40 | // Internal method to reset values 41 | // 42 | void AlertLED::reset(bool s) { 43 | state = s; 44 | count = 0; 45 | digitalWrite(this->gpio,s?HIGH:LOW); 46 | } 47 | 48 | // 49 | // Method to start the alert: 50 | // 51 | void AlertLED::alert() { 52 | 53 | if ( !thandle ) { 54 | thandle = xTimerCreate( 55 | "alert_tmr", 56 | pdMS_TO_TICKS(period_ms/20), 57 | pdTRUE, 58 | this, 59 | AlertLED::callback); 60 | assert(thandle); 61 | } 62 | reset(true); 63 | xTimerStart(thandle,portMAX_DELAY); 64 | } 65 | 66 | // 67 | // Method to stop an alert: 68 | // 69 | void AlertLED::cancel() { 70 | if ( thandle ) { 71 | xTimerStop(thandle,portMAX_DELAY); 72 | digitalWrite(gpio,LOW); 73 | } 74 | } 75 | 76 | // static method, acting as the 77 | // timer callback: 78 | // 79 | void AlertLED::callback(TimerHandle_t th) { 80 | AlertLED *obj = (AlertLED*)pvTimerGetTimerID(th); 81 | 82 | assert(obj->thandle == th); 83 | obj->state ^= true; 84 | digitalWrite(obj->gpio,obj->state?HIGH:LOW); 85 | 86 | if ( ++obj->count >= 5 * 2 ) { 87 | obj->reset(true); 88 | xTimerChangePeriod(th,pdMS_TO_TICKS(obj->period_ms/20),portMAX_DELAY); 89 | } else if ( obj->count == 5 * 2 - 1 ) { 90 | xTimerChangePeriod(th, 91 | pdMS_TO_TICKS(obj->period_ms/20+obj->period_ms/2), 92 | portMAX_DELAY); 93 | assert(!obj->state); 94 | } 95 | } 96 | 97 | // 98 | // Global objects 99 | // 100 | static AlertLED alert1(GPIO_LED,1000); 101 | static unsigned loop_count = 0; 102 | 103 | // 104 | // Initialization: 105 | // 106 | void setup() { 107 | // delay(2000); // Allow USB to connect 108 | alert1.alert(); 109 | } 110 | 111 | void loop() { 112 | if ( loop_count >= 70 ) { 113 | alert1.alert(); 114 | loop_count = 0; 115 | } 116 | 117 | delay(100); 118 | 119 | if ( ++loop_count >= 50 ) 120 | alert1.cancel(); 121 | } 122 | -------------------------------------------------------------------------------- /basic_tasks/basic_tasks.ino: -------------------------------------------------------------------------------- 1 | // basic_tasks.ino 2 | // MIT License (see file LICENSE) 3 | 4 | // Change the following if you want to use 5 | // different GPIO pins for the three LEDs 6 | 7 | #define LED1 12 // GPIO 12 8 | #define LED2 13 // etc. 9 | #define LED3 15 10 | 11 | struct s_led { 12 | byte gpio; // LED GPIO number 13 | byte state; // LED state 14 | unsigned napms; // Delay to use (ms) 15 | TaskHandle_t taskh; // Task handle 16 | }; 17 | 18 | static s_led leds[3] = { 19 | { LED1, 0, 500, 0 }, 20 | { LED2, 0, 200, 0 }, 21 | { LED3, 0, 750, 0 } 22 | }; 23 | 24 | static void led_task_func(void *argp) { 25 | s_led *ledp = (s_led*)argp; 26 | unsigned stack_hwm = 0, temp; 27 | 28 | delay(1000); 29 | 30 | for (;;) { 31 | digitalWrite(ledp->gpio,ledp->state ^= 1); 32 | temp = uxTaskGetStackHighWaterMark(nullptr); 33 | if ( !stack_hwm || temp < stack_hwm ) { 34 | stack_hwm = temp; 35 | printf("Task for gpio %d has stack hwm %u\n", 36 | ledp->gpio,stack_hwm); 37 | } 38 | delay(ledp->napms); 39 | } 40 | } 41 | 42 | void setup() { 43 | int app_cpu = 0; // CPU number 44 | 45 | delay(500); // Pause for serial setup 46 | 47 | app_cpu = xPortGetCoreID(); 48 | printf("app_cpu is %d (%s core)\n", 49 | app_cpu, 50 | app_cpu > 0 ? "Dual" : "Single"); 51 | 52 | printf("LEDs on gpios: "); 53 | for ( auto& led : leds ) { 54 | pinMode(led.gpio,OUTPUT); 55 | digitalWrite(led.gpio,LOW); 56 | xTaskCreatePinnedToCore( 57 | led_task_func, 58 | "led_task", 59 | 2048, 60 | &led, 61 | 1, 62 | &led.taskh, 63 | app_cpu 64 | ); 65 | printf("%d ",led.gpio); 66 | } 67 | putchar('\n'); 68 | } 69 | 70 | void loop() { 71 | delay(1000); 72 | } 73 | -------------------------------------------------------------------------------- /basicshell/basicshell.ino: -------------------------------------------------------------------------------- 1 | // basicshell.ino 2 | 3 | void setup() { 4 | delay(2000); // Allow for serial setup 5 | printf("Hello from setup()\n"); 6 | } 7 | 8 | void loop() { 9 | printf("Hello from loop()\n"); 10 | delay(1000); 11 | } 12 | -------------------------------------------------------------------------------- /countsem/countsem.ino: -------------------------------------------------------------------------------- 1 | // countsem.ino 2 | // The Dining Philosophers Problem 3 | // MIT License (see file LICENSE) 4 | 5 | #define PREVENT_DEADLOCK 1 6 | #define N 4 7 | #define N_EATERS (N-1) 8 | 9 | static QueueHandle_t msgq; 10 | static SemaphoreHandle_t csem; 11 | static int app_cpu = 0; 12 | 13 | enum State { 14 | Thinking=0, 15 | Hungry, 16 | Eating 17 | }; 18 | 19 | static const char *state_name[] = { 20 | "Thinking", 21 | "Hungry", 22 | "Eating" 23 | }; 24 | 25 | struct s_philosopher { 26 | TaskHandle_t task; 27 | unsigned num; 28 | State state; 29 | unsigned seed; 30 | }; 31 | 32 | struct s_message { 33 | unsigned num; 34 | State state; 35 | }; 36 | 37 | static s_philosopher philosophers[N]; 38 | static SemaphoreHandle_t forks[N]; 39 | static volatile unsigned logno = 0; 40 | 41 | // 42 | // Send the P. state by queue 43 | // 44 | static void send_state(s_philosopher *philo) { 45 | s_message msg; 46 | BaseType_t rc; 47 | 48 | msg.num = philo->num; 49 | msg.state = philo->state; 50 | rc = xQueueSendToBack(msgq,&msg,portMAX_DELAY); 51 | } 52 | 53 | // 54 | // The Philosopher task 55 | // 56 | static void philo_task(void *arg) { 57 | s_philosopher *philo = (s_philosopher*)arg; 58 | SemaphoreHandle_t fork1=0, fork2=0; 59 | BaseType_t rc; 60 | 61 | delay(rand_r(&philo->seed)%5+1); 62 | 63 | for (;;) { 64 | philo->state = Thinking; 65 | send_state(philo); 66 | delay(rand_r(&philo->seed)%5+1); 67 | 68 | philo->state = Hungry; 69 | send_state(philo); 70 | delay(rand_r(&philo->seed)%5+1); 71 | 72 | #if PREVENT_DEADLOCK 73 | rc = xSemaphoreTake(csem,portMAX_DELAY); 74 | assert(rc == pdPASS); 75 | #endif 76 | 77 | // Pick up forks: 78 | fork1 = forks[philo->num]; 79 | fork2 = forks[(philo->num+1) % N]; 80 | rc = xSemaphoreTake(fork1,portMAX_DELAY); 81 | assert(rc == pdPASS); 82 | delay(rand_r(&philo->seed)%5+1); 83 | rc = xSemaphoreTake(fork2,portMAX_DELAY); 84 | assert(rc == pdPASS); 85 | 86 | philo->state = Eating; 87 | send_state(philo); 88 | delay(rand_r(&philo->seed)%5+1); 89 | 90 | // Put down forks: 91 | rc = xSemaphoreGive(fork1); 92 | assert(rc == pdPASS); 93 | delay(1); 94 | rc = xSemaphoreGive(fork2); 95 | assert(rc == pdPASS); 96 | 97 | #if PREVENT_DEADLOCK 98 | rc = xSemaphoreGive(csem); 99 | assert(rc == pdPASS); 100 | #endif 101 | } 102 | } 103 | 104 | // 105 | // Program Initialization 106 | // 107 | void setup() { 108 | BaseType_t rc; 109 | 110 | app_cpu = xPortGetCoreID(); 111 | msgq = xQueueCreate(30,sizeof(s_message)); 112 | assert(msgq); 113 | 114 | for ( unsigned x=0; x 15 | 16 | #define WIFI_RDY 0b0001 17 | #define LED_CHG 0b0010 18 | 19 | static EventGroupHandle_t hevt; 20 | static WiFiServer http(80); 21 | static WiFiUDP udp; 22 | 23 | static int leds[N_LED] = 24 | { GPIO_LED1, GPIO_LED2, GPIO_LED3 }; 25 | 26 | static char const 27 | *ssid = WIFI_SSID, 28 | *passwd = WIFI_PASSWD; 29 | 30 | static bool getline(String& s,WiFiClient client) { 31 | char ch; 32 | bool flag = false; 33 | 34 | s.clear(); 35 | while ( client.connected() ) { 36 | if ( client.available() ) { 37 | ch = client.read(); 38 | flag = true; 39 | 40 | if ( ch == '\r' ) 41 | continue; // Ignore CR 42 | if ( ch == '\n' ) 43 | break; 44 | s += ch; 45 | } else { 46 | taskYIELD(); 47 | } 48 | } 49 | return client.connected() && flag; 50 | } 51 | 52 | static void http_server(void *arg) { 53 | 54 | xEventGroupWaitBits( 55 | hevt, // Event group 56 | WIFI_RDY, // bits to wait for 57 | pdFALSE, // no clear 58 | pdFALSE, // wait for all bits 59 | portMAX_DELAY); // timeout 60 | 61 | auto subnet = WiFi.subnetMask(); 62 | printf("Server ready: %s port 80\n", 63 | WiFi.localIP().toString().c_str()); 64 | 65 | for (;;) { 66 | WiFiClient client = http.available(); 67 | 68 | if ( client ) { 69 | // A client has connected: 70 | String line, header; 71 | bool gothdrf = false; 72 | 73 | printf("New client connect from %s\n", 74 | client.remoteIP().toString().c_str()); 75 | 76 | while ( client.connected() ) { 77 | if ( getline(header,client) ) { 78 | while ( getline(line,client) && line.length() > 0 ) 79 | ; 80 | } 81 | if ( !client.connected() ) 82 | break; 83 | 84 | client.println("HTTP/1.1 200 OK"); 85 | client.println("Content-type:text/html"); 86 | client.println("Connection: close"); 87 | client.println(); 88 | 89 | if ( !strncmp(header.c_str(),"GET /led",8) ) { 90 | const char *cp = header.c_str() + 8; 91 | bool changedf = false; 92 | 93 | if ( cp[0] >= '0' && cp[0] <= ('0'+N_LED) ) { 94 | int ledx = uint8_t(cp[0]) - uint8_t('0'); 95 | 96 | if ( cp[1] == '/' 97 | && ( cp[2] == '0' || cp[2] == '1' ) ) { 98 | bool onoff = !!(cp[2] & 1); 99 | printf("LED%d = %d\n",ledx,onoff); 100 | if ( onoff != !!digitalRead(leds[ledx]) ) { 101 | digitalWrite(leds[ledx],onoff?HIGH:LOW); 102 | changedf = true; 103 | } 104 | } 105 | } 106 | if ( changedf ) 107 | xEventGroupSetBits(hevt,LED_CHG); 108 | } 109 | 110 | client.println(""); 111 | client.println(""); 113 | client.println(""); 114 | client.println(""); 124 | client.println("

ESP32 Event Groups " 125 | "(eventgr.ino)

"); 126 | 127 | for ( int x=0; xLED%d - State ",x); 132 | client.println(temp); 133 | client.println(String(state ? "on" : "off") + "

"); 134 | client.println("

"); 140 | } 141 | 142 | client.println(""); 143 | client.println(); 144 | break; 145 | } 146 | client.stop(); 147 | header = ""; 148 | Serial.println("Client disconnected."); 149 | Serial.println(""); 150 | } 151 | } 152 | } 153 | 154 | static void udp_broadcast(void *arg) { 155 | 156 | xEventGroupWaitBits( 157 | hevt, // Event Group 158 | WIFI_RDY, // bits to wait for 159 | pdFALSE, // no clear 160 | pdFALSE, // wait for all bits 161 | portMAX_DELAY); // timeout 162 | 163 | // Determine IPv4 broadcast address: 164 | 165 | auto localip = WiFi.localIP(); 166 | auto subnet = WiFi.subnetMask(); 167 | auto broadcast = localip; 168 | 169 | for ( short x=0; x<4; ++x ) { 170 | broadcast[x] = 0xFF & ~(subnet[x]); 171 | broadcast[x] |= localip[x] & subnet[x]; 172 | } 173 | 174 | printf("UDP ready: netmask %s broadcast %s\n", 175 | subnet.toString().c_str(), 176 | broadcast.toString().c_str() 177 | ); 178 | 179 | // Send "Ready:\n" 180 | udp.beginPacket(broadcast,9000); 181 | udp.write((uint8_t const*)"Ready:\n",7); 182 | udp.endPacket(); 183 | 184 | for (;;) { 185 | xEventGroupWaitBits( 186 | hevt, // handle 187 | LED_CHG, // bits to wait for 188 | pdTRUE, // clear bits 189 | pdFALSE, // wait for all bits 190 | portMAX_DELAY); // timeout 191 | char temp[16]; 192 | 193 | // Send UDP packet: 194 | udp.beginPacket(broadcast,9000); 195 | for ( short x=0; x 14 | #include "driver/periph_ctrl.h" 15 | #include "driver/pcnt.h" 16 | 17 | // In landscape mode; 18 | static unsigned height = TFT_WIDTH; 19 | static unsigned width = TFT_HEIGHT; 20 | 21 | static int app_cpu = 0; 22 | static pcnt_isr_handle_t isr_handle = nullptr; 23 | static SemaphoreHandle_t sem_h; 24 | static QueueHandle_t evtq_h; 25 | 26 | static void IRAM_ATTR pulse_isr(void *arg) { 27 | static uint32_t usecs0; 28 | uint32_t intr_status = PCNT.int_st.val; 29 | uint32_t evt_status, usecs; 30 | BaseType_t woken = pdFALSE; 31 | 32 | if ( intr_status & BIT(0) ) { 33 | // PCNT_UNIT_0 Interrupt 34 | evt_status = PCNT.status_unit[0].val; 35 | if ( evt_status & PCNT_STATUS_THRES0_M ) { 36 | usecs0 = micros(); 37 | } else if ( evt_status & PCNT_STATUS_THRES1_M ) { 38 | usecs = micros() - usecs0; 39 | xQueueSendFromISR(evtq_h,&usecs,&woken); 40 | pcnt_counter_pause(PCNT_UNIT_0); 41 | } 42 | PCNT.int_clr.val = BIT(0); 43 | } 44 | if ( woken ) { 45 | portYIELD_FROM_ISR(); 46 | } 47 | } 48 | 49 | static void lock_lcd() { 50 | xSemaphoreTake(sem_h,portMAX_DELAY); 51 | } 52 | 53 | static void unlock_lcd() { 54 | xSemaphoreGive(sem_h); 55 | } 56 | 57 | static void lcd_gen(uint32_t f,uint32_t adc) { 58 | static const int vh = height/3; 59 | static const int cw = 26; 60 | static const int ch = 60; 61 | unsigned bw = adc * 100 / 4096; 62 | 63 | lock_lcd(); 64 | M5.Lcd.fillRoundRect(cw,vh-60,width-2*cw,ch,6,OLIVE); 65 | M5.Lcd.setTextColor(WHITE); 66 | M5.Lcd.setTextSize(3); 67 | M5.Lcd.setCursor(cw+8,vh-ch+18); 68 | M5.Lcd.printf("PWM: %6u Hz\n",f); 69 | M5.Lcd.fillRect(cw,height-vh/2-10,width-2*cw,ch,GREEN); 70 | M5.Lcd.progressBar(cw,height-vh/2-10,width-2*cw,ch/2,bw); 71 | unlock_lcd(); 72 | } 73 | 74 | static void lcd_freq(uint32_t f) { 75 | static const int vh = height/3*2; 76 | static const int cw = 26; 77 | static const int ch = 60; 78 | 79 | lock_lcd(); 80 | M5.Lcd.fillRoundRect(cw,vh-60,width-2*cw,ch,6,BLUE); 81 | M5.Lcd.setTextColor(WHITE); 82 | M5.Lcd.setTextSize(3); 83 | M5.Lcd.setCursor(cw+8,vh-ch+18); 84 | M5.Lcd.printf("Freq:%6u Hz\n",f); 85 | unlock_lcd(); 86 | } 87 | 88 | static uint32_t retarget(uint32_t freq,uint32_t usec) { 89 | static const uint32_t target_usecs = 100000; 90 | uint64_t f = freq, t, u; 91 | 92 | auto target = [&freq](uint32_t usec) { 93 | if ( freq > 100000 ) 94 | return uint64_t(freq) / 1000 * usec / 1000 + 10; 95 | else 96 | return uint64_t(freq) * usec / 1000000 + 10; 97 | }; 98 | 99 | auto useconds = [&freq](uint64_t t) { 100 | return (t - 10) * 1000000 / freq; 101 | }; 102 | 103 | if ( (t = target(usec)) > 32000 ) { 104 | t = target(target_usecs); 105 | if ( t > 32500 ) 106 | t = 32500; 107 | u = useconds(t); 108 | t = target(u); 109 | } else { 110 | t = target(target_usecs); 111 | if ( t < 25 ) 112 | t = 25; 113 | u = useconds(t); 114 | t = target(u); 115 | } 116 | if ( t > 32500 ) 117 | t = 32500; 118 | else if ( t < 25 ) 119 | t = 25; 120 | return t; 121 | } 122 | 123 | static void monitor(void *arg) { 124 | uint32_t usecs; 125 | int16_t thres; 126 | BaseType_t rc; 127 | 128 | for (;;) { 129 | rc = pcnt_counter_clear(PCNT_UNIT_0); 130 | assert(!rc); 131 | xQueueReset(evtq_h); 132 | rc = pcnt_counter_resume(PCNT_UNIT_0); 133 | assert(!rc); 134 | 135 | rc = pcnt_get_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,&thres); 136 | assert(!rc); 137 | rc = xQueueReceive(evtq_h,&usecs,500); 138 | if ( rc == pdPASS ) { 139 | uint32_t freq = uint64_t(thres-10) 140 | * uint64_t(1000000) / usecs; 141 | lcd_freq(freq); 142 | thres = retarget(freq,usecs); 143 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,thres); 144 | assert(!rc); 145 | } else { 146 | rc = pcnt_counter_pause(PCNT_UNIT_0); 147 | assert(!rc); 148 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,25); 149 | assert(!rc); 150 | } 151 | } 152 | } 153 | 154 | static void m5_init() { 155 | static const int cw = 26; 156 | static const int ch = 60; 157 | 158 | M5.begin(true,false,false,false); 159 | M5.Power.begin(); 160 | dacWrite(25,0); // Silence speaker 161 | M5.Lcd.fillScreen(GREEN); 162 | M5.Lcd.fillRoundRect(cw,height/2-ch/2,width-2*cw,ch,6,BLUE); 163 | M5.Lcd.setTextColor(WHITE); 164 | M5.Lcd.setTextSize(3); 165 | M5.Lcd.setCursor(cw+4,height/2-10); 166 | M5.Lcd.printf("freqctr-m5.ino"); 167 | M5.setWakeupButton(BUTTON_A_PIN); 168 | 169 | delay(2000); 170 | M5.Lcd.fillScreen(GREEN); 171 | } 172 | 173 | static void analog_init() { 174 | adcAttachPin(GPIO_ADC); 175 | analogReadResolution(12); 176 | analogSetPinAttenuation(GPIO_ADC,ADC_11db); 177 | } 178 | 179 | static void counter_init() { 180 | pcnt_config_t cfg; 181 | int rc; 182 | 183 | memset(&cfg,0,sizeof cfg); 184 | cfg.pulse_gpio_num = GPIO_PULSEIN; 185 | cfg.ctrl_gpio_num = PCNT_PIN_NOT_USED; 186 | cfg.channel = PCNT_CHANNEL_0; 187 | cfg.unit = PCNT_UNIT_0; 188 | cfg.pos_mode = PCNT_COUNT_INC; // Count up on the positive edge 189 | cfg.neg_mode = PCNT_COUNT_DIS; 190 | cfg.lctrl_mode = PCNT_MODE_KEEP; 191 | cfg.hctrl_mode = PCNT_MODE_KEEP; 192 | cfg.counter_h_lim = 32767; 193 | cfg.counter_l_lim = 0; 194 | rc = pcnt_unit_config(&cfg); 195 | assert(!rc); 196 | 197 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_0,10); 198 | assert(!rc); 199 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,10000); 200 | assert(!rc); 201 | rc = pcnt_event_enable(PCNT_UNIT_0,PCNT_EVT_THRES_0); 202 | assert(!rc); 203 | rc = pcnt_event_enable(PCNT_UNIT_0,PCNT_EVT_THRES_1); 204 | assert(!rc); 205 | rc = pcnt_counter_pause(PCNT_UNIT_0); 206 | assert(!rc); 207 | rc = pcnt_isr_register(pulse_isr,nullptr,0,&isr_handle); 208 | assert(!rc); 209 | rc = pcnt_intr_enable(PCNT_UNIT_0); 210 | assert(!rc); 211 | } 212 | 213 | void setup() { 214 | unsigned ms; 215 | BaseType_t rc; // Return code 216 | 217 | app_cpu = xPortGetCoreID(); 218 | sem_h = xSemaphoreCreateMutex(); 219 | assert(sem_h); 220 | evtq_h = xQueueCreate(20,sizeof(uint32_t)); 221 | assert(evtq_h); 222 | 223 | // Use PWM to drive CLKIN 224 | ledcSetup(PWM_CH,PWM_FREQ,PWM_RES); 225 | ledcAttachPin(PWM_GPIO,PWM_CH); 226 | ledcWrite(PWM_CH,1); // 50% 227 | 228 | counter_init(); 229 | m5_init(); 230 | 231 | // Start the monitor task 232 | rc = xTaskCreatePinnedToCore( 233 | monitor, // Function 234 | "monitor", // Name 235 | 4096, // Stack size 236 | nullptr, // Argument 237 | 1, // Priority 238 | nullptr, // Handle ptr 239 | app_cpu // CPU 240 | ); 241 | assert(rc == pdPASS); 242 | } 243 | 244 | void loop() { 245 | uint32_t f; // Frequency 246 | uint32_t adc; 247 | 248 | f = (adc = analogRead(GPIO_ADC)) * 80 + 500; 249 | lcd_gen(f,adc); 250 | 251 | ledcSetup(PWM_CH,f,PWM_RES); 252 | ledcAttachPin(PWM_GPIO,PWM_CH); 253 | ledcWrite(PWM_CH,1); // 50% 254 | 255 | M5.update(); 256 | if ( M5.BtnA.wasPressed() ) 257 | M5.powerOFF(); 258 | delay(500); 259 | } 260 | 261 | -------------------------------------------------------------------------------- /freqctr-ttgo/freqctr-ttgo.ino: -------------------------------------------------------------------------------- 1 | // freqctr.ino - TTGO ESP32 T-Display 2 | 3 | #define GPIO_PULSEIN 25 4 | #define GPIO_FREQGEN 26 5 | #define GPIO_ADC 15 6 | 7 | // GPIO for PWM output 8 | #define PWM_GPIO GPIO_FREQGEN 9 | #define PWM_CH 0 10 | #define PWM_FREQ 2000 11 | #define PWM_RES 1 12 | 13 | #include 14 | #include 15 | #include "driver/periph_ctrl.h" 16 | #include "driver/pcnt.h" 17 | 18 | static TFT_eSPI tft; 19 | 20 | static int app_cpu = 0; 21 | static pcnt_isr_handle_t isr_handle = nullptr; 22 | static SemaphoreHandle_t sem_h; 23 | static QueueHandle_t evtq_h; 24 | 25 | static void IRAM_ATTR pulse_isr(void *arg) { 26 | static uint32_t usecs0; 27 | uint32_t intr_status = PCNT.int_st.val; 28 | uint32_t evt_status, usecs; 29 | BaseType_t woken = pdFALSE; 30 | 31 | if ( intr_status & BIT(0) ) { 32 | // PCNT_UNIT_0 Interrupt 33 | evt_status = PCNT.status_unit[0].val; 34 | if ( evt_status & PCNT_STATUS_THRES0_M ) { 35 | usecs0 = micros(); 36 | } else if ( evt_status & PCNT_STATUS_THRES1_M ) { 37 | usecs = micros() - usecs0; 38 | xQueueSendFromISR(evtq_h,&usecs,&woken); 39 | pcnt_counter_pause(PCNT_UNIT_0); 40 | } 41 | PCNT.int_clr.val = BIT(0); 42 | } 43 | if ( woken ) { 44 | portYIELD_FROM_ISR(); 45 | } 46 | } 47 | 48 | static void lock_tft() { 49 | xSemaphoreTake(sem_h,portMAX_DELAY); 50 | } 51 | 52 | static void unlock_tft() { 53 | xSemaphoreGive(sem_h); 54 | } 55 | 56 | static void tft_gen(uint32_t f) { 57 | char buf[32]; 58 | const char *cp; 59 | 60 | snprintf(buf,sizeof buf,"%u Hz",f); 61 | 62 | lock_tft(); 63 | int16_t height = tft.height() / 2; 64 | int16_t width = tft.width(); 65 | int16_t fheight = tft.fontHeight(4); 66 | tft.fillRect(0,0,width,height,TFT_GREEN); 67 | tft.setTextColor(TFT_BLACK); 68 | tft.setTextFont(4); 69 | cp = "PWM Generator:"; 70 | tft.drawString(cp, 71 | (width-tft.textWidth(cp))/2, 72 | (height/2-fheight)); 73 | tft.drawString(buf, 74 | (width-tft.textWidth(buf))/2, 75 | (height/2)); 76 | unlock_tft(); 77 | } 78 | 79 | static void tft_freq(uint32_t f) { 80 | char buf[32]; 81 | const char *cp; 82 | 83 | snprintf(buf,sizeof buf,"%u Hz",f); 84 | lock_tft(); 85 | int16_t height = tft.height() / 2; 86 | int16_t width = tft.width(); 87 | int16_t fheight = tft.fontHeight(4); 88 | tft.fillRect(0,height,width,tft.height(),TFT_BLUE); 89 | tft.setTextColor(TFT_WHITE); 90 | tft.setTextFont(4); 91 | cp = "Freq. Counter:"; 92 | tft.drawString(cp, 93 | (width-tft.textWidth(cp))/2, 94 | height+height/2-fheight+5); 95 | tft.drawString(buf, 96 | (width-tft.textWidth(buf))/2, 97 | height+height/2+5); 98 | unlock_tft(); 99 | } 100 | 101 | static uint32_t retarget(uint32_t freq,uint32_t usec) { 102 | static const uint32_t target_usecs = 100000; 103 | uint64_t f = freq, t, u; 104 | 105 | auto target = [&freq](uint32_t usec) { 106 | if ( freq > 100000 ) 107 | return uint64_t(freq) / 1000 * usec / 1000 + 10; 108 | else 109 | return uint64_t(freq) * usec / 1000000 + 10; 110 | }; 111 | 112 | auto useconds = [&freq](uint64_t t) { 113 | return (t - 10) * 1000000 / freq; 114 | }; 115 | 116 | if ( (t = target(usec)) > 32000 ) { 117 | t = target(target_usecs); 118 | if ( t > 32500 ) 119 | t = 32500; 120 | u = useconds(t); 121 | t = target(u); 122 | } else { 123 | t = target(target_usecs); 124 | if ( t < 25 ) 125 | t = 25; 126 | u = useconds(t); 127 | t = target(u); 128 | } 129 | if ( t > 32500 ) 130 | t = 32500; 131 | else if ( t < 25 ) 132 | t = 25; 133 | return t; 134 | } 135 | 136 | static void monitor(void *arg) { 137 | uint32_t usecs; 138 | int16_t thres; 139 | BaseType_t rc; 140 | 141 | for (;;) { 142 | rc = pcnt_counter_clear(PCNT_UNIT_0); 143 | assert(!rc); 144 | xQueueReset(evtq_h); 145 | rc = pcnt_counter_resume(PCNT_UNIT_0); 146 | assert(!rc); 147 | 148 | rc = pcnt_get_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,&thres); 149 | assert(!rc); 150 | rc = xQueueReceive(evtq_h,&usecs,500); 151 | if ( rc == pdPASS ) { 152 | uint32_t freq = uint64_t(thres-10) 153 | * uint64_t(1000000) / usecs; 154 | tft_freq(freq); 155 | thres = retarget(freq,usecs); 156 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,thres); 157 | assert(!rc); 158 | } else { 159 | rc = pcnt_counter_pause(PCNT_UNIT_0); 160 | assert(!rc); 161 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,25); 162 | assert(!rc); 163 | } 164 | } 165 | } 166 | 167 | static void tft_init() { 168 | const char *cp; 169 | 170 | tft.init(); 171 | tft.setRotation(1); // Landscape 172 | tft.fillScreen(TFT_GREEN); 173 | tft.setTextColor(TFT_BLACK); 174 | tft.setTextFont(4); 175 | cp = "freqctr-ttgo.ino"; 176 | tft.drawString(cp, 177 | (tft.width()-tft.textWidth(cp))/2, 178 | (tft.height()-tft.fontHeight(4))/2); 179 | delay(2000); 180 | tft.fillScreen(TFT_RED); 181 | } 182 | 183 | static void analog_init() { 184 | adcAttachPin(GPIO_ADC); 185 | analogReadResolution(12); 186 | analogSetPinAttenuation(GPIO_ADC,ADC_11db); 187 | } 188 | 189 | static void counter_init() { 190 | pcnt_config_t cfg; 191 | int rc; 192 | 193 | memset(&cfg,0,sizeof cfg); 194 | cfg.pulse_gpio_num = GPIO_PULSEIN; 195 | cfg.ctrl_gpio_num = PCNT_PIN_NOT_USED; 196 | cfg.channel = PCNT_CHANNEL_0; 197 | cfg.unit = PCNT_UNIT_0; 198 | cfg.pos_mode = PCNT_COUNT_INC; // Count up on the positive edge 199 | cfg.neg_mode = PCNT_COUNT_DIS; 200 | cfg.lctrl_mode = PCNT_MODE_KEEP; 201 | cfg.hctrl_mode = PCNT_MODE_KEEP; 202 | cfg.counter_h_lim = 32767; 203 | cfg.counter_l_lim = 0; 204 | rc = pcnt_unit_config(&cfg); 205 | assert(!rc); 206 | 207 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_0,10); 208 | assert(!rc); 209 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,10000); 210 | assert(!rc); 211 | rc = pcnt_event_enable(PCNT_UNIT_0,PCNT_EVT_THRES_0); 212 | assert(!rc); 213 | rc = pcnt_event_enable(PCNT_UNIT_0,PCNT_EVT_THRES_1); 214 | assert(!rc); 215 | rc = pcnt_counter_pause(PCNT_UNIT_0); 216 | assert(!rc); 217 | rc = pcnt_isr_register(pulse_isr,nullptr,0,&isr_handle); 218 | assert(!rc); 219 | rc = pcnt_intr_enable(PCNT_UNIT_0); 220 | assert(!rc); 221 | } 222 | 223 | void setup() { 224 | unsigned ms; 225 | BaseType_t rc; // Return code 226 | 227 | app_cpu = xPortGetCoreID(); 228 | sem_h = xSemaphoreCreateMutex(); 229 | assert(sem_h); 230 | evtq_h = xQueueCreate(20,sizeof(uint32_t)); 231 | assert(evtq_h); 232 | 233 | // Use PWM to drive CLKIN 234 | ledcSetup(PWM_CH,PWM_FREQ,PWM_RES); 235 | ledcAttachPin(PWM_GPIO,PWM_CH); 236 | ledcWrite(PWM_CH,1); // 50% 237 | 238 | counter_init(); 239 | tft_init(); 240 | 241 | // Start the monitor task 242 | rc = xTaskCreatePinnedToCore( 243 | monitor, // Function 244 | "monitor", // Name 245 | 4096, // Stack size 246 | nullptr, // Argument 247 | 1, // Priority 248 | nullptr, // Handle ptr 249 | app_cpu // CPU 250 | ); 251 | assert(rc == pdPASS); 252 | } 253 | 254 | void loop() { 255 | uint32_t f; // Frequency 256 | 257 | f = analogRead(GPIO_ADC) * 80 + 500; 258 | tft_gen(f); 259 | 260 | ledcSetup(PWM_CH,f,PWM_RES); 261 | ledcAttachPin(PWM_GPIO,PWM_CH); 262 | ledcWrite(PWM_CH,1); // 50% 263 | delay(500); 264 | } 265 | 266 | -------------------------------------------------------------------------------- /freqctr/freqctr.ino: -------------------------------------------------------------------------------- 1 | // freqctr.ino - Wemos Lolin ESP32 2 | 3 | #define GPIO_PULSEIN 25 4 | #define GPIO_FREQGEN 26 5 | #define GPIO_ADC 14 6 | 7 | // GPIO for PWM output 8 | #define PWM_GPIO GPIO_FREQGEN 9 | #define PWM_CH 0 10 | #define PWM_FREQ 2000 11 | #define PWM_RES 1 12 | 13 | #include "SSD1306.h" 14 | #include "driver/periph_ctrl.h" 15 | #include "driver/pcnt.h" 16 | 17 | #define SSD1306_ADDR 0x3C 18 | #define SSD1306_SDA 5 19 | #define SSD1306_SCL 4 20 | 21 | static SSD1306 oled( 22 | SSD1306_ADDR, 23 | SSD1306_SDA, 24 | SSD1306_SCL 25 | ); 26 | 27 | static int app_cpu = 0; 28 | static pcnt_isr_handle_t isr_handle = nullptr; 29 | static SemaphoreHandle_t sem_h; 30 | static QueueHandle_t evtq_h; 31 | 32 | static void IRAM_ATTR pulse_isr(void *arg) { 33 | static uint32_t usecs0; 34 | uint32_t intr_status = PCNT.int_st.val; 35 | uint32_t evt_status, usecs; 36 | BaseType_t woken = pdFALSE; 37 | 38 | if ( intr_status & BIT(0) ) { 39 | // PCNT_UNIT_0 Interrupt 40 | evt_status = PCNT.status_unit[0].val; 41 | if ( evt_status & PCNT_STATUS_THRES0_M ) { 42 | usecs0 = micros(); 43 | } else if ( evt_status & PCNT_STATUS_THRES1_M ) { 44 | usecs = micros() - usecs0; 45 | xQueueSendFromISR(evtq_h,&usecs,&woken); 46 | pcnt_counter_pause(PCNT_UNIT_0); 47 | } 48 | PCNT.int_clr.val = BIT(0); 49 | } 50 | if ( woken ) { 51 | portYIELD_FROM_ISR(); 52 | } 53 | } 54 | 55 | static void lock_oled() { 56 | xSemaphoreTake(sem_h,portMAX_DELAY); 57 | } 58 | 59 | static void unlock_oled() { 60 | xSemaphoreGive(sem_h); 61 | } 62 | 63 | static void oled_gen(uint32_t f) { 64 | char buf[32]; 65 | 66 | snprintf(buf,sizeof buf,"%u gen",f); 67 | lock_oled(); 68 | oled.setColor(BLACK); 69 | oled.fillRect(0,0,128,31); 70 | oled.setColor(WHITE); 71 | oled.drawString(64,0,buf); 72 | oled.drawHorizontalLine(0,26,128); 73 | oled.display(); 74 | unlock_oled(); 75 | } 76 | 77 | static void oled_freq(uint32_t f) { 78 | char buf[32]; 79 | 80 | snprintf(buf,sizeof buf,"%u Hz",f); 81 | lock_oled(); 82 | oled.setColor(BLACK); 83 | oled.fillRect(0,32,128,63); 84 | oled.setColor(WHITE); 85 | oled.drawString(64,32,buf); 86 | oled.display(); 87 | unlock_oled(); 88 | } 89 | 90 | static uint32_t retarget(uint32_t freq,uint32_t usec) { 91 | static const uint32_t target_usecs = 100000; 92 | uint64_t f = freq, t, u; 93 | 94 | auto target = [&freq](uint32_t usec) { 95 | if ( freq > 100000 ) 96 | return uint64_t(freq) / 1000 * usec / 1000 + 10; 97 | else 98 | return uint64_t(freq) * usec / 1000000 + 10; 99 | }; 100 | 101 | auto useconds = [&freq](uint64_t t) { 102 | return (t - 10) * 1000000 / freq; 103 | }; 104 | 105 | if ( (t = target(usec)) > 32000 ) { 106 | t = target(target_usecs); 107 | if ( t > 32500 ) 108 | t = 32500; 109 | u = useconds(t); 110 | t = target(u); 111 | } else { 112 | t = target(target_usecs); 113 | if ( t < 25 ) 114 | t = 25; 115 | u = useconds(t); 116 | t = target(u); 117 | } 118 | if ( t > 32500 ) 119 | t = 32500; 120 | else if ( t < 25 ) 121 | t = 25; 122 | return t; 123 | } 124 | 125 | static void monitor(void *arg) { 126 | uint32_t usecs; 127 | int16_t thres; 128 | BaseType_t rc; 129 | 130 | for (;;) { 131 | rc = pcnt_counter_clear(PCNT_UNIT_0); 132 | assert(!rc); 133 | xQueueReset(evtq_h); 134 | rc = pcnt_counter_resume(PCNT_UNIT_0); 135 | assert(!rc); 136 | 137 | rc = pcnt_get_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,&thres); 138 | assert(!rc); 139 | rc = xQueueReceive(evtq_h,&usecs,500); 140 | if ( rc == pdPASS ) { 141 | uint32_t freq = uint64_t(thres-10) 142 | * uint64_t(1000000) / usecs; 143 | oled_freq(freq); 144 | thres = retarget(freq,usecs); 145 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,thres); 146 | assert(!rc); 147 | } else { 148 | rc = pcnt_counter_pause(PCNT_UNIT_0); 149 | assert(!rc); 150 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,25); 151 | assert(!rc); 152 | } 153 | } 154 | } 155 | 156 | static void oled_init() { 157 | oled.init(); 158 | oled.clear(); 159 | oled.flipScreenVertically(); 160 | oled.invertDisplay(); 161 | oled.setTextAlignment(TEXT_ALIGN_CENTER); 162 | oled.setFont(ArialMT_Plain_24); 163 | oled.drawString(64,0,"freqctr.ino"); 164 | oled.drawHorizontalLine(0,0,128); 165 | oled.drawHorizontalLine(0,26,128); 166 | oled.display(); 167 | } 168 | 169 | static void analog_init() { 170 | adcAttachPin(GPIO_ADC); 171 | analogReadResolution(12); 172 | analogSetPinAttenuation(GPIO_ADC,ADC_11db); 173 | } 174 | 175 | static void counter_init() { 176 | pcnt_config_t cfg; 177 | int rc; 178 | 179 | memset(&cfg,0,sizeof cfg); 180 | cfg.pulse_gpio_num = GPIO_PULSEIN; 181 | cfg.ctrl_gpio_num = PCNT_PIN_NOT_USED; 182 | cfg.channel = PCNT_CHANNEL_0; 183 | cfg.unit = PCNT_UNIT_0; 184 | cfg.pos_mode = PCNT_COUNT_INC; // Count up on the positive edge 185 | cfg.neg_mode = PCNT_COUNT_DIS; 186 | cfg.lctrl_mode = PCNT_MODE_KEEP; 187 | cfg.hctrl_mode = PCNT_MODE_KEEP; 188 | cfg.counter_h_lim = 32767; 189 | cfg.counter_l_lim = 0; 190 | rc = pcnt_unit_config(&cfg); 191 | assert(!rc); 192 | 193 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_0,10); 194 | assert(!rc); 195 | rc = pcnt_set_event_value(PCNT_UNIT_0,PCNT_EVT_THRES_1,10000); 196 | assert(!rc); 197 | rc = pcnt_event_enable(PCNT_UNIT_0,PCNT_EVT_THRES_0); 198 | assert(!rc); 199 | rc = pcnt_event_enable(PCNT_UNIT_0,PCNT_EVT_THRES_1); 200 | assert(!rc); 201 | rc = pcnt_counter_pause(PCNT_UNIT_0); 202 | assert(!rc); 203 | rc = pcnt_isr_register(pulse_isr,nullptr,0,&isr_handle); 204 | assert(!rc); 205 | rc = pcnt_intr_enable(PCNT_UNIT_0); 206 | assert(!rc); 207 | } 208 | 209 | void setup() { 210 | unsigned ms; 211 | BaseType_t rc; // Return code 212 | 213 | app_cpu = xPortGetCoreID(); 214 | sem_h = xSemaphoreCreateMutex(); 215 | assert(sem_h); 216 | evtq_h = xQueueCreate(20,sizeof(uint32_t)); 217 | assert(evtq_h); 218 | 219 | // Use PWM to drive CLKIN 220 | ledcSetup(PWM_CH,PWM_FREQ,PWM_RES); 221 | ledcAttachPin(PWM_GPIO,PWM_CH); 222 | ledcWrite(PWM_CH,1); // 50% 223 | 224 | counter_init(); 225 | oled_init(); 226 | delay(2000); 227 | 228 | // Start the monitor task 229 | rc = xTaskCreatePinnedToCore( 230 | monitor, // Function 231 | "monitor", // Name 232 | 4096, // Stack size 233 | nullptr, // Argument 234 | 1, // Priority 235 | nullptr, // Handle ptr 236 | app_cpu // CPU 237 | ); 238 | assert(rc == pdPASS); 239 | } 240 | 241 | void loop() { 242 | uint32_t f; // Frequency 243 | 244 | f = analogRead(GPIO_ADC) * 80 + 500; 245 | oled_gen(f); 246 | 247 | ledcSetup(PWM_CH,f,PWM_RES); 248 | ledcAttachPin(PWM_GPIO,PWM_CH); 249 | ledcWrite(PWM_CH,1); // 50% 250 | delay(500); 251 | } 252 | 253 | -------------------------------------------------------------------------------- /gatekeeper/gatekeeper.ino: -------------------------------------------------------------------------------- 1 | // gatekeeper.ino 2 | 3 | // GPIOs used for I2C 4 | #define I2C_SDA 25 5 | #define I2C_SCL 26 6 | 7 | // Set to 1 for PCF8574A 8 | #define PCF8574A 0 9 | 10 | // Buttons: 11 | #define BUTTON0 12 // P4 on dev1 12 | #define BUTTON1 5 // P5 on dev0 13 | #define BUTTON2 4 // P4 on dev0 14 | #define NB 3 // # Buttons 15 | 16 | // LEDs: 17 | #define LED0 1 // P1 on dev0 18 | #define LED1 2 // P2 on dev0 19 | #define LED2 3 // P3 on dev0 20 | 21 | #define LED3 8 // P0 on dev1 22 | 23 | #include 24 | 25 | #if PCF8574A 26 | // Newer PCF8574A addresses 27 | #define DEV0 0x38 28 | #define DEV1 0x39 29 | #else 30 | // Original PCF8574 addresses 31 | #define DEV0 0x20 32 | #define DEV1 0x21 33 | #endif 34 | 35 | #define N_DEV 2 // PCF8574 devices 36 | #define GATEKRDY 0b0001 // Gatekeeper ready 37 | 38 | #define IO_RDY 0b0001 // Task notification 39 | #define IO_ERROR 0b0010 // Task notification 40 | #define IO_BIT 0b0100 // Task notification 41 | 42 | #define STOP int(1) // Arduino I2C API 43 | 44 | static struct s_gatekeeper { 45 | EventGroupHandle_t grpevt; // Group Event handle(*) 46 | QueueHandle_t queue; // Request queue handle 47 | uint8_t states[N_DEV]; // Current states 48 | } gatekeeper = { 49 | nullptr, nullptr, { 0xFF, 0xFF } 50 | }; 51 | 52 | // Message struct for Message/Response queue 53 | struct s_ioport { 54 | uint8_t input : 1; // 1=input else output 55 | uint8_t value : 1; // Bit value 56 | uint8_t error : 1; // Error bit, when used 57 | uint8_t port : 5; // Port number 58 | TaskHandle_t htask; // Reply Task handle 59 | }; 60 | 61 | // Gatekeeper task: Owns I2C bus operations and 62 | // state management of the PCF8574P devices. 63 | 64 | static void gatekeeper_task(void *arg) { 65 | static int i2caddr[N_DEV] = { DEV0, DEV1 }; 66 | int addr; // Device I2C address 67 | uint8_t devx, portx; // Device index, bit index 68 | s_ioport ioport; // Queue message pointer 69 | uint8_t data; // Temp data byte 70 | uint32_t notify; // Task Notification word 71 | BaseType_t rc; // Return code 72 | 73 | // Create API communication queues 74 | gatekeeper.queue = xQueueCreate(8,sizeof ioport); 75 | assert(gatekeeper.queue); 76 | 77 | // Start I2C Bus Support: 78 | Wire.begin(I2C_SDA,I2C_SCL); 79 | 80 | // Configure all GPIOs as inputs 81 | // by writing 0xFF 82 | for ( devx=0; devx 0 ) { 112 | data = Wire.read(); // Read all bits 113 | ioport.error = false; // Successful 114 | ioport.value = !!(data & (1 << portx)); 115 | } else { 116 | // Return GPIO fail: 117 | ioport.error = true; 118 | ioport.value = false; 119 | } 120 | } else { 121 | // COMMAND: WRITE A GPIO BIT: 122 | data = gatekeeper.states[devx]; 123 | if ( ioport.value ) 124 | data |= 1 << portx; // Set a bit 125 | else 126 | data &= ~(1 << portx); // Clear a bit 127 | Wire.beginTransmission(addr); 128 | Wire.write(data); 129 | ioport.error = Wire.endTransmission() != 0; 130 | if ( !ioport.error ) 131 | gatekeeper.states[devx] = data; 132 | } 133 | 134 | notify = IO_RDY; 135 | if ( ioport.error ) 136 | notify |= IO_ERROR; 137 | if ( ioport.value ) 138 | notify |= IO_BIT; 139 | 140 | // Notify client about completion 141 | if ( ioport.htask ) 142 | xTaskNotify( 143 | ioport.htask, 144 | notify, 145 | eSetValueWithOverwrite); 146 | } 147 | } 148 | 149 | // Block caller until gatekeeper ready: 150 | static void pcf8574_wait_ready() { 151 | 152 | xEventGroupWaitBits( 153 | gatekeeper.grpevt, // Event group handle 154 | GATEKRDY, // Bits to wait for 155 | 0, // Bits to clear 156 | pdFALSE, // Wait for all bits 157 | portMAX_DELAY // Wait forever 158 | ); 159 | } 160 | 161 | // Get GPIO pin status: 162 | // RETURNS: 163 | // 0 - GPIO is low 164 | // 1 - GPIO is high 165 | // -1 - Failed to read GPIO 166 | 167 | static short pcf8574_get(uint8_t port) { 168 | s_ioport ioport; // Port pin (0..15) 169 | uint32_t notify; // Returned notification word 170 | BaseType_t rc; 171 | 172 | assert(port < 16); 173 | ioport.input = true; // Read request 174 | ioport.port = port; // 0..15 port pin 175 | ioport.htask = xTaskGetCurrentTaskHandle(); 176 | 177 | pcf8574_wait_ready(); // Block until ready 178 | 179 | rc = xQueueSendToBack( 180 | gatekeeper.queue, 181 | &ioport, 182 | portMAX_DELAY); 183 | assert(rc == pdPASS); 184 | 185 | 186 | // Wait to be notified: 187 | rc = xTaskNotifyWait( 188 | 0, // no clear on entry 189 | IO_RDY|IO_ERROR|IO_BIT, // clear on exit 190 | ¬ify, 191 | portMAX_DELAY); 192 | assert(rc == pdTRUE); 193 | 194 | return (notify & IO_ERROR) 195 | ? -1 196 | : !!(notify & IO_BIT); 197 | } 198 | 199 | // Write GPIO pin for a PCF8574 port: 200 | // RETURNS: 201 | // 0 or 1: Succesful bit write 202 | // -1: Failed GPIO write 203 | 204 | static short pcf8574_put(uint8_t port,bool value) { 205 | s_ioport ioport; // Port pin (0..15) 206 | BaseType_t rc; 207 | uint32_t notify; // Returned notification word 208 | 209 | assert(port < 16); 210 | ioport.input = false; // Write request 211 | ioport.value = value; // Bit value 212 | ioport.port = port; // PCF8574 port pin 213 | ioport.htask = xTaskGetCurrentTaskHandle(); 214 | 215 | pcf8574_wait_ready(); // Block until ready 216 | 217 | rc = xQueueSendToBack( 218 | gatekeeper.queue, 219 | &ioport, 220 | portMAX_DELAY); 221 | assert(rc == pdPASS); 222 | 223 | // Wait to be notified: 224 | rc = xTaskNotifyWait( 225 | 0, // no clear on entry 226 | IO_RDY|IO_ERROR|IO_BIT, // clear on exit 227 | ¬ify, 228 | portMAX_DELAY); 229 | assert(rc == pdTRUE); 230 | 231 | return (notify & IO_ERROR) 232 | ? -1 233 | : !!(notify & IO_BIT); 234 | } 235 | 236 | // User task: Uses gatekeeper task for 237 | // reading/writing PCF8574 port pins. 238 | // 239 | // Pins: 240 | // 0..7 Device 0 (address DEV0) 241 | // 8..15 Device 1 (address DEV1) 242 | // 243 | // Detect button press, and then activate 244 | // corresponding LED. 245 | 246 | static void usr_task1(void *argp) { 247 | static const struct s_state { 248 | uint8_t button; 249 | uint8_t led; 250 | } states[3] = { 251 | { BUTTON0, LED0 }, 252 | { BUTTON1, LED1 }, 253 | { BUTTON2, LED2 } 254 | }; 255 | short rc; 256 | 257 | // Initialize all LEDs high (inactive): 258 | for ( unsigned bx=0; bx 0 && usecs < 50000UL ) 83 | report_cm(usecs); 84 | else 85 | printf("No echo\n"); 86 | } 87 | } 88 | 89 | // 90 | // Send sync to range_task every 1 sec 91 | // 92 | static void sync_task(void *argp) { 93 | BaseType_t rc; 94 | TickType_t ticktime; 95 | 96 | delay(1000); 97 | 98 | ticktime = xTaskGetTickCount(); 99 | 100 | for (;;) { 101 | vTaskDelayUntil(&ticktime,repeat_ticks); 102 | rc = xSemaphoreGive(barrier); 103 | // assert(rc == pdPASS); 104 | } 105 | } 106 | 107 | // 108 | // Program Initialization 109 | // 110 | void setup() { 111 | int app_cpu = xPortGetCoreID(); 112 | TaskHandle_t h; 113 | BaseType_t rc; 114 | 115 | barrier = xSemaphoreCreateBinary(); 116 | assert(barrier); 117 | 118 | pinMode(GPIO_LED,OUTPUT); 119 | digitalWrite(GPIO_LED,LOW); 120 | pinMode(GPIO_TRIGGER,OUTPUT); 121 | digitalWrite(GPIO_TRIGGER,LOW); 122 | pinMode(GPIO_ECHO,INPUT_PULLUP); 123 | 124 | #if USE_SSD1306 125 | oled.init(); 126 | oled.clear(); 127 | oled.flipScreenVertically(); 128 | oled.setColor(WHITE); 129 | oled.setTextAlignment(TEXT_ALIGN_CENTER); 130 | oled.setFont(ArialMT_Plain_24); 131 | oled.drawString(64,0,"hcsr04.ino"); 132 | oled.drawHorizontalLine(0,0,128); 133 | oled.drawHorizontalLine(0,26,128); 134 | oled.display(); 135 | #endif 136 | 137 | delay(2000); // Allow USB to connect 138 | 139 | printf("\nhcsr04.ino:\n"); 140 | 141 | rc = xTaskCreatePinnedToCore( 142 | range_task, 143 | "rangetsk", 144 | 2048, // Stack size 145 | nullptr, 146 | 1, // Priority 147 | &h, // Task handle 148 | app_cpu // CPU 149 | ); 150 | assert(rc == pdPASS); 151 | assert(h); 152 | 153 | rc = xTaskCreatePinnedToCore( 154 | sync_task, 155 | "synctsk", 156 | 2048, // Stack size 157 | nullptr, 158 | 1, // Priority 159 | &h, // Task handle 160 | app_cpu // CPU 161 | ); 162 | assert(rc == pdPASS); 163 | assert(h); 164 | } 165 | 166 | // Not used: 167 | void loop() { 168 | vTaskDelete(nullptr); 169 | } 170 | -------------------------------------------------------------------------------- /i2cscan/i2cscan.ino: -------------------------------------------------------------------------------- 1 | // i2cscan.ino 2 | // MIT License (see file LICENSE) 3 | 4 | #define I2C_SDA 25 5 | #define I2C_SCL 26 6 | 7 | #include 8 | 9 | void setup() { 10 | BaseType_t rc; 11 | 12 | Wire.begin(I2C_SDA,I2C_SCL); 13 | 14 | delay(2000); // Allow USB to connect 15 | printf("\ni2cscan.ino:\n"); 16 | } 17 | 18 | void loop() { 19 | uint8_t error; 20 | int count = 0; 21 | 22 | printf("\nScanning I2C bus SDA=%u, SCL=%u...\n", 23 | I2C_SDA,I2C_SCL); 24 | 25 | for ( uint8_t addr=1; addr < 127; ++addr ) { 26 | Wire.beginTransmission(addr); 27 | error = Wire.endTransmission(); 28 | if ( !error ) { 29 | printf(" device found at 0x%02X\n",addr); 30 | ++count; 31 | } else if ( error == 4 ) { 32 | printf(" Error at 0x%02X\n",addr); 33 | } 34 | } 35 | 36 | if ( !count ) 37 | printf("No I2C devices found\n"); 38 | else 39 | printf("%d devices found.\n",count); 40 | delay(5000); 41 | } 42 | -------------------------------------------------------------------------------- /mailbox/mailbox.ino: -------------------------------------------------------------------------------- 1 | // mailbox.ino 2 | // MIT License (see file LICENSE) 3 | 4 | // Configuration 5 | #define I2C_SDA 25 6 | #define I2C_SCL 26 7 | #define DEV_Si7021 (uint8_t(0x1E)) 8 | #define DEV_HMC5883L (uint8_t(0x40)) 9 | 10 | #include 11 | #include 12 | 13 | static Adafruit_Si7021 si7021; 14 | 15 | static int app_cpu = 0; 16 | static QueueHandle_t comph = nullptr; 17 | static QueueHandle_t temph = nullptr; 18 | static SemaphoreHandle_t chsem = nullptr; 19 | static SemaphoreHandle_t i2sem = nullptr; 20 | 21 | struct s_compass { 22 | uint16_t x; 23 | uint16_t y; 24 | uint16_t z; 25 | }; 26 | 27 | struct s_temp { 28 | float temp; 29 | float humidity; 30 | }; 31 | 32 | // 33 | // Lock I2C Bus 34 | // 35 | static inline void i2c_lock() { 36 | BaseType_t rc; 37 | 38 | rc = xSemaphoreTake(i2sem,portMAX_DELAY); 39 | assert(rc == pdPASS); 40 | } 41 | 42 | // 43 | // Unlock I2C Bus 44 | // 45 | static inline void i2c_unlock() { 46 | BaseType_t rc; 47 | 48 | rc = xSemaphoreGive(i2sem); 49 | assert(rc == pdPASS); 50 | } 51 | 52 | // 53 | // Temperature and Humidity Task 54 | // 55 | static void temp_task(void *argp) { 56 | s_temp reading; 57 | uint8_t er; 58 | BaseType_t rc; 59 | 60 | i2c_lock(); 61 | 62 | if ( !si7021.begin() ) { 63 | i2c_unlock(); 64 | vTaskDelete(nullptr); 65 | } 66 | 67 | si7021.reset(); 68 | i2c_unlock(); 69 | 70 | reading.temp = 0.0; 71 | reading.humidity = 0.0; 72 | 73 | for (;;) { 74 | i2c_lock(); 75 | reading.temp = si7021.readTemperature(); 76 | reading.humidity = si7021.readHumidity(); 77 | i2c_unlock(); 78 | 79 | rc = xQueueOverwrite(temph,&reading); 80 | assert(rc == pdPASS); 81 | 82 | // Notify disp_task: 83 | xSemaphoreGive(chsem); 84 | 85 | delay(500); 86 | } 87 | } 88 | 89 | // 90 | // Read MSB + LSB for compass reading 91 | // 92 | static inline int16_t read_i16() { 93 | uint8_t ub1, ub2; 94 | 95 | ub1 = Wire.read(); 96 | ub2 = Wire.read(); 97 | return int16_t((ub1 << 8)|ub2); 98 | } 99 | 100 | // 101 | // Compass reading task: 102 | // 103 | static void comp_task(void *argp) { 104 | s_compass reading; 105 | BaseType_t rc; 106 | int16_t i16; 107 | uint8_t s; 108 | bool status; 109 | 110 | for (;;) { 111 | status = false; 112 | 113 | i2c_lock(); 114 | Wire.beginTransmission(DEV_HMC5883L); 115 | Wire.write(9); // Status register 116 | rc = Wire.requestFrom(DEV_HMC5883L,uint8_t(1)); 117 | if ( rc != 1 ) { 118 | i2c_unlock(); 119 | printf("I2C Fail for HMC5883L\n"); 120 | sleep(1000); 121 | continue; 122 | } 123 | 124 | s = Wire.read(); // Status 125 | i2c_unlock(); 126 | 127 | if ( !(s & 0x01) ) 128 | continue; 129 | 130 | // Device is ready for reading 131 | i2c_lock(); 132 | Wire.beginTransmission(DEV_HMC5883L); 133 | Wire.write(3); 134 | rc = Wire.requestFrom(DEV_HMC5883L,uint8_t(6)); 135 | if ( rc == 6 ) { 136 | reading.x = read_i16(); 137 | reading.z = read_i16(); 138 | reading.y = read_i16(); 139 | status = true; 140 | } 141 | i2c_unlock(); 142 | 143 | if ( status ) { 144 | rc = xQueueOverwrite(comph,&reading); 145 | assert(rc == pdPASS); 146 | 147 | // Notify disp_task: 148 | xSemaphoreGive(chsem); 149 | } 150 | delay(500); 151 | } 152 | } 153 | 154 | // 155 | // Display task (Serial Monitor) 156 | // 157 | static void disp_task(void *argp) { 158 | s_temp temp_reading; 159 | s_compass comp_reading; 160 | BaseType_t rc; 161 | 162 | for (;;) { 163 | // Wait for change notification: 164 | rc = xSemaphoreTake(chsem,portMAX_DELAY); 165 | assert(rc == pdPASS); 166 | 167 | // Grab temperature, if any: 168 | rc = xQueuePeek(temph,&temp_reading,0); 169 | if ( rc == pdPASS ) { 170 | printf("Temperature: %.2fC, RH %.2f %%\n", 171 | temp_reading.temp, 172 | temp_reading.humidity); 173 | } else { 174 | printf("Temperature & RH not available.\n"); 175 | } 176 | 177 | // Grab compass readings, if any: 178 | rc = xQueuePeek(comph,&comp_reading,0); 179 | if ( rc == pdPASS ) { 180 | printf("Compass readings: %d, %d, %d\n", 181 | comp_reading.x, 182 | comp_reading.y, 183 | comp_reading.z); 184 | } else { 185 | printf("Compass reading not available.\n"); 186 | } 187 | } 188 | } 189 | 190 | // 191 | // Program Initialization 192 | // 193 | void setup() { 194 | BaseType_t rc; 195 | 196 | app_cpu = xPortGetCoreID(); 197 | 198 | // Change notification: 199 | chsem = xSemaphoreCreateBinary(); 200 | assert(chsem); 201 | 202 | // I2C locking semaphore: 203 | i2sem = xSemaphoreCreateBinary(); 204 | assert(i2sem); 205 | rc = xSemaphoreGive(i2sem); 206 | assert(rc == pdPASS); 207 | 208 | // Compass Mailbox: 209 | comph = xQueueCreate(1,sizeof(s_compass)); 210 | assert(comph); 211 | 212 | // Temperature and RH Mailbox: 213 | temph = xQueueCreate(1,sizeof(s_temp)); 214 | assert(temph); 215 | 216 | // Start I2C Bus Support: 217 | Wire.begin(I2C_SDA,I2C_SCL); 218 | 219 | // Allow USB to Serial to start: 220 | delay(2000); 221 | printf("\nmailbox.ino:\n"); 222 | 223 | // Temperature Reading Task: 224 | rc = xTaskCreatePinnedToCore( 225 | temp_task, 226 | "temptsk", 227 | 2400, // Stack size 228 | nullptr, 229 | 1, // Priority 230 | nullptr, // Task handle 231 | app_cpu // CPU 232 | ); 233 | assert(rc == pdPASS); 234 | 235 | // Compass Reading Task: 236 | rc = xTaskCreatePinnedToCore( 237 | comp_task, 238 | "comptsk", 239 | 2400, // Stack size 240 | nullptr, 241 | 1, // Priority 242 | nullptr, // Task handle 243 | app_cpu // CPU 244 | ); 245 | assert(rc == pdPASS); 246 | 247 | // Display task: 248 | rc = xTaskCreatePinnedToCore( 249 | disp_task, 250 | "disptsk", 251 | 4000, // Stack size 252 | nullptr, 253 | 1, // Priority 254 | nullptr, // Task handle 255 | app_cpu // CPU 256 | ); 257 | assert(rc == pdPASS); 258 | } 259 | 260 | // Not used: 261 | void loop() { 262 | vTaskDelete(nullptr); 263 | } 264 | -------------------------------------------------------------------------------- /mutex/mutex.ino: -------------------------------------------------------------------------------- 1 | // mutex.ino 2 | 3 | // GPIOs used for I2C 4 | #define I2C_SDA 25 5 | #define I2C_SCL 26 6 | 7 | // Set to 1 for PCF8574A 8 | #define PCF8574A 0 9 | 10 | #include 11 | 12 | #if PCF8574A 13 | // Newer PCF8574A addresses 14 | #define DEV0 0x38 15 | #define DEV1 0x39 16 | #else 17 | // Original PCF8574 addresses 18 | #define DEV0 0x20 19 | #define DEV1 0x21 20 | #endif 21 | 22 | static int app_cpu = 0; 23 | static SemaphoreHandle_t mutex; 24 | static int pcf8574_1 = DEV0; 25 | static int pcf8574_2 = DEV1; 26 | 27 | // 28 | // Lock I2C Bus with mutex 29 | // 30 | static void lock_i2c() { 31 | BaseType_t rc; 32 | 33 | rc = xSemaphoreTake(mutex,portMAX_DELAY); 34 | assert(rc == pdPASS); 35 | } 36 | 37 | // 38 | // Unlock I2C Bus with mutex 39 | // 40 | static void unlock_i2c() { 41 | BaseType_t rc; 42 | 43 | rc = xSemaphoreGive(mutex); 44 | assert(rc == pdPASS); 45 | } 46 | 47 | // 48 | // I2C extender blink task: 49 | // 50 | static void led_task(void *argp) { 51 | int i2c_addr = *(unsigned*)argp; 52 | bool led_status = false; 53 | int rc; 54 | 55 | lock_i2c(); 56 | printf("Testing I2C address 0x%02X\n", 57 | i2c_addr); 58 | Wire.begin(); 59 | Wire.requestFrom(i2c_addr,1); 60 | rc = Wire.available(); 61 | if ( rc > 0 ) { 62 | Wire.read(); 63 | Wire.beginTransmission(i2c_addr); 64 | Wire.write(0xFF); // All GPIOs high 65 | Wire.endTransmission(); 66 | printf("I2C address 0x%02X present.\n", 67 | i2c_addr); 68 | } else { 69 | printf("I2C address 0x%02X not responding.\n", 70 | i2c_addr); 71 | } 72 | unlock_i2c(); 73 | 74 | if ( rc <= 0 ) { 75 | // Cancel task if I2C fail 76 | vTaskDelete(nullptr); 77 | } 78 | 79 | // 80 | // Blink loop 81 | // 82 | for (;;) { 83 | lock_i2c(); 84 | led_status ^= true; 85 | printf("LED 0x%02X %s\n", 86 | i2c_addr, 87 | led_status ? "on" : "off"); 88 | Wire.beginTransmission(i2c_addr); 89 | Wire.write(led_status ? 0b11110111 : 0b11111111); 90 | Wire.endTransmission(); 91 | unlock_i2c(); 92 | 93 | // Use different delays per task 94 | delay(i2c_addr & 1 ? 500 : 600); 95 | } 96 | } 97 | 98 | // 99 | // Initialize 100 | // 101 | void setup() { 102 | BaseType_t rc; // Return code 103 | 104 | app_cpu = xPortGetCoreID(); 105 | 106 | // Create mutex 107 | mutex = xSemaphoreCreateMutex(); 108 | assert(mutex); 109 | 110 | // Start I2C Bus Support: 111 | Wire.begin(I2C_SDA,I2C_SCL); 112 | 113 | delay(2000); // Allow USB to connect 114 | printf("\nmutex.ino:\n"); 115 | 116 | rc = xTaskCreatePinnedToCore( 117 | led_task, // Function 118 | "led_task1",// Name 119 | 2000, // Stack size 120 | &pcf8574_1, // Argument 121 | 1, // Priority 122 | nullptr, // Handle ptr 123 | app_cpu // CPU 124 | ); 125 | assert(rc == pdPASS); 126 | 127 | rc = xTaskCreatePinnedToCore( 128 | led_task, // Function 129 | "led_task2",// Name 130 | 2000, // Stack size 131 | &pcf8574_2, // Argument 132 | 1, // Priority 133 | nullptr, // Handle ptr 134 | app_cpu // CPU 135 | ); 136 | assert(rc == pdPASS); 137 | } 138 | 139 | // Not used: 140 | void loop() { 141 | vTaskDelete(nullptr); 142 | } 143 | -------------------------------------------------------------------------------- /pcf8563/pcf8563.ino: -------------------------------------------------------------------------------- 1 | // pcf8563.ino 2 | 3 | // Set to zero if using PWM 4 | // instead of crystal. Wire 5 | // GPIO PWM_GPIO to CLKIN 6 | // when not using crystal. 7 | 8 | #define WITH_XTAL 1 9 | 10 | // GPIOs used for I2C 11 | 12 | #define I2C_SDA 25 13 | #define I2C_SCL 26 14 | 15 | // GPIOs used for input interrupts 16 | 17 | #define GPIO_PULSEIN 14 18 | #define GPIO_INT 12 19 | 20 | #if !WITH_XTAL 21 | // GPIO for PWM output 22 | #define PWM_GPIO 16 23 | #define PWM_CH 0 24 | #define PWM_FREQ 32768 25 | #define PWM_RES 8 26 | #endif 27 | 28 | #include 29 | #include 30 | 31 | // PCF8563 Register Definitions 32 | 33 | #define I2C_DEV 0x51 34 | #define REG_CTL1 0x00 35 | #define REG_CTL2 0x01 36 | #define REG_VLSEC 0x02 37 | #define REG_MALRM 0X09 38 | #define REG_CLKOUT 0x0D 39 | 40 | static int app_cpu = 0; 41 | 42 | static SemaphoreHandle_t hpulse; 43 | static SemaphoreHandle_t hint; 44 | static SemaphoreHandle_t hserial; 45 | static SemaphoreHandle_t hi2c; 46 | static QueueSetHandle_t hqset; 47 | 48 | //////////////////////////////////////// 49 | // Interrupt service for /INT 50 | //////////////////////////////////////// 51 | 52 | void IRAM_ATTR isr_int() { 53 | BaseType_t woken = pdFALSE; 54 | 55 | xSemaphoreGiveFromISR(hint,&woken); 56 | // ignore return value 57 | if ( woken ) 58 | portYIELD_FROM_ISR(); 59 | } 60 | 61 | //////////////////////////////////////// 62 | // Interrupt service for CLKOUT 63 | //////////////////////////////////////// 64 | 65 | void IRAM_ATTR isr_pulse() { 66 | BaseType_t woken = pdFALSE; 67 | 68 | xSemaphoreGiveFromISR(hpulse,&woken); 69 | // ignore return value 70 | if ( woken ) 71 | portYIELD_FROM_ISR(); 72 | } 73 | 74 | //////////////////////////////////////// 75 | // Locking for I2C Bus 76 | //////////////////////////////////////// 77 | 78 | static void lock_i2c() { 79 | BaseType_t rc 80 | = xSemaphoreTakeRecursive(hi2c,portMAX_DELAY); 81 | assert(rc == pdPASS); 82 | } 83 | 84 | static void unlock_i2c() { 85 | BaseType_t rc 86 | = xSemaphoreGiveRecursive(hi2c); 87 | assert(rc == pdPASS); 88 | } 89 | 90 | //////////////////////////////////////// 91 | // Locking for Serial out 92 | //////////////////////////////////////// 93 | 94 | static void lock_serial() { 95 | BaseType_t rc 96 | = xSemaphoreTakeRecursive(hserial,portMAX_DELAY); 97 | assert(rc == pdPASS); 98 | } 99 | 100 | static void unlock_serial() { 101 | BaseType_t rc 102 | = xSemaphoreGiveRecursive(hserial); 103 | assert(rc == pdPASS); 104 | } 105 | 106 | //////////////////////////////////////// 107 | // Self-locking printf() 108 | //////////////////////////////////////// 109 | 110 | void logf(const char *format,...) { 111 | va_list ap; 112 | 113 | lock_serial(); 114 | va_start(ap,format); 115 | vprintf(format,ap); 116 | va_end(ap); 117 | unlock_serial(); 118 | } 119 | 120 | //////////////////////////////////////// 121 | // Read 1 byte from I2C Register 122 | //////////////////////////////////////// 123 | 124 | int read_i2c(uint8_t reg) { 125 | int rc, v; 126 | 127 | lock_i2c(); 128 | Wire.beginTransmission(I2C_DEV); 129 | Wire.write(reg); 130 | Wire.endTransmission(false); 131 | rc = Wire.requestFrom(uint16_t(I2C_DEV),1u,true); 132 | if ( rc == 1 ) 133 | v = Wire.read(); 134 | else 135 | v = -1; 136 | Wire.endTransmission(); 137 | unlock_i2c(); 138 | return v; 139 | } 140 | 141 | //////////////////////////////////////// 142 | // Read n bytes starting with I2C register 143 | //////////////////////////////////////// 144 | 145 | int read_i2c(uint8_t reg,uint8_t *buf,unsigned n) { 146 | int rc; 147 | 148 | for ( int x=0; x= 0 ) { 58 | // Button press 59 | state |= 1 << event; 60 | } else { 61 | // Button release 62 | state &= ~(1 << -event); 63 | } 64 | 65 | if ( state == enable ) { 66 | // Activate press when both 67 | // Left and Right buttons are 68 | // pressed. 69 | digitalWrite(GPIO_LED,HIGH); 70 | } else { 71 | // Deactivate press 72 | digitalWrite(GPIO_LED,LOW); 73 | } 74 | } 75 | } 76 | 77 | // 78 | // Initialization: 79 | // 80 | void setup() { 81 | int app_cpu = xPortGetCoreID(); 82 | static int left = GPIO_BUTTONL; 83 | static int right = GPIO_BUTTONR; 84 | TaskHandle_t h; 85 | BaseType_t rc; 86 | 87 | delay(2000); // Allow USB to connect 88 | queue = xQueueCreate(40,sizeof(int)); 89 | assert(queue); 90 | 91 | pinMode(GPIO_LED,OUTPUT); 92 | pinMode(GPIO_BUTTONL,INPUT_PULLUP); 93 | pinMode(GPIO_BUTTONR,INPUT_PULLUP); 94 | 95 | rc = xTaskCreatePinnedToCore( 96 | debounce_task, 97 | "debounceL", 98 | 2048, // Stack size 99 | &left, // Left button gpio 100 | 1, // Priority 101 | &h, // Task handle 102 | app_cpu // CPU 103 | ); 104 | assert(rc == pdPASS); 105 | assert(h); 106 | 107 | rc = xTaskCreatePinnedToCore( 108 | debounce_task, 109 | "debounceR", 110 | 2048, // Stack size 111 | &right, // Right button gpio 112 | 1, // Priority 113 | &h, // Task handle 114 | app_cpu // CPU 115 | ); 116 | assert(rc == pdPASS); 117 | assert(h); 118 | 119 | rc = xTaskCreatePinnedToCore( 120 | press_task, 121 | "led", 122 | 2048, // Stack size 123 | nullptr, // Not used 124 | 1, // Priority 125 | &h, // Task handle 126 | app_cpu // CPU 127 | ); 128 | assert(rc == pdPASS); 129 | assert(h); 130 | } 131 | 132 | // Not used: 133 | void loop() { 134 | vTaskDelete(nullptr); 135 | } 136 | -------------------------------------------------------------------------------- /press2/press2.ino: -------------------------------------------------------------------------------- 1 | // press2.ino 2 | // MIT License (see file LICENSE) 3 | 4 | // LED is active high 5 | #define GPIO_LED 12 6 | #define GPIO_BUTTONL 25 7 | #define GPIO_BUTTONR 26 8 | 9 | static QueueHandle_t queue; 10 | static const int reset_press = -998; 11 | 12 | // 13 | // Button Debouncing task: 14 | // 15 | static void debounce_task(void *argp) { 16 | unsigned button_gpio = *(unsigned*)argp; 17 | uint32_t level, state = 0; 18 | uint32_t mask = 0x7FFFFFFF; 19 | int event, last = -999; 20 | 21 | for (;;) { 22 | level = !digitalRead(button_gpio); 23 | state = (state << 1) | level; 24 | if ( (state & mask) == mask ) 25 | event = button_gpio; // Press 26 | else 27 | event = -button_gpio; // Release 28 | 29 | if ( event != last ) { 30 | if ( xQueueSendToBack(queue,&event,0) == pdPASS ) { 31 | last = event; 32 | } else if ( event < 0 ) { 33 | // Queue full, and we need to send a 34 | // button release event. Send a reset_press 35 | // event. 36 | do { 37 | xQueueReset(queue); // Empty queue 38 | } while ( xQueueSendToBack(queue,&reset_press,0) != pdPASS ); 39 | last = event; 40 | } 41 | } 42 | taskYIELD(); 43 | } 44 | } 45 | 46 | // 47 | // Hydraulic Press Task (LED) 48 | // 49 | static void press_task(void *argp) { 50 | static const uint32_t enable = (1 << GPIO_BUTTONL) 51 | | (1 << GPIO_BUTTONR); 52 | BaseType_t s; 53 | int event; 54 | uint32_t state = 0; 55 | 56 | // Make sure press is OFF 57 | digitalWrite(GPIO_LED,LOW); 58 | 59 | for (;;) { 60 | s = xQueueReceive( 61 | queue, 62 | &event, 63 | portMAX_DELAY 64 | ); 65 | assert(s == pdPASS); 66 | 67 | if ( event == reset_press ) { 68 | digitalWrite(GPIO_LED,LOW); 69 | state = 0; printf("RESET!!\n"); 70 | continue; 71 | } 72 | 73 | if ( event >= 0 ) { 74 | // Button press 75 | state |= 1 << event; 76 | } else { 77 | // Button release 78 | state &= ~(1 << -event); 79 | } 80 | 81 | if ( state == enable ) { 82 | // Activate press when both 83 | // Left and Right buttons are 84 | // pressed. 85 | digitalWrite(GPIO_LED,HIGH); 86 | } else { 87 | // Deactivate press 88 | digitalWrite(GPIO_LED,LOW); 89 | } 90 | } 91 | } 92 | 93 | // 94 | // Initialization: 95 | // 96 | void setup() { 97 | int app_cpu = xPortGetCoreID(); 98 | static int left = GPIO_BUTTONL; 99 | static int right = GPIO_BUTTONR; 100 | TaskHandle_t h; 101 | BaseType_t rc; 102 | 103 | delay(2000); // Allow USB to connect 104 | queue = xQueueCreate(2,sizeof(int)); 105 | assert(queue); 106 | 107 | pinMode(GPIO_LED,OUTPUT); 108 | pinMode(GPIO_BUTTONL,INPUT_PULLUP); 109 | pinMode(GPIO_BUTTONR,INPUT_PULLUP); 110 | 111 | rc = xTaskCreatePinnedToCore( 112 | debounce_task, 113 | "debounceL", 114 | 2048, // Stack size 115 | &left, // Left button gpio 116 | 1, // Priority 117 | &h, // Task handle 118 | app_cpu // CPU 119 | ); 120 | assert(rc == pdPASS); 121 | assert(h); 122 | 123 | rc = xTaskCreatePinnedToCore( 124 | debounce_task, 125 | "debounceR", 126 | 2048, // Stack size 127 | &right, // Right button gpio 128 | 1, // Priority 129 | &h, // Task handle 130 | app_cpu // CPU 131 | ); 132 | assert(rc == pdPASS); 133 | assert(h); 134 | 135 | rc = xTaskCreatePinnedToCore( 136 | press_task, 137 | "led", 138 | 2048, // Stack size 139 | nullptr, // Not used 140 | 1, // Priority 141 | &h, // Task handle 142 | app_cpu // CPU 143 | ); 144 | assert(rc == pdPASS); 145 | assert(h); 146 | } 147 | 148 | // Not used: 149 | void loop() { 150 | vTaskDelete(nullptr); 151 | } 152 | -------------------------------------------------------------------------------- /qset/qset.ino: -------------------------------------------------------------------------------- 1 | // qset.ino - Demonstrate Queue Set 2 | 3 | // LED GPIOs: 4 | #define GPIO_LED1 18 5 | #define GPIO_LED2 19 6 | #define GPIO_LED3 21 7 | 8 | // Button GPIOs: 9 | #define GPIO_BUT1 27 10 | #define GPIO_BUT2 26 11 | #define GPIO_BUT3 25 12 | 13 | #define N_BUTTONS 3 14 | #define Q_DEPTH 8 15 | 16 | // ISR Routines, forward decls: 17 | static void IRAM_ATTR isr_gpio1(); 18 | static void IRAM_ATTR isr_gpio2(); 19 | static void IRAM_ATTR isr_gpio3(); 20 | 21 | typedef void (*isr_t)(); // ISR routine type 22 | 23 | static struct s_button { 24 | int butn_gpio; // Button 25 | int led_gpio; // LED 26 | QueueHandle_t qh; // Queue 27 | isr_t isr; // ISR routine 28 | } buttons[N_BUTTONS] = { 29 | { GPIO_BUT1, GPIO_LED1, nullptr, isr_gpio1 }, 30 | { GPIO_BUT2, GPIO_LED2, nullptr, isr_gpio2 }, 31 | { GPIO_BUT3, GPIO_LED3, nullptr, isr_gpio3 } 32 | }; 33 | 34 | // Event Task: 35 | 36 | static void evtask(void *arg) { 37 | QueueSetHandle_t hqset = (QueueSetHandle_t*)arg; 38 | QueueSetMemberHandle_t mh; 39 | bool bstate; 40 | BaseType_t rc; 41 | 42 | for (;;) { 43 | // Wait for an event from our 3 queues: 44 | mh = xQueueSelectFromSet(hqset,portMAX_DELAY); 45 | for ( unsigned ux=0; uxgpio,ledp->state ^= 1); 32 | temp = uxTaskGetStackHighWaterMark(nullptr); 33 | if ( !stack_hwm || temp < stack_hwm ) { 34 | stack_hwm = temp; 35 | printf("Task for gpio %d has stack hwm %u, heap %u bytes\n", 36 | ledp->gpio,stack_hwm, 37 | unsigned(xPortGetFreeHeapSize())); 38 | } 39 | delay(ledp->napms); 40 | } 41 | } 42 | 43 | void setup() { 44 | int app_cpu = 0; // CPU number 45 | 46 | delay(500); // Pause for serial setup 47 | 48 | app_cpu = xPortGetCoreID(); 49 | printf("app_cpu is %d (%s core)\n", 50 | app_cpu, 51 | app_cpu > 0 ? "Dual" : "Single"); 52 | 53 | printf("LEDs on gpios: "); 54 | for ( auto& led : leds ) { 55 | pinMode(led.gpio,OUTPUT); 56 | digitalWrite(led.gpio,LOW); 57 | xTaskCreatePinnedToCore( 58 | led_task_func, 59 | "led_task", 60 | 2048, 61 | &led, 62 | 1, 63 | &led.taskh, 64 | app_cpu 65 | ); 66 | printf("%d ",led.gpio); 67 | } 68 | putchar('\n'); 69 | printf("There are %u heap bytes available.\n", 70 | unsigned(xPortGetFreeHeapSize())); 71 | } 72 | 73 | void loop() { 74 | // Delete self (main task) 75 | vTaskDelete(nullptr); 76 | } 77 | -------------------------------------------------------------------------------- /task_suspend/task_suspend.ino: -------------------------------------------------------------------------------- 1 | // task_suspend.ino 2 | // MIT License (see file LICENSE) 3 | 4 | // Change the following if you want to use 5 | // different GPIO pins for the three LEDs 6 | 7 | #define LED1 12 // GPIO 12 8 | #define LED2 13 // etc. 9 | #define LED3 15 10 | 11 | struct s_led { 12 | byte gpio; // LED GPIO number 13 | byte state; // LED state 14 | unsigned napms; // Delay to use (ms) 15 | TaskHandle_t taskh; // Task handle 16 | }; 17 | 18 | static s_led leds[3] = { 19 | { LED1, 0, 500, 0 }, 20 | { LED2, 0, 200, 0 }, 21 | { LED3, 0, 750, 0 } 22 | }; 23 | 24 | static void led_task_func(void *argp) { 25 | s_led *ledp = (s_led*)argp; 26 | unsigned stack_hwm = 0, temp; 27 | 28 | delay(1000); 29 | 30 | for (;;) { 31 | digitalWrite(ledp->gpio,ledp->state ^= 1); 32 | temp = uxTaskGetStackHighWaterMark(nullptr); 33 | if ( !stack_hwm || temp < stack_hwm ) { 34 | stack_hwm = temp; 35 | printf("Task for gpio %d has stack hwm %u, heap %u bytes\n", 36 | ledp->gpio,stack_hwm, 37 | unsigned(xPortGetFreeHeapSize())); 38 | } 39 | delay(ledp->napms); 40 | } 41 | } 42 | 43 | void setup() { 44 | int app_cpu = 0; // CPU number 45 | 46 | delay(500); // Pause for serial setup 47 | 48 | app_cpu = xPortGetCoreID(); 49 | printf("app_cpu is %d (%s core)\n", 50 | app_cpu, 51 | app_cpu > 0 ? "Dual" : "Single"); 52 | 53 | printf("LEDs on gpios: "); 54 | for ( auto& led : leds ) { 55 | pinMode(led.gpio,OUTPUT); 56 | digitalWrite(led.gpio,LOW); 57 | xTaskCreatePinnedToCore( 58 | led_task_func, 59 | "led_task", 60 | 2048, 61 | &led, 62 | 1, 63 | &led.taskh, 64 | app_cpu 65 | ); 66 | printf("%d ",led.gpio); 67 | } 68 | putchar('\n'); 69 | printf("There are %u heap bytes available.\n", 70 | unsigned(xPortGetFreeHeapSize())); 71 | } 72 | 73 | void loop() { 74 | delay(5000); 75 | 76 | printf("Suspending middle LED task.\n"); 77 | vTaskSuspend(leds[1].taskh); 78 | delay(5000); 79 | printf("Resuming middle LED task.\n"); 80 | vTaskResume(leds[1].taskh); 81 | } 82 | -------------------------------------------------------------------------------- /task_yield/task_yield.ino: -------------------------------------------------------------------------------- 1 | // task_yield.ino 2 | // MIT License (see file LICENSE) 3 | 4 | #define GPIO 12 5 | 6 | static void gpio_on(void *argp) { 7 | for (;;) { 8 | for ( short x=0; x<1000; ++x ) 9 | digitalWrite(GPIO,HIGH); 10 | taskYIELD(); 11 | } 12 | } 13 | 14 | static void gpio_off(void *argp) { 15 | for (;;) { 16 | digitalWrite(GPIO,LOW); 17 | } 18 | } 19 | 20 | void setup() { 21 | int app_cpu = xPortGetCoreID(); 22 | 23 | pinMode(GPIO,OUTPUT); 24 | delay(1000); 25 | printf("Setup started..\n"); 26 | 27 | xTaskCreatePinnedToCore( 28 | gpio_on, 29 | "gpio_on", 30 | 2048, 31 | nullptr, 32 | 1, 33 | nullptr, 34 | app_cpu 35 | ); 36 | xTaskCreatePinnedToCore( 37 | gpio_off, 38 | "gpio_off", 39 | 2048, 40 | nullptr, 41 | 1, 42 | nullptr, 43 | app_cpu 44 | ); 45 | } 46 | 47 | void loop() { 48 | vTaskDelete(xTaskGetCurrentTaskHandle()); 49 | } 50 | -------------------------------------------------------------------------------- /taskcreate/taskcreate.ino: -------------------------------------------------------------------------------- 1 | // taskcreate.ino 2 | // MIT License (see file LICENSE) 3 | 4 | static int app_cpu = 0; 5 | 6 | void task2(void *argp) { 7 | 8 | printf("Task2 executing, priority %u.\n", 9 | (unsigned)uxTaskPriorityGet(nullptr)); 10 | vTaskDelete(nullptr); 11 | } 12 | 13 | void task1(void *argp) { 14 | BaseType_t rc; 15 | TaskHandle_t h2; 16 | 17 | printf("Task1 executing, priority %u.\n", 18 | (unsigned)uxTaskPriorityGet(nullptr)); 19 | 20 | rc = xTaskCreatePinnedToCore( 21 | task2, 22 | "task2", 23 | 2000, // Stack size 24 | nullptr, 25 | 4, // Priority 26 | nullptr, // Task handle 27 | app_cpu // CPU 28 | ); 29 | assert(rc == pdPASS); 30 | printf("Task2 created.\n"); 31 | vTaskDelete(nullptr); 32 | } 33 | 34 | void setup() { 35 | BaseType_t rc; 36 | unsigned priority = 0; 37 | TaskHandle_t h1; 38 | 39 | app_cpu = xPortGetCoreID(); 40 | 41 | delay(2000); // Allow USB init time 42 | 43 | vTaskPrioritySet(nullptr,3); 44 | priority = uxTaskPriorityGet(nullptr); 45 | assert(priority == 3); 46 | 47 | printf("\ntaskcreate.ino:\n"); 48 | printf("loopTask priority is %u.\n", 49 | priority); 50 | 51 | rc = xTaskCreatePinnedToCore( 52 | task1, 53 | "task1", 54 | 2000, // Stack size 55 | nullptr, 56 | 2, // Priority 57 | &h1, // Task handle 58 | app_cpu // CPU 59 | ); 60 | assert(rc == pdPASS); 61 | // delay(1); 62 | printf("Task1 created.\n"); 63 | 64 | vTaskPrioritySet(h1,3); 65 | } 66 | 67 | // Not used: 68 | void loop() { 69 | vTaskDelete(nullptr); 70 | } 71 | -------------------------------------------------------------------------------- /taskcreate2/taskcreate2.ino: -------------------------------------------------------------------------------- 1 | // taskcreate2.ino 2 | // MIT License (see file LICENSE) 3 | 4 | static int app_cpu = 0; 5 | 6 | void task1(void *argp) { 7 | 8 | printf("Task1 executing, priority %u.\n", 9 | (unsigned)uxTaskPriorityGet(nullptr)); 10 | vTaskDelete(nullptr); 11 | } 12 | 13 | void setup() { 14 | BaseType_t rc; 15 | TaskHandle_t h1; 16 | 17 | app_cpu = xPortGetCoreID(); 18 | 19 | delay(2000); // Allow USB init time 20 | 21 | printf("\ntaskcreate2.ino:\n"); 22 | printf("loopTask priority is %u.\n", 23 | (unsigned)uxTaskPriorityGet(nullptr)); 24 | 25 | rc = xTaskCreatePinnedToCore( 26 | task1, 27 | "task1", 28 | 2000, // Stack size 29 | nullptr, 30 | 0, // Priority 31 | &h1, // Task handle 32 | app_cpu // CPU 33 | ); 34 | assert(rc == pdPASS); 35 | 36 | printf("Task1 created.\n"); 37 | 38 | vTaskSuspend(h1); 39 | vTaskPrioritySet(h1,3); 40 | 41 | printf("Zzzz... 3 secs\n"); 42 | delay(3000); 43 | 44 | vTaskResume(h1); 45 | } 46 | 47 | // Not used: 48 | void loop() { 49 | vTaskDelete(nullptr); 50 | } 51 | -------------------------------------------------------------------------------- /tasklocal/tasklocal.ino: -------------------------------------------------------------------------------- 1 | // tasklocal.ino 2 | 3 | // LED GPIOs: 4 | #define GPIO_LED1 18 5 | #define GPIO_LED2 19 6 | #define GPIO_LED3 21 7 | 8 | #define N_LED 3 9 | 10 | static int leds[N_LED] = 11 | { GPIO_LED1, GPIO_LED2, GPIO_LED3 }; 12 | 13 | struct s_task_local { 14 | int index; 15 | int led_gpio; 16 | bool state; 17 | }; 18 | 19 | static void blink_led() { 20 | s_task_local *plocal = (s_task_local*) 21 | pvTaskGetThreadLocalStoragePointer(nullptr,0); 22 | 23 | delay(plocal->index*250+250); 24 | plocal->state ^= true; 25 | digitalWrite(plocal->led_gpio,plocal->state); 26 | } 27 | 28 | static void led_task(void *arg) { 29 | int x = (int)arg; 30 | s_task_local *plocal = new s_task_local; 31 | 32 | plocal->index = x; 33 | plocal->led_gpio = leds[x]; 34 | plocal->state = false; 35 | 36 | pinMode(plocal->led_gpio,OUTPUT); 37 | digitalWrite(plocal->led_gpio,LOW); 38 | 39 | vTaskSetThreadLocalStoragePointer( 40 | nullptr, 41 | 0, 42 | plocal); 43 | 44 | for (;;) { 45 | blink_led(); 46 | } 47 | } 48 | 49 | void setup() { 50 | int app_cpu = xPortGetCoreID(); 51 | BaseType_t rc; 52 | 53 | for ( int x=0; x 4 | 5 | extern bool loopTaskWDTEnabled; 6 | static TaskHandle_t htask; 7 | 8 | void setup() { 9 | esp_err_t er; 10 | 11 | htask = xTaskGetCurrentTaskHandle(); 12 | loopTaskWDTEnabled = true; 13 | delay(2000); 14 | 15 | er = esp_task_wdt_status(htask); 16 | assert(er == ESP_ERR_NOT_FOUND); 17 | 18 | if ( er == ESP_ERR_NOT_FOUND ) { 19 | er = esp_task_wdt_init(5,true); 20 | assert(er == ESP_OK); 21 | er = esp_task_wdt_add(htask); 22 | assert(er == ESP_OK); 23 | printf("Task is subscribed to TWDT.\n"); 24 | } 25 | } 26 | 27 | static int dly = 1000; 28 | 29 | void loop() { 30 | esp_err_t er; 31 | 32 | printf("loop(dly=%d)..\n",dly); 33 | er = esp_task_wdt_status(htask); 34 | assert(er == ESP_OK); 35 | delay(dly); 36 | dly += 1000; 37 | } 38 | -------------------------------------------------------------------------------- /watchdog2/watchdog2.ino: -------------------------------------------------------------------------------- 1 | // watchdog2.ino 2 | 3 | #include 4 | 5 | #define GPIO_LED 19 6 | 7 | extern bool loopTaskWDTEnabled; 8 | static TaskHandle_t htask; 9 | 10 | static void task2(void *arg) { 11 | esp_err_t er; 12 | unsigned seed = hallRead(); 13 | 14 | er = esp_task_wdt_add(nullptr); 15 | assert(er == ESP_OK); 16 | 17 | for (;;) { 18 | digitalWrite(GPIO_LED, 19 | 1 ^ !!digitalRead(GPIO_LED)); 20 | esp_task_wdt_reset(); 21 | delay(rand_r(&seed)%7*1000); 22 | } 23 | } 24 | 25 | void setup() { 26 | int app_cpu = xPortGetCoreID(); 27 | BaseType_t rc; 28 | esp_err_t er; 29 | 30 | pinMode(GPIO_LED,OUTPUT); 31 | digitalWrite(GPIO_LED,LOW); 32 | 33 | htask = xTaskGetCurrentTaskHandle(); 34 | loopTaskWDTEnabled = true; 35 | delay(2000); 36 | 37 | er = esp_task_wdt_status(htask); 38 | assert(er == ESP_ERR_NOT_FOUND); 39 | 40 | if ( er == ESP_ERR_NOT_FOUND ) { 41 | er = esp_task_wdt_init(5,true); 42 | assert(er == ESP_OK); 43 | er = esp_task_wdt_add(htask); 44 | assert(er == ESP_OK); 45 | } 46 | 47 | rc = xTaskCreatePinnedToCore( 48 | task2, // function 49 | "task2", // Name 50 | 2000, // Stack size 51 | nullptr, // Parameters 52 | 1, // Priority 53 | nullptr, // handle 54 | app_cpu // CPU 55 | ); 56 | assert(rc == pdPASS); 57 | } 58 | 59 | static int dly = 1000; 60 | 61 | void loop() { 62 | esp_err_t er; 63 | 64 | printf("loop(dly=%d)..\n",dly); 65 | er = esp_task_wdt_status(htask); 66 | assert(er == ESP_OK); 67 | delay(dly); 68 | dly += 1000; 69 | } 70 | -------------------------------------------------------------------------------- /worms1/worms1.ino: -------------------------------------------------------------------------------- 1 | // worms1.ino 2 | // MIT License (see file LICENSE) 3 | 4 | // Worm task priorities 5 | #define WORM1_TASK_PRIORITY 9 6 | #define WORM2_TASK_PRIORITY 8 7 | #define WORM3_TASK_PRIORITY 7 8 | 9 | // loop() must have highest priority 10 | #define MAIN_TASK_PRIORITY 10 11 | 12 | #include "SSD1306.h" 13 | 14 | class Display : public SSD1306 { 15 | int w, h; // Width, height 16 | public: 17 | Display( 18 | int width=128, 19 | int height=64, 20 | int addr=0x3C, 21 | int sda=5, 22 | int scl=4); 23 | int width() { return w; } 24 | int height() { return h; } 25 | void lock(); 26 | void unlock(); 27 | void clear(); 28 | void init(); 29 | }; 30 | 31 | class InchWorm { 32 | static const int segw = 9, segsw = 4, segh = 3; 33 | Display& disp; 34 | int worm; 35 | int x, y; // Coordinates of worm (left) 36 | int wormw=30; 37 | int wormh=10; 38 | int dir=1; // Direction 39 | int state=0; 40 | public: 41 | InchWorm(Display& disp,int worm); 42 | void draw(); 43 | }; 44 | 45 | InchWorm::InchWorm(Display& disp,int worm) 46 | : disp(disp), worm(worm) { 47 | } 48 | 49 | void 50 | InchWorm::draw() { 51 | int py = 7 + (worm-1) * 20; 52 | int px = 2 + x; 53 | 54 | py += wormh - 3; 55 | 56 | disp.setColor(WHITE); 57 | disp.fillRect(px,py-2*segh,3*segw,3*segh); 58 | disp.setColor(BLACK); 59 | 60 | switch ( state ) { 61 | case 0: // _-_ 62 | disp.fillRect(px,py,segw,segh); 63 | disp.fillRect(px+segw,py-segh,segsw,segh); 64 | disp.fillRect(px+segw+segsw,py,segw,segh); 65 | break; 66 | case 1: // _^_ (high hump) 67 | disp.fillRect(px,py,segw,segh); 68 | disp.fillRect(px+segw,py-2*segh,segsw,segh); 69 | disp.fillRect(px+segw+segsw,py,segw,segh); 70 | disp.drawLine(px+segw,py,px+segw,py-2*segh); 71 | disp.drawLine(px+segw+segsw,py,px+segw+segsw,py-2*segh); 72 | break; 73 | case 2: // _^^_ (high hump, stretched) 74 | if ( dir < 0 ) 75 | px -= segsw; 76 | disp.fillRect(px,py,segw,segh); 77 | disp.fillRect(px+segw,py-2*segh,segw,segh); 78 | disp.fillRect(px+2*segw,py,segw,segh); 79 | disp.drawLine(px+segw,py,px+segw,py-2*segh); 80 | disp.drawLine(px+2*segw,py,px+2*segw,py-2*segh); 81 | break; 82 | case 3: // _-_ (moved) 83 | if ( dir < 0 ) 84 | px -= segsw; 85 | else 86 | px += segsw; 87 | disp.fillRect(px,py,segw,segh); 88 | disp.fillRect(px+segw,py-segh,segsw,segh); 89 | disp.fillRect(px+segw+segsw,py,segw,segh); 90 | break; 91 | default: 92 | ; 93 | } 94 | state = (state+1) % 4; 95 | if ( !state ) { 96 | x += dir*segsw; 97 | if ( dir > 0 ) { 98 | if ( x + 3*segw+segsw >= disp.width() ) 99 | dir = -1; 100 | } else if ( x <= 2 ) 101 | dir = +1; 102 | } 103 | disp.display(); 104 | } 105 | 106 | Display::Display( 107 | int width, 108 | int height, 109 | int addr, 110 | int sda, 111 | int scl) 112 | : w(width), h(height), 113 | SSD1306(addr,sda,scl) { 114 | } 115 | 116 | void 117 | Display::init() { 118 | SSD1306::init(); 119 | clear(); 120 | flipScreenVertically(); 121 | display(); 122 | } 123 | 124 | void 125 | Display::clear() { 126 | SSD1306::clear(); 127 | setColor(WHITE); 128 | fillRect(0,0,w,h); 129 | setColor(BLACK); 130 | } 131 | 132 | static Display oled; 133 | static InchWorm worm1(oled,1); 134 | static InchWorm worm2(oled,2); 135 | static InchWorm worm3(oled,3); 136 | static QueueHandle_t qh = 0; 137 | static int app_cpu = 0; // Updated by setup() 138 | 139 | void worm_task(void *arg) { 140 | InchWorm *worm = (InchWorm*)arg; 141 | 142 | for (;;) { 143 | for ( int x=0; x<800000; ++x ) 144 | __asm__ __volatile__("nop"); 145 | xQueueSendToBack(qh,&worm,0); 146 | // vTaskDelay(10); 147 | } 148 | } 149 | 150 | void setup() { 151 | TaskHandle_t h = xTaskGetCurrentTaskHandle(); 152 | 153 | app_cpu = xPortGetCoreID(); // Which CPU? 154 | oled.init(); 155 | vTaskPrioritySet(h,MAIN_TASK_PRIORITY); 156 | qh = xQueueCreate(4,sizeof(InchWorm*)); 157 | 158 | // Draw at least one worm each: 159 | worm1.draw(); 160 | worm2.draw(); 161 | worm3.draw(); 162 | 163 | xTaskCreatePinnedToCore( 164 | worm_task, // Function 165 | "worm1", // Task name 166 | 3000, // Stack size 167 | &worm1, // Argument 168 | WORM1_TASK_PRIORITY, 169 | nullptr, // No handle returned 170 | app_cpu); 171 | 172 | xTaskCreatePinnedToCore( 173 | worm_task, // Function 174 | "worm2", // Task name 175 | 3000, // Stack size 176 | &worm2, // Argument 177 | WORM2_TASK_PRIORITY, 178 | nullptr, // No handle returned 179 | app_cpu); 180 | 181 | xTaskCreatePinnedToCore( 182 | worm_task, // Function 183 | "worm3", // Task name 184 | 3000, // Stack size 185 | &worm3, // Argument 186 | WORM3_TASK_PRIORITY, 187 | nullptr, // No handle returned 188 | app_cpu); 189 | 190 | delay(1000); // Allow USB to connect 191 | printf("worms1.ino: CPU %d\n",app_cpu); 192 | } 193 | 194 | void loop() { 195 | InchWorm *worm = nullptr; 196 | 197 | if ( xQueueReceive(qh,&worm,1) == pdPASS ) 198 | worm->draw(); 199 | else 200 | delay(1); 201 | } 202 | --------------------------------------------------------------------------------