-
-
Save Jamesits/92394675c0fe786467b26f90e95d3904 to your computer and use it in GitHub Desktop.
// High-accuracy square wave generator | |
// based on ESP8266 | |
// with runtime adjustable frequency, PWM width and offset | |
// Output wave at pin 5 (configurable from 0 to 15, but not 16) | |
// by James Swineson <github@public.swineson.me>, 2017-10 | |
// https://gist.github.com/Jamesits/92394675c0fe786467b26f90e95d3904 | |
// See https://blog.swineson.me/implementation-of-6mbps-high-speed-io-on-esp8266-arduino-ide/ | |
// for more information (article in Chinese) | |
// Arduino UNO version: https://gist.github.com/Jamesits/8d164818946a65d0cafcd6203e3e5049 | |
#include <Esp.h> | |
#include <esp8266_peri.h> | |
#include <eagle_soc.h> | |
#define WDT_CTL 0x60000900 | |
#define WDT_CTL_ENABLE (BIT(0)) | |
// Configurable from 0 to 15; 16 not available | |
// Please refer to your breakout board datasheet for pin mapping | |
// WARNING: some pins are used internally for connecting the ESP to ROM chip; | |
// DO NOT USE THEM or your ESP will be bricked | |
#define PINOUT 5 | |
double freq; // Hz | |
double offset; // percent (0.0 to 1.0) | |
double width; // percent (0.0 to 1.0) | |
// unit: microsecond | |
unsigned long cycle_time; | |
unsigned long raising_edge; | |
unsigned long falling_edge; | |
unsigned long prev_micros; | |
// compare 2 unsigned value | |
// true if X > Y while for all possible (X, Y), X - Y < Z | |
#define TIME_CMP(X, Y, Z) (((X) - (Y)) < (Z)) | |
inline void setHigh() { | |
GPOS = (1 << PINOUT); | |
} | |
inline void setLow() { | |
GPOC = (1 << PINOUT); | |
} | |
void setup() { | |
// disable hardware watchdog | |
CLEAR_PERI_REG_MASK(WDT_CTL, WDT_CTL_ENABLE); | |
// disable software watchdog | |
ESP.wdtDisable(); | |
// set IO pin mode | |
pinMode(PINOUT, OUTPUT); | |
// calculate arguments | |
freq = 1; | |
width = 0.5; | |
offset = 0.0; | |
cycle_time = 1000000 / freq; | |
raising_edge = (unsigned long)(offset * cycle_time) % cycle_time; | |
falling_edge = (unsigned long)((offset + width) * cycle_time) % cycle_time; | |
prev_micros = micros(); | |
// do pinout shifting | |
while(1) { | |
if (width + offset < 1) { | |
// raising edge should appear earlier | |
while (TIME_CMP(micros(), prev_micros + raising_edge, cycle_time)); setHigh(); | |
while (TIME_CMP(micros(), prev_micros + falling_edge, cycle_time)); setLow(); | |
} else { | |
// falling edge should appear earlier | |
while (TIME_CMP(micros(), prev_micros + falling_edge, cycle_time)); setLow(); | |
while (TIME_CMP(micros(), prev_micros + raising_edge, cycle_time)); setHigh(); | |
} | |
prev_micros += cycle_time; | |
} | |
} | |
void loop() { | |
// it won't ever get there; | |
// however if this function is missing | |
// the ESP8266 Arduino refuse to compile | |
} |
@stfschaefer There are external codes around loop()
(watchdog feeding, WiFi MAC, etc. ) which will make the output waveform unstable.
Thanks.
Please check https://github.com/khoih-prog/ESP8266TimerInterrupt
Could implement this, instead of use setup(), so you can run some code between timer interrupts.
@terminet85 I know how to use an interrupt, but a timer interrupt is only suitable for low-frequency, high-accuracy and small workload. (Try for yourself a timer interrupt at 250KHz!) This program is designed to get everything out of its GPIO interface. (You can use DMA to achieve a higher switching freq on a specific pin, but it is another story.)
@Jamesits thanks for your hints.
What do you think using timer interrupts in some application as 0,01Hz-10Hz. I would like to implement an pulse energy counter, with some relays to switch on and off.
@terminet85 10Hz should be perfectly fine for timer interrupts.
@Jamesits thank you and apologize for the off-topic!
You can put the section after while(1) into the function loop() so also the line ... while(1) ... not needet!