From 89c1857d572d1acc0e3f08b4e97c6bdf6ad45dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 10 Jun 2023 23:07:09 +0200 Subject: [PATCH] Initial commit, LCD, RE, Moisture, Keypad working, TODO I2C --- .gitignore | 3 + CMakeLists.txt | 23 +++ Makefile | 14 ++ pico_sdk_import.cmake | 73 ++++++++ src/lcd.c | 396 ++++++++++++++++++++++++++++++++++++++++++ src/lcd.c.bak | 294 +++++++++++++++++++++++++++++++ src/lcd.h | 62 +++++++ src/main.c | 161 +++++++++++++++++ src/pinout.h | 29 ++++ 9 files changed, 1055 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Makefile create mode 100644 pico_sdk_import.cmake create mode 100644 src/lcd.c create mode 100644 src/lcd.c.bak create mode 100644 src/lcd.h create mode 100644 src/main.c create mode 100644 src/pinout.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46cb5a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +cmake-build-* +.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ddc5b6f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.13) + +# initialize the SDK based on PICO_SDK_PATH +# note: this must happen before project() +include(pico_sdk_import.cmake) + +project(zavlaha) + +# initialize the Raspberry Pi Pico SDK +pico_sdk_init() + +# rest of your project + +add_executable(zavlaha + src/main.c + src/lcd.c +) + +# Add pico_stdlib library which aggregates commonly used features +target_link_libraries(zavlaha pico_stdlib hardware_i2c hardware_adc hardware_irq) + +# create map/bin/hex/uf2 file in addition to ELF. +pico_add_extra_outputs(zavlaha) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cdffbba --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +.PHONY: build + +TARGET=build/zavlaha.elf +TARGET_U2F=build/zavlaha.uf2 + +build: + cd build && cmake .. && make -j + arm-none-eabi-size ${TARGET} + +upload: build $(TARGET_U2F) + cp $(TARGET_U2F) /run/media/ondra/RPI-RP2/ + +flash: build + pico-openocd -f target/rp2040.cfg -c "adapter speed 1000" -c "program ${TARGET} verify reset exit" diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..65f8a6f --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + endif () + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/src/lcd.c b/src/lcd.c new file mode 100644 index 0000000..f4d4874 --- /dev/null +++ b/src/lcd.c @@ -0,0 +1,396 @@ +#include +#include + +#include +#include "lcd.h" +#include "pinout.h" + +#define LCD_D7 PIN_LCD_D7 +#define LCD_D6 PIN_LCD_D6 +#define LCD_D5 PIN_LCD_D5 +#define LCD_D4 PIN_LCD_D4 +#define LCD_RS PIN_LCD_RS +#define LCD_RW PIN_LCD_RW +#define LCD_E PIN_LCD_E + +#define set_pin(num, val) gpio_put((num), (val)) +#define get_pin(num) gpio_get((num)) +#define pin_high(num) set_pin((num), 1) +#define pin_low(num) set_pin((num), 0) +#define get_bit(a, pos) (((a) >> (pos)) & 1) +#define as_output(pin) gpio_set_dir((pin), 1) +#define as_input_pu(pin) do { gpio_set_dir((pin), 0); gpio_pull_up((pin)); } while (0) +#define _delay_ms(ms) sleep_ms((ms)) +#define _delay_us(us) sleep_us((us)) +#define _delay_ns(ns) sleep_us(1 + ((ns) / 1000)) // TODO + +// Start address of rows +const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54}; + +// Internal prototypes +void _lcd_mode_r(); + +void _lcd_mode_w(); + +void _lcd_clk(); + +void _lcd_wait_bf(); + +void _lcd_write_byte(uint8_t bb); + +uint8_t _lcd_read_byte(); + +void lcd_command(uint8_t bb); + + +// Write utilities +#define _lcd_write_low(bb) _lcd_write_nibble((bb) & 0x0F) +#define _lcd_write_high(bb) _lcd_write_nibble(((bb) & 0xF0) >> 4) + +#define _lcd_write_nibble(nib) \ + gpio_put_masked((1 << PIN_LCD_D7) | (1 << PIN_LCD_D6) | (1 << PIN_LCD_D5) | (1 << PIN_LCD_D4), (nib) << PIN_LCD_D4) + +//#define _lcd_write_nibble(nib) do { \ +// set_pin(LCD_D7, get_bit((nib), 3)); \ +// set_pin(LCD_D6, get_bit((nib), 2)); \ +// set_pin(LCD_D5, get_bit((nib), 1)); \ +// set_pin(LCD_D4, get_bit((nib), 0)); \ +//} while(0) + + + +// --- Commands --- + +// Clear screen (reset) +#define LCD_CLEAR 0b00000001 +// Move cursor to (0,0), unshift... +#define LCD_HOME 0b00000010 + +// Set mode: Increment + NoShift +#define LCD_MODE_INC 0b00000110 +// Set mode: Increment + Shift +#define LCD_MODE_INC_SHIFT 0b00000111 + +// Set mode: Decrement + NoShift +#define LCD_MODE_DEC 0b00000100 +// Set mode: Decrement + Shift +#define LCD_MODE_DEC_SHIFT 0b00000101 + +// Disable display (data remains untouched) +#define LCD_DISABLE 0b00001000 + +// Disable cursor +#define LCD_CURSOR_NONE 0b00001100 +// Set cursor to still underscore +#define LCD_CURSOR_BAR 0b00001110 +// Set cursor to blinking block +#define LCD_CURSOR_BLINK 0b00001101 +// Set cursor to both of the above at once +#define LCD_CURSOR_BOTH (LCD_CURSOR_BAR | LCD_CURSOR_BLINK) + +// Move cursor +#define LCD_MOVE_LEFT 0b00010000 +#define LCD_MOVE_RIGHT 0b00010100 + +// Shift display +#define LCD_SHIFT_LEFT 0b00011000 +#define LCD_SHIFT_RIGHT 0b00011100 + +// Set iface to 5x7 font, 1-line +#define LCD_IFACE_4BIT_1LINE 0b00100000 +#define LCD_IFACE_8BIT_1LINE 0b00110000 +// Set iface to 5x7 font, 2-line +#define LCD_IFACE_4BIT_2LINE 0b00101000 +#define LCD_IFACE_8BIT_2LINE 0b00111000 + + + +// 0 W, 1 R +bool _lcd_mode; + +struct { + uint8_t x; + uint8_t y; +} _pos; + +enum { + TEXT = 0, + CG = 1 +} _addrtype; + + +/** Initialize the display */ +void lcd_init() +{ + // configure pins as output + as_output(LCD_E); + as_output(LCD_RW); + as_output(LCD_RS); + _lcd_mode = 1; // force data pins to output + _lcd_mode_w(); + + // Magic sequence to invoke Cthulhu (or enter 4-bit mode) + _delay_ms(16); + _lcd_write_nibble(0b0011); + _lcd_clk(); + _delay_ms(5); + _lcd_clk(); + _delay_ms(5); + _lcd_clk(); + _delay_ms(5); + _lcd_write_nibble(0b0010); + _lcd_clk(); + _delay_us(100); + + // Configure the display + lcd_command(LCD_IFACE_4BIT_2LINE); + lcd_command(LCD_DISABLE); + lcd_command(LCD_CLEAR); + lcd_command(LCD_MODE_INC); + + // mark as enabled + lcd_enable(); + + _pos.x = 0; + _pos.y = 0; + _addrtype = TEXT; +} + + +/** Send a pulse on the ENABLE line */ +void _lcd_clk() +{ + pin_high(LCD_E); + _delay_ns(450); + pin_low(LCD_E); + _delay_ns(450); +} + + +/** Enter READ mode */ +void _lcd_mode_r() +{ + if (_lcd_mode == 1) { return; } // already in R mode + + pin_high(LCD_RW); + + as_input_pu(LCD_D7); + as_input_pu(LCD_D6); + as_input_pu(LCD_D5); + as_input_pu(LCD_D4); + + _lcd_mode = 1; +} + + +/** Enter WRITE mode */ +void _lcd_mode_w() +{ + if (_lcd_mode == 0) { return; } // already in W mode + + pin_low(LCD_RW); + + as_output(LCD_D7); + as_output(LCD_D6); + as_output(LCD_D5); + as_output(LCD_D4); + + _lcd_mode = 0; +} + + +/** Read a byte */ +uint8_t _lcd_read_byte() +{ + _lcd_mode_r(); + + uint8_t res = 0; + + _lcd_clk(); + res = (get_pin(LCD_D7) << 7) | (get_pin(LCD_D6) << 6) | (get_pin(LCD_D5) << 5) | (get_pin(LCD_D4) << 4); + + _lcd_clk(); + res |= (get_pin(LCD_D7) << 3) | (get_pin(LCD_D6) << 2) | (get_pin(LCD_D5) << 1) | (get_pin(LCD_D4) << 0); + + return res; +} + + +/** Write an instruction byte */ +void lcd_command(uint8_t bb) +{ + _lcd_wait_bf(); + pin_low(LCD_RS); // select instruction register + _lcd_write_byte(bb); // send instruction byte +} + + +/** Write a data byte */ +void lcd_write(uint8_t bb) +{ + if (_addrtype == TEXT) { + if (bb == '\r') { + // CR + _pos.x = 0; + lcd_xy(_pos.x, _pos.y); + return; + } + + if (bb == '\n') { + // LF + _pos.y++; + lcd_xy(_pos.x, _pos.y); + return; + } + + _pos.x++; + } + + _lcd_wait_bf(); + pin_high(LCD_RS); // select data register + _lcd_write_byte(bb); // send data byte +} + + +/** Read BF & Address */ +uint8_t lcd_read_bf_addr() +{ + pin_low(LCD_RS); + return _lcd_read_byte(); +} + + +/** Read CGRAM or DDRAM */ +uint8_t lcd_read() +{ + if (_addrtype == TEXT) { _pos.x++; } + + pin_high(LCD_RS); + return _lcd_read_byte(); +} + + +/** Write a byte using the 4-bit interface */ +void _lcd_write_byte(uint8_t bb) +{ + _lcd_mode_w(); // enter W mode + + _lcd_write_high(bb); + _lcd_clk(); + + _lcd_write_low(bb); + _lcd_clk(); +} + + +/** Wait until the device is ready */ +void _lcd_wait_bf() +{ + uint8_t d = 0; + while (d++ < 200 && lcd_read_bf_addr() & (1 << 7)) + _delay_us(1); +} + + +/** Send a string to LCD */ +void lcd_puts(char *str_p) +{ + char c; + while ((c = *str_p++)) { + lcd_putc(c); + } +} + +/** Sedn a char to LCD */ +void lcd_putc(const char c) +{ + lcd_write(c); +} + + +/** Set cursor position */ +void lcd_xy(const uint8_t x, const uint8_t y) +{ + _pos.x = x; + _pos.y = y; + lcd_addr(LCD_ROW_ADDR[y] + (x)); +} + + +uint8_t _lcd_old_cursor = CURSOR_NONE; +bool _lcd_enabled = false; + +/** Set LCD cursor. If not enabled, only remember it. */ +void lcd_cursor(uint8_t type) +{ + _lcd_old_cursor = (type & CURSOR_BOTH); + + if (_lcd_enabled) { lcd_command(LCD_CURSOR_NONE | _lcd_old_cursor); } +} + + +/** Display display (preserving cursor) */ +void lcd_disable() +{ + lcd_command(LCD_DISABLE); + _lcd_enabled = false; +} + + +/** Enable display (restoring cursor) */ +void lcd_enable() +{ + _lcd_enabled = true; + lcd_cursor(_lcd_old_cursor); +} + + +/** Go home */ +void lcd_home() +{ + lcd_command(LCD_HOME); + _pos.x = 0; + _pos.y = 0; + _addrtype = TEXT; +} + + +/** Clear the screen */ +void lcd_clear() +{ + lcd_command(LCD_CLEAR); + _pos.x = 0; + _pos.y = 0; + _addrtype = TEXT; + _delay_ms(1); // it tends to lose the first character otherwise! +} + + +/** Define a glyph */ +void lcd_glyph(const uint8_t index, const uint8_t *array) +{ + lcd_addr_cg(index * 8); + for (uint8_t i = 0; i < 8; ++i) { + lcd_write(array[i]); + } + + // restore previous position + lcd_xy(_pos.x, _pos.y); + _addrtype = TEXT; +} + + +/** Set address in CGRAM */ +void lcd_addr_cg(const uint8_t acg) +{ + _addrtype = CG; + lcd_command(0b01000000 | ((acg) & 0b00111111)); +} + + +/** Set address in DDRAM */ +void lcd_addr(const uint8_t add) +{ + _addrtype = TEXT; + lcd_command(0b10000000 | ((add) & 0b01111111)); +} diff --git a/src/lcd.c.bak b/src/lcd.c.bak new file mode 100644 index 0000000..b448a1c --- /dev/null +++ b/src/lcd.c.bak @@ -0,0 +1,294 @@ +#include +#include + +#include + +#include "pinout.h" +#include "lcd.h" + +// ------------------ PORTING ---------------------- + +#define lcd_mode_instr(bb) gpio_put(PIN_LCD_DC, 0) +#define lcd_mode_data(bb) gpio_put(PIN_LCD_DC, 1) + +#define lcd_mode_write(bb) gpio_put(PIN_LCD_RW, 0) +#define lcd_mode_read(bb) gpio_put(PIN_LCD_RW, 1) + +//// Write utilities +//#define lcd_write_nibble(nib) \ +// gpio_put_masked((1 << PIN_LCD_D7) | (1 << PIN_LCD_D6) | (1 << PIN_LCD_D5) | (1 << PIN_LCD_D4), (nib) << PIN_LCD_D4) + + +// Write utilities +#define lcd_write_nibble(nib) do { \ + gpio_put(PIN_LCD_D7, nib & 0b1000); \ + gpio_put(PIN_LCD_D6, nib & 0b0100); \ + gpio_put(PIN_LCD_D5, nib & 0b0010); \ + gpio_put(PIN_LCD_D4, nib & 0b0001); \ +} while (0) + +/** Wait until the device is ready */ +static void lcd_wait_ready() +{ + sleep_us(160); +} + +/** Send a pulse on the ENABLE line */ +static inline void lcd_clk() +{ + gpio_put(PIN_LCD_CLK, 1); + sleep_us(1); + gpio_put(PIN_LCD_CLK, 0); + sleep_us(1); +} + +// --------------------------------------------------------------- + + + + +// --- Commands --- + +// Clear screen (reset) +#define LCD_CLEAR 0b00000001 +// Move cursor to (0,0), unshift... +#define LCD_HOME 0b00000010 + +// Set mode: Increment + NoShift +#define LCD_MODE_INC 0b00000110 +// Set mode: Increment + Shift +#define LCD_MODE_INC_SHIFT 0b00000111 + +// Set mode: Decrement + NoShift +#define LCD_MODE_DEC 0b00000100 +// Set mode: Decrement + Shift +#define LCD_MODE_DEC_SHIFT 0b00000101 + +// Disable display (data remains untouched) +#define LCD_DISABLE 0b00001000 + +// Disable cursor +#define LCD_CURSOR_NONE 0b00001100 +// Set cursor to still underscore +#define LCD_CURSOR_BAR 0b00001110 +// Set cursor to blinking block +#define LCD_CURSOR_BLINK 0b00001101 +// Set cursor to both of the above at once +#define LCD_CURSOR_BOTH (LCD_CURSOR_BAR | LCD_CURSOR_BLINK) + +// Move cursor +#define LCD_MOVE_LEFT 0b00010000 +#define LCD_MOVE_RIGHT 0b00010100 + +// Shift display +#define LCD_SHIFT_LEFT 0b00011000 +#define LCD_SHIFT_RIGHT 0b00011100 + +// Set iface to 5x7 font, 1-line +#define LCD_IFACE_4BIT_1LINE 0b00100000 +#define LCD_IFACE_8BIT_1LINE 0b00110000 +// Set iface to 5x7 font, 2-line +#define LCD_IFACE_4BIT_2LINE 0b00101000 +#define LCD_IFACE_8BIT_2LINE 0b00111000 + +/** Write an instruction byte */ +static void lcd_command(uint8_t bb); + +/** Write a data byte, waiting for ready and handling CRLF if in text mode */ +static void lcd_write(uint8_t bb); + +/** Write a byte (handles just the data pins and clk) */ +static void lcd_write_byte(uint8_t bb); + +#define lcd_write_low(bb) lcd_write_nibble((bb) & 0x0F) +#define lcd_write_high(bb) lcd_write_nibble(((bb) & 0xF0) >> 4) + +static struct { + uint8_t x; + uint8_t y; +} lcd_pos; + +static enum { + LCD_ADDRTYPE_TEXT = 0, + LCD_ADDRTYPE_CG = 1 +} lcd_addrtype; + +static uint8_t lcd_old_cursor = CURSOR_NONE; +static bool lcd_enabled = false; + +// Start address of rows +const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54}; + +/** Initialize the display */ +void lcd_init() +{ + // Magic sequence to invoke Cthulhu (or enter 4-bit mode) + sleep_ms(100); + lcd_write_nibble(0b0011); + lcd_clk(); + sleep_ms(5); + lcd_clk(); + sleep_ms(1); + lcd_clk(); + sleep_ms(1); + lcd_write_nibble(0b0010); + lcd_clk(); + sleep_us(100); + + // Configure the display + lcd_command(LCD_IFACE_4BIT_2LINE); + lcd_command(LCD_DISABLE); + lcd_command(LCD_CLEAR); + lcd_command(LCD_MODE_INC); + + // mark as enabled + lcd_enable(); + + lcd_pos.x = 0; + lcd_pos.y = 0; + lcd_addrtype = LCD_ADDRTYPE_TEXT; +} + +/** Write an instruction byte */ +void lcd_command(uint8_t bb) +{ + lcd_wait_ready(); + lcd_mode_instr(); + lcd_write_byte(bb); // send instruction byte +} + + +/** Write a data byte */ +void lcd_write(uint8_t bb) +{ + if (lcd_addrtype == LCD_ADDRTYPE_TEXT) { + if (bb == '\r') { + // CR + lcd_pos.x = 0; + lcd_xy(lcd_pos.x, lcd_pos.y); + return; + } + + if (bb == '\n') { + // LF + lcd_pos.y++; + lcd_xy(lcd_pos.x, lcd_pos.y); + return; + } + + lcd_pos.x++; + } + + lcd_wait_ready(); + lcd_mode_data(); + lcd_write_byte(bb); // send data byte +} + + +/** Write a byte using the 4-bit interface */ +static void lcd_write_byte(uint8_t bb) +{ + lcd_write_high(bb); + lcd_clk(); + + lcd_write_low(bb); + lcd_clk(); +} + + +/** Send a string to LCD */ +void lcd_puts(char *str) +{ + char c; + while ((c = *str++)) { + lcd_putc(c); + } +} + +/** Sedn a char to LCD */ +void lcd_putc(const char c) +{ + lcd_write(c); +} + +/** Set cursor position */ +void lcd_xy(const uint8_t x, const uint8_t y) +{ + lcd_pos.x = x; + lcd_pos.y = y; + lcd_addr(LCD_ROW_ADDR[y] + (x)); +} + +/** Set LCD cursor. If not enabled, only remember it. */ +void lcd_set_cursor(uint8_t type) +{ + lcd_old_cursor = (type & CURSOR_BAR_BLINK); + + if (lcd_enabled) { lcd_command(LCD_CURSOR_NONE | lcd_old_cursor); } +} + + +/** Disable display (preserving cursor) */ +void lcd_disable() +{ + lcd_command(LCD_DISABLE); + lcd_enabled = false; +} + + +/** Enable display (restoring cursor) */ +void lcd_enable() +{ + lcd_enabled = true; + lcd_set_cursor(lcd_old_cursor); +} + + +/** Go home */ +void lcd_home() +{ + lcd_command(LCD_HOME); + lcd_pos.x = 0; + lcd_pos.y = 0; + lcd_addrtype = LCD_ADDRTYPE_TEXT; +} + + +/** Clear the screen */ +void lcd_clear() +{ + lcd_command(LCD_CLEAR); + lcd_pos.x = 0; + lcd_pos.y = 0; + lcd_addrtype = LCD_ADDRTYPE_TEXT; +} + + +/** Define a glyph */ +void lcd_glyph(const uint8_t index, const uint8_t *array) +{ + lcd_addr_cg(index * 8); + for (uint8_t i = 0; i < 8; ++i) { + lcd_write(array[i]); + } + + // restore previous position + lcd_xy(lcd_pos.x, lcd_pos.y); + lcd_addrtype = LCD_ADDRTYPE_TEXT; +} + + +/** Set address in CGRAM */ +void lcd_addr_cg(const uint8_t acg) +{ + lcd_addrtype = LCD_ADDRTYPE_CG; + lcd_command(0b01000000 | ((acg) & 0b00111111)); +} + + +/** Set address in DDRAM */ +void lcd_addr(const uint8_t add) +{ + lcd_addrtype = LCD_ADDRTYPE_TEXT; + lcd_command(0b10000000 | ((add) & 0b01111111)); +} diff --git a/src/lcd.h b/src/lcd.h new file mode 100644 index 0000000..c1e78e5 --- /dev/null +++ b/src/lcd.h @@ -0,0 +1,62 @@ +#pragma once + +// HD44780 LCD display driver - 4-bit mode +// +// LCD pins are configured using a file lcd_config.h, which you +// have to add next to your main.c file. +// +// Content can be something like this: +// +// + +#include +#include + +/** Initialize the display */ +void lcd_init(); + +/** Send a string to LCD */ +void lcd_puts(char* str_p); + +/** Sedn a char to LCD */ +void lcd_putc(char c); + +/** Show string at X, Y */ +#define lcd_puts_xy(x, y, str_p) do { lcd_xy((x), (y)); lcd_puts((str_p)); } while(0) + +/** Show char at X, Y */ +#define lcd_putc_xy(x, y, c) do { lcd_xy((x), (y)); lcd_putc((c)); } while(0) + +/** Set cursor position */ +void lcd_xy(uint8_t x, uint8_t y); + +/** Set LCD cursor. If not enabled, only remember it. */ +#define CURSOR_NONE 0b00 +#define CURSOR_BAR 0b10 +#define CURSOR_BLINK 0b01 +#define CURSOR_BOTH 0b11 +void lcd_set_cursor(uint8_t type); + +/** Display display (preserving cursor) */ +void lcd_disable(); + +/** Enable display (restoring cursor) */ +void lcd_enable(); + +/** Go home */ +void lcd_home(); + +/** Clear the screen */ +void lcd_clear(); + +/** + * Define a glyph - 8 bytes, right 5 bits are used. + * There are total 8 custom glyphs that can be defined. + */ +void lcd_glyph(uint8_t index, const uint8_t* array); + +/** Set address in CGRAM */ +void lcd_addr_cg(uint8_t acg); + +/** Set address in DDRAM */ +void lcd_addr(uint8_t add); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ecd06e2 --- /dev/null +++ b/src/main.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include +#include "pinout.h" +#include "lcd.h" + +/* + + PINOUT + + Keypad - UART RX - receives symbols from STM8 + + LCD - HD44780 + 14 - RS (H-data, L-command) + 15 - EN (CLK) + 21,20,19,18 - D7,D6,D5,D4 + RW tied low (always write) + TODO backlight switching + + I2C + 16 - SDA0 + 17 - SCL0 + + EEPROM 24C32 - addr 1010_000 + + RTC DS3231 - addr 1101_000 + + Relay + 5, 4, 22, 26 - active high + + Moisture + 28 - ADC2 0-3V + + + + +*/ + +void setup_output(uint num) +{ + gpio_set_dir(num, GPIO_OUT); + gpio_put(num, 0); + gpio_set_function(num, GPIO_FUNC_SIO); +} + +// RX interrupt handler +void irq_uart() { + while (uart_is_readable(uart0)) { + uint8_t ch = uart_getc(uart0); + + // TODO do something useful + if (uart_is_writable(uart0)) { + uart_putc(uart0, ch); + } + } +} + +void set_relays(bool one, bool two, bool three, bool four) { + gpio_put(PIN_RE1, one); + gpio_put(PIN_RE2, two); + gpio_put(PIN_RE3, three); + gpio_put(PIN_RE1, four); +} + +void open_valve(uint num) { + gpio_put(PIN_RE1, num == 1); + gpio_put(PIN_RE2, num == 2); + gpio_put(PIN_RE3, num == 3); + gpio_put(PIN_RE4, num == 4); +} + +uint16_t moisture_read() { + adc_select_input(ADC_NUM_MOISTURE); + return adc_read(); +} + +int main() +{ + // picotool binary info + bi_decl(bi_1pin_with_name(PIN_LED, "LED")); + bi_decl(bi_1pin_with_name(PIN_RE1, "RE1")); + bi_decl(bi_1pin_with_name(PIN_RE2, "RE2")); + bi_decl(bi_1pin_with_name(PIN_RE3, "RE3")); + bi_decl(bi_1pin_with_name(PIN_RE4, "RE4")); + bi_decl(bi_1pin_with_name(PIN_LCD_RS, "LCD RS")); + bi_decl(bi_1pin_with_name(PIN_LCD_E, "LCD CLK")); + bi_decl(bi_1pin_with_name(PIN_LCD_D7, "LCD D7")); + bi_decl(bi_1pin_with_name(PIN_LCD_D6, "LCD D6")); + bi_decl(bi_1pin_with_name(PIN_LCD_D5, "LCD D5")); + bi_decl(bi_1pin_with_name(PIN_LCD_D4, "LCD D4")); + bi_decl(bi_2pins_with_func(PIN_I2C_SDA, PIN_I2C_SCL, GPIO_FUNC_I2C)); + bi_decl(bi_1pin_with_name(PIN_MOISTURE, "ADC (moisture)")); + bi_decl(bi_program_description("Zavlaha Kuchynka")); + + setup_default_uart(); + irq_set_exclusive_handler(UART0_IRQ, irq_uart); + irq_set_enabled(UART0_IRQ, true); + uart_set_irq_enables(uart0, 1, 0); + + /* On-board LED */ + setup_output(PIN_LED); + + /* Valve relays */ + setup_output(PIN_RE1); + setup_output(PIN_RE2); + setup_output(PIN_RE3); + setup_output(PIN_RE4); + + /* LCD */ + setup_output(PIN_LCD_RS); + setup_output(PIN_LCD_RW); + setup_output(PIN_LCD_E); + setup_output(PIN_LCD_D7); + setup_output(PIN_LCD_D6); + setup_output(PIN_LCD_D5); + setup_output(PIN_LCD_D4); + + /* I2C */ + i2c_init(i2c0, 100 * 1000); // 100 kbps - play it safe + gpio_set_function(PIN_I2C_SDA, GPIO_FUNC_I2C); + gpio_set_function(PIN_I2C_SCL, GPIO_FUNC_I2C); + gpio_pull_up(PIN_I2C_SDA); + gpio_pull_up(PIN_I2C_SCL); + + /* ADC for moisture */ + adc_init(); + adc_gpio_init(PIN_MOISTURE); + + lcd_init(); + lcd_puts("HELLO WORLD!"); + + uint cnt = 0; + char buf[16]; + while (1) { + lcd_clear(); + lcd_puts("HELLO WORLD!"); + + gpio_put(PIN_LED, 1); // LED on + + uint16_t moisture = moisture_read(); + sprintf(buf, "ADC = %lu", moisture); + lcd_xy(0, 1); + lcd_puts(buf); + lcd_xy(15, 3); + lcd_putc('0' + cnt); + + sleep_ms(100); + gpio_put(PIN_LED, 0); // LED off + sleep_ms(100); + + // demo that valve control works + cnt += 1; + if (cnt == 5) { + cnt = 0; + } + open_valve(cnt); + } +} diff --git a/src/pinout.h b/src/pinout.h new file mode 100644 index 0000000..6730df2 --- /dev/null +++ b/src/pinout.h @@ -0,0 +1,29 @@ +/** + * TODO file description + */ + +#ifndef ZAVLAHA_PINOUT_H +#define ZAVLAHA_PINOUT_H + +#define PIN_LED PICO_DEFAULT_LED_PIN // 25 +#define PIN_RE1 5 +#define PIN_RE2 4 +#define PIN_RE3 22 +#define PIN_RE4 26 +#define PIN_MOISTURE 28 +#define ADC_NUM_MOISTURE 2 // ADC2 +#define PIN_I2C_SDA 16 +#define PIN_I2C_SCL 17 +#define PIN_LCD_RS 14 +#define PIN_LCD_RW 6 +#define PIN_LCD_E 15 +#define PIN_LCD_D7 21 +#define PIN_LCD_D6 20 +#define PIN_LCD_D5 19 +#define PIN_LCD_D4 18 + +// must be shifted left before adding the RW bit! +#define I2C_ADDR_EEPROM 0b1010000 +#define I2C_ADDR_RTC 0b1101000 + +#endif //ZAVLAHA_PINOUT_H