#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hardware/adc.h"
#include "ssd1306.h"
// ------------------ DEFINIÇÕES ------------------
#define SDA_PIN 20
#define SCL_PIN 21
#define OLED_ADDR 0x3C
#define LCD_ADDR 0x27
#define JOY_X 27
#define JOY_Y 28
#define LCD_BACKLIGHT 0x08
#define LCD_ENABLE_BIT 0x04
#define LCD_COMMAND 0
#define LCD_CHARACTER 1
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_FUNCTIONSET 0x20
#define LCD_SETDDRAMADDR 0x80
#define LCD_ENTRYLEFT 0x02
#define LCD_DISPLAYON 0x04
#define LCD_2LINE 0x08
// ------------------ VARIÁVEIS ------------------
ssd1306_t disp;
static int lcd_addr = LCD_ADDR;
// ------------------ FUNÇÕES LCD ------------------
void i2c_write_byte(uint8_t val) {
i2c_write_blocking(i2c0, lcd_addr, &val, 1, false);
}
void lcd_toggle_enable(uint8_t val) {
sleep_us(500);
i2c_write_byte(val | LCD_ENABLE_BIT);
sleep_us(500);
i2c_write_byte(val & ~LCD_ENABLE_BIT);
sleep_us(500);
}
void lcd_send_byte(uint8_t val, int mode) {
uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;
uint8_t low = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;
i2c_write_byte(high);
lcd_toggle_enable(high);
i2c_write_byte(low);
lcd_toggle_enable(low);
}
void lcd_send_cmd(uint8_t cmd) {
lcd_send_byte(cmd, LCD_COMMAND);
}
void lcd_send_data(uint8_t data) {
lcd_send_byte(data, LCD_CHARACTER);
}
void lcd_init() {
sleep_ms(50);
lcd_send_cmd(0x03);
lcd_send_cmd(0x03);
lcd_send_cmd(0x03);
lcd_send_cmd(0x02);
lcd_send_cmd(LCD_FUNCTIONSET | LCD_2LINE);
lcd_send_cmd(LCD_DISPLAYCONTROL | LCD_DISPLAYON);
lcd_send_cmd(LCD_ENTRYMODESET | LCD_ENTRYLEFT);
lcd_send_cmd(LCD_CLEARDISPLAY);
sleep_ms(2);
}
void lcd_clear() {
lcd_send_cmd(LCD_CLEARDISPLAY);
sleep_ms(2);
}
void lcd_set_cursor(int line, int pos) {
int addr = (line == 0 ? 0x80 : 0xC0) + pos;
lcd_send_cmd(addr);
}
void lcd_string(const char *str) {
while (*str) {
lcd_send_data(*str++);
}
}
// ------------------ CONFIGURAÇÃO ------------------
void setup_I2C() {
i2c_init(i2c0, 100 * 1000);
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);
}
// ------------------ DESENHO CÍRCULO PREENCHIDO ------------------
void draw_filled_circle(ssd1306_t *disp, int x0, int y0, int r) {
for (int y = -r; y <= r; y++) {
for (int x = -r; x <= r; x++) {
if (x*x + y*y <= r*r) {
ssd1306_draw_pixel(disp, x0 + x, y0 + y);
}
}
}
}
// ------------------ MAIN ------------------
int main() {
stdio_init_all();
setup_I2C();
adc_init();
adc_gpio_init(JOY_X);
adc_gpio_init(JOY_Y);
lcd_init();
// OLED
disp.external_vcc = false;
ssd1306_init(&disp, 128, 64, OLED_ADDR, i2c0);
ssd1306_clear(&disp);
while (true) {
// Leitura dos eixos analógicos
adc_select_input(1);
uint16_t x_raw = adc_read();
adc_select_input(2);
uint16_t y_raw = adc_read();
int16_t x = x_raw - 2048;
int16_t y = y_raw - 2048;
char direcao[16] = "Centro";
if (y > 800 && abs(x) < 600) strcpy(direcao, "Norte");
else if (y < -800 && abs(x) < 600) strcpy(direcao, "Sul");
else if (x > 800 && abs(y) < 600) strcpy(direcao, "Oeste");
else if (x < -800 && abs(y) < 600) strcpy(direcao, "Leste");
else if (x > 600 && y > 600) strcpy(direcao, "Noroeste");
else if (x < -600 && y > 600) strcpy(direcao, "Nordeste");
else if (x > 600 && y < -600) strcpy(direcao, "Sudoeste");
else if (x < -600 && y < -600) strcpy(direcao, "Sudeste");
// --- LCD ---
lcd_clear();
lcd_set_cursor(0, 0);
char linha1[17];
sprintf(linha1, "X:%4d Y:%4d", x_raw, y_raw);
lcd_string(linha1);
lcd_set_cursor(1, 0);
lcd_string(direcao);
// --- OLED ---
ssd1306_clear(&disp);
int cx = 64, cy = 32; // centro
int len = 20; // comprimento da seta
int thick = 2; // espessura
// ponto central (substitui o draw_filled_circle)
draw_filled_circle(&disp, cx, cy, 3);
// direção da seta
int dx = 0, dy = 0;
if (strcmp(direcao, "Norte") == 0) { dx = 0; dy = -len; }
else if (strcmp(direcao, "Sul") == 0) { dx = 0; dy = len; }
else if (strcmp(direcao, "Leste") == 0) { dx = len; dy = 0; }
else if (strcmp(direcao, "Oeste") == 0) { dx = -len; dy = 0; }
else if (strcmp(direcao, "Nordeste") == 0){ dx = len; dy = -len; }
else if (strcmp(direcao, "Noroeste") == 0){ dx = -len; dy = -len; }
else if (strcmp(direcao, "Sudeste") == 0){ dx = len; dy = len; }
else if (strcmp(direcao, "Sudoeste") == 0){ dx = -len; dy = len; }
// seta mais grossa
for (int i = -thick; i <= thick; i++) {
ssd1306_draw_line(&disp, cx + i, cy, cx + dx + i, cy + dy);
ssd1306_draw_line(&disp, cx, cy + i, cx + dx, cy + dy + i);
}
// texto grande e centralizado
ssd1306_draw_string(&disp, 25, 2, 2, direcao);
ssd1306_show(&disp);
sleep_ms(200);
}
}