// 功能:Rust、no_std、ESP32C3、LCD1602、I2C、LED
// 作者:WuFengSheng
// 日期:2024-09-27
// 博客:https://blog.csdn.net/weixin_42607526
// 文章:https://blog.csdn.net/weixin_42607526/article/details/142602505
// Github:https://github.com/WuFengSheng/esp-rs-demo

#![no_std]
#![no_main]
#![allow(dead_code)]

use esp_backtrace as _;
use esp_hal::{
    clock::ClockControl, 
    delay::Delay, 
    peripherals::{Peripherals, I2C0}, 
    Blocking,
    prelude::*, 
    system::SystemControl, 
    gpio::{Io, Level, Output},
    i2c::I2C
};

const I2C_ADDR: u8 = 0x27; // LCD1602的I2C地址

// LCD 指令
const LCD_CMD_CLEAR: u8 = 0x01;
const LCD_CMD_HOME: u8 = 0x02;
const LCD_CMD_ENTRY_MODE: u8 = 0x04;
const LCD_CMD_DISPLAY_CONTROL: u8 = 0x08;
const LCD_CMD_FUNCTION_SET: u8 = 0x20;
const LCD_CMD_SET_DDRAM_ADDR: u8 = 0x80;

// LCD 控制位
const LCD_BACKLIGHT: u8 = 0x08;
const ENABLE: u8 = 0x04;
const RW_WRITE: u8 = 0x00;
const RS_DATA: u8 = 0x01;
const RS_COMMAND: u8 = 0x00;

#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take();
    let system = SystemControl::new(peripherals.SYSTEM);

    let clocks = ClockControl::max(system.clock_control).freeze();
    let delay = Delay::new(&clocks);

    esp_println::logger::init_logger_from_env();

    // Set GPIO0 as an output, and set its state high initially.
    let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
    let mut led = Output::new(io.pins.gpio0, Level::High);

    // 初始化 I2C
    let mut i2c: I2C<I2C0, Blocking> = I2C::new(peripherals.I2C0, io.pins.gpio6, io.pins.gpio5, 400.kHz(), &clocks);
    log::info!("The type of i2c is: {}", core::any::type_name_of_val(&i2c));

    // 初始化 LCD
    lcd_init(&mut i2c);
    lcd_write_string(&mut i2c, "Hello, World!");

    loop {
        log::info!("Hello world! \n");
        led.toggle();
        delay.delay(500.millis());
    }
}

fn lcd_init(i2c: &mut I2C<I2C0, Blocking>) {
    // 设置 4-bit 模式
    lcd_command(i2c, 0x33); // 初始化指令
    lcd_command(i2c, 0x32); // 设置4-bit模式
    lcd_command(i2c, LCD_CMD_FUNCTION_SET | 0x08); // 2行显示
    lcd_command(i2c, LCD_CMD_DISPLAY_CONTROL | 0x0F); // 打开显示,有光标,会闪烁
    lcd_command(i2c, LCD_CMD_CLEAR); // 清屏
    lcd_command(i2c, LCD_CMD_ENTRY_MODE | 0x02); // 设置光标移动方向
}

fn lcd_command(i2c: &mut I2C<I2C0, Blocking>, command: u8) {
    lcd_write(i2c, command, RS_COMMAND);
}

fn lcd_data(i2c: &mut I2C<I2C0, Blocking>, data: u8) {
    lcd_write(i2c, data, RS_DATA);
}

fn lcd_write(i2c: &mut I2C<I2C0, Blocking>, data: u8, mode: u8) {
    let high_nibble = data & 0xF0;
    let low_nibble = (data << 4) & 0xF0;
    lcd_send_nibble(i2c, high_nibble | mode);
    lcd_send_nibble(i2c, low_nibble | mode);
}

fn lcd_send_nibble(i2c: &mut I2C<I2C0, Blocking>, nibble: u8) {
    // 发送高4位
    let data = nibble | LCD_BACKLIGHT;
    let _ = i2c.write(I2C_ADDR, &[data | ENABLE]);
    let _ = i2c.write(I2C_ADDR, &[data & !ENABLE]);
}

fn lcd_write_string(i2c: &mut I2C<I2C0, Blocking>, s: &str) {
    for c in s.chars() {
        lcd_data(i2c, c as u8);
    }
}