/** \file I2Csensor.c
\brief Treiberschicht für das kDevice812: I²C-Anbindung und LowLevel für MS5611.
\author Helmut Stettmaier (stm)
\version 1.0
\date ... 2014
*/
#include "LPC8xx.h"// Registernamen
#include "LPC8xxB.h"// weitere Definitionen (nicht CMSIS) für Bit-Namen
#include "hal.h"
#include "I2Csensor.h"
#include "main.h"
void I2CCtrlStartSequence();
void I2CCtrlSendAddressRead();
void I2CCtrlSendResetCmd();
void I2CCtrlStartTemp();
void I2CCtrlStartPressure();
void I2CCtrlSendCalReadCmd();
void I2CCtrlReadADCCommand();
void I2CCtrlStop();
void I2CCtrlSequStop();
void I2CCtrlResetHardware(void);
void I2CCtrlReadByte1();
void I2CCtrlReadByte2();
void I2CCtrlCalReadByte2();
void I2CCtrlTempReadByte3();
void I2CCtrlPressureReadByte3();
void writeI2CErrRecord(unsigned int v);
/* Note for LPC Forum readers: I²C is set up in the HAL as follows:
LPC_SYSCON->SYSAHBCLKCTRL |= (
SYSCON_SYSAHBCLKCTRL_Bits | UART_SYSAHBCLKCTRL_Bits |
GPIO_SYSAHBCLKCTRL_Bits | I2C_SYSAHBCLKCTRL_Bits |
SCT_SYSAHBCLKCTRL_Bits); // SCT temporär für den Test-Ohrhöhrer
LPC_SYSCON->PRESETCTRL= (
SYSCON_PRESETCTRL_Bits | UART_PRESETCTRL_Bits |
GPIO_PRESETCTRL_Bits | I2C_PRESETCTRL_Bits |
SCT_PRESETCTRL_Bits); // SCT temporär für den Test-Ohrhöhrer
...
LPC_I2C->DIV= 3; // 14;
LPC_I2C->CFG= 1;
LPC_I2C->TIMEOUT= 0x002f; // -->48
*/
/** Die I²C-Adresse des Sensors (CS=low) */
#define MS5611_I2C_ADR 0xee
/** Die 6 Kalibrierungswerte des MS5611 ohne CRC. */
static uint16_t C[7];
/** Namen: C[1] ist C1 u.s.w. */
#define C1 C[1]
#define C2 C[2]
#define C3 C[3]
#define C4 C[4]
#define C5 C[5]
#define C6 C[6]
/** Zähler für die Kalibrierwerte-Schleife im I²C-Automaten. */
static uint8_t cnt;
/** Status des I²C-Automaten. */
uint8_t MS5611state;
/** Zwischengröße: Differenz zur "Referenztemperatur" in D[2].
Wird zur Kompensation des Temperatur-Fehlers im Druckwert benötigt. */
uint32_t dT;
/** Export: Temperatur-kompensierter Druckwert in Pa. */
uint32_t rawPressure;
/** . */
static uint32_t input;
/** Start des MS5611-Abfrage-Automaten. */
void resetMS5611(void) {
set_mrt_repeatedmode (2, (110*__SYSCLOCK_100us)); // 11ms
MS5611state= 0;
cnt= 6;
input= 0;
}
/** Wert von LPC_I2C->STAT. */
static uint32_t cpyI2CSTAT;
/** Steuer-Bit für I²C: Master - Starte Senden. */
#define MSTSTART 0x02
/** Steuer-Bit für I²C: Master - Setze Kommunikation fort. */
#define MSTCONTINUE 0x01
/** Steuer-Bit für I²C: Master - Beende Kommunikation. */
#define MSTSTOP 0x04
/** Besonderer Zustand der I2C-Engine: Setzt die Maschinerie zurück. */
#define I2CCtrlReset0
/** Besonderer Zustand der I2C-Engine: Schleifen-Beginn für die Kal.-Konstanten. */
#define I2CCtrlCalLoop3
/** Besonderer Zustand der I2C-Engine: Startet neu (ohne Rücksetzen). */
#define I2CCtrlRestart9
/** Besonderer Zustand der I2C-Engine: Endlos-Schleife für Temperatur und Druck. */
#define I2CCtrlLoop12
/** Größte erlaubte Zustands-Zahl für Bereichsprüfung. */
#define I2CCtrlUppLimit31
//void (*I2CCtrlTable[])() = {
/** . */
typedef void (* I2Cstep)(void);
/** . */
const I2Cstep I2CCtrlTable[]= {
/* MS5611-Reset-Kommando */
I2CCtrlStartSequence,// 0 Adresse (Write)MSTSTART
I2CCtrlSendResetCmd,// 1 Kommando-ByteMSTCONTINUE
I2CCtrlSequStop,// 2 MSTSTOP, INTENCLR
/* 6mal Kalibrier-Konstante einlesen (C[1]..C[6]) */
I2CCtrlStartSequence,// 3 Adresse (Write)MSTSTART
I2CCtrlSendCalReadCmd,// 4 Kommando-ByteMSTCONTINUE
I2CCtrlStop,// 5 MSTSTOP
I2CCtrlSendAddressRead,// 6 Adresse (Read)MSTSTART
I2CCtrlReadByte1,// 7 lies 1. ByteMSTCONTINUE
I2CCtrlCalReadByte2,// 8 lies 2. ByteMSTSTOP, INTENCLR
/* zurück zu 4 (I2CCtrlCalLoop) falls noch Kalibrierwerte zu lesen sind */
/* MS5611-Reset-Kommando */
I2CCtrlStartSequence,// 9 Adresse (Write)MSTSTART
I2CCtrlSendResetCmd,// 10 Kommando-ByteMSTCONTINUE
I2CCtrlSequStop,// 11 MSTSTOP, INTENCLR
/* Temperatur-Messung einleiten */
I2CCtrlStartSequence,// 12 Adresse (Write)MSTSTART
I2CCtrlStartTemp,// 13 Kommando-ByteMSTCONTINUE
I2CCtrlSequStop,// 14 MSTSTOP, INTENCLR
/* Temperatur-Wert einlesen und kalibrieren */
I2CCtrlStartSequence,// 15 Adresse (Write)MSTSTART
I2CCtrlReadADCCommand,// 16 Kommando-ByteMSTCONTINUE
I2CCtrlStop,// 17 MSTSTOP
I2CCtrlSendAddressRead,// 18 Adresse (Read)MSTSTART
I2CCtrlReadByte1,// 19 lies 1. ByteMSTCONTINUE
I2CCtrlReadByte2,// 20 lies 2. ByteMSTCONTINUE
I2CCtrlTempReadByte3,// 21 lies 3. ByteMSTSTOP, INTENCLR
/* Druck-Messung einleiten */
I2CCtrlStartSequence,// 22 Adresse (Write)MSTSTART
I2CCtrlStartPressure,// 23 Kommando-ByteMSTCONTINUE
I2CCtrlSequStop,// 24 MSTSTOP, INTENCLR
/* Druck-Wert einlesen und kalibrieren */
I2CCtrlStartSequence,// 25 Adresse (Write)MSTSTART
I2CCtrlReadADCCommand,// 26 Kommando-ByteMSTCONTINUE
I2CCtrlStop,// 27 MSTSTOP
I2CCtrlSendAddressRead,// 28 Adresse (Read)MSTSTART
I2CCtrlReadByte1,// 29 lies 1. ByteMSTCONTINUE
I2CCtrlReadByte2,// 30 lies 2. ByteMSTCONTINUE
I2CCtrlPressureReadByte3// 31 lies 3. ByteMSTSTOP, INTENCLR
/* zurück zu 10 (I2CCtrlLoop) */
};
/** Sende die MS5611-Adresse (als Beginn einer I²C-Kommandosequenz). */
void I2CCtrlStartSequence(void) {
LPC_I2C->MSTDAT= MS5611_I2C_ADR;
LPC_I2C->MSTCTL= MSTSTART;
debugRedHigh;//<<<<<<<<<<<<<<<<<
LPC_I2C->INTENSET= (MSTPENDINGEN | EVENTTIMEOUTEN);
input= 0;
};
/** Beende eine I²C-Kommandosequenz. */
void I2CCtrlSequStop(void) {
LPC_I2C->INTENCLR= 1;
LPC_I2C->MSTCTL= MSTSTOP;
}
/** Sende die MS5611-Adresse zum Lesen. */
void I2CCtrlSendAddressRead(void) {
LPC_I2C->MSTDAT= MS5611_I2C_ADR | 1;
LPC_I2C->MSTCTL= MSTSTART;
};
/** Sende den Reset-Befehl als Datenbyte. */
void I2CCtrlSendResetCmd(void) {
LPC_I2C->MSTDAT= 0x1e;
LPC_I2C->MSTCTL= MSTCONTINUE;
}
/** Sende das Initialize-Temperature-Measurement-Kommando als Datenbyte. */
void I2CCtrlStartTemp(void) {
LPC_I2C->MSTDAT= 0x58;
LPC_I2C->MSTCTL= MSTCONTINUE;
}
/** Sende das Initialize-Pressure-Measurement-Kommando als Datenbyte. */
void I2CCtrlStartPressure(void) {
LPC_I2C->MSTDAT= 0x48;
LPC_I2C->MSTCTL= MSTCONTINUE;
}
/** Sende das CalConst-Lese-Kommando als Datenbyte. cnt ist # der CalConst. */
void I2CCtrlSendCalReadCmd(void) {
LPC_I2C->MSTDAT= 0xa0 | (cnt<<1);
LPC_I2C->MSTCTL= MSTCONTINUE;
}
/** . */
void I2CCtrlReadADCCommand(void) {
LPC_I2C->MSTDAT= 0x00;
LPC_I2C->MSTCTL= MSTCONTINUE;
}
/** Sende Stop-Condition, lasse den Interrupt aber eingeschaltet. */
void I2CCtrlStop(void) {
LPC_I2C->MSTCTL= MSTSTOP;
}
/** Lesen: 1. Byte. */
void I2CCtrlReadByte1(void) {
input= LPC_I2C->MSTDAT;
LPC_I2C->MSTCTL= MSTCONTINUE;
}
/** Lesen: 2. Byte (neutral). */
void I2CCtrlReadByte2(void) {
input= input<<8 | LPC_I2C->MSTDAT;
LPC_I2C->MSTCTL= MSTCONTINUE;
}
/** Kalibrier-Konstante lesen: 2. Byte, zusammensetzen & abspeichern.
cnt ist # der CalConst. */
void I2CCtrlCalReadByte2(void) {
C[cnt]= input<<8 | LPC_I2C->MSTDAT;
if (--cnt) MS5611state= I2CCtrlCalLoop-1;
LPC_I2C->MSTCTL= MSTSTOP;
LPC_I2C->INTENCLR= 1;
}
/** Lies das 3. Byte des Temperatur-Wertes ein, Berechne dT. */
void I2CCtrlTempReadByte3(void) {
uint32_t D2= input<<8 | LPC_I2C->MSTDAT;
dT = D2 - (((int32_t)(C5)) << 8);
LPC_I2C->MSTCTL= MSTSTOP;
}
/** Lies das 3. Byte des Druck-Wertes ein, Berechne den Roh-Druckwert. */
void I2CCtrlPressureReadByte3(void) {
int64_t s, o;
uint32_t D1= input<<8 | LPC_I2C->MSTDAT;
o =(((int64_t)(C2))<<16)+ (((C4)* (int64_t)dT)>>7);
s =(((int64_t)(C1))<<15)+ (((C3)* (int64_t)dT)>>8);
rawPressure =((((int64_t)D1 *s)>>21) -o)>>15;
LPC_I2C->MSTCTL= MSTSTOP;
mainStatus|= mstatMS5611PrValread;
MS5611state= I2CCtrlLoop-1;
}
/* Note for readers from LPC-Forum:
I2C_MRTaction is part of the MRT-Interrupt handler
which is copied here:
void MRT_IRQHandler(void) {
if (LPC_MRT->Channel[2].STAT & MRT_STAT_IRQ_FLAG) {
LPC_MRT->Channel[2].STAT = MRT_STAT_IRQ_FLAG;
debugYellowLow; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
I2C_MRTaction();
debugYellowHigh; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
if (LPC_MRT->Channel[3].STAT & MRT_STAT_IRQ_FLAG) {
LPC_MRT->Channel[3].STAT = MRT_STAT_IRQ_FLAG;
debugBlueLEDon; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
MSBRxTiming();
debugBlueLEDoff; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
NVIC->ICPR[0] = MRT_INTERRUPT_BIT;
return;
}
*/
/** Bearbeitet den für den I²C-Verkehr ausgelösten MRT-Interrupt:
Startet die nächste anstehende I²C-Kommandosequenz. */
inline void I2C_MRTaction(void) {
if (MS5611state>I2CCtrlUppLimit) {
debugRedLow;//<<<<<<<<<<<<<<<<<<
writeI2CErrRecord(3);
mainStatus&= ~mstatMS5611PrValread;
MS5611state= I2CCtrlRestart;
}
if (!(LPC_I2C->STAT & MSTPENDING)) {
debugRedLow;//<<<<<<<<<<<<<<<<<<
writeI2CErrRecord(1);
_Panic_; // still write a panic handler <<<<<<<<<
}
I2CCtrlStartSequence(); // ex (*I2CCtrlTable[MS5611state])();
MS5611state++;
return;
}
/** I²C-Interrupt: Setzt die laufende I²C-Kommandosequenz fort. */
void I2C_IRQHandler(void) {
cpyI2CSTAT= LPC_I2C->STAT;
debugOrangeLow; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
if (MS5611state>I2CCtrlUppLimit) {
mainStatus&= ~mstatMS5611PrValread;
MS5611state= I2CCtrlRestart;
}
if (!(cpyI2CSTAT & MSTPENDING)) {
writeI2CErrRecord(2);
_Panic_; // still write a panic handler <<<<<<<<<
}
if (cpyI2CSTAT & EVENTTIMEOUT
|| ((cpyI2CSTAT & MSTSTATEMASK)==MSTADDRNAK)
|| ((cpyI2CSTAT & MSTSTATEMASK)==MSTDATANAK)) { // ugly
//debugRedLow;//<<<<<<<<<<<<<<<<<<
writeI2CErrRecord(0);
setLED_BlinkPattern(LED_Blink_2Blinks);
LPC_I2C->STAT |= EVENTTIMEOUT;
LPC_I2C->INTENCLR= (MSTPENDINGEN | EVENTTIMEOUTEN);
LPC_I2C->MSTCTL= MSTSTOP;
mainStatus&= ~mstatMS5611PrValread;
cnt= 6;
MS5611state= I2CCtrlRestart;
} else {
(*I2CCtrlTable[MS5611state])();
MS5611state++;
}
debugOrangeHigh; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
return;
}
void writeI2CErrRecord(unsigned int v) {
int i= (xErrRecord++) & 0x07;
ErrRecord.tag= ErrRecordI2C+v;
ErrRecord.subtag= MS5611state;
ErrRecord.SysTickStamp= (uint16_t)(SysTickCounter);
ErrRecord.v.I2C.I2CSTAT= cpyI2CSTAT;
ErrRecord.v.I2C.currI2Cdata= input;
}
|