Delay of 0.25 microseconds

I have been working on an LED controller that uses a Return-to-zero protocol that requires me to send high and low signals at delays of 0.25 and 0.75 microseconds.

I noticed that the delayMicroseconds() only accepts whole numbers meaning, it won’t allow me to create 0.25us delays. Additionally, in the resources section of this site, it says:

"This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times."

Any help on how can achieve this?



Annotation 2019-02-21 105904.jpg

Assuming a 16MHz AVR (such as Uno, Nano, Pro Mini, etc):
You can use a timer's output compare to do this, but the big problem is... .25 microseconds is 4 clock cycles.

If you need to send a .25us HIGH and a .75us LOW, that you can do with a hardware timer's output compare, if you take over the timer.

If you need to do it repeatedly, you can do that.

If you need to do any sort of processing within that timeframe, that's not going to happen - 1 microsecond is 16 clock cycles - 16 instructions at best.

You could also achieve the same thing by disabling interrupts and using a bit of inline assembler - harder to write than the hardware timer method, and blocking, but you don't care if it's blocking in a case like this, and if you need more flexibility than you could get with the timer, this is the only way.
Edit: looked at the section of the manual you posted; pretty sure you need inline assembler for this.

The problem with very short delays with delay microseconds is that the time it takes to figure out how to generate the delay you want is not negligible compared to the length of the delay; even the overhead associated with a function call is non-negligible at that point. If you look at the source, it's full of comments about compensating for the time taken to do the math and comparisons it just did.

As an aside, digitalWrite() is WAY too slow for this, because of the overhead of converting the arduino pin number to the port and pin registers - direct port manipulation is a must.

Doing it with other, faster processors is somewhat easier, but still non-trivial (the delayMicroseconds() caveat is also based on 16MHz AVR; it's likely better with very short delays on faster boards, ex due, zero, and similar)

I will need to process things as its a 4*8 LED array controller IC that I'm trying to control. I will need to do a bit of processing to decide which LEDto turn on and which to turn off. Besides if the Input is LOW for 1us continuously, it will cause the IC to take that as a RESET signal (refer to the attachment in the initial post)

Edit: I will need to use the Arduino pro-Mini or similar due to size restrictions in this project.

If the DUE CORE (a arduino DUE compatible board) can fit your needs (54 mm x 58 mm), a 250ns delay would be easy to achieve because one clock cycle = 11.9 ns.

I will have to check on that with the project leader but in case it isn't possible is there any other to achieve this result with the pro mini?

No

With a 16Mhz clock giving 62ns instruction cycle what do you expect?

Following up on Dr Azzy's suggestion in reply #1, the avr assembly code for a no operation "nop" is very simple to use if you can tolerate a blocking delay. At 16MHz, there are 16 nops per microsecond. Four "nops" will give you the .25us and 12 the .75us.

You can call them up like this

__asm__ __volatile__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");  \\ gang them up like this

You use __asm__volatile so the compiler will not throw them away.

There are some very handy avr built in functions. This one calls multiple nops.

 __builtin_avr_delay_cycles ();

So for your two delays, you would call

 __builtin_avr_delay_cycles (4);
 __builtin_avr_delay_cycles (12);

jaainaveen:
Any help on how can achieve this?

Please post a link to the complete datasheet.

I wonder if the data in the table you posted are mandatory or just typical.

...R

ffe70b4f439147a92c41d6c50193c0366b43312f.jpg

that looks VERY similar to the waveform used by “NeoPixels”, so you can perhaps look at some of the code designed to drive them…

larryd:
No

With a 16Mhz clock giving 62ns instruction cycle what do you expect?

Why not? Admittedly, it’s not a lot of time, but you can squeeze it in there

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

Since I don’t have a SM1688B or an oscilloscope laying around, I only verified the timing in the simulator. 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.

The OP hasn’t said what else they want to do, but cannot imagine they only want to generate this waveform with the said pro mini.

DrAzzy:
If you need to do any sort of processing within that timeframe, that's not going to happen - 1 microsecond is 16 clock cycles - 16 instructions at best.

And many instructions, in particular jumps and calls, take multiple clocks to execute...
Regards,
Ray L.

larryd:
The OP hasn’t said what else they want to do, but cannot imagine they only want to generate this waveform with the said pro mini.

What would prevent the OP to do other things when he/she is not sending something to the led controller?

RayLivingston:
And many instructions, in particular jumps and calls, take multiple clocks to execute...
Regards,
Ray L.

Since it is a led controller I'm feeling like the processing can/should be done before sending the led commands to the controller. I might be wrong however, because I couldn't find an english version of the datasheet. When he/she needs to do other stuff during the communication there is no way to do it on a pro mini, because there is just not enough time.

that looks VERY similar to the waveform used by “NeoPixels”, so you can perhaps look at some of the code designed to drive them…

In particular, one way is apparently to “stretch” your bits to an appropriate length, and then output using SPI hardware (be careful to use SPI that doesn’t insert bits between bytes. I believe an AVR “USART in SPI mode” will do the right thing.)
In this case, a 0 bit can expand to 0b1000, and a 1 bit to 0b1110, each two bits of your message yielding one byte to be shifted out at 4Mbps…
See also: Inefficient NeoPixel Control Solved With Hardware Hackery | Hackaday (including the comments)
and some of it’s “reactions”, like: Besting Ben Heck (use a 33 cent microcontroller to drive NeoPixels instead of multivibrators) - Wayne's Tinkering Page

The OP really needs to step up.

larryd:
The OP really needs to step up.

Why? OP exhibits completely normal behaviour; Keep asking and rephrasing until supplied with working code and POOF, gone he is. Avoid this by supplying pseudo-code to newbies :slight_smile: