Forum: Mikrocontroller und Digitale Elektronik BASCOM: Byte Array an I2C RGB LED


von Alfred S. (hood)


Lesenswert?

Hallo ihr Byteschubser!

Per (BASCOM) I2C sollen Farbwerte an einen angebundenen Slave gesendet 
werden, an dem eine RGB LED mit Chip hängt.

Folgende -funktionierende- Lib läuft auf dem Slave:
https://github.com/usedbytes/neopixel_i2c

INFO:
1
'The slave address is currently hardcoded to 0x40 in the firmware
2
'
3
'Writes look like this:
4
'Start         Slave Address         Register Address         Data         Stop
5
'The register address will auto-increment after every byte, so you can write data in bursts.
6
'The LED values are only updated after a STOP is received.
7
'
8
'*** Register Map:
9
'The register map consists of a number of global control registers - address 0x00-0x03 - followed by an array of registers which hold the individual value for each LED in normal mode.
10
'
11
'Address         Name         Description             Access      Reset
12
'0x00            CTRL         Control Register        R/W         0
13
'0x01            GLB_G        Global Green Value      R/W         0
14
'0x02            GLB_R        Global Red Value        R/W         0
15
'0x03            GLB_B        Global Blue Value       R/W         0
16
'*** Register Descriptions:
17
'CTRL
18
'The control register sets the operating mode.
19
'Name:         RSVD         RSVD         RSVD         RSVD         RSVD         RSVD         GLB         RST
20
'Bit:           7            6            5            4            3            2            1           0
21
'Access:        r            r            r            r            r            r            rw          rw
22
'
23
'RST
24
'Writing a 1 to this bit will reset the LED controler, setting all LEDs to OFF. This bit will be automatically cleared once the reset has completed.
25
'
26
'GLB
27
'Writing a one to this bit causes the global color value to be displayed on all LEDs at the end of the transaction.
28
'Normally you would set the GLB_R, GLB_G, and GLB_B values in the same transaction as setting the GLB bit, so that the new colour is immediately applied.
29
'Writing a zero to this bit will disable the global colour override and return to normal operation.
30
'
31
'*** GLB_R, GLB_G, GLB_B:
32
'These registers hold the global colour value. When the GLB bit in the CTRL register is set, all LEDs will display this colour.
33
'
34
'*** LED Value Array:
35
'Everything after the global registers is an array of data for each LED. When the GLB bit is not set, each LED will display whatever value is programmed in its corresponding register set.
36
37
38
'Info aus der I2C Lib des Slaves:
39
'Here's an example of I2C Communication to start a SRF08 ranging in cm:
40
'i2c_start();              // send start sequence
41
'i2c_tx(0xE0);             // SRF08 I2C address with R/W bit clear
42
'i2c_tx(0x00);             // SRF08 command register address
43
'i2c_tx(0x51);             // command to start ranging in cm
44
'i2c_stop();               // send stop sequence


##############################################

BASCOM:
Die Prints kommen in der finales Version natürlich weg!
1
$regfile "m328pdef.dat"
2
$crystal = 8000000
3
4
$framesize = 32
5
$swstack = 32
6
$hwstack = 64
7
8
$baud = 4800
9
$lib "i2c_twi.lbx"
10
Config Twi = 100000                                         ' Init TWBR und TWSR - wanted clock frequency - 100kHz Standard , 400kHz Fast , 3.4MHz High speed
11
12
' TWI gleich einschalten, das macht Bascom ansonsten erst beim I2CStart:
13
Twcr = &B00000100                                           ' nur TWEN setzen
14
15
Const I2c_slave_adress_write = &H40                         ' Slave Address WRITE
16
Const I2c_slave_adress_read = &H41                          ' Slave Address + 1  READ
17
Const I2c_slave_ctrl_reg = &H00                             ' Control Register CTRL = 0x00
18
19
'******** hier bin ich nicht sicher, was besser wäre:
20
'Dim Colors_byte_array(3) As Byte
21
'Greenvalue Alias Colors_byte_array(_base) : Redvalue Alias Colors_byte_array(_base + 1) : Bluevalue Alias Colors_byte_array(_base + 2)
22
Dim Greenvalue As Byte , Redvalue As Byte , Bluevalue As Byte
23
'******** 
24
25
Config Sda = Portc.4
26
Config Scl = Portc.5
27
28
29
Do
30
31
    Greenvalue = 0 : Redvalue = 255 : Bluevalue = 255       ' define a color
32
33
    'Farbwerte besser in einer Tabelle bereitstellen? Performance?
34
    'Colors_byte_array = Lookup (4, Farbbytetabelle)                    ' hol den Wert aus der 4. Zeile in der Tabelle namens "Farbbytetabelle"
35
36
    Print Greenvalue ; " " ; Redvalue ; " " ; Bluevalue
37
    I2cstart                                                                      
38
        If Err = 0 Then Print "i2c start ok"           ' In der Systemvariablen err wird das Ack-Bit hinterlegt
39
40
    I2csend I2c_slave_adress_write                          ' Slave ansprechen   'I2CSEND combines the i2cstart,i2cwbyte and i2cstop statements.
41
       If Err = 0 Then Print "i2c slave adresse ok"
42
43
    I2cwbyte I2c_slave_ctrl_reg                             ' Slave Control Register auswählen
44
         If Err = 0 Then Print "i2c CTRL register ok"
45
46
    I2cwbyte &H01                                           ' GLB_G Register auswählen
47
    I2cwbyte Greenvalue                                     ' Farbwerte ins Register schreiben
48
         If Err = 0 Then Print "i2c farbbyte uebertragen"
49
50
    I2cwbyte &H02                                           ' GLB_R Register auswählen
51
    I2cwbyte Redvalue                                       ' Farbwerte ins Register schreiben
52
         If Err = 0 Then Print "i2c farbbyte uebertragen"
53
54
    I2cwbyte &H03                                           ' GLB_B Register auswählen
55
    I2cwbyte Bluevalue                                      ' Farbwerte ins Register schreiben
56
         If Err = 0 Then Print "i2c farbbyte uebertragen"
57
58
    I2cstop                                                 ' The LED values are only updated after a STOP is received
59
        If Err = 0 Then Print "i2c stop - LED values updated"
60
61
62
63
    Print Err                                               ' Err = 0 -> kein Fehler !  Mit ERR kann nach jedem gesendeten Byte abgefragt werden, ob es mit ACK quittiert wurde (=0), oder nicht (=1).
64
    Print
65
66
         Wait 5
67
68
' NÄCHSTE FARBE
69
70
Loop
71
72
End

Ich schaffe es einfach nicht das RGB Bytearray bzw. die Einzelwerte 
korrekt an den Slave zu schicken.

Muss ich nach dem Control-Register-Aufruf die 3 GLB_ Register einzeln 
aufrufen, gefolgt von Farbbytes oder schicke ich sofort 3 Farbbytes nach 
dem Aufruf des Control Registers?

Muss ich in das Control Register vor den Farbbytes irgendwas bestimmtes 
reinschreiben?

Wie könnte ich die 3 RGB Bytes "eleganter" übergeben, als alles einzelne 
Programmzeilen zu erstellen?

Interessant zu wissen ist folgendes:
"I2CSEND combines the i2cstart,i2cwbyte and i2cstop statements."
Wenn ich "I2c_slave_adress_write" mit I2c*wbyte* aufrufe, erhalte ich 
kein ACK und die restlichen I2C Kommandos werden übersprungen. Wenn ich 
es mit dem Kombinationsbefehl I2c*send* mache, dann bekomme ich das ACK 
und alle weiteren Prints werden ausgegeben. Vom Prinzip her müsste es 
aber mit I2c*wbyte* funktioneren.

Hat jemand eine Ahnung warum das nicht mit I2c*wbyte* funktioniert?

von Uwe D. (monkye)


Lesenswert?

Hallo Alfred,

wenn Du die aktuelle Bascom Version (ab 2.0.7.9) verwendest, dann musst 
Du eigentlich nur die so genannte Rainbow Library verwenden - und 
fertig. Die Hilfe (F1) hält dazu auch ganz ordentliche Beispiele parat.

Du sparst Zeit und hast schneller Erfolg...

monkye

von Alfred S. (hood)


Lesenswert?

Danke für den Hinweis Uwe. Ich möchte aber lieber flexibel bleiben und 
das Prozedere auch für andere Projekte einsetzen. Die Rainbow-lib kommt 
nicht in Frage ;)

von Alfred S. (hood)


Lesenswert?

Hello? Nur C und Assembler Fans hier? =)

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Fortran wird hier nicht helfen ;)
Wenn Du schon kein ACK bekommst - stimmt die Adresse des I2C-Slave?

MfG

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alfred S. schrieb:
> Hello? Nur C und Assembler Fans hier? =)

 Hat nichts mit der Sprache zu tun. Du must erst mal verstehen wie
 das Ganze funktioniert (auch I2C).

 Also, so wie ich das oben rausgelesen habe, funktioniert es
 folgendermassen:

 a) Alle LEDs selbe Farbe z.B. RED
1
     i2cstart
2
     i2cwbyte (SlaveAdress*2)    /* Weiss allerdings nicht, wie die Adressierung beim Bascom ist... */
3
     i2cwbyte 1                  /* Wenn es stimmt was du oben geschrieben hast, gehen alle LEDs aus */
4
     i2cstop                     /* Ende, alle LEDs sollten jetzt Aus sein */
5
     waitms 50                   /* Bisschen warten... */
6
     i2cstart
7
     i2cwbyte (SlaveAdress*2)    /* Weiss allerdings nicht, wie die Adressierung beim Bascom ist... */
8
     i2cwbyte 2                  /* Global Value (in deinem Fall RED) wird angezeigt */
9
     i2cwbyte 0                  /* GLB_G (GREEN) ist OFF */
10
     i2cwbyte 255                /* GLB_R (RED) ist ON */
11
     i2cwbyte 0                  /* GLB_B (BLUE) ist OFF */
12
     i2cstop                     /* Ende, alle LEDs sollten jetzt Rot leuchten */


 b) Die ersten 5 LEDs leuchten in verschiedenen Farben:
1
  Dim NrOfLeds, lWert As Byte
2
3
  i2cstart
4
  i2cwbyte (SlaveAdress*2)
5
  For NrOfLeds=1 To 4
6
     i2cwbyte 0                  /* CTRL + GLB_X sind OFF */
7
  Next
8
  Restore ledRGB
9
  For NrOfLeds=1 To 15
10
    Read lWert
11
    i2cwbyte lWert
12
  Next
13
  i2cstop                     /* Ende, alle LEDs sollten jetzt in verschiedenen Farben leuchten */
14
  End
15
16
ledRGB:
17
Data 255,0,0, 0,255,0, 0,0,255, 150,150,0, 0,150,150

 Sollte funktionieren - ausprobieren, Resultat posten...

von Alfred S. (hood)


Lesenswert?

Also:
1
i2cstart
2
i2cwbyte (Slaveadresse)
3
i2cwbyte 1
4
i2cstop

Das geht einfach nicht mit "wbyte" an der Slaveadresse. Musste es durch 
"send" ersetzen. Ich musste den Slave insgesamt nur 1x an seiner 
Slaveadresse ansprechen. Habe es hinbekommen! Allerdings besteht noch 
Klärungbedarf:

1
Const I2c_slave_hello = &H40                                ' Slave Address WRITE
2
Const I2c_slave_read = &H41                                 ' Slave Address + 1  READ
3
Const I2c_slave_ctrl_reg = &H00                             ' Control Register CTRL = 0x00
4
5
6
        I2csend I2c_slave_hello    ' Slave ansprechen   'I2CSEND combines the i2cstart,i2cwbyte and i2cstop statements.
7
8
Do
9
10
11
Print "es geht los:"
12
13
    Greenvalue = 0 : Redvalue = 255 : Bluevalue = 0         ' define a color - ROT
14
    Print Greenvalue ; " " ; Redvalue ; " " ; Bluevalue
15
16
    I2cstart
17
         'If Err = 0 Then Print "i2c start ok"
18
19
    I2cwbyte I2c_slave_ctrl_reg                             ' Slave Control Register auswählen
20
         'If Err = 0 Then Print "i2c CTRL register ok"
21
22
    'I2cwbyte &B00000000                                     ' GLB auf 0 und RST auf 0 = LEDs aus = 00000000 -> Dezimal "0"
23
    'I2cwbyte &B00000001                                     ' GLB auf 0 und RST auf 1 = LEDs ein (FALSCHE FARBEN) = 00000001 -> Dezimal "1"
24
    'I2cwbyte &B00000010                                     ' GLB auf 1 und RST auf 0 = LEDs ein (FALSCHE FARBEN) = 00000010 -> Dezimal "2"
25
    I2cwbyte &B00000011                                     ' GLB auf 1 und RST auf 1 = LEDs ein (RICHTIGE FARBEN) = 00000011 -> Dezimal "3"
26
         'If Err = 0 Then Print "CTRLbyte uebertragen"
27
   '*******
28
    I2cwbyte &H01
29
         If Err = 0 Then Print "was soll das hier - ohne gehts nicht"
30
   '*******
31
    I2cwbyte Greenvalue                                     ' GLB_G Register: Farbwert ins Register schreiben
32
         'If Err = 0 Then Print "i2c farbbyte uebertragen"
33
34
    I2cwbyte Redvalue                                       ' GLB_R Register: Farbwert ins Register schreiben
35
         'If Err = 0 Then Print "i2c farbbyte uebertragen"
36
37
    I2cwbyte Bluevalue                                      ' GLB_B Register: Farbwert ins Register schreiben
38
         'If Err = 0 Then Print "i2c farbbyte uebertragen"
39
40
    I2cstop                                                 ' The LED values are only updated after a STOP is received
41
         'If Err = 0 Then Print "i2c stop - LED values updated"
42
43
    Print Err                                               ' Err = 0 -> kein Fehler !  Mit ERR kann nach jedem gesendeten Byte abgefragt werden, ob es mit ACK quittiert wurde (=0), oder nicht (=1).
44
    Print
45
46
         Wait 2
47
48
    Greenvalue = 255 : Redvalue = 255 : Bluevalue = 0       ' define a color - GELB
49
    Print Greenvalue ; " " ; Redvalue ; " " ; Bluevalue
50
51
    I2cstart
52
    I2cwbyte I2c_slave_ctrl_reg
53
    I2cwbyte &B00000011
54
   '*******
55
    I2cwbyte &H01
56
         If Err = 0 Then Print "was soll das hier - ohne gehts nicht"
57
   '*******
58
    I2cwbyte Greenvalue
59
    I2cwbyte Redvalue
60
    I2cwbyte Bluevalue
61
    I2cstop
62
    Print Err
63
64
         Wait 2
65
66
'weitere Farben
67
68
Loop

Bei "was soll das hier - ohne gehts nicht" musste ich ein Byte 
übertragen, sonst wären die Farbwerte nicht übertragen worden. Es war 
dabei quasi egal welchen Wert es enthielt. Hätte auch &HFF / 255 sein 
können.
Läuft so seit Stunden mit 10 verschiedenen Farben durch und funktioniert 
wohl einwandfrei.
Frage: Warum musste ich ein Byte rüberschicken, wo der Wert quasi egal 
ist? Das CTRL Register hatte ich doch bereits ausgerufen und einen Wert 
reingeschrieben...

Auch komisch: bei der Übertragung von &B00000001 ins CTRL Register wurde 
das RESET Bit auf 1 gesetzt... und die LEDs blieben an (allerdings mit 
falscher Farbdarstellung). Gibt es dazu eine Erklärung? Habe 
verschiedene CTRL Registerwerte ausprobiert und die nicht 
funktionierenden auskommentiert.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alfred S. schrieb:
> Auch komisch: bei der Übertragung von &B00000001 ins CTRL Register wurde
> das RESET Bit auf 1 gesetzt... und die LEDs blieben an (allerdings mit
> falscher Farbdarstellung). Gibt es dazu eine Erklärung? Habe

 Ja.
 Das ist alles so falsch, dass du diesen armen LED-Treiber (was ist das
 überhaupt für ein Ding ?) wahrscheinlich total durcheinandergebracht
 hast.

 Also, ohne durch deinen (Horror) Program ganz durchzugehen:
1
        I2csend I2c_slave_hello    'I2CSEND combines the i2cstart,i2cwbyte and i2cstop statements.
 Wenn es stimmt, dann ist es damit auch schon zu Ende - Device muss
 erst wieder neu adressiert werden, damit es weitergeht...

1
    I2cstart
2
         'If Err = 0 Then Print "i2c start ok"
3
    I2cwbyte I2c_slave_ctrl_reg                             ' Slave Control Register auswählen
 Nein, nicht Control Register auswählen.
 Das, was du oben (zufällig) gemacht hast, nennt sich General Call,
 und nur deswegen geht es überhaupt weiter...

 Und von da an hatte ich keine Lust mehr.

 Entweder hörst du auf rumzuspielen, machst ganz einfach ein copy &
 paste mit von mir gepostetem Program und sagst ob es geht/nicht geht
 oder es wird nie etwas.

 Wenn es mit von dir oben gepostetem Program geht (wenn auch falsch),
 dann muss es mit meinem Program erst recht gehen .

 Das einzige, was bei mir ev. geändert werden müsste, ist die Adresse
 für Slave, also einfach 0x40 als Adresse reinschreiben, mit dem Rest
 nicht rumspielen, nicht ändern.
 So:
1
  i2cwbyte (0x40)

von Alfred S. (hood)


Lesenswert?

Ich habe dein Programm getestet. Es lief nicht durch. Von daher habe ich 
die Änderungen eingebaut.

Hier der Test mit deinem Programm:
1
Do
2
3
Print "es geht los:"
4
5
     i2cstart
6
         If Err = 0 Then Print "i2c start ok"
7
     I2cwbyte &H40                                          'Slaveadressierung
8
         If Err = 0 Then Print "i2c slave ok"
9
     I2cwbyte 1                                             '/* Wenn es stimmt was du oben geschrieben hast, gehen alle LEDs aus */
10
         If Err = 0 Then Print "i2c byte ok"
11
     I2cstop                                                '/* Ende, alle LEDs sollten jetzt Aus sein */
12
         If Err = 0 Then Print "i2c stop ok"
13
     Waitms 50                                              '/* Bisschen warten... */
14
     I2cstart
15
         If Err = 0 Then Print "i2c start ok"
16
     I2cwbyte &H40                                          'Slaveadressierung
17
         If Err = 0 Then Print "i2c slave ok"
18
     I2cwbyte 2                                             '/* Global Value (in deinem Fall RED) wird angezeigt */
19
         If Err = 0 Then Print "i2c global value  ok"
20
     I2cwbyte 0                                             '/* GLB_G (GREEN) ist OFF */
21
         If Err = 0 Then Print "i2c GLB_G ok"
22
     I2cwbyte 255                                           '/* GLB_R (RED) ist ON */
23
         If Err = 0 Then Print "i2c GLB_R ok"
24
     I2cwbyte 0                                             '/* GLB_B (BLUE) ist OFF */
25
         If Err = 0 Then Print "i2c GLB_B ok"
26
     I2cstop                                                '/* Ende, alle LEDs sollten jetzt Rot leuchten */
27
         If Err = 0 Then Print "i2c stop ok"
28
29
     Print Err
30
31
     Wait 2
32
33
'nächste Farbe
34
Loop

Ausgabe im Terminal:
1
es geht los:
2
i2c start ok
3
i2c start ok
4
1

Gemäß der Anleitung und gemäß deines Codes müsste es funktionieren wie 
von dir beschrieben. Tut es leider nicht.

Der Slave wird korrekt adressiert und da geht es nicht weiter. Hast du 
einen Verdacht was es sein könnte? Adresse ist korrekt!

von Alfred S. (hood)


Lesenswert?

Habe das hier mal Testweise probiert:
1
Scanbus:                                                    'Scan bus for valid I2C addresses on bus
2
' ( test for ACK to come back from Busaddress )
3
   Print
4
   Print "Scan start"
5
   For Busaddress = 0 To 254 Step 2                         'for all even addresses
6
     I2cstart                                               'send start
7
     I2cwbyte Busaddress                                    'send Busaddress
8
     If Err = 0 Then                                        'we got an ack
9
        Print "Slave at : " ; Busaddress ; "d,  =  " ; Hex(busaddress) ; "hex"
10
        Chipaddress = Busaddress \ 2
11
        Print " with chip address " ; Hex(chipaddress) ; "h"
12
        Print
13
        End If
14
     I2cstop                                                'free bus
15
   Next
16
   Print "End Scan"
17
Return

Ausgabe im Terminal:
1
Scan start
2
Slave at : 0d,  =  00hex
3
 with chip address 00h
4
5
Slave at : 128d,  =  80hex
6
 with chip address 40h
7
8
End Scan

Es gibt nur 1 Slave.
Hilft das weiter?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alfred S. schrieb:
> Der Slave wird korrekt adressiert und da geht es nicht weiter. Hast du
> einen Verdacht was es sein könnte? Adresse ist korrekt!

 Nein.
 Aber da du 2 Mal hintereinander "i2c start ok" kriegst und danach
 eine 1 (wahrscheinlich für Err), nehme ich an, dass dieses Ding
 die Adresse nicht bestätigt, also die Adresse falsch ist.

 Probiere es mal mit General Call Adresse 0x00, so:
1
     I2cwbyte &H0                                          'Slaveadressierung

 Wenn es durchgeht, dann ist das Ding in Ordnung, nur die Adresse
 stimmt eben nicht - danach kannst du die Adresse 0x80 probieren, wenn
 das aber nicht klappt, musst du die Adressen eine nach der anderen
 abklappern...

 Und zum zweiten Mal: Was ist das überhaupt für ein Ding ?

von Alfred S. (hood)


Lesenswert?

Mit der geänderten Adresse auf &H80 funktioniert dein Programm (es 
musste nur noch das CTRL Register eingefügt werden.)
Mit Dezimal 1 gehen die LEDs aus und mit 2 gehen die an.
Ich muss das CTRL Register und das CTRL Byte aber bei JEDEM Farbwechsel 
mitschicken, sonst ändert sich nichts.

Const I2c_slave_hello = &H80
Const I2c_slave_ctrl_reg = &H00

    I2cstart
    I2cwbyte I2c_slave_hello
    I2cwbyte I2c_slave_ctrl_reg
    I2cwbyte &B00000010    'Dezimal 2
    I2cwbyte Greenvalue
    I2cwbyte Redvalue
    I2cwbyte Bluevalue
    I2cstop

Was ist das überhaupt für ein Ding ? Siehe 1. Post:
https://github.com/usedbytes/neopixel_i2c

Das mit dem General Call funktioniert übrigens auch. Danke für den 
Hinweis, dass es sowas gibt!
Insgesamt mal wieder was dazu gelernt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.