Bit bang PWM misses the first cycle

Hi,

I have a a function that is supposed to take in inputs and output a pwm signal that steps up its duty cycle over time. The function works almost and its the almost part that is driving me nuts. The first pulse should be high for a tenth of the period but its not. The pulse that does output first is what I would expect the second pulse to be. I am using a scope to verify the signal. When I have the while loop serial out the counter of the loop, 0-9 show as expected. What am I missing? For reference the inputs I have been using are pwm(0.1, 0.9, 10, 3000);

void pwm(float dutyStart, float dutyEnd, int cycles, int frequency)
{
int periodmicros = (int)(1000000/frequency); //frequency in Hz
float offset = (dutyEnd-dutyStart)/(cycles-1);
int i = 0;
while(i < cycles)
{
digitalWrite(10, HIGH);
delayMicroseconds(dutyStartperiodmicros); //Stay high for the duty cycle
digitalWrite(10, LOW);
delayMicroseconds(periodmicros-(dutyStart
periodmicros)); //Stay low for the remainder of the period
dutyStart += offset; //Increments the duty cycle by the average.
i++;
}
}

How have you got the scope trigger set up? :slight_smile: Can we see a screen shot?

I can get one tuesday but the scope is showing 10 pulses, triggering on rising edge and and set to like 3V.

At those speeds, the floating point math might be adding crippling delays. Your on period is only 33us.

void pwm(float dutyStart, float dutyEnd, int cycles, int frequency)
{
int periodmicros = (int)(1000000/frequency); //frequency in Hz
float offset = (dutyEnd-dutyStart)/(cycles-1);
int i = 0;
while(i < cycles)
{
digitalWrite(10, HIGH);
delayMicroseconds(dutyStartperiodmicros); //Stay high for the duty cycle
digitalWrite(10, LOW);
delayMicroseconds(periodmicros-(dutyStart
periodmicros)); //Stay low for the remainder of the period
dutyStart += offset; //Increments the duty cycle by the average.
i++;
}
}

delayMicroseconds(dutyStart*periodmicros); //Stay high for the duty cycle
how long (micros) does this take to compute??

delayMicroseconds(periodmicros-(dutyStart*periodmicros)); //Stay low for the remainder of the period
again..same thing..

i doubt this is valid for any microsenconds() function..
this is like trying to time a bullet with a stopwatch..

NOPS to the rescue...

if u need tight timimg down into the microseconds--
forget about calling some function...
make ur own function..

pwm(start, stop){

for x=0 to start------
output --whatever
nop nop nop

for x= 0 to stop---
output---whatever
nop
nop
nop
}

use the cycle freq of the processor u are using for the determining bit increment of time..

@bwj, I disagree. delayMicroseconds() is okay for this. You just need to precompute integer timing values before you begin the while loop. But digitalWrite() takes some time. It would be good to use port manipulation for the output pin. Also, you seem to be overlooking the fact that your solution hard codes the number of NOP's.

The ultimate solution is using a timer with an output compare pin.

nops are not hard-coded.. simply for x=0 to WHAT..

a very tight pin driven output is defined this way..
it just depends how much 'jitter' is allowable..
u could be right..just use microseconds()

We need to know from the OP, what accuracy is needed.

On mobile and can not seem to reply to specific posts, so general it is. The accuracy is not terribly important, but I would like to be as accurate as possible. Precalculating the values for the delay would be a great fix to try but as the delay being calculated is dependant on incrementing in the loop I am unsure how to do it unless I pull from an external array which may work but will eat memory. I looked at just doing this with timers but the accuracy of the frequencies seemed lacking, the frequency required will vary greatly and the pwm function will be called for up to outputs, theres a multiplexer in my future. I can look at the nop solution when I am near a scope. I hope that answers questions if not shoot back at me. Also I could not seem to find reponse time for digital write anyone know. Oh, also lastly the loop seems to workish. Its just outputting what would be expected to be pulse width two as one and the last pulse width is longer by the average.