Nanosecond trigger pulse

I am trying to write some code, that when a push button is pressed,
turns a transistor "off" for 250ns then turns it back on untill the button is pressed again

To make testing this code easier im currently substituting in an LED for the transistor and am using a longer delay so that the LED switching is visible. in the code below you can see i have included the 250ns delay using NOP commands but have commented it out

The trouble im having is getting the LED to turn back on again if the push switch is held down as the code I've written means the button press resets the delay. Because pressing the push switch takes longer that 250ns im ending up with a longer pulse than i need.

int buttonInput = 3;
int lightOutput = 13;
int buttonState = 0;

void setup(){
  
  pinMode(lightOutput, OUTPUT);
    pinMode(buttonInput, INPUT);
    digitalWrite(buttonInput, HIGH);
}

void loop(){
   
  buttonState = digitalRead(buttonInput);
  
    if(buttonState == HIGH){
        digitalWrite(lightOutput, HIGH);
    } 
    
    else {
      digitalWrite(lightOutput, LOW);
        //__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); }
        delay (1000);
        digitalWrite (lightOutput, HIGH);
       
}}

All the functions, like digitalWrite, digitalRead, etc, take quite a lot of clock cycles to perform. At 16MHz that's 63ns per instruction (for 1 cycle instructions), so from the point of turning on to the point of turning off would give you 4 instructions to play with.

Using direct port manipulation (using PINx and PORTx) would be considerably faster than digitalRead() and digitalWrite().

Say we used port B1:

    	PORTB &= ~0x02;  // Turn off bit 1
    	PORTB |= 0x02; // Turn on bit 1

That compiles to:

 114:   29 9a           cbi 0x05, 1 ; 5
 116:   29 98           sbi 0x05, 1 ; 5
  • just 2 instructions. However, the CBI and SBI instructions are 2 clock cycles each. They are "read, modify, write" instructions, which means the read the register, modify it, and write it back. So you can expect the output state to change once the instruction is written. Break it down to:
1: Read & Modify 
2: Write // turns off
3: Read & Modify
4: Write // Turns on

So if it turns off and on at the same point in the write stage, then you're looking at 2 complete clock cycles in between each state of the output. So adding 2 NOPs in there should theoretically give you 250ns

And now to address your other problem: You're reading, and responding to, the level of the input. What you need to do is respond to the edge of the input. You need to read the input, and compare it to the state of the input last time you read it. Only when the state changes on two successive passes (from LOW to HIGH) do you want to trigger your pulse.

Again, using direct port manipulation can speed things up, by looking at a bit in a PINx register, such as pin B5:

unsigned char currentState = (PINB & 0x20); // Will either be 0x20 or 0x00 for HIGH and LOW.  Don't compare to HIGH, just use it as a truth (non-zero) value.

majenko:
All the functions, like digitalWrite, digitalRead, etc, take quite a lot of clock cycles to perform. At 16MHz that's 63ns per instruction (for 1 cycle instructions), so from the point of turning on to the point of turning off would give you 4 instructions to play with.

Using direct port manipulation (using PINx and PORTx) would be considerably faster than digitalRead() and digitalWrite().

Say we used port B1:

    	PORTB &= ~0x02;  // Turn off bit 1

PORTB |= 0x02; // Turn on bit 1



That compiles to:


114:   29 9a           cbi 0x05, 1 ; 5
116:   29 98           sbi 0x05, 1 ; 5



- just 2 instructions. However, the CBI and SBI instructions are 2 clock cycles each. They are "read, modify, write" instructions, which means the read the register, modify it, and write it back. So you can expect the output state to change once the instruction is written. Break it down to:


1: Read & Modify
2: Write // turns off
3: Read & Modify
4: Write // Turns on



So if it turns off and on at the same point in the write stage, then you're looking at 2 complete clock cycles in between each state of the output. So adding 2 NOPs in there should theoretically give you 250ns

And now to address your other problem: You're reading, and responding to, the level of the input. What you need to do is respond to the *edge* of the input. You need to read the input, and compare it to the state of the input last time you read it. Only when the state changes on two successive passes (from LOW to HIGH) do you want to trigger your pulse.

Again, using direct port manipulation can speed things up, by looking at a bit in a PINx register, such as pin B5:


unsigned char currentState = (PINB & 0x20); // Will either be 0x20 or 0x00 for HIGH and LOW.  Don't compare to HIGH, just use it as a truth (non-zero) value.

Hi,
Thanks a lot for the help. You have said exactly what I'm after. As you can probably tell I'm new to Arduino so although I understand what you mean I'm finding it difficult to code what you suggested and am getting a lot of errors. Could you please elaborate on how to implement the code? Thanks in advance

Of course debounceing the button will take longer 250nS. Use external hardware for this job not software.

Mark

Another option for the button, if you use hardware debouncing, is to use a rising edge interrupt. I wouldn’t do that for your long test delay - delay() doesn’t work in an interrupt.

I ment use external h/w to generate the pulse, not the debounce.

Mark

Typical (though untested) code for detecting a button edge (active low, using built-in pullups, no software debouncing):

const byte button = 4;
const byte led = 13;

void setup() {
  pinMode(button, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
}

void loop() {
  static byte ledState = LOW;
  static byte lastState = HIGH;
  byte currentState = digitalRead(button);

  if (currentState != lastState) {
    lastState = currentState;

    if (currentState == LOW) {
      ledState = HIGH - ledState;
      digitalWrite(led, ledState);
    }
  }
}

Jonesy135:
Because pressing the push switch takes longer that 250ns im ending up with a longer pulse than i need.

For this part of the problem you need to use a technique that is usually called 'edge detection' to detect when the input changes between high/low and only send your pulse when the input changes. This technique is demonstrated in the StateChangeDetection example sketch.

Of course debounceing the button will take longer 250nS. Use external hardware for this job not software.

Yeah, but it's the duration of the output pulse that needs to be limited to 250 nS, not the debounce. So what if the debounce takes 20 mS?

// Untested code

const int buttonInput = 3;
const int lightOutput = 13;
int buttonState;

void setup(){
  pinMode(lightOutput, OUTPUT);
  digitalWrite(lightOutput, HIGH);
  pinMode(buttonInput, INPUT);
  digitalWrite(buttonInput, HIGH);
}

void loop(){
  static unsigned long lastLowTimeStamp = 0L;   
  buttonState = digitalRead(buttonInput);
  
  if(buttonState == LOW){
    if (millis() - lastLowTimeStamp >= 50) {    // Debounce 20 mS
      digitalWrite(lightOutput, LOW);
      //PORTB &= ~0x20;         // Pin 13  = B5
      //__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
      delay (1000);
      digitalWrite (lightOutput, HIGH);
      // PORTB |= 0x20;
    }
    lastLowTimeStamp = millis();
  }
}

TanHadron:

Of course debounceing the button will take longer 250nS. Use external hardware for this job not software.

Yeah, but it's the duration of the output pulse that needs to be limited to 250 nS, not the debounce. So what if the debounce takes 20 mS?

// Untested code

const int buttonInput = 3;
const int lightOutput = 13;
int buttonState;

void setup(){
  pinMode(lightOutput, OUTPUT);
  digitalWrite(lightOutput, HIGH);
  pinMode(buttonInput, INPUT);
  digitalWrite(buttonInput, HIGH);
}

void loop(){
  static unsigned long lastLowTimeStamp = 0L;   
  buttonState = digitalRead(buttonInput);
 
  if(buttonState == LOW){
    if (millis() - lastLowTimeStamp >= 50) {    // Debounce 20 mS
      digitalWrite(lightOutput, LOW);
      //PORTB &= ~0x20;         // Pin 13  = B5
      //asm("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
      delay (1000);
      digitalWrite (lightOutput, HIGH);
      // PORTB |= 0x20;
    }
    lastLowTimeStamp = millis();
  }
}

This is perfect thank you!!

I have just put this code in and so far it is doing exactly what i need it too!

i've commented out the digityalwrite lines and uncommented the port manipulation part (keeping in the Delay function) and that works

ive now just got hold of an oscilloscope so i can test the NOP Delay...

So thanks very much Tad ... im trying to give you + karama but i thin kbecause im new here i cant :frowning:

Cheers!!