#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h"

// Definino pinos e enderecos
#define LCD_ADDR 0x27
#define DS1307_ADDR 0x68
#define I2C_PORT i2c0
#define SDA_PIN 0
#define SCL_PIN 1

// Comandos para o LCD
#define LCD_CLEAR 0x01
#define LCD_CMD 0
#define LCD_DATA 1
#define LCD_ENABLE 0x04
#define LCD_BACKLIGHT 0x08

//
// FUNCOES PARA O LCD
//
void lcd_write(uint8_t val, uint8_t mode)
  {
  uint8_t data[] = { (val & 0xF0) | mode | LCD_BACKLIGHT, ((val << 4) & 0xF0) | mode | LCD_BACKLIGHT };
  for (int i = 0; i < 2; i++)
    {
    i2c_write_blocking(I2C_PORT, LCD_ADDR, &data[i], 1, false);
    data[i] |= LCD_ENABLE;
    i2c_write_blocking(I2C_PORT, LCD_ADDR, &data[i], 1, false);
    data[i] &= ~LCD_ENABLE;
    i2c_write_blocking(I2C_PORT, LCD_ADDR, &data[i], 1, false);
    }
  }

void lcd_command(uint8_t cmd)
  {
  lcd_write(cmd, LCD_CMD);
  }

void lcd_data(uint8_t data)
  {
  lcd_write(data, LCD_DATA);
  }

void lcd_clear()
  {
  lcd_command(LCD_CLEAR);
  }

void lcd_putstr(const char *str)
  {
  while (*str)
    {
    lcd_data(*str++);
    }
  }

void lcd_set_cursor(uint8_t row, uint8_t col)
  {
  lcd_command(0x80 | (row * 0x40 + col));
  }


uint8_t bcd_to_dec(uint8_t val)
  {
  return (val >> 4) * 10 + (val & 0xF);
  }

uint8_t dec_to_bcd(uint8_t val)
  {
  return (val / 10 << 4) | (val % 10);
  }
//
// FUNCOES PARA O LCD
//

// Funcao para iniciar o i2c
void setup_i2c()
  {
  // Inicia o i2c nos pinos definidos
  i2c_init(I2C_PORT, 120000);
  gpio_set_function(SDA_PIN, GPIO_FUNC_I2C);
  gpio_set_function(SCL_PIN, GPIO_FUNC_I2C);
  gpio_pull_up(SDA_PIN);
  gpio_pull_up(SCL_PIN);
  }

// Funcao para iniciar o lcd
void setup_lcd()
  {
  // Envia comandos para iniciar o lcd
  lcd_command(0x03);
  lcd_command(0x02);
  lcd_command(0x28);
  lcd_command(0x0C);
  lcd_clear();
  lcd_command(0x06);
  }

// Funcao para atualizar a hora no display
void ds1307_read_data()
  {
  // Le a informacao de tempo
  uint8_t reg = 0x00, data[7];
  i2c_write_blocking(I2C_PORT, DS1307_ADDR, &reg, 1, true);
  i2c_read_blocking(I2C_PORT, DS1307_ADDR, data, sizeof(data), false);

  // Colocando a data no display
  char buffer[17];
  snprintf(buffer, sizeof(buffer), "Data->%02d/%02d/20%02d", bcd_to_dec(data[4]), bcd_to_dec(data[5]), bcd_to_dec(data[6]));
  lcd_clear();
  lcd_set_cursor(0, 0);
  lcd_putstr(buffer);

  // Colocando a hora no display
  snprintf(buffer, sizeof(buffer), "Hora->%02d:%02d:%02d", bcd_to_dec(data[2]), bcd_to_dec(data[1]), bcd_to_dec(data[0]));
  lcd_set_cursor(1, 0);
  lcd_putstr(buffer);

  // Colocando a data e hora no console
  printf("Data: %02d/%02d/20%02d - Hora: %02d:%02d:%02d\n", 
  bcd_to_dec(data[4]), bcd_to_dec(data[5]), bcd_to_dec(data[6]),
  bcd_to_dec(data[2]), bcd_to_dec(data[1]), bcd_to_dec(data[0]));
  }

// Funcao para definir a hora no display
void ds1307_set_data()
  {
  uint8_t data[] =
    {
    0x00,
    dec_to_bcd(0),  // Segundo 0
    dec_to_bcd(27), // Minuto 27
    dec_to_bcd(13), // Hora 13
    dec_to_bcd(2),  // Dia da Semana 2
    dec_to_bcd(24), // Dia do Mes 24
    dec_to_bcd(9),  // Mes 9
    dec_to_bcd(24)  // Ano 2024
    };

  //Coloca data no RTC
  i2c_write_blocking(I2C_PORT, DS1307_ADDR, data, sizeof(data), false);

  //Coloca a data no display
  ds1307_read_data();
  }

// Funcao principal
int main()
  {
  // Inicia os requisitos
  stdio_init_all();
  setup_i2c();
  setup_lcd();

  // Define data para o RTC
  ds1307_set_data();

  // Loop principal
  while (true)
    {
    //Atualiza a data no display a cada 5 segundos
    sleep_ms(5000);
    ds1307_read_data();
    }
  }
GND5VSDASCLSQWRTCDS1307+