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

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
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: