diff options
Diffstat (limited to 'stm32/app')
-rw-r--r-- | stm32/app/Makefile | 80 | ||||
-rw-r--r-- | stm32/app/asm_fns.h | 4 | ||||
-rw-r--r-- | stm32/app/board.h | 15 | ||||
-rw-r--r-- | stm32/app/cdcacm.c | 221 | ||||
-rw-r--r-- | stm32/app/clock.ld | 43 | ||||
-rw-r--r-- | stm32/app/dfu.c | 86 | ||||
-rw-r--r-- | stm32/app/dummy_kb.c | 26 | ||||
-rw-r--r-- | stm32/app/events.c | 101 | ||||
-rw-r--r-- | stm32/app/gdb.script | 2 | ||||
-rw-r--r-- | stm32/app/hands.c | 90 | ||||
-rw-r--r-- | stm32/app/main.c | 50 | ||||
-rw-r--r-- | stm32/app/motor.c | 75 | ||||
-rw-r--r-- | stm32/app/pins.h | 54 | ||||
-rw-r--r-- | stm32/app/project.h | 50 | ||||
-rw-r--r-- | stm32/app/prototypes.h | 81 | ||||
-rw-r--r-- | stm32/app/ring.c | 64 | ||||
-rw-r--r-- | stm32/app/ring.h | 7 | ||||
-rw-r--r-- | stm32/app/rtc.c | 170 | ||||
-rw-r--r-- | stm32/app/ticker.c | 74 | ||||
-rw-r--r-- | stm32/app/time_fn.c | 650 | ||||
-rw-r--r-- | stm32/app/time_fn.h | 47 | ||||
-rw-r--r-- | stm32/app/usart.c | 88 | ||||
-rw-r--r-- | stm32/app/usb.c | 148 |
23 files changed, 2226 insertions, 0 deletions
diff --git a/stm32/app/Makefile b/stm32/app/Makefile new file mode 100644 index 0000000..997011f --- /dev/null +++ b/stm32/app/Makefile @@ -0,0 +1,80 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de> +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library 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 this library. If not, see <http://www.gnu.org/licenses/>. +## + +CPROTO=cproto +PROG=clock + +LDLIBS+=-lm + +V=1 +default: ${PROG}.elf + +CSRCS= main.c cdcacm.c dfu.c ring.c usart.c ticker.c dummy_kb.c usb.c rtc.c time_fn.c events.c hands.c motor.c +HSRCS = project.h + + +BINARY = ${PROG} +OBJS = ${CSRCS:%.c=%.o} + +include ../Makefile.include + +DID=$(shell printf '\#include "id.h"\nID_PRODUCT' | ${CC} -I.. -E - | grep -v ^\# ) +VID=$(shell printf '\#include "id.h"\nID_VENDOR' | ${CC} -I.. -E - | grep -v ^\# ) + +INCLUDES += -I.. + + +%.bin: %.elf + $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + +%.dfu:%.bin + ../tools/dfuse-pack.py -D 0x483:0xff03 -b 0x08002000:$< $@ + +dfu:${PROG}.bin + dfu-util -R -a 0 -d 0483:ff03,0483:df11 -s 0x08002000:leave -D $< + +program: ${PROG}.elf + echo halt | nc -t localhost 4444 + echo flash write_image erase ${PWD}/$< 0x2000 | nc -t localhost 4444 + echo reset run | nc -t localhost 4444 + +ds: + $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) \ + +debug: ${PROG}.elf + ${PREFIX}-gdb -x gdb.script ${PROG}.elf + +reset: + $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) \ + -c "init" -c "reset run" \ + -c shutdown + + + + +protos: + echo -n > prototypes.h + ${CPROTO} $(INCLUDES) $(DEFINES) -e -v ${CSRCS} > prototypes.h.tmp + mv -f prototypes.h.tmp prototypes.h + + +tidy: + astyle -A3 -s2 --attach-extern-c -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd ${CSRCS} ${HSRCS} || astyle -A3 -s2 -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd ${CSRCS} ${HSRCS} diff --git a/stm32/app/asm_fns.h b/stm32/app/asm_fns.h new file mode 100644 index 0000000..b0d10d9 --- /dev/null +++ b/stm32/app/asm_fns.h @@ -0,0 +1,4 @@ +static inline void compiler_mb(void) { +asm volatile (""); +} + diff --git a/stm32/app/board.h b/stm32/app/board.h new file mode 100644 index 0000000..41eeafc --- /dev/null +++ b/stm32/app/board.h @@ -0,0 +1,15 @@ + +#define LED1 GPIO8 +#define LED1_PORT GPIOB + +#define LED2 GPIO9 +#define LED2_PORT GPIOB + + +#define USART1_TX GPIO_USART1_TX +#define USART1_TX_PORT GPIOA + +#define USART1_RX GPIO_USART1_RX +#define USART1_RX_PORT GPIOA + + diff --git a/stm32/app/cdcacm.c b/stm32/app/cdcacm.c new file mode 100644 index 0000000..0b4a4e3 --- /dev/null +++ b/stm32/app/cdcacm.c @@ -0,0 +1,221 @@ +#include "project.h" + + +#define BUFFER_SIZE 512 + +ring_t cdcacm_rx_ring; +static uint8_t cdcacm_rx_ring_buf[BUFFER_SIZE]; + +ring_t cdcacm_tx_ring; +static uint8_t cdcacm_tx_ring_buf[BUFFER_SIZE]; + +static int cdcacm_ready = 0; + +#define COMM_EP 0x83 +#define DATA_IN 0x01 +#define DATA_OUT 0x82 + +static const struct usb_endpoint_descriptor comm_endp[] = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = COMM_EP, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 16, + .bInterval = 255, + } +}; + +static const struct usb_endpoint_descriptor data_endp[] = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = DATA_IN, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 1, + }, + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = DATA_OUT, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 1, + } +}; + +static const struct { + struct usb_cdc_header_descriptor header; + struct usb_cdc_call_management_descriptor call_mgmt; + struct usb_cdc_acm_descriptor acm; + struct usb_cdc_union_descriptor cdc_union; +} __attribute__ ((packed)) cdcacm_functional_descriptors = { + .header = { + .bFunctionLength = sizeof (struct usb_cdc_header_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_HEADER, + .bcdCDC = 0x0110, + }, + .call_mgmt = { + .bFunctionLength = + sizeof (struct usb_cdc_call_management_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, + .bmCapabilities = 0, + .bDataInterface = 3, + }, + .acm = { + .bFunctionLength = sizeof (struct usb_cdc_acm_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_ACM, + .bmCapabilities = 0, + }, + .cdc_union = { + .bFunctionLength = sizeof (struct usb_cdc_union_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_UNION, + .bControlInterface = 2, + .bSubordinateInterface0 = 3, + } +}; + +const struct usb_interface_descriptor comm_iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 2, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, + .iInterface = 0, + .endpoint = comm_endp, + .extra = &cdcacm_functional_descriptors, + .extralen = sizeof (cdcacm_functional_descriptors) +}; + +const struct usb_interface_descriptor data_iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 3, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + .endpoint = data_endp, +}; + + +const struct usb_iface_assoc_descriptor cdc_iface_assoc = { + .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 2, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_CDC, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTOCOL_AT, + .iFunction = 6, +}; + + + +enum usbd_request_return_codes +cdcacm_control_request (usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, + uint16_t *len, + usbd_control_complete_callback *complete) { + (void) complete; + (void) buf; + (void) usbd_dev; + + + switch (req->bRequest) + { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: { + /* + * This Linux cdc_acm driver requires this to be implemented + * even though it's optional in the CDC spec, and we don't + * advertise it in the ACM functional descriptor. + */ + return USBD_REQ_HANDLED; + } + + case USB_CDC_REQ_SET_LINE_CODING: + if (*len < sizeof (struct usb_cdc_line_coding)) + return USBD_REQ_NOTSUPP; + + return USBD_REQ_HANDLED; + } + + return USBD_REQ_NEXT_CALLBACK; +} + + + +void cdcacm_tick (void) +{ + unsigned ep = DATA_OUT; + uint8_t buf[16]; + uint8_t *ptr = buf; + size_t n = 0; + + if (!cdcacm_ready) + return; + + if (ring_empty (&cdcacm_tx_ring)) + return; + + /* Return if endpoint is already enabled. */ + if ((*USB_EP_REG (ep & 0x7f) & USB_EP_TX_STAT) == USB_EP_TX_STAT_VALID) + return; + + while (!ring_read_byte (&cdcacm_tx_ring, ptr++)) { + n++; + + if (n == sizeof (buf)) break; + } + + usbd_ep_write_packet (usb_device, ep, buf, n); +} + +int cdcacm_write (char *ptr, int len) +{ + int ret; + + ret = ring_write (&cdcacm_tx_ring, (uint8_t *) ptr, len); + return ret; +} + + + +static void cdcacm_data_rx_cb (usbd_device *usbd_dev, uint8_t ep) +{ + (void) ep; + uint8_t buf[64]; + int len = usbd_ep_read_packet (usbd_dev, DATA_IN, buf, 64); + + if (len) + ring_write (&cdcacm_rx_ring, buf, len); +} + +void cdcacm_set_config (usbd_device *usbd_dev, uint16_t wValue) +{ + (void) wValue; + + usbd_ep_setup (usbd_dev, DATA_IN, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb); + usbd_ep_setup (usbd_dev, DATA_OUT, USB_ENDPOINT_ATTR_BULK, 64, NULL); + usbd_ep_setup (usbd_dev, COMM_EP, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); + cdcacm_ready = 1; +} + +void cdcacm_rings_init (void) +{ + ring_init (&cdcacm_rx_ring, cdcacm_rx_ring_buf, sizeof (cdcacm_rx_ring_buf)); + ring_init (&cdcacm_tx_ring, cdcacm_tx_ring_buf, sizeof (cdcacm_tx_ring_buf)); +} + + diff --git a/stm32/app/clock.ld b/stm32/app/clock.ld new file mode 100644 index 0000000..20cbff9 --- /dev/null +++ b/stm32/app/clock.ld @@ -0,0 +1,43 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de> + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08002000, LENGTH = 120K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include the common ld script. */ +INCLUDE cortex-m-generic.ld + +dfu_shared_location = ORIGIN(ram) + LENGTH(ram) - 1024; + +/* PROVIDE(_stack = dfu_shared_location ); */ + +SECTIONS +{ + .dfu_shared dfu_shared_location :{ + dfu_flag = .; + } +} + + diff --git a/stm32/app/dfu.c b/stm32/app/dfu.c new file mode 100644 index 0000000..29a2428 --- /dev/null +++ b/stm32/app/dfu.c @@ -0,0 +1,86 @@ +#include "project.h" + +const struct usb_dfu_descriptor dfu_function = { + .bLength = sizeof (struct usb_dfu_descriptor), + .bDescriptorType = DFU_FUNCTIONAL, + .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, + .wDetachTimeout = 255, + .wTransferSize = 1024, + .bcdDFUVersion = 0x011A, +}; + +const struct usb_interface_descriptor dfu_iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = 0xFE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, + .iInterface = 4, + .extra = &dfu_function, + .extralen = sizeof (dfu_function), +}; + + +const struct usb_iface_assoc_descriptor dfu_iface_assoc = { + .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 1, + .bInterfaceCount = 1, + .bFunctionClass = 0xfe, + .bFunctionSubClass = 1, + .bFunctionProtocol = 1, + .iFunction = 5, +}; + + +static void +dfu_detach_complete (usbd_device *usbd_dev, struct usb_setup_data *req) +{ + (void) req; + (void) usbd_dev; + dfu_flag = 0xfee1dead; + + scb_reset_core(); +} + +enum usbd_request_return_codes +dfu_control_request (usbd_device *usbd_dev, struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback *complete) { + (void) buf; + (void) len; + (void) usbd_dev; + + if ((req->bmRequestType & 0x7F) != 0x21) + return USBD_REQ_NEXT_CALLBACK; + + switch (req->bRequest) + { + case DFU_GETSTATUS: { + (*buf) [0] = DFU_STATUS_OK; + (*buf) [1] = 0; + (*buf) [2] = 0; + (*buf) [3] = 0; + (*buf) [4] = STATE_APP_IDLE; + (*buf) [5] = 0; /* iString not used here */ + *len = 6; + return USBD_REQ_HANDLED; + } + + case DFU_GETSTATE: + /* Return state with no state transision. */ + *buf[0] = STATE_APP_IDLE; + *len = 1; + return USBD_REQ_HANDLED; + + case DFU_DETACH: + *complete = dfu_detach_complete; + return USBD_REQ_HANDLED; + } + + return USBD_REQ_NEXT_CALLBACK; +} + diff --git a/stm32/app/dummy_kb.c b/stm32/app/dummy_kb.c new file mode 100644 index 0000000..a1d61c7 --- /dev/null +++ b/stm32/app/dummy_kb.c @@ -0,0 +1,26 @@ +#include "project.h" + +const struct usb_interface_descriptor dummy_kb_iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, +}; + + +const struct usb_iface_assoc_descriptor dummy_kb_iface_assoc = { + .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 0, + .bInterfaceCount = 1, + .bFunctionClass = 0xff, + .bFunctionSubClass = 0, + .bFunctionProtocol = 0, + .iFunction = 4, +}; + diff --git a/stm32/app/events.c b/stm32/app/events.c new file mode 100644 index 0000000..2ad9b47 --- /dev/null +++ b/stm32/app/events.c @@ -0,0 +1,101 @@ +#include "project.h" + +#define D2R(a) ((a)*(M_PI/180.)) + + + + +// shamelessly stolen from Meeus Astronmical Algorithms Chapter 27 table 27.B +// chapter 25 is probably more tractable, but this is easier to code up + +static double mean_vernal_equinox (unsigned year) +{ + double y = year; + y -= 2000; + y *= 0.001; + return 2451623.80984 + 365242.37404 * y + 0.05169 * (y * y) - 0.00411 * (y * y * y) - 0.00057 * (y * y * y * y); +} + +static double mean_summer_solstice (unsigned year) +{ + double y = year; + y -= 2000; + y *= 0.001; + return 2451716.56767 + 365241.62603 * y + 0.00325 * (y * y) + 0.00888 * (y * y * y) - 0.00030 * (y * y * y * y); +} + +static double mean_autumnal_equinox (unsigned year) +{ + double y = year; + y -= 2000; + y *= 0.001; + return 2451810.21715 + 365242.01767 * y - 0.11575 * (y * y) + 0.00337 * (y * y * y) + 0.00078 * (y * y * y * y); +} + +static double mean_winter_solstice (unsigned year) +{ + double y = year; + y -= 2000; + y *= 0.001; + return 2451900.05952 + 365242.74049 * y - 0.06223 * (y * y) - 0.00823 * (y * y * y) + 0.00032 * (y * y * y * y); +} + + +static double orbital_periodic_terms (double t) +{ +#define N 24 + const double A[N] = {485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45, + 44, 29, 18, 17, 16, 14, 12, 12, 12, 9, 8 + }; + const double B[N] = {D2R (324.96), D2R (337.23), D2R (342.08), D2R (27.85), + D2R (73.14), D2R (171.52), D2R (222.54), D2R (296.72), + D2R (243.58), D2R (119.81), D2R (297.17), D2R (21.02), + D2R (247.54), D2R (325.15), D2R (60.93), D2R (155.12), + D2R (288.79), D2R (198.04), D2R (199.76), D2R (95.39), + D2R (287.11), D2R (320.81), D2R (227.73), D2R (15.45) + }; + const double C[N] = {D2R (1934.136), D2R (32964.467), D2R (20.186), + D2R (445267.112), D2R (45036.886), D2R (22518.443), + D2R (65928.934), D2R (3034.906), D2R (9037.513), + D2R (33718.147), D2R (150.678), D2R (2281.226), + D2R (29929.562), D2R (31555.956), D2R (4443.417), + D2R (67555.328), D2R (4562.452), D2R (62894.029), + D2R (31436.921), D2R (14577.848), D2R (31931.756), + D2R (34777.259), D2R (1222.114), D2R (16859.074) + }; + double s = 0; + unsigned i; + + for (i = 0; i < N; ++i) + s += A[i] * cos (B[i] + (C[i] * t)); + + return s; +} + + +static double mean_to_real (double j0) +{ + + double t = (j0 - 2451545.) / 36525.; + double w = D2R ((35999.373 * t) - 2.47); + double dl = 1 + 0.0334 * cos (w) + 0.0007 * cos (2. * w); + double s = orbital_periodic_terms (t); + +#if 0 + printf ("j0=%.6f\r\n", j0); + printf ("t=%.6f\r\n", t); + printf ("w=%.6f\r\n", w); + printf ("dl=%.6f\r\n", dl); + printf ("s=%.6f\r\n", s); +#endif + + return j0 + ((0.00001 * s) / dl); +} + + + +double autumnal_equinox (unsigned y) +{ + return mean_to_real (mean_autumnal_equinox (y)); + // return mean_to_real (mean_summer_solstice (y)); +} diff --git a/stm32/app/gdb.script b/stm32/app/gdb.script new file mode 100644 index 0000000..7cf9d09 --- /dev/null +++ b/stm32/app/gdb.script @@ -0,0 +1,2 @@ +target remote localhost:3333 +cont diff --git a/stm32/app/hands.c b/stm32/app/hands.c new file mode 100644 index 0000000..5414d9e --- /dev/null +++ b/stm32/app/hands.c @@ -0,0 +1,90 @@ +#include "project.h" + + +unsigned hands_pos[HANDS]; +unsigned hands_ready; + + +static unsigned calc_minute_pos (MTIME *m) +{ + uint64_t p; + + p = m->minute; + p *= 100; + p += m->second; + p *= 1000; + p += m->nanosecond / 1000000; + + p *= MOTOR_STEPS; + p /= 10000000; + + + + return (unsigned) p; + +} + +static unsigned calc_hour_pos (MTIME *m) +{ + uint64_t p; + + p = m->hour; + p *= 100; + p += m->minute; + p *= 100; + p += m->second; + p *= 1000; + p += m->nanosecond / 1000000; + + p *= MOTOR_STEPS; + p /= 100000000; + + + + return (unsigned) p; +} + + + + +void hands_tick (void) +{ + EPOCH e; + UTC u; + MTIME m; + static unsigned old_pos[HANDS]; + unsigned i, p; + + + static uint32_t last; + + if ((ticks - last) < 100) return; + + last = ticks; + + if (!rtc_ready) return; + + + e = rtc_get(); + u = time_epoch_to_utc (e); + m = time_to_metric (e, u); + + + hands_pos[0] = calc_hour_pos (&m); + hands_pos[1] = calc_minute_pos (&m); + hands_ready = 1; + + for (i = 0, p = 0; i < HANDS; ++i) { + if (hands_pos[i] != old_pos[i]) p++; + + old_pos[i] = hands_pos[i]; + + } + + if (p) { + time_print_utc ("UTC: ", &u, NULL); + time_print_metric ("Metric: ", &m, NULL); + printf ("Hands: hour %4d/%4d min %4d/%4d\r\n", hands_pos[0], MOTOR_STEPS, hands_pos[1], MOTOR_STEPS); + } + +} diff --git a/stm32/app/main.c b/stm32/app/main.c new file mode 100644 index 0000000..4722479 --- /dev/null +++ b/stm32/app/main.c @@ -0,0 +1,50 @@ +#include "project.h" + + + + + +int main (void) +{ + /*set up pll */ + rcc_clock_setup_pll (&rcc_hse_configs[RCC_HSE_CONFIG]); + + /*turn on clocks to periferals */ + rcc_periph_clock_enable (RCC_GPIOA); + rcc_periph_clock_enable (RCC_GPIOB); + rcc_periph_clock_enable (RCC_GPIOC); + rcc_periph_clock_enable (RCC_USART1); + rcc_periph_clock_enable (RCC_AFIO); + rcc_periph_clock_enable (RCC_PWR); + rcc_periph_clock_enable (RCC_BKP); + pwr_disable_backup_domain_write_protect(); + DWT_CTRL |= DWT_CTRL_CYCCNTENA; + + + nvic_set_priority (NVIC_SYSTICK_IRQ, 0x80); + nvic_set_priority (NVIC_USART1_IRQ, 0x40); + nvic_set_priority (NVIC_USB_HP_CAN_TX_IRQ, 0x40); + nvic_set_priority (NVIC_USB_LP_CAN_RX0_IRQ, 0x40); + nvic_set_priority (NVIC_RTC_IRQ, 0x10); + + MAP_OUTPUT_OD (LED1); + MAP_OUTPUT_OD (LED2); + + + usart_init(); + cdcacm_rings_init(); + rtc_init(); + + printf ("Startup:\r\n"); + + motor_init(); + ticker_init(); + usb_init(); + + + for (;;); + + + return 0; +} + diff --git a/stm32/app/motor.c b/stm32/app/motor.c new file mode 100644 index 0000000..392089b --- /dev/null +++ b/stm32/app/motor.c @@ -0,0 +1,75 @@ +#include "project.h" + + +static unsigned motor_pos[HANDS]; + + +static void coils (unsigned m, int a, int b, int c, int d) +{ + printf ("Motor %d: %+d %+d\r\n", m, a - b, c - d); +} + +static void step (unsigned m, int d) +{ + unsigned s = motor_pos[m]; + + if (d < 0) d += MOTOR_STEPS; + + s += d; + s %= MOTOR_STEPS; + + coils (m, !! ((s + 6) & 4), !! ((s + 1) & 4), !! ((s + 4) & 4), !! ((s + 7) & 4)); + + if (!m) + BKP_DR8 = motor_pos[0] = s; + else + BKP_DR9 = motor_pos[1] = s; + + +} + + + + + +void motor_tick (void) +{ + static uint32_t last; + unsigned i, d; + + if ((ticks - last) < 10) return; + + last = ticks; + + if (!hands_ready) return; + + //printf("HANDS: %d -> %d %d -> %d\r\n",hands_pos[0],motor_pos[0],hands_pos[1],motor_pos[1]); + + + for (i = 0; i < HANDS; ++i) { + d = MOTOR_STEPS + hands_pos[i]; + d -= motor_pos[i]; + d %= MOTOR_STEPS; + + if (d) { + if (d < (MOTOR_STEPS / 2)) + step (i, 1); + else + step (i, -1); + } + } +} + + + + +void motor_init (void) +{ + + motor_pos[0] = BKP_DR8; + motor_pos[1] = BKP_DR9; + + step (0, 0); + step (1, 0); + +} diff --git a/stm32/app/pins.h b/stm32/app/pins.h new file mode 100644 index 0000000..954396f --- /dev/null +++ b/stm32/app/pins.h @@ -0,0 +1,54 @@ +#ifndef _PINS_H_ +#define _PINS_H_ + +/* st seem to change these with every chip revision */ + +#define MAP_ANA(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, a ); \ + } while (0) + +#define MAP_AF(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, a ); \ + } while (0) + +/* STM32F1 doesn't have AF pull up, but also doesn't disconnect af inputs so just use regular pull up */ +#define MAP_AF_PU(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \ + gpio_set( a ## _PORT, a); \ + } while (0) + +#define MAP_AF_OD(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, a ); \ + } while (0) + + +#define MAP_OUTPUT_PP(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, a ); \ + } while (0) + + +#define MAP_OUTPUT_OD(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, a ); \ + } while (0) + + +/* STM32F1 madly uses the output register to drive the other end of the resistor, so pull up */ +/* requires us to write a 1 there */ + +#define MAP_INPUT_PU(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \ + gpio_set( a ## _PORT, a); \ + } while (0) + + +#define MAP_INPUT(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, a); \ + } while (0) + + +#define CLEAR(a) gpio_clear( a ## _PORT, a) +#define SET(a) gpio_set( a ## _PORT, a) +#define GET(a) gpio_get( a ## _PORT, a) +#define TOGGLE(a) gpio_toggle( a ## _PORT, a) + +#endif diff --git a/stm32/app/project.h b/stm32/app/project.h new file mode 100644 index 0000000..fd71876 --- /dev/null +++ b/stm32/app/project.h @@ -0,0 +1,50 @@ +#include <stdlib.h> +#include <math.h> +#include <stdio.h> +#include <errno.h> + +#include <libopencm3/stm32/rcc.h> +#include <libopencm3/stm32/gpio.h> +#include <libopencm3/stm32/usart.h> +#include <libopencm3/stm32/i2c.h> +#include <libopencm3/stm32/st_usbfs.h> +#include <libopencm3/stm32/rtc.h> +#include <libopencm3/stm32/pwr.h> +#include <libopencm3/stm32/f1/bkp.h> +#include <libopencm3/cm3/systick.h> +#include <libopencm3/cm3/nvic.h> +#include <libopencm3/cm3/cortex.h> +#include <libopencm3/cm3/scb.h> +#include <libopencm3/cm3/dwt.h> +#include <libopencm3/usb/usbd.h> +#include <libopencm3/usb/hid.h> +#include <libopencm3/usb/dfu.h> +#include <libopencm3/usb/cdc.h> +#include <id.h> + + + +#define RCC_HSE_CONFIG RCC_CLOCK_HSE8_72MHZ +#define US (72) +#define MS (US * 1000) +#define HZ (MS * 1000) +#define MS_TO_TICKS(a) ((a) *2) + +#define TRACE do { printf("%s:%d\r\n",__FILE__,__LINE__); } while (0) + +#define DWT_FREQ 48000000 + +#define HANDS 2 +#define MOTOR_STEPS (1080 * 2) + +#include "board.h" +#include "ring.h" +#include "pins.h" +#include "time_fn.h" +#include "asm_fns.h" + +#include "prototypes.h" + + + +extern uint32_t dfu_flag; diff --git a/stm32/app/prototypes.h b/stm32/app/prototypes.h new file mode 100644 index 0000000..751ba25 --- /dev/null +++ b/stm32/app/prototypes.h @@ -0,0 +1,81 @@ +/* main.c */ +extern int main(void); +/* cdcacm.c */ +extern ring_t cdcacm_rx_ring; +extern ring_t cdcacm_tx_ring; +extern const struct usb_interface_descriptor comm_iface; +extern const struct usb_interface_descriptor data_iface; +extern const struct usb_iface_assoc_descriptor cdc_iface_assoc; +extern enum usbd_request_return_codes cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete); +extern void cdcacm_tick(void); +extern int cdcacm_write(char *ptr, int len); +extern void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue); +extern void cdcacm_rings_init(void); +/* dfu.c */ +extern const struct usb_dfu_descriptor dfu_function; +extern const struct usb_interface_descriptor dfu_iface; +extern const struct usb_iface_assoc_descriptor dfu_iface_assoc; +extern enum usbd_request_return_codes dfu_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete); +/* ring.c */ +extern void ring_init(ring_t *r, uint8_t *buf, size_t len); +extern int ring_write_byte(ring_t *r, uint8_t c); +extern int ring_empty(ring_t *r); +extern int ring_read_byte(ring_t *r, uint8_t *c); +extern int ring_write(ring_t *r, uint8_t *buf, size_t len); +/* usart.c */ +extern ring_t usart_rx_ring; +extern ring_t usart_tx_ring; +extern void usart1_isr(void); +extern void usart_kick(void); +extern int _write(int file, char *ptr, int len); +extern void usart_init(void); +/* ticker.c */ +extern volatile uint32_t ticks; +extern unsigned led2; +extern void delay_us(uint32_t d); +extern void sys_tick_handler(void); +extern void ticker_init(void); +extern void delay_ms(uint32_t d); +/* dummy_kb.c */ +extern const struct usb_interface_descriptor dummy_kb_iface; +extern const struct usb_iface_assoc_descriptor dummy_kb_iface_assoc; +/* usb.c */ +extern uint8_t usbd_control_buffer[128]; +extern usbd_device *usb_device; +extern void usb_hp_can_tx_isr(void); +extern void usb_lp_can_rx0_isr(void); +extern void usb_init(void); +/* rtc.c */ +extern volatile unsigned rtc_ready; +extern void rtc_isr(void); +extern EPOCH rtc_get(void); +extern void rtc_init(void); +/* time_fn.c */ +extern EPOCH time_epoch_sub(EPOCH a, EPOCH b); +extern EPOCH time_epoch_add(EPOCH a, EPOCH b); +extern UTC time_epoch_to_utc(EPOCH epoch); +extern EPOCH time_utc_to_epoch(UTC u); +extern void utc_to_str(char *dst, UTC *u); +extern void time_print_utc(const char *p, UTC *u, const char *t); +extern void time_print_epoch(const char *p, EPOCH e, const char *t); +extern double time_utc_to_tjd(UTC u); +extern EPOCH time_tjd_to_epoch(double tjd); +extern UTC time_tjd_to_utc(double tjd); +extern RA ra_normalize(RA r); +extern RA time_utc_to_ra(UTC u); +extern RA time_st_to_ra(ST st); +extern ST time_ra_to_st(RA r); +extern ST time_utc_to_lst(UTC u, double lon); +extern MTIME time_to_metric(EPOCH e, UTC u); +extern MTIME time_utc_to_metric(UTC u); +extern MTIME time_epoch_to_metric(EPOCH e); +extern void time_print_metric(const char *p, MTIME *m, const char *t); +/* events.c */ +extern double autumnal_equinox(unsigned y); +/* hands.c */ +extern unsigned hands_pos[2]; +extern unsigned hands_ready; +extern void hands_tick(void); +/* motor.c */ +extern void motor_tick(void); +extern void motor_init(void); diff --git a/stm32/app/ring.c b/stm32/app/ring.c new file mode 100644 index 0000000..26bba3a --- /dev/null +++ b/stm32/app/ring.c @@ -0,0 +1,64 @@ +#include "project.h" + + +static inline size_t +ring_next (ring_t *r, size_t p) +{ + p++; + + if (p >= r->size) + p -= r->size; + + return p; +} + +void +ring_init (ring_t *r, uint8_t *buf, size_t len) +{ + r->data = buf; + r->size = len; + r->write = 0; + r->read = 0; +} + +int +ring_write_byte (ring_t *r, uint8_t c) +{ + size_t n = ring_next (r, r->write); + + if (n == r->read) + return -1; + + r->data[r->write] = c; + r->write = n; + return 0; +} + +int ring_empty (ring_t *r) +{ + return (r->read == r->write); +} + +int +ring_read_byte (ring_t *r, uint8_t *c) +{ + size_t n = ring_next (r, r->read); + + if (r->read == r->write) + return -1; + + *c = r->data[r->read]; + r->read = n; + return 0; +} + +int +ring_write (ring_t *r, uint8_t *buf, size_t len) +{ + while (len--) { + if (ring_write_byte (r, * (buf++))) + return -1; + } + + return 0; +} diff --git a/stm32/app/ring.h b/stm32/app/ring.h new file mode 100644 index 0000000..ba8887b --- /dev/null +++ b/stm32/app/ring.h @@ -0,0 +1,7 @@ +typedef struct ring +{ + uint8_t *data; + size_t size; + size_t write; + size_t read; +} ring_t; diff --git a/stm32/app/rtc.c b/stm32/app/rtc.c new file mode 100644 index 0000000..0030cfd --- /dev/null +++ b/stm32/app/rtc.c @@ -0,0 +1,170 @@ +#include "project.h" + +static volatile uint32_t hz = HZ; +static volatile uint32_t cycle_ref; +static volatile uint32_t rtc_ref; + +#define RTC_IN_LH 0x21f7 +#define RTC_IN_UH 0x6a6d + +static EPOCH rtc_offset; +static uint16_t rtc_half; + +volatile unsigned rtc_ready; + + + + +static EPOCH bkp_read_off (void) +{ + EPOCH e; + uint64_t v; + + v = BKP_DR1 & 0xffff; + v <<= 16; + v |= (BKP_DR2 & 0xffff); + v <<= 16; + v |= (BKP_DR3 & 0xffff); + v <<= 16; + v |= (BKP_DR4 & 0xffff); + + e.s = (int64_t) v; + + v = BKP_DR5 & 0xffff; + v <<= 16; + v |= (BKP_DR6 & 0xffff); + + e.ns = (int64_t) v; + + return e; +} + +static void bkp_write_off (EPOCH e) +{ + uint64_t v; + + v = (uint64_t) e.s; + + BKP_DR4 = (uint16_t) (v & 0xffff); + v >>= 16; + BKP_DR3 = (uint16_t) (v & 0xffff); + v >>= 16; + BKP_DR2 = (uint16_t) (v & 0xffff); + v >>= 16; + BKP_DR1 = (uint16_t) (v & 0xffff); + + v = (uint64_t) e.ns; + + BKP_DR6 = (uint16_t) (v & 0xffff); + v >>= 16; + BKP_DR5 = (uint16_t) (v & 0xffff); + +} + + + +void rtc_isr (void) +{ + uint32_t now; + static uint32_t then; + uint32_t v; + static int warm_up = 3; + + + now = DWT_CYCCNT; + compiler_mb(); + rtc_clear_flag (RTC_SEC); + + v = rtc_get_counter_val(); + + TOGGLE (LED1); + + cycle_ref = now; + + if (warm_up) warm_up--; + else hz = now - then; + + + switch (rtc_half) { + case RTC_IN_LH: + if (! (v & 0x8000000)) break; + + BKP_DR7 = rtc_half = RTC_IN_UH; + break; + + case RTC_IN_UH: + if (v & 0x8000000) break; + + BKP_DR7 = rtc_half = RTC_IN_LH; + rtc_offset.s += 0x100000000ULL; + bkp_write_off (rtc_offset); + break; + + default: + if (v & 0x8000000) + BKP_DR7 = rtc_half = RTC_IN_UH; + else + BKP_DR7 = rtc_half = RTC_IN_LH; + } + + + compiler_mb(); + rtc_ref = v; + rtc_ready = 1; + + + // printf ("Ctr %d %u %u\r\n", (int) v, (int) (now - then),(unsigned) rtc_offset.s); + + then = now; +} + + +EPOCH rtc_get (void) +{ + EPOCH e, o; + uint32_t h, c, r1, r2; + uint32_t now = DWT_CYCCNT; + + do { + r1 = rtc_ref; + compiler_mb(); + c = cycle_ref; + h = hz; + o = rtc_offset; + compiler_mb(); + r2 = rtc_ref; + } while (r1 != r2); + + now -= c; + + e.ns = now; + e.ns *= 1000000000ULL; + + if (h) e.ns /= h; + else e.ns /= HZ; + + e.s = r1; + + return time_epoch_add (e, o); +} + + +void rtc_init (void) +{ + rtc_offset = bkp_read_off(); + rtc_half = BKP_DR7; + +#if 1 + rtc_offset.s = 1637490753; + rtc_offset.ns = 498866999; + bkp_write_off (rtc_offset); +#endif + + + rtc_auto_awake (RCC_LSE, 0x7fff); //32768Hz + rtc_interrupt_enable (RTC_SEC); + nvic_enable_irq (NVIC_RTC_IRQ); + +} + + diff --git a/stm32/app/ticker.c b/stm32/app/ticker.c new file mode 100644 index 0000000..f124307 --- /dev/null +++ b/stm32/app/ticker.c @@ -0,0 +1,74 @@ +#include "project.h" + + +volatile uint32_t ticks; +static uint32_t scale = 7; + +unsigned led2 = 1000; + + + +void +delay_us (uint32_t d) +{ + d *= scale; + + while (d--) + __asm__ ("nop"); +} + +void +sys_tick_handler (void) +{ + ticks++; + cdcacm_tick(); + hands_tick(); + motor_tick(); + + if (led2) + led2--; + + if (led2) + CLEAR (LED2); + + else + SET (LED2); +} + +void +ticker_init (void) +{ + uint32_t v, w; + /*Start periodic timer */ + systick_set_clocksource (STK_CSR_CLKSOURCE_AHB_DIV8); + /* 72MHz / 8 = > 9Mhz */ + systick_set_reload (9000); + /* 9MHz / 9000 => 1kHz */ + systick_interrupt_enable(); + systick_counter_enable(); + + /*Calibrate the delay loop */ + do { + scale--; + v = ticks; + + while (v == ticks) + ; + + delay_us (1000); + w = ticks; + v++; + w -= v; + } while (w); + +} + + +void delay_ms (uint32_t d) +{ + uint32_t v = ticks; + + while ((ticks - v) < d); +} + + diff --git a/stm32/app/time_fn.c b/stm32/app/time_fn.c new file mode 100644 index 0000000..825d90f --- /dev/null +++ b/stm32/app/time_fn.c @@ -0,0 +1,650 @@ +#include <math.h> +#include "project.h" + +static int is_leap (unsigned year) +{ + if (year % 4) + return 0; + + if (year % 100) + return 1; + + if (year % 400) + return 0; + + return 1; +} + +EPOCH time_epoch_sub (EPOCH a, EPOCH b) +{ + EPOCH ret; + + ret.ns = a.ns - b.ns; + ret.s = a.s - b.s; + + if (!ret.s) return ret; + + if (ret.ns < 0) { + ret.s--; + ret.ns += 1000000000; + } + + return ret; +} + + +EPOCH time_epoch_add (EPOCH a, EPOCH b) +{ + EPOCH ret; + + ret.ns = a.ns + b.ns; + ret.s = a.s + b.s; + + while (ret.ns > 1000000000) { + ret.s++; + ret.ns -= 1000000000; + } + + return ret; +} + + +UTC time_epoch_to_utc (EPOCH epoch) +{ + UTC u = {0}; + uint64_t day; + unsigned y400, y100, y4; + + day = epoch.s / 86400; + epoch.s -= day * 86400; + + day += 134774; + + u.wday = day % 7; + u.wday++; + + + y400 = day / 146097; /*146097 days in 400 years */ + day -= (y400 * 146097); + + y100 = day / 36524; /*36524 days in 100 years */ + day -= (y100 * 36524); + + y4 = day / 1461; /*1461 days in 4 years */ + day -= (y4 * 1461); + + /* This may look redundant but 31 Dec in year 4 is special case */ + if (day < 1095) { /*1095 days in 3 years */ + u.year = day / 365; /*365 days in a year */ + day -= (365 * u.year); + } else { + u.year = 3; + day -= 1095; + } + + + /* Now put it all back together */ + u.year += 1601; + u.year += 4 * y4; + u.year += 100 * y100; + u.year += 400 * y400; + + + u.jday = day + 1; + + u.is_leap = is_leap (u.year); + + + if (!u.is_leap) { + /*Days and months for ordinary years */ + if (u.jday < 32) { + u.month = 1; + u.mday = u.jday; + } else if (u.jday < 60) { + u.month = 2; + u.mday = u.jday - 31; + } else if (u.jday < 91) { + u.month = 3; + u.mday = u.jday - 59; + } else if (u.jday < 121) { + u.month = 4; + u.mday = u.jday - 90; + } else if (u.jday < 152) { + u.month = 5; + u.mday = u.jday - 120; + } else if (u.jday < 182) { + u.month = 6; + u.mday = u.jday - 151; + } else if (u.jday < 213) { + u.month = 7; + u.mday = u.jday - 181; + } else if (u.jday < 244) { + u.month = 8; + u.mday = u.jday - 212; + } else if (u.jday < 274) { + u.month = 9; + u.mday = u.jday - 243; + } else if (u.jday < 305) { + u.month = 10; + u.mday = u.jday - 273; + } else if (u.jday < 335) { + u.month = 11; + u.mday = u.jday - 304; + } else { + u.month = 12; + u.mday = u.jday - 334; + } + } else { + /*And leap years */ + if (u.jday < 32) { + u.month = 1; + u.mday = u.jday; + } else if (u.jday < 61) { + u.month = 2; + u.mday = u.jday - 31; + } else if (u.jday < 92) { + u.month = 3; + u.mday = u.jday - 60; + } else if (u.jday < 122) { + u.month = 4; + u.mday = u.jday - 91; + } else if (u.jday < 153) { + u.month = 5; + u.mday = u.jday - 121; + } else if (u.jday < 183) { + u.month = 6; + u.mday = u.jday - 152; + } else if (u.jday < 214) { + u.month = 7; + u.mday = u.jday - 182; + } else if (u.jday < 245) { + u.month = 8; + u.mday = u.jday - 213; + } else if (u.jday < 275) { + u.month = 9; + u.mday = u.jday - 244; + } else if (u.jday < 306) { + u.month = 10; + u.mday = u.jday - 274; + } else if (u.jday < 336) { + u.month = 11; + u.mday = u.jday - 305; + } else { + u.month = 12; + u.mday = u.jday - 335; + } + } + + u.hour = epoch.s / 3600; + epoch.s -= u.hour * 3600; + u.minute = epoch.s / 60; + epoch.s -= u.minute * 60; + u.second = epoch.s; + + u.nanosecond = epoch.ns; + + return u; +} + +EPOCH time_utc_to_epoch (UTC u) +{ + unsigned y400; + unsigned y100; + unsigned y4; + + EPOCH ret; + + static int const mdays[] = + { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + static int const lmdays[] = + { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }; + + u.is_leap = is_leap (u.year); + + if (u.year < 100) u.year += 2000; + + + if (!u.jday) { + if (u.is_leap) + u.jday = u.mday + lmdays[u.month]; + else + u.jday = u.mday + mdays[u.month]; + } + + u.year -= 1601; + y400 = u.year / 400; + u.year -= y400 * 400; + y100 = u.year / 100; + u.year -= y100 * 100; + y4 = u.year / 4; + u.year -= y4 * 4; + + + + ret.s = u.jday - 1; + ret.s += u.year * 365; + ret.s += y4 * 1461; + ret.s += y100 * 36524; + ret.s += y400 * 146097; + + ret.s -= 134774; + + ret.s *= 86400; + + ret.s += u.second; + ret.s += u.minute * 60; + ret.s += u.hour * 3600; + + ret.ns = u.nanosecond; + + return ret; +} + +void utc_to_str (char *dst, UTC *u) +{ + const char *dname[] = {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat", "Sun"}; + const char *mname[] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + sprintf (dst, "%s %04d-%s-%02d %02d:%02d:%02d.%09d", dname[u->wday], u->year, mname[u->month], u->mday, u->hour, u->minute, u->second, u->nanosecond); +} + + +void time_print_utc (const char *p, UTC *u, const char *t) +{ + const char *dname[] = {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat", "Sun"}; + const char *mname[] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + printf ("%s%s %04d-%s-%02d %02d:%02d:%02d.%09d %s\r\n", p ? p : "", dname[u->wday], u->year, mname[u->month], u->mday, u->hour, u->minute, u->second, u->nanosecond, t ? t : ""); +} + +void time_print_epoch (const char *p, EPOCH e, const char *t) +{ + UTC u = time_epoch_to_utc (e); + time_print_utc (p, &u, t); +} + + +double +time_utc_to_tjd (UTC u) +{ + unsigned y400; + unsigned y100; + unsigned y4; + + double ret; + unsigned jd; + + static int const mdays[] = + { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + static int const lmdays[] = + { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }; + + u.is_leap = is_leap (u.year); + + if (u.year < 100) + u.year += 2000; + + + if (!u.jday) { + if (u.is_leap) + u.jday = u.mday + lmdays[u.month]; + else + u.jday = u.mday + mdays[u.month]; + } + + u.year -= 1601; + y400 = u.year / 400; + u.year -= y400 * 400; + y100 = u.year / 100; + u.year -= y100 * 100; + y4 = u.year / 4; + u.year -= y4 * 4; + + jd = u.jday - 1; + jd += u.year * 365; + jd += y4 * 1461; + jd += y100 * 36524; + jd += y400 * 146097; + + + jd += 2305813; + jd -= 2451545; + + + + ret = (double) u.nanosecond; + ret /= 1000000000.; + ret += (double) u.second; + ret /= 60.; + ret += (double) u.minute; + ret /= 60.; + ret += (double) u.hour; + ret /= 24.; + + ret += .5; + + ret += (double) jd; + + return ret; +} + + +EPOCH time_tjd_to_epoch (double tjd) +{ + EPOCH e; + + + tjd -= 2440587.5; + + tjd *= 86400.; + e.s = (uint64_t) floor (tjd); + + tjd -= (double) e.s; + + tjd *= 1000000000.; + e.ns = (unsigned) floor (tjd); + + return e; +} + +UTC time_tjd_to_utc (double tjd) +{ + return time_epoch_to_utc (time_tjd_to_epoch (tjd)); +} + + + +RA ra_normalize (RA r) +{ + + while (r.ra < 0.) { + r.ra += 360.; + r.days--; + } + + if (r.ra >= 3600.0) { + r.days = (unsigned) (floor (r.ra / 360.) + .5); + r.ra = remainder (r.ra, 360.); + } else while (r.ra >= 360.0) { + r.ra -= 360.; + r.days++; + } + + return r; +} + + +RA time_utc_to_ra (UTC u) +{ + RA r; + double tjd = time_utc_to_tjd (u); + double T = tjd / 36525.; + + r.ra = + 280.46061837 + (360.98564736629 * tjd) + (0.000387933 * T * T) - + (T * T * T / 38710000.0); + + r.days = 0; + + return ra_normalize (r); +} + + + +RA time_st_to_ra (ST st) +{ + RA r; + unsigned i; + + r.days = st.days; + + i = st.hour; + i *= 60; + i += st.minute; + i *= 60; + i += st.second; + + r.ra = (double) st.nanosecond; + r.ra /= 1000000000.; + r.ra += (double) i; + + r.ra /= 240.; + + return r; +} + + + +ST +time_ra_to_st (RA r) +{ + ST ret; + unsigned i; + + r = ra_normalize (r); + + ret.days = r.days; + + r.ra *= 240.0; + + i = (int) (floor (r.ra) + .5); + r.ra -= (double) i; + r.ra *= 1000000000.; + + ret.nanosecond = (unsigned) (r.ra + .5); + ret.second = i % 60; + i /= 60; + ret.minute = i % 60; + i /= 60; + ret.hour = i; + + + return ret; +} + + + +ST time_utc_to_lst (UTC u, double lon) +{ + RA r; + + r = time_utc_to_ra (u); + + r.ra += lon; + + return time_ra_to_st (r); +} + + + +#if 0 +int time_ra_cmp (double a, double b) +{ + return (ra_normalize (a - b) < 180.) ? -1 : 1;; +} + + +/*Find the next occuring time for RA tra */ + +static int test_side (EPOCH m, double tra) +{ + + double ra; + UTC u; + + u = time_epoch_to_utc (m); + ra = time_utc_to_ra (u); + + return time_ra_cmp (ra, tra); +} + + +EPOCH time_ra_to_next_epoch (EPOCH l, double tra) +{ + EPOCH r, m; + unsigned n; + + int dog = 48; + + printf ("time_ra_to_next_epoch %.9f\n", tra); + + /* XXX: we should use newton raphson, but */ + /* 1) we're on team hooke */ + /* 2) we want to limit the domain of solutions */ + /* So IB works better */ + + + while ((test_side (l, tra) != 1) && dog--) + l.s += 3600; + + r = l; + + while ((test_side (r, tra) != -1) && dog--) + r.s += 3600; + + for (n = 0; n < 64; ++n) { + + m = time_epoch_sub (r, l); + + if (m.s < 0) { + m.s = 0; + m.ns = 0; + break; + } + + if (m.s & 1) + m.ns += 1000000000; + + m.s /= 2; + m.ns /= 2; + + m = time_epoch_add (l, m); + + if (test_side (m, tra) < 0) + r = m; + else + l = m; + } + + return m; +} + +#endif + + + + + +/* + * Returns the EPOCH for the start of the metric year which + * starts in the custumary year y + * + * the metric year begins on the autumnal equinox (at the paris observatory natch.) + */ +static EPOCH start_of_year (unsigned y) +{ + double tjd = autumnal_equinox (y); + UTC u; + + tjd += (2.3372305555 / 360); /* Offset of paris meridian from greenwich */ + + u = time_tjd_to_utc (tjd); + + u.hour = + u.minute = + u.second = + u.nanosecond = 0; + + return time_utc_to_epoch (u); +} + +MTIME time_to_metric (EPOCH e, UTC u) +{ + MTIME ret; + + static EPOCH year_start, year_end; + static unsigned year; + + unsigned s; + unsigned d; + + double fd; + + EPOCH equinox; + + if ((e.s > year_end.s) || (e.s < year_start.s)) { + /*calculate the equinox in this customary year */ + + equinox = start_of_year (u.year); + + if (e.s < equinox.s) { + year_start = start_of_year (u.year - 1); + year_end = equinox; + year = u.year - 1792; + } else { + year_start = equinox; + year_end = start_of_year (u.year + 1); + year = u.year - 1791; + } + } + + ret.year = year; + + e = time_epoch_sub (e, year_start); + s = e.s; + d = s / 86400; + s -= d * 86400; + + ret.jday = d; + + ret.month = (d / 30) + 1; + ret.mday = (d % 30) + 1; + ret.wday = d % 10; + + /* Now for the horror of time of day */ + + fd = (double) e.ns; + fd /= 1000000000.; + fd += (double) s; + fd /= 86400.; + fd *= 100000; + + s = (int) fd; + fd -= (double) s; + fd *= 1000000000.; + + ret.nanosecond = (int) fd; + + ret.second = s % 100; + s -= ret.second; + s /= 100; + + ret.minute = s % 100; + s -= ret.minute; + s /= 100; + + ret.hour = s; + + return ret; +} + + +MTIME time_utc_to_metric (UTC u) +{ + return time_to_metric (time_utc_to_epoch (u), u); +} + + +MTIME time_epoch_to_metric (EPOCH e) +{ + return time_to_metric (e, time_epoch_to_utc (e)); +} + + + +void time_print_metric (const char *p, MTIME *m, const char *t) +{ + const char *dname[] = {"Pri", "Duo", "Tri", "Qua", "Qui", "Sex", "Sep", "Oct", "Non", "Dec" }; + const char *mname[] = {"", "Ven", "Bru", "Fri", "Niv", "Plu", "Ven", "Ger", "Flo", "Pra", "Mes", "The", "Fru", "San"}; + printf ("%s%s %03d-%s-%02d %01d:%02d:%02d.%09d %s\r\n", p ? p : "", dname[m->wday], m->year, mname[m->month], m->mday, m->hour, m->minute, m->second, m->nanosecond, t ? t : ""); +} + + diff --git a/stm32/app/time_fn.h b/stm32/app/time_fn.h new file mode 100644 index 0000000..0775a0f --- /dev/null +++ b/stm32/app/time_fn.h @@ -0,0 +1,47 @@ +typedef struct { + unsigned year; + unsigned is_leap; + unsigned jday; + unsigned month; + unsigned mday; + unsigned wday; + unsigned hour; + unsigned minute; + unsigned second; + unsigned nanosecond; +} UTC; + +typedef struct { + int64_t s; + int64_t ns; +} EPOCH; + + +typedef struct { + unsigned days; + double ra; +} RA; + +typedef struct { + unsigned days; + unsigned hour; + unsigned minute; + unsigned second; + unsigned nanosecond; +} ST; + + +typedef struct { + unsigned year; + unsigned is_leap; + unsigned jday; + unsigned month; + unsigned mday; + unsigned wday; + unsigned hour; + unsigned minute; + unsigned second; + unsigned nanosecond; +} MTIME; + + diff --git a/stm32/app/usart.c b/stm32/app/usart.c new file mode 100644 index 0000000..6b44656 --- /dev/null +++ b/stm32/app/usart.c @@ -0,0 +1,88 @@ +#include "project.h" + +#define BUFFER_SIZE 512 + +ring_t usart_rx_ring; +static uint8_t usart_rx_ring_buf[BUFFER_SIZE]; + +ring_t usart_tx_ring; +static uint8_t usart_tx_ring_buf[BUFFER_SIZE]; + +void +usart1_isr (void) +{ + uint8_t data; + + /* Check if we were called because of RXNE. */ + if (((USART_CR1 (USART1) & USART_CR1_RXNEIE) != 0) && + ((USART_SR (USART1) & USART_SR_RXNE) != 0)) { + /* Retrieve the data from the peripheral. */ + data = usart_recv (USART1); + ring_write_byte (&usart_rx_ring, data); + } + + /* Check if we were called because of TXE. */ + if (((USART_CR1 (USART1) & USART_CR1_TXEIE) != 0) && + ((USART_SR (USART1) & USART_SR_TXE) != 0)) { + if (ring_read_byte (&usart_tx_ring, &data)) { + /*No more data, Disable the TXE interrupt, it's no longer needed. */ + USART_CR1 (USART1) &= ~USART_CR1_TXEIE; + } else + usart_send (USART1, data); + } +} + +void usart_kick (void) +{ + if (!ring_empty (&usart_tx_ring)) + USART_CR1 (USART1) |= USART_CR1_TXEIE; +} + + +int +_write (int file, char *ptr, int len) +{ + int ret; + + if (file == 1) { + ret = ring_write (&usart_tx_ring, (uint8_t *) ptr, len); + usart_kick(); + ring_write (&cdcacm_tx_ring, (uint8_t *) ptr, len); + + if (ret < 0) + ret = -ret; + + return ret; + } + + errno = EIO; + return -1; +} + + + + +void +usart_init (void) +{ + ring_init (&usart_rx_ring, usart_rx_ring_buf, sizeof (usart_rx_ring_buf)); + ring_init (&usart_tx_ring, usart_tx_ring_buf, sizeof (usart_tx_ring_buf)); + /* Enable the USART1 interrupt. */ + nvic_enable_irq (NVIC_USART1_IRQ); + + MAP_AF (USART1_TX); + MAP_AF_PU (USART1_RX); + + /* Setup UART1 parameters. */ + usart_set_baudrate (USART1, 115200); + usart_set_databits (USART1, 8); + usart_set_stopbits (USART1, USART_STOPBITS_1); + usart_set_parity (USART1, USART_PARITY_NONE); + usart_set_flow_control (USART1, USART_FLOWCONTROL_NONE); + usart_set_mode (USART1, USART_MODE_TX_RX); + + /* Enable USART1 Receive interrupt. */ + USART_CR1 (USART1) |= USART_CR1_RXNEIE; + /* Finally enable USART1. */ + usart_enable (USART1); +} diff --git a/stm32/app/usb.c b/stm32/app/usb.c new file mode 100644 index 0000000..e644270 --- /dev/null +++ b/stm32/app/usb.c @@ -0,0 +1,148 @@ +#include "project.h" + +#define USB_DM GPIO11 +#define USB_DM_PORT GPIOA +#define USB_DP GPIO12 +#define USB_DP_PORT GPIOA + +/* Buffer to be used for control requests. */ +uint8_t usbd_control_buffer[128]; +usbd_device *usb_device; + +static const struct usb_device_descriptor dev = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0xef, + .bDeviceSubClass = 0x02, + .bDeviceProtocol = 0x01, + .bMaxPacketSize0 = 64, + .idVendor = 0x0483, + .idProduct = 0xff03, + .bcdDevice = 0x0200, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + + +static const struct usb_interface ifaces[] = { + { + .num_altsetting = 1, + .altsetting = &dummy_kb_iface, + .iface_assoc = &dummy_kb_iface_assoc, + }, + { + .num_altsetting = 1, + .altsetting = &dfu_iface, + .iface_assoc = &dfu_iface_assoc, + }, + { + .num_altsetting = 1, + .altsetting = &comm_iface, + .iface_assoc = &cdc_iface_assoc, + }, + { + .num_altsetting = 1, + .altsetting = &data_iface, + }, +}; + +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 4, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, + .interface = ifaces, +}; +#define N_USB_STRINGS (sizeof(usb_strings)/sizeof(usb_strings[0])) + +static const char *usb_strings[] = { + VENDOR_NAME, /*1*/ + PRODUCT_NAME, /*2*/ + SERIAL_NUMBER, /*3*/ + "dummy device", /*4*/ + "DFU interface", /*5*/ + "Debug interface", /*6*/ +}; + +void usb_hp_can_tx_isr (void) +{ + usbd_poll (usb_device); +} + +void usb_lp_can_rx0_isr (void) +{ + usbd_poll (usb_device); +} + +static enum usbd_request_return_codes control_request (usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, + uint16_t *len, + usbd_control_complete_callback *complete) +{ + enum usbd_request_return_codes ret; + + ret = dfu_control_request (usbd_dev, req, buf, len, complete); + + if (ret != USBD_REQ_NEXT_CALLBACK) + return ret; + + ret = cdcacm_control_request (usbd_dev, req, buf, len, complete); + + if (ret != USBD_REQ_NEXT_CALLBACK) + return ret; + + return USBD_REQ_NOTSUPP; +} + +static void set_config (usbd_device *usbd_dev, uint16_t wValue) +{ + + cdcacm_set_config (usbd_dev, wValue); + + usbd_register_control_callback (usbd_dev, + USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + control_request); + +} + + + +void usb_init (void) +{ + /*Force USB reset */ + gpio_set_mode (GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO11); + gpio_set_mode (GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); + gpio_clear (GPIOA, GPIO12); + gpio_clear (GPIOA, GPIO13); + delay_us (50000); + gpio_set_mode (GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO11); + gpio_set_mode (GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO12); + + + usb_device = usbd_init (&st_usbfs_v1_usb_driver, + &dev, + &config, + usb_strings, + N_USB_STRINGS, + usbd_control_buffer, + sizeof (usbd_control_buffer)); + + usbd_register_set_config_callback (usb_device, set_config); + + nvic_enable_irq (NVIC_USB_HP_CAN_TX_IRQ); + nvic_enable_irq (NVIC_USB_LP_CAN_RX0_IRQ); + + + +} + + |