-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PWM and tone() incompatibility #4349
Comments
I have the same problem too. Hardware: NodeMCU V1.0 (but I chose "Generic ESP8266 Module in Arduino IDE, v1.8.5) |
I tested with core 2.3.0 and it works! Hardware: NodeMCU V1.0 (but I chose "Generic ESP8266 Module in Arduino IDE, v1.8.5) |
A have the same problem too. After "tone" on one pin and then "analogWrite" on another pin, "tone" doesn't work anymore, when I use core version 2.4.1 for NodeMCU 1.0 (ESP-12 Module). When I use core version 2.3.0 the behavior is different, but also not usable: after "tone" on one pin "analogWrite" works correct at another pin, but after "noTone" "analogWrite" doesn't work anymore. I don't know, how to flash the "Generic ESP8266 Module" for my NodeMCU. All four flash modes caused an upload error. |
Personally in my current project I prefer the
I am on fairly recent Git core, and assuming you are using Arduino IDE like me, this is how I flash my NodeMCU V1.0. I also have a NodeMCU V3 Lolin, not sure if it is a clone or genuine. I can flash it with the above settings but it won't run (won't work). But simply change the flash mode from DIO to QIO, then it works. |
Thank you, may be I had forgotten the Reset Method: "nodemcu". But the behavior is the same as with Nodecmu v1 core version 2.3. After using tone and then analogWrite and tone once more, analogWrite doesn't work anymore. Best would be, if I would write an own tone and analogWrite function, which work both together. But if I therefore would use timer1 probably the serial connection wouldn't work anymore. |
I wrote an own tone and noTone function with names itone and noItone. itone works with frequencies beginning with 1 Hz. But with core 2.4.1 itone has the same behavior as tone after using analogWrite. analogWrite is very aggressive. If called only once timer1 can't be used nomore for other purposes. analogWrite always grabs timer1 back. This means, if I need timer1, and I need it for precise measurements, then analogWrite mustn't be used. These are my itone and noItone functions:
In the same kind also an analogWrite function could be written, which is compatible with a modified itone function. Oh, the serial connecton still works, when timer1 is used for itone. |
hi sir my linux arduino ide using to program nodemcu but port always not
enable u have any solution for this problem sir
…On Mar 22, 2018 6:22 PM, "AlfonsMittelmeyer" ***@***.***> wrote:
I wrote an own tone and noTone function with names itone and noItone.
itone works with frequencies beginning with 1 Hz. But with core 2.4.1 itone
has the same behavior as tone after using analogWrite. analogWrite is very
aggressive. If called only once timer1 can't be used nomore for other
purposes. analogWrite always grabs timer1 back. This means, if I need
timer1, and I need it for precise measurements, then analogWrite mustn't be
used.
These are my itone and noItone functions:
/*
ESP8266 example for tone via timer1
Hardware: NodeMCU
2018
tone using Timer
*/
#define ITONE_CYCLE 2500000 // 1 second in 1/16 CPU clock for 80 MHz
// #define ITONE_CYCLE 5000000 // 1 second in 1/16 CPU clock for 160 MHz
//=======================================================================
// itone, noItone
//=======================================================================
byte _itonepin = D0;
byte _itoneval = HIGH;
void ICACHE_RAM_ATTR _onItoneTimerISR(){
_itoneval ^= 1;
digitalWrite(_itonepin,_itoneval); //Toggle LED Pin
}
void itone(byte pin,unsigned long frequency) {
timer1_detachInterrupt();
timer1_attachInterrupt(_onItoneTimerISR);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
_itoneval = HIGH;
_itonepin = pin;
pinMode(pin,OUTPUT);
digitalWrite(pin,HIGH);
timer1_write(ITONE_CYCLE/frequency);
}
void noItone() {
timer1_detachInterrupt();
}
//=======================================================================
// Setup
// we test the serial connection during itone running
// result: it still works
//=======================================================================
void setup()
{
Serial.begin(115200);
itone(D0,1); // LED blinks with 1 Hz
}
//=======================================================================
// MAIN LOOP
//=======================================================================
void loop()
{
}
In the same kind also an analogWrite function could be written, which is
compatible with a modified itone function.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#4349 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/Ajj29SVBP_1HaHI3hjtvH2gNU3vG69ABks5tg55zgaJpZM4SBV0J>
.
|
I use the arduino ide on my raspberry pi3 and port /dev/ttyUSB0 works without any problems. You should try this command without NodeMCU connected and after with NodeMCU connected:
then you may compare, whether the serial connection was detected and with which name |
ls /dev/tty*
am try this one in terminal but output is lot more to come like this
…On Mar 23, 2018 12:26 AM, "AlfonsMittelmeyer" ***@***.***> wrote:
I use the arduino ide on my raspberry pi3 and port /dev/ttyUSB0 works
without any problems. You should try this command without NodeMCU connected
and after with NodeMCU connected:
ls /dev/tty*
then you may compare, whether the serial connection was detected and with
which name
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#4349 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/Ajj29bruoe04s6rgsQQikY4Cxb_lrh-6ks5tg_PcgaJpZM4SBV0J>
.
|
yes, you see a lot beginneng with /dev/tty and you should try this with and without the NodeCMU connected and then you can compare, whether there is one more with connected NodeCMU. |
ok sir you are in whatsapp or Skype because its useful for my further
doubts
am more interested in using microcontroller like arduino ,nodemcu
and raspberry pi
…On Mar 23, 2018 3:13 PM, "AlfonsMittelmeyer" ***@***.***> wrote:
yes, you see a lot beginneng with /dev/tty and you should try this with
and without the NodeCMU connected and then you can compare, whether there
is one more with connected NodeCMU.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#4349 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/Ajj29b5Aeuz-OvUT_lPjt0nZAl8yjOZoks5thMOxgaJpZM4SBV0J>
.
|
was there one one /dev/tty* ? The raspberry po3 is my favored desktop py and I don't like to use its GPIO ports. What if I would destroy my raspberry? But if something would happen to the NodeCMU, that's not any problem. |
Please @AlfonsMittelmeyer @santhosh000 stop, this is an esp8266 issue not a Raspberry support forum. |
I don't want to talk about the raspberry specially, because it's only my favored linux pc. And talked about connecting the NodeCMU with a linux pc. |
great
…On Mar 23, 2018 3:38 PM, "AlfonsMittelmeyer" ***@***.***> wrote:
I don't want to talk about the raspberry specially, because it's only my
favored linux pc. And talked about connecting the NodeCMU with a linux pc.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#4349 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/Ajj29XewF9KkMFt46ZQAkcwwAcPtCUlUks5thMmpgaJpZM4SBV0J>
.
|
@santhosh000: could your linux pc detect an additional device /dev/tty* ? |
no sir what I'll do
…On Mar 23, 2018 4:17 PM, "AlfonsMittelmeyer" ***@***.***> wrote:
@santhosh000 <https://github.com/santhosh000>: could your linux pc detect
an additional device /dev/tty* ?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#4349 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/Ajj29R_IMlr0wipS0Vd5oM3bbq5vgNIUks5thNK-gaJpZM4SBV0J>
.
|
@santhosh000: what you should do is a detailed error description including pc hardware and os. For example Dell PC OPTIPLEX 760 Linux Ubuntu 16.04 LTS. I tried it with this computer and got /dev/ttyUSB0 when using ls /dev/tty*. The bug could be a defect NodeCMU, a defect USB cable or a PC or a linux, which can't recognize the connection. So try another USB cable, check whether you inserted it correct into the USB connector, try other linux systems and windows systems and other PCS. When nothing works, then it seems to be a defect NodeMCU. |
see my port always disable state I'll do all ideas
On Mar 23, 2018 5:11 PM, "AlfonsMittelmeyer" <[email protected]> wrote:
@santhosh000 <https://github.com/santhosh000>: what you should do is a
detailed error description including pc hardware and os. For example Dell
PC OPTIPLEX 760 Linux Ubuntu 16.04 LTS. I tried it with this computer and
got /dev/ttyUSB0 when using ls /dev/tty*. The bug could be a defect
NodeCMU, a defect USB cable or a PC or a linux, which can't recognize the
connection. So try another USB cable, check whether you inserted it correct
into the USB connector, try other linux systems and window systems and
other PCS. When nothing works, then it seems to be a defect NodeMCU.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#4349 (comment)>,
or mute
the thread
<https://github.com/notifications/unsubscribe-auth/Ajj29dkkTuruq15lBabA3avyQEHTjADEks5thN95gaJpZM4SBV0J>
.
|
@santhosh000 : aren't you able to tell about your linux pc? The NodeMCU isn't recognized by all linux systems, especially elderly versions. So I have an old Android TV stick MK808 B, which I had flashed with an old linux picuntu. It doesn't recognize the NodeMCU. Maybe you have to install an USB to UART Bridge driver: https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers read also: https://cityos-air.readme.io/docs/1-usb-drivers-for-nodemcu-v10 |
@santhosh000 @AlfonsMittelmeyer This is not a support forum. This is an issue tracker for issues in the core libs. In addition, this specific issue is about tone and pwm incompatibility, and has nothing to do with usb or tty or rpi. Please discuss at a general forum like esp8266.com or stackoverflow, and stop hijacking. |
latest version debian running now on my linux pc
…On Mar 23, 2018 5:54 PM, "AlfonsMittelmeyer" ***@***.***> wrote:
@santhosh000 <https://github.com/santhosh000> : aren't you able to tell
about your linux pc? The NodeMCU isn't recognized by all linux systems,
especially elderly versions. So I have an old Android TV stick MK808 B,
which I had flashed with an old linux picuntu. It doesn't recognize the
NodeMCU.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#4349 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/Ajj29XAtHwwmOFGXFtSzGJdGCnGnWCxfks5thOmUgaJpZM4SBV0J>
.
|
@santhosh000: we should end this discussion here about your NodeMCU, because this thread is about incompatibility of PWM and tone. I don't want to install debian for further research. Because raspbian is derived from debian, I would think, that also latest versions of debian should recognize a NodeCMU. So most probably your NodeMCU is defect. You could check this by installing Ubuntu 16.04 LTS, because this linux version worked. And if not, then send back your NodeCMU and exchange it with one, which works. |
Back to the problem incompatibility PMW and tone. It's clear, that there can't be a solution if PWM and tone is handled in different libraries. Tone has to be integrated in PWM, if we would like to use both. I just began to deal with NodeMCU and ESP8266 and wonder, where I can find a good documentation. About timer1 I found only this via google: and the header file on github: Then I wanted to know about an accurate time, not in microseconss but in CPU clocks. Via google I could detect an assemler code for this: So I had enough information for writing an own PWM function, in which I could integrate a tone function in the next step. But still there was a problem. I couldn't find any information for which values timer1_write works. Seemingly for values below 28 an overflow takes place, which results in a pause of nearly one second. Sorry that I am a new bee in connection with the NodeMCU. Is there somebody who knows, where I could find useful documentation? |
I think, it's not great problem to use ananogWrite and tone. Just write an own analogWrite function, where you may integrate an own tone function. This is such an analogWrite function. It differs from the original analogWrite function in two ways: 1.) digitalWrite will not stop this analogWrite. It has to be stopped by analogWrite(pin,0) for LOW or analogWrite(pin,1024) for HIGH, if we don't want to write an own digitalWrite function too, which considers this itself. 2.) it doesn't grab timer1 back, if we detach this timer for other purposes What do you think about this analogWrite:
Oh, I forgot to tell, that I used a two color LED connected with D1 and D2. Normal LEDs also would be fine. |
I recognized a small bug in my algorithm when testing with this code:
I saw a short flicker, when 1024 was reached. It's clear, that changes shouldn't be made during a cycle. Changes should be prepared which take effect exactly at the beginning of the next cycle. So I should change my algorithm. |
Now this algorithm seems to work perfect. Does anybody like to integrate tone?
|
@minida28 : hi I did it. PWM at pin D2 (take a LED) and sound at pin D3 (oh susanna). Here is the solution:
Of course, also duo-tones, triads and chords would be possible. |
Oh, I forgot the duration, which I also should implement. And about music for several voices. It's clear that this cannot be done by only one pin for tone, because we have only HIGH or LOW. But If we would use several tone channels, the channels at different pins and if we would connect these pins by resistors, then this should work. Wouldn't you think so? |
Oh PWM and tone work very fine. Is there no further interest? I tested PWM with music for several voices. It works very well. Of cource, square waves are not the nicest sound. Here an excerpt of my program:
|
It's difficult to get a constant execution time. In former times code was in the Rom or in the RAM and had a predictable execution time. But now the execution time depends also on location in chache. This function would be one part of the time delay function:
But I don't know how I get a constant execution time. For a parameter value of 1000 I got execution times of 6000 until 18000 ticks. I would like to have a constant execution time, which is an integer multiple of the parameter value. For thousand something like 6000 would be good, but not something like 7346. Does somebody know, how to get such a constant execution time for this function? |
Maybe what you need is delayMicroseconds()? |
@AlfonsMittelmeyer As part of my much delayed ESP8266-eggduino for Easter, I've been writing a new timer1-driven waveform generator which might give you some ideas on how to proceed. Since a 3d printer (well, 2.5d in the egg case) needs to produce multiple tone()s, analogWrites(), servo signals, and stepper motor control motions, it's set up as a generic, IRQ driven waveform generator driven off of "when is the next edge I need to generate?" What may help your use is that it uses both Timer1 and the ESP cycle counter in a feedback loop. Timer1 is loaded with the # of ticks (- epsilon) until the next edge of the multiple waveforms, to ensure the IRQ gets called. But because there's much jitter in when the IRQ actually gets called, I use the ESP's internal cycle counter register to base all the waveform logic off of. That way even if the IRQ is delayed it's taken account of for all other waveforms and not additive. Also, I'm not sure I understand why you're worried about tick accuracy. 1 tick @ div-by-1 == 1 ESP8266 instruction = 12.5ns or 6.25ns, no? That's unreasonable to expect outside of fixed-function HW like the AVRs which, IIRC, can actually toggle an IO bit in HW off of their internal timers. What frequency are you trying to generate? At 1khz you've got 0.5ms/edge, so if you can keep accuracy to w/in 1% that's 5us = +/-400 or 800 instructions. @ 0.1% accuracy = 1us => not gonna happen with IRQs, there's no time for any user code. |
@devyte No, thanks, it's not microseconds, what I would like to have. Currently I have 6 ticks, which is about µs/13. It would be nice to have µs/80 or µs/160 in case of 160 MHz CPU frequency. Of course this exactness would not be necessary. I only wonder, whether it could be done. Yesterday I tried to find a solution in C/C++, but couldn't. Often a while instruction consumes 320 additional ticks and I don't have any idea why and how to avoid this. So now I will try it in assembler. |
I don't quite understand the problem you're describing, but here are some thoughts:
|
@devyte: The problem with problems is, that problems are difficult to unterstand, because they have some complexity. I found a solution, which should only be used in nmi interrupts, because it doesn't readjust to time consumed by interruptions like interrupts or tickers. My function delay_ticks works correct for values >= 4 If you would like to unterstand, what's the problem with while statements, then try to replace my assembler routine "delay_ticks_resolution4" by using while instead. I tried a lot without any success. A while routine works correct, if we don't try to adjust also for remaining ticks. If there are also other statements involved, then the compiler compiles something with a timing behaviour, which isn't usable. Instead of 6 ticks for a loop it could be 10. I also got 20. But most disturbing is an additional delay of about 320 ticks, which the compiler produces. I don't know, why it does this, maybe pushing a lot of register contents to the stack? The same behaviour of an additional delay of 70 or 130 ticks I also got by using an assembler routine, which uses the ccount register. Maybe I should try to implement the switch cases also in assembler. Then such a delay_function could also be used outside of nmi interrupts. Of course, if an interrupt exceeds the duration of a delay function, the delay function cannot stop during an interruption, only readjust in the case, thad the delay exceeds the interrupt. Here is my code for the delay_ticks function:
|
You wrote:
This I do too. The functions, which replace T1L = ticks, have an integrated ccount measurement. For PWM I implemented precedence over other shared timers, if it's only a small time span. Therefore exists an own loop for small timespans with only very little overhead. And for pwm there exists also an own trigger function, which executes very fast, but lacks some features like timespans until 0x7FFFFFFF ticks instead only until 0x7FFFFF:
Here you see, that function "timer_shared_write_pwm(ticks)" is a define, which contains the ccount measurement, already before the real trigger function is called. I couldn't use inline here for the time measurement, because other modules need C binding. Offering an inline version for modules compiled with C++ also doesn't make sense, because this would result in a different -epsilon, which cannot be distinguished without wasting time for flag storage. You wrote further:
This is correct, but not sufficient. There is a time delay between the counter expires until the ISR is called, maybe of 130 ticks for NMI timer1 interrupts. This means short time spans like analogWrite(1) or analogWrite(2) cannot be handled by triggering the next IRQ, they must be handled by a loop, which consumes time. I measured, that currently PWM for small values and also for large values are very incorrect. Because it wasn't a precise measurement done by pulseIn I only can estimate it. I would think, that the PWM cycles are 3 µs too long, which results in an error for small values, which shouldn't be tolerated und that pin LOW is often missing for a value of 1020 and fully missing vor values in the range of 1021 .. 1023. You wrote further:
I measured, that there isn't any jitter, when using an NMI interrupt and only one shared timer. Of course using more than one shared timers would result in some jitter, when there would be an overlapping of shared timers. In case of TIM_LOOP, which fits for tone, the jitter isn't additive. In case of TIM_SINGLE the user isr callback for the shared timer has to handle itself, whether the jitter shall be additive or not. You wrote:
Oh it's simple. The default frequency of PWM is 1 khz with analogWrite values from 1 to 1024. and with 1024 for the full cycle. Value 1 means 80000 / 1024 ticks (for 80 MHz CPU frequency). This means about 0.98 µs steps for analogWrite values or 78 CPU ticks for analogWrite(1). I implemented, that if ticks are triggered at the very end of the user isr, then this will be the ticks until the very beginning of the next user isr callback. If it should be the ticks from triggering ticks until next time triggering ticks, then this has to be implemented in the user callback by considering it's execution time. My implementation allows these 78 ticks for PWM. But for a timespan from tick triggering to tick triggering, by subtracting further ticks in PWM, the accuracy will not be fully sufficient for analogWrite(1). analogWrite(2) shouldn't be a problem. PWM would be very exact, with my shared timer implementation. It could be more exact, if the PWM isr itself would consider its own execution time. But then for analogWrite(1) the accuracy will not be enough when using 80 MHz, could be 160 Mhz could do it. I saw, that the CPU offers loops, which are performed by the processor without consuming time. Such loops are ideal for signal processing. But what a pity. The GNU assembler doesn't know this loop option. A loop done in assembler be decrementing and branching costs 4 ticks per loop. The C/C++ compiler could waste an unspecified time of 6 to 20 ticks for one loop depending on some compiler internals, like code length of the function or number of used registers for local variables. And then there are also unmotivied delays, like 320 ticks, only because I had used a while when I tried to implement a delay_ticks function. After having noticed such unpredictable timing behaviour of C++ now I understand, why some institutions use ADA instead. |
CPU configuration used in ESP8266 doesn't have zero-overhead loops. The one used in ESP32 does. |
@igrr : thank you. Then I don't need to look further for it. The implementation with switch cases is a very great hack, which works for 4 cases, but not for seven. More cases produce more overhead and different times for execution. It's pure luck, that it worked in this case and should be implemented in assembler in a proper mannner. Such switch cases implemented in assembler would also be usable for taking ccount into account. An assembler routine, which considers ccount, would be this:
Completed by adding the switch cases and by adding not enter the loop for only a few ticks, this would be the ideal delay_tick function. Today I was ill, but tomorrow I will try to complete the shared timer module. |
Sorry, my solution with the switch cases, which I had presented before, is no solution. It only works for constant values. I had thought this over. How could such a function work without any overhead, which it did. This is strict impossible for a real function, which is compiled for variable values. If we have this function:
And if we call this function by:
It simply compiles:
For more cases it would compile something different and for a variable value something else. So this hack simply isn't something useful. A real implementation of a real delay_ticks function in assembler would have probably an overhead of 5 ticks. And such a time consuming switch case may be implemented in this way:
|
I got the delay_ticks function, which is needed:
It has an internal overhead of 6 ticks, which is subracted. So it works correct for values >= 6, if the parameter is passed by a local variable (register) or by a constant value < 2048. For values below 6, the execution time will be 6 ticks. For constant values >= 2048 the compiler produces an additional overhead of 6 ticks for parameter passing. It's clear that also passing a value from a variable in the RAM produces an overhead, which has to be considered. |
Wasn't this a nice idea? This never could have be done using c/c++ without assembler. |
By better measurement via a register (local variable) I saw, that the overhead was only 5 ticks and by further optimising I got now an inline version for exact delays >= 4 ticks and a ICACHE_RAM_ATTR version for exact delays >= 11 ticks. Now there is enough done about this and so I should complete my work about the shared timers. Further otimising of the shared timers could be done later after a pull request and releasing the first version. |
@AlfonsMittelmeyer please look me up in gitter to discuss your work, I have several questions. Also, I'm having a bit of trouble keeping up with what you're doing, and I suspect I'll be reviewing later on, so I'd like to have a direct channel open with you for quick back and forth. |
@devyte I just visited gitter for the first time. It's time to begin with discussing the details. Yesterday I had the idea about the optimal interface between the shared timer isr and the user callbacks. First I had implemented a complicated write function, which could replace T1L = ticks. But yesterday I had the optimal idea: return ticks; And for stopping the user isr callback from inside: return 0; (which applies for tone with duration) PWM should work jitterless and 80 ticks for analogWrite(1) shouldn't be a problem. There isn't much to change: simply forget about TEIE and T1L, just return. But for tone I would like to optimize the isr. digitalWrite with 69 ticks shouln't be used, if this could also be done by a few ticks. |
Does somebody know, how to do this, without causing a bug?
The problem is, that this variable is also used by an interrupt, which changes other bits. If this interrupt would occur between this statement, which changes other bits, I would undo this changes. I remember, that a former colleague had this problem, when using ^=. And he searched for the bug maybe two weeks and couldn't find it. Then he asked me to help him. I told him, that he has to include the statement in disable and enable the interrupt, because this statement are three instructions:
And after having done this, the bug was fixed. But now it's an NMI interrupt, which cannot be masked. Maybe the CPU knows an instruction for an |= for memory? if not, then I have to invest 6 ticks more for reading a flag from another variable. What a pity, currently I could reduce the minimum time for triggering the next call of the PWM ISR to 32 ticks. For doing this, the delay_ticks function was very helpful. |
Hi guys! Any progress here? |
Yes, there is progress. earlepilhower presented a solution and my solution will be ready also in a few days. Sorry, that it took a long time. Currently there are only very few proplems. I test with multichannel tone with 7 pins for 7 tone channels and pwm at the same time. Tone with 7 channels works very well in combination with PWM frequencies until 4 khz. But not good any more for 10 khz PWM frequency and very bad, when I use 30 khz for PWM. Further 40 khz PWM frequency doesn't work at all. There is some little work still to do. But it shouldn't take much days any more. |
Oh, the problem with bad sound in combination with 30 khz PWM could easily be solved. I used timers, which were configurated to be additive to jitter. Changing the Parameter to not additive to jitter solved the problem. Then there is only the problem, that frequencies of more than 30 kHz cause a reset. But I want up to 50 kHz. Not for PWM, but for sound sampling with 44.2 kHz. I must do measurements for exact adjustment of the software timers, so that I may know, which frequencies are possible and how to adjust the timers correctly. This means to know exactly, when the hardware timer (timer 1) shall be triggered and when the loop within the interrupt should be executed. Some details about the timers: there are High Priority timers and Low Priority Timers and one First Priority Timer. LOW priority timers don't disturb High Prioriy Timers, because they only use big enough gaps (configurable) between High Priority timers. And the First Priority timer may be configured, so that other High Priority timers don't disturb it. This means, that the First priority timer may execute at the very exact time (in CPU ticks). So exact steps until one CPU clock tick (1/80 µs or 1/160 µs) are possible, if only one pin for PWM is used and if the lowest analogWrite values don't matter. If there are more pins used for PWM, then 2kHz PWM frequency should be possible without any tick jitter, if pin D0 isn't used - I still have to do a little change for reaching this goal. |
@AlfonsMittelmeyer sounds fantastic! Thanks! |
I could optimize the code. 2 kHz PWM frequency may now work also jitterless for more than one PWM pin (if pin D0 isn't used). But there is a little bug. I clearly saw the difference of analogWrite(pin,1) and analogWrite(pin,2), when I looked at a LED. After this I measured the ticks. I had chosen a PWM frequency of 2 kHz, a PWM range of 1000 and the PWM value 1. So we should expect to measure a difference of 40 ticks from setting the pin to HIGH until setting the pin to LOW. But I measured 39 ticks, which is 1 tick short. The same problem also when I used 1000 kHz, then I got 79 ticks instead of 80. I think, that I forgot rounding in my algorithm. For the PWM frequency I had used float instead of integer, because I think, that this would make more sense. |
I found the bug. The problem is this function call:
It works correct, when the code of the function call is located at a 32 bit word border. I inserted a no operation code somewhere before and then it worked correct. A pity that this function call always has to be checked after code changes. Maybe I could implement a function, which works correct independent of the code location of its call? Even if it doesn't work for delays >= 4 ticks but only for delays >= 5 ticks. An interesting question is about the PWM frequency, up to which jitterless PWM may be done even, when more pins are used for PWM. I measured 35 ticks as minimum. 2 ticks have to be subracted, which I had added for measurement. So the minimum is 33 ticks, which would mean a PWM frequency of up to 2.4 kHz. |
I found the solution for this problem. Instead of:
I use now:
This is the following macro:
The correctness is independent of the location, because it doesn't matter, whether it takes one tick longer at a different code location. Important is only, that for one code location, the behaviour is the same for all values. |
I thought, that today I could finish my work. But then a bug occurred. This works fine for 10 kHz:
But it doesn't work well for 20 kHz. At the end the brightness of the LED should be very low. But for 20 kHz at the end the LED brightness jumps to seemingly full brightness. Seems to be a calculation error somewhere. |
@X-Stas-EEE , if you're still trying to do multiple analogWrite()s and tone()s, you could try pull request #4640 which also seems to fix an occasional WDT in the existing analogWrite. |
@earlephilhower thanks a lot! I'll try it as soon as I have time! |
@earlephilhower Many thanks! It works fine! |
Closing this issue as fixed with #4640 commit. |
Basic Infos
Hardware
Hardware: NodeMCU ESP8266
Core Version: 2.4.0
Description
Hi. I'm having trouble using tone() and analogWrite() on the NodeMCU ESP8266.
Calling only tone() works fine, however calling analogWrite() on another pin, setting it to LOW and then calling tone() (on a different pin) only produces an annoying noise.
Sketch
The text was updated successfully, but these errors were encountered: