Hallo zusammen,
ich versuche verzweifelt einen Drucksensor von VTI (SCP1000) per
I2C-Interface anzusprechen. Der Sensor hängt an einem MSP430 mit
I2C-Schnittstelle. Laut Datenblatt des Sensors sollte vor dem Auslesen
von Druckwerten eine Routine zur Abfrage durchgeführt werden, die
anzeigt, ob der Sensor richtig initialisiert wurde und ob
Prüfsummenfehler im integrierten EEPROM aufgetreten sind. Also tue ich,
wie mir gehießen.
Das Problem: Die Routine gibt beim debuggen im Einzelschrittbetrieb
keinen Fehler zurück - also alles ok. Lasse ich den Controller
ungezügelt auf den Bus los, krieg ich nen Fehler für eine falsche
EEPROM-CRC zurück. Ähnliches auch, wenn ich die Startup-Routine im
Einzelschrittbetrieb durchmache und später Werte mit vollem Takt
auslesen oder schreiben will. Dann bleibe ich irgendwo in den
I2C-Funktionen zum schreiben oder lesen an der Abfrage der
Interruptflags hängen.
Jetzt drängt sich natürlich der Bustakt auf. Den hab ich aber auch schon
variiert. Ich habe von 100kHz bis 1kHz schon alles durchgemacht.
Pullup-Widerstände hängen auch dran: 10k, vielleicht ein bisschen hoch,
sollten aber dennoch gehen. Den Bus kann ich mit einem Oszi mit
Bus-Analyzer verfolgen, die Signale schauen sauber aus und es werden
auch die Start-/Stop- und Acknowledge-Signale richtig bedient.
Ich bin vollkommen überfragt. Vielleicht fällt euch was ein?
Hier sind die wichtigsten Routinen:
In der Main-Funktion wird die Portrichtung gesetzt und der Sensor
anschließend aus dem Power-Down-Mode genommen und die Zeit abgewartet,
die er zur Initialisierung braucht. Solange schläft der MSP einen seiner
Ruhezustände um Energie zu sparen. Dann wird die Initialisierungsabfrage
aufgerufen:
1 | //Initialize Ports and Directions for specific sensor
|
2 | P6DIR |= SCP_PD; //Port-pin for power-down pin on SCP as output
|
3 | P6OUT |= SCP_PD; //hold PD-pin high to remain in power down
|
4 | //mode during the stabilization of the power supplies
|
5 |
|
6 | // Initialize Hardware:
|
7 | initI2C();
|
8 |
|
9 | //Setup timer for periodic wakeup
|
10 | TACTL |= (!TASSEL1 + TASSEL0); //Timer A clock source is ACKL
|
11 | TACTL |= (ID1 + ID0); //Input divider = 8
|
12 | TACTL |= TAIE; //enable timer interrupt
|
13 |
|
14 |
|
15 | for (;;){
|
16 | P6OUT &= ~SCP_PD; //release power down mode for normal operation
|
17 | TACCR0 = 0x01FF; //wait ~60ms for sensor startup time (FFh = 62,256ms / 1FFh = 125ms)
|
18 | START_TIMER;
|
19 |
|
20 | // MSP power down (LPM3: all but ACKL disabled), general interrupts enable
|
21 | _BIS_SR(GIE + CPUOFF + SCG1 + SCG0);
|
22 |
|
23 | if (!checkStartupStatus()) { //startup fail? -> no error:
|
24 | (...)
|
25 | }
|
Die Funktion "checkStartupStatus()" im Detail:
1 | /*------------------------------------------------------------------------------
|
2 | //::FUNCTION: checkStartupStatus()
|
3 | ------------------------------------------------------------------------------*/
|
4 | // DESCRIPTION:
|
5 | // ~~~~~~~~~~~~
|
6 | /** \brief Checks whether start-up procedure is finished. The STATUS register
|
7 | * (0x07) can be read to verify that start-up procedure is finished.
|
8 | * If the STARTUP bit (LSB) of the STATUS register (0x07) is '0', the
|
9 | * start-up procedure is finished successfully. If the STARTUP bit of
|
10 | * the STATUS register is '1', the start-up procedure is still running.
|
11 | *
|
12 | * The start-up status check includes EEPROM checksum error result
|
13 | * check. If the content of DATARD8 is 0x00, EEPROM checksum
|
14 | * calculation indicated an error and the start-up procedure is failed.
|
15 | * Correct EEPROM checksum calculation result is indicated by content
|
16 | * of 0x01 in DATARD8.
|
17 | *
|
18 | * \return 0 - If start-up procedure has finished without errors
|
19 | * 1 - If start-up procedure has failed with unfinished initialization
|
20 | * 2 - If start-up procedure has failed with EEPROM checksum error
|
21 | * 3 - If start-up procedure has failed with both errors
|
22 | */
|
23 | /*----------------------------------------------------------------------------*/
|
24 |
|
25 | uint8_t checkStartupStatus(void) {
|
26 | uint8_t success = 0;
|
27 |
|
28 | for (int retries = 0; retries < 6; retries++) {
|
29 | ((readI2C(STATUS) & 0x01) == 0 ? (success = 0) : (success = 1));
|
30 | if (!success)
|
31 | break;
|
32 | }
|
33 |
|
34 | ((readI2C(DATARD8) & 0x01) == 1 ? (success += 0) : (success += 2));
|
35 |
|
36 | return success;
|
37 | }
|
Hier wird im Einzelschritt "success = 0" zurückgegeben, bei voller
Geschwindigkeit "success = 2".
Die Initialisierung des I2C-Ports:
1 | void initI2C(void) {
|
2 | P3SEL |= 0x0A; // Assign pins to i2c module function
|
3 |
|
4 | U0CTL |= SWRST; // hold usart in software reset
|
5 | U0CTL |= SYNC; // select "SYNC" bit and...
|
6 | U0CTL |= I2C; // ..."I2C" bit to select I2C mode
|
7 | U0CTL &= ~XA; // 7 bit addresses
|
8 | U0CTL &= ~SWRST; // Start USART1
|
9 |
|
10 | U0CTL &= ~I2CEN; // disable I2C mode for further configuration
|
11 | I2CTCTL &= ~I2CWORD; // I2C data register is 8bit
|
12 | I2CTCTL &= ~I2CRM; // number of bytes transmitted is controlled by I2CNDAT
|
13 | I2CTCTL = I2CSSEL_2; // SMCLK as clock source. After PUC ( power up clear)
|
14 | // SMCLK is sourced from DCOCLK with ~800kHz
|
15 | I2CPSC = 0x03; // Set Prescaler. Divide SMCLK / 3 (Values more then 4
|
16 | // are not recommended). Use I2CSCLL and I2CSCLH instead.
|
17 | // I2CSCLH = 0xFF; // set i2c clock high-periode
|
18 | // I2CSCLL = 0xFF; // set i2c clock low-periode
|
19 | U0CTL |= I2CEN; // I2C is now 8Bit-mode, activated and stuff from above
|
20 | }
|
I2CSCLH und I2CSCLL nehme ich, um den Bustakt zu drosseln. Schaut am
Oszi optimal aus, die High- und Low-Zeiten im Sensordatenblatt werden
eingehalten.
Und zuletzt die Funktionen zum schreiben und lesen auf dem Bus:
1 | uint8_t readI2C(uint8_t addr2) {
|
2 | I2CNDAT = 1; // number of bytes to send
|
3 | I2CSA = SCP1000_ADDRESS; // Slave Adresse
|
4 | U0CTL |= MST; // Master mode
|
5 | I2CTCTL |= I2CSTT+I2CTRX; // Start + Transmit mode (no stop bit!)
|
6 | while ((I2CIFG & TXRDYIFG) == 0); // Wait for transmitter to be ready
|
7 | I2CDRB = addr2; // write register addr. to sca3000
|
8 | while ((I2CIFG & ARDYIFG) == 0);
|
9 |
|
10 | I2CNDAT = 1; // number of bytes to read
|
11 | U0CTL |= MST; // Master mode
|
12 | I2CTCTL &= ~I2CTRX; // RX Mode
|
13 | I2CTCTL |= I2CSTT + I2CSTP; // Repeated Start + Stop
|
14 | while ((I2CTCTL & I2CSTP) == I2CSTP); // Wait for Stop Condition
|
15 | I2CIFG = 0x00; // Clear I2C interrupt Flags
|
16 | return I2CDRB;
|
17 |
|
18 | }
|
19 |
|
20 |
|
21 | void writeI2C(uint8_t addr, uint8_t data) {
|
22 | I2CNDAT = 0x02; // Bytes to send (1 for addr. + 1 data byte)
|
23 | I2CSA = SCP1000_ADDRESS; // Slave Adresse
|
24 | U0CTL |= MST; // Master mode
|
25 | I2CTCTL |= I2CSTT+I2CSTP+I2CTRX; // Start + Stop + Transmit mode
|
26 | while ((I2CIFG & TXRDYIFG) == 0); // Wait for transmitter to be ready
|
27 | I2CDRB = addr; // write register addr. to sca3000
|
28 | while ((I2CIFG & TXRDYIFG) == 0); // Wait for transmitter to be ready
|
29 | I2CDRB = data; // write data addr. to sca3000
|
30 | // while ((I2CTCTL & I2CSTP) == 0x02); // Wait for Stop Condition
|
31 | while ((I2CTCTL & I2CSTP) == I2CSTP); // Wait for Stop Condition
|
32 | // I2CIFG = 0x00; // Clear I2C interrupt Flags
|
33 | }
|
Vielen Dank, dass ihr euch die Zeit nehmt!
Viele Grüße
Daniel