Go Down

Topic: How quickly I can change pin states on Arduino? (Read 1 time) previous topic - next topic

JarkkoL

Hi,

I'm thinking of implementing a project which would require quite a lot of data to be transferred to shift registers and I'm thinking what the limitations in performance I could expect from Arduino to plan the project properly. Instead of using digitalWrite() that sets the state of pins individually my understanding is that I can write the state directly to PORTA/B/C/D global variables, which sets the state of 8 pins in each port at once. If I understood correctly, memory write on ATMega takes 2 cycles, so at 16MHz that would mean I can change the state of 8 pins at 8MHz, is that right?

Thanks, Jarkko

Grumpy_Mike

Well you can write at that rate but you have to do something else like fetch the data and that will slow you down.
For transferring data to a shift register look at using the SPI interface, I think that will run at 1MHz

Graynomad

Quote
change the state of 8 pins at 8MHz, is that right?

Only if the writes are inline, if you have a loop you have to add the JMP time.

Also you can use the IN and OUT instructions I would assume, they work in 1 cycle.

But you are getting data from somewhere and having to shift the bits etc, that will slow things down again.

Depending on the nature of the transfers you can organise the data in RAM such that all the clocking and data information is decoded into an array, then blat that array as fast as possible to a port.

Is there any reason for not using the SPI hardware?

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

CrossRoads

SPI is quickest - 8 MHz clock, can send out a byte about every 1 uS if you do it right.
This test spat out 41 bytes in 46uS I think, 58uS for the loop, pulling data from an array to send out.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Nick Gammon


I'm thinking of implementing a project which would require quite a lot of data to be transferred to shift registers


Read this post about clocking out pixel data to a VGA monitor:

http://www.gammon.com.au/forum/?id=11608

I don't think you can get much faster than that. Using SPI (a shift register, effectively) it clocks out one bit every two clock cycles (ie. every 125 nS), however there is a one clock cycle gap between bytes.

In the 17 clock cycles that the hardware is clocking out that byte (8 bits) you just have time to load up the next byte from the array in memory where you have them waiting.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

westfw

Quote
there is a one clock cycle gap between bytes.

I've heard that you can eliminate this by using "the usart in SPI mode", on those devices that have the more broadly "universal" USART.
(I think that includes the 328.  It's definitely getting attention on the Xmega chips, which have many usarts AND dma; people over on avrfreaks are doing "interesting stuff."

Nick Gammon

Interesting point. I don't believe my testing confirmed that, but I'm willing to try again. It was like (strangely) that the hardware needed one extra clock cycle to "recover" from sending that byte.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

DMA would be another kettle of fish, of course. I don't recall seeing reference to it on the Atmega chip lines.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

PeterH


require quite a lot of data to be transferred to shift registers


How much data are you talking about here? Where is it coming from?

My limited experience with shift registers is as serial in / parallel out devices i.e. one shift register per pin. Are you writing to multiple shift registers simultaneously?
I only provide help via the forum - please do not contact me for private consultancy.

CrossRoads

Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Graynomad

Rob Gray aka the GRAYnomad www.robgray.com

westfw

Back to the original subject.
Quote
I can change the state of 8 pins at 8MHz

Theoretically.  Note that the ATmega328 on the Uno only has one IO port (PORTD) with a full 8 pins, and those pins include the serial rx/tx pins.
If you want to maintain serial connectivity as well, you end up with several possibilities for writing 6 pins at 8MHz...

CrossRoads

I suppose if you had:

label A;
PORTD = (PORTD & B00000011) | B10101011;
PORTD = (PORTD & B00000011) | B01010111;
GOTO A; // or however labeling works

you could flip all but the Rx/Tx bits pretty quick.
Hard to do much else tho. Anything like pulling the data from an array would slow it down:
//          clears all but Rx/Tx         lets bits 7:2 be set
PORTD = (PORTD & B00000011) | (dataArray[0] & B11111100); // set the 6 bits per whats in the array
PORTD = (PORTD & B00000011) | (dataArray[1] & B11111100); // set the 6 bits per whats in the array
PORTD = (PORTD & B00000011) | (dataArray[2] & B11111100); // set the 6 bits per whats in the array
PORTD = (PORTD & B00000011) | (dataArray[3] & B11111100); // set the 6 bits per whats in the array
PORTD = (PORTD & B00000011) | (dataArray[4] & B11111100); // set the 6 bits per whats in the array
PORTD = (PORTD & B00000011) | (dataArray[5] & B11111100); // set the 6 bits per whats in the array
PORTD = (PORTD & B00000011) | (dataArray[6] & B11111100); // set the 6 bits per whats in the array
PORTD = (PORTD & B00000011) | (dataArray[7] & B11111100); // set the 6 bits per whats in the array

Don't do this in a for loop, that adds 12uS with each jump to the next for value.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Nick Gammon


Theoretically.  Note that the ATmega328 on the Uno only has one IO port (PORTD) with a full 8 pins, and those pins include the serial rx/tx pins.
If you want to maintain serial connectivity as well, you end up with several possibilities for writing 6 pins at 8MHz...


In any case I don't see what this is to do with the shift registers the OP mentioned.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

JarkkoL

Thanks for the replies and sorry the delay!


Is there any reason for not using the SPI hardware?


I didn't know about SPI, and thanks for pointing it out. If I got it right, there's single SPI on Uno, and that's able to push data at 8Mbits/s. I believe transfer() call to SPI is asynchronous so that data loads & looping can be done in parallel on the CPU while SPI feeds the data to the pins?


How much data are you talking about here? Where is it coming from? My limited experience with shift registers is as serial in / parallel out devices i.e. one shift register per pin. Are you writing to multiple shift registers simultaneously?

I'm looking to control ~300 RGB LED's, where each RGB channel takes 8-bit PWM data. This is rotating LED cylinder with radius of ~15cm and I'm looking for ~5mm resolution, so the cylindrical "display" size is 28200 pixels. I would like to have refresh rate of 20fps, so that would require 28200*20*3*8 bits/sec = ~13Mbits/sec data pushed from the main board. Of course the specs are adjustable to whatever is feasible to implement, but higher the better. While I would like to push the data directly to shift registers, I think I need additional microcontrollers in between, because the cylinder probably need to rotate faster than the actual update rate to maintain steady image (e.g. 100fps).


Theoretically.  Note that the ATmega328 on the Uno only has one IO port (PORTD) with a full 8 pins, and those pins include the serial rx/tx pins.

I checked that it should take 8 cycles to update 8 bits of data from an array to the PORTD. If I unroll the loop few times you could shave off ~couple of cycles for higher transfer rate:
1) read data from memory to a register (2 cycles)
2) out the register to port D (1 cycle)
3) out 1 to cycle pin (1 cycle)
4) out 0 to cycle pin (1 cycle)
5) increment data address counter (1 cycle)
6) compare and loop (2 cycles)
So at 16MHz that should give 16Mbits/sec transfer speed.

Jarkko

Go Up