#include "stm32f1xx.h"
#include <stdint.h>
#include <string.h>
// Konfigurasi I2C (PB6=SCL, PB7=SDA)
#define LCD_ADDR (0x27 << 1) // Alamat I2C LCD (0x27 dalam 7-bit, digeser ke 8-bit)
#define I2C_TIMEOUT 100
// Fungsi delay sederhana
void delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms * 4000; i++) {
__asm__("nop");
}
}
// Inisialisasi I2C1
void I2C_Init() {
// 1. Enable clock untuk GPIOB dan I2C1
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
// 2. Konfigurasi PB6 (SCL) dan PB7 (SDA) sebagai alternate open-drain
GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_CNF7);
GPIOB->CRL |= (GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_1); // Alternate open-drain
GPIOB->CRL |= (GPIO_CRL_MODE6 | GPIO_CRL_MODE7); // Output mode 50MHz
// 3. Konfigurasi I2C1
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C sebelum konfigurasi
I2C1->CR2 = (36 << 0); // Freq clock (MHz) - misal 36MHz APB1
I2C1->CCR = 180; // Clock 100kHz (36MHz / (2*180) = 100kHz)
I2C1->TRISE = 37; // Rise time (1000ns / (1/36MHz) + 1)
I2C1->CR1 |= I2C_CR1_PE; // Enable I2C
}
// Kirim data via I2C
void I2C_Write(uint8_t addr, uint8_t data) {
// 1. Tunggu sampai I2C tidak sibuk
while (I2C1->SR2 & I2C_SR2_BUSY);
// 2. Generate START condition
I2C1->CR1 |= I2C_CR1_START;
while (!(I2C1->SR1 & I2C_SR1_SB));
// 3. Kirim alamat slave (mode write)
I2C1->DR = addr;
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2; // Clear ADDR flag
// 4. Kirim data
while (!(I2C1->SR1 & I2C_SR1_TXE));
I2C1->DR = data;
while (!(I2C1->SR1 & I2C_SR1_BTF));
// 5. Generate STOP condition
I2C1->CR1 |= I2C_CR1_STOP;
}
// Kirim command ke LCD
void LCD_SendCmd(uint8_t cmd) {
uint8_t upper = (cmd & 0xF0) | 0x04; // EN=1, RS=0
uint8_t lower = (cmd << 4) | 0x04; // EN=1, RS=0
I2C_Write(LCD_ADDR, upper);
delay_ms(1);
I2C_Write(LCD_ADDR, upper & ~0x04); // EN=0
delay_ms(1);
I2C_Write(LCD_ADDR, lower);
delay_ms(1);
I2C_Write(LCD_ADDR, lower & ~0x04); // EN=0
delay_ms(1);
}
// Kirim data ke LCD
void LCD_SendData(uint8_t data) {
uint8_t upper = (data & 0xF0) | 0x05; // EN=1, RS=1
uint8_t lower = (data << 4) | 0x05; // EN=1, RS=1
I2C_Write(LCD_ADDR, upper);
delay_ms(1);
I2C_Write(LCD_ADDR, upper & ~0x04); // EN=0
delay_ms(1);
I2C_Write(LCD_ADDR, lower);
delay_ms(1);
I2C_Write(LCD_ADDR, lower & ~0x04); // EN=0
delay_ms(1);
}
// Inisialisasi LCD
void LCD_Init() {
delay_ms(50);
// Mode 4-bit
LCD_SendCmd(0x33);
LCD_SendCmd(0x32);
LCD_SendCmd(0x28); // 4-bit, 2 line, 5x8 font
LCD_SendCmd(0x0C); // Display on, cursor off
LCD_SendCmd(0x06); // Increment cursor
LCD_SendCmd(0x01); // Clear display
delay_ms(2);
}
// Tulis string ke LCD
void LCD_Print(char *str) {
while (*str) {
LCD_SendData(*str++);
}
}
int main(void) {
// Inisialisasi
I2C_Init();
LCD_Init();
// Tampilkan pesan
LCD_SendCmd(0x80); // Pindah ke baris 1, kolom 0
LCD_Print("STM32 BluePill");
LCD_SendCmd(0xC0); // Pindah ke baris 2, kolom 0
LCD_Print("LCD I2C Bare-Metal");
while (1) {
// Loop utama
}
}