#include "i2c_lcd.h" #include #include /* Adjust to your module’s I²C address (often 0x27 or 0x3F) */ #define LCD_I2C_ADDR 0x27 /* PCF8574 pin ↔ LCD pin mapping (change if your backpack is different) */ #define P_RS 0x01 /* P0 → RS */ #define P_RW 0x02 /* P1 → RW */ #define P_EN 0x04 /* P2 → E */ #define P_BL 0x08 /* P3 → Backlight */ #define DATA_MASK 0xF0 /** spin until BUS not busy, or timeoutCycles hits zero */ static bool waitI2CBusyClear(uint32_t timeoutCycles) { /* keep looping while the BUSY bit remains set */ while ((EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE) == EUSCI_B_I2C_BUS_BUSY) && --timeoutCycles) ; return (timeoutCycles != 0); } static void i2cWriteByte(uint8_t b) { /* make sure bus is free */ if (!waitI2CBusyClear(50000)) return; /* generate START + send first byte */ EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, b); /* wait until the byte is actually out and bus goes busy→idle */ if (!waitI2CBusyClear(50000)) return; /* STOP to free the bus again */ EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B0_BASE); /* optional small delay for the expander to settle */ __delay_cycles(50); } static void pulse(uint8_t data) { i2cWriteByte(data | P_EN); __delay_cycles(200); i2cWriteByte(data & ~P_EN); __delay_cycles(200); } /* send upper or lower nibble with control bits */ static void write4(uint8_t nibble, uint8_t control) { uint8_t out = (nibble << 4) & DATA_MASK; out |= control | P_BL; i2cWriteByte(out); pulse(out); } static void send(uint8_t value, uint8_t mode) { write4(value >> 4, mode); write4(value & 0x0F, mode); } /** Commands (mode = 0), data (mode = P_RS) */ static inline void cmd(uint8_t c) { send(c, 0); } static inline void data(uint8_t d){ send(d, P_RS); } void lcdInit(void) { /* 1) Init the MSP I²C peripheral on B0 (P1.6=SCL, P1.7=SDA) */ GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_P1, GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION ); EUSCI_B_I2C_initMasterParam i2cCfg = { .selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK, .i2cClk = CS_getSMCLK(), .dataRate = EUSCI_B_I2C_SET_DATA_RATE_100KBPS, .byteCounterThreshold = 1, .autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP, }; EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &i2cCfg); EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE, LCD_I2C_ADDR); EUSCI_B_I2C_enable(EUSCI_B0_BASE); __delay_cycles(50000); /* 4-bit init sequence */ write4(0x03, 0); __delay_cycles(50000); write4(0x03, 0); __delay_cycles(50000); write4(0x03, 0); __delay_cycles(50000); write4(0x02, 0); __delay_cycles(50000); /* function set: 2-line, 5×8 dots */ cmd(0x28); /* display off */ cmd(0x08); /* clear */ cmd(0x01); __delay_cycles(20000); /* entry mode: cursor moves right */ cmd(0x06); /* display on, cursor off, blink off */ cmd(0x0C); } void lcdPrint(const char *str) { while (*str) { data((uint8_t)*str++); } } void lcdPutChar(char c) { data((uint8_t)c); } void lcdSetCursor(uint8_t row, uint8_t col) { /* DDRAM addresses: 0x00 + col for row0, 0x40 + col for row1 */ uint8_t addr = (row == 0 ? 0x00 : 0x40) + col; cmd(0x80 | addr); }