133 lines
3.4 KiB
C
133 lines
3.4 KiB
C
#include "i2c_lcd.h"
|
||
#include <driverlib.h>
|
||
#include <msp430.h>
|
||
|
||
/* 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);
|
||
}
|