Help regarding pulse generation

Hello everyone

I am currently working on a project in which I want to convert all PWM/pulses generated by different timing ICs in an analog circuit and replace them all with a microcontroller, so the size of the circuit gets reduced considerably. For this purpose I am using atmega328p for now. I have replicated couple of pulses but the only problem I am facing with these pulses. when the CLK pulse turns off immediately the MOSFET pulse should turn on as shown in (PICTURE 1).

PICTURE 1 = clk and mosfet

but my pulses generated using atmega have all parameters same, everything is good except this problem I am having 10us delay between pulses as shown in (PICTURE 2)

PICTURE 2 =

this is the code which I have created till now for these two pulses

//assigning pins
const byte clk = 3;
const byte mosfet = 5;
//const byte delaypin = 6;
//const byte groundB_pin = 9;
const byte clk_pot = A0;
//const byte mosfet_pot = A1;
//const byte delay_pot = A2;
//const byte groundB_pot = A3;

//constants

const float clockcycle = 62.5E-9; //one clock cycle time (1/16Mhz)62.5nS
const unsigned long maxcount = 65535; // value of 2^16-1
const float prescalar = 8.0;
const byte readdelaylimit = 100; //wait for 100ms to read delay pots
const byte readsettinglimit =25;
const byte clkon = HIGH;
const byte clkoff = LOW;
const byte mosfeton = HIGH;
const byte mosfetoff =  LOW;
const float clk_multiply_value = 41.2;  //value to multiply pot with to achieve desired timing
const float  mosfet_multiply_value = 5.3764;



//setting timings
float clk_on_time = 84E-6;                                      //84us is clk "ON" time 
float mosfet_pulse_delay =0;                                 //mostfet delay reference to clk 
float mosfetdefault_on_time = 110E-6;                         // 110us on time on lowest value of POT
float mosfet_on_time = mosfetdefault_on_time;               // variable from POT
float clkperiod_default = 8.10E-3;                    //8.10mS
float clk_period = clkperiod_default ;              //varibale from POT

//timing offsets to get our desired timing pulses right (can only be set while real time testing of pulses)

float clkOnOffset = 3E-6;
float mosfetDelayOffset =0;
float mosfetOnOffset =4E-6;
float clkPeriodOffset = 20E-6;

// program variables
float cal1,cal2,cal3,cal4,cal5,cal6,cal7;      // variables just for calculations
word clkOnCount;                              // clk pulse
word mosfetDelayCount;
word mosfetOnCount;
word clkPeriodCount;
byte readsettingscounter =0;
byte readsettingstate =0;
byte intState = 0;                         //interrupt routine state machine
boolean readsettings= false;


void setup() {
  //Serial.begin(115200);
 pinMode (clk , OUTPUT);
 pinMode (mosfet,OUTPUT);
 //pinMode (delaypin,OUTPUT);
 //pinMode (groundB_pin,OUTPUT);
 calcTimerValues();                    //calculate all timer values in start first 
 cli();                               //to stop interrupts
 TCCR1A = 0;                         //Timer 1 registers
 TCCR1B = 0;
 TIMSK0 = 0;                        // clearing timer0 to eliminate jitter if occurs in wave (important :delay() function wont work now)
 TCNT1 = clkOnCount;                // loading timer1 with clk On value
 TCCR1B |= (1<<CS11);               // setting prescalar to 8 
 TIMSK1 |= (1<<TOIE1);              // enabling timer1 overflow interrupt
 sei();                             //enable interrupts
}


void calcTimerValues()
{
  cal1 = (clk_on_time - clkOnOffset) / (clockcycle * prescalar);  
  clkOnCount = maxcount - int(cal1);                                 //clkOnCount for timer1 

  cal2 = (mosfet_pulse_delay - mosfetDelayOffset) / (clockcycle * prescalar);
  mosfetDelayCount = maxcount - int(cal2);                            //mosfetdelaycount for timer1

  cal3 = (mosfet_on_time - mosfetOnOffset) / (clockcycle * prescalar);
  mosfetOnCount = maxcount - int(cal3);

  cal7 = (clk_period - clkPeriodOffset) / (clockcycle * prescalar);
  cal7 -=cal1+cal2+cal3;
  clkPeriodCount = maxcount - int(cal7);

}

ISR(TIMER1_OVF_vect)
{
  switch (intState)
  {
    case 0:
     TCNT1 = clkOnCount;
     digitalWrite(clk , clkon);
     intState = 1;
     break;

    case 1:
     TCNT1 = mosfetDelayCount;
     digitalWrite(clk ,clkoff);
     intState = 2;
     break;

    case 2:
     TCNT1 = mosfetOnCount;
     digitalWrite(mosfet ,mosfeton);
     intState = 3;
     break;

    case 3:
     TCNT1 = clkPeriodCount;
     digitalWrite(mosfet ,mosfetoff);
      if (readsettings == false)
     {
      readsettingscounter++;
      if (readsettingscounter >=readsettinglimit)
      {
        readsettings=true;
        readsettingscounter=0;
      }
     }
     intState = 0;
                
     break;

    default:
     intState = 0;
     break;
    

 }
}

void loop() {

if(readsettings == true)
{
  switch(readsettingstate)
  {
   case 0:
    clkpotValue = analogRead(clk_pot) * clk_multiply_value;
    clk_period = clkperiod_default + (clkpotValue * clockcycle);
    calcTimerValues();
    break;

   case 1:
    mosfetpotValue = analogRead(mosfet_pot) * mosfet_multiply_value; 
    mosfet_on_time = mosfetdefault_on_time + (mosfetpotValue * clockcycle);
    calcTimerValues();
    break;
    
    
  }
  readsettingstate++;
  if(readsettingstate == 2) 
  {
    readsettingstate = 0;
    readsettings = false;
  }
}
}

I see two possible causes of your timing issues:
1: You're using floating point math to calculate the timings. I don't see a compelling reason to do so, and on a 328P this will cost valuable clock cycles as it doesn't have a FP unit. So in short: don't use floats here, but use integers instead. There's no reason why they wouldn't work.
2: This may be one of the somewhat rare cases where direct port manipulation is smarter than using convenient digitalWrite()'s. The latter are user friendly, but relatively slow.

question:
Q)If I don't use float and use integer instead then how I am going to give timing value to the pulse because pulses are in a microsecond?

In my opinion, correct me if I am wrong this 10us delay is occurring I think due to the delay of the code that is why I have given offsets value to different parameters. But this is where it bites me back I cannot set offset for mosfet_pulse_delay because it is already zero. I will try your method of direct port manipulation instead of using digital write for speed but I think it won't reduce it much from a 10us delay between pulses. In theory, it should work.

All variables that are used inside and outside an ISR must be declared as a volatile type.

volatile byte intState = 0;

Anyway I think you are going about this all wrong. Especially as you want to convert more signals.

Basically what you are wanting to do is to pass those signals through a monostable to lengthen the pulses. This can be done simply with a monostable chip, the 74LS122, contains two monostables, or you can make one from a common NE555 chip.

If you want a software solution then, don’t use an analogue read, that takes 100nS to do and effectively blocks any other pulse detection, make these digital inputs instead.

I would use the pin change interrupt function enabled for the falling edge. Then in the ISR when you sort out what pin has caused the interrupt just raise the appropriate output pin and set the time given by the millis() function in an array with each pin you use as an output having its own entry.

Then as part of the loop function look at this array and for every element that is not zero and whose value exceeds the current millis() value by more than the stretching you want, you lower the associated output pin, and replace the millis value in that array with zero.

Note there will always be a delay between the falling edge and your rising edge, because of the time it takes software to respond to any change. This is governed by the clock of the processor and the code it has to go through to react to input. Even with hardware there will be a delay, determined by the propagation delay of the chips involved. So what do you consider an acceptable delay?

Hi mike,

I cannot use PCINT because there are no inputs everything is done in software and what atmega is doing only giving outputs. Yes I was thinking that analog read is pretty slow, after generating more pulses I think I will use registers for adc rather than using analog read if I cannot fix this problem. thanks for pointing out volatile problem I totally forgot about it but without that the program is operating as it should but I will use it and see what are the results.

between these two pulses of clk and mosfet there should not be any delay when the clk pulses turn off the mosfet should immediately turn on as shown in the picture above. That is what I am trying to rectify everything else is ok and according to what I want. I dont know if my formula is flawed.

So what is the line
mosfetpotValue = analogRead(mosfet_pot)
Doing if not being an input?

Maybe I don’t understand correctly what you are trying to do with your code.
A digitalWrite takes about 4uS where as a port manipulation takes just one clock cycle of the 16MHz clock.

Please note my edit of the last post last paragraph.

That won’t help, you need to adjust the pre scaler register to get a faster read.

The minimum delay will be achieved if the clock and FET signals are changed in the same port manipulation operation. This means some restrictions on what pins you can use.

Yeah, so? Microseconds are still integer values. In the end, you all calculate it back to an 'int' type in your code anyway, so there's no reason whatsoever to mess with floats.
There appears to be a logical flaw in your code as well BTW, since you have defined maxcount (which oddly is an unsigned long - why not just an uint16_t!?) as 65535 (0xFFFF) whereas you use a plain 'int' (which is signed!) for your final calculations. Hence, at least conceptually you have an overflow risk if you try to subtract more than 32768 from maxcount and then store it in a signed int. If this problem materializes depends on how the values work out; I didn't do the math to check it.

So how about going over your math once more? Odds are you have a mistake there, I agree with you on that.
But if the clk vs. mosfet pulse timing is so critical, why not make a low-going clk always be followed with a high-going mosfet pulse?

clk potentiometer is used to change its frequency because the analog circuit which I am working on required variable frequency from 95hz something to 122-125hz which is happening in this code and mosfet potentiometer is used to change the mosfet pulse ON time from 110us to 440us which is also being done in this code perfectly. what i want is that when clk pulse goes from high to low I immediately want mosfet signal to appear without any delay unlike in my code I am having 10us delay. And about prescalar I have choosed prescalar 8 after doing calculations, If i choose higher prescalar that wont work.

how can I make clk pulse follow mosfet pulse ? I did not knew about this

As I said

I've found that using digitalWrite inside the ISR took about 1.5us longer than setting a flag and doing the digitalWrite in the main program loop. I don't know why, maybe worth looking at.

1 Like

Eliminate case 1 from your ISR. It only seems to serve the purpose if creating a delay between pulling clk low and mosfet high, which is exactly what you don't want.

oh yes, I already tried that no difference at all.

just an update I tried port manipulation but no effect unfortunately.

Why not trim it down to the bare essential: just create a clock pulse followed by a mosfet pulse, nothing else, at a single frequency. Once you get that to work, make it more fancy with the additional functionality you need.

We've taken several stabs at possible causes, I think breaking it down to atomics (the approach above) will prevents us from further stabbing in the dark, guesswork and conjecture.

1 Like

Then I suspect you are not doing it correctly. Please post what you tried.

Maybe you meant 4 microseconds, not nanoseconds. At least that's how much I measured with an oscilloscope on ATmega328@16MHz - from 4 to 6 microseconds dependent for wich pin is use.

Yes sorry you are right 4uS. Original post corrected.

hello guys just an update

I was able to achieve what I was trying to do turns out I was just an dumb ass. Thanks a lot mike for an idea of port manipulation, I was just implementing it a wrong way and realized my mistake just now. also thank you koraks

Great, nice to hear it works now! :slight_smile:

1 Like