| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888 |
- ##############################################################################
- # $Id: 52_I2C_PCA9685.pm 13355 2017-02-07 22:16:01Z klausw $
- #
- ##############################################################################
- # Modul for I2C PWM Driver PCA9685
- #
- # define <name> I2C_PCA9685 <I2C-Adresse>
- # set <name> <port> <value>
- #
- # contributed by Klaus Wittstock (2015) email: klauswittstock bei gmail punkt com
- #
- ##############################################################################
- #Inhalte des Hashes:
- #i2caddress 00-127(7F) I2C-Adresse
- #direction i2cread|i2cwrite Richtung
- #reg 00-255|"" Registeradresse (kann weggelassen werden fuer IC's ohne Registeradressierung)
- #nbyte Zahl Anzahl Register, die bearbeitet werden sollen (im mom 0-99)
- #data 00-255 ... Daten die an I2C geschickt werden sollen (muessen, wenn nbyte benutzt wird immer ein Vielfaches Desselben sein)
- #received 00-255 ... Daten die vom I2C empfangen wurden, durch Leerzeichen getrennt (bleibt leer wenn Daten geschrieben werden)
- #pname_SENDSTAT Ok|error zeigt uebertragungserfolg an
- package main;
- use strict;
- use warnings;
- use SetExtensions;
- #use POSIX;
- my $setdim = ":slider,0,1,4095 ";
- my %setsP = (
- 'off' => 0,
- 'on' => 1,
- );
- my %confregs = (
- 0 => 'modereg1',
- 1 => 'modereg2',
- 2 => 'SUBADR1',
- 3 => 'SUBADR2',
- 4 => 'SUBADR3',
- 5 => 'ALLCALLADR',
- );
- my %defaultreg = (
- 'modereg1' => 32, #32-> Bit 5 -> Autoincrement
- 'modereg2' => 0,
- 'SUBADR1' => 113,
- 'SUBADR2' => 114,
- 'SUBADR3' => 116,
- 'ALLCALLADR' => 112,
- 'PRESCALE' => 30,
- );
- my %mr1 = (
- 'EXTCLK' => 64,
- 'SLEEP' => 16,
- 'SUBADR1' => 8,
- 'SUBADR2' => 4,
- 'SUBADR3' => 2,
- 'ALLCALLADR' => 1,
- );
- my %mr2 = (
- 'INVRT' => 16,
- 'OCH' => 8,
- 'OUTDRV'=> 4,
- 'OUTNE1'=> 2,
- 'OUTNE0'=> 1,
- );
- #############################################################################
- sub I2C_PCA9685_Initialize($) { #
- my ($hash) = @_;
- $hash->{DefFn} = "I2C_PCA9685_Define";
- $hash->{InitFn} = 'I2C_PCA9685_Init';
- $hash->{UndefFn} = "I2C_PCA9685_Undefine";
- $hash->{AttrFn} = "I2C_PCA9685_Attr";
- $hash->{StateFn} = "I2C_PCA9685_State";
- $hash->{SetFn} = "I2C_PCA9685_Set";
- #$hash->{GetFn} = "I2C_PCA9685_Get";
- $hash->{I2CRecFn} = "I2C_PCA9685_I2CRec";
- $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ".
- "prescale:slider,0,1,255 OnStartup ".
- "SUBADR1 SUBADR2 SUBADR3 ALLCALLADR ".
- "modereg1:multiple-strict,EXTCLK,SUBADR1,SUBADR2,SUBADR3,ALLCALLADR ".
- "modereg2:multiple-strict,INVRT,OCH,OUTDRV,OUTNE0,OUTNE1 ".
- "$readingFnAttributes dummy:0,1 extClock";
- }
- #############################################################################
- sub I2C_PCA9685_Define($$) {
- my ($hash, $def) = @_;
- my @a = split("[ \t]+", $def);
- $hash->{STATE} = 'defined';
- if ($main::init_done) {
- eval { I2C_PCA9685_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
- return I2C_PCA9685_Catch($@) if $@;
- }
- return undef;
- }
- #############################################################################
- sub I2C_PCA9685_Init($$) { # wird ausgefuehrt bei Initialisierung und Connect/Reconnect des DEVio
- my ( $hash, $args ) = @_;
- #my @a = split("[ \t]+", $args);
- my $name = $hash->{NAME};
- if (defined $args && int(@$args) < 1) {
- return "Define: Wrong syntax. Usage:\n" .
- "define <name> I2C_PCA9685 <i2caddress>";
- }
- if (defined (my $address = shift @$args)) {
- $hash->{I2C_Address} = $address =~ /^0x.*$/ ? oct($address) : $address;
- return "$name: I2C Address not valid" unless ($hash->{I2C_Address} < 128 && $hash->{I2C_Address} > 3);
- } else {
- return "$name: no I2C Address defined" unless defined($hash->{I2C_Address});
- }
- if (defined (my $maxbuff = shift @$args)) {
- return "$name: I2C buffer size must be a number" if $maxbuff =~ m/^d+$/;
- $hash->{I2C_Buff} = $maxbuff;
- }
- my $msg = '';
- eval {
- Log3 $hash, 4, "$hash->{NAME}: Init1 Konfigurationsregister auslesen";
- AssignIoPort($hash);
- #Config Register lesen (einzeln, da Blockweises lesen noch aktiviert werden muss)
- I2C_PCA9685_i2cread($hash, 0, 1); # Modereg1
- I2C_PCA9685_i2cread($hash, 1, 1); # Modereg2
- I2C_PCA9685_i2cread($hash, 2, 1); # Subadr1
- I2C_PCA9685_i2cread($hash, 3, 1); # Subadr2
- I2C_PCA9685_i2cread($hash, 4, 1); # Subadr3
- I2C_PCA9685_i2cread($hash, 5, 1); # Allcalladr
- I2C_PCA9685_i2cread($hash, 254, 1); # Frequenz fuer Internal
- $hash->{STATE} = 'Initializing';
- InternalTimer(gettimeofday() + 10, 'I2C_PCA9685_InitError', $hash, 0); # nach 10s Initialisierungsfehler ablegen
- };
- return I2C_BME280_Catch($@) if $@;
- }
- #############################################################################
- sub I2C_PCA9685_Init2($) { # wird audgefuehrt wenn Frequenzregisterinhalt empfangen wird und entsprechendes Internal noch leer ist
- my ( $hash ) = @_;
- my $name = $hash->{NAME};
- eval {
- Log3 $hash, 4, "$hash->{NAME}: Init2 Konfigurationsregister beschreiben";
- RemoveInternalTimer($hash); # Timer fuer Initialisierungsfehler stoppen
- # Mode register wiederherstellen
- I2C_PCA9685_Attr(undef, $name, "modereg1", AttrVal($name, "modereg1", undef));
- I2C_PCA9685_Attr(undef, $name, "modereg2", AttrVal($name, "modereg2", undef));
- # alternative I2C Adressen wiederherstellen
- I2C_PCA9685_Attr(undef, $name, "SUBADR1", AttrVal($name, "SUBADR1", undef));
- I2C_PCA9685_Attr(undef, $name, "SUBADR2", AttrVal($name, "SUBADR2", undef));
- I2C_PCA9685_Attr(undef, $name, "SUBADR3", AttrVal($name, "SUBADR3", undef));
- I2C_PCA9685_Attr(undef, $name, "ALLCALLADR", AttrVal($name, "ALLCALLADR", undef));
- # PWM Frequenz wiederherstellen
- I2C_PCA9685_Attr(undef, $name, "prescale", AttrVal($name, "prescale", undef));
- #Portzustände wiederherstellen
- foreach (0..15) {
- my $port = "Port".sprintf ('%02d', $_);
- I2C_PCA9685_Set($hash, $name, $port, ReadingsVal($name,$port ,0) );
- }
- $hash->{STATE} = 'Initialized';
- };
- return I2C_BME280_Catch($@) if $@;
- }
- #############################################################################
- sub I2C_PCA9685_InitError($) { # wird audgefuehrt wenn 10s nach Init immer noch keine Registerwerte empfangen wurden
- my ( $hash ) = @_;
- $hash->{STATE} = 'Error during Initialisation';
- }
- #############################################################################
- sub I2C_PCA9685_Catch($) { # Fehlermeldung von eval formattieren
- my $exception = shift;
- if ($exception) {
- $exception =~ /^(.*)( at.*FHEM.*)$/;
- return $1;
- }
- return undef;
- }
- #############################################################################
- sub I2C_PCA9685_State($$$$) { # reload readings at FHEM start
- my ($hash, $tim, $sname, $sval) = @_;
- Log3 $hash, 5, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim";
- if ($sname =~ m/^Port(((0|)[0-9])|(1[0-5]))$/i) {
- substr($sname,0,4,"");
- $sname = sprintf('%d', $sname);
- my %onstart = split /[,=]/, AttrVal($hash->{NAME}, "OnStartup", "");
- if ( exists($onstart{$sname}) && ( exists($setsP{$onstart{$sname}}) || ($onstart{$sname} =~ m/^\d+$/ && $onstart{$sname} < 4095) ) ) {
- Log3 $hash, 5, "$hash->{NAME}: Port" . sprintf('%02d', $sname) . " soll auf $onstart{$sname} gesetzt werden";
- readingsSingleUpdate($hash,"Port". sprintf('%02d', $sname), $onstart{$sname}, 1);
- } else {
- Log3 $hash, 4, "$hash->{NAME}: Port" . sprintf('%02d', $sname) . " soll auf Altzustand: $sval gesetzt werden";
- $hash->{READINGS}{'Port'. sprintf('%02d', $sname)}{VAL} = $sval;
- $hash->{READINGS}{'Port'. sprintf('%02d', $sname)}{TIME} = $tim;
- }
- }
- return undef;
- }
- #############################################################################
- sub I2C_PCA9685_Undefine($$) { # wird beim loeschen des Device ausgefuehrt
- my ($hash, $arg) = @_;
- my ($msg, $data, $reg) = I2C_PCA9685_CalcRegs($hash, 61, 'off', undef); # Registerinhalte berechnen alle Ports aus
- $msg = I2C_PCA9685_i2cwrite($hash,$reg, $data) unless($msg); # Rausschicken
- RemoveInternalTimer($hash);
- return undef
- }
- #############################################################################
- sub I2C_PCA9685_Attr(@) { # wird beim setzen eines Attributes ausgefuehrt
- my ($command, $name, $attr, $val) = @_;
- my $hash = $defs{$name};
- my $msg = '';
- if ($command && $command eq "set" && $attr && $attr eq "IODev") {
- if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
- main::AssignIoPort($hash,$val);
- my @def = split (' ',$hash->{DEF});
- I2C_PCA9685_Init($hash,\@def) if (defined ($hash->{IODev}));
- }
- } elsif ($attr && $attr =~ m/^prescale$/i) { # Frequenz
- $val = $defaultreg{'PRESCALE'} unless (defined($val)); #beim loeschen wieder auf Standard setzen
- return "wrong value: $val for \"set $name $attr\" use 0-255"
- unless($val =~ m/^(\d+)$/ && $val >= 0 && $val < 256);
- Log3 $hash, 5, $hash->{NAME} . ": $attr alter Wert: ".(defined($hash->{confregs}{PRESCALE})?$hash->{confregs}{PRESCALE}:"empty")." neuer Wert: ".$val;
- if ($main::init_done && $val != $hash->{confregs}{PRESCALE}) {
- my $modereg1 = defined $hash->{confregs}{$confregs{0}} ? $hash->{confregs}{$confregs{0}} : $defaultreg{'modereg1'};
- my $modereg1mod = ( $modereg1 & 0x7F ) | $mr1{ "SLEEP" };
- $msg = I2C_PCA9685_i2cwrite($hash, 0, $modereg1mod); #sleep Mode aktivieren
- $msg = I2C_PCA9685_i2cwrite($hash, 254 ,$val); #Frequenz aktualisieren
- $msg = I2C_PCA9685_i2cwrite($hash, 0 ,$modereg1); #sleep Mode wieder aus
- foreach (0..15) { #Portzustände wiederherstellen
- my $port = "Port".sprintf ('%02d', $_);
- I2C_PCA9685_Set($hash, $name, $port, ReadingsVal($name,$port ,0) );
- }
- }
- } elsif ($attr && $attr =~ m/^(SUBADR[1-3])|ALLCALLADR$/i) { # weitere I2C Adressen
- $val = $defaultreg{$attr} unless defined($val);
- substr($attr,0,6,"");
- my $regaddr = ($attr =~ m/^l/i) ? 5 : $attr + 1;
- my $SUBADR = $val =~ /^0x.*$/ ? oct($val) : $val;
- return "I2C Address not valid" if $SUBADR > 127;
- Log3 $hash, 5, $hash->{NAME} . ": $confregs{$regaddr} alter Wert: ".$hash->{confregs}{$confregs{$regaddr}}." neuer Wert: ".($SUBADR << 1);
- $msg = I2C_PCA9685_i2cwrite($hash, $regaddr ,$SUBADR << 1) if $main::init_done && ($SUBADR << 1) != $hash->{confregs}{$confregs{$regaddr}};
- } elsif ($attr && $attr =~ m/^modereg1$/i) { # Mode register 1
- my @inp = split(/,/, $val) if defined($val);
- my $data = 32; # Auto increment soll immer gesetzt sein
- foreach (@inp) {
- return "wrong value: $_ for \"attr $name $attr\" use comma separated list of " . join(',', (sort { $mr1{ $a } <=> $mr1{ $b } } keys %setsP) )
- unless(exists($mr1{$_}));
- $data |= $mr1{$_};
- if ($main::init_done && $_ eq "EXTCLK" && ($hash->{confregs}{$confregs{0}} & $mr1{"EXTCLK"}) == 0) { #wenn externer Oszillator genutzt werden soll, zuerst den sleep mode aktivieren
- my $modereg1 = defined $hash->{confregs}{$confregs{0}} ? $hash->{confregs}{$confregs{0}} : $defaultreg{'modereg1'};
- my $modereg1mod = ( $modereg1 & 0x7F ) | $mr1{ "SLEEP" };
- Log3 $hash, 5, "$hash->{NAME}: sleep Mode aktivieren (Vorbereitung fuer EXTCLK)";
- $msg = I2C_PCA9685_i2cwrite($hash, 0 ,$modereg1mod); #sleep Mode aktivieren
- # $data += $mr1{"SLEEP"}; #???????? muss hier nicht deaktiviert werden?????
- }
- }
- if ($main::init_done && defined $hash->{confregs}{$confregs{0}} && ($hash->{confregs}{$confregs{0}} & $mr1{"EXTCLK"}) == $mr1{"EXTCLK"} && ($data & $mr1{"EXTCLK"}) == 0 ) { #reset wenn EXTCLK abgeschaltet wird
- $msg = I2C_PCA9685_i2cwrite($hash, 0 , $data | 0x80);
- }
- Log3 $hash, 5, $hash->{NAME} . ": $attr alter Wert: ".$hash->{confregs}{$confregs{0}}." neuer Wert: ".$data;
- if ( $main::init_done && $data != $hash->{confregs}{$confregs{0}} ) {
- I2C_PCA9685_UpdReadings($hash, 0, $data); #schonmal in den Internals ablegen lassen (damit wärend Initialisierung mit korrekten daten gearbeitet wird... bei Frequenz z.B.)
- $msg = I2C_PCA9685_i2cwrite($hash, 0 , $data);
- }
- } elsif ($attr && $attr =~ m/^modereg2$/i) { #Mode register 2
- my @inp = split(/,/, $val) if defined($val);
- my $data = 0;
- foreach (@inp) {
- return "wrong value: $_ for \"attr $name $attr\" use comma separated list of " . join(',', (sort { $mr2{ $a } <=> $mr2{ $b } } keys %setsP) )
- unless(exists($mr2{$_}));
- $data += $mr2{$_};
- }
- Log3 $hash, 5, $hash->{NAME} . ": $attr alter Wert: ".(defined($hash->{confregs}{$confregs{1}})?$hash->{confregs}{$confregs{1}}:"")." neuer Wert: ".$data;
- $msg = I2C_PCA9685_i2cwrite($hash, 1, $data) if $main::init_done && $data != $hash->{confregs}{$confregs{1}};
- } elsif ($attr && $attr eq "OnStartup") {
- if (defined $val) {
- foreach (split (/,/,$val)) {
- my @pair = split (/=/,$_);
- $msg = "wrong value: $_ for \"attr $hash->{NAME} $attr\" use comma separated <port>=on|off|0..4095|last where <port> = 0 - 15 "
- unless ( scalar(@pair) == 2 &&
- (($pair[0] =~ m/(^[0-9]|1[0-5])$/i &&
- ( $pair[1] eq "last" || exists($setsP{$pair[1]}) ||
- ( $pair[1] =~ m/^\d+$/ && $pair[1] < 4095 ) ) ) )
- );
- }
- }
- } elsif ($attr && $attr eq "extClock") {
- $val = defined($val) ? $val : 25;
- return "wrong value: $val for \"set $name $attr\" use point number"
- unless($val =~ m/^[1-9][0-9]*\.?[0-9]*$/);
- I2C_PCA9685_i2cread($hash, 254, 1); # Frequenz fuer Internal neu auslesen und berechnen
- }
- return ($msg) ? $msg : undef;
- }
- #############################################################################
- sub I2C_PCA9685_Set($@) { #
- my ($hash, $name, @rest) = @_;
-
- my $dimstep = AttrVal($name, "dimstep", "1");
- my $dimcount = AttrVal($name, "dimcount", "4095");
- my $msg;
- my $str = join(' ',@rest);
- #Log3 undef, 5, "$name: empfangen: $str";
- if ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))/i && index($str, ',') == -1) { # Nur ein Port
- my ($port, $dim, $delay) = split(' ', $str);
- $port =~ tr/(P|p)(ort|)//d;
- #Log3 undef, 5, "$name: ein Port: $port, $dim, $delay";
- ($msg, my $data, my $reg) = I2C_PCA9685_CalcRegs($hash, $port, $dim, $delay); # Registerinhalte berechnen
- $msg = I2C_PCA9685_i2cwrite($hash,$reg, $data) unless($msg); # Rausschicken
- # } elsif ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))( *, *(P(ort|)((0|)[0-9]|1[0-5]))){1,} +\d+( +\d*)?/i ) { # Format P[ort]x,P[ort]y[,P..] Dimwert[ Delay]
- } elsif ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))( *, *(P(ort|)((0|)[0-9]|1[0-5]))){1,}/i ) { # Format P[ort]x,P[ort]y[,P..] Dimwert[ Delay]
- Log3 undef, 5, "mehrere ports und ein wert";
- $str =~ tr/(P|p)(ort|)//d;
- my @einzel = split(',', $str);
- my @port;
- my (undef, $dim, $delay) = split(' ', $einzel[$#einzel]);
- for my $i (0..$#einzel){
- ($port[$i]) = split(' ', $einzel[$i]);
- }
- my ($data, $reg) = undef;
- my $j = 1;
- for my $i (0..$#einzel){
- ($msg, my $tdata, my $treg) = I2C_PCA9685_CalcRegs($hash, $port[$i], $dim, ( defined($delay) ? $delay : undef ) ); # Registerinhalte berechnen
- return $msg if defined($msg);
- Log3 $hash, 5, "$name: Port: $port[$i], Reg: $treg, Inhalt: $tdata, Rohwerte: $einzel[$i], Dimwert: $dim, Delay: ". ( defined($delay) ? ( $delay = "" ? "leer" : $delay ) : "leer" );
- if ( defined($data) && defined($reg) ) { # bereits Werte für Ports vorhanden
- $j += 1;
- $data .= " " . $tdata;
- } else {
- $data = $tdata;
- $reg = $treg;
- }
- unless ( $j < (int( (defined($hash->{I2C_Buff})?$hash->{I2C_Buff}:30) / 4)) && $i < $#einzel && ($port[$i] + 1) == $port[$i+1]){ #wenn der naechste Port nicht der direkt Nachfolgende ist oder mehr als 8 Ports (32Bytes)
- $msg = I2C_PCA9685_i2cwrite($hash,$reg, $data); # Rausschicken
- ($data, $reg) = undef;
- $j = 1;
- }
- }
- } elsif ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))( ){1,3}\d*(( ){1,3}\d*)?(( ){0,3},( ){0,3}(P(ort|)((0|)[0-9]|1[0-5]))( ){1,3}\d*(( ){1,3}\d*)?){1,}( ){0,3}$/i ) { # Mehrere Ports auf versch. Werte setzen
- Log3 undef, 5, "mehrere ports und unterschiedliche Werte";
- $str =~ tr/(P|p)(ort|)//d;
- my @einzel = split(',', $str);
- my (@port, @dim, @delay);
- #@einzel = sort { $a <=> $b } @einzel;
- for my $i (0..$#einzel){
- ($port[$i], $dim[$i], $delay[$i]) = split(' ', $einzel[$i]);
- }
- my ($data, $reg) = undef;
- my $j = 1;
- for my $i (0..$#einzel){
- ($msg, my $tdata, my $treg) = I2C_PCA9685_CalcRegs($hash, $port[$i], $dim[$i], ( defined($delay[$i]) ? $delay[$i] : undef ) ); # Registerinhalte berechnen
- return $msg if defined($msg);
- Log3 $hash, 5, "$name: Port: $port[$i], Reg: $treg, Inhalt: $tdata, Rohwerte: $einzel[$i], Dimwert: $dim[$i], Delay: ". ( defined($delay[$i]) ? ( $delay[$i] =~ m/ */ ? "leer" : $delay[$i] ) : "leer" );
- if ( defined($data) && defined($reg) ) { # bereits Werte für Ports vorhanden
- $j += 1;
- $data .= " " . $tdata;
- } else {
- $data = $tdata;
- $reg = $treg;
- }
- unless ( $j < int( (defined($hash->{I2C_Buff})?$hash->{I2C_Buff}:30) / 4) && $i < $#einzel && ($port[$i] + 1) == $port[$i+1]){ #wenn der naechste Port nicht der direkt Nachfolgende ist oder mehr als 8 Ports (32Bytes)
- $msg = I2C_PCA9685_i2cwrite($hash,$reg, $data); # Rausschicken
- ($data, $reg) = undef;
- $j = 1;
-
- }
- }
- } elsif ($str =~ m/(a(ll|)( ){0,3}((\d{1,4})|on|off)(( ){0,3}\d{1,4})?)( ){0,3}$/i) { # Alle Ports gleichzeitig
- my ($port, $dim, $delay) = split(' ', $str);
- $port = 61; # Portnummer auf 61 für All setzen (All Startreg ist 250)
- Log3 undef, 5, "$name: alle Ports: $port, $dim" . (defined $delay ? ", $delay" : "" );
- my ($msg, $data, $reg) = I2C_PCA9685_CalcRegs($hash, $port, $dim, $delay); # Registerinhalte berechnen
- $msg = I2C_PCA9685_i2cwrite($hash,$reg, $data) unless($msg); # Rausschicken
- } else {
- my $list = undef;
- foreach (0..15) {
- $list .= "Port" . sprintf ('%02d', $_) . ":slider,0,$dimstep,$dimcount ";
- }
- $list .= "all:slider,0,$dimstep,$dimcount";
- $msg = "Unknown argument $str, choose one of " . $list;
- }
- return (defined($msg) ? $msg : undef);
- }
- #############################################################################
- sub I2C_PCA9685_CalcRegs($$$$) { # Registerinhalte berechnen
- my ($hash, $port, $dim, $del) = @_;
- my $name = $hash->{NAME};
- #$port =~ tr/P(ort|)//d; #Nummer aus Port extrahieren
- my $dimcount = AttrVal($hash->{NAME}, "dimcount", "4095");
- my $data;
- my $msg = undef;
- if (defined($dim) && $dim eq "on") {
- $data = "0 16 0 0";
- } elsif (defined($dim) && $dim eq "off") {
- $data = "0 0 0 16";
- } elsif (defined($dim) && $dim =~ m/^\d+$/ && $dim >= 0 && $dim <= $dimcount) {
- my $delaytime = 0;
- if ($dimcount < 4095) { #DimmWert anpassen bei anderem Faktor
- $dim = int($dim * 4095 / $dimcount);
- }
- if (defined $del) { #Delaytime angegeben?
- $msg = "wrong delay value: \"$del\" for \"$name Port$port $dim\" use value between 0 and $dimcount"
- unless ($del =~ m/^\d+$/ && $del >= 0 && $del <= $dimcount);
- if ($dimcount < 4095) { #DelayWert anpassen bei anderem Faktor
- $del = int($del * 4095 / $dimcount);
- }
- $delaytime = $del
- } else { #...wenn nicht aus Reading holen (für all kommt immer 0 raus)
- $delaytime = ReadingsVal($name,'Port_d'.sprintf ('%02d', $port),"0");
- }
- unless($msg) { # nur berechnen wenn es keine Fehlermeldung gibt
- my $LEDx_OFF = $delaytime + $dim - (( $dim + $delaytime < 4096 ) ? 0 : 4096);
- if ($LEDx_OFF == $delaytime) { #beide Register dürfen nicht gleichen Inhalt haben, das entpricht "aus"
- $data = "0 0 0 16";
- } else {
- my @LEDx = unpack("C*", pack("S", $delaytime));
- push @LEDx, unpack("C*", pack("S", $LEDx_OFF)); #Array $LEDx[0] = LEDx_ON_L, $LEDx[1] = LEDx_ON_H, $LEDx[2] = LEDx_OFF_L, $LEDx[3] = LEDx_OFF_H
- # $data = sprintf "%01d " x 4, @LEDx;
- $data = sprintf "%01d %01d %01d %01d", @LEDx;
- }
- }
- } else {
- $msg = "wrong dimvalue: \"".(defined($dim)?$dim:"...")."\" for \"$name Port$port\" use one of: " .
- join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " 0..$dimcount";
- }
- my $reg = 6 + 4 * $port if defined $port; # Nummer des entspechenden LEDx_ON_L Registers (LED0_ON_L = 0x06) jede LED hat 4 Register
- return $msg, $data, $reg;
- }
- #############################################################################
- sub I2C_PCA9685_Get($@) { # Portwerte bei laden der Datailseite aktualisieren
- my ($hash, @a) = @_;
- unless ($hash->{IODev}->{TYPE} eq 'RPII2C') { #fuer FRM, etc. Register zurücklesen (bei RPII2C kommt bei erfolgreicher Uebertragung die Botschaft zurueck)
- my $reg = int( (defined($hash->{I2C_Buff})?$hash->{I2C_Buff}:30) / 4) * 4; # Anzahl moegliche 4er Registergruppen pro Lesevorgang
- my $n = int(64 / $reg); # Anzahl Lesevorgänge (abgerundet)
- foreach (0 .. ($n-1)) {
- I2C_PCA9685_i2cread($hash, 6 + $_ * $reg, $reg);
- }
- I2C_PCA9685_i2cread($hash, 6 + $n * $reg, $reg - ($reg * ($n+1) - 64)) if (($n+1) * $reg) > 64;
- } else {
- I2C_PCA9685_i2cread($hash, 0x6, 64);
- }
- return;
- }
- #############################################################################
- sub I2C_PCA9685_i2cread($$$) { # Lesebefehl an Hardware absetzen (antwort kommt in I2C_*****_I2CRec an)
- my ($hash, $reg, $nbyte) = @_;
- if (defined (my $iodev = $hash->{IODev})) {
- Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} read $nbyte Byte from Register $reg";
- CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
- direction => "i2cread",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- nbyte => $nbyte
- });
- } else {
- if (AttrVal($hash->{NAME}, "dummy", 0) == 1) {
- Log3 $hash, 1, "attr dummy -> kann nix lesen";
- } else {
- return "no IODev assigned to '$hash->{NAME}'";
- } }
- }
- #############################################################################
- sub I2C_PCA9685_i2cwrite($$$) { # Schreibbefehl an Hardware absetzen
- my ($hash, $reg, $data) = @_;
- if (defined (my $iodev = $hash->{IODev})) {
- Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} write " . $data . " to Register $reg";
- CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
- direction => "i2cwrite",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- data => $data,
- });
- unless ($hash->{IODev}->{TYPE} eq 'RPII2C') { #fuer FRM, etc. Register zurücklesen (bei RPII2C kommt bei erfolgreicher Uebertragung die Botschaft zurueck)
- my $nbyte = () = $data =~ / /gi;
- unless ($reg == 250) {
- I2C_PCA9685_i2cread($hash, $reg, $nbyte + 1);
- } else {
- #I2C_PCA9685_UpdReadings($hash, $reg, $data);
- my $reg = int( (defined($hash->{I2C_Buff})?$hash->{I2C_Buff}:30) / 4) * 4; # Anzahl moegliche 4er Registergruppen pro Lesevorgang
- my $n = int(64 / $reg); # Anzahl Lesevorgänge (abgerundet)
- foreach (0 .. ($n-1)) {
- I2C_PCA9685_i2cread($hash, 6 + $_ * $reg, $reg);
- }
- I2C_PCA9685_i2cread($hash, 6 + $n * $reg, $reg - ($reg * ($n+1) - 64)) if (($n+1) * $reg) > 64;
- }
- }
- return undef;
- } else {
- if (AttrVal($hash->{NAME}, "dummy", 0) == 1) {
- I2C_PCA9685_UpdReadings($hash, $reg, $data); # Zeile zum testen (Werte werden direkt zu I2CRec umgeleitet)
- } else {
- return "no IODev assigned to '$hash->{NAME}'";
- }
- }
- }
- #############################################################################
- sub I2C_PCA9685_I2CRec($@) { # vom IODev aufgerufen
- my ($hash, $clientmsg) = @_;
- my $name = $hash->{NAME};
- my $phash = $hash->{IODev};
- my $pname = $phash->{NAME};
- while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen
- $hash->{$k} = $v if $k =~ /^$pname/ ;
- }
- if ($clientmsg->{direction} && defined($clientmsg->{reg}) && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok") {
- if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {
- I2C_PCA9685_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{received});
- readingsSingleUpdate($hash,"state", "Ok", 1);
- } elsif ( $clientmsg->{direction} eq "i2cwrite" && defined($clientmsg->{data}) ) { #readings aktualisieren wenn uebertragung ok (bei FRM kommt nix zurueck)
- I2C_PCA9685_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{data});
- readingsSingleUpdate($hash,"state", "Ok", 1);
-
- } else {
- readingsSingleUpdate($hash,"state", "transmission error", 1);
- Log3 $hash, 3, "$name: failure in message from $pname";
- Log3 $hash, 3, (defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
- (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
- (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
- (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
- (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
- }
- } else {
- readingsSingleUpdate($hash,"state", "transmission error", 1);
- Log3 $hash, 3, "$name: failure in message from $pname";
- Log3 $hash, 3, (defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
- (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
- (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
- (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
- (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
- }
- }
- #############################################################################
- sub I2C_PCA9685_CalcVal($@) { # Readings aus Registerwerten berechnen
- my ($dimcount, @reginh) = @_;
- my $delay = undef;
- my $dimval;
- if ($reginh[1] > 15) {
- $dimval = "on";
- } elsif ($reginh[3] > 15) {
- $dimval = "off";
- } else {
- $delay = $reginh[1] * 256 + $reginh[0];
- my $temp = $reginh[3] * 256 + $reginh[2];
- $dimval = $temp - $delay + (( $temp > $delay ) ? 0 : 4096);
- if ($dimcount < 4095) { #Wert anpassen bei anderem Faktor
- $dimval = int($dimval * $dimcount / 4095);
- $delay = int($delay * $dimcount / 4095);
- }
- }
- return $dimval, $delay;
- }
- #############################################################################
- sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Readings/Internals schreiben
- my ($hash, $reg, $inh) = @_;
- my $name = $hash->{NAME};
- Log3 $hash, 5, "$name Received from Register $reg: $inh"; #sprintf("0x%.2X", $reg)
- my @reginh = split(" ", $inh);
- my $dimstep = AttrVal($name, "dimstep", "1");
- my $dimcount = AttrVal($name, "dimcount", "4095");
- my $delay = undef;
- my $dimval;
- readingsBeginUpdate($hash);
- if ($reg == 250 && @reginh == 4) { # wenn All
- ($dimval, $delay) = I2C_PCA9685_CalcVal($dimcount, @reginh);
- foreach (0..15) {
- readingsBulkUpdate($hash, 'Port'.sprintf('%02d', $_) , $dimval) if (ReadingsVal($name, 'Port'.sprintf('%02d', $_), "failure") ne $dimval); #nur wenn Wert geaendert
- readingsBulkUpdate($hash, 'Port_d'.sprintf('%02d', $_) , $delay) if (defined $delay && ReadingsVal($name, 'Port_d'.$hash->{confregs}, "failure") ne $delay); #nur wenn Wert geaendert
- }
- } elsif ( $reg < 70 && $reg > 5 && @reginh == 4) { #Wenn PortRegister
- my $port = sprintf ('%02d', ($reg - 6) / 4);
- ($dimval, $delay) = I2C_PCA9685_CalcVal($dimcount, @reginh);
- readingsBulkUpdate($hash, 'Port'.$port , $dimval) if (ReadingsVal($name, 'Port'.$port, "failure") ne $dimval); #nur wenn Wert geaendert
- readingsBulkUpdate($hash, 'Port_d'.$port , $delay) if (defined $delay && ReadingsVal($name, 'Port_d'.$port, "failure") ne $delay); #nur wenn Wert geaendert
- Log3 $hash, 5, "$name: lese einen Port - Reg: $reg, Inh: @reginh";
-
- } elsif ( $reg < 70 && $reg > 5 && @reginh > 4 ) { #Wenn mehrere Ports abgefragt werden
- for (my $i = 0; $i < @reginh; $i++) {
- next unless ( ($reg + $i - 2) / 4 =~ m/^\d+$/ && defined($reginh[$i + 3]) );
- my @regpart = ( $reginh[$i], $reginh[$i + 1], $reginh[$i + 2], $reginh[$i + 3] );
- my $port = sprintf ('%02d', ($reg + $i - 6) / 4);
- ($dimval, $delay) = I2C_PCA9685_CalcVal($dimcount, @regpart);
- readingsBulkUpdate($hash, 'Port'.$port , $dimval) if (ReadingsVal($name, 'Port'.$port, "failure") ne $dimval); #nur wenn Wert geaendert
- readingsBulkUpdate($hash, 'Port_d'.$port , $delay) if (defined $delay && ReadingsVal($name, 'Port_d'.$port, "failure") ne $delay); #nur wenn Wert geaendert
- Log3 $hash, 5, "$name: lese mehrere Ports - Reg: $reg, i: $i; Inh: @regpart";
- $i += 3;
- }
- } elsif ($reg == 254) { #wenn Frequenz Register
- my $clock = AttrVal($name, "extClock", 25);
- my $init = 1 unless defined($hash->{Frequency});
- $hash->{confregs}{PRESCALE} = $inh;
- $hash->{Frequency} = sprintf( "%.1f", $clock * 1000000 / (4096 * ($inh + 1)) ) . " Hz";
- I2C_PCA9685_Init2($hash) if defined($init);
- } elsif ( $reg >= 0 && $reg < 6 ) { #Konfigurations Register
- $hash->{confregs}{$confregs{$reg}} = $inh;
- }
- readingsEndUpdate($hash, 1);
- return;
- }
- #############################################################################
- 1;
- =pod
- =item device
- =item summary controls PWM outputs from an via I2C connected PCA9685
- =item summary_DE steuern der PWM Ausgänge eines über I2C angeschlossenen PCA9685
- =begin html
- <a name="I2C_PCA9685"></a>
- <h3>I2C_PCA9685</h3>
- (en | <a href="commandref_DE.html#I2C_PCA9685">de</a>)
- <ul>
- <a name="I2C_PCA9685"></a>
- Provides an interface to the PCA9685 I2C 16 channel PWM IC.
- The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
- or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
- <b>attribute IODev must be set</b><br>
- <a name="I2C_PCA9685Define"></a><br>
- <b>Define</b>
- <ul>
- <code>define <name> I2C_PCA9685 <I2C Address> [<I2C Buffer Size>]</code><br>
- where <code><I2C Address></code> can be written as decimal value or 0xnn<br>
- <code><I2C Buffer Size></code> sets the maximum size of the I2C-Packet.
- Without this option the packet size is 30 Bytes (32 incl. Address and Register number).
- For RPII2C this option has no influence, cause it can deal with arbitrary packet sizes.<br>
- </ul>
- <a name="I2C_PCA9685Set"></a>
- <b>Set</b>
- <ul>
- <code>set <name> <port> <dimvalue> [<delay>]</code><br><br>
- <li>where <code><port></code> is one of Port0 to Port15<br>
- and <code><dimvalue></code> one of<br>
- <ul>
- <code>
- off<br>
- on<br>
- 0..4095<br>
- </code>
- </ul>
- <code><delay></code> defines the switch on time inside the PWM counting loop. It does not have an influence to the duty cycle.
- Default value is 0 and, possible values are 0..4095<br>
- </li><br>
- <li>
- It is also possible to change more than one port at the same time. Just separate them by comma.
- If only the last of the comma separated ports has dimvalue (and delay), all ports will set to the same values.
- Sequently ports will set at once (useful for multi color LED's).<br>
- Also P instead of Port is Possible.
- </li><br>
-
- <br>
- Examples:
- <ul>
- <code>set mod1 Port04 543</code><br>
- <code>set mod1 Port4 434 765</code><br>
- <code>set mod1 Port1, Port14 434 765</code><br>
- <code>set mod1 Port1 on, P14 434 765</code><br>
- </ul><br>
- </ul>
- <a name="I2C_PCA9685Get"></a>
- <b>Get</b>
- <ul>
- <code>get <name></code>
- <br><br>
- refreshes all readings
- </ul><br>
- <a name="I2C_PCA9685Attr"></a>
- <b>Attributes</b>
- <ul>
- <li>SUBADR1,SUBADR2,SUBADR3,ALLCALLADR<br>
- Alternative slave addresses, if you want to control more than one PCA9685 with one define
- Respective flag in modereg1 must be set as well<br>
- Default: SUBADR1=113,SUBADR2=114,SUBADR3=116,ALLCALLADR=112, valid values: valid I2C Address <br><br>
- </li>
- <li>OnStartup<br>
- Comma separated list of output ports/PWM registers and their desired state after start<br>
- Without this atribut all output ports will set to last state<br>
- Default: -, valid values: <port>=on|off|0..4095|last where <port> = 0 - 15<br><br>
- </li>
- <li>prescale<br>
- Sets PWM Frequency. The Formula is: Fx = 25MHz/(4096 * (prescale + 1)).
- The corresponding frequency value is shown under internals.
- If provided, attribute extClock will be used for frequency calculation. Otherwise 25MHz<br>
- Default: 30 (200Hz for 25MHz clock), valid values: 0-255<br><br>
- </li>
- <li>modereg1<br>
- Comma separated list of:
- <ul>
- <li>EXTCLK<br>
- If set the an external connected clock will be used instead of the internal 25MHz oscillator.
- Use the attribute extClock to provide the external oscillater value.
- </li>
- <li>SUBADR1<br>
- If set the PCA9685 responds to I2C-bus SUBADR 1.
- </li>
- <li>SUBADR2<br>
- If set the PCA9685 responds to I2C-bus SUBADR 2.
- </li>
- <li>SUBADR3<br>
- If set the PCA9685 responds to I2C-bus SUBADR 3.
- </li>
- <li>ALLCALLADR<br>
- If set the PCA9685 responds to I2C-bus ALLCALLADR address.
- </li>
- </ul>
- </li>
- <li>modereg2<br>
- Comma separated list of:
- <ul>
- <li>INVRT<br>
- If set the Output logic state is inverted.<br>
- </li>
- <li>OCH<br>
- If set the outputs changes on ACK (after every byte sent).<br>
- Otherwise the output changes on STOP command (bus write action finished)<br>
- </li>
- <li>OUTDRV<br>
- If set the outputs are configured with a totem pole structure.<br>
- Otherwise the outputs are configured with open-drain.<br>
- </li>
- Behaviour when OE = 1 (if OE = 0 the output will act according OUTDRV configuration):
- <li>OUTNE0<br>
- If set:<br>
- LEDn = 1 when OUTDRV = 1<br>
- LEDn = high-impedance when OUTDRV = 0<br>
- If not set:
- LEDn = 0.<br>
- </li>
- <li>OUTNE1<br>
- LEDn = high-impedance.<br>
- OUTNE1 overrides OUTNE0<br><br>
- </li>
- </ul>
- </li>
- <li><a href="#IODev">IODev</a></li>
- <li><a href="#ignore">ignore</a></li>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#showtime">showtime</a></li>
- </ul>
- <br>
- </ul>
- =end html
- =begin html_DE
- <a name="I2C_PCA9685"></a>
- <h3>I2C_PCA9685</h3>
- (<a href="commandref.html#I2C_PCA9685">en</a> | de)
- <ul>
- <a name="I2C_PCA9685"></a>
- Ermöglicht die Verwendung eines PCA9685 I2C 16 Kanal PWM IC.
- I2C-Botschaften werden über ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
- oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
- <b>Das Attribut IODev muss definiert sein.</b><br>
- <a name="I2C_PCA9685Define"></a><br>
- <b>Define</b>
- <ul>
- <code>define <name> I2C_PCA9685 <I2C Address> [<I2C Buffer Size>]</code><br>
- Der Wert <code><I2C Address></code> ist ein zweistelliger Hex-Wert im Format 0xnn oder eine Dezimalzahl<br>
- <code><I2C Buffer Size></code> gibt die maximale Anzahl von Datenbytes pro I2C Datenpaket an. Nicht angegeben, wird der Wert 30 verwendet
- ( entspricht 32 Bytes incl. Adresse und Registernummer). RPII2C kann mit beliebig großen Paketlängen umgehen, daher ist diese Option dort inaktiv.<br>
- </ul>
- <a name="I2C_PCA9685Set"></a>
- <b>Set</b>
- <ul>
- <code>set <name> <port> <dimvalue> [<delay>]</code><br><br>
- <li>Als <code><port></code> kann Port00 bis Port15 verwendet werden<br>
- <code><dimvalue></code> kann folgende Werte annehmen:<br>
- <ul>
- <code>
- off<br>
- on<br>
- 0..4095<br>
- </code>
- </ul>
- <code><delay></code> gibt den Wert innerhalb der Zählschleife an, an dem der Ausgang eingeschaltet wird.
- Damit lassen sich die 16 Ausgänge zu unterschiedlichen Zeiten einschalten um Stromspitzen zu minimieren.
- Dieser Wert hat keinerlei Einfluss auf die Pulsbreite. Stardartwert ist 0, mögliche Werte sind 0..4095<br>
- </li>
- <li>
- Um mehrer Ports mit einem Befehl zu ändern können mehrere Befehle per Komma getrennt eingegeben werden.
- Dabei kann jeder Port auf einen separaten, oder alle Ports auf den selben Wert gesettz werden.
- Fär letzteres darf nur der letzte Befehl dimvalue (und delay) enthalten.
- Aufeinanerfolgene Ports werden mit einem Befehl geschrieben. So können beispielsweise multicolor LED's ohne flackern geschaltet werden.<br>
- Anstelle von Port kann auch einfach ein P verwendet werden.
- </li>
- <br>
- Examples:
- <ul>
- <code>set mod1 Port04 543</code><br>
- <code>set mod1 Port4 434 765</code><br>
- <code>set mod1 Port1, Port2, Port14 434 765</code><br>
- <code>set mod1 Port1 on, P14 434 765</code><br>
- </ul><br>
- </ul>
- <a name="I2C_PCA9685Get"></a>
- <b>Get</b>
- <ul>
- <code>get <name></code>
- <br><br>
- Aktualisierung aller Werte
- </ul><br>
- <a name="I2C_PCA9685Attr"></a>
- <b>Attribute</b>
- <ul>
- <li>SUBADR1,SUBADR2,SUBADR3,ALLCALLADR<br>
- Alternative slave Adressen, zum kontrollieren mehrerer PCA9685 mit einem define
- Zusätzlich zu diesen Registern müssen die Passenden Bits in modereg1 gesetzt werden.<br>
- Standard: SUBADR1=113,SUBADR2=114,SUBADR3=116,ALLCALLADR=112, gültige Werte: I2C Adresse <br><br>
- </li>
- <li>OnStartup<br>
- Kommagetrennte Liste der Ports mit den gewünschten Startwerten.<br>
- Nicht gelistete Ports werden auf en letzte state wiederhergestellt.<br>
- Standard: last, gültige Werte: <port>=on|off|0..4095|last wobei <port> = 0 - 15<br><br>
- </li>
- <li>prescale<br>
- PWM Frequenz setzen. Formel: Fx = 25MHz/(4096 * (prescale + 1)).
- Die eingestellte Frequenz wird in den Internals angezeigt.
- Wenn das Attribut extclock angegeben ist, wird dieses zur Frequenzberechnung verwendet. Andernfalls 25MHz.<br>
- Standard: 30 (200Hz für 25MHz clock), gültige Werte: 0-255<br><br>
- </li>
- <li>modereg1<br>
- Durch Komma getrennte Liste von:
- <ul>
- <li>EXTCLK<br>
- Anstelle des internen 25MHz Oszillators wird ein extern Angeschlossener verwendet.
- Die Frequenz des externen Oszillators kann über das Attribut extclock angegeben werden.
- </li>
- <li>SUBADR1<br>
- Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 1.
- </li>
- <li>SUBADR2<br>
- Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 2.
- </li>
- <li>SUBADR3<br>
- Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 3.
- </li>
- <li>ALLCALLADR<br>
- Wenn gesetzt, antwortet der PCA9685 auf I2C-bus ALLCALLADR Adresse.
- </li>
- </ul>
- </li>
- <li>modereg2<br>
- Durch Komma getrennte Liste von:
- <ul>
- <li>INVRT<br>
- Wenn gesetzt, werden die Ausgänge invertiert.<br>
- </li>
- <li>OCH<br>
- Wenn gesetzt, werden die Ports nach jedem ACK gesetzt (also nach jedem gesendeten Byte).<br>
- Andernfalls werden sie nach einem STOP Kommando gesetzt (Bus Schreibaktion fertig, also nach einem Datenpaket)<br>
- </li>
- <li>OUTDRV<br>
- Wenn gesetzt, werden die Ausgänge als totem pole konfiguriert.<br>
- Andernfalls sind sie open-drain.<br>
- </li>
- Verhalten bei OE = 1 (wenn OE = 0 verhalten sich die Ausgänge wie in OUTDRV eingestellt):
- <li>OUTNE0<br>
- Wenn gesetzt:<br>
- LEDn = 1 wenn OUTDRV = 1<br>
- LEDn = hochohmig wenn OUTDRV = 0<br>
- Wenn nicht gesetzt:
- LEDn = 0.<br>
- </li>
- <li>OUTNE1<br>
- LEDn = hochohmig.<br>
- Wenn OUTNE1 gesetzt wird OUTNE0 ignoriert.<br><br>
- </li>
- </ul>
- </li>
- <li><a href="#IODev">IODev</a></li>
- <li><a href="#ignore">ignore</a></li>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#showtime">showtime</a></li>
- </ul>
- <br>
- </ul>
- =end html_DE
- =cut
|