如何实现 ESP8266 5 kHz PWM?
How to implement ESP8266 5 kHz PWM?
我需要实现 5 kHz +/- 5% 的 PWM 输出。 (大概是由于它馈入的滤波器电路,我无法控制。)
这可以用 ESP8266 实现吗(最好用 NodeMCU)?
我了解到ESP8266的软件PWM最大频率为1kHz,而sigma-delta可以实现固定频率约300kHz的PWM。
那么有没有可靠的方法可以达到 5 kHz?我知道有人用 I2S 外设做波形输出实验,但我不确定它是否可以用于 5kHz 输出。
有没有人研究过类似的问题?
基本代码是:
#define qap 2 // Quick As Possible ... Duty cycle only 0, 50, 100%
#define HFreq 5150
#define pPulse D2 // a NodeMCU/ESP8266 GPIO PWM pin
analogWriteRange(qap); analogWriteFreq( HFreq ); analogWrite(pPulse, 1); // start PWM
长话短说;博士
刚刚做了一些粗略的 benchamrks
// had HFreq=126400 with 75 KHz pulse at 80 MHz ESP clock, every 1 sec but proof of evidence lost
标称 80 MHz 时钟得到
// 72.24 KHz 361178 2015061929
// 72.23 KHz 361163 2415062390
// 72.23 KHz 361133 2815062824
和
// 141.52 KHz 353809 2009395076
// 141.54 KHz 353846 2409395627
// 141.52 KHz 353806 2809395946
使用 160 MHz 时钟
注意事项!!!
1.占空比范围为2!
2. 由于系统没有其他条件,因此串行 IO 等仍然存在其他时序伪影。特别是由于 WDT 和 WiFi 倒数第二出现导致的不稳定。 (无论如何,大概只有当ESP8266受到压力和胁迫时,这才是问题。)
// 141.50 KHz 353754 619466806
// 141.52 KHz 353810 1019467038
// ...
// ad infinitum cum tempore finitum, infinitus est ad nauseum?
// ...
// 141.54 KHz 353857 735996888
//
// ets Jan 8 2013,rst cause:4, boot mode:(1,7)
//
//wdt reset
当使用以下代码生成 5 KHz 方波信号时,上述注意事项不是问题,也不会发生。
// ------------ test results for 80 MHz clock --------------
//
//
// PWM pulse test
//
// F_CPU: 80000000L
// ESP8266_CLOCK: 80000000UL
// PWM "freq.": 5150
//
//
// connect D1 to D2
//
//
// raw MPU
// frequency count cycle
// 0.00 KHz 1 407976267
// 4.74 KHz 9482 567976702
// 5.00 KHz 10007 727977137
// 5.00 KHz 10006 887977572
// 5.00 KHz 10006 1047978007
// 5.00 KHz 10007 1207978442
// 5.00 KHz 10006 1367978877
// 5.00 KHz 10006 1527979312
// 5.00 KHz 10007 1687979747
// 5.00 KHz 10006 1847980182
// 5.00 KHz 10006 2007980617
// 5.00 KHz 10007 2167981052
// 5.00 KHz 10006 2327981487
// 5.00 KHz 10006 2487981922
// 5.00 KHz 10007 2647982357 ...
//
// crude testing for 5KHz signal
// extracted from:
// highest frequency / shortest period pin pulse generate / detect test
//
// uses raw ESP8266 / NodeMCU V1.0 hardware primitive interface of Arduino IDE (no included libraries)
//
// timing dependencies: WDT, WiFi, I2S, I2C, one wire, UART, SPI, ...
//
// Arduino GPIO 16 5 4 0 2 14 12 13 15 3 1 0 1 2 3 4 5 ... 12 13 14 15 16
// NodeMCU D pin 0 1 2 3 4 5 6 7 8 9 10 3 10 4 9 2 1 ... 6 7 5 8 0
// | | | | | | | | | | |
// a WAKE | | F Tx1 | | Rx2 Tx2 Rx0 Tx0
// k (NO PWM or | | L blue | | | |
// a' interrupt) | S A * H | H | H | | * led's
// s red S D S S M S H
// * C A H C I I C
// L T L S M S
// K A K O O
// └ - - - - └----UART's----┘
// └--I2C--┘ └-----SPI------┘
//
// rules of engagement are obscure and vague for effects of argument values for the paramters of these functions:
// analogWriteRange(qap); analogWriteFreq( HFreq ); analogWrite(pPulse, 1);
//
//
//
// system #defines: F_CPU ESP8266_CLOCK
#define pInt D1 // HWI pin: NOT D0 ie. GPIO16 is not hardwared interrupt or PWM pin
#define pPulse D2 // PWM pulsed frequency source ... ditto D0 (note: D4 = blue LED)
#define countFor 160000000UL
#define gmv(p) #p // get macro value
#define em(p) gmv(p) // evaluate macro
#define qap 2 // minimal number of duty cycle levels (0, 50, 100% ) Quick As Possible ...
#define HFreq 5150 //((long int) F_CPU==80000000L ? 125000 : 250000) // ... to minimize time of a cycle period
// max values ^ and ^ found empirically
// had HFreq=126400 with 75 KHz pulse at 80 MHz ESP clock, every 1 sec but proof of evidence lost
#define infoTxt (String) \
"\n\n\t PWM pulse test " \
"\n F_CPU: " em(F_CPU) \
"\n ESP8266_CLOCK: " em(ESP8266_CLOCK) \
"\n PWM \"freq.\": " + HFreq + "\n" \
"\n\n connect " em(pInt) " to " em(pPulse) "\n" \
"\n\n raw MPU " \
" \n frequency count cycle "
long int oc=1, cntr=1;
unsigned long int tc=0;
void hwISR(){ cntr++; } // can count pulses if pInt <---> to pPulse
void anISR(){ tc=ESP.getCycleCount(); timer0_write( countFor + tc ); oc=cntr; cntr=1; }
void setup() { // need to still confirm duty cycle=50% (scope it or ...)
noInterrupts();
Serial.begin(115200); Serial.println(infoTxt); delay(10); // Serial.flush(); Serial.end(); // Serial timing?
analogWriteRange(qap); analogWriteFreq( HFreq ); analogWrite(pPulse, 1); // start PWM
pinMode( pInt, INPUT ); attachInterrupt(pInt, hwISR, RISING); // count pulses
timer0_isr_init(); timer0_attachInterrupt( anISR ); anISR(); //
interrupts();
}
void loop() { delay(10); if (oc==0) return;
Serial.println((String)" "+(oc/1000.0*F_CPU/countFor)+" KHz "+oc+" "+tc); oc=0; }
//
您还可以使用时钟速度可调的硬件SPI接口。
通过写入连续数据,输出波形出现在SCLK上。
我在 ESP8266 上使用 IC 74hc595 创建骑士效果时发现了上述问题,使用 MicroPython(确实很慢,因为它是一种脚本语言)。
它对我有用高达 4MHz SPI 时钟速度。
缺点是,对于永久波形,您需要永远向MOSI写入数据(因为当SPI数据缓冲区变空时,SCLK上就没有信号了)。
我需要实现 5 kHz +/- 5% 的 PWM 输出。 (大概是由于它馈入的滤波器电路,我无法控制。)
这可以用 ESP8266 实现吗(最好用 NodeMCU)?
我了解到ESP8266的软件PWM最大频率为1kHz,而sigma-delta可以实现固定频率约300kHz的PWM。
那么有没有可靠的方法可以达到 5 kHz?我知道有人用 I2S 外设做波形输出实验,但我不确定它是否可以用于 5kHz 输出。
有没有人研究过类似的问题?
基本代码是:
#define qap 2 // Quick As Possible ... Duty cycle only 0, 50, 100%
#define HFreq 5150
#define pPulse D2 // a NodeMCU/ESP8266 GPIO PWM pin
analogWriteRange(qap); analogWriteFreq( HFreq ); analogWrite(pPulse, 1); // start PWM
长话短说;博士 刚刚做了一些粗略的 benchamrks
// had HFreq=126400 with 75 KHz pulse at 80 MHz ESP clock, every 1 sec but proof of evidence lost
标称 80 MHz 时钟得到
// 72.24 KHz 361178 2015061929
// 72.23 KHz 361163 2415062390
// 72.23 KHz 361133 2815062824
和
// 141.52 KHz 353809 2009395076
// 141.54 KHz 353846 2409395627
// 141.52 KHz 353806 2809395946
使用 160 MHz 时钟
注意事项!!!
1.占空比范围为2!
2. 由于系统没有其他条件,因此串行 IO 等仍然存在其他时序伪影。特别是由于 WDT 和 WiFi 倒数第二出现导致的不稳定。 (无论如何,大概只有当ESP8266受到压力和胁迫时,这才是问题。)
// 141.50 KHz 353754 619466806
// 141.52 KHz 353810 1019467038
// ...
// ad infinitum cum tempore finitum, infinitus est ad nauseum?
// ...
// 141.54 KHz 353857 735996888
//
// ets Jan 8 2013,rst cause:4, boot mode:(1,7)
//
//wdt reset
当使用以下代码生成 5 KHz 方波信号时,上述注意事项不是问题,也不会发生。
// ------------ test results for 80 MHz clock --------------
//
//
// PWM pulse test
//
// F_CPU: 80000000L
// ESP8266_CLOCK: 80000000UL
// PWM "freq.": 5150
//
//
// connect D1 to D2
//
//
// raw MPU
// frequency count cycle
// 0.00 KHz 1 407976267
// 4.74 KHz 9482 567976702
// 5.00 KHz 10007 727977137
// 5.00 KHz 10006 887977572
// 5.00 KHz 10006 1047978007
// 5.00 KHz 10007 1207978442
// 5.00 KHz 10006 1367978877
// 5.00 KHz 10006 1527979312
// 5.00 KHz 10007 1687979747
// 5.00 KHz 10006 1847980182
// 5.00 KHz 10006 2007980617
// 5.00 KHz 10007 2167981052
// 5.00 KHz 10006 2327981487
// 5.00 KHz 10006 2487981922
// 5.00 KHz 10007 2647982357 ...
//
// crude testing for 5KHz signal
// extracted from:
// highest frequency / shortest period pin pulse generate / detect test
//
// uses raw ESP8266 / NodeMCU V1.0 hardware primitive interface of Arduino IDE (no included libraries)
//
// timing dependencies: WDT, WiFi, I2S, I2C, one wire, UART, SPI, ...
//
// Arduino GPIO 16 5 4 0 2 14 12 13 15 3 1 0 1 2 3 4 5 ... 12 13 14 15 16
// NodeMCU D pin 0 1 2 3 4 5 6 7 8 9 10 3 10 4 9 2 1 ... 6 7 5 8 0
// | | | | | | | | | | |
// a WAKE | | F Tx1 | | Rx2 Tx2 Rx0 Tx0
// k (NO PWM or | | L blue | | | |
// a' interrupt) | S A * H | H | H | | * led's
// s red S D S S M S H
// * C A H C I I C
// L T L S M S
// K A K O O
// └ - - - - └----UART's----┘
// └--I2C--┘ └-----SPI------┘
//
// rules of engagement are obscure and vague for effects of argument values for the paramters of these functions:
// analogWriteRange(qap); analogWriteFreq( HFreq ); analogWrite(pPulse, 1);
//
//
//
// system #defines: F_CPU ESP8266_CLOCK
#define pInt D1 // HWI pin: NOT D0 ie. GPIO16 is not hardwared interrupt or PWM pin
#define pPulse D2 // PWM pulsed frequency source ... ditto D0 (note: D4 = blue LED)
#define countFor 160000000UL
#define gmv(p) #p // get macro value
#define em(p) gmv(p) // evaluate macro
#define qap 2 // minimal number of duty cycle levels (0, 50, 100% ) Quick As Possible ...
#define HFreq 5150 //((long int) F_CPU==80000000L ? 125000 : 250000) // ... to minimize time of a cycle period
// max values ^ and ^ found empirically
// had HFreq=126400 with 75 KHz pulse at 80 MHz ESP clock, every 1 sec but proof of evidence lost
#define infoTxt (String) \
"\n\n\t PWM pulse test " \
"\n F_CPU: " em(F_CPU) \
"\n ESP8266_CLOCK: " em(ESP8266_CLOCK) \
"\n PWM \"freq.\": " + HFreq + "\n" \
"\n\n connect " em(pInt) " to " em(pPulse) "\n" \
"\n\n raw MPU " \
" \n frequency count cycle "
long int oc=1, cntr=1;
unsigned long int tc=0;
void hwISR(){ cntr++; } // can count pulses if pInt <---> to pPulse
void anISR(){ tc=ESP.getCycleCount(); timer0_write( countFor + tc ); oc=cntr; cntr=1; }
void setup() { // need to still confirm duty cycle=50% (scope it or ...)
noInterrupts();
Serial.begin(115200); Serial.println(infoTxt); delay(10); // Serial.flush(); Serial.end(); // Serial timing?
analogWriteRange(qap); analogWriteFreq( HFreq ); analogWrite(pPulse, 1); // start PWM
pinMode( pInt, INPUT ); attachInterrupt(pInt, hwISR, RISING); // count pulses
timer0_isr_init(); timer0_attachInterrupt( anISR ); anISR(); //
interrupts();
}
void loop() { delay(10); if (oc==0) return;
Serial.println((String)" "+(oc/1000.0*F_CPU/countFor)+" KHz "+oc+" "+tc); oc=0; }
//
您还可以使用时钟速度可调的硬件SPI接口。 通过写入连续数据,输出波形出现在SCLK上。
我在 ESP8266 上使用 IC 74hc595 创建骑士效果时发现了上述问题,使用 MicroPython(确实很慢,因为它是一种脚本语言)。 它对我有用高达 4MHz SPI 时钟速度。
缺点是,对于永久波形,您需要永远向MOSI写入数据(因为当SPI数据缓冲区变空时,SCLK上就没有信号了)。