From f6b959086671fb1bf8b2003d5c5384c7311a7735 Mon Sep 17 00:00:00 2001 From: jaseg Date: Tue, 16 Apr 2019 23:38:46 +0900 Subject: center/fw: rename --- center_fw/.gitignore | 12 + center_fw/Makefile | 108 +++ center_fw/Scope.ipynb | 1679 ++++++++++++++++++++++++++++++++++ center_fw/adc.c | 271 ++++++ center_fw/adc.h | 96 ++ center_fw/base.c | 25 + center_fw/cmsis_exports.c | 48 + center_fw/datagen.py | 39 + center_fw/global.h | 51 ++ center_fw/main.c | 201 ++++ center_fw/openocd.cfg | 17 + center_fw/protocol.c | 66 ++ center_fw/protocol.h | 29 + center_fw/protocol_test.c | 162 ++++ center_fw/scope.gdb | 12 + center_fw/startup_stm32f030x6.s | 273 ++++++ center_fw/stm32_flash.ld | 136 +++ center_fw/system_stm32f0xx.c | 336 +++++++ center_fw/tools/gen_cmsis_exports.py | 30 + 19 files changed, 3591 insertions(+) create mode 100644 center_fw/.gitignore create mode 100644 center_fw/Makefile create mode 100644 center_fw/Scope.ipynb create mode 100644 center_fw/adc.c create mode 100644 center_fw/adc.h create mode 100644 center_fw/base.c create mode 100644 center_fw/cmsis_exports.c create mode 100644 center_fw/datagen.py create mode 100644 center_fw/global.h create mode 100644 center_fw/main.c create mode 100644 center_fw/openocd.cfg create mode 100644 center_fw/protocol.c create mode 100644 center_fw/protocol.h create mode 100644 center_fw/protocol_test.c create mode 100644 center_fw/scope.gdb create mode 100644 center_fw/startup_stm32f030x6.s create mode 100644 center_fw/stm32_flash.ld create mode 100644 center_fw/system_stm32f0xx.c create mode 100644 center_fw/tools/gen_cmsis_exports.py (limited to 'center_fw') diff --git a/center_fw/.gitignore b/center_fw/.gitignore new file mode 100644 index 0000000..ed41bfa --- /dev/null +++ b/center_fw/.gitignore @@ -0,0 +1,12 @@ +*.elf +*.o +*.expand +*.hex +*.lst +*.map +*.bin +sources.c +sources.tar.xz +sources.tar.xz.zip +8b10b_test_decode +8b10b_test_encode diff --git a/center_fw/Makefile b/center_fw/Makefile new file mode 100644 index 0000000..16cf5c7 --- /dev/null +++ b/center_fw/Makefile @@ -0,0 +1,108 @@ +# Megumin LED display firmware +# Copyright (C) 2018 Sebastian Götte +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +CUBE_PATH ?= $(wildcard ~)/resource/STM32CubeF0 +CMSIS_PATH ?= $(CUBE_PATH)/Drivers/CMSIS +CMSIS_DEV_PATH ?= $(CMSIS_PATH)/Device/ST/STM32F0xx +HAL_PATH ?= $(CUBE_PATH)/Drivers/STM32F0xx_HAL_Driver + +MAC_ADDR ?= 0xdeadbeef + +CC := arm-none-eabi-gcc +LD := arm-none-eabi-ld +OBJCOPY := arm-none-eabi-objcopy +OBJDUMP := arm-none-eabi-objdump +SIZE := arm-none-eabi-size + +CFLAGS = -g -Wall -std=gnu11 -O0 -fdump-rtl-expand -DMAC_ADDR=$(MAC_ADDR) -DADC_BUFSIZE=1024 +CFLAGS += -mlittle-endian -mcpu=cortex-m0 -march=armv6-m -mthumb +#CFLAGS += -ffunction-sections -fdata-sections +LDFLAGS = -nostartfiles +#LDFLAGS += -specs=rdimon.specs -DSEMIHOSTING +LDFLAGS += -Wl,-Map=main.map -nostdlib +#LDFLAGS += -Wl,--gc-sections +LIBS = -lgcc +#LIBS += -lrdimon + +# Technically we're using an STM32F030F4, but apart from the TSSOP20 package that one is largely identical to the +# STM32F030*6 and there is no separate device header provided for it, so we're faking a *6 device here. This is +# even documented in stm32f0xx.h. Thanks ST! +CFLAGS += -DSTM32F030x6 -DHSE_VALUE=8000000 + +LDFLAGS += -Tstm32_flash.ld +CFLAGS += -I$(CMSIS_DEV_PATH)/Include -I$(CMSIS_PATH)/Include -I$(HAL_PATH)/Inc -Iconfig -Wno-unused -I../common +LDFLAGS += -L$(CMSIS_PATH)/Lib/GCC -larm_cortexM0l_math + +################################################### + +.PHONY: program clean + +all: main.elf + +cmsis_exports.c: $(CMSIS_DEV_PATH)/Include/stm32f030x6.h $(CMSIS_PATH)/Include/core_cm0.h + python3 tools/gen_cmsis_exports.py $^ > $@ + +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $^ +# $(CC) -E $(CFLAGS) -o $(@:.o=.pp) $^ + +%.o: %.s + $(CC) -c $(CFLAGS) -o $@ $^ +# $(CC) -E $(CFLAGS) -o $(@:.o=.pp) $^ + +%.dot: %.elf + r2 -a arm -qc 'aa;agC' $< 2>/dev/null >$@ + +sources.tar.xz: main.c adc.c ../common/8b10b.c Makefile + tar -caf $@ $^ + +# don't ask... +sources.tar.xz.zip: sources.tar.xz + zip $@ $^ + +sources.c: sources.tar.xz.zip + xxd -i $< | head -n -1 | sed 's/=/__attribute__((section(".source_tarball"))) =/' > $@ + +main.elf: main.o startup_stm32f030x6.o system_stm32f0xx.o $(HAL_PATH)/Src/stm32f0xx_ll_utils.o base.o cmsis_exports.o ../common/8b10b.o adc.o protocol.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + $(OBJCOPY) -O ihex $@ $(@:.elf=.hex) + $(OBJCOPY) -O binary $@ $(@:.elf=.bin) + $(OBJDUMP) -St $@ >$(@:.elf=.lst) + $(SIZE) $@ + +program: main.elf openocd.cfg + openocd -f openocd.cfg -c "program $< verify reset exit" + +8b10b_test_encode: 8b10b_test_encode.c 8b10b.c + gcc -o $@ $^ + +8b10b_test_decode: 8b10b_test_decode.c 8b10b.c + gcc -o $@ $^ + +protocol_test: protocol.c protocol_test.c + gcc -o $@ -O0 -Wall -Wextra -g -I../common $^ + +clean: + rm -f **.o + rm -f main.elf main.hex main.bin main.map main.lst + rm -f **.expand + rm -f cmsis_exports.c + rm -f sources.tar.xz + rm -f sources.tar.xz.zip + rm -f sources.c + rm -f *.dot + rm -f protocol_test + diff --git a/center_fw/Scope.ipynb b/center_fw/Scope.ipynb new file mode 100644 index 0000000..9222f06 --- /dev/null +++ b/center_fw/Scope.ipynb @@ -0,0 +1,1679 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot as plt\n", + "%matplotlib notebook\n", + "import struct\n", + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_data(offx=0, end=-1, signed=False, channels=1):\n", + " with open('/tmp/scope_dump.bin', 'rb') as f:\n", + " raw_data = f.read()\n", + " data = struct.unpack(f'<{len(raw_data)//2}{\"h\" if signed else \"H\"}', raw_data)\n", + " \n", + " fig, axs = plt.subplots(channels, 1, squeeze=False, sharex=True, figsize=(10, 5))\n", + " for i, ax in enumerate(axs.flatten()):\n", + " ax.plot([math.nan if x==-255 else x for x in data[offx:][:end][i::channels]])\n", + " ax.grid()\n", + " \n", + " return data" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('
');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " if (mpl.ratio != 1) {\n", + " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", + " }\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " fig.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '
');\n", + " var titletext = $(\n", + " '
');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('
');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var backingStore = this.context.backingStorePixelRatio ||\n", + "\tthis.context.webkitBackingStorePixelRatio ||\n", + "\tthis.context.mozBackingStorePixelRatio ||\n", + "\tthis.context.msBackingStorePixelRatio ||\n", + "\tthis.context.oBackingStorePixelRatio ||\n", + "\tthis.context.backingStorePixelRatio || 1;\n", + "\n", + " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband = $('');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width * mpl.ratio);\n", + " canvas.attr('height', height * mpl.ratio);\n", + " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('
')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('
');\n", + " var button = $('');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(el){\n", + " var fig = this\n", + " el.on(\"remove\", function(){\n", + "\tfig.close_ws(fig, {});\n", + " });\n", + "}\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import random, struct, numpy as np\n", + "\n", + "def plot_avg():\n", + " with open('/tmp/foo', 'rb') as f:\n", + " vals = np.frombuffer(f.read(), dtype='uint16')\n", + " \n", + " vals = vals.copy()\n", + " idx = 1\n", + " vals &= 1<>= idx\n", + " \n", + " delta = 10000\n", + " plotdata = [sum(vals[i:i+delta])/delta for i in range(0, len(vals), delta)]\n", + " plt.plot(plotdata)\n", + "plot_avg()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/center_fw/adc.c b/center_fw/adc.c new file mode 100644 index 0000000..eb5d4b5 --- /dev/null +++ b/center_fw/adc.c @@ -0,0 +1,271 @@ +/* Megumin LED display firmware + * Copyright (C) 2018 Sebastian Götte + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adc.h" + +#include +#include + +#define DETECTOR_CHANNEL a + +volatile uint16_t adc_buf[ADC_BUFSIZE]; +volatile struct adc_state adc_state = {0}; +#define st adc_state +volatile struct adc_measurements adc_data; + +static void adc_dma_init(int burstlen, bool enable_interrupt); +static void adc_timer_init(int psc, int ivl); + + +/* Mode that can be used for debugging */ +void adc_configure_scope_mode(uint8_t channel_mask, int sampling_interval_ns) { + /* The constant SAMPLE_FAST (0) when passed in as sampling_interval_ns is handled specially in that we turn the ADC + to continuous mode to get the highest possible sampling rate. */ + + /* First, disable trigger timer, DMA and ADC in case we're reconfiguring on the fly. */ + TIM1->CR1 &= ~TIM_CR1_CEN; + ADC1->CR &= ~ADC_CR_ADSTART; + DMA1_Channel1->CCR &= ~DMA_CCR_EN; + + /* keep track of current mode in global variable */ + st.adc_mode = ADC_SCOPE; + + adc_dma_init(sizeof(adc_buf)/sizeof(adc_buf[0]), true); + + /* Clock from PCLK/4 instead of the internal exclusive high-speed RC oscillator. */ + ADC1->CFGR2 = (2< total conversion time 2.17us*/ + ADC1->SMPR = (2<CFGR1 = ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | ADC_CFGR1_CONT; + else /* Trigger from timer 1 Channel 4 */ + ADC1->CFGR1 = ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | (2<CHSELR = channel_mask; + /* Perform self-calibration */ + ADC1->CR |= ADC_CR_ADCAL; + while (ADC1->CR & ADC_CR_ADCAL) + ; + /* Enable conversion */ + ADC1->CR |= ADC_CR_ADEN; + ADC1->CR |= ADC_CR_ADSTART; + + if (sampling_interval_ns == SAMPLE_FAST) + return; /* We don't need the timer to trigger in continuous mode. */ + + /* An ADC conversion takes 1.1667us, so to be sure we don't get data overruns we limit sampling to every 1.5us. + Since we don't have a spare PLL to generate the ADC sample clock and re-configuring the system clock just for this + would be overkill we round to 250ns increments. The minimum sampling rate is about 60Hz due to timer resolution. */ + int cycles = sampling_interval_ns > 1500 ? sampling_interval_ns/250 : 6; + if (cycles > 0xffff) + cycles = 0xffff; + adc_timer_init(12/*250ns/tick*/, cycles); +} + +/* Regular operation receiver mode */ +void adc_configure_monitor_mode(struct command_if_def *cmd_if, int ivl_us) { + /* First, disable trigger timer, DMA and ADC in case we're reconfiguring on the fly. */ + TIM1->CR1 &= ~TIM_CR1_CEN; + ADC1->CR &= ~ADC_CR_ADSTART; + DMA1_Channel1->CCR &= ~DMA_CCR_EN; + + /* keep track of current mode in global variable */ + st.adc_mode = ADC_MONITOR; + + for (int i=0; iCFGR1 = ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | (2<CFGR2 = (2< total conversion time 2.17us*/ + ADC1->SMPR = (2<CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL16 | ADC_CHSELR_CHSEL17; + /* Enable internal voltage reference and temperature sensor */ + ADC->CCR = ADC_CCR_TSEN | ADC_CCR_VREFEN; + /* Perform ADC calibration */ + ADC1->CR |= ADC_CR_ADCAL; + while (ADC1->CR & ADC_CR_ADCAL) + ; + /* Enable ADC */ + ADC1->CR |= ADC_CR_ADEN; + ADC1->CR |= ADC_CR_ADSTART; + + adc_timer_init(SystemCoreClock/1000000/*1.0us/tick*/, ivl_us); +} + +static void adc_dma_init(int burstlen, bool enable_interrupt) { + /* Configure DMA 1 Channel 1 to get rid of all the data */ + DMA1_Channel1->CPAR = (unsigned int)&ADC1->DR; + DMA1_Channel1->CMAR = (unsigned int)&adc_buf; + DMA1_Channel1->CNDTR = burstlen; + DMA1_Channel1->CCR = (0<CCR |= + DMA_CCR_CIRC /* circular mode so we can leave it running indefinitely */ + | (1<IFCR |= DMA_IFCR_CGIF1; + } + + DMA1_Channel1->CCR |= DMA_CCR_EN; /* Enable channel */ +} + +static void adc_timer_init(int psc, int ivl) { + TIM1->BDTR = TIM_BDTR_MOE; /* MOE is needed even though we only "output" a chip-internal signal TODO: Verify this. */ + TIM1->CCMR2 = (6<CCER = TIM_CCER_CC4E; /* Enable capture/compare unit 4 connected to ADC */ + TIM1->CCR4 = 1; /* Trigger at start of timer cycle */ + /* Set prescaler and interval */ + TIM1->PSC = psc-1; + TIM1->ARR = ivl-1; + /* Preload all values */ + TIM1->EGR |= TIM_EGR_UG; + TIM1->CR1 = TIM_CR1_ARPE; + /* And... go! */ + TIM1->CR1 |= TIM_CR1_CEN; +} + +/* This acts as a no-op that provides a convenient point to set a breakpoint for the debug scope logic */ +static void gdb_dump(void) { +} + +void receive_bit(struct bit_detector_st *st, int bit) { + int symbol = xfr_8b10b_feed_bit((struct state_8b10b_dec *)&st->rx8b10b, bit); + if (symbol == -K28_1) + st->sync = 1; + + if (symbol == -DECODING_IN_PROGRESS) + return; + + if (symbol == -DECODING_ERROR) + st->sync = 0; + /* Fall through so we also pass the error to receive_symbol */ + + GPIOA->BSRR = 1<<9; + receive_symbol(&st->rx_st, symbol); + GPIOA->BRR = 1<<9; + + /* Debug scope logic */ + /* + static int debug_buf_pos = 0; + if (st->sync) { + if (debug_buf_pos < NCH) { + debug_buf_pos = NCH; + } else { + adc_buf[debug_buf_pos++] = symbol; + + if (debug_buf_pos >= sizeof(adc_buf)/sizeof(adc_buf[0])) { + debug_buf_pos = 0; + st->sync = 0; + gdb_dump(); + for (int i=0; ilast_bit; + int diff = a-5500; + if (diff < - st->hysteresis_mv/2) + new_bit = 0; + else if (diff > st->hysteresis_mv/2) + new_bit = 1; + else + blank(); + + st->len_ctr++; + if (new_bit != st->last_bit) { + st->last_bit = new_bit; + st->len_ctr = 0; + st->committed_len_ctr = st->base_interval_cycles>>1; + + } else if (st->len_ctr >= st->committed_len_ctr) { + st->committed_len_ctr += st->base_interval_cycles; + receive_bit(st, st->last_bit); + } +} + +void DMA1_Channel1_IRQHandler(void) { + GPIOA->BSRR = 1<<5; + /* ISR timing measurement for debugging */ + //int start = SysTick->VAL; + + /* Clear the interrupt flag */ + DMA1->IFCR |= DMA_IFCR_CGIF1; + + if (st.adc_mode == ADC_SCOPE) + return; + + /* This has been copied from the code examples to section 12.9 ADC>"Temperature sensor and internal reference + * voltage" in the reference manual with the extension that we actually measure the supply voltage instead of + * hardcoding it. This is not strictly necessary since we're running off a bored little LDO but it's free and + * the current supply voltage is a nice health value. + */ + // FIXME DEBUG adc_data.adc_vcc_mv = (3300 * VREFINT_CAL)/(st.adc_aggregate[VREF_CH]); + + int64_t vcc = 3300; + /* FIXME debug + int64_t vcc = adc_data.adc_vcc_mv; + int64_t read = st.adc_aggregate[TEMP_CH] * 10 * 10000; + int64_t cal = TS_CAL1 * 10 * 10000; + adc_data.adc_temp_celsius_tenths = 300 + ((read/4096 * vcc) - (cal/4096 * 3300))/43000; + */ + + const long vmeas_r_total = VMEAS_R_HIGH + VMEAS_R_LOW; + //int a = adc_data.adc_vmeas_a_mv = (st.adc_aggregate[VMEAS_A]*(vmeas_r_total * vcc / VMEAS_R_LOW)) >> 12; + int a = adc_data.adc_vmeas_a_mv = (adc_buf[VMEAS_A]*13300) >> 12; + bit_detector((struct bit_detector_st *)&st.det_st, a); + + /* ISR timing measurement for debugging */ + /* + int end = SysTick->VAL; + int tdiff = start - end; + if (tdiff < 0) + tdiff += SysTick->LOAD; + st.dma_isr_duration = tdiff; + */ + GPIOA->BRR = 1<<5; +} + diff --git a/center_fw/adc.h b/center_fw/adc.h new file mode 100644 index 0000000..a3e9d53 --- /dev/null +++ b/center_fw/adc.h @@ -0,0 +1,96 @@ +/* Megumin LED display firmware + * Copyright (C) 2018 Sebastian Götte + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ADC_H__ +#define __ADC_H__ + +#include "global.h" +#include "8b10b.h" +#include "protocol.h" + +struct adc_measurements { + int16_t adc_vcc_mv; + int16_t adc_temp_celsius_tenths; + int16_t adc_vmeas_a_mv; + int16_t adc_vmeas_b_mv; + int16_t adc_mean_a_mv; + int16_t adc_mean_b_mv; + int16_t adc_mean_diff_mv; +}; + +enum channel_mask { + MASK_VMEAS_A = ADC_CHSELR_CHSEL0, + MASK_VMEAS_B = ADC_CHSELR_CHSEL1 +}; + +enum adc_mode { + ADC_UNINITIALIZED, + ADC_MONITOR, + ADC_SCOPE +}; + +enum sampling_mode { + SAMPLE_FAST = 0 +}; + +/* The weird order is to match the channels' order in the DMA buffer. Due to some configuration mistake I can't be +bothered to fix, the DMA controller outputs ADC measurements off-by-one into the output buffer. */ +enum adc_channels { + VREF_CH, + VMEAS_A, + VMEAS_B, + TEMP_CH, + NCH +}; + +struct bit_detector_st { + int hysteresis_mv; + int sync; + int base_interval_cycles; + struct proto_rx_st rx_st; + /* private stuff */ + int last_bit; + int len_ctr; + int committed_len_ctr; + struct state_8b10b_dec rx8b10b; +}; + +struct adc_state { + enum adc_mode adc_mode; + int dma_isr_duration; + struct bit_detector_st det_st; + /* private stuff */ + uint32_t adc_aggregate[NCH]; /* oversampling accumulator */ + uint32_t mean_aggregate_ctr; + uint32_t mean_aggregator[3]; +}; + +extern volatile struct adc_state adc_state; +extern volatile uint16_t adc_buf[ADC_BUFSIZE]; +extern volatile struct adc_measurements adc_data; + +void adc_init(void); +void adc_configure_scope_mode(uint8_t channel_mask, int sampling_interval_ns); +void adc_configure_monitor_mode(struct command_if_def *cmd_if, int ivl_us); + +void bit_detector(struct bit_detector_st *st, int a); +void receive_bit(struct bit_detector_st *st, int bit); + +void blank(void); +void unblank(int new_bit); + +#endif/*__ADC_H__*/ diff --git a/center_fw/base.c b/center_fw/base.c new file mode 100644 index 0000000..8e7c03b --- /dev/null +++ b/center_fw/base.c @@ -0,0 +1,25 @@ + +#include +#include + +int __errno = 0; +void *_impure_ptr = NULL; + +void __sinit(void) { +} + +void *memset(void *s, int c, size_t n) { + char *end = (char *)s + n; + for (char *p = (char *)s; p < end; p++) + *p = (char)c; + return s; +} + +size_t strlen(const char *s) { + const char *start = s; + while (*s++); + return s - start - 1; +} + +void __assert_func(bool value) { +} diff --git a/center_fw/cmsis_exports.c b/center_fw/cmsis_exports.c new file mode 100644 index 0000000..39874b5 --- /dev/null +++ b/center_fw/cmsis_exports.c @@ -0,0 +1,48 @@ +#ifndef __GENERATED_CMSIS_HEADER_EXPORTS__ +#define __GENERATED_CMSIS_HEADER_EXPORTS__ + +#include + +/* stm32f030x6.h */ +TIM_TypeDef *tim3 = TIM3; +TIM_TypeDef *tim14 = TIM14; +RTC_TypeDef *rtc = RTC; +WWDG_TypeDef *wwdg = WWDG; +IWDG_TypeDef *iwdg = IWDG; +I2C_TypeDef *i2c1 = I2C1; +PWR_TypeDef *pwr = PWR; +SYSCFG_TypeDef *syscfg = SYSCFG; +EXTI_TypeDef *exti = EXTI; +ADC_TypeDef *adc1 = ADC1; +ADC_Common_TypeDef *adc1_common = ADC1_COMMON; +ADC_Common_TypeDef *adc = ADC; +TIM_TypeDef *tim1 = TIM1; +SPI_TypeDef *spi1 = SPI1; +USART_TypeDef *usart1 = USART1; +TIM_TypeDef *tim16 = TIM16; +TIM_TypeDef *tim17 = TIM17; +DBGMCU_TypeDef *dbgmcu = DBGMCU; +DMA_TypeDef *dma1 = DMA1; +DMA_Channel_TypeDef *dma1_channel1 = DMA1_Channel1; +DMA_Channel_TypeDef *dma1_channel2 = DMA1_Channel2; +DMA_Channel_TypeDef *dma1_channel3 = DMA1_Channel3; +DMA_Channel_TypeDef *dma1_channel4 = DMA1_Channel4; +DMA_Channel_TypeDef *dma1_channel5 = DMA1_Channel5; +FLASH_TypeDef *flash = FLASH; +OB_TypeDef *ob = OB; +RCC_TypeDef *rcc = RCC; +CRC_TypeDef *crc = CRC; +GPIO_TypeDef *gpioa = GPIOA; +GPIO_TypeDef *gpiob = GPIOB; +GPIO_TypeDef *gpioc = GPIOC; +GPIO_TypeDef *gpiod = GPIOD; +GPIO_TypeDef *gpiof = GPIOF; + +#include + +/* core_cm0.h */ +SCB_Type *scb = SCB; +SysTick_Type *systick = SysTick; +NVIC_Type *nvic = NVIC; + +#endif//__GENERATED_CMSIS_HEADER_EXPORTS__ diff --git a/center_fw/datagen.py b/center_fw/datagen.py new file mode 100644 index 0000000..63402dc --- /dev/null +++ b/center_fw/datagen.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import statistics + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('infile') + + parser.add_argument('-b', '--bitlen', type=float, default=250, help='Average length of a bit in fractional samples') + parser.add_argument('-j', '--jitter', type=float, default=0, help='Bit length jitter standard deviation in fractional samples') + + parser.add_argument('-v', '--high', type=float, default=12.0, help='High level in V') + parser.add_argument('-l', '--low', type=float, default=0.2, help='Low level in V') + parser.add_argument('-n', '--noise', type=float, default=0, help='Gaussian white voltage noise amplitude on top of signal') + + parser.add_argument('-r', '--risetime', type=int, default=1, help='Rise time in sp/V') + parser.add_argument('-f', '--falltime', type=int, default=1, help='Fall time in sp/V') + #parser.add_argument('-g', '--glitchfreq', type=float, default=0, help='Add glitches every {g} samples on average (set to 0 to turn off)') + #parser.add_argument('--glitchlen', type=float, default=0, help='Length of glitches to add in samples') + #parser.add_argument('-t', '--glitch-constant', type=float, default=0, help='When the signal stays constant between bits add a glitch with probability {t}') + args = paresr.parse_args() + + with open(args.infile, 'r') as f: + bits = f.read().replace(' ', '').splitlines() + + + def join_transitions(chunks): + for l, r in zip(chunks, chunks[1:]): + t = args.risetime if l[-1] < r[0] else args.falltime + yield from (l[-1] + (r[0] - l[-1] * i/t) for i in range(t)) + yield from c[args.risetime:-args.falltime] + + def add_noise(vals): + yield from (val + random.gauss(0, args.noise) for val in vals) + + def encode_bits(bitstring): + return add_noise(join_transitions([ args.high if bit == '1' else args.low for _ in range(random.gauss(args.bitlen, args.jitter)) ])) + diff --git a/center_fw/global.h b/center_fw/global.h new file mode 100644 index 0000000..15a5e87 --- /dev/null +++ b/center_fw/global.h @@ -0,0 +1,51 @@ +/* Megumin LED display firmware + * Copyright (C) 2018 Sebastian Götte + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GLOBAL_H__ +#define __GLOBAL_H__ + +/* Workaround for sub-par ST libraries */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#include +#include +#include +#pragma GCC diagnostic pop + +#include + +#include +#include +#include +#include + +/* Microcontroller part number: STM32F030F4C6 */ + +/* Things used for module status reporting. */ +#define FIRMWARE_VERSION 1 +#define HARDWARE_VERSION 0 + +#define TS_CAL1 (*(uint16_t *)0x1FFFF7B8) +#define VREFINT_CAL (*(uint16_t *)0x1FFFF7BA) + +#define VMEAS_R_HIGH 10000 /* kiloohms */ +#define VMEAS_R_LOW 3300 /* kiloohms */ + +extern volatile unsigned int sys_time; +extern volatile unsigned int sys_time_seconds; + +#endif/*__GLOBAL_H__*/ diff --git a/center_fw/main.c b/center_fw/main.c new file mode 100644 index 0000000..2340ecb --- /dev/null +++ b/center_fw/main.c @@ -0,0 +1,201 @@ +/* Megumin LED display firmware + * Copyright (C) 2018 Sebastian Götte + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "global.h" + +#include "adc.h" + +volatile unsigned int sys_time = 0; +volatile unsigned int sys_time_seconds = 0; + +void TIM1_BRK_UP_TRG_COM_Handler() { + TIM1->SR &= ~TIM_SR_UIF_Msk; +} + +enum packet_type { + PKT_TYPE_RESERVED = 0, + PKT_TYPE_SET_OUTPUTS_BINARY = 1, + PKT_TYPE_SET_GLOBAL_BRIGHTNESS = 2, + PKT_TYPE_SET_OUTPUTS = 3, + PKT_TYPE_MAX +}; + +struct { + struct command_if_def cmd_if; + int payload_len[PKT_TYPE_MAX]; +} cmd_if = {{.packet_type_max=PKT_TYPE_MAX}, { + [PKT_TYPE_RESERVED] = 0, + [PKT_TYPE_SET_OUTPUTS_BINARY] = 1, + [PKT_TYPE_SET_GLOBAL_BRIGHTNESS] = 1, + [PKT_TYPE_SET_OUTPUTS] = 8 } +}; + +void set_drv_gpios(uint8_t val) { + int a=!!(val&1), b=!!(val&2), c=!!(val&4), d=!!(val&8); + GPIOA->ODR &= ~(!a<<3 | !b<<7 | c<<6 | d<<4); + GPIOA->ODR |= a<<3 | b<<7 | !c<<6 | !d<<4; +} + +uint8_t out_state = 0x01; +void set_outputs(uint8_t val[8]) { + /* TODO implement BCM for digital brightness control */ + int x = 0; + for (int i=0; i<8; i++) + if (val[i] > 127) + x |= 1<> 4); +} + +void TIM3_IRQHandler(void) { + GPIOA->BSRR = 1<<10; + if (TIM3->SR & TIM_SR_UIF) + unblank_low(); + else + blank(); + + TIM3->SR = 0; + GPIOA->BRR = 1<<10; +} + +void handle_command(int command, uint8_t *args) { + static int global_brightness = 0xff; + switch (command) { + case PKT_TYPE_SET_OUTPUTS_BINARY: + set_outputs_binary(args[0], global_brightness); + break; + + case PKT_TYPE_SET_GLOBAL_BRIGHTNESS: + global_brightness = args[0]; + break; + + case PKT_TYPE_SET_OUTPUTS: + set_outputs(args); + break; + } +} + +int main(void) { + RCC->CR |= RCC_CR_HSEON; + while (!(RCC->CR&RCC_CR_HSERDY)); + RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk; + RCC->CFGR |= ((6-2)< 48.0MHz */ + RCC->CR |= RCC_CR_PLLON; + while (!(RCC->CR&RCC_CR_PLLRDY)); + RCC->CFGR |= (2<AHBENR |= RCC_AHBENR_DMAEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_FLITFEN; + RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_ADCEN| RCC_APB2ENR_DBGMCUEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM1EN;; + RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; + + /* TIM3 foo */ + TIM3->CCMR2 = (6<CCER = TIM_CCER_CC4E; /* Enable capture/compare unit 4 connected to ADC */ + + TIM3->PSC = 48-1; + TIM3->CCR4 = 170-1; + TIM3->ARR = 200-1; + + TIM3->DIER |= TIM_DIER_UIE | TIM_DIER_CC4IE; + + TIM3->CR1 |= TIM_CR1_CEN; + NVIC_EnableIRQ(TIM3_IRQn); + NVIC_SetPriority(TIM3_IRQn, 3<<5); + + GPIOA->MODER |= + (0<OSPEEDR |= + (2<IDR & (1<<0); + if (new != bit) { + bit = new; + TIM3->EGR |= TIM_EGR_UG; + unblank_low(); + } + /* idle */ + } +} + +void NMI_Handler(void) { + asm volatile ("bkpt"); +} + +void HardFault_Handler(void) __attribute__((naked)); +void HardFault_Handler() { + asm volatile ("bkpt"); +} + +void SVC_Handler(void) { + asm volatile ("bkpt"); +} + + +void PendSV_Handler(void) { + asm volatile ("bkpt"); +} + +void SysTick_Handler(void) { + static int n = 0; + sys_time++; + if (n++ == 1000) { + n = 0; + sys_time_seconds++; + } +} + diff --git a/center_fw/openocd.cfg b/center_fw/openocd.cfg new file mode 100644 index 0000000..ce164b7 --- /dev/null +++ b/center_fw/openocd.cfg @@ -0,0 +1,17 @@ +telnet_port 4445 +gdb_port 3334 +tcl_port 6667 + +source [find interface/stlink-v2.cfg] +#interface jlink +#interface stlink-v2 +#adapter_khz 10000 +#transport select swd + +#source /usr/share/openocd/scripts/target/stm32f0x.cfg +source [find target/stm32f0x_stlink.cfg] + +init +arm semihosting enable + +#flash bank sysflash.alias stm32f0x 0x00000000 0 0 0 $_TARGETNAME diff --git a/center_fw/protocol.c b/center_fw/protocol.c new file mode 100644 index 0000000..87d46e4 --- /dev/null +++ b/center_fw/protocol.c @@ -0,0 +1,66 @@ + +#include +#include "protocol.h" +#include "8b10b.h" + +void reset_receiver(struct proto_rx_st *st, struct command_if_def *cmd_if) { + st->rxpos = -1; + st->address = 5; /* FIXME debug code */ + st->cmd_if = cmd_if; +} + +void receive_symbol(struct proto_rx_st *st, int symbol) { + if (symbol == -K28_1) { /* Comma/frame delimiter */ + st->rxpos = 0; + /* Fall through and return and just ignore incomplete packets */ + + } else if (symbol == -DECODING_ERROR) { + goto reset; + + } else if (symbol < 0) { /* Unknown comma symbol or error */ + goto reset; + + } else if (st->rxpos == -1) { + return; + + } else if (st->rxpos == 0) { /* First data symbol, and not an error or comma symbol */ + st->packet_type = symbol & ~PKT_TYPE_BULK_FLAG; + if (st->packet_type >= st->cmd_if->packet_type_max) + goto reset; + + int payload_len = st->cmd_if->payload_len[st->packet_type]; + st->is_bulk = symbol & PKT_TYPE_BULK_FLAG; + st->offset = (st->is_bulk) ? (st->address*payload_len + 1) : 2; + st->rxpos++; + + if (payload_len == 0 && st->is_bulk) { + handle_command(st->packet_type, NULL); + goto reset; + } + + } else if (!st->is_bulk && st->rxpos == 1) { + if (symbol != st->address) + goto reset; + + if (st->cmd_if->payload_len[st->packet_type] == 0) { + handle_command(st->packet_type, NULL); + goto reset; + } + st->rxpos = 2; + + } else { + if (st->rxpos - st->offset >= 0) + st->argbuf[st->rxpos - st->offset] = symbol; + st->rxpos++; + + if (st->rxpos - st->offset == st->cmd_if->payload_len[st->packet_type]) { + handle_command(st->packet_type, (uint8_t *)st->argbuf); + goto reset; + } + } + + return; +reset: + st->rxpos = -1; +} + diff --git a/center_fw/protocol.h b/center_fw/protocol.h new file mode 100644 index 0000000..9178c42 --- /dev/null +++ b/center_fw/protocol.h @@ -0,0 +1,29 @@ +#ifndef __PROTOCOL_H__ +#define __PROTOCOL_H__ + +#include + +#define PKT_TYPE_BULK_FLAG 0x80 + +struct proto_rx_st { + int packet_type; + int is_bulk; + int rxpos; + int address; + uint8_t argbuf[8]; + int offset; + struct command_if_def *cmd_if; +}; + +struct command_if_def { + int packet_type_max; + int payload_len[0]; +}; + +/* Callback */ +void handle_command(int command, uint8_t *args); + +void receive_symbol(struct proto_rx_st *st, int symbol); +void reset_receiver(struct proto_rx_st *st, struct command_if_def *cmd_if); + +#endif diff --git a/center_fw/protocol_test.c b/center_fw/protocol_test.c new file mode 100644 index 0000000..d417a1b --- /dev/null +++ b/center_fw/protocol_test.c @@ -0,0 +1,162 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include "protocol.h" +#include "8b10b.h" + +static int urandom_fd = -1; +static long long n_tests = 0; + +struct test_cmd_if { + struct command_if_def cmd_if; + int payload_len[256]; +}; + +struct { + int ncalls; + int last_cmd; + uint8_t last_args[sizeof(((struct proto_rx_st *)0)->argbuf)]; +} handler_state; + + +void handle_command(int command, uint8_t *args) { + handler_state.ncalls++; + handler_state.last_cmd = command; + if (args) + memcpy(handler_state.last_args, args, 8); +} + +void send_test_command_single(struct test_cmd_if *cmd_if, struct proto_rx_st *st, int cmd, int address, unsigned char pattern[256]) { + n_tests++; + receive_symbol(st, -K28_1); + receive_symbol(st, cmd); + receive_symbol(st, address); + for (int i=0; ipayload_len[cmd]; i++) + receive_symbol(st, pattern[i]); +} + +void send_test_command_bulk(struct test_cmd_if *cmd_if, struct proto_rx_st *st, int cmd, int index, int len, unsigned char pattern[256]) { + n_tests++; + receive_symbol(st, -K28_1); + receive_symbol(st, cmd | PKT_TYPE_BULK_FLAG); + for (int j=0; jpayload_len[cmd]; i++) { + if (j == index) + receive_symbol(st, pattern[i]); + else + receive_symbol(st, 0xaa); + } + } +} + +void test_commands_with_pattern(struct test_cmd_if *cmd_if, unsigned char pattern[256]) { + struct proto_rx_st st; + + for (int cmd=0; cmdcmd_if.packet_type_max; cmd++) { + /* Addresssed tests */ + reset_receiver(&st, &cmd_if->cmd_if); + st.address = 23; + handler_state.ncalls = 0; + send_test_command_single(cmd_if, &st, cmd, 23, pattern); + assert(handler_state.ncalls == 1); + assert(handler_state.last_cmd == cmd); + assert(!memcmp(handler_state.last_args, pattern, cmd_if->payload_len[cmd])); + + reset_receiver(&st, &cmd_if->cmd_if); + st.address = 23; + handler_state.ncalls = 0; + send_test_command_single(cmd_if, &st, cmd, 5, pattern); + assert(handler_state.ncalls == 0); + + reset_receiver(&st, &cmd_if->cmd_if); + st.address = 5; + handler_state.ncalls = 0; + send_test_command_single(cmd_if, &st, cmd, 5, pattern); + assert(handler_state.ncalls == 1); + assert(handler_state.last_cmd == cmd); + assert(!memcmp(handler_state.last_args, pattern, cmd_if->payload_len[cmd])); + + /* Bulk test */ + reset_receiver(&st, &cmd_if->cmd_if); + st.address = 5; + handler_state.ncalls = 0; + send_test_command_bulk(cmd_if, &st, cmd, 5, 8, pattern); + assert(handler_state.ncalls == 1); + assert(handler_state.last_cmd == cmd); + assert(!memcmp(handler_state.last_args, pattern, cmd_if->payload_len[cmd])); + } +} + +void test_commands(struct test_cmd_if *cmd_if) { + unsigned char data[256]; + + memset(data, 0, sizeof(data)); + test_commands_with_pattern(cmd_if, data); + + memset(data, 1, sizeof(data)); + test_commands_with_pattern(cmd_if, data); + + memset(data, 255, sizeof(data)); + test_commands_with_pattern(cmd_if, data); + + for (int i=0; i<5; i++) { + assert(read(urandom_fd, (char *)data, sizeof(data)) == sizeof(data)); + test_commands_with_pattern(cmd_if, data); + } +} + +int main(void) { + struct test_cmd_if cmd_if; + + urandom_fd = open("/dev/urandom", O_RDONLY); + assert(urandom_fd > 0); + + for (int ncmds=1; ncmds<128; ncmds++) { + cmd_if.cmd_if.packet_type_max = ncmds; + + /* Case 1 */ + for (int i=0; iFLASH + + /* the program code is stored in the .text section, which goes to Flash */ + .text : { + . = ALIGN(4); + + *(.text) /* normal code */ + *(.text.*) /* -ffunction-sections code */ + *(.rodata) /* read-only data (constants) */ + *(.rodata*) /* -fdata-sections read only data */ + *(.glue_7) /* TBD - needed ? */ + *(.glue_7t) /* TBD - needed ? */ + + *(.source_tarball) + + /* Necessary KEEP sections (see http://sourceware.org/ml/newlib/2005/msg00255.html) */ + KEEP (*(.init)) + KEEP (*(.fini)) + KEEP (*(.source_tarball)) + + . = ALIGN(4); + _etext = .; + /* This is used by the startup in order to initialize the .data section */ + _sidata = _etext; + } >FLASH + + /* + .configflash : { + . = ALIGN(0x400); + *(.configdata) + _econfig = .; + } >FLASH + */ + + /* This is the initialized data section + The program executes knowing that the data is in the RAM + but the loader puts the initial values in the FLASH (inidata). + It is one task of the startup to copy the initial values from FLASH to RAM. */ + .data : AT ( _sidata ) { + . = ALIGN(4); + /* This is used by the startup in order to initialize the .data secion */ + _sdata = . ; + _data = . ; + + *(.data) + *(.data.*) + *(.RAMtext) + + . = ALIGN(4); + /* This is used by the startup in order to initialize the .data secion */ + _edata = . ; + } >RAM + + /* This is the uninitialized data section */ + .bss : { + . = ALIGN(4); + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; + _bss = .; + + *(.bss) + *(.bss.*) /* patched by elias - allows the use of -fdata-sections */ + *(COMMON) + + . = ALIGN(4); + /* This is used by the startup in order to initialize the .bss secion */ + _ebss = . ; + } >RAM + + PROVIDE ( end = _ebss); + PROVIDE (_end = _ebss); + + __exidx_start = .; + __exidx_end = .; + + /* after that it's only debugging information. */ + + /* remove the debugging information from the standard libraries */ +/* /DISCARD/ : { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + }*/ + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/center_fw/system_stm32f0xx.c b/center_fw/system_stm32f0xx.c new file mode 100644 index 0000000..a43c3d6 --- /dev/null +++ b/center_fw/system_stm32f0xx.c @@ -0,0 +1,336 @@ +/** + ****************************************************************************** + * @file system_stm32f0xx.c + * copied from: STM32Cube/Drivers/CMSIS/Device/ST/STM32F0xx/Source/Templates + * @author MCD Application Team + * @version V2.3.1 + * @date 04-November-2016 + * @brief CMSIS Cortex-M0 Device Peripheral Access Layer System Source File. + * + * 1. This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f0xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used + * by the user application to setup the SysTick + * timer or configure other parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * 2. After each device reset the HSI (8 MHz) is used as system clock source. + * Then SystemInit() function is called, in "startup_stm32f0xx.s" file, to + * configure the system clock before to branch to main program. + * + * 3. This file configures the system clock as follows: + *============================================================================= + * Supported STM32F0xx device + *----------------------------------------------------------------------------- + * System Clock source | HSI + *----------------------------------------------------------------------------- + * SYSCLK(Hz) | 8000000 + *----------------------------------------------------------------------------- + * HCLK(Hz) | 8000000 + *----------------------------------------------------------------------------- + * AHB Prescaler | 1 + *----------------------------------------------------------------------------- + * APB1 Prescaler | 1 + *----------------------------------------------------------------------------- + *============================================================================= + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f0xx_system + * @{ + */ + +/** @addtogroup STM32F0xx_System_Private_Includes + * @{ + */ + +#include "stm32f0xx.h" + +/** + * @} + */ + +/** @addtogroup STM32F0xx_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F0xx_System_Private_Defines + * @{ + */ +#if !defined (HSE_VALUE) + #define HSE_VALUE ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz. + This value can be provided and adapted by the user application. */ +#endif /* HSE_VALUE */ + +#if !defined (HSI_VALUE) + #define HSI_VALUE ((uint32_t)8000000) /*!< Default value of the Internal oscillator in Hz. + This value can be provided and adapted by the user application. */ +#endif /* HSI_VALUE */ + +#if !defined (HSI48_VALUE) +#define HSI48_VALUE ((uint32_t)48000000) /*!< Default value of the HSI48 Internal oscillator in Hz. + This value can be provided and adapted by the user application. */ +#endif /* HSI48_VALUE */ +/** + * @} + */ + +/** @addtogroup STM32F0xx_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F0xx_System_Private_Variables + * @{ + */ + /* This variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetHCLKFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock there is no need to + call the 2 first functions listed above, since SystemCoreClock variable is + updated automatically. + */ +uint32_t SystemCoreClock = 8000000; + +const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; +const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + +/** + * @} + */ + +/** @addtogroup STM32F0xx_System_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F0xx_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system. + * Initialize the default HSI clock source, vector table location and the PLL configuration is reset. + * @param None + * @retval None + */ +void SystemInit(void) +{ + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set HSION bit */ + RCC->CR |= (uint32_t)0x00000001U; + +#if defined (STM32F051x8) || defined (STM32F058x8) + /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */ + RCC->CFGR &= (uint32_t)0xF8FFB80CU; +#else + /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */ + RCC->CFGR &= (uint32_t)0x08FFB80CU; +#endif /* STM32F051x8 or STM32F058x8 */ + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t)0xFEF6FFFFU; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t)0xFFFBFFFFU; + + /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */ + RCC->CFGR &= (uint32_t)0xFFC0FFFFU; + + /* Reset PREDIV[3:0] bits */ + RCC->CFGR2 &= (uint32_t)0xFFFFFFF0U; + +#if defined (STM32F072xB) || defined (STM32F078xx) + /* Reset USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */ + RCC->CFGR3 &= (uint32_t)0xFFFCFE2CU; +#elif defined (STM32F071xB) + /* Reset USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */ + RCC->CFGR3 &= (uint32_t)0xFFFFCEACU; +#elif defined (STM32F091xC) || defined (STM32F098xx) + /* Reset USART3SW[1:0], USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */ + RCC->CFGR3 &= (uint32_t)0xFFF0FEACU; +#elif defined (STM32F030x6) || defined (STM32F030x8) || defined (STM32F031x6) || defined (STM32F038xx) || defined (STM32F030xC) + /* Reset USART1SW[1:0], I2C1SW and ADCSW bits */ + RCC->CFGR3 &= (uint32_t)0xFFFFFEECU; +#elif defined (STM32F051x8) || defined (STM32F058xx) + /* Reset USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */ + RCC->CFGR3 &= (uint32_t)0xFFFFFEACU; +#elif defined (STM32F042x6) || defined (STM32F048xx) + /* Reset USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */ + RCC->CFGR3 &= (uint32_t)0xFFFFFE2CU; +#elif defined (STM32F070x6) || defined (STM32F070xB) + /* Reset USART1SW[1:0], I2C1SW, USBSW and ADCSW bits */ + RCC->CFGR3 &= (uint32_t)0xFFFFFE6CU; + /* Set default USB clock to PLLCLK, since there is no HSI48 */ + RCC->CFGR3 |= (uint32_t)0x00000080U; +#else + #warning "No target selected" +#endif + + /* Reset HSI14 bit */ + RCC->CR2 &= (uint32_t)0xFFFFFFFEU; + + /* Disable all interrupts */ + RCC->CIR = 0x00000000U; + +} + +/** + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or configure + * other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any configuration + * based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) + * or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f0xx_hal.h file (default value + * 8 MHz) but the real value may vary depending on the variations + * in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f0xx_hal.h file (default value + * 8 MHz), user has to ensure that HSE_VALUE is same as the real + * frequency of the crystal used. Otherwise, this function may + * have wrong result. + * + * - The result of this function could be not correct when using fractional + * value for HSE crystal. + * + * @param None + * @retval None + */ +void SystemCoreClockUpdate (void) +{ + uint32_t tmp = 0, pllmull = 0, pllsource = 0, predivfactor = 0; + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case RCC_CFGR_SWS_HSI: /* HSI used as system clock */ + SystemCoreClock = HSI_VALUE; + break; + case RCC_CFGR_SWS_HSE: /* HSE used as system clock */ + SystemCoreClock = HSE_VALUE; + break; + case RCC_CFGR_SWS_PLL: /* PLL used as system clock */ + /* Get PLL clock source and multiplication factor ----------------------*/ + pllmull = RCC->CFGR & RCC_CFGR_PLLMUL; + pllsource = RCC->CFGR & RCC_CFGR_PLLSRC; + pllmull = ( pllmull >> 18) + 2; + predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1; + + if (pllsource == RCC_CFGR_PLLSRC_HSE_PREDIV) + { + /* HSE used as PLL clock source : SystemCoreClock = HSE/PREDIV * PLLMUL */ + SystemCoreClock = (HSE_VALUE/predivfactor) * pllmull; + } +#if defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F072xB) || defined(STM32F078xx) || defined(STM32F091xC) || defined(STM32F098xx) + else if (pllsource == RCC_CFGR_PLLSRC_HSI48_PREDIV) + { + /* HSI48 used as PLL clock source : SystemCoreClock = HSI48/PREDIV * PLLMUL */ + SystemCoreClock = (HSI48_VALUE/predivfactor) * pllmull; + } +#endif /* STM32F042x6 || STM32F048xx || STM32F072xB || STM32F078xx || STM32F091xC || STM32F098xx */ + else + { +#if defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F070x6) \ + || defined(STM32F078xx) || defined(STM32F071xB) || defined(STM32F072xB) \ + || defined(STM32F070xB) || defined(STM32F091xC) || defined(STM32F098xx) || defined(STM32F030xC) + /* HSI used as PLL clock source : SystemCoreClock = HSI/PREDIV * PLLMUL */ + SystemCoreClock = (HSI_VALUE/predivfactor) * pllmull; +#else + /* HSI used as PLL clock source : SystemCoreClock = HSI/2 * PLLMUL */ + SystemCoreClock = (HSI_VALUE >> 1) * pllmull; +#endif /* STM32F042x6 || STM32F048xx || STM32F070x6 || + STM32F071xB || STM32F072xB || STM32F078xx || STM32F070xB || + STM32F091xC || STM32F098xx || STM32F030xC */ + } + break; + default: /* HSI used as system clock */ + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK clock frequency ----------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK clock frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/center_fw/tools/gen_cmsis_exports.py b/center_fw/tools/gen_cmsis_exports.py new file mode 100644 index 0000000..ba3422b --- /dev/null +++ b/center_fw/tools/gen_cmsis_exports.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import re +import os + +if __name__ == '__main__': + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('cmsis_device_header', nargs='+', type=argparse.FileType('rb')) + args = parser.parse_args() + + print('#ifndef __GENERATED_CMSIS_HEADER_EXPORTS__') + print('#define __GENERATED_CMSIS_HEADER_EXPORTS__') + print() + for header in args.cmsis_device_header: + lines = header.readlines() + name = os.path.basename(header.name) + print('#include <{}>'.format(name)) + print() + + print('/* {} */'.format(name)) + for l in lines: + match = re.match(b'^#define (\w+)\s+\W*(\w+_TypeDef|\w+_Type).*$', l) + if match: + inst, typedef = match.groups() + inst, typedef = inst.decode(), typedef.decode() + print('{} *{} = {};'.format(typedef, inst.lower(), inst)) + print() + print('#endif//__GENERATED_CMSIS_HEADER_EXPORTS__') + -- cgit