/crap
/pkg
/mnt
+/demo
+/bbctrl-*
node_modules
*~
\#*
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.
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
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)
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
--- /dev/null
+# 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
{
"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+",
+++ /dev/null
-#!/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
--- /dev/null
+[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
--- /dev/null
+#!/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" "$@"
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
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
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/
# 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
#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"
#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
#include <stdint.h>
#include <stdbool.h>
+#include <util/atomic.h>
+
+
#ifndef RING_BUF_NAME
#error Must define RING_BUF_NAME
#endif
#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; \
// 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;
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;
}
#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 {
--- /dev/null
+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
--- /dev/null
+/******************************************************************************\
+
+ 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);
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
|
| 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.
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)
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
def _serial_read(self):
try:
+ data = ''
data = self.sp.read(self.sp.in_waiting)
self.read_cb(data)
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
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')
# #
# 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> #
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
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)
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))
import hashlib
import glob
import tempfile
+import signal
from concurrent.futures import Future
from tornado import gen, process, iostream
import bbctrl
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):
def invalidate(self, filename):
if filename in self.plans:
- self.plans[filename].terinate()
+ self.plans[filename].terminate()
del self.plans[filename]
ctrl.close()
ctrl = None
- sys.exit(1)
+ sys.exit(0)
def time_str():
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')