Speed-Tuning für "shiftout" ?

Hallo,

bin gerade etwas erstaunt wie langsam dieses “shiftout”, also die klassische Ansteuerung von Schieberegistern wie z.B. 4094 oder 74xx595 eigentlich geht.

Also im Prinzip folgender Code (z.B. 74595)

    ...
    digitalWrite(latchPin, 0);
    shiftOut(dataPin, clockPin, data);   
    digitalWrite(latchPin, 1);
    ...

Das ist doch eigentlich eine Kleinigkeit für den Atmel und sollte in vielleicht 50 Takten abgearbeitet sein - weit gefehlt, Arduino bzw. der Compiler macht daraus wohl einen riesen Zinober, oder baut künstliche Wartepausen ein?

Kann man das irgendwie “tunen” ohne gleich zum (Inline-)Assembler zu greifen?

Christian

http://arduino.cc/en/pmwiki.php?n=Reference/ShiftOut: This is a software implementation; see also the SPI library, which provides a hardware implementation that is faster but works only on specific pins.

Über SPI sollte es also wesentlich schneller laufen.

Was da gemacht wird kannst du dir ansehen. In x:\Arduino\hardware\arduino\avr\cores\arduino\wiring_shift.c

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
	uint8_t i;

	for (i = 0; i < 8; i++)  {
		if (bitOrder == LSBFIRST)
			digitalWrite(dataPin, !!(val & (1 << i)));
		else	
			digitalWrite(dataPin, !!(val & (1 << (7 - i))));
			
		digitalWrite(clockPin, HIGH);
		digitalWrite(clockPin, LOW);		
	}
}

Ein digitalWrite() alleine braucht da 4-5µs. Schleifen kosten auch Zeit, wenn der Compiler kein loop unrolling macht.

Verwende einfach SPI und schon wird das größtenteils in Hardware erledigt

Pack deine Schieberegitser an die Hardware SPI Schnitstelle (MOSI Master Out Slave In) und dann mit dem

http://arduino.cc/en/Reference/SPI

SPI.transfer die bytes raus

Den Latch brauchst du aber immer noch und schon ist dies xfach schneller.

Oben noch den Include für die SPI Lib.

  digitalWrite(latchPin, 0);
  SPI.transfer(val); 
  digitalWrite(latchPin, 1);

Gruß DerDani

Wenn SPI Pins nicht machbar sind, könnte man das shiftOut so implementieren, dass Port und Bit für DataPin und ClockPin nur einmal bestimmt werden und man digitalWrite durch direkte Port-Änderung ersetzt. Sollte ca 8fache Geschwindigkeit ergeben. Nicht ganz SPI, aber erlaubt beliebige Pins.

Hallo,

hmm, wenn das “intern” auch alles mit den digitalWrites abläuft summiert es sich deutlich :slightly_frowning_face:

Trotzdem würde ich gerne alles herausholen was mit Software möglich ist. Also muß ich zuerst einmal das böse digitalWrite durch was effizienteres ersetzen, das scheint das Hauptübel zu sein…

Was ist z.B. von solchen Projekten zu halten (Webfund), klingt interessant…
http://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

Christian

Das ist soweit ich sehen kann schneller: https://code.google.com/p/digitalwritefast/

Die Pins müssen dabei zur Compile-Zeit feststehen. Also nicht per Parameter an die Funktion übergeben.

Kannst du denn nun die Hardware Schnittstelle überhaupt noch nutzen?

Versuch mal folgendes in den Sketch einzubauen.

*
* Created: 06.01.2015 20:57:03
*  Author: sschultewolter
*/


#ifndef SERPA_H_
#define SERPA_H_

#include <avr/io.h>

#define SERPA_DDR DDRA
#define SERPA_PORT PORTA
#define SERPA_DATA PORTA6
#define SERPA_LAT PORTA7 // frei waehlbar auf PORTA
#define SERPA_SCLK PORTA4

#define SERPA_MAX_BYTES 2

#define OFF 0
#define ON 1
#define TOGGLE 2

int8_t serpa_init(void);
int8_t serpa_shiftout(void);

void serpa_one_bit(uint8_t serpa_bit, uint8_t value);

void serpa_one_byte(uint8_t serpa_num_byte, uint8_t value);

void serpa_all_bytes(uint8_t value);

uint8_t spera_get_one_bit(uint8_t serpa_num_bit);

uint8_t serpa_get_one_byte(uint8_t serpa_num_byte);

#endif /* SERPA_H_ */
/*
* serpa.c
*
* Created: 06.01.2015 20:57:17
*  Author: sschultewolter
*/

#include "serpa.h"

uint8_t serpa_array[SERPA_MAX_BYTES];

int8_t serpa_init(void)
{
 SERPA_DDR |= (1<<SERPA_LAT) | (1<<SERPA_DATA) | (1<<SERPA_SCLK);
 SPCR |= (1<<SPE) | (1<<MSTR);
 SPSR |= (1<<SPI2X);
 return 1;
}

int8_t serpa_shiftout(void)
{
 SERPA_PORT &= ~(1<<SERPA_LAT);
 for(uint8_t serpa_num_byte = 0; serpa_num_byte < SERPA_MAX_BYTES; serpa_num_byte++)
 {
 SPDR = serpa_array[serpa_num_byte];
 while(!(SPSR & 1<<SPIF));
 }
 SERPA_PORT |= 1<<SERPA_LAT;
 return 1;
}

void serpa_one_bit(uint8_t serpa_bit, uint8_t value)
{
 uint8_t serpa_num_byte = serpa_bit/8;
 uint8_t serpa_bit1= serpa_bit%8;
 uint8_t t = serpa_array[serpa_num_byte];
 
 switch(value)
 {
 case 0: // off
 t &= ~(1<<serpa_bit1);
 break;
 
 case 1: // on
 t |= (1<<serpa_bit1);
 break;
 
 case 2: // toggle
 t ^= (1<<serpa_bit1);
 break;
 
 default:
 return;
 }
 serpa_one_byte(serpa_num_byte, t);
 }

void serpa_one_byte(uint8_t serpa_num_byte, uint8_t value)
{
 if(serpa_num_byte < SERPA_MAX_BYTES)
 {
 serpa_array[(SERPA_MAX_BYTES-1)-serpa_num_byte] = value;
 }
}

void serpa_all_bytes(uint8_t value)
{
 for(int8_t serpa_num_bytes = 0; serpa_num_bytes < SERPA_MAX_BYTES; serpa_num_bytes++)
 {
 serpa_array[(SERPA_MAX_BYTES-1)-serpa_num_bytes] = value;
 }
}

uint8_t spera_get_one_bit(uint8_t serpa_num_bit)
{
 uint8_t serpa_num_byte = serpa_num_bit/8;
 uint8_t serpa_bit = serpa_num_bit%8;
}

uint8_t serpa_get_one_byte(uint8_t serpa_num_byte)
{
 if(serpa_num_byte < SERPA_MAX_BYTES)
 {
 return serpa_array[(SERPA_MAX_BYTES-1)-serpa_num_byte];
 }
}