Write LCD booting screen from AVR bootloader
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Sat, 22 Jul 2017 07:51:02 +0000 (00:51 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Sat, 22 Jul 2017 07:51:02 +0000 (00:51 -0700)
src/boot/src/boot.c
src/boot/src/lcd.c [new file with mode: 0644]
src/boot/src/lcd.h [new file with mode: 0644]
src/py/bbctrl/LCD.py

index a0ac310e1704876dc72705521d3f5d1933931a32..a2a6c5ae6deae958ece7917ccc2023a474ac047c 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "boot.h"
 #include "sp_driver.h"
+#include "lcd.h"
 
 #include <util/delay.h>
 #include <util/crc16.h>
@@ -59,6 +60,15 @@ void clock_init() {
 }
 
 
+void lcd_splash(uint8_t addr) {
+  lcd_init(addr);
+  lcd_goto(addr, 1, 1);
+  lcd_pgmstr(addr, "Controller booting");
+  lcd_goto(addr, 3, 2);
+  lcd_pgmstr(addr, "Please wait...");
+}
+
+
 bool uart_has_char() {return UART_DEVICE.STATUS & USART_RXCIF_bm;}
 uint8_t uart_recv_char() {return UART_DEVICE.DATA;}
 
@@ -219,26 +229,11 @@ void BlockRead(unsigned size, uint8_t mem, uint32_t *address) {
 }
 
 
-int main() {
-  // Init
-  clock_init();
-  uart_init();
-  watchdog_init();
-
-  // Check for trigger
-  bool in_bootloader = false;
-  uint16_t j = INITIAL_WAIT;
-  while (!in_bootloader && 0 < j--) {
-    if (uart_has_char()) in_bootloader = uart_recv_char() == CMD_SYNC;
-    watchdog_reset();
-    _delay_ms(1);
-  }
-
-  // Main bootloader
+void bootloader() {
   uint32_t address = 0;
   uint16_t i = 0;
 
-  while (in_bootloader) {
+  while (true) {
     uint8_t val = get_char();
     watchdog_reset();
 
@@ -341,8 +336,8 @@ int main() {
       break;
 
     case CMD_EXIT_BOOTLOADER:
-      in_bootloader = false;
       send_char(REPLY_ACK);
+      return;
       break;
 
     case CMD_PROGRAMMER_TYPE: send_char('S'); break; // serial
@@ -435,6 +430,26 @@ int main() {
     // Wait for any lingering SPM instructions to finish
     nvm_wait();
   }
+}
+
+
+int main() {
+  // Init
+  clock_init();
+  uart_init();
+  watchdog_init();
+
+  // Check for trigger
+  uint16_t j = INITIAL_WAIT;
+  while (0 < j--) {
+    if (uart_has_char() && uart_recv_char() == CMD_SYNC) {
+      bootloader();
+      break;
+    }
+
+    watchdog_reset();
+    _delay_ms(1);
+  }
 
   // Deinit
   uart_deinit();
@@ -443,6 +458,9 @@ int main() {
   // Disable further self programming until next reset
   SP_LockSPM();
 
+  lcd_splash(0x27);
+  lcd_splash(0x3f);
+
   // Jump to application code
   asm("jmp 0");
 }
diff --git a/src/boot/src/lcd.c b/src/boot/src/lcd.c
new file mode 100644 (file)
index 0000000..7379a2f
--- /dev/null
@@ -0,0 +1,105 @@
+/******************************************************************************\
+
+                This file is part of the Buildbotics firmware.
+
+                  Copyright (c) 2015 - 2017 Buildbotics LLC
+          Copyright (c) 2010 Alex Forencich <alex@alexforencich.com>
+                            All rights reserved.
+
+     This file ("the software") is free software: you can redistribute it
+     and/or modify it under the terms of the GNU General Public License,
+      version 2 as published by the Free Software Foundation. You should
+      have received a copy of the GNU General Public License, version 2
+     along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+     The software is distributed in the hope that it will be useful, but
+          WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+               Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+                License along with the software.  If not, see
+                       <http://www.gnu.org/licenses/>.
+
+                For information regarding this software email:
+                  "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+#include "lcd.h"
+
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include <stdbool.h>
+
+
+void lcd_init(uint8_t addr) {
+  // Enable I2C master
+  TWIC.MASTER.BAUD = 0x9b; // 100 KHz with 32MHz clock
+  TWIC.MASTER.CTRLA = TWI_MASTER_ENABLE_bm;
+  TWIC.MASTER.CTRLB = TWI_MASTER_TIMEOUT_DISABLED_gc;
+  TWIC.MASTER.STATUS |= 1; // Force idle
+
+  _delay_ms(50);
+  lcd_nibble(addr, 3 << 4); // Home
+  _delay_ms(50);
+  lcd_nibble(addr, 3 << 4); // Home
+  _delay_ms(50);
+  lcd_nibble(addr, 3 << 4); // Home
+  lcd_nibble(addr, 2 << 4); // 4-bit
+
+  lcd_write(addr,
+            LCD_FUNCTION_SET | LCD_2_LINE | LCD_5x8_DOTS | LCD_4_BIT_MODE, 0);
+  lcd_write(addr, LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON, 0);
+  lcd_write(addr, LCD_ENTRY_MODE_SET | LCD_ENTRY_SHIFT_INC, 0);
+
+  lcd_write(addr, LCD_CLEAR_DISPLAY, 0);
+  lcd_write(addr, LCD_RETURN_HOME, 0);
+}
+
+
+static void _write_i2c(uint8_t addr, uint8_t data) {
+  data |= BACKLIGHT_BIT;
+
+  TWIC.MASTER.ADDR = addr << 1;
+  while (!(TWIC.MASTER.STATUS & TWI_MASTER_WIF_bm)) continue;
+
+  TWIC.MASTER.DATA = data;
+  while (!(TWIC.MASTER.STATUS & TWI_MASTER_WIF_bm)) continue;
+
+  TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
+
+  _delay_us(100);
+}
+
+
+void lcd_nibble(uint8_t addr, uint8_t data) {
+  _write_i2c(addr, data);
+  _write_i2c(addr, data | ENABLE_BIT);
+  _delay_us(500);
+  _write_i2c(addr, data & ~ENABLE_BIT);
+  _delay_us(100);
+}
+
+
+void lcd_write(uint8_t addr, uint8_t cmd, uint8_t flags) {
+  lcd_nibble(addr, flags | (cmd & 0xf0));
+  lcd_nibble(addr, flags | ((cmd << 4) & 0xf0));
+}
+
+
+void lcd_goto(uint8_t addr, uint8_t x, uint8_t y) {
+  static uint8_t row[] = {0, 64, 20, 84};
+  lcd_write(addr, LCD_SET_DDRAM_ADDR | (row[y] + x), 0);
+}
+
+
+void lcd_putchar(uint8_t addr, uint8_t c) {
+  lcd_write(addr, c, REG_SELECT_BIT);
+}
+
+
+void lcd_pgmstr(uint8_t addr, const char *s) {
+  while (*s) lcd_putchar(addr, *s++);
+}
diff --git a/src/boot/src/lcd.h b/src/boot/src/lcd.h
new file mode 100644 (file)
index 0000000..29fc616
--- /dev/null
@@ -0,0 +1,102 @@
+/******************************************************************************\
+
+                This file is part of the Buildbotics firmware.
+
+                  Copyright (c) 2015 - 2017 Buildbotics LLC
+          Copyright (c) 2010 Alex Forencich <alex@alexforencich.com>
+                            All rights reserved.
+
+     This file ("the software") is free software: you can redistribute it
+     and/or modify it under the terms of the GNU General Public License,
+      version 2 as published by the Free Software Foundation. You should
+      have received a copy of the GNU General Public License, version 2
+     along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+     The software is distributed in the hope that it will be useful, but
+          WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+               Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+                License along with the software.  If not, see
+                       <http://www.gnu.org/licenses/>.
+
+                For information regarding this software email:
+                  "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+#pragma once
+
+#include <avr/pgmspace.h>
+
+#include <stdint.h>
+
+
+// Control flags
+enum {
+  REG_SELECT_BIT          = 1 << 0,
+  READ_BIT                = 1 << 1,
+  ENABLE_BIT              = 1 << 2,
+  BACKLIGHT_BIT           = 1 << 3,
+};
+
+
+// Commands
+enum {
+  LCD_CLEAR_DISPLAY       = 1 << 0,
+  LCD_RETURN_HOME         = 1 << 1,
+  LCD_ENTRY_MODE_SET      = 1 << 2,
+  LCD_DISPLAY_CONTROL     = 1 << 3,
+  LCD_CURSOR_SHIFT        = 1 << 4,
+  LCD_FUNCTION_SET        = 1 << 5,
+  LCD_SET_CGRAM_ADDR      = 1 << 6,
+  LCD_SET_DDRAM_ADDR      = 1 << 7,
+};
+
+
+// Entry Mode Set flags
+#define LCD_ENTRY_SHIFT_DISPLAY (1 << 0)
+#define LCD_ENTRY_SHIFT_INC     (1 << 1)
+#define LCD_ENTRY_SHIFT_DEC     (0 << 1)
+
+
+// Display Control flags
+#define LCD_BLINK_ON            (1 << 0)
+#define LCD_BLINK_OFF           (0 << 0)
+#define LCD_CURSOR_ON           (1 << 1)
+#define LCD_CURSOR_OFF          (0 << 1)
+#define LCD_DISPLAY_ON          (1 << 2)
+#define LCD_DISPLAY_OFF         (0 << 2)
+
+
+// Cursor Shift flags
+#define LCD_SHIFT_RIGHT         (1 << 2)
+#define LCD_SHIFT_LEFT          (0 << 2)
+#define LCD_SHIFT_DISPLAY       (1 << 3)
+#define LCD_SHIFT_CURSOR        (0 << 3)
+
+
+// Function Set flags
+#define LCD_5x11_DOTS           (1 << 2)
+#define LCD_5x8_DOTS            (0 << 2)
+#define LCD_2_LINE              (1 << 3)
+#define LCD_1_LINE              (0 << 3)
+#define LCD_8_BIT_MODE          (1 << 4)
+#define LCD_4_BIT_MODE          (0 << 4)
+
+
+// Text justification flags
+enum {
+  JUSTIFY_LEFT            = 0,
+  JUSTIFY_RIGHT           = 1,
+  JUSTIFY_CENTER          = 2,
+};
+
+
+void lcd_init(uint8_t addr);
+void lcd_nibble(uint8_t addr, uint8_t data);
+void lcd_write(uint8_t addr, uint8_t cmd, uint8_t flags);
+void lcd_goto(uint8_t addr, uint8_t x, uint8_t y);
+void lcd_putchar(uint8_t addr, uint8_t c);
+void lcd_pgmstr(uint8_t addr, const char *s);
index b12b2abb4583700410a08f19797deba4d9776891..6616baa4b629ed5b2c89e32e20622a60016cd38a 100644 (file)
@@ -65,8 +65,6 @@ class LCD:
         self.current_page = 0
         self.screen = self.new_screen()
 
-        self.set_message('Loading')
-
         # Redraw screen every 5 seconds
         self.redraw_timer = tornado.ioloop.PeriodicCallback(self._redraw, 5000,
                                                             self.ctrl.ioloop)
@@ -181,4 +179,4 @@ class LCD:
             self.redraw_timer.stop()
             self.redraw_timer = None
 
-        if self.lcd is not None: self.set_message('Goodbye')
+        if self.lcd is not None: self.set_message('')