0.25 -0.5 us delay repeated with 60 Hz frequency

I need to drive LED with 250- 500 us pulses repeated with 60 Hz frequency. There is a solution proposed on this forum how to generate 250 us pulse but I neither understand the logic no how can I incorporate it in the program.
Here is the code:

 Needed for sbi/cbi and SREG operations
.equ PORTB, 0x05
.equ SREG, 0x3F

// Set this to your output port
.equ OUT_PORT, PORTB
// Set this to your output pin number
.equ OUT_PIN, 5

.global sm1688b_send

/*******************************************************************************
	Name:		sm1688b_send
	
	C-Function:     void sm1688b_send(uint32_t)

	Purpose:	Sends out 32 bits of data in the timing the SM1688B expects
				That is, for each 0-bit, output 250ns high and 750ns low.
				For every 1-bit output 750ns high and 250ns low.
	
	Registers:	Register r18 is used to keep track of the number of bits that
				have to be send. It is decremented in the code and after each
				bit the content of r18 is compared to 0.

				Input data is expected to be in R25:22. The LSB of R22 is 
				outputted first.

 *******************************************************************************/
sm1688b_send:
	// Save the SREG
	in r18, SREG
	push r18

	// Load the bitcounter
	ldi r18, 32
  
	// load first bit into carry
	lsr r25
	ror r24
	ror r23
	ror r22

	// disable interrupts for timing
	cli

// send loop
send_loop:
	sbi OUT_PORT, OUT_PIN // 2 ticks (after 2 ticks, output goes high)
	brcc send_a_0
 
send_a_1:                 // 1 tick
	// signal needs to stay high for 12 ticks = 750ns. 1 tick already elapsed, so we have
	// 11 ticks, before we need to change the output. It takes 2 cycles to change the output
	// so we really only have 9 ticks to load new data
	lsr r25               // 1 tick
	ror r24               // 1 tick
	ror r23               // 1 tick
	ror r22               // 1 tick
	nop                   // 1 tick
	nop                   // 1 tick
	nop                   // 1 tick
	nop                   // 1 tick
	dec r18               // 1 tick
	// 10 ticks have elapsed, change signal to low
	cbi OUT_PORT, OUT_PIN
	// signal needs to stay low for 4 cycles, beginning from now on
	// in this 4 cycles we need to jump back to the beginning, if necessary
	// after jumping to the beginning it takes 2 cycles to turn the output high,
	// so we only have 2 cycles to get to the beginning
	brne send_loop
	// we are done
	rjmp send_loop_exit

send_a_0:               // 2 tick
	cbi OUT_PORT, OUT_PIN // 2 ticks (after 2 ticks, output goes low)
	// signal needs to stay low for 12 ticks = 750ns from now on
	// after jumping to the beginning, it takes 2 cycles for the signal to change
	// to high. So we have 12 - 2 = 10 cycles to load new data and complete the 
	// jump to the beginning 
	lsr r25               // 1 tick
	ror r24               // 1 tick
	ror r23               // 1 tick
	ror r22               // 1 tick
	nop                   // 1 tick
	nop                   // 1 tick
	nop                   // 1 tick
	dec r18               // 1 tick
	brne send_loop        // 2 ticks
        // we are done
        rjmp send_loop_exit

send_loop_exit:
        // output back to high
        sbi OUT_PORT, OUT_PIN
	// restore sreg - also sets interrupt flag if it was set
	pop r18
	out SREG, r18
	ret

Low pulse is 250ns and high pulse is 750ns.

Put the above code in a separate file named whatever.S (note capital S!). In your arduino code, add this to the top of your file:

extern "C" void sm1688b_send(uint32_t);

Now you should be able to call the “sm1688b_send” function.

Hello
I guess your sketch needs two timers at least.

Which Arduino are you using?
The standard Arduino's Timer1 is 16bit, and if when driven at 1MHz with a prescaler, it can count enough to repeat 60Hz.
It allows you to easily specify pulse width in 1us increments on two channels.

I'm using Nano and Uno and Attiny 85.

Nano can do a good work.
You will get the desired pulse from Nano D9 PIN with this code.

void setup() {
  pinMode(9, OUTPUT);
  TCCR1A = 0xC2;
  TCCR1B = 0x1A;
  cli();
  ICR1 = 0x8235;
  OCR1A = ICR1;
  sei();
}
void loop() {
  pulseWidth(250); // change pulse width to 250us
  delay(3000);
  pulseWidth(500); // change pulse width to 500us
  delay(3000);
  pulseWidth(0); // disable pulse output
  delay(3000);
}
void pulseWidth(unsigned int pw) {
  cli();
  pw = ICR1 - (pw << 1);
  OCR1A = pw > ICR1 ? 0 : pw;
  sei();
}

You can freely change the pulse width according to the format written in the loop().

EDIT:
Timer mode change for more accuracy as 60Hz.

Thank you very much! I will try to upload in the morning. Can I change delay to get higher frequency or it is limited by the processor frequency?

Yes it can.
However, if the needed max pulse length is 500us, the maximum frequency is 2kHz.
Because, If over 2kHz the 500us pulse length exceeds the period.
If I feel like it, I will post the code.

It adjustable between 20Hz and 2kHz.
The output pin has moved to D10.

void setup() {
  pinMode(10, OUTPUT);
  pulseFreq(60); // set frequency to 60Hz
}
void loop() {
  pulseFreq(60); // set frequency to 60Hz
  delay(2000);
  pulseWidth(250); // change pulse width to 250us
  delay(2000);
  pulseWidth(500); // change pulse width to 500us
  delay(2000);
  pulseFreq(1000); // set frequency to 1kHz
  delay(2000);
  pulseWidth(0); // disable pulse output
  delay(2000);
}
void pulseWidth(unsigned int pw) {
  cli();
  OCR1B = pw > OCR1A ? OCR1A : pw;
  sei();
}
void pulseFreq(unsigned int frq) {
  TCCR1A = 0x21;
  TCCR1B = 0x12;
  frq = constrain(frq, 20, 2000);
  frq = (F_CPU >> 4) / frq;
  cli();
  OCR1A = frq;
  sei();
}

It looks like I managed to fry both Nano's that I have or may be I've got an issue with the cable. I got Atmega A328 U. Would it work if I program it with UNO? The chip has the same spec I believe.

This is at odds with the topic title.

"0.25 -0.5 us delay repeated with 60 Hz frequency"

My bad, I meant 250-500 ns.

Good point.
I missed it.

Okay, my code is microsecond.
So it doesn't work and needs to be fixed.

I have another LED that is less powerful so it looks like it needs around 600 us delay. to give me required power output. As I don't have working Nano I tried UNO and used microsecond () command and it works fine.

As your pulses are short and rep rate low you can over-drive LEDs to get much more output.

Is it solved by a code I misunderstood that outputs a pulse in microseconds?
Since post the code that outputs the actual nanosecond pulse, might as well.

enum pulseWidth {
  ns250_0, // 250.0ns
  ns312_5, // 312.5ns
  ns375_0, // 375.0ns
  ns437_5, // 437.5ns
  ns500_0  // 500.0ns
};
void setup() {
  pinMode(8, OUTPUT); // Pulse output is D8
}
void loop() {
  pulseBegin(60, ns250_0);     //   60Hz,250ns
  delay(3000);
  pulseBegin(1000, ns500_0);   //   1kHz,500ns
  delay(3000);
  pulseBegin(100000, ns500_0); // 100kHz,500ns
  delay(3000);
  pulseBegin(2.5, ns312_5);    //  2.5Hz,312.5ns
  delay(3000);
  pulseEnd(); // Pulse output disable
  delay(3000);
}
void pulseBegin(float Hz, unsigned char pW) { // Support 1Hz to 400kHz
  if (Hz <= 0) pulseEnd();
  else if  (Hz > 400000) pulseEnd();
  else {
    byte x = 0x18;
    if (Hz > (F_CPU >> 16)) {
      x += 1; Hz = F_CPU / Hz;
    } else if (Hz > (F_CPU >> 19)) {
      x += 2; Hz = (F_CPU >> 3) / Hz;
    } else if (Hz > (F_CPU >> 22)) {
      x += 3; Hz = (F_CPU >> 6) / Hz;
    } else {
      x += 4; Hz = (F_CPU >> 8) / Hz;
    }
    GPIOR2 = pW;
    TCCR1A = 0x03;
    cli();
    TIFR1  = 0x02;
    TIMSK1 = 0x02;
    TCCR1B = x;
    OCR1A  = Hz - 1;
    sei();
  }
}
void pulseEnd(void) {
  TIMSK1 = 0;
  TCCR1A = 0;
  TCCR1B = 0;
  GPIOR2 = 0;
}
ISR(TIMER1_COMPA_vect, ISR_NAKED) {
  __asm__ __volatile__ ( // 29cy + reti
    "push r30 \n in r30,0x3f \n push r30 \n push r31 \n"
    "ldi r30,lo8(gs(p_end)) \n in r31,0x2b \n sub r30,r31 \n"
    "ldi r31,hi8(gs(p_end)) \n sbci r31,0 \n sbi 0x05,0 \n"
    "ijmp \n nop \n nop \n nop \n nop \n p_end: cbi 0x05,0 \n"
    "pop r31 \n pop r30 \n out 0x3f,r30 \n pop r30 \n reti");
}
#if F_CPU != 16000000UL
#error "UNSUPPORTED CLOCK! RUN 16MHz ONLY!"
#endif

Pulse output pin is D8.
The pulse width can be selected from 5 types, 250ns to 500ns in 62.5ns steps.
(Because Arduino running at 16MHz, so one cycle is 62.5ns.)
The frequency can be set from 1Hz to 400kHz. (Decimal point allowed)

1 Like

I know that in theory it is possible to overdrive LEDs but I could not get neither from Mouser nor from Newark what is the safe current limit. In the spcecs OEMs often publish max current without specifying the conditions. Are there any rule of thumbs something like as long as an average current does not exceed the spec the the pulse current is OK.

Baloney. Most of the LED spec sheets I have seen, do specify a pulse current. Typically, they will tell you the max current for a 10% duty cycle. Here is one:
https://cdn-shop.adafruit.com/datasheets/WP7113SRD-D.pdf

See "Peak Forward Current [1]" in "Absolute Maximum Ratings".
"Notes: 1. 1/10 Duty Cycle, 0.1ms Pulse Width."

You haven't given any details or even mentioned how the LED is driven from the Arduino. I really hope you have some drive circuit and not just connected it directly to an output pin.

Thank you! For now 250 ns is the shortest pules that I need but it is good to know that if I need I can do 62.5 and 125 ns. I’ve ordered some Nano’s from Amazon which I should get on Friday. Eventually I will migrate to a chip. Do you know what pin can I use on Atmega 328?

Any port on the ATmega328 can be used for output.
I specified the pins because I hard-coded the pin numbers in the ISR to output very short pulses.

Unless I miss something this one https://www.we-online.com/catalog/datasheet/15400585F3590.pdf does no specify conditions for 1A pulsed current. For sure I can't get any info from Chinese sellers. In many cases I need 30 and even 50% duty cycle.