├── .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 |
--------------------------------------------------------------------------------