Boost HDMI signal, Increased AVR serial and command buffers, Changed AVR serial inter...
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Fri, 17 May 2019 21:21:47 +0000 (14:21 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Fri, 17 May 2019 21:21:47 +0000 (14:21 -0700)
25 files changed:
.gitignore
CHANGELOG.md
MANIFEST.in
Makefile
docs/cross_compile_rpi_kernel_module.md [new file with mode: 0644]
package.json
scripts/bbctrl.init.d [deleted file]
scripts/bbctrl.service [new file with mode: 0644]
scripts/demo-chroot [new file with mode: 0755]
scripts/install.sh
scripts/rc.local
src/avr/src/command.c
src/avr/src/config.h
src/avr/src/ringbuf.def
src/avr/src/usart.c
src/avr/src/usart.h
src/bbserial/Makefile [new file with mode: 0644]
src/bbserial/bbserial.c [new file with mode: 0644]
src/pug/templates/tool-view.pug
src/py/bbctrl/AVR.py
src/py/bbctrl/AVREmu.py
src/py/bbctrl/Camera.py
src/py/bbctrl/Log.py
src/py/bbctrl/Preplanner.py
src/py/bbctrl/__init__.py

index ba12fc8a4aefd02d851646bd2baa97a8313637c3..2157929c18c3ae32ce549628b5cb245d9e36eeea 100644 (file)
@@ -5,6 +5,8 @@
 /crap
 /pkg
 /mnt
+/demo
+/bbctrl-*
 node_modules
 *~
 \#*
index 472d9aef143e076bffa1621da5dd2dff4c42ee90..8f744fd5a2c6a93e990d4e2674fca994ab564a9d 100644 (file)
@@ -1,6 +1,14 @@
 Buildbotics CNC Controller Firmware Changelog
 =============================================
 
+## v0.4.8
+ - Fixed log rotating.
+ - Use systemd serivce instead of init.d.
+ - Fix planner terminate.
+ - Changed AVR serial interrupt priorites.
+ - Increased AVR serial and command buffers.
+ - Boost HDMI signal.
+
 ## v0.4.7
  - Fix homing switch to motor channel mapping with non-standard axis order.
  - Added ``switch-debounce`` and ``switch-lockout`` config options.
index 9dbe3dee74db4076a3300954ae2381e776f3f6b2..32d4570bb6ceb4c1466dca6d6b054379dd0fdfef 100644 (file)
@@ -1,9 +1,11 @@
 recursive-include src/py/bbctrl/http *
 include package.json README.md scripts/install.sh
 include src/avr/bbctrl-avr-firmware.hex
+include src/bbserial/bbserial.ko
 include scripts/avr109-flash.py
 include scripts/buildbotics.gc
 include scripts/xinitrc
 include scripts/rc.local
+include scripts/bbctrl.service
 recursive-include src/py/camotics *
 global-exclude .gitignore
index 31d1049af2b19df06223bbcfa9e2cf580b0c6bfd..0aed58e7f4ea9cf98efb5cffbebde0705354186e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -42,12 +42,15 @@ endif
 all: $(HTML) $(RESOURCES)
        @for SUB in $(SUBPROJECTS); do $(MAKE) -C src/$$SUB; done
 
-pkg: all $(AVR_FIRMWARE)
+pkg: all $(AVR_FIRMWARE) bbserial
        ./setup.py sdist
 
 beta-pkg: pkg
        cp dist/$(PKG_NAME).tar.bz2 dist/$(BETA_PKG_NAME).tar.bz2
 
+bbserial:
+       $(MAKE) -C src/bbserial
+
 gplan: $(GPLAN_TARGET)
 
 $(GPLAN_TARGET): $(GPLAN_MOD)
@@ -136,4 +139,4 @@ clean: tidy
 dist-clean: clean
        rm -rf node_modules
 
-.PHONY: all install clean tidy pkg gplan lint pylint jshint
+.PHONY: all install clean tidy pkg gplan lint pylint jshint bbserial
diff --git a/docs/cross_compile_rpi_kernel_module.md b/docs/cross_compile_rpi_kernel_module.md
new file mode 100644 (file)
index 0000000..2b04dd3
--- /dev/null
@@ -0,0 +1,68 @@
+# Install the cross compiler
+
+    sudo apt-get update
+    sudo apt-get install -y gcc-arm-linux-gnueabihf
+
+# Determine the correct kernel version on the target pi
+
+    dpkg-query -l raspberrypi-kernel
+
+# Get the kernel source for the correct kernel version
+
+    wget https://github.com/raspberrypi/linux/archive/raspberrypi-kernel_1.20171029-1.tar.gz
+    tar xf raspberrypi-kernel_1.20171029-1.tar.gz
+    cd linux-raspberrypi-kernel_1.20171029-1
+
+# Prep the kernel source
+
+    KERNEL=kernel7
+    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
+    make -j 8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_prepare
+
+# Create hello.c module
+
+    #include <linux/init.h>
+    #include <linux/module.h>
+
+    MODULE_LICENSE("GPL");
+
+    static int hello_init(void) {
+      printk(KERN_ALERT "Hello world!\n");
+      return 0;
+    }
+
+    static void hello_exit(void) {
+      printk(KERN_ALERT "Goodbye cruel world!\n");
+    }
+
+    module_init(hello_init);
+    module_exit(hello_exit);
+
+# Create a Makefile
+
+    CROSS := arm-linux-gnueabihf-
+    DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+    obj-m := hello.o
+
+    all:
+        $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) modules
+
+    clean:
+        $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) clean
+
+# Compile the module
+
+    ln -sf path/to/rpi-kernel kernel
+    make
+
+# Copy module to rpi
+
+    scp hello.ko pi.local:
+
+# Load it on the pi
+
+    sudo insmod hello.ko
+
+# Look for message in syslog
+
+    tail /var/log/syslog
index e936b177d887406c246cf071da78f930170cab3e..326139fa965972097be9401f556f8466227ac841 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "bbctrl",
-  "version": "0.4.7",
+  "version": "0.4.8",
   "homepage": "http://buildbotics.com/",
   "repository": "https://github.com/buildbotics/bbctrl-firmware",
   "license": "GPL-3.0+",
diff --git a/scripts/bbctrl.init.d b/scripts/bbctrl.init.d
deleted file mode 100755 (executable)
index 54d4366..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/bin/bash
-
-### BEGIN INIT INFO
-# Provides:          bbctl
-# Required-Start:    $local_fs $network
-# Required-Stop:     $local_fs $network
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: Buildbotics Controller Web service
-# Description:       Buildbotics Controller Web service
-### END INIT INFO
-
-
-DAEMON_NAME=bbctrl
-DAEMON=/usr/local/bin/$DAEMON_NAME
-DAEMON_OPTS=""
-DAEMON_USER=root
-DAEMON_DIR=/var/lib/$DAEMON_NAME
-PIDFILE=/var/run/$DAEMON_NAME.pid
-DEFAULTS=/etc/default/$DAEMON_NAME
-
-
-. /lib/lsb/init-functions
-
-
-if [ -e $DEFAULTS ]; then
-    . $DEFAULTS
-fi
-
-
-do_start () {
-    log_daemon_msg "Starting system $DAEMON_NAME daemon"
-    mkdir -p $DAEMON_DIR &&
-    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile \
-        --user $DAEMON_USER --chuid $DAEMON_USER --chdir $DAEMON_DIR \
-        --startas $DAEMON -- $DAEMON_OPTS -l /var/log/$DAEMON_NAME.log
-    log_end_msg $?
-}
-
-
-do_stop () {
-    log_daemon_msg "Stopping system $DAEMON_NAME daemon"
-    start-stop-daemon --stop --pidfile $PIDFILE --retry 10
-    log_end_msg $?
-}
-
-
-case "$1" in
-    start|stop) do_${1} ;;
-    restart|reload|force-reload) do_stop; do_start ;;
-    status)
-        status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
-        ;;
-
-    *)
-        echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
-        exit 1
-        ;;
-esac
diff --git a/scripts/bbctrl.service b/scripts/bbctrl.service
new file mode 100644 (file)
index 0000000..8b22cc7
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=Buildbotics Controller
+After=network.target
+
+[Service]
+User=root
+ExecStart=/usr/local/bin/bbctrl -l /var/log/bbctrl.log
+WorkingDirectory=/var/lib/bbctrl
+Restart=always
+StandardOutput=null
+Nice=-10
+KillMode=process
+
+[Install]
+WantedBy=multi-user.target
diff --git a/scripts/demo-chroot b/scripts/demo-chroot
new file mode 100755 (executable)
index 0000000..75e7578
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash -ex
+
+ROOT="$PWD/demo"
+
+# Clean up on EXIT
+function cleanup {
+    umount "$ROOT"/{dev/pts,dev,sys,proc} 2>/dev/null || true
+}
+trap cleanup EXIT
+
+# mount binds
+mount --bind /dev "$ROOT/dev/"
+mount --bind /sys "$ROOT/sys/"
+mount --bind /proc "$ROOT/proc/"
+mount --bind /dev/pts "$ROOT/dev/pts"
+
+chroot "$ROOT" "$@"
index 49965e89d4f1a2b9e83ea40a2386c133fd64bc74..86f796e686723f7106737c7f3fec82fb0ec0c27d 100755 (executable)
@@ -13,36 +13,42 @@ while [ $# -gt 0 ]; do
 done
 
 
-if $UPDATE_PY; then
-    if [ -e /var/run/bbctrl.pid ]; then
-        service bbctrl stop
+function update_config_txt() {
+    MATCH="$1"
+    CONFIG="$2"
+
+    grep "$MATCH" /boot/config.txt
+
+    if [ $? -ne 0 ]; then
+        mount -o remount,rw /boot &&
+            echo "$CONFIG" >> /boot/config.txt
+        mount -o remount,ro /boot
     fi
+
+}
+
+
+if $UPDATE_PY; then
+    service bbctrl stop
+
+    # Update service
+    rm -f /etc/init.d/bbctrl
+    cp scripts/bbctrl.service /etc/systemd/system/
+    systemctl daemon-reload
+    systemctl enable bbctrl
 fi
 
 if $UPDATE_AVR; then
     ./scripts/avr109-flash.py src/avr/bbctrl-avr-firmware.hex
 fi
 
-# Increase USB current
-grep max_usb_current /boot/config.txt >/dev/null
-if [ $? -ne 0 ]; then
-    mount -o remount,rw /boot &&
-    echo max_usb_current=1 >> /boot/config.txt
-    mount -o remount,ro /boot
-fi
-
+# Update config.txt
+update_config_txt ^max_usb_current max_usb_current=1
+update_config_txt ^config_hdmi_boost config_hdmi_boost=8
 
 # TODO Enable GPU
-#grep ^dtoverlay=vc4-kms-v3d /boot/config.txt >/dev/null
-#if [ $? -ne 0 ]; then
-#    mount -o remount,rw /boot &&
-#    (
-#        echo
-#        echo dtoverlay=vc4-kms-v3d
-#        echo gpu_mem=16
-#    ) >> /boot/config.txt
-#    mount -o remount,ro /boot
-#fi
+#update_config_txt dtoverlay=vc4-kms "dtoverlay=vc4-kms-v3d"
+#update_config_txt gpu_mem "gpu_mem=16"
 #chmod ug+s /usr/lib/xorg/Xorg
 
 # Fix camera
@@ -68,17 +74,6 @@ if [ -e /etc/init.d/hawkeye ]; then
     apt-get remove --purge -y hawkeye
 fi
 
-# Enable USB audio
-if [ ! -e /etc/asound.conf ]; then
-    (
-        echo "pcm.!default {"
-        echo "  type asym"
-        echo "  playback.pcm \"plug:hw:0\""
-        echo "  capture.pcm \"plug:dsnoop:1\""
-        echo "}"
-    ) > etc/asound.conf
-fi
-
 # Decrease boot delay
 sed -i 's/^TimeoutStartSec=.*$/TimeoutStartSec=1/' \
     /etc/systemd/system/network-online.target.wants/networking.service
@@ -118,6 +113,16 @@ cp scripts/xinitrc ~pi/.xinitrc
 chmod +x ~pi/.xinitrc
 chown pi:pi ~pi/.xinitrc
 
+# Install bbserial
+MODSRC=src/bbserial/bbserial.ko
+MODDST=/lib/modules/$(uname -r)/kernel/drivers/tty/serial/bbserial.ko
+diff -q $MODSRC $MODDST 2>/dev/null >/dev/null
+if [ $? -ne 0 ]; then
+    cp $MODSRC $MODDST
+    depmod
+    REBOOT=true
+fi
+
 # Install rc.local
 cp scripts/rc.local /etc/
 
index 127071509179ab5a8af6a3bffd926b434040b7d3..aecc57f13d4f974289291bcea2eaf5c4ba2eab1c 100755 (executable)
@@ -3,6 +3,11 @@
 # Mount /boot read only
 mount -o remount,ro /boot
 
+# Load bbserial
+echo 3f201000.serial > /sys/bus/amba/drivers/uart-pl011/unbind
+modprobe -r bbserial
+modprobe bbserial
+
 # Set SPI GPIO mode
 gpio mode 27 alt3
 
index 17aee9c58c98ea8edddac7fd969f5eab114edbe4..916c388b499f3ff502ac4eb2abf1ae648255a13f 100644 (file)
 #include <stdlib.h>
 
 
-#define RING_BUF_ATOMIC_COPY(TO, FROM)          \
-  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) TO = FROM
-
 #define RING_BUF_NAME sync_q
 #define RING_BUF_TYPE uint8_t
 #define RING_BUF_INDEX_TYPE volatile uint16_t
 #define RING_BUF_SIZE SYNC_QUEUE_SIZE
+#define RING_BUF_ATOMIC_COPY 1
 #include "ringbuf.def"
 
 
index a99be90fd38417b7035e9b6a6bdd70f2faa911e7..512b3f37d8d3f9596a4a32599c2aa8b6f5dc6c8e 100644 (file)
@@ -219,7 +219,7 @@ enum {
 #define VELOCITY_MULTIPLIER      1000.0
 #define ACCEL_MULTIPLIER         1000000.0
 #define JERK_MULTIPLIER          1000000.0
-#define SYNC_QUEUE_SIZE          2048
+#define SYNC_QUEUE_SIZE          4096
 #define EXEC_FILL_TARGET         8
 #define EXEC_DELAY               250 // ms
 #define JOG_STOPPING_UNDERSHOOT  1   // % of stopping distance
index 1f444a0d7e5ee5cbaa3df7d950a59a0985e262b9..9fff9641b718f6b9da932e404e4dec75c6156154 100644 (file)
@@ -53,6 +53,9 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+#include <util/atomic.h>
+
+
 #ifndef RING_BUF_NAME
 #error Must define RING_BUF_NAME
 #endif
@@ -106,6 +109,11 @@ RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _init)() {
 
 #ifdef RING_BUF_ATOMIC_COPY
 
+#undef RING_BUF_ATOMIC_COPY
+#define RING_BUF_ATOMIC_COPY(TO, FROM)          \
+  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) TO = FROM
+
+
 #define RING_BUF_ATOMIC_READ_INDEX(INDEX)                               \
   RING_BUF_FUNC RING_BUF_INDEX_TYPE CONCAT(RING_BUF_NAME, _read_##INDEX)() { \
     RING_BUF_INDEX_TYPE index;                                          \
index 17dba80433e36de3ec83756fe3309a6d9265de31..f36433244b91d644e8c79c722bfb1845c5fac3ab 100644 (file)
 
 
 // Ring buffers
+#define RING_BUF_INDEX_TYPE volatile uint16_t
 #define RING_BUF_NAME tx_buf
 #define RING_BUF_SIZE USART_TX_RING_BUF_SIZE
+#define RING_BUF_ATOMIC_COPY 1
 #include "ringbuf.def"
 
+#define RING_BUF_INDEX_TYPE volatile uint16_t
 #define RING_BUF_NAME rx_buf
 #define RING_BUF_SIZE USART_RX_RING_BUF_SIZE
+#define RING_BUF_ATOMIC_COPY 1
 #include "ringbuf.def"
 
 static bool _flush = false;
@@ -60,9 +64,9 @@ static void _set_rxc_interrupt(bool enable) {
     if (SERIAL_CTS_THRESH <= rx_buf_space())
       OUTCLR_PIN(SERIAL_CTS_PIN); // CTS Lo (enable)
 
-    SERIAL_PORT.CTRLA |= USART_RXCINTLVL_MED_gc;
+    SERIAL_PORT.CTRLA |= USART_RXCINTLVL_HI_gc;
 
-  } else SERIAL_PORT.CTRLA &= ~USART_RXCINTLVL_MED_gc;
+  } else SERIAL_PORT.CTRLA &= ~USART_RXCINTLVL_HI_gc;
 }
 
 
index 727c6976dcea22f2d926dd9173d08f4e62536d44..b59f4ed2a19f29ec5310d66d71aee69c7eb08c65 100644 (file)
@@ -33,8 +33,9 @@
 #include <stdbool.h>
 
 
-#define USART_TX_RING_BUF_SIZE 256
-#define USART_RX_RING_BUF_SIZE 256
+// NOTE, RING_BUF_INDEX_TYPE must be be large enough to cover the buffer
+#define USART_TX_RING_BUF_SIZE 1024
+#define USART_RX_RING_BUF_SIZE 1024
 
 
 typedef enum {
diff --git a/src/bbserial/Makefile b/src/bbserial/Makefile
new file mode 100644 (file)
index 0000000..6482bfc
--- /dev/null
@@ -0,0 +1,10 @@
+CROSS := arm-linux-gnueabihf-
+DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+obj-m := bbserial.o
+ccflags-y := -std=gnu99 -Wno-declaration-after-statement
+
+all:
+       $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) modules
+
+clean:
+       $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) clean
diff --git a/src/bbserial/bbserial.c b/src/bbserial/bbserial.c
new file mode 100644 (file)
index 0000000..8a5745f
--- /dev/null
@@ -0,0 +1,558 @@
+/******************************************************************************\
+
+                 This file is part of the Buildbotics firmware.
+
+                   Copyright (c) 2015 - 2019, Buildbotics LLC
+                              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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/serial.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <asm/ioctls.h>
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joseph Coffland");
+MODULE_DESCRIPTION("Buildbotics controller serial port driver");
+MODULE_VERSION("0.2");
+
+
+#define DEVICE_NAME "ttyBB0"
+#define BUF_SIZE (1 << 16)
+
+
+enum {
+  REG_DR,
+  REG_ST_DMAWM,
+  REG_ST_TIMEOUT,
+  REG_FR,
+  REG_LCRH_RX,
+  REG_LCRH_TX,
+  REG_IBRD,
+  REG_FBRD,
+  REG_CR,
+  REG_IFLS,
+  REG_IMSC,
+  REG_RIS,
+  REG_MIS,
+  REG_ICR,
+  REG_DMACR,
+  REG_ST_XFCR,
+  REG_ST_XON1,
+  REG_ST_XON2,
+  REG_ST_XOFF1,
+  REG_ST_XOFF2,
+  REG_ST_ITCR,
+  REG_ST_ITIP,
+  REG_ST_ABCR,
+  REG_ST_ABIMSC,
+  REG_ARRAY_SIZE,
+};
+
+
+static u16 _offsets[REG_ARRAY_SIZE] = {
+  [REG_DR]      = UART01x_DR,
+  [REG_FR]      = UART01x_FR,
+  [REG_LCRH_RX] = UART011_LCRH,
+  [REG_LCRH_TX] = UART011_LCRH,
+  [REG_IBRD]    = UART011_IBRD,
+  [REG_FBRD]    = UART011_FBRD,
+  [REG_CR]      = UART011_CR,
+  [REG_IFLS]    = UART011_IFLS,
+  [REG_IMSC]    = UART011_IMSC,
+  [REG_RIS]     = UART011_RIS,
+  [REG_MIS]     = UART011_MIS,
+  [REG_ICR]     = UART011_ICR,
+  [REG_DMACR]   = UART011_DMACR,
+};
+
+
+static int _debug = 0;
+
+struct ring_buf {
+  unsigned char *buf;
+  unsigned head;
+  unsigned tail;
+};
+
+static struct {
+  struct clk            *clk;
+  struct ring_buf       tx_buf;
+  struct ring_buf       rx_buf;
+  spinlock_t            lock;
+  unsigned              open;
+  unsigned char __iomem *base;
+  wait_queue_head_t     wait;
+  unsigned long         req_events;
+  unsigned              irq;
+  unsigned              im;             // interrupt mask
+
+  unsigned              rx_bytes;
+  unsigned              tx_bytes;
+
+  unsigned              brk_errs;
+  unsigned              parity_errs;
+  unsigned              frame_errs;
+  unsigned              overruns;
+
+  int                   major;
+  struct class          *class;
+  struct device         *dev;
+} _port;
+
+
+#define RING_BUF_INC(BUF, INDEX)                                        \
+  do {(BUF).INDEX = ((BUF).INDEX + 1) & (BUF_SIZE - 1);} while (0)
+
+#define RING_BUF_POP(BUF) RING_BUF_INC(BUF, tail)
+
+#define RING_BUF_PUSH(BUF, C)                   \
+  do {                                          \
+    (BUF).buf[(BUF).head] = C;                  \
+    RING_BUF_INC(BUF, head);                    \
+  } while (0)
+
+#define RING_BUF_POKE(BUF) (BUF).buf[(BUF).head]
+#define RING_BUF_PEEK(BUF) (BUF).buf[(BUF).tail]
+
+#define RING_BUF_MOVE(SRC, DST)                 \
+  do {                                          \
+    RING_BUF_PUSH(DST, RING_BUF_PEEK(SRC));     \
+    RING_BUF_POP(SRC);                          \
+  } while (0)
+
+
+#define RING_BUF_SPACE(BUF) ((((BUF).tail) - ((BUF).head + 1)) & (BUF_SIZE - 1))
+#define RING_BUF_FILL(BUF) ((((BUF).head) - ((BUF).tail)) & (BUF_SIZE - 1))
+
+
+static unsigned _read(unsigned reg) {
+  return readw_relaxed(_port.base + _offsets[reg]);
+}
+
+
+static void _write(unsigned val, unsigned reg) {
+  writew_relaxed(val, _port.base + _offsets[reg]);
+}
+
+
+static unsigned _tx_chars(void) {
+  unsigned bytes = 0;
+  unsigned fill = RING_BUF_FILL(_port.tx_buf);
+
+  for (int i = 0; i < fill; i++) {
+    // Check if UART FIFO full
+    if (_read(REG_FR) & UART01x_FR_TXFF) break;
+
+    _write(RING_BUF_PEEK(_port.tx_buf), REG_DR);
+    RING_BUF_POP(_port.tx_buf);
+    mb();
+    _port.tx_bytes++;
+    bytes++;
+  }
+
+  // Stop TX when buffer is empty
+  if (!RING_BUF_FILL(_port.tx_buf)) {
+    _port.im &= ~UART011_TXIM;
+    _write(_port.im, REG_IMSC);
+  }
+
+  return bytes;
+}
+
+
+static unsigned _rx_chars(void) {
+  unsigned bytes = 0;
+  unsigned space = RING_BUF_SPACE(_port.rx_buf);
+
+  for (int i = 0; i < space; i++) {
+    // Check if UART FIFO empty
+    unsigned status = _read(REG_FR);
+    if (status & UART01x_FR_RXFE) break;
+
+    // Read char from FIFO and update status
+    unsigned ch = _read(REG_DR);
+    _port.rx_bytes++;
+
+    // Record errors
+    if (ch & UART011_DR_BE) _port.brk_errs++;
+    if (ch & UART011_DR_PE) _port.parity_errs++;
+    if (ch & UART011_DR_FE) _port.frame_errs++;
+    if (ch & UART011_DR_OE) _port.overruns++;
+
+    // Queue char
+    RING_BUF_PUSH(_port.rx_buf, ch);
+    bytes++;
+  }
+
+  // Stop RX interrupts when buffer is full
+  if (!RING_BUF_SPACE(_port.rx_buf)) {
+    _port.im &= ~(UART011_RXIM | UART011_RTIM | UART011_FEIM | UART011_PEIM |
+                  UART011_BEIM | UART011_OEIM);
+    _write(_port.im, REG_IMSC);
+  }
+
+  return bytes;
+}
+
+
+static irqreturn_t _interrupt(int irq, void *id) {
+  unsigned long flags;
+  spin_lock_irqsave(&_port.lock, flags);
+
+  unsigned rBytes = 0;
+  unsigned wBytes = 0;
+
+  for (int pass = 0; pass < 256; pass++) {
+    unsigned status = _read(REG_MIS);
+    if (!status) break;
+
+    // Clear interrupt status
+    _write(status, REG_ICR);
+
+    // Read and/or write
+    if (status & (UART011_RTIS | UART011_RXIS)) rBytes += _rx_chars();
+    if (status & UART011_TXIS) wBytes += _tx_chars();
+  }
+
+  spin_unlock_irqrestore(&_port.lock, flags);
+
+  // Notify pollers
+  if ((rBytes && (_port.req_events & POLLIN)) ||
+      (wBytes && (_port.req_events & POLLOUT)))
+    wake_up_interruptible(&_port.wait);
+
+  return IRQ_HANDLED;
+}
+
+
+static void _enable_tx(void) {
+  unsigned long flags;
+  spin_lock_irqsave(&_port.lock, flags);
+
+  _port.im |= UART011_TXIM;
+  _write(_port.im, REG_IMSC);
+  _tx_chars(); // Must prime the pump
+
+  spin_unlock_irqrestore(&_port.lock, flags);
+}
+
+
+static void _enable_rx(void) {
+  unsigned long flags;
+  spin_lock_irqsave(&_port.lock, flags);
+
+  _port.im |= UART011_RTIM | UART011_RXIM;
+  _write(_port.im, REG_IMSC);
+
+  spin_unlock_irqrestore(&_port.lock, flags);
+}
+
+
+static int _dev_open(struct inode *inodep, struct file *filep) {
+  if (_debug) printk(KERN_INFO "bbserial: open()\n");
+  if (_port.open) return -EBUSY;
+  _port.open = 1;
+  return 0;
+}
+
+
+static ssize_t _dev_read(struct file *filep, char *buffer, size_t len,
+                         loff_t *offset) {
+  if (_debug) printk(KERN_INFO "bbserial: read() len=%d\n", len);
+  ssize_t bytes = 0;
+
+  // TODO read whole blocks
+  while (len && RING_BUF_FILL(_port.rx_buf)) {
+    put_user(RING_BUF_PEEK(_port.rx_buf), buffer++);
+    RING_BUF_POP(_port.rx_buf);
+    len--;
+    bytes++;
+  }
+
+  if (bytes) _enable_rx();
+
+  return bytes ? bytes : -EAGAIN;
+}
+
+
+static ssize_t _dev_write(struct file *filep, const char *buffer, size_t len,
+                         loff_t *offset) {
+  if (_debug)
+    printk(KERN_INFO "bbserial: write() len=%d tx=%d rx=%d\n",
+           len, RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf));
+
+  ssize_t bytes = 0;
+
+  // TODO write whole blocks
+  while (len && RING_BUF_SPACE(_port.tx_buf)) {
+    get_user(RING_BUF_POKE(_port.tx_buf), buffer++);
+    RING_BUF_INC(_port.tx_buf, head);
+    len--;
+    bytes++;
+  }
+
+  if (bytes) _enable_tx();
+
+  return bytes ? bytes : -EAGAIN;
+}
+
+
+static int _dev_release(struct inode *inodep, struct file *filep) {
+  printk(KERN_INFO "bbserial: release()\n");
+  _port.open = 0;
+  return 0;
+}
+
+
+static unsigned _dev_poll(struct file *file, poll_table *wait) {
+  if (_debug) printk(KERN_INFO "bbserial: poll(tx=%d rx=%d)\n",
+                     RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf));
+
+  _port.req_events = poll_requested_events(wait);
+  poll_wait(file, &_port.wait, wait);
+  _port.req_events = 0;
+
+  unsigned ret = 0;
+  if (RING_BUF_SPACE(_port.tx_buf)) ret |= POLLOUT | POLLWRNORM;
+  if (RING_BUF_FILL(_port.rx_buf))  ret |= POLLIN  | POLLRDNORM;
+
+  return ret;
+}
+
+
+static long _dev_ioctl(struct file *file, unsigned cmd, unsigned long arg) {
+  if (_debug) printk(KERN_INFO "bbserial: ioctl() cmd=%d arg=%lu\n", cmd, arg);
+
+  int __user *ptr = (int __user *)arg;
+
+  switch (cmd) {
+  case TCGETS:   // TODO Get serial port settings
+  case TCSETS:   // TODO Set serial port settings
+  case TIOCMBIS: // TODO Set modem control state
+    return 0;
+
+  case TCFLSH: return 0;
+  case FIONREAD: return put_user(RING_BUF_FILL(_port.rx_buf), ptr);
+  default: return -ENOIOCTLCMD;
+  }
+
+  return 0;
+}
+
+
+static struct file_operations _ops = {
+  .owner          = THIS_MODULE,
+  .open           = _dev_open,
+  .read           = _dev_read,
+  .write          = _dev_write,
+  .release        = _dev_release,
+  .poll           = _dev_poll,
+  .unlocked_ioctl = _dev_ioctl,
+};
+
+
+static int _probe(struct amba_device *dev, const struct amba_id *id) {
+  if (_debug) printk(KERN_INFO "bbserial: probing\n");
+
+  // Allocate buffers
+  _port.tx_buf.buf = devm_kzalloc(&dev->dev, BUF_SIZE, GFP_KERNEL);
+  _port.rx_buf.buf = devm_kzalloc(&dev->dev, BUF_SIZE, GFP_KERNEL);
+  if (!_port.tx_buf.buf || !_port.rx_buf.buf) return -ENOMEM;
+
+  // Map IO memory
+  _port.base = devm_ioremap_resource(&dev->dev, &dev->res);
+  if (IS_ERR(_port.base)) {
+    dev_err(&dev->dev, "bbserial: failed to map IO memory\n");
+   return PTR_ERR(_port.base);
+  }
+
+  // Get and enable clock
+  _port.clk = devm_clk_get(&dev->dev, 0);
+  if (IS_ERR(_port.clk)) {
+    dev_err(&dev->dev, "bbserial: failed to get clock\n");
+    return PTR_ERR(_port.clk);
+  }
+
+  int ret = clk_prepare_enable(_port.clk);
+  if (ret) {
+    dev_err(&dev->dev, "bbserial: clock prepare failed\n");
+    return ret;
+  }
+
+  // Disable UART and mask interrupts
+  _write(0, REG_CR);
+  _write(0, REG_IMSC);
+
+  // Set baud rate
+  const unsigned baud = 230400;
+  unsigned brd = clk_get_rate(_port.clk) * 16 / baud;
+  if ((brd & 3) == 3) brd = (brd >> 2) + 1;
+  else brd >>= 2;
+  _write(brd & 0x3f, REG_FBRD);
+  _write(brd >> 6,   REG_IBRD);
+
+  // N81 & enable FIFOs
+  _write(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, REG_LCRH_RX);
+
+  // Enable, TX, RX, RTS, DTR & CTS
+  unsigned cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE |
+    UART011_CR_RTS | UART011_CR_DTR | UART011_CR_CTSEN;
+  _write(cr, REG_CR);
+
+  // Set interrupt FIFO trigger levels
+  _write(UART011_IFLS_RX2_8 | UART011_IFLS_TX6_8, REG_IFLS);
+
+  // Clear pending interrupts
+  _write(0x7ff, REG_ICR);
+
+  // Enable read interrupts
+  _port.im = _read(REG_IMSC);
+  _enable_rx();
+
+  // Allocate IRQ
+  _port.irq = dev->irq[0];
+  ret = request_irq(_port.irq, _interrupt, 0, "bbserial", &_port);
+
+  if (ret) {
+    dev_err(&dev->dev, "bbserial: request for IRQ failed\n");
+    clk_disable_unprepare(_port.clk);
+    return ret;
+  }
+
+  // Dynamically allocate device major number
+  _port.major = register_chrdev(0, DEVICE_NAME, &_ops);
+  if (_port.major < 0) {
+    clk_disable_unprepare(_port.clk);
+    dev_err(&dev->dev, "bbserial: failed to register a major number\n");
+    return _port.major;
+  }
+
+  // Register device class
+  _port.class = class_create(THIS_MODULE, "bbs");
+  if (IS_ERR(_port.class)) {
+    unregister_chrdev(_port.major, DEVICE_NAME);
+    clk_disable_unprepare(_port.clk);
+    dev_err(&dev->dev, "bbserial: ailed to register device class\n");
+    return PTR_ERR(_port.class);
+  }
+
+  // Register device driver
+  _port.dev =
+    device_create(_port.class, 0, MKDEV(_port.major, 0), 0, DEVICE_NAME);
+  if (IS_ERR(_port.dev)) {
+    class_destroy(_port.class);
+    unregister_chrdev(_port.major, DEVICE_NAME);
+    dev_err(&dev->dev, "bbserial: failed to create the device\n");
+    return PTR_ERR(_port.dev);
+  }
+
+  return 0;
+}
+
+
+static int _remove(struct amba_device *dev) {
+  if (_debug) printk(KERN_INFO "bbserial: removing\n");
+
+  unsigned long flags;
+  spin_lock_irqsave(&_port.lock, flags);
+
+  // Mask and clear interrupts
+  _write(0, REG_IMSC);
+  _write(0x7ff, REG_ICR);
+
+  // Disable UART
+  _write(0, REG_CR);
+
+  spin_unlock_irqrestore(&_port.lock, flags);
+
+  // Free IRQ
+  free_irq(_port.irq, &_port);
+  synchronize_irq(_port.irq);
+
+  // Shut down the clock producer
+  clk_disable_unprepare(_port.clk);
+
+  // Unload char dev
+  device_destroy(_port.class, MKDEV(_port.major, 0));
+  class_unregister(_port.class);
+  class_destroy(_port.class);
+  unregister_chrdev(_port.major, DEVICE_NAME);
+
+  return 0;
+}
+
+
+static struct amba_id _ids[] = {
+  {
+    .id    = 0x00041011,
+    .mask  = 0x000fffff,
+    .data  = 0,
+  },
+  {0, 0},
+};
+
+
+MODULE_DEVICE_TABLE(amba, _ids);
+
+
+static struct amba_driver _driver = {
+  .drv = {.name  = "bbserial"},
+  .id_table = _ids,
+  .probe    = _probe,
+  .remove   = _remove,
+};
+
+
+static int __init bbserial_init(void) {
+  printk(KERN_INFO "bbserial: loaded\n");
+
+  // Clear memory
+  memset(&_port, 0, sizeof(_port));
+
+  // Init lock
+  spin_lock_init(&_port.lock);
+
+  // Init wait queues
+  init_waitqueue_head(&_port.wait);
+
+  return amba_driver_register(&_driver);
+}
+
+
+static void __exit bbserial_exit(void) {
+  amba_driver_unregister(&_driver);
+  printk(KERN_INFO "bbserial: unloaded\n");
+}
+
+
+module_init(bbserial_init);
+module_exit(bbserial_exit);
index 3ef7bf5f91765b956fc21a7802c2876500032784..3ce29b924752982c23a771a431921a97f5f6fefa 100644 (file)
@@ -58,7 +58,7 @@ script#tool-view-template(type="text/x-template")
       fieldset.modbus-program(
         v-if="is_modbus && this.tool_type != 'HUANYANG VFD'")
         h2 Active Modbus Program
-        p
+        p(v-if="$root.modified")
           | (Click #[tt(class="save") Save] to activate the selected
           | #[b tool-type].)
         table.modbus-regs.fixed-regs
@@ -146,6 +146,46 @@ script#tool-view-template(type="text/x-template")
           |
           | and spindle type.
 
+      .notes(v-if="tool_type.startsWith('NOWFOREVER VFD')")
+        h2 Notes
+        p Set the following using the VFD's frontpanel.
+        table.modbus-regs.fixed-regs
+          tr
+            th Address
+            th Value
+            th Meaning
+            th Description
+          tr
+            td.reg-addr P0-000
+            td.reg-value 2
+            td Modbus communication
+            td Command source
+          tr
+            td.reg-addr P0-001
+            td.reg-value 0
+            td Main frequence X
+            td Select frequency source
+          tr
+            td.reg-addr P0-002
+            td.reg-value 6
+            td Modbus communication
+            td Main frequency X
+          tr
+            td.reg-addr P0-055
+            td.reg-value 1
+            td Modbus ID
+            td Must match #[tt bus-id] above
+          tr
+            td.reg-addr P0-056
+            td.reg-value 2
+            td 9600 baud
+            td Must match #[tt baud] above
+          tr
+            td.reg-addr P0-057
+            td.reg-value 0
+            td 1 start, 8 data, no parity, 1 stop
+            td Must match #[tt parity] above
+
       .notes(v-if="tool_type.startsWith('DELTA VFD015M21A')")
         h2 Notes
         p Set the following using the VFD's frontpanel.
index 110e51313950abc016f77fc9293ce963db4e02c7..6ef0db7e1da5ea0376c1d5271be2ba3c43a5e552 100644 (file)
@@ -65,7 +65,7 @@ def serial_set_low_latency(sp):
 
     ss = serial_struct()
     fcntl.ioctl(sp, termios.TIOCGSERIAL, ss)
-    ss.flags |= 1 << ASYNCB_LOW_LATENCY
+    ss.flags |= 1 << ASYNCB_LOW_LATENCY # pylint: disable=no-member
     fcntl.ioctl(sp, termios.TIOCSSERIAL, ss)
 
 
@@ -88,7 +88,7 @@ class AVR(object):
             self.sp = serial.Serial(self.ctrl.args.serial, self.ctrl.args.baud,
                                     rtscts = 1, timeout = 0, write_timeout = 0)
             self.sp.nonblocking()
-            serial_set_low_latency(self.sp)
+            #serial_set_low_latency(self.sp)
 
         except Exception as e:
             self.sp = None
@@ -122,6 +122,7 @@ class AVR(object):
 
     def _serial_read(self):
         try:
+            data = ''
             data = self.sp.read(self.sp.in_waiting)
             self.read_cb(data)
 
index 495e746cace943cc13a33a97d11239129c044b59..7f6be715071415e2b1b99cae34d5102796156cf5 100644 (file)
@@ -39,66 +39,83 @@ class AVREmu(object):
         self.ctrl = ctrl
         self.log = ctrl.log.get('AVREmu')
 
-        self.avrOut = None
-        self.avrIn = None
-        self.i2cOut = None
-        self.read_cb = None
+        self.avrOut   = None
+        self.avrIn    = None
+        self.i2cOut   = None
+        self.read_cb  = None
         self.write_cb = None
-        self.pid = None
+        self.pid      = None
 
 
     def close(self):
-        if self.pid is None: return
-        os.kill(self.pid, signal.SIGKILL)
-        os.waitpid(self.pid, 0)
-        self.pid = None
+        # Close pipes
+        def _close(fd, withHandle):
+            if fd is None: return
+            try:
+                if withHandle: self.ctrl.ioloop.remove_handler(fd)
+            except: pass
+            try:
+                os.close(fd)
+            except: pass
+
+        _close(self.avrOut, True)
+        _close(self.avrIn,  True)
+        _close(self.i2cOut, False)
+
+        self.avrOut, self.avrIn, self.i2cOut = None, None, None
+
+        # Kill process and wait for it
+        if self.pid is not None:
+            os.kill(self.pid, signal.SIGKILL)
+            os.waitpid(self.pid, 0)
+            self.pid = None
 
 
     def _start(self):
         try:
             self.close()
 
-            if self.avrOut is not None:
-                self.ctrl.ioloop.remove_handler(self.avrOut)
-                os.close(self.avrOut)
-
-            if self.avrIn is not None:
-                self.ctrl.ioloop.remove_handler(self.avrIn)
-                os.close(self.avrIn)
-
-            if self.i2cOut is not None: os.close(self.i2cOut)
-
-            stdinFDs = os.pipe()
+            # Create pipes
+            stdinFDs  = os.pipe()
             stdoutFDs = os.pipe()
-            i2cFDs = os.pipe()
+            i2cFDs    = os.pipe()
+
             self.pid = os.fork()
 
-            if not self.pid: # Child
-                os.dup2(stdinFDs[0], 0)
+            if not self.pid:
+                # Dup child ends
+                os.dup2(stdinFDs[0],  0)
                 os.dup2(stdoutFDs[1], 1)
-                os.dup2(i2cFDs[0], 3)
+                os.dup2(i2cFDs[0],    3)
 
+                # Close orig fds
                 os.close(stdinFDs[0])
                 os.close(stdoutFDs[1])
                 os.close(i2cFDs[0])
 
+                # Close parent ends
+                os.close(stdinFDs[1])
+                os.close(stdoutFDs[0])
+                os.close(i2cFDs[1])
+
                 cmd = ['bbemu']
                 if self.ctrl.args.fast_emu: cmd.append('--fast')
 
                 os.execvp(cmd[0], cmd)
                 os._exit(1) # In case of failure
 
-            # Parent
+            # Parent, close child ends
             os.close(stdinFDs[0])
             os.close(stdoutFDs[1])
             os.close(i2cFDs[0])
 
-            os.set_blocking(stdinFDs[1], False)
+            # Non-blocking IO
+            os.set_blocking(stdinFDs[1],  False)
             os.set_blocking(stdoutFDs[0], False)
-            os.set_blocking(i2cFDs[1], False)
+            os.set_blocking(i2cFDs[1],    False)
 
             self.avrOut = stdinFDs[1]
-            self.avrIn = stdoutFDs[0]
+            self.avrIn  = stdoutFDs[0]
             self.i2cOut = i2cFDs[1]
 
             ioloop = self.ctrl.ioloop
@@ -110,8 +127,7 @@ class AVREmu(object):
             self.write_enabled = True
 
         except Exception:
-            self.pid = None
-            self.avrOut, self.avrIn, self.i2cOut = None, None, None
+            self.close()
             self.log.exception('Failed to start bbemu')
 
 
index 589ee40f03079d3581b79c330b82991de3de83a2..a5f0fcdef5101f8b42af99309c46ddf40fd4537d 100755 (executable)
@@ -19,7 +19,7 @@
 #                                                                              #
 #       You should have received a copy of the GNU Lesser General Public       #
 #                License along with the software.  If not, see                 #
-#                       <http://www.gnu.org/icenses/>.                        #
+#                       <http://www.gnu.org/icenses/>.                         #
 #                                                                              #
 #                For information regarding this software email:                #
 #                  "Joseph Coffland" <joseph@buildbotics.com>                  #
index cea8558cbcf3d33453d46f26c3830b0c016dea30..090be3648b069a6041af697962cffeffcd588d98 100644 (file)
@@ -87,7 +87,9 @@ class Log(object):
 
         self.level = DEBUG if args.verbose else INFO
 
-        self.f = None if self.path is None else open(self.path, 'w')
+        # Open log, rotate if necessary
+        self.f = None
+        self._open()
 
         # Log header
         version = pkg_resources.require('bbctrl')[0].version
@@ -123,8 +125,10 @@ class Log(object):
         s = hdr + ('\n' + hdr).join(msg.split('\n'))
 
         if self.f is not None:
+            if 1e22 <= self.bytes_written + len(s) + 1: self._open()
             self.f.write(s + '\n')
             self.f.flush()
+            self.bytes_written += len(s) + 1
 
         print(s)
 
@@ -135,3 +139,22 @@ class Log(object):
         if where is not None: msg['where'] = where
 
         self.broadcast(dict(log = msg))
+
+
+    def _open(self):
+        if self.path is None: return
+        if self.f is not None: self.f.close()
+        self._rotate(self.path)
+        self.f = open(self.path, 'w')
+        self.bytes_written = 0
+
+
+    def _rotate(self, path, n = None):
+        fullpath = '%s.%d' % (path, n) if n is not None else path
+        nextN = (0 if n is None else n) + 1
+
+        if os.path.exists(fullpath):
+            if n == 16: os.unlink(fullpath)
+            else: self._rotate(path, nextN)
+
+            os.rename(fullpath, '%s.%d' % (path, nextN))
index 2e251795cea0c2e8abec7f6ecd9b129b72bed3b6..d961d78067aadce39329cdd3380fd79906df58e0 100644 (file)
@@ -31,6 +31,7 @@ import json
 import hashlib
 import glob
 import tempfile
+import signal
 from concurrent.futures import Future
 from tornado import gen, process, iostream
 import bbctrl
@@ -91,7 +92,10 @@ class Plan(object):
     def terminate(self):
         if self.cancel: return
         self.cancel = True
-        if self.pid is not None: os.kill(self.pid)
+        if self.pid is not None:
+            try:
+                os.kill(self.pid, signal.SIGKILL)
+            except: pass
 
 
     def delete(self):
@@ -226,7 +230,7 @@ class Preplanner(object):
 
     def invalidate(self, filename):
         if filename in self.plans:
-            self.plans[filename].terinate()
+            self.plans[filename].terminate()
             del self.plans[filename]
 
 
index 4035c91d47f5cc10b5876c71ab2e665c3295c5b1..b8eed91cb489a6abe696ea3a2a540ab920002d81 100644 (file)
@@ -80,7 +80,7 @@ def on_exit(sig = 0, func = None):
         ctrl.close()
         ctrl = None
 
-    sys.exit(1)
+    sys.exit(0)
 
 
 def time_str():
@@ -125,7 +125,7 @@ def parse_args():
                         type = int, help = 'HTTP port')
     parser.add_argument('-a', '--addr', metavar = 'IP', default = '0.0.0.0',
                         help = 'HTTP address to bind')
-    parser.add_argument('-s', '--serial', default = '/dev/ttyAMA0',
+    parser.add_argument('-s', '--serial', default = '/dev/ttyBB0',
                         help = 'Serial device')
     parser.add_argument('-b', '--baud', default = 230400, type = int,
                         help = 'Serial baud rate')