ESR-2025/i2c.c

149 lines
3.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ========================================================================== */
/* I2C.c */
/* ========================================================================== */
/**
* @file I2C.c
* @author wehrberger
* @date 31.05.2025
*
* @brief Implementierung eines minimalen blockierenden I²C-Master-Treibers.
*/
#include "i2c.h"
#include "msp430fr2355.h"
#include <stdint.h>
#include <stdbool.h>
/** Pointer auf das aktuell zu übertragende Byte. */
static char *packet;
/** Index des nächsten zu übertragenden Bytes. */
static unsigned int dataCount;
/** Anzahl der Bytes in @ref packet. */
static unsigned int packetLength;
/** Speicher für das zuletzt vom ISR empfangene Byte. */
static char dataIn;
static volatile bool i2cDone = false;
void I2C_init(void)
{
// USCI in Reset setzen um Konfiguration zu ermöglichen
UCB0CTLW0 |= UCSWRST;
// SMCLK wählen und durch 20 teilen → 50 kHz SCL
UCB0CTLW0 |= UCSSEL_3;
UCB0BRW = 20;
// I²C Master, 7-Bit Adressierung
UCB0CTLW0 |= UCMODE_3 | UCMST;
// Automatischer STOP nach Byte-Zähler (UCB0TBCNT) erreicht Null
UCB0CTLW1 |= UCASTP_2;
// Port-Mapping: P1.2 = SDA, P1.3 = SCL
P1SEL1 &= ~(BIT2 | BIT3);
P1SEL0 |= BIT2 | BIT3;
// Modul aktivieren
UCB0CTLW0 &= ~UCSWRST;
// Interrupts: RX, TX, STOP, NACK
UCB0IE |= UCRXIE0 | UCTXIE0 | UCSTPIE | UCNACKIE;
__enable_interrupt();
}
void I2C_write(uint8_t slaveAddress, char data[], uint8_t length)
{
UCB0I2CSA = slaveAddress;
packet = data;
packetLength = length;
dataCount = 0;
// Master-Transmit-Modus
UCB0CTLW0 |= UCTR;
UCB0TBCNT = length;
// START generieren, dann schlafen bis STOP
UCB0CTLW0 |= UCTXSTT;
i2cDone = false;
while (!i2cDone)
{
LPM3; // Warten auf STOP → ISR weckt uns auf
}
}
char I2C_read_reg(uint8_t slaveAddress, uint8_t registerAddress)
{
// Registeradresse zuerst senden
char addressBuffer[1] = {registerAddress};
I2C_write(slaveAddress, addressBuffer, 1);
// In Empfangsmodus wechseln und 1 Byte anfordern
UCB0CTLW0 &= ~UCTR;
UCB0TBCNT = 1;
UCB0CTLW0 |= UCTXSTT; // Repeated START
i2cDone = false;
while (!i2cDone)
{
LPM3; // Warten auf STOP → ISR weckt uns auf
}
return dataIn;
}
/* ========================================================================== */
/* Interrupt Service Routine */
/* ========================================================================== */
/**
* @brief Vereinheitlichte ISR für alle USCI_B0 I²C-Ereignisse.
*
* Nur vier Interrupt-Ursachen werden derzeit behandelt:
* - UCNACKIFG : Kennzeichnet fehlendes ACK (nur Debug-Hook)
* - UCSTPIFG : STOP erkannt → LPM3 verlassen
* - UCRXIFG0 : Ein Byte empfangen
* - UCTXIFG0 : Sendepuffer bereit für nächstes Byte
*
* Alle anderen Ursachen fallen durch zum default.
*/
#pragma vector = EUSCI_B0_VECTOR
__interrupt void EUSCI_B0_I2C_ISR(void)
{
switch (__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
{
case USCI_I2C_UCNACKIFG:
// NACK → CPU aufwecken (LPM3 verlassen)
i2cDone = true;
__bic_SR_register_on_exit(LPM3_bits);
break;
case USCI_I2C_UCSTPIFG:
// STOP → CPU aufwecken (LPM3 verlassen)
i2cDone = true;
__bic_SR_register_on_exit(LPM3_bits);
break;
case USCI_I2C_UCRXIFG0:
// Empfangenes Byte speichern
dataIn = UCB0RXBUF;
break;
case USCI_I2C_UCTXIFG0:
// Nächstes Datenbyte senden oder Übertragung beenden
UCB0TXBUF = packet[dataCount++];
if (dataCount >= packetLength)
dataCount = 0; // Für nächste Transaktion zurücksetzen
break;
default:
// Unbehandelter Vektor nichts zu tun
break;
}
}