Skip to content

Instantly share code, notes, and snippets.

  • Star 17 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Jamesits/92394675c0fe786467b26f90e95d3904 to your computer and use it in GitHub Desktop.
High-accuracy square wave generator (up to 250KHz) based on ESP8266, with runtime adjustable frequency, PWM width and offset.
// 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
}
@Jamesits
Copy link
Author

Jamesits commented Dec 3, 2020

@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.)

@terminet85
Copy link

@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.

@Jamesits
Copy link
Author

Jamesits commented Dec 3, 2020

@terminet85 10Hz should be perfectly fine for timer interrupts.

@terminet85
Copy link

@Jamesits thank you and apologize for the off-topic!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment