Forum: Mikrocontroller und Digitale Elektronik Mit dsPIC33FJ128GP802 ein LCD Display über I²C ansteuern


von Nord L. (flap)


Lesenswert?

Moin,

vorweg: Ich bin fortgeschrittener Student, habe gute C-Kenntnisse und 
Arduinoerfahrung hoch und runter.
Nun möchte ich in dem Rahmen eines Moduls weg von dem Arduinospielkram 
mal "richtige" Mikrocontroller ausprobieren.
Ich nutze MPlab X IDE als Programmierumgebung.
Der Witz des Moduls ist, dass man keinerlei fremde Bibliotheken nutzt, 
sondern von Grund auf alles selbst programmiert, solang man kein 
Assembler nutzt, sondern in C bleibt.
Ich habe bereits Timer, Interrupts, IOs und PWM-Signale genutzt und mir 
eigene kleine Bibiotheken geschrieben. Eine Basis ist also da :)

Mein Problem bei der Programmierung mit dem PIC ist eigentlich nur der 
Mangel an Beispielen und Erklärungen. Ich habe ein Beispielcode 
gefunden, wie man den PIC30F (nicht ganz meine Version) als Master für 
I²C nutzt:
http://taur-tech.com/wp/2016/07/18/a-working-example-of-i2c-communication-using-a-microchip-dspic30f-as-master/
Ansonsten sah das relativ mau aus. Ich werde mich wohl mit dem Link und 
diesem "Handbuch" hier
http://ww1.microchip.com/downloads/en/DeviceDoc/70000195f.pdf
da entlang hangeln müssen.

So viel zum Thema, nun zu den Fragen:
Habt ihr andere Links zu dem Thema I²C für den PIC33? Wie macht man das, 
wenn man keine Anleitung findet?
Und wie finde ich heraus wie mein Datenwort, dass dann hoffentlich 
geschickt wird, codiert sein muss, damit ein LCD Display wie bspw. 
dieses hier
https://www.amazon.de/AZDelivery-HD44780-Display-Schnittstelle-Anzeigen/dp/B01N3B8JMN/ref=sr_1_20_sspa?ie=UTF8&qid=1511897562&sr=8-20-spons&keywords=tft+display+arduino&psc=1
damit etwas anfangen kann? Arduino Bibs aufdröseln und daraus schlau 
werden?
Wo bezieht man dafür die Info?

Beste Grüße
Flap

von Udo (Gast)


Lesenswert?

Findet man eigentlich alles in den
Datenblättern, vom Mikrocontroller und vom Display.

von Dumpf Backe (Gast)


Lesenswert?

Nord L. schrieb:
> vorweg: Ich bin fortgeschrittener Student, habe gute C-Kenntnisse und
> Arduinoerfahrung hoch und runter.

Aber weiss immer noch nicht dass "LCD Display"

"Liquid Crystal Display Display"

bedeuted.

Und der Rest steht doch tatsächlich alles in den Datenblättern.

von Hurra (Gast)


Angehängte Dateien:

Lesenswert?

Ich bin bekennender I2C-Hasse.
Drum habe ich das nur für PIC32 selber geschrieben, denn für PIC24 gibts 
das fertig. Die Unterschiede zu deinem PIC dürften sich in Grenzen 
halten.
Siehe Anhang.

Warnung: Mein Code das sehr simpel und blockierend. Es gibt z.B. keine 
Timeouts, was fatal sein kann, wenn sich der Slave nicht zu einem ACK 
herablässt.
Aber, das kannst du ja leicht verbessern.

Ich bin so vorgegangen:
Zunächst zerpflückt man die I2C Kommunikation in wiederverwendbare 
Einzelschritte, welche die I2C-Schnittstelle des PIC kann.
Das wird sein:
- startcondition setzen
- daten senden
- daten lesen
- stopconditon setzen

Zu jedem habe ich mir eine kleine Funktion geschrieben.
Eine Kommunikation klöppelt man sich aus diesen Einzelschritten 
zusammen.

Das kann dann Beispielsweise so aussehen:
1
bool MCP7940_read(uint8_t adress, uint8_t *data, uint8_t amount){
2
    //start 
3
    startcondition();
4
    //slave adress
5
    if(writeNAck(MCP7940_WRITE)) return false;
6
    //write address
7
    if(writeNAck(adress))  return false;
8
    //restart
9
    startcondition();
10
    //slave adress, read
11
    if(writeNAck(MCP7940_READ)) return false;
12
    //read, ack all bits, nack the last
13
    int i = 0;
14
    uint8_t *ptr = data;
15
    do{
16
        i++;
17
        *ptr = read();
18
        ptr++;
19
        if(i<amount) NACK(false);
20
        else{
21
            NACK(true);
22
        }
23
    }
24
    while(i<amount);
25
    //stop
26
    stopcondition();
27
    return true;
28
}

Wie du das nicht blockierend mit Interrupts machen kannst, musst du dir 
selber überlgen :-)

von Frager (Gast)


Lesenswert?

Kenne mich mit PIC nicht aus, aber gibt es da nicht etwas 
Hardware-mäßiges, was man über Register steuern kann? Stelle mir sowas 
wie TWI bei den Atmel-MC´s vor. Gibt es sowas bei PIC nicht?
Wenn nicht, dann wäre das ja mal ein Vorschlag für Mikrochip!

von Hurra (Gast)


Lesenswert?

Frager schrieb:
> Kenne mich mit PIC nicht aus, aber gibt es da nicht etwas
> Hardware-mäßiges, was man über Register steuern kann? Stelle mir sowas
> wie TWI bei den Atmel-MC´s vor. Gibt es sowas bei PIC nicht?
> Wenn nicht, dann wäre das ja mal ein Vorschlag für Mikrochip!

Klar gibts das. Bei den PICs heißt das, tada, I2C. Mein Code verwendet 
genau das.

Ist ein sehr gängiges Feature.

Was dem TE nicht klar ist, ist dass das bei den PICs über fast alle PICs 
sehr ähnlich funktioniert. D.h. es ist nicht nötig, ein spezifisches 
Beispiel für PIC33F herauszusuchen. Oft sind nur minimale Anpassungen 
nötig.
Das gilt von 8-32Bit.

von Nord L. (flap)


Lesenswert?

Hurra schrieb:
> Was dem TE nicht klar ist, ist dass das bei den PICs über fast alle PICs
> sehr ähnlich funktioniert. D.h. es ist nicht nötig, ein spezifisches
> Beispiel für PIC33F herauszusuchen. Oft sind nur minimale Anpassungen
> nötig.
> Das gilt von 8-32Bit.
Danke! Das war wichtige Info für mich :>


Hier der Code.

Header:
1
// This is a guard condition so that contents of this file are not included
2
// more than once.  
3
#ifndef I2C_MGMNT_H
4
#define  I2C_MGMNT_H
5
6
//#include <xc.h> // include processor files - each processor file is guarded.  
7
8
#ifdef  __cplusplus
9
extern "C" {
10
#endif /* __cplusplus */
11
12
#ifndef FCY
13
#define FCY             (unsigned long)10000000 //default FCY 10MHz
14
#endif
15
//#include <libpic30.h>
16
#ifndef I2C_BAUDRATE
17
#define I2C_BAUDRATE    (unsigned long)100000 //default baud rate 100kHz
18
#endif
19
#define I2C_ERROR           -1
20
#define I2C_OK              1
21
22
extern void i2c_init(void);
23
extern int setBaudRate(void);
24
extern int I2C_WriteReg(char dev_addr, char reg_addr, char value);
25
extern int I2C_ReadReg(char dev_addr, char reg_addr, char *value);
26
    
27
28
#ifdef  __cplusplus
29
}
30
#endif /* __cplusplus */
31
32
#endif  /* XC_HEADER_TEMPLATE_H */

Die .c source file
1
/* I2C Register - Übersicht
2
 * http://ww1.microchip.com/downloads/en/DeviceDoc/70000195f.pdf
3
 * 
4
 * I2CxCON: I2Cx Control Register
5
 * I2CxSTAT: I2Cx Status Register
6
 * I2CxMSK: I2Cx Slave Mode Address Mask Register
7
 *      -> für mehrere Adressen
8
 * 
9
 * I2CxRCV: I2Cx Receive Buffer Register
10
 *      -> hier werden Daten gelesen
11
 * 
12
 * I2CxTRN: I2Cx Transmit Register
13
 *      -> hier werden Daten gesendet
14
 * 
15
 * I2CxADD: I2Cx Address Register
16
 *      -> Slave Adresse
17
 * 
18
 * I2CxBRG: I2Cx Baud Rate Generator Reload Register
19
 * 
20
 * ACKSTAT:
21
 *  Acknowledge Status bit 
22
 * 1 = NACK received from slave
23
 * 0 = ACK received from slave
24
 *  * 
25
 * TRSTAT: Transmit Status bit (I2C? Master mode transmit operation)
26
1 = Master transmit is in progress (8 bits + ACK)
27
0 = Master transmit is not in progress
28
 */
29
30
#include "i2c_mgmnt.h"
31
#include "p33FJ128GP802.h"
32
33
void i2c_init(void) {
34
35
    I2C1CONbits.I2CEN = 1; // enable I2C functionality
36
    I2C1CONbits.I2CSIDL = 0; // continue when idle
37
    I2C1CONbits.SCLREL = 1; // used in master mode           (nicht 100% sicher)
38
    I2C1CONbits.IPMIEN = 0; // don't use IPMI                (ein industriestandard, der dinge verumständlicht)
39
    I2C1CONbits.A10M = 0; // use 7-bit addressing
40
    I2C1CONbits.DISSLW = 1; // slew rate control disabled (nicht nötig in slow mode)
41
    I2C1CONbits.SMEN = 0; // don't use SMBus thresholds
42
    I2C1CONbits.GCEN = 0; //General call address disabled
43
    I2C1CONbits.STREN = 0; //Disable Software or release clock
44
45
46
    /*I2C1BRG = [( 1/Fscl - PGD)*Fcy/2]-2 = 195,4
47
     * Fcy = 40MHz
48
     * Fscl = 100KHz
49
     * PGD = 130ns
50
     */
51
    I2C1BRG = 195; // set the baud rate
52
 
53
    //__delay_ms(1);
54
    int i;
55
    for(i=0; i<1000; i++);
56
57
}
58
59
int I2C_WriteReg(char dev_addr, char reg_addr, char value)
60
{
61
    char wr_dev_addr = dev_addr << 1; //first bis is ACK
62
    // Send I2C start condition
63
  I2C1CONbits.SEN = 1;      
64
  while(I2C1CONbits.SEN == 1);
65
  // Send I2C device address on the bus for write operation
66
  I2C1TRN = wr_dev_addr;      
67
  while(I2C1STATbits.TRSTAT);      
68
  if (I2C1STATbits.ACKSTAT)        
69
  {                
70
    I2C1CONbits.PEN = 1;
71
    while(I2C1CONbits.PEN);      
72
    return I2C_ERROR;          
73
  }
74
    // Send register address on the bus
75
  I2C1TRN = reg_addr;
76
  while(I2C1STATbits.TRSTAT);
77
  if (I2C1STATbits.ACKSTAT)
78
  {
79
    I2C1CONbits.PEN = 1;
80
    while(I2C1CONbits.PEN);
81
    return I2C_ERROR;
82
  }
83
  // Send register value on the bus    
84
  I2C1TRN = value;
85
  while(I2C1STATbits.TRSTAT);
86
  if (I2C1STATbits.ACKSTAT)
87
  {
88
    I2C1CONbits.PEN = 1;
89
    while(I2C1CONbits.PEN);
90
    return I2C_ERROR;
91
  }
92
  /// Send I2C stop condition
93
  I2C1CONbits.PEN = 1;
94
  while(I2C1CONbits.PEN);
95
  return I2C_OK;
96
}

Und schließlicht die main file
1
#include <p33FJ128GP802.h>
2
//Prozessorspezifische Headerdatei
3
#include "port_mgmnt.h"
4
#include "adw_mgmnt.h"
5
#include "pwm_mgmnt.h"
6
#include "system_init.h"
7
#include "read_write.h"
8
#include "timer_mgmnt.h"
9
#include "i2c_mgmnt.h"
10
//custom headers
11
12
void main(void) {
13
    clk_boost(80);
14
    clr_ports();
15
    
16
    i2c_init();
17
    
18
    while(1){
19
        //I2C_WriteReg(0x27, 0x40, 0xFF);
20
        
21
        I2C1CONbits.SEN = 1;  
22
        I2C1TRN = 0x56;
23
        
24
        while(I2C1CONbits.SEN == 1);
25
        
26
        I2C1TRN = 0x56;
27
    }
28
}

Egal ob ich den
1
I2C_WriteReg(0x27, 0x40, 0xFF);
 Befehl ausführe oder den Code in der while-Schleife, er kommt da nicht 
heraus.
Ich gehe da im Debugmode mit einem ICD 3 durch. Ich schreibe 
gleichzeitig in das TRN Register, weil es im Handbuch hieß, es müsste 
etwas auf den Bus geschrieben werden, damit die Startcondition 
ausgeführt werden könnte.
Ich habe ein Oszi direkt am clk-Pin hängen, es kommt auch kein Signal.
Hat wer Ideen woran es liegen könnte?

: Bearbeitet durch User
von Rainer S. (enevile) Benutzerseite


Lesenswert?

Hast du Leds die du ansteuern kannst?
Dann kannst du in den Unterprogramm suchen wo er festhängt bzw. die Leds 
in den Unterprogramm in Abschnitten testen.

Warum hast du keine Klammern in den While schleifen?

von Dachdecker (Gast)


Lesenswert?

Das hier ist dir bekannt?
http://ww1.microchip.com/downloads/en/DeviceDoc/70000195f.pdf

das I2C Modul ist ident für alle PIC24 und DSPIC33. Das Hauptdatenblatt 
ist ja immer etwas einfach gehalten. Das oben verlinkte ist sehr viel 
detaillierter.

Ich sehe jedoch, du hast .SCLREL gesetzt. Das Bit hat nur Auswirkungen 
im Slave-Mode. Ich würde es mal wegtun.

Außerdem sollte man das Modul erst dann einschalten, wenn die Baudrate 
gesetzt wird. Ob das wirklich nötig ist, weiß ich aber nicht.

Bei mir war für I2C Starcondition immer nur folgendes nötig:
- I2C2BRG : Baudrate setzen
- I2CEN = 1 (Modul einschalten)
- ACKDT = 0 (eventuell vorhandene alte Acks löschen)
- SEN = 1, und dann warten.

Dann musst du am Oszilloskop sehen, wie zuerst SDA, dann SCL auf LOW 
gehen.

PS:
I2C ist halt Mist und zickig. Das ist nun mal so. Da hilft nur 
Durchbeißen. Es lohnt sich aber, das I2C-Modul der PICs ist recht gut.

von Nordlicht (Gast)


Lesenswert?

Rainer S. schrieb:
> Hast du Leds die du ansteuern kannst?
> Dann kannst du in den Unterprogramm suchen wo er festhängt bzw. die Leds
> in den Unterprogramm in Abschnitten testen.

Was sind Leds? Ich bin mit dem ICD3 Debugger Schritt für Schritt 
durchgegangen, er hängt in der genannten Funktion.

> Warum hast du keine Klammern in den While schleifen?

Ddie main-whileSchleife hat Klammern. Die Schleife zum Warten auf dsa 
Setzen des .SEN bits braucht keine, soll ja nur warten.

Dachdecker schrieb:
> Das hier ist dir bekannt?
> http://ww1.microchip.com/downloads/en/DeviceDoc/70000195f.pdf

Jo, danke.

> Ich sehe jedoch, du hast .SCLREL gesetzt. Das Bit hat nur Auswirkungen
> im Slave-Mode. Ich würde es mal wegtun.

Geht klar.

> Außerdem sollte man das Modul erst dann einschalten, wenn die Baudrate
> gesetzt wird. Ob das wirklich nötig ist, weiß ich aber nicht.
>
> Bei mir war für I2C Starcondition immer nur folgendes nötig:
> - I2C2BRG : Baudrate setzen
> - I2CEN = 1 (Modul einschalten)
> - ACKDT = 0 (eventuell vorhandene alte Acks löschen)
> - SEN = 1, und dann warten.
>
> Dann musst du am Oszilloskop sehen, wie zuerst SDA, dann SCL auf LOW
> gehen.

Komme erst wieder Montag dazu weiter zu arbeiten, dann werden die sehr 
konstruktiven Vorschläge umgesetzt! Vielen Dank soweit.

Nordlicht

von Nord L. (flap)


Angehängte Dateien:

Lesenswert?

Moin moin,
das mit dem I2C hat gut geklappt (deshalb musste ich mich auch nicht 
eher melden grins), nun hapert es noch am Display.
Das Display muss im 4bit Mode angesprochen werden, weil bereits 4 Bit 
des I2C Datenworts für Steuerbit verbraucht werden.
Die Initialisierung funktioniert soweit, dass ich das Display anschalten 
kann und den Cursor zum blinken bringen kann.
In einer früheren Version konnte ich auch schon Buchstaben anzeigen, 
allerdings bin ich dahintergekommen, dass wohl die 4bit Initialisierung 
nicht geklappt hat, denn es konnte nur die unterste Reihe der Matrix des 
Manuals, LINK:
https://www.sparkfun.com/datasheets/LCD/HD44780.pdf  (Seite 17)
angezeigt werden, auch wenn ich in die unteren 4 Bit des "data"bytes in 
der main() Funktion nicht mit 1111 beschrieben habe.
Nach meiner Arbeit an der 4Bit Initialiserung können jedoch keinerlei 
Buchstaben mehr angezeigt werden. Nun bin ich mir auch unsicher, ob ich 
das Ganze verschlimmbessert habe, oder nicht.
Ein Indiz, dass die 4Bit Initialisiserung geklappt hat, ist das am Ende 
des Init-Blocks mit diesen Zeilen Code das Display noch gesteuert werden 
kann:
1
    i2c_write(deviceid << 1);
2
    //ansprechen des Slaves mit Schreibbefehl
3
    display_send(0b00000111);
4
    //übertragen einer instruction set Anweisung
5
    //entry mode set
Das dürfte ja eigentlich nur dann funktionieren, wenn die 4Bit Init 
erfolgreich war.
Was dagegen spricht, ist das es keinerlei Unterschied macht, ob dieser 
Block auskommentiert ist oder nicht:
1
    ////////////////////////////
2
    i2c_start();
3
    //starten des i2c Bus
4
    i2c_write(deviceid << 1);
5
    //ansprechen des Slaves mit Schreibbefehl
6
7
    i2c_write(0b00110100); //write func set second time
8
    //übertragen einer function set Anweisung
9
    waitXus(1);
10
    i2c_write(0b00110000);
11
    //übertragen einer function set Anweisung
12
    i2c_stop();
13
    waitXus(21500);
14
    //////////////////////////////////////
Ich habe ein wenig in einer Arduinobib gespickt, die ich für das Display 
getestet habe. Dort wurde der 4 Bit Mode 3 mal geschrieben, entgegen des 
Handbuches, wo er nur 2 mal geschrieben wurde.

Falls ihr der Meinung seid, dass die Init komplett Grütze ist, kann ich 
gern die alte Version, in der im 8Bitmode die unterste Reihe der Matrix 
angezigt werden konnte, noch zur Verfügung stellen, die ist gesichtert.


Meine Frage an euch:
Ist die Init Mist oder die main Schleife? Und was ist Mist?

Der Controller ist auf 80MHz getaktet. Die Wartezeiten in der Init 
zwischen den Befehlen habe ich mit einem Oszi geprüft, vor der 
jeweiligen WhileSchleife ein Pin auf 1 gesetzt, danach wieder runter 
gezogen. Die Zeiten passen und sind ca 20% über der Mindestzeit. Auf dem 
Bus liegt an was geschrieben wird, das habe ich mit einem Logikanalyzer 
geprüft.

Hier der Code:
1
//display_mgmnt.c
2
#include "display_mgmnt.h"
3
#include <p33FJ128GP802.h>
4
//Prozessorspezifische Headerdatei
5
#include "i2c_mgmnt.h"
6
#include "timer_mgmnt.h"
7
#include "port_mgmnt.h"
8
//custom headers
9
10
11
#define backlight_on 0x04
12
13
14
int waitfor = 1;
15
int timecount = 0;
16
17
/*I2C Backpack: PCF8574AT, K44001, 05, KNM00463
18
 *standard I2C Adresse des PCF8574AT ICs ist 0x3F
19
 * I2C Datenword hat 8 Bit, wovon die unteren vier Bit für die Steuerung 
20
 * des Displays parallel an das HD44780U weitergegeben werden. Die oberen
21
 * drei Bit sind das enable, register select und das r/w Bit!
22
 * 
23
 * Die Frage ist, wo am IO Port des Displays das LSB des seriellen Datenwortes ausgegeben wird.
24
 * Pins am Display:     x   x   x   RS  RW  E   DB0 DB1 DB2 DB3 DB4 DB5 DB6 DB7 x   x
25
 * Pins vom PCF8574AT:              P0  P1  P2                  P4 P5 P6 P7
26
 * ==> Datenwort muss wie folgt an den PCF8574AT Übertragen werden: (MSB) DB7 DB6 DB5 DB4 x E RW RS (LSB)
27
 *  
28
 * Display muss also im 4 Bit Mode betrieben werden:
29
 * Es werden nur DB7-DB4 genutzt, 8 Bit Daten werden als zwei mal 4 Bit übertragen,
30
 * wobei erst die oberen und dann die unteren Bits gesendet werden. Nach jedem Paar 4 Bit 
31
 * Daten muss die Busy Flag abgefragt werden.
32
 * 
33
 * Das Display wird anfangs durch den Internal Reset Circuit mit verschiedenen Parametern initialisiert.
34
 * Für unseren Zweck müssen wir ein paar dieser Parameter ändern (siehe display_init).
35
 * 
36
 * Grundlegende Befehle:
37
 * RS 0, RW 0: Instruction Register write as internal operation
38
 * RS 0, RW 1: Read Busy Flag and AC (DB0 bis DB7)
39
 * RS 1, RW 0: Data Register write as internal operation
40
 * RS 1, RW 1: Data Register read as internal operation
41
 *
42
 * function set Befehl:
43
 * RS   RW  DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
44
 * 0    0   0   0   1   DL  N   F   x   x
45
 * DL data/interface length: 1 8 Bit, 0 4 Bit
46
 * N number of display lines: 1 2 lines, 0 1 line
47
 * F character font: 1 5x10 dots, 0 5x8 dots
48
 * 
49
 * display control Befehl:
50
 * RS   RW  DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
51
 * 0    0   0   0   0   0   1   D   C   B
52
 * D display on/off
53
 * C cursor on/off
54
 * B blinking cursor on/off
55
 * 
56
 * entry mode Befehl:
57
 * RS   RW  DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
58
 * 0    0   0   0   0   0   0   1   I/D S
59
 * I/D: 0 decrement, 1 increment
60
 * S: 1 accompanies display shift
61
 * 
62
 */
63
64
void waitXus(int val) {
65
    timecount = 0;
66
    set_timer4(0, val, 16);
67
    while (timecount == 0);
68
    timecount = 0;
69
    T4CONbits.TON = 0;
70
    //Disable Timer 4
71
}
72
73
void display_send(unsigned int data) {
74
    //data: 8 Bit, MSB DB7 ... DB0 LSB
75
    //nur instructions: RS 0, RW 0
76
77
    i2c_write((data & 0xF0) | 0x04);
78
    //übertragen der der oberen Bit mit E high
79
    waitXus(1);
80
    i2c_write(data & 0xF0);
81
    //übertragen der der oberen Bit mit E low
82
    waitXus(250);
83
    i2c_write((data << 4) | 0x04);
84
    //übertragen der der unteren Bit mit E high
85
    waitXus(1);
86
    i2c_write(data << 4);
87
    //übertragen der der unteren Bit mit E low
88
    waitXus(250);
89
}
90
91
int check_bf(char deviceid) {
92
    //ließt des Status des busy flags aus und übergibt ihn
93
94
    unsigned char bf = 0;
95
    i2c_write(deviceid << 1);
96
    //ansprechen des Slaves mit Schreibbefehl
97
    i2c_write(0b00000110);
98
    //Schreiben der busy flag read instruction
99
    //setzen des enable bits gleichzeitig mit dem Rest. Eventuell früher nötig?
100
    i2c_restart();
101
    //Neustart des Bus
102
    i2c_write((deviceid << 1) | 0x1);
103
    //ansprechen des Slaves mit Lesebefehl
104
    bf = i2c_read(8);
105
    //Funktion muss auch vor dem Einstellen der 4 Bit operation funktionieren
106
    //eigentlich nur die oberen 4 Bit interessant, deshalb nur einmaliges Lesen
107
    //zurückgegebene Bits entsprechen: x RS RW E DB7 DB6 DB5 DB4
108
    i2c_restart();
109
    //Neustart des Bus
110
    if (bf & 0x80) return 1;
111
        //busy flag up!
112
    else return 0;
113
    //no busy flag
114
}
115
116
void display_clear(char deviceid) {
117
    //leert das gesamte Display
118
119
    while (check_bf(deviceid)); //wait for busy flag to clear
120
    i2c_write(deviceid << 1); //ansprechen des Slaves mit Schreibbefehl
121
    i2c_write(0b00000100); //übertragen einer instruction set Anweisung Teil 1
122
    waitXus(1);
123
    i2c_write(0b00010100);
124
    //übertragen einer instruction set Anweisung Teil 2
125
    waitXus(250);
126
    //display clear
127
}
128
129
void display_init(char deviceid) {
130
    //initiiert das LCD und führt einen clear aus
131
    //Werte für das Warten mit Timer 4:
132
    //21500 4,3 ms
133
    //600 120us
134
    //250 50us
135
136
    timecount = 0; //warte auf betriebsspannung ~50ms
137
    set_timer4(0, 25000, 16);
138
    while (timecount < 10);
139
    timecount = 0;
140
    //40ms warten
141
    T4CONbits.TON = 0;
142
    //Disable Timer 4
143
144
    i2c_start();
145
    //starten des i2c Bus
146
    i2c_write(deviceid << 1);
147
    //ansprechen des Slaves mit Schreibbefehl
148
149
    i2c_write(0b00110100); //write func set first time
150
    //übertragen einer function set Anweisung
151
    //waitXus(1);
152
    i2c_write(0b00110000);
153
    //übertragen einer function set Anweisung
154
    i2c_stop();
155
    waitXus(21500);
156
157
    ////////////////////////////
158
    i2c_start();
159
    //starten des i2c Bus
160
    i2c_write(deviceid << 1);
161
    //ansprechen des Slaves mit Schreibbefehl
162
163
    i2c_write(0b00110100); //write func set second time
164
    //übertragen einer function set Anweisung
165
    waitXus(1);
166
    i2c_write(0b00110000);
167
    //übertragen einer function set Anweisung
168
    i2c_stop();
169
    waitXus(21500);
170
    //////////////////////////////////////
171
172
    i2c_start();
173
    //starten des i2c Bus
174
    i2c_write(deviceid << 1);
175
    //ansprechen des Slaves mit Schreibbefehl
176
177
    i2c_write(0b00110100); //write func set third time
178
    //übertragen einer function set Anweisung
179
    //waitXus(1);
180
    i2c_write(0b00110000);
181
    //übertragen einer function set Anweisung
182
    i2c_stop();
183
    //waitXus(250);
184
185
    i2c_start();
186
    //starten des i2c Bus
187
    i2c_write(deviceid << 1);
188
    //ansprechen des Slaves mit Schreibbefehl
189
190
    i2c_write(0b00110100);
191
    //übertragen einer function set Anweisung
192
    //waitXus(1);
193
    i2c_write(0b00110000);
194
    //übertragen einer function set Anweisung
195
    i2c_stop();
196
    //waitXus(250);
197
198
    i2c_start();
199
    i2c_write(deviceid << 1);
200
    //ansprechen des Slaves mit Schreibbefehl    
201
    i2c_write(0b00100100);
202
    //übertragen einer function set Anweisung    
203
    //waitXus(1);
204
    i2c_write(0b00100000);
205
    //übertragen einer function set Anweisung
206
    //waitXus(250);
207
    while (check_bf(deviceid));
208
    //wait for busy flag to clear
209
210
    i2c_write(deviceid << 1);
211
    //ansprechen des Slaves mit Schreibbefehl
212
    display_send(0b00111000);
213
    //übertragen der function set Anweisung
214
    //4 Bit mode, 2 lines, 5x8 dots
215
    while (check_bf(deviceid));
216
    //wait for busy flag to clear
217
    i2c_write(deviceid << 1);
218
    //ansprechen des Slaves mit Schreibbefehl
219
    display_send(0b00001111);
220
    //übertragen einer instruction set Anweisung
221
    //display & cursor off, blinking cursor off
222
223
    display_clear(deviceid);
224
225
    while (check_bf(deviceid));
226
    //wait for busy flag to clear
227
228
    i2c_write(deviceid << 1);
229
    //ansprechen des Slaves mit Schreibbefehl
230
    display_send(0b00000111);
231
    //übertragen einer instruction set Anweisung
232
    //entry mode set
233
234
    i2c_stop();
235
    waitXus(21500);
236
}
237
238
void __attribute__((__interrupt__, no_auto_psv)) _T4Interrupt(void) {
239
240
    waitfor = 0;
241
    timecount++;
242
243
    //d_write(25, ~d_read(25));
244
    //T4CONbits.TON = 0;
245
    //Timer 4 wieder ausschalten
246
    IFS1bits.T4IF = 0;
247
    //Clear Timer 4 Interrupt Flag
248
}
249
250
int display_write(char deviceid, char *text) {
251
    //schreibt eine Abfolge von Zeichen sequentiell auf das Display
252
    return 1;
253
}
254
255
------ Ende display_mgmnt.h
256
257
/* 
258
 * File:   display_mgmnt.h
259
 * Author: finst
260
 *
261
 * Created on 13. Dezember 2017, 20:00
262
 */
263
264
#ifndef DISPLAY_MGMNT_H
265
#define  DISPLAY_MGMNT_H
266
267
#ifdef  __cplusplus
268
extern "C" {
269
#endif
270
271
    extern void display_clear(char deviceid);
272
    extern void display_init(char deviceid);
273
    extern void __attribute__((__interrupt__, no_auto_psv)) _T4Interrupt(void);
274
    extern int timecount;
275
    extern void display_send(unsigned int data);
276
    extern int check_bf(char deviceid);
277
    extern void waitXus(int val);
278
279
280
#ifdef  __cplusplus
281
}
282
#endif
283
284
#endif  /* DISPLAY_MGMNT_H */
285
286
-------
287
//i2c_mgmnt.c
288
/* I2C Register - Übersicht
289
 * http://ww1.microchip.com/downloads/en/DeviceDoc/70000195f.pdf
290
 * 
291
 * I2CxCON: I2Cx Control Register
292
 * I2CxSTAT: I2Cx Status Register
293
 * I2CxMSK: I2Cx Slave Mode Address Mask Register
294
 *      -> für mehrere Adressen
295
 * 
296
 * I2CxRCV: I2Cx Receive Buffer Register
297
 *      -> hier werden Daten gelesen
298
 * 
299
 * I2CxTRN: I2Cx Transmit Register
300
 *      -> hier werden Daten gesendet
301
 * 
302
 * I2CxADD: I2Cx Address Register
303
 *      -> Slave Adresse
304
 * 
305
 * I2CxBRG: I2Cx Baud Rate Generator Reload Register
306
 * 
307
 * ACKSTAT:
308
 * Acknowledge Status bit 
309
 * 1 = NACK received from slave
310
 * 0 = ACK received from slave
311
 * 
312
 * TRSTAT: Transmit Status bit (I2C? Master mode transmit operation)
313
 * 1 = Master transmit is in progress (8 bits + ACK)
314
 * 0 = Master transmit is not in progress
315
 * 
316
 * Beim Senden der Adresse muss an der Stelle Null des Datenwortes ein R/!W Bit eingefügt werden, 
317
 * sodass der Slave weiß, wer sendet und wer empfängt!
318
 * 0: master tx, slave rx
319
 * 1: master rx, slave tx
320
 * 
321
 * 
322
 * Generelle Funktionsstruktur nach Patz:
323
 * start - startbiz, deviceid, ACK y/n
324
 * writedata - data (reg addr / data), ACK y/n
325
 * stop
326
 * readdata - data, ACK y/n
327
 */
328
329
#include "i2c_mgmnt.h"
330
#include "p33FJ128GP802.h"
331
#include "port_mgmnt.h"
332
333
void i2c_init(void) { 
334
    //I2C1BRG = [( 1/Fscl - PGD)*Fcy/2]-2 = 195,4
335
    //Fcy = 40MHz
336
    //Fscl = 100KHz
337
    //PGD = 130ns
338
    pin_cfg(17, 'd', 'i');
339
    pin_cfg(18, 'd', 'i');
340
    //default Konfiguration der Pins, sonst läuft nix
341
    //SDA, SCL fest verbunden mit Pin 17 & 18
342
    I2C1BRG = 195; 
343
    //set baud rate, siehe oben
344
    I2C1CONbits.I2CEN = 1; 
345
    //enable I2C functionality
346
    while(I2C1CONbits.I2CEN == 0);
347
    //wait until enabled
348
}
349
350
int i2c_start(void){
351
    IFS1bits.MI2C1IF = 0;
352
    //löschen des Interrpt Flags
353
    I2C1CONbits.SEN = 1;
354
    //send I2C start condition
355
    while(!IFS1bits.MI2C1IF == 1);
356
    //warten auf interrupt flag zur Bestätigung
357
    IFS1bits.MI2C1IF = 0;
358
    //IF der I2C  Master Events klären
359
360
    if (I2C1STATbits.ACKSTAT){
361
        //Fehler?                      
362
        return 0;          
363
    }
364
    else return 1;
365
    //Start erfolgreich
366
}
367
368
int i2c_restart(void){
369
    IFS1bits.MI2C1IF = 0;
370
    //löschen des Interrpt Flags
371
    while(I2C1CON & 0x001F);
372
    //untere fünf Bit des Control Registers müssen null sein, sonst kein Restart zulässig
373
    I2C1CONbits.RSEN = 1;
374
    //send I2C restart condition
375
    while(!IFS1bits.MI2C1IF == 1);
376
    //warten auf interrupt flag zur Bestätigung
377
    IFS1bits.MI2C1IF = 0;
378
    //IF der I2C  Master Events klären
379
380
    if (I2C1STATbits.ACKSTAT){
381
        //Fehler?                      
382
        return 0;          
383
    }
384
    else return 1;
385
    //Restart erfolgreich
386
}
387
388
int i2c_stop(void){
389
    IFS1bits.MI2C1IF = 0;
390
    //löschen des Interrpt Flags
391
    while(I2C1CON & 0x001F);
392
    //untere fünf Bit des Control Registers müssen null sein, sonst kein Stop zulässig
393
    I2C1CONbits.PEN = 1;
394
    //send I2C start condition
395
    while(!IFS1bits.MI2C1IF == 1);
396
    //warten auf interrupt flag zur Bestätigung
397
    IFS1bits.MI2C1IF = 0;
398
    //IF der I2C  Master Events klären
399
400
    if (I2C1STATbits.ACKSTAT){
401
        //Fehler?                      
402
        return 0;          
403
    }
404
    else return 1;
405
    //Stop erfolgreich
406
}
407
408
int i2c_write(char data){
409
    IFS1bits.MI2C1IF = 0;
410
    //IF der I2C  Master Events klären
411
  I2C1TRN = data;  
412
    //Schreiben der Daten ins Transmit Register
413
  while(!IFS1bits.MI2C1IF == 1);
414
    //warten auf interrupt flag zur Bestätigung
415
    IFS1bits.MI2C1IF = 0;
416
    //IF der I2C  Master Events klären
417
        
418
  if (I2C1STATbits.ACKSTAT){
419
        //Fehler?
420
    return 0;          
421
  }
422
    else return 1;
423
    //Schreiben erfolgreich
424
}
425
426
void i2c_ack(int yn){
427
    //yn: 0 send ACK, 1 send NACK
428
    I2C1CONbits.ACKDT=yn;
429
    //entweder ACK oder NACK senden
430
    while(I2C1CON & 0x001F);
431
    //untere fünf Bit des Control Registers müssen null sein, sonst kein Lesen zulässig
432
    IFS1bits.MI2C1IF = 0;
433
    //IF der I2C  Master Events klären
434
    I2C1CONbits.ACKEN=1;
435
    //starten der acknowledge Sequenz
436
  while(!IFS1bits.MI2C1IF == 1);
437
    //warten auf interrupt flag zur Bestätigung
438
    IFS1bits.MI2C1IF = 0;
439
    //IF der I2C  Master Events klären
440
}
441
442
char i2c_read(int bits){
443
    //bits: 8 empfangen von 8 Bit, 16 empfangen von 16 Bit
444
    //Diese Funktion kann entweder 8 oder 16 Bit empfangen.
445
    //Mehr ist in unserem Fall nicht notwendig, da unsere Slaves eher 
446
    //16 Bit oder kleinere Register haben.
447
    
448
    unsigned char buffer=0;
449
    //Speicher für den Rückgabewert
450
    while(I2C1CON & 0x001F);
451
    //untere fünf Bit des Control Registers müssen null sein, sonst kein Lesen zulässig
452
    IFS1bits.MI2C1IF = 0;
453
    //IF der I2C  Master Events klären
454
    I2C1CONbits.RCEN=1;
455
    //ready to receive
456
  while(!IFS1bits.MI2C1IF == 1);
457
    //warten auf interrupt flag zur Bestätigung
458
    buffer = I2C1RCV;
459
    //speichern des empfangenen Bytes
460
    IFS1bits.MI2C1IF = 0;
461
    //IF der I2C  Master Events klären
462
    
463
    if(bits == 16){
464
        i2c_ack(0);
465
        //falls empfangene Nachricht nicht komplett: senden eines acknowledge
466
        IFS1bits.MI2C1IF = 0;
467
        //IF der I2C  Master Events klären
468
        I2C1CONbits.RCEN=1;
469
        //ready to receive
470
        buffer = buffer << 8;
471
        //Platz machen für die nächsten 8 Bit
472
        while(!IFS1bits.MI2C1IF == 1);
473
        //warten auf interrupt flag zur Bestätigung
474
        buffer = buffer + I2C1RCV;
475
        //speichern des empfangenen Bytes
476
        IFS1bits.MI2C1IF = 0;
477
        //IF der I2C  Master Events klären
478
    }
479
    
480
    i2c_ack(1);
481
    //falls empfangene Nachricht komplett: senden eines not acknowledge
482
    
483
  return buffer;
484
    //Rückgabe des empfangenen Wertes
485
}
486
----- ende i2c_mgmnt.c
487
488
489
//main
490
#include <p33FJ128GP802.h>
491
//Prozessorspezifische Headerdatei
492
493
#include "port_mgmnt.h"
494
//#include "adw_mgmnt.h"
495
//#include "pwm_mgmnt.h"
496
#include "system_init.h"
497
//#include "read_write.h"
498
#include "timer_mgmnt.h"
499
#include "i2c_mgmnt.h"
500
#include "clk_mgmnt.h"
501
#include "display_mgmnt.h"
502
//custom headers
503
504
void main(void) {
505
    char deviceid = 0x3F;
506
507
508
    clk_boost(80);
509
    clr_ports();
510
    i2c_init();
511
    //setup
512
513
    display_init(deviceid);
514
515
    pin_cfg(25, 'd', 'o');
516
517
    //set_timer4(0, 250, 16);
518
519
    /*i2c_start();
520
    i2c_write(deviceid << 1);
521
    //ansprechen des Slaves mit Schreibbefehl
522
    display_send(0b00001111);
523
    check_bf(deviceid);
524
    
525
    int data = 0b01000001;
526
    
527
    i2c_write(deviceid << 1);
528
    //ansprechen des Slaves mit Schreibbefehl
529
    
530
    i2c_write((data & 0xF0) | 0x05);
531
    //übertragen der der oberen Bit mit E high
532
    set_timer4(0, 1, 16);
533
    while(timecount == 0);
534
    timecount = 0;
535
    T4CONbits.TON = 0;      
536
    //Disable Timer 4
537
    i2c_write((data & 0xF0) | 0x01);
538
    //übertragen der der oberen Bit mit E low
539
    set_timer4(0, 250, 16);
540
    while(timecount == 0);
541
    timecount = 0;
542
    T4CONbits.TON = 0;      
543
    //Disable Timer 4
544
    i2c_write((data<<4) | 0x05);
545
    //übertragen der der unteren Bit mit E high
546
    set_timer4(0, 1, 16);
547
    while(timecount == 0);
548
    timecount = 0;
549
    T4CONbits.TON = 0;      
550
    //Disable Timer 4
551
    i2c_write((data<<4) | 0x01);
552
    //übertragen der der unteren Bit mit E low
553
    set_timer4(0, 250, 16);
554
    while(timecount == 0);
555
    timecount = 0;
556
    T4CONbits.TON = 0;      
557
    //Disable Timer 4
558
    
559
    i2c_write(0b00001000); // turn backlight on
560
    
561
    
562
    i2c_stop();*/
563
564
565
    while (1) {
566
567
        i2c_start();
568
569
        while(check_bf(deviceid));
570
571
        int data = 0b11101101;
572
573
        i2c_write(deviceid << 1);
574
        //ansprechen des Slaves mit Schreibbefehl
575
576
        i2c_write((data & 0xF0) | 0x05);
577
        //übertragen der der oberen Bit mit E high
578
        waitXus(1);
579
        i2c_write((data & 0xF0) | 0x01);
580
        //übertragen der der oberen Bit mit E low        
581
        waitXus(250);
582
        i2c_write((data << 4) | 0x05);
583
        //übertragen der der unteren Bit mit E high
584
        waitXus(1);
585
        i2c_write((data << 4) | 0x01);
586
        //übertragen der der unteren Bit mit E low
587
        waitXus(250);
588
589
        i2c_write(0b00001000); // turn backlight on
590
591
592
        i2c_stop();
593
594
    }
595
}

Falls ihr euch die Zeit für das Problem nehmt, vielen vielen Dank.

Beste Grüße
das Nordlicht

von Hmm (Gast)


Lesenswert?

Ich kann dir nur vorschlagen, das Problem sauber zu gliedern, und in 
einen kürzeren Beitrag zu fassen.

Niemand hat Lust, sich in seiner Freizeit durch eine km-lange Textwurst 
zu wühlen.

Mal ganz davon abgesehen, dass man da drin nichts findet. In der einen 
Minute habe ich die Stelle nicht gefunden, wo das Display initialisiert 
wird.

von Gott (Gast)


Lesenswert?

Dumpf Backe schrieb:
> Nord L. schrieb:
>> vorweg: Ich bin fortgeschrittener Student, habe gute C-Kenntnisse und
>> Arduinoerfahrung hoch und runter.
>
> Aber weiss immer noch nicht dass "LCD Display"
>
> "Liquid Crystal Display Display"
>
> bedeuted.
>
> Und der Rest steht doch tatsächlich alles in den Datenblättern.

na da war wohl wieder einer besonders schlagfertig
schon klar, warum er Dumpf Backe heist

von Weihnachts Ente (Gast)


Lesenswert?

Gott schrieb:
> na da war wohl wieder einer besonders schlagfertig

Und Gott ist so schlagfertig dass er nur zwei Jahre
gebraucht hat um diesen Sachverhalt herauszufinden.

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.