From 29e7ab707b1d58f02064fdb7b907dc37482547e6 Mon Sep 17 00:00:00 2001 From: Matthew Via Date: Tue, 2 May 2023 22:03:21 -0400 Subject: [PATCH] stm32f4: re-implement using cmsis device headers This drops the libopencm3 dependency, and adds libusb_stm32 and the cmsis device headers for the stm32f4. Functionality mirrors the gd32f4 port. Support for the stm32f4[01][57] is dropped due to the DMA errata, and flashing relies on dual bank support. --- .gitmodules | 9 +- Makefile | 6 +- README.md | 30 +- contrib/cmsis_device_f4 | 1 + contrib/libopencm3 | 1 - contrib/libusb_stm32 | 1 + src/platforms/common/ad7888_adc.h | 5 + src/platforms/stm32f4-discovery.c | 1083 ----------------------- src/platforms/stm32f4-discovery.ld | 122 --- src/platforms/stm32f4/README.md | 35 + src/platforms/stm32f4/stm32f4.c | 280 ++++++ src/platforms/stm32f4/stm32f4.ld | 64 ++ src/platforms/stm32f4/stm32f4_pwm.c | 67 ++ src/platforms/stm32f4/stm32f4_sched.c | 198 +++++ src/platforms/stm32f4/stm32f4_sensors.c | 162 ++++ src/platforms/stm32f4/stm32f4_usb.c | 339 +++++++ src/platforms/stm32f4/stm32f4_vectors.s | 460 ++++++++++ targets/stm32f4.mk | 50 +- 18 files changed, 1664 insertions(+), 1249 deletions(-) create mode 160000 contrib/cmsis_device_f4 delete mode 160000 contrib/libopencm3 create mode 160000 contrib/libusb_stm32 delete mode 100644 src/platforms/stm32f4-discovery.c delete mode 100644 src/platforms/stm32f4-discovery.ld create mode 100644 src/platforms/stm32f4/README.md create mode 100644 src/platforms/stm32f4/stm32f4.c create mode 100644 src/platforms/stm32f4/stm32f4.ld create mode 100644 src/platforms/stm32f4/stm32f4_pwm.c create mode 100644 src/platforms/stm32f4/stm32f4_sched.c create mode 100644 src/platforms/stm32f4/stm32f4_sensors.c create mode 100644 src/platforms/stm32f4/stm32f4_usb.c create mode 100644 src/platforms/stm32f4/stm32f4_vectors.s diff --git a/.gitmodules b/.gitmodules index e6a89742..abf38356 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "libopencm3"] - path = contrib/libopencm3 - url = https://github.com/libopencm3/libopencm3 [submodule "tinycbor"] path = contrib/tinycbor url = https://github.com/intel/tinycbor @@ -8,3 +5,9 @@ path = contrib/CMSIS_5 url = https://github.com/ARM-software/CMSIS_5.git shallow = true +[submodule "contrib/cmsis_device_f4"] + path = contrib/cmsis_device_f4 + url = https://github.com/STMicroelectronics/cmsis_device_f4.git +[submodule "contrib/libusb_stm32"] + path = contrib/libusb_stm32 + url = https://github.com/dmitrystu/libusb_stm32.git diff --git a/Makefile b/Makefile index f9a6b6a6..ca887e69 100644 --- a/Makefile +++ b/Makefile @@ -29,14 +29,16 @@ CFLAGS+=-Isrc/ -Isrc/platforms/common -Wall -Wextra -g -std=c11 -DGIT_DESCRIBE=\ CFLAGS+=-I${TINYCBOR_DIR}/src LDFLAGS+= -lm -L${OBJDIR} -l:${TINYCBOR_LIB} -OPENCM3_DIR=$(PWD)/contrib/libopencm3 - VPATH+=src src/platforms src/platforms/common DESTOBJS = $(addprefix ${OBJDIR}/, ${OBJS}) $(OBJDIR): mkdir -p ${OBJDIR} + +$(OBJDIR)/%.o: %.s + ${AS} -c -o $@ $< + $(OBJDIR)/%.o: %.c ${CC} ${CFLAGS} -MMD -c -o $@ $< diff --git a/README.md b/README.md index 65b73cef..99ffab56 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,7 @@ Requires: - GNU make - check (for unit tests) -Before trying to compile, make sure to bring in the libopencm3 submodule: +Before trying to compile, make sure to bring in submodules in contrib: ``` git submodule update --init ``` @@ -250,28 +250,12 @@ scheduling latency), make sure the threads have permissions to be realtime, or otherwise constrain it to a single cpu. # Hardware -The current primary hardware platform is an ST Micro STM32F407VGT -microcontroller. There are a few non-production-ready hardware designs +The current supported microcontrollers are the STMicro STM32F427 and GigaDevice +GD32F450/470. There are a few non-production-ready hardware designs available under https://github.com/via/viaems-boards, though I would recommend -anyone attempting to use those designs contact me. The STM32F4-DISCOVERY board -*can* be used with this firmware, with appropriate extra hardware for vehicle -interfacing. +anyone attempting to use those designs contact me. Details on each platform are +available in the README files in their platform directories. +## [STM32F4](src/platforms/stm32f4/README.md) -## STM32F4 -Note: These are subject to change with the next hardware design, which will be -moving to better support a 64 pin chip and more flexible PWM outputs. - -System | Pins ---- | --- -Trigger 0 | `PA0` -Trigger 1 | `PA1` -Frequency | `PA0`, `PA1`, `PA2`, `PA3` -Test Trigger | `PB10`, `PB11` -`CANL` | `PB5` -`CANH` | `PB6` -USB | `PA9`, `PA11`, and `PA12` -ADC | `PB12`, `PB13`, `PB14`, and `PB15` -OUT | `PD0`-`PD15` -GPIO | `PE0`-`PE15` -PWM | `PC6`, `PC7`, `PC8`, and `PC9` (currently all fixed at same frequenty) +## [GD32F4](src/platforms/gd32f4/README.md) diff --git a/contrib/cmsis_device_f4 b/contrib/cmsis_device_f4 new file mode 160000 index 00000000..7ac69098 --- /dev/null +++ b/contrib/cmsis_device_f4 @@ -0,0 +1 @@ +Subproject commit 7ac69098b3e5f9b2d929acd4f087e92a7e59e0e9 diff --git a/contrib/libopencm3 b/contrib/libopencm3 deleted file mode 160000 index e9d87a9c..00000000 --- a/contrib/libopencm3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e9d87a9cb7e6a687ddbfde085eddb461c89a3c09 diff --git a/contrib/libusb_stm32 b/contrib/libusb_stm32 new file mode 160000 index 00000000..9168e2a3 --- /dev/null +++ b/contrib/libusb_stm32 @@ -0,0 +1 @@ +Subproject commit 9168e2a31db946326fb84016a74ea2ab5bf87f54 diff --git a/src/platforms/common/ad7888_adc.h b/src/platforms/common/ad7888_adc.h index 58808ad3..93c6453e 100644 --- a/src/platforms/common/ad7888_adc.h +++ b/src/platforms/common/ad7888_adc.h @@ -62,4 +62,9 @@ static inline bool adc_response_is_valid(const uint16_t *values) { return true; } +/* Not implemented */ +static void process_knock_inputs(const uint16_t *values) { + (void)values; +} + #endif diff --git a/src/platforms/stm32f4-discovery.c b/src/platforms/stm32f4-discovery.c deleted file mode 100644 index c8adae98..00000000 --- a/src/platforms/stm32f4-discovery.c +++ /dev/null @@ -1,1083 +0,0 @@ -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "decoder.h" -#include "limits.h" -#include "platform.h" -#include "scheduler.h" -#include "sensors.h" -#include "tasks.h" -#include "util.h" - -#include "stm32_sched_buffers.h" - -/* Hardware setup: - * Discovery board LEDs: - * LD3 - Orange - PD13 - * LD4 - Green - PD12 - * LD5 - Red - PD14 - * LD6 - Blue - PD15 - * - * Fixed pin mapping: - * Primary time base is TIM2, slaved off TIM8 - * Trigger 0 - PA0 (TIM2_CH1) - * Trigger 1 - PA1 (TIM2_CH2) - * - * Test/Out Trigger 0 - PB10 - * Test/Out Trigger 1 - PB11 - * - * Event Timer (TIM2_CH4) - * - * CAN2: - * PB5, PB6 - * - * USB: - * PA9, PA11, PA12 - * - * TLC2543 or ADC7888 on SPI2 (PB12-15) CS, SCK, MISO, MOSI - * - Uses TIM7 dma1 stream 2 chan 1 to trigger DMA at about 50 khz for 10 - * inputs - * - RX uses SPI2 dma1 stream 3 chan 0 - * - On end of RX dma, trigger interrupt - * - * Configurable pin mapping: - * Scheduled Outputs: - * - 0-15 maps to port D, pins 0-15 - * PWM Outputs - * - 0-PC6 1-PC7 2-PC8 3-PC9 TIM3 channel 1-4 - * GPIO (Digital Sensor or Output) - * - 0-15 Maps to Port E - * - * nvic priorities: - * tim6 (test trigger) 0 - * dma2s1 (buffer swap) 16 - * tim2 (triggers, callbacks, scheduling, fueling calcs) 32 - * dma1s3 (adc) 64 - * otg (usb) 128 - * systick 128 - */ - -static int capture_edge_from_config(trigger_edge e) { - switch (e) { - case RISING_EDGE: - return TIM_IC_RISING; - case FALLING_EDGE: - return TIM_IC_FALLING; - case BOTH_EDGES: - return TIM_IC_BOTH; - } - return RISING_EDGE; -} - -static void platform_setup_tim2() { - /* Set up TIM2 as 32bit clock that is slaved off TIM8*/ - - timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); - timer_slave_set_mode(TIM2, TIM_SMCR_SMS_ECM1); - timer_slave_set_trigger(TIM2, TIM_SMCR_TS_ITR1); /* TIM2 slaved off TIM8 */ - timer_set_period(TIM2, 0xFFFFFFFF); - timer_set_prescaler(TIM2, 0); - timer_disable_preload(TIM2); - timer_continuous_mode(TIM2); - /* Setup output compare registers */ - timer_disable_oc_output(TIM2, TIM_OC1); - timer_disable_oc_output(TIM2, TIM_OC2); - timer_disable_oc_output(TIM2, TIM_OC3); - timer_disable_oc_output(TIM2, TIM_OC4); - - timer_set_oc_slow_mode(TIM2, TIM_OC4); - timer_set_oc_mode(TIM2, TIM_OC4, TIM_OCM_FROZEN); - - /* Setup input captures for CH1-4 Triggers */ - gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLDOWN, GPIO0); - gpio_set_af(GPIOA, GPIO_AF1, GPIO0); - gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLDOWN, GPIO1); - gpio_set_af(GPIOA, GPIO_AF1, GPIO1); - - timer_ic_set_input(TIM2, TIM_IC1, TIM_IC_IN_TI1); - timer_ic_set_filter(TIM2, TIM_IC1, TIM_IC_CK_INT_N_2); - timer_ic_set_polarity( - TIM2, TIM_IC1, capture_edge_from_config(config.freq_inputs[0].edge)); - timer_ic_enable(TIM2, TIM_IC1); - - timer_ic_set_input(TIM2, TIM_IC2, TIM_IC_IN_TI2); - timer_ic_set_filter(TIM2, TIM_IC2, TIM_IC_CK_INT_N_2); - timer_ic_set_polarity( - TIM2, TIM_IC2, capture_edge_from_config(config.freq_inputs[1].edge)); - timer_ic_enable(TIM2, TIM_IC2); - - timer_enable_counter(TIM2); -} - -void platform_setup_tim8() { - /* Set up TIM8 to cause an update event at 4 MHz */ - timer_set_mode(TIM8, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); - timer_set_period(TIM8, 41); /* 0.25 uS */ - timer_set_prescaler(TIM8, 0); - timer_disable_preload(TIM8); - timer_continuous_mode(TIM8); - timer_disable_oc_output(TIM8, TIM_OC1); - timer_disable_oc_output(TIM8, TIM_OC2); - timer_disable_oc_output(TIM8, TIM_OC3); - timer_disable_oc_output(TIM8, TIM_OC4); - timer_set_master_mode(TIM8, TIM_CR2_MMS_UPDATE); - timer_enable_counter(TIM8); - timer_enable_update_event(TIM8); - timer_update_on_overflow(TIM8); - timer_set_dma_on_update_event(TIM8); - TIM8_DIER |= TIM_DIER_UDE; /* Enable update dma */ -} - -static void platform_init_eventtimer() { - - platform_setup_tim2(); - - /* Only enable interrupts for trigger/sync inputs */ - if (config.freq_inputs[0].type == TRIGGER) { - timer_enable_irq(TIM2, TIM_DIER_CC1IE); - } - if (config.freq_inputs[1].type == TRIGGER) { - timer_enable_irq(TIM2, TIM_DIER_CC2IE); - } - - nvic_enable_irq(NVIC_TIM2_IRQ); - nvic_set_priority(NVIC_TIM2_IRQ, 32); - - /* Set debug unit to stop the timer on halt */ - *((volatile uint32_t *)0xE0042008) |= 29; /*TIM2, TIM5, and TIM7 and */ - *((volatile uint32_t *)0xE004200C) |= 2; /* TIM8 stop */ - - platform_setup_tim8(); -} - -static void platform_init_pwm() { - - gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6); - gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO7); - gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO8); - gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9); - gpio_set_af(GPIOC, GPIO_AF2, GPIO6); - gpio_set_af(GPIOC, GPIO_AF2, GPIO7); - gpio_set_af(GPIOC, GPIO_AF2, GPIO8); - gpio_set_af(GPIOC, GPIO_AF2, GPIO9); - - timer_disable_oc_output(TIM3, TIM_OC1); - timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); - /* 30ish Hz */ - timer_set_period(TIM3, 65535); - timer_set_prescaler(TIM3, 32); - timer_enable_preload(TIM3); - timer_continuous_mode(TIM3); - /* Setup output compare registers */ - timer_ic_set_input(TIM3, TIM_IC1, TIM_IC_OUT); - timer_disable_oc_clear(TIM3, TIM_OC1); - timer_enable_oc_preload(TIM3, TIM_OC1); - timer_set_oc_slow_mode(TIM3, TIM_OC1); - timer_set_oc_mode(TIM3, TIM_OC1, TIM_OCM_PWM1); - timer_set_oc_value(TIM3, TIM_OC1, 0); - timer_set_oc_polarity_high(TIM3, TIM_OC1); - timer_enable_oc_output(TIM3, TIM_OC1); - - timer_ic_set_input(TIM3, TIM_IC2, TIM_IC_OUT); - timer_disable_oc_clear(TIM3, TIM_OC2); - timer_enable_oc_preload(TIM3, TIM_OC2); - timer_set_oc_slow_mode(TIM3, TIM_OC2); - timer_set_oc_mode(TIM3, TIM_OC2, TIM_OCM_PWM1); - timer_set_oc_value(TIM3, TIM_OC2, 0); - timer_set_oc_polarity_high(TIM3, TIM_OC2); - timer_enable_oc_output(TIM3, TIM_OC2); - - timer_ic_set_input(TIM3, TIM_IC3, TIM_IC_OUT); - timer_disable_oc_clear(TIM3, TIM_OC3); - timer_enable_oc_preload(TIM3, TIM_OC3); - timer_set_oc_slow_mode(TIM3, TIM_OC3); - timer_set_oc_mode(TIM3, TIM_OC3, TIM_OCM_PWM1); - timer_set_oc_value(TIM3, TIM_OC3, 0); - timer_set_oc_polarity_high(TIM3, TIM_OC3); - timer_enable_oc_output(TIM3, TIM_OC3); - - timer_ic_set_input(TIM3, TIM_IC4, TIM_IC_OUT); - timer_disable_oc_clear(TIM3, TIM_OC4); - timer_enable_oc_preload(TIM3, TIM_OC4); - timer_set_oc_slow_mode(TIM3, TIM_OC4); - timer_set_oc_mode(TIM3, TIM_OC4, TIM_OCM_PWM1); - timer_set_oc_value(TIM3, TIM_OC4, 0); - timer_set_oc_polarity_high(TIM3, TIM_OC4); - timer_enable_oc_output(TIM3, TIM_OC4); - timer_enable_counter(TIM3); -} - -void set_pwm(int output, float percent) { - if (percent < 0.0f) { - percent = 0.0f; - } else if (percent > 100.0f) { - percent = 100.0f; - } - int ival = (percent / 100.0f) * 65535; - switch (output) { - case 1: - return timer_set_oc_value(TIM3, TIM_OC1, ival); - case 2: - return timer_set_oc_value(TIM3, TIM_OC2, ival); - case 3: - return timer_set_oc_value(TIM3, TIM_OC3, ival); - case 4: - return timer_set_oc_value(TIM3, TIM_OC4, ival); - } -} - -static void platform_init_scheduled_outputs() { - gpio_clear(GPIOD, 0xFFFF); - unsigned int i; - for (i = 0; i < MAX_EVENTS; ++i) { - if (config.events[i].inverted && config.events[i].type != DISABLED_EVENT) { - set_output(config.events[i].pin, 1); - } - } - gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, 0xFFFF & ~GPIO5); - gpio_set_output_options( - GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, 0xFFFF & ~GPIO5); - gpio_mode_setup(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, 0xFF); - gpio_set_output_options(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, 0xFF); - - /* dma2 stream 1, channel 7*/ - dma_stream_reset(DMA2, DMA_STREAM1); - dma_set_priority(DMA2, DMA_STREAM1, DMA_SxCR_PL_HIGH); - dma_enable_double_buffer_mode(DMA2, DMA_STREAM1); - dma_set_memory_size(DMA2, DMA_STREAM1, DMA_SxCR_MSIZE_32BIT); - dma_set_peripheral_size(DMA2, DMA_STREAM1, DMA_SxCR_PSIZE_32BIT); - dma_enable_memory_increment_mode(DMA2, DMA_STREAM1); - dma_set_transfer_mode(DMA2, DMA_STREAM1, DMA_SxCR_DIR_MEM_TO_PERIPHERAL); - dma_enable_circular_mode(DMA2, DMA_STREAM1); - dma_set_peripheral_address(DMA2, DMA_STREAM1, (uint32_t)&GPIOD_BSRR); - dma_set_memory_address(DMA2, DMA_STREAM1, (uint32_t)output_buffers[0].slots); - dma_set_memory_address_1( - DMA2, DMA_STREAM1, (uint32_t)output_buffers[1].slots); - dma_set_number_of_data(DMA2, DMA_STREAM1, NUM_SLOTS); - dma_channel_select(DMA2, DMA_STREAM1, DMA_SxCR_CHSEL_7); - dma_enable_direct_mode(DMA2, DMA_STREAM1); - dma_enable_transfer_complete_interrupt(DMA2, DMA_STREAM1); - - timer_disable_counter(TIM8); - dma_enable_stream(DMA2, DMA_STREAM1); - timer_enable_counter(TIM8); - - nvic_enable_irq(NVIC_DMA2_STREAM1_IRQ); - nvic_set_priority(NVIC_DMA2_STREAM1_IRQ, 16); -} - -void platform_enable_event_logging() { - - nvic_enable_irq(NVIC_EXTI0_IRQ); - nvic_enable_irq(NVIC_EXTI1_IRQ); - nvic_enable_irq(NVIC_EXTI2_IRQ); - nvic_enable_irq(NVIC_EXTI3_IRQ); - nvic_enable_irq(NVIC_EXTI4_IRQ); - nvic_enable_irq(NVIC_EXTI9_5_IRQ); - nvic_enable_irq(NVIC_EXTI15_10_IRQ); - - exti_select_source(0xFF, GPIOD); - exti_set_trigger(0xFF, EXTI_TRIGGER_BOTH); - exti_enable_request(0xFF); -} - -void platform_disable_event_logging() { - nvic_disable_irq(NVIC_EXTI0_IRQ); - nvic_disable_irq(NVIC_EXTI1_IRQ); - nvic_disable_irq(NVIC_EXTI2_IRQ); - nvic_disable_irq(NVIC_EXTI3_IRQ); - nvic_disable_irq(NVIC_EXTI4_IRQ); - nvic_disable_irq(NVIC_EXTI9_5_IRQ); - nvic_disable_irq(NVIC_EXTI15_10_IRQ); -} - -static void show_scheduled_outputs() { - uint32_t flag_changes = exti_get_flag_status(0xFF); - console_record_event((struct logged_event){ - .type = EVENT_OUTPUT, - .time = current_time(), - .value = gpio_port_read(GPIOD), - }); - exti_reset_request(flag_changes); - __asm__("dsb"); - __asm__("isb"); -} - -void exti0_isr() { - show_scheduled_outputs(); -} -void exti1_isr() { - show_scheduled_outputs(); -} -void exti2_isr() { - show_scheduled_outputs(); -} -void exti3_isr() { - show_scheduled_outputs(); -} -void exti4_isr() { - show_scheduled_outputs(); -} -void exti9_5_isr() { - show_scheduled_outputs(); -} -void exti15_10_isr() { - show_scheduled_outputs(); -} - -/* We use TIM7 to control the sample rate. It is set up to trigger a DMA event - * on counter update to TX on SPI2. When the full 16 bits is transmitted and - * the SPI RX buffer is filled, the RX DMA event will fill, and populate - * spi_rx_raw_adc. - */ - -#ifdef SPI_AD7888 -#include "ad7888_adc.h" -#define SPI_FREQ_DIVIDER SPI_CR1_BAUDRATE_FPCLK_DIV_32 /* 1.3125 MHz */ -#define SPI_SAMPLE_RATE 70000 -#define ADC_SAMPLE_RATE 5000 -#elif SPI_TLV2553 -#include "tlv2553_adc.h" -#define SPI_FREQ_DIVIDER SPI_CR1_BAUDRATE_FPCLK_DIV_4 /* 10.5 MHz */ -#define SPI_SAMPLE_RATE 150000 -#define ADC_SAMPLE_RATE 5000 -#define KNOCK_SAMPLE_RATE 50000 -#else -#error No ADC specified! -#endif - -static volatile uint16_t spi_rx_raw_adc[2][NUM_SPI_TX] = { 0 }; - -static void platform_init_spi_adc() { - /* Configure SPI output */ - gpio_mode_setup( - GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO13 | GPIO14 | GPIO15); - gpio_set_af(GPIOB, GPIO_AF5, GPIO13 | GPIO14 | GPIO15); - gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO12); - gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIO12); - gpio_clear(GPIOB, GPIO12); - spi_disable(SPI2); - spi_reset(SPI2); - spi_enable_software_slave_management(SPI2); - spi_set_nss_high(SPI2); - spi_init_master(SPI2, - SPI_FREQ_DIVIDER, - SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, - SPI_CR1_CPHA_CLK_TRANSITION_1, - SPI_CR1_DFF_16BIT, - SPI_CR1_MSBFIRST); - - spi_enable_rx_dma(SPI2); - spi_enable(SPI2); - - /* Configure DMA for SPI RX*/ - dma_enable_stream(DMA1, DMA_STREAM3); - dma_stream_reset(DMA1, DMA_STREAM3); - dma_set_priority(DMA1, DMA_STREAM3, DMA_SxCR_PL_LOW); - dma_set_memory_size(DMA1, DMA_STREAM3, DMA_SxCR_MSIZE_16BIT); - dma_set_peripheral_size(DMA1, DMA_STREAM3, DMA_SxCR_PSIZE_16BIT); - dma_enable_memory_increment_mode(DMA1, DMA_STREAM3); - dma_set_transfer_mode(DMA1, DMA_STREAM3, DMA_SxCR_DIR_PERIPHERAL_TO_MEM); - dma_set_peripheral_address(DMA1, DMA_STREAM3, (uint32_t)&SPI2_DR); - dma_enable_double_buffer_mode(DMA1, DMA_STREAM3); - dma_set_memory_address(DMA1, DMA_STREAM3, (uint32_t)spi_rx_raw_adc[0]); - dma_set_memory_address_1(DMA1, DMA_STREAM3, (uint32_t)spi_rx_raw_adc[1]); - dma_set_number_of_data(DMA1, DMA_STREAM3, NUM_SPI_TX); - dma_channel_select(DMA1, DMA_STREAM3, DMA_SxCR_CHSEL_0); - dma_enable_direct_mode(DMA1, DMA_STREAM3); - dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM3); - dma_enable_stream(DMA1, DMA_STREAM3); - - nvic_enable_irq(NVIC_DMA1_STREAM3_IRQ); - nvic_set_priority(NVIC_DMA1_STREAM3_IRQ, 64); - - /* Configure TIM7 to drive DMA for SPI */ - timer_set_mode(TIM7, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); - const uint32_t period = (84000000 / SPI_SAMPLE_RATE) - 1; - timer_set_period(TIM7, period); - timer_disable_preload(TIM7); - timer_continuous_mode(TIM7); - /* Setup output compare registers */ - timer_disable_oc_output(TIM7, TIM_OC1); - timer_disable_oc_output(TIM7, TIM_OC2); - timer_disable_oc_output(TIM7, TIM_OC3); - timer_disable_oc_output(TIM7, TIM_OC4); - - timer_set_prescaler(TIM7, 0); - - /* Set up DMA for SPI TX */ - dma_stream_reset(DMA1, DMA_STREAM2); - dma_set_priority(DMA1, DMA_STREAM2, DMA_SxCR_PL_LOW); - dma_set_memory_size(DMA1, DMA_STREAM2, DMA_SxCR_MSIZE_16BIT); - dma_set_peripheral_size(DMA1, DMA_STREAM2, DMA_SxCR_PSIZE_16BIT); - dma_enable_circular_mode(DMA1, DMA_STREAM2); - dma_enable_memory_increment_mode(DMA1, DMA_STREAM2); - dma_set_transfer_mode(DMA1, DMA_STREAM2, DMA_SxCR_DIR_MEM_TO_PERIPHERAL); - dma_set_peripheral_address(DMA1, DMA_STREAM2, (uint32_t)&SPI2_DR); - dma_set_memory_address(DMA1, DMA_STREAM2, (uint32_t)adc_transmit_sequence); - dma_set_number_of_data(DMA1, DMA_STREAM2, NUM_SPI_TX); - dma_channel_select(DMA1, DMA_STREAM2, DMA_SxCR_CHSEL_1); - dma_enable_direct_mode(DMA1, DMA_STREAM2); - dma_enable_stream(DMA1, DMA_STREAM2); - - timer_enable_counter(TIM7); - timer_enable_update_event(TIM7); - timer_update_on_overflow(TIM7); - timer_set_dma_on_update_event(TIM7); - TIM7_DIER |= TIM_DIER_UDE; /* Enable update dma */ -} - -/* Sensor sampling complete */ -void dma1_stream3_isr(void) { - if (!dma_get_interrupt_flag(DMA1, DMA_STREAM3, DMA_TCIF)) { - return; - } - dma_clear_interrupt_flags(DMA1, DMA_STREAM3, DMA_TCIF); - - const uint16_t *sequence = (dma_get_target(DMA1, DMA_STREAM3) == 0) - ? (const uint16_t *)spi_rx_raw_adc[1] - : (const uint16_t *)spi_rx_raw_adc[0]; - - bool fault = !adc_response_is_valid(sequence); - - for (int i = 0; i < NUM_SENSORS; ++i) { - if (config.sensors[i].source == SENSOR_ADC) { - config.sensors[i].fault = fault ? FAULT_CONN : FAULT_NONE; - config.sensors[i].raw_value = - read_adc_pin(sequence, config.sensors[i].pin); - } - } - - sensors_process(SENSOR_ADC); -} - -static uint8_t usbd_control_buffer[128]; -static usbd_device *usbd_dev; - -/* Most of the following is copied from libopencm3-examples */ -static enum usbd_request_return_codes cdcacm_control_request( - usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, - uint16_t *len, - void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)) { - (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_NOTSUPP; -} - -#define USB_RX_BUF_LEN 1024 -static char usb_rx_buf[USB_RX_BUF_LEN]; -static volatile size_t usb_rx_len = 0; -static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) { - (void)ep; - usb_rx_len += usbd_ep_read_packet( - usbd_dev, 0x01, usb_rx_buf + usb_rx_len, USB_RX_BUF_LEN - usb_rx_len); -} - -static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue) { - (void)wValue; - - usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb); - usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL); - usbd_ep_setup(usbd_dev, 0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); - - usbd_register_control_callback(usbd_dev, - USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - cdcacm_control_request); -} - -static const struct usb_device_descriptor dev = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = USB_CLASS_CDC, - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, - .bMaxPacketSize0 = 64, - .idVendor = 0x0483, - .idProduct = 0x5740, - .bcdDevice = 0x0200, - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 3, - .bNumConfigurations = 1, -}; - -static const struct usb_endpoint_descriptor comm_endp[] = { { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x83, - .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 = 0x01, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = 64, - .bInterval = 1, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x82, - .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 = 1, - }, - .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 = 0, - .bSubordinateInterface0 = 1, - } -}; - -static const struct usb_interface_descriptor comm_iface[] = { - { .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .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) } -}; - -static const struct usb_interface_descriptor data_iface[] = { { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0, - - .endpoint = data_endp, -} }; - -static const struct usb_interface ifaces[] = { { - .num_altsetting = 1, - .altsetting = comm_iface, - }, - { - .num_altsetting = 1, - .altsetting = data_iface, - } }; - -static const struct usb_config_descriptor usb_config = { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = 2, - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0x80, - .bMaxPower = 0x32, - - .interface = ifaces, -}; - -static const char *const usb_strings[] = { - "https://github.com/via/viaems/", - "ViaEMS console", - "0", -}; - -void otg_fs_isr() { - usbd_poll(usbd_dev); -} - -void platform_init_usb() { - - gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); - gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12); - - usbd_dev = usbd_init(&otgfs_usb_driver, - &dev, - &usb_config, - usb_strings, - 3, - usbd_control_buffer, - sizeof(usbd_control_buffer)); - - usbd_register_set_config_callback(usbd_dev, cdcacm_set_config); - nvic_enable_irq(NVIC_OTG_FS_IRQ); - nvic_set_priority(NVIC_OTG_FS_IRQ, 128); -} - -static void setup_task_handler() { - /* Set up systick for 100 hz */ - systick_set_reload(1680000); - systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); - systick_counter_enable(); - nvic_set_priority(NVIC_SYSTICK_IRQ, 128); - systick_interrupt_enable(); - - /* Setup IWDG to reset if we pass 30 mS without an interrupt */ - iwdg_set_period_ms(30); - iwdg_start(); -} - -void platform_benchmark_init() { - rcc_clock_setup_pll(&rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_168MHZ]); - rcc_wait_for_osc_ready(RCC_HSE); - rcc_periph_clock_enable(RCC_SYSCFG); - rcc_periph_clock_enable(RCC_GPIOA); - rcc_periph_clock_enable(RCC_GPIOE); - rcc_periph_clock_enable(RCC_OTGFS); - gpio_mode_setup(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLDOWN, 0xFFFF); - dwt_enable_cycle_counter(); - platform_init_usb(); -} - -void platform_init() { - - /* 168 Mhz clock */ - rcc_clock_setup_pll(&rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_168MHZ]); - - /* Enable clocks for subsystems */ - rcc_periph_clock_enable(RCC_GPIOA); - rcc_periph_clock_enable(RCC_GPIOB); - rcc_periph_clock_enable(RCC_GPIOC); - rcc_periph_clock_enable(RCC_GPIOD); - rcc_periph_clock_enable(RCC_GPIOE); - rcc_periph_clock_enable(RCC_SYSCFG); - rcc_periph_clock_enable(RCC_DMA1); - rcc_periph_clock_enable(RCC_DMA2); - rcc_periph_clock_enable(RCC_TIM1); - rcc_periph_clock_enable(RCC_TIM2); - rcc_periph_clock_enable(RCC_TIM3); - rcc_periph_clock_enable(RCC_TIM6); - rcc_periph_clock_enable(RCC_TIM7); - rcc_periph_clock_enable(RCC_TIM8); - rcc_periph_clock_enable(RCC_SPI2); - rcc_periph_clock_enable(RCC_OTGFS); - - /* Wait for clock to spin up */ - rcc_wait_for_osc_ready(RCC_HSE); - - scb_set_priority_grouping(SCB_AIRCR_PRIGROUP_GROUP16_NOSUB); - platform_init_scheduled_outputs(); - platform_init_eventtimer(); - platform_init_spi_adc(); - platform_init_pwm(); - platform_init_usb(); - - for (int i = 0; i < NUM_SENSORS; ++i) { - if (config.sensors[i].source == SENSOR_DIGITAL) { - gpio_mode_setup(GPIOE, - GPIO_MODE_INPUT, - GPIO_PUPD_PULLDOWN, - (1 << config.sensors[i].pin)); - } - } - dwt_enable_cycle_counter(); - - setup_task_handler(); -} - -void sys_tick_handler(void) { - run_tasks(); - iwdg_reset(); -} - -static void platform_disable_periphs() { - rcc_periph_reset_pulse(RST_OTGFS); - - /* Shut down subsystems */ - rcc_periph_reset_pulse(RST_GPIOA); - rcc_periph_clock_disable(RCC_GPIOA); - - rcc_periph_reset_pulse(RST_GPIOB); - rcc_periph_clock_disable(RCC_GPIOB); - - rcc_periph_reset_pulse(RST_GPIOC); - rcc_periph_clock_disable(RCC_GPIOC); - - rcc_periph_reset_pulse(RST_GPIOD); - rcc_periph_clock_disable(RCC_GPIOD); - - rcc_periph_reset_pulse(RST_GPIOE); - rcc_periph_clock_disable(RCC_GPIOE); - - rcc_periph_reset_pulse(RST_SYSCFG); - rcc_periph_clock_disable(RCC_SYSCFG); - - rcc_periph_reset_pulse(RST_DMA1); - rcc_periph_clock_disable(RCC_DMA1); - - rcc_periph_reset_pulse(RST_DMA2); - rcc_periph_clock_disable(RCC_DMA2); - - rcc_periph_reset_pulse(RST_TIM1); - rcc_periph_clock_disable(RCC_TIM1); - - rcc_periph_reset_pulse(RST_TIM2); - rcc_periph_clock_disable(RCC_TIM2); - - rcc_periph_reset_pulse(RST_TIM3); - rcc_periph_clock_disable(RCC_TIM3); - - rcc_periph_reset_pulse(RST_TIM6); - rcc_periph_clock_disable(RCC_TIM6); - - rcc_periph_reset_pulse(RST_TIM7); - rcc_periph_clock_disable(RCC_TIM7); - - rcc_periph_reset_pulse(RST_TIM8); - rcc_periph_clock_disable(RCC_TIM8); - - rcc_periph_reset_pulse(RST_SPI2); - rcc_periph_clock_disable(RCC_SPI2); - - rcc_periph_reset_pulse(RST_OTGFS); - rcc_periph_clock_disable(RCC_OTGFS); -} - -#define BOOTLOADER_ADDR 0x1fff0000 -void platform_reset_into_bootloader() { - handle_emergency_shutdown(); - - platform_disable_periphs(); - - /* 168 Mhz clock */ - rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_3V3_168MHZ]); - - __asm__ volatile("msr msp, %0" ::"g"(*(volatile uint32_t *)BOOTLOADER_ADDR)); - (*(void (**)())(BOOTLOADER_ADDR + 4))(); - while (1) - ; -} - -uint64_t cycle_count() { - return dwt_read_cycle_counter(); -} - -uint64_t cycles_to_ns(uint64_t cycles) { - return cycles * 1000 / 168; -} - -/* This is now the lowest priority interrupt, with buffer swapping and sensor - * reading interrupts being higher priority. Keep scheduled callbacks quick to - * keep scheduling delay low. Currently nothing takes more than 15 uS - * (rescheduling individual events). All fueling calculations and scheduling is - * now done in the ISR for trigger updates. - */ -void tim2_isr() { - - bool cc1_fired = false; - bool cc2_fired = false; - timeval_t cc1; - timeval_t cc2; - - if (timer_get_flag(TIM2, TIM_SR_CC1IF)) { - cc1_fired = true; - cc1 = TIM2_CCR1; - timer_clear_flag(TIM2, TIM_SR_CC1IF); - } - - if (timer_get_flag(TIM2, TIM_SR_CC2IF)) { - cc2_fired = true; - cc2 = TIM2_CCR2; - timer_clear_flag(TIM2, TIM_SR_CC2IF); - } - - /* Did we miss a capture event? */ - if (timer_get_flag(TIM2, TIM_SR_CC1OF) || - timer_get_flag(TIM2, TIM_SR_CC2OF)) { - timer_clear_flag(TIM2, TIM_SR_CC1OF); - timer_clear_flag(TIM2, TIM_SR_CC2OF); - decoder_desync(DECODER_OVERFLOW); - return; - } - - if (cc1_fired && cc2_fired && time_before(cc2, cc1)) { - decoder_update_scheduling(1, cc2); - decoder_update_scheduling(0, cc1); - } else { - if (cc1_fired) { - decoder_update_scheduling(0, cc1); - } - if (cc2_fired) { - decoder_update_scheduling(1, cc2); - } - } - - if (timer_get_flag(TIM2, TIM_SR_CC4IF)) { - timer_clear_flag(TIM2, TIM_SR_CC4IF); - scheduler_callback_timer_execute(); - } -} -void dma2_stream1_isr(void) { - if (dma_get_interrupt_flag(DMA2, DMA_STREAM1, DMA_TCIF)) { - dma_clear_interrupt_flags(DMA2, DMA_STREAM1, DMA_TCIF); - stm32_buffer_swap(); - - if (stm32_current_buffer() != dma_get_target(DMA2, DMA_STREAM1)) { - /* We have overflowed or gone out of sync, abort immediately */ - abort(); - } - } -} - -volatile int interrupt_disables = 0; -void enable_interrupts() { - interrupt_disables -= 1; - assert(interrupt_disables >= 0); - cm_enable_interrupts(); -} - -/* Returns current enabled status prior to call */ -void disable_interrupts() { - cm_disable_interrupts(); - interrupt_disables += 1; -} - -bool interrupts_enabled() { - return !cm_is_masked_interrupts(); -} - -void schedule_event_timer(timeval_t time) { - assert(!interrupts_enabled()); - /* Clear out any pending interrupt */ - timer_disable_irq(TIM2, TIM_DIER_CC4IE); - timer_clear_flag(TIM2, TIM_SR_CC4IF); - - /* Set timer and enable interrupt */ - timer_set_oc_value(TIM2, TIM_OC4, time); - timer_enable_irq(TIM2, TIM_DIER_CC4IE); - - /* If time is prior to now, we may have missed it, set the interrupt pending - * just in case */ - if (time_before_or_equal(time, current_time())) { - timer_generate_event(TIM2, TIM_EGR_CC4G); - } -} - -timeval_t current_time() { - return timer_get_counter(TIM2); -} - -int get_output(int output) { - return gpio_get(GPIOD, (1 << output)) != 0; -} - -void set_output(int output, char value) { - if (value) { - gpio_set(GPIOD, (1 << output)); - } else { - gpio_clear(GPIOD, (1 << output)); - } -} - -int get_gpio(int output) { - return gpio_get(GPIOE, (1 << output)) != 0; -} - -void set_gpio(int output, char value) { - if (value) { - gpio_set(GPIOE, (1 << output)); - } else { - gpio_clear(GPIOE, (1 << output)); - } -} - -extern unsigned _configdata_loadaddr, _sconfigdata, _econfigdata; -void platform_load_config() { - volatile unsigned *src, *dest; - for (src = &_configdata_loadaddr, dest = &_sconfigdata; dest < &_econfigdata; - src++, dest++) { - *dest = *src; - } -} - -void platform_save_config() { - volatile unsigned *src, *dest; - int n_sectors, conf_bytes; - - handle_emergency_shutdown(); - platform_disable_periphs(); - /* Flash erase takes longer than our watchdog */ - iwdg_set_period_ms(5000); - - flash_unlock(); - - /* configrom is sectors 1 through 3, each 16k, - * compute how many we need to erase */ - conf_bytes = ((char *)&_econfigdata - (char *)&_sconfigdata); - n_sectors = (conf_bytes + 16385) / 16386; - - for (int sector = 1; sector < 1 + n_sectors; sector++) { - flash_erase_sector(sector, 2); - } - for (dest = &_configdata_loadaddr, src = &_sconfigdata; src < &_econfigdata; - src++, dest++) { - flash_program_word((uint32_t)dest, *src); - } - - flash_lock(); - reset_handler(); -} - -size_t console_read(void *buf, size_t max) { - disable_interrupts(); - size_t amt = usb_rx_len > max ? max : usb_rx_len; - memcpy(buf, usb_rx_buf, amt); - memmove(usb_rx_buf, usb_rx_buf + amt, usb_rx_len - amt); - usb_rx_len -= amt; - enable_interrupts(); - return amt; -} - -size_t console_write(const void *buf, size_t count) { - size_t rem = count > 64 ? 64 : count; - /* https://github.com/libopencm3/libopencm3/issues/531 - * We can't let the usb irq be called while writing */ - nvic_disable_irq(NVIC_OTG_FS_IRQ); - __asm__("dsb"); - __asm__("isb"); - rem = usbd_ep_write_packet(usbd_dev, 0x82, buf, rem); - nvic_enable_irq(NVIC_OTG_FS_IRQ); - __asm__("dsb"); - __asm__("isb"); - return rem; -} - -/* Use usb to send text from newlib printf */ -ssize_t __attribute__((externally_visible)) -_write(int fd, const char *buf, size_t count) { - (void)fd; - size_t pos = 0; - while (pos < count) { - pos += console_write(buf + pos, count - pos); - usbd_poll(usbd_dev); - } - return count; -} - -void __attribute__((externally_visible)) _exit(int status) { - (void)status; - - handle_emergency_shutdown(); - while (1) - ; -} - -/* TODO: implement graceful shutdown of outputs on fault */ -#define STACK_CHK_GUARD 0xe2dee396 -uintptr_t __attribute__((externally_visible)) __stack_chk_guard = - STACK_CHK_GUARD; - -__attribute__((noreturn)) __attribute__((externally_visible)) void -__stack_chk_fail(void) { - while (1) - ; -} - -void platform_freeze_timers() { - timer_disable_counter(TIM8); -} - -void platform_unfreeze_timers() { - timer_enable_counter(TIM8); -} - -uint32_t platform_adc_samplerate(void) { -#ifdef ADC_SAMPLE_RATE - return ADC_SAMPLE_RATE; -#else - return 0; -#endif -} -uint32_t platform_knock_samplerate(void) { -#ifdef KNOCK_SAMPLE_RATE - return KNOCK_SAMPLE_RATE; -#else - return 1000; -#endif -} diff --git a/src/platforms/stm32f4-discovery.ld b/src/platforms/stm32f4-discovery.ld deleted file mode 100644 index e6c35db4..00000000 --- a/src/platforms/stm32f4-discovery.ld +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of the libopencm3 project. - * - * Copyright (C) 2009 Uwe Hermann - * Copyright (C) 2011 Stephen Caudle - * - * 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 . - */ - -/* Linker script for ST STM32F4DISCOVERY (STM32F407VG, 1024K flash, 128K RAM). */ - -/* Define memory regions. */ -MEMORY -{ - vectors(rx) : ORIGIN = 0x08000000, LENGTH = 16K - configrom (rx) : ORIGIN = 0x08004000, LENGTH = 48K - rom (rx) : ORIGIN = 0x08010000, LENGTH = 960K - ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K - ccm (rw) : ORIGIN = 0x10000000, LENGTH = 64K -} - -EXTERN (vector_table) -ENTRY(reset_handler) - -SECTIONS { - .vectors : { - *(.vectors) - } > vectors - - .text : { - *(.text*) /* Program code */ - . = ALIGN(4); - *(.rodata*) /* Read-only data */ - . = ALIGN(4); - } >rom - - /* C++ Static constructors/destructors, also used for __attribute__ - * ((constructor)) and the likes */ - .preinit_array : { - . = ALIGN(4); - __preinit_array_start = .; - KEEP (*(.preinit_array)) - __preinit_array_end = .; - } >rom - .init_array : { - . = ALIGN(4); - __init_array_start = .; - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array)) - __init_array_end = .; - } >rom - .fini_array : { - . = ALIGN(4); - __fini_array_start = .; - KEEP (*(.fini_array)) - KEEP (*(SORT(.fini_array.*))) - __fini_array_end = .; - } >rom - - /* - * Another section used by C++ stuff, appears when using newlib with - * 64bit (long long) printf support - */ - .ARM.extab : { - *(.ARM.extab*) - } >rom - .ARM.exidx : { - __exidx_start = .; - *(.ARM.exidx*) - __exidx_end = .; - } >rom - - . = ALIGN(4); - _etext = .; - - .data : { - _data = .; - *(.data*) /* Read-write initialized data */ - *(.dmadata*) /* DMA regions, no different from normal data on stm32f4 */ - . = ALIGN(4); - _edata = .; - } >ram AT >rom - _data_loadaddr = LOADADDR(.data); - - .configdata : { - _sconfigdata = .; - *(.configdata*) - . = ALIGN(4); - _econfigdata = .; - } >ram AT >configrom - _configdata_loadaddr = LOADADDR(.configdata); - - .bss : { - *(.bss*) /* Read-write zero initialized data */ - *(COMMON) - . = ALIGN(4); - _ebss = .; - } >ram - - /* - * The .eh_frame section appears to be used for C++ exception handling. - * You may need to fix this if you're using C++. - */ - /DISCARD/ : { *(.eh_frame) } - - . = ALIGN(4); - end = .; -} - -PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); - diff --git a/src/platforms/stm32f4/README.md b/src/platforms/stm32f4/README.md new file mode 100644 index 00000000..4ace5faa --- /dev/null +++ b/src/platforms/stm32f4/README.md @@ -0,0 +1,35 @@ +## STM32F4xx + +This platform primarily targets the STM32F427VGT 1MB 100 LQFP package, and +explicitly does not support the STM32F407. This is primarily due to: + - Errata with the DMA that affects the 407 + - Dual bank flash allows hot updates in the 427 + +Other part numbers could be supported with minor changes. + +Config data is stored in sectors 12-15, which are the first 64K of bank 2. For +1MB chips, make sure DB1M in OPTCR DB1M is set to use dual bank support. On 2MB +chips, please update the linker script for the appropriate + +### Pins +Use | Pins +--- | --- +Trigger 0 | `PA0` +Trigger 1 | `PA1` +ADC | `PA5`, `PA6`, `PA7`, and `PA8` +USB | `PA9`, `PA11`, and `PA12` +OUT | `PD0`-`PD15` +GPIO | `PE0`-`PE15` +PWM | `PC6`-`PC9` + +### Resources +System | Use +--- | --- +SPI1 | TLV2553 ADC +TIM1 | SPI(ADC) sample timer +TIM2 | Main CPU Clock, event timer, and capture units for trigger inputs +TIM3 | PWM driver, fixed at 30 Hz +TIM8 | TIM2 4 MHz tick driver, triggers TIM2 on update +USBFS | Console +SYSTICK | 100 Hz task driver + diff --git a/src/platforms/stm32f4/stm32f4.c b/src/platforms/stm32f4/stm32f4.c new file mode 100644 index 00000000..cbd355c4 --- /dev/null +++ b/src/platforms/stm32f4/stm32f4.c @@ -0,0 +1,280 @@ +#include "stm32f427xx.h" +#include + +#include "platform.h" +#include "tasks.h" + +#ifndef CRYSTAL_FREQ +#define CRYSTAL_FREQ 8 +#endif + +void platform_enable_event_logging() {} + +void platform_disable_event_logging() {} + +#define BOOTLOADER_FLAG 0x56780123 +static volatile uint32_t bootloader_flag = 0; +void platform_reset_into_bootloader() { + bootloader_flag = BOOTLOADER_FLAG; + NVIC_SystemReset(); +} + +void disable_interrupts() { + __disable_irq(); +} + +void enable_interrupts() { + __enable_irq(); +} + +uint64_t cycles_to_ns(uint64_t cycles) { + return cycles * 1000 / 168; +} + +uint64_t cycle_count() { + return DWT->CYCCNT; +} + +bool interrupts_enabled() { + return __get_PRIMASK() == 0; +} + +void set_output(int output, char value) { + if (value) { + GPIOD->ODR |= (1 << output); + } else { + GPIOD->ODR &= ~(1 << output); + } +} + +void set_gpio(int output, char value) { + if (value) { + GPIOE->ODR |= (1 << output); + } else { + GPIOE->ODR &= ~(1 << output); + } +} + +int get_gpio(int output) { + return (GPIOE->IDR & (1 << output)) > 0; +} + +static void enable_peripherals(void) { + RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN; + + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM8EN | RCC_APB2ENR_SPI1EN; + + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | + RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN | + RCC_AHB1ENR_GPIOEEN | RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMA2EN; + + RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; +} + +/* Setup power, HSE, clock tree, and flash wait states */ +static void setup_clocks() { + RCC->CR |= RCC_CR_HSEON; + while ((RCC->CR & RCC_CR_HSERDY) == 0) + ; /* Wait for HSE to be stable */ + + /* Configure PLL with given crystal to produce a 168 MHz P and 48 MHz Q (For + * USB) */ + RCC->PLLCFGR = RCC_PLLCFGR_PLLSRC_HSE | + _VAL2FLD(RCC_PLLCFGR_PLLM, CRYSTAL_FREQ) | /* PLL M = 8 */ + _VAL2FLD(RCC_PLLCFGR_PLLN, 336) | /* PLL N = 336 */ + _VAL2FLD(RCC_PLLCFGR_PLLQ, 7) | /* PLL Q = 7 */ + _VAL2FLD(RCC_PLLCFGR_PLLP, 0); /* PLL P = 2 */ + + RCC->CR |= RCC_CR_PLLON; + while ((RCC->CR & RCC_CR_PLLRDY) == 0) + ; /* Wait for PLL to be stable */ + + PWR->CR |= PWR_CR_ODEN; + while ((PWR->CR & PWR_CSR_ODRDY) == 0) + ; /* Wait for overdrive ready */ + + PWR->CR |= PWR_CR_ODSWEN; + while ((PWR->CR & PWR_CSR_ODSWRDY) == 0) + ; /* Wait for overdrive switch */ + + RCC->CFGR = RCC_CFGR_PPRE2_DIV2 | /* APB2 = HCLK / 2 = 84 MHz */ + RCC_CFGR_PPRE1_DIV4 | /* APB1 = HCLK / 4 = 42 MHz */ + _VAL2FLD(RCC_CFGR_HPRE, 0); /* AHB = HCLK = 168 MHz */ + + FLASH->ACR = + FLASH_ACR_LATENCY_5WS | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN; + + while ((RCC->CR & RCC_CR_PLLRDY) == 0) + ; /* Wait for PLL lock */ + + RCC->CFGR |= RCC_CFGR_SW_PLL; /* Enable PLL as system clock source */ +} + +static void setup_dwt() { + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + DWT->CYCCNT = 0; +} + +static void reset_watchdog() { + IWDG->KR = 0x0000AAAA; +} + +static void setup_watchdog() { + IWDG->KR = 0x0000CCCC; /* Start watchdog */ + IWDG->KR = 0x00005555; /* Magic unlock sequence */ + IWDG->RLR = 245; /* Approx 30 mS */ + + DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_IWDG_STOP; + reset_watchdog(); +} + +void SysTick_Handler(void) { + run_tasks(); + reset_watchdog(); +} + +static void setup_systick() { + NVIC_SetPriority(SysTick_IRQn, 15); + SysTick->LOAD = 1680000; /* 100 Hz at 168 MHz HCLK */ + SysTick->CTRL = SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_CLKSOURCE_Msk | /* Use HCLK */ + SysTick_CTRL_ENABLE_Msk; +} + +extern unsigned _configdata_loadaddr, _sconfigdata, _econfigdata; +void platform_save_config() { + + /* Unlock FLASH_CR */ + FLASH->KEYR = 0x45670123; + FLASH->KEYR = 0xCDEF89AB; + + FLASH->CR &= ~FLASH_CR_PSIZE; + FLASH->CR |= _VAL2FLD(FLASH_CR_PSIZE, 2); /* 32 bit write parallelism */ + for (int s = 12; s <= 15; s++) { + uint16_t s_to_erase = + (0x10 - 12) + s; /* Bank 2 sectors start at 0x10 vs 12*/ + FLASH->CR &= ~(FLASH_CR_SNB); + FLASH->CR |= + FLASH_CR_SER | _VAL2FLD(FLASH_CR_SNB, s_to_erase); /* Erase sector 8 */ + FLASH->CR |= FLASH_CR_STRT; + while (FLASH->SR & FLASH_SR_BSY) + ; + } + + FLASH->ACR &= ~FLASH_ACR_DCEN; /* Errata 2.2.12: Disable flash data cache + while programming */ + FLASH->CR |= FLASH_CR_PG; + + uint32_t *src; + uint32_t *dest; + for (src = &_sconfigdata, dest = &_configdata_loadaddr; src < &_econfigdata; + src++, dest++) { + *dest = *src; + /* Wait for BSY to clear */ + while (FLASH->SR & FLASH_SR_BSY) + ; + } + + FLASH->CR |= FLASH_CR_LOCK; /* Re-lock */ + + FLASH->ACR |= FLASH_ACR_DCRST; /* Flush and re-enable data cache */ + FLASH->ACR |= FLASH_ACR_DCEN; +} + +void platform_load_config() { + volatile unsigned *src, *dest; + for (src = &_configdata_loadaddr, dest = &_sconfigdata; dest < &_econfigdata; + src++, dest++) { + *dest = *src; + } +} + +/* Common symbols exported by the linker script(s): */ +extern uint32_t _data_loadaddr, _sdata, _edata, _ebss; + +void Reset_Handler(void) { + bool jump_to_bootloader = false; + if (bootloader_flag == BOOTLOADER_FLAG) { + bootloader_flag = 0; + jump_to_bootloader = true; + } + + volatile uint32_t *src, *dest; + for (src = &_data_loadaddr, dest = &_sdata; dest < &_edata; src++, dest++) { + *dest = *src; + } + + while (dest < &_ebss) { + *dest++ = 0; + } + + SCB->CPACR |= + ((3UL << (10 * 2)) | (3UL << (11 * 2))); /* set CP10 and CP11 Full Access */ + SCB->CCR |= SCB_CCR_STKALIGN_Msk; + + if (jump_to_bootloader) { + /* We've set this flag and reset the cpu, jump to system bootloader */ + RCC->AHB1ENR |= RCC_APB2ENR_SYSCFGEN; + + volatile uint32_t *bootloader_msp = (uint32_t *)0x1fff0000; /* Per AN2606 */ + volatile uint32_t *bootloader_addr = bootloader_msp + 1; + + __set_MSP(*bootloader_msp); + void (*bootloader)() = (void (*)(void))(*bootloader_addr); + + SYSCFG->MEMRMP = + _VAL2FLD(SYSCFG_MEMRMP_MEM_MODE, 0x1); /* Remap System Mem */ + bootloader(); + } + + /* Call the application's entry point. */ + int main(); + (void)main(); +} + +/* Use usb to send text from newlib printf */ +int __attribute__((externally_visible)) +_write(int fd, const char *buf, size_t count) { + (void)fd; + size_t pos = 0; + while (pos < count) { + pos += console_write(buf + pos, count - pos); + } + return count; +} + +static void setup_gpios(void) { + GPIOE->MODER = 0x55555555; /* All of GPIOE is output */ + GPIOE->OSPEEDR = 0xffffffff; /* All GPIOE set to High speed*/ +} + +extern void stm32f4_configure_scheduler(void); +extern void stm32f4_configure_usb(void); +extern void stm32f4_configure_adc(void); +extern void stm32f4_configure_pwm(void); + +void platform_init() { + enable_peripherals(); + setup_clocks(); + + setup_dwt(); + + NVIC_SetPriorityGrouping(3); /* 16 priority preemption levels */ + + setup_systick(); + setup_watchdog(); + setup_gpios(); + + stm32f4_configure_scheduler(); + stm32f4_configure_usb(); + stm32f4_configure_adc(); + stm32f4_configure_pwm(); +} + +void platform_benchmark_init() { + enable_peripherals(); + setup_clocks(); + setup_dwt(); + NVIC_SetPriorityGrouping(3); /* 16 priority preemption levels */ + stm32f4_configure_usb(); +} diff --git a/src/platforms/stm32f4/stm32f4.ld b/src/platforms/stm32f4/stm32f4.ld new file mode 100644 index 00000000..413c351b --- /dev/null +++ b/src/platforms/stm32f4/stm32f4.ld @@ -0,0 +1,64 @@ +/* Memory layout for STM32F427, dual bank 1 MB package. */ + +MEMORY +{ + rom(rx) : ORIGIN = 0x08000000, LENGTH = 512K + + /* Store configrom in the first 64K of the second bank. + * To support 2MB in dual bank, change to 0x8100000 */ + configrom(rx) : ORIGIN = 0x08080000, LENGTH = 64K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K + ccm (rw) : ORIGIN = 0x10000000, LENGTH = 64K +} + +ENTRY(Reset_Handler) + +SECTIONS { + + .text : { + KEEP(*(.isr_vector)) + . = ALIGN(4); + *(.text*) /* Program code */ + . = ALIGN(4); + *(.rodata*) /* Read-only data */ + . = ALIGN(4); + _etext = .; + } >rom + + + .data : { + _sdata = .; + *(.data*) /* Read-write initialized data */ + *(.dmadata*) /* DMA regions, no different from normal data on stm32f4 */ + . = ALIGN(4); + _edata = .; + } >ram AT >rom + _data_loadaddr = LOADADDR(.data); + + .configdata : { + _sconfigdata = .; + *(.configdata*) + . = ALIGN(4); + _econfigdata = .; + } >ram AT >configrom + _configdata_loadaddr = LOADADDR(.configdata); + + .bss : { + *(.bss*) /* Read-write zero initialized data */ + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + + /* + * The .eh_frame section appears to be used for C++ exception handling. + * You may need to fix this if you're using C++. + */ + /DISCARD/ : { *(.eh_frame) } + + . = ALIGN(4); + end = .; +} + +PROVIDE(_estack = ORIGIN(ram) + LENGTH(ram)); + diff --git a/src/platforms/stm32f4/stm32f4_pwm.c b/src/platforms/stm32f4/stm32f4_pwm.c new file mode 100644 index 00000000..6b1953e2 --- /dev/null +++ b/src/platforms/stm32f4/stm32f4_pwm.c @@ -0,0 +1,67 @@ +#include "platform.h" +#include "tasks.h" + +#include "stm32f427xx.h" + +/* Use TIM3 as a fixed-frequency 4-output PWM timer. This has the downside of + * requiring all four PWM outputs to be the same frequency, but uses a + * contiguous port-C block that isn't used elsewhere. In the future we can also + * use TIMER9 and TIMER12 to add more frequency selections. Other + * considerations were TIMER10, 11, and 13, but their outputs overlap with CAN0 + * and SPI. + */ + +/* For now, only set up TIM3 hardcoded to 30 Hz, and configure PC6-PC9 as the + * four outputs */ +void stm32f4_configure_pwm(void) { + /* Set GPIOC 6-9 to AF */ + GPIOC->MODER |= _VAL2FLD(GPIO_MODER_MODE6, 2) | + _VAL2FLD(GPIO_MODER_MODE7, 2) | + _VAL2FLD(GPIO_MODER_MODE8, 2) | _VAL2FLD(GPIO_MODER_MODE9, 2); + + /* AF 2 */ + GPIOC->AFR[0] |= + _VAL2FLD(GPIO_AFRL_AFSEL6, 2) | _VAL2FLD(GPIO_AFRL_AFSEL7, 2); + GPIOC->AFR[1] |= + _VAL2FLD(GPIO_AFRH_AFSEL8, 2) | _VAL2FLD(GPIO_AFRH_AFSEL9, 2); /* AF1 */ + + TIM3->ARR = 65535; + /* TIM3 is 2x APB1's clock = 84 MHz, Hz when counting to 65536 we want to + * target 30 Hz */ + uint32_t period = 84000000 / 65536 / 30; + TIM3->PSC = period - 1; + + TIM3->CCR1 = 0; + TIM3->CCR2 = 0; + TIM3->CCR3 = 0; + TIM3->CCR4 = 0; + + /* Enable preload and PWM mode 1 for all outputs */ + TIM3->CCMR1 = _VAL2FLD(TIM_CCMR1_OC1M, 0x6) | TIM_CCMR1_OC1PE | + _VAL2FLD(TIM_CCMR1_OC2M, 0x6) | TIM_CCMR1_OC2PE; + TIM3->CCMR2 = _VAL2FLD(TIM_CCMR2_OC3M, 0x6) | TIM_CCMR2_OC3PE | + _VAL2FLD(TIM_CCMR2_OC4M, 0x6) | TIM_CCMR2_OC4PE; + + /* Enable all outputs */ + TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E; + + TIM3->CR1 = TIM_CR1_CEN; /* Start timer */ +} + +void set_pwm(int output, float percent) { + uint16_t duty = (percent / 100.0f) * 65535; + switch (output) { + case 1: + TIM3->CCR1 = duty; + break; + case 2: + TIM3->CCR2 = duty; + break; + case 3: + TIM3->CCR3 = duty; + break; + case 4: + TIM3->CCR4 = duty; + break; + } +} diff --git a/src/platforms/stm32f4/stm32f4_sched.c b/src/platforms/stm32f4/stm32f4_sched.c new file mode 100644 index 00000000..b645d9e6 --- /dev/null +++ b/src/platforms/stm32f4/stm32f4_sched.c @@ -0,0 +1,198 @@ +#include "config.h" +#include "decoder.h" +#include "platform.h" +#include "scheduler.h" +#include "stm32f427xx.h" +#include "util.h" + +#include "stm32_sched_buffers.h" + +/* The primary outputs are driven by the DMA copying from a circular + * double-buffer to the GPIO's BSRR register. TIM8 is configured to generate + * an update even at 4 MHz, and that update event triggers the DMA. The same + * update event also triggers TIM2, which keeps track of the current time. + * + * Each DMA buffer represents 32 uS of time (128 time points at 4 MHz), and each + * 32 bit word in the DMA buffer is directly written to BSRR. The high 16 bits + * of this word cause the GPIO for that bit to go high, the low 16 bits cause it + * to go low. + * + * The DMA buffers are updated and modified only in the DMA ISR, which is + * triggered once the DMA has finished the buffer and is currently executing on + * the other buffer. At this time, we iterate through all events that would have + * been in the time range of the completed buffer and set them to FIRED. We + * then enumerate all events that are scheduled for the next time range and set + * the bits appropriately. + * + * Since TIM2 is the main time base and also has input captures, we use the + * first two capture units as the trigger inputs. These inputs trigger a + * capture interrupt, the captured time is used to directly call the decoder, + * which optionally may do fuel/ignition calculations and reschedule events. + */ + +static void setup_tim8(void) { + TIM8->CR2 = _VAL2FLD(TIM_CR2_MMS, 2); /* Master mode: TRGO on update */ + TIM8->ARR = 41; /* Period of (41+1) with 168 MHz Clock produces 4 MHz */ + TIM8->DIER = TIM_DIER_UDE; /* Enable DMA event on update*/ + TIM8->CR1 = TIM_CR1_CEN; /* Start counter */ + + DBGMCU->APB2FZ |= DBGMCU_APB2_FZ_DBG_TIM8_STOP; /* Stop timer in debug hold */ +} + +static void configure_trigger_inputs(void) { + trigger_edge cc1_edge = config.freq_inputs[0].edge; + trigger_edge cc2_edge = config.freq_inputs[1].edge; + bool cc1_en = (config.freq_inputs[0].type == TRIGGER); + bool cc2_en = (config.freq_inputs[1].type == TRIGGER); + + bool cc1p = (cc1_edge == FALLING_EDGE) || (cc1_edge == BOTH_EDGES); + bool cc1np = (cc1_edge == BOTH_EDGES); + bool cc2p = (cc2_edge == FALLING_EDGE) || (cc2_edge == BOTH_EDGES); + bool cc2np = (cc2_edge == BOTH_EDGES); + + /* Ensure CC1 and CC2 are disabled */ + TIM2->CCER &= ~(TIM_CCER_CC1E | TIM_CCER_CC2E); + + /* Configure 2 input captures for trigger inputs */ + uint32_t trigger_filter = 0x0; /* No filtering */ + TIM2->CCMR1 = _VAL2FLD(TIM_CCMR1_IC1F, trigger_filter) | + _VAL2FLD(TIM_CCMR1_CC1S, 0x1) | /* CC1 uses TI1 */ + _VAL2FLD(TIM_CCMR1_IC2F, trigger_filter) | + _VAL2FLD(TIM_CCMR1_CC2S, 0x1); /* CC2 uses TI2 */ + TIM2->CCER = (cc1p ? TIM_CCER_CC1P : 0) | (cc1np ? TIM_CCER_CC2NP : 0) | + (cc2p ? TIM_CCER_CC2P : 0) | (cc2np ? TIM_CCER_CC2NP : 0) | + TIM_CCER_CC4E | /* Setup output compare for callbacks */ + (cc1_en ? TIM_CCER_CC1E : 0) | (cc2_en ? TIM_CCER_CC2E : 0); +} + +static void setup_tim2(void) { + + TIM2->SMCR = _VAL2FLD(TIM_SMCR_SMS, 7) | /* External clock mode 1 */ + _VAL2FLD(TIM_SMCR_TS, 1); /* ITR1 (TRGO from TIM8) */ + TIM2->ARR = 0xffffffff; + + configure_trigger_inputs(); + + NVIC_SetPriority(TIM2_IRQn, 3); + NVIC_EnableIRQ(TIM2_IRQn); + + TIM2->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE; /* Enable trigger input capture + interrupts */ + TIM2->CR1 |= TIM_CR1_CEN; + + DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM2_STOP; +} + +timeval_t current_time() { + return TIM2->CNT; +} + +void schedule_event_timer(timeval_t time) { + TIM2->DIER &= ~TIM_DIER_CC4IE; /* Disable CC4 interrupt */ + TIM2->SR = ~TIM_SR_CC4IF; /* Clear any pending CC4 interrupt */ + + TIM2->CCR4 = time; + TIM2->DIER |= TIM_DIER_CC4IE; /* Re-enable CC4 with time set */ + + /* If time is prior to now, we may have missed it, set the interrupt pending + * just in case */ + if (time_before_or_equal(time, current_time())) { + TIM2->EGR = TIM_EGR_CC4G; + } +} + +void TIM2_IRQHandler(void) { + bool cc1_fired = false; + bool cc2_fired = false; + timeval_t cc1; + timeval_t cc2; + + if (TIM2->SR & TIM_SR_CC1IF) { + cc1 = TIM2->CCR1; + cc1_fired = true; + } + + if (TIM2->SR & TIM_SR_CC1IF) { + cc2 = TIM2->CCR2; + cc2_fired = true; + } + + if (cc1_fired && cc2_fired && time_before(cc2, cc1)) { + decoder_update_scheduling(1, cc2); + decoder_update_scheduling(0, cc1); + } else { + if (cc1_fired) { + decoder_update_scheduling(0, cc1); + } + if (cc2_fired) { + decoder_update_scheduling(1, cc2); + } + } + + if (TIM2->SR & TIM_SR_CC4IF) { + TIM2->SR = ~TIM_SR_CC4IF; + scheduler_callback_timer_execute(); + } +} + +extern void abort(void); /* TODO handle this better */ + +void DMA2_Stream1_IRQHandler(void) { + if (DMA2->LISR & DMA_LISR_TCIF1) { + DMA2->LIFCR = DMA_LIFCR_CTCIF1; + stm32_buffer_swap(); + } + + /* If we overran, abort */ + uint32_t dma_current_buffer = _FLD2VAL(DMA_SxCR_CT, DMA2_Stream1->CR); + if (dma_current_buffer != stm32_current_buffer()) { + abort(); + } + + /* In the event of a transfer error, abort */ + if (DMA2->LISR & DMA_LISR_TEIF1) { + abort(); + } +} + +static void setup_scheduled_outputs(void) { + + /* While still in hi-z, set outputs that are active-low */ + for (int i = 0; i < MAX_EVENTS; ++i) { + if (config.events[i].inverted && config.events[i].type != DISABLED_EVENT) { + GPIOD->ODR |= (1 << config.events[i].pin); + } + } + + GPIOD->MODER = 0x55555555; /* All of GPIOD is output */ + GPIOD->OSPEEDR = 0xffffffff; /* All GPIOD set to High speed*/ + + /* Enable Interrupt on buffer swap at highest priority */ + NVIC_SetPriority(DMA2_Stream1_IRQn, 0); + NVIC_EnableIRQ(DMA2_Stream1_IRQn); + + /* Use DMA2 Stream 1 Channel 7 to write to GPIOD's BSRR whenever TIMER8 + * updates */ + DMA2_Stream1->PAR = (uint32_t)&GPIOD->BSRR; + DMA2_Stream1->M0AR = (uint32_t)&output_buffers[0].slots; + DMA2_Stream1->M1AR = (uint32_t)&output_buffers[1].slots; + DMA2_Stream1->NDTR = NUM_SLOTS; + + DMA2_Stream1->CR = + _VAL2FLD(DMA_SxCR_CHSEL, 7) | /* TIMER7 Update */ + DMA_SxCR_DBM | /* Switch-buffer mode */ + _VAL2FLD(DMA_SxCR_PL, 3) | /* High priority */ + _VAL2FLD(DMA_SxCR_MSIZE, 0x2) | /* 32 bit memory size */ + _VAL2FLD(DMA_SxCR_PSIZE, 0x2) | /* 32 bit peripheral size */ + DMA_SxCR_MINC | /* Memory-increment */ + _VAL2FLD(DMA_SxCR_DIR, 0x1) | /* Memory -> peripheral */ + DMA_SxCR_TCIE | /* Enable interrupt on buffer swap */ + DMA_SxCR_TEIE | /* Enable interrupt on transfer error */ + DMA_SxCR_EN; /* Enable DMA */ +} + +void stm32f4_configure_scheduler() { + setup_scheduled_outputs(); + setup_tim8(); + setup_tim2(); +} diff --git a/src/platforms/stm32f4/stm32f4_sensors.c b/src/platforms/stm32f4/stm32f4_sensors.c new file mode 100644 index 00000000..25d4477f --- /dev/null +++ b/src/platforms/stm32f4/stm32f4_sensors.c @@ -0,0 +1,162 @@ +#include "config.h" +#include "sensors.h" + +#include "stm32f427xx.h" + +#ifdef SPI_TLV2553 +#include "tlv2553_adc.h" +#define SPI_FREQ_DIVIDER 0x2 /* Prescaler = 8, 10.5 MHz */ +#define SPI_SAMPLE_RATE 150000 +#define ADC_SAMPLE_RATE 5000 +#define KNOCK_SAMPLE_RATE 50000 +#elif SPI_AD7888 +#include "ad7888_adc.h" +#define SPI_FREQ_DIVIDER 0x5 /* Prescaler 64, 1.3125 MHz */ +#define SPI_SAMPLE_RATE 70000 +#define ADC_SAMPLE_RATE 5000 +#define KNOCK_SAMPLE_RATE -1 /* No knock inputs */ +#else +#error No ADC specified! +#endif + +/* Configure TIM1 to cycle at the SPI_SAMPLE_RATE. It uses compare outputs to + * manage CS, and on update it'll trigger DMA to send a SPI word. DMA is also + * configured to receive the resulting SPI response and place it into a double + * circular buffer. After each full set of SPI commands, an interrupt is fired, + * and ADC processing is triggered */ + +static void setup_tim1(void) { + GPIOA->MODER |= _VAL2FLD(GPIO_MODER_MODE8, 2); /* Pin 8 AF */ + GPIOA->AFR[1] |= _VAL2FLD(GPIO_AFRH_AFSEL8, 1); /* AF1 */ + GPIOA->OSPEEDR |= _VAL2FLD(GPIO_OSPEEDR_OSPEED8, 1); /* 25 MHz */ + + /* TIM1 is on APB2's CK_TIMER, driven at 168 MHz (2x APB2's clock) */ + const uint32_t period = 168000000 / SPI_SAMPLE_RATE; + TIM1->ARR = period - 1; + + TIM1->DIER = TIM_DIER_UDE; + + /* Configure TIMER0's CH0 as a PWM output to act as CS. On each cycle start + low and after the duty% is reached, go high */ + TIM1->CCR1 = 0.9f * period; + TIM1->CCMR1 = _VAL2FLD(TIM_CCMR1_OC1M, 0x7); /* PWM Mode 2 */ + TIM1->CCER = TIM_CCER_CC1E; /* Enable compare output 1 */ + TIM1->BDTR = TIM_BDTR_MOE | TIM_BDTR_OSSR; /* Enable outputs */ + TIM1->CR1 = TIM_CR1_CEN; /* Start timer */ + + DBGMCU->APB2FZ |= DBGMCU_APB2_FZ_DBG_TIM1_STOP; +} + +static void setup_spi1(void) { + + /* Set pins 5, 6, 7 to AF mode */ + GPIOA->MODER |= _VAL2FLD(GPIO_MODER_MODE5, 2) | + _VAL2FLD(GPIO_MODER_MODE6, 2) | _VAL2FLD(GPIO_MODER_MODE7, 2); + /* Set pins 5, 6, 7 to AF 5, SPI1 */ + GPIOA->AFR[0] |= _VAL2FLD(GPIO_AFRL_AFSEL5, 5) | + _VAL2FLD(GPIO_AFRL_AFSEL6, 5) | + _VAL2FLD(GPIO_AFRL_AFSEL7, 5); + + /* Set pins 5, 6, 7 to Speed 1, 25 MHz max */ + GPIOA->OSPEEDR |= _VAL2FLD(GPIO_OSPEEDR_OSPEED5, 1) | + _VAL2FLD(GPIO_OSPEEDR_OSPEED6, 1) | + _VAL2FLD(GPIO_OSPEEDR_OSPEED7, 1); + + SPI1->CR1 = _VAL2FLD(SPI_CR1_DFF, 1) | /* 16 bit frame */ + SPI_CR1_MSTR | /* Master mode */ + _VAL2FLD(SPI_CR1_BR, SPI_FREQ_DIVIDER); + + SPI1->CR2 = SPI_CR2_SSOE | /* Always master */ + SPI_CR2_RXDMAEN; /* RX DMA */ + + SPI1->CR1 |= SPI_CR1_SPE; /* Start SPI */ +} + +/* Use TIM1 to trigger the DMA to write SPI1. Use circular mode to transmit + * the SPI commands. */ +void setup_spi1_tx_dma(void) { + + /* Use DMA2 Stream 5 Channel 6 (TIM1_UP) to write to SPI1_DR */ + DMA2_Stream5->PAR = (uint32_t)&SPI1->DR; + DMA2_Stream5->M0AR = (uint32_t)&adc_transmit_sequence[0]; + DMA2_Stream5->NDTR = NUM_SPI_TX; + + DMA2_Stream5->CR = + _VAL2FLD(DMA_SxCR_CHSEL, 6) | /* TIM1_UP */ + DMA_SxCR_CIRC | /* Circular mode */ + _VAL2FLD(DMA_SxCR_PL, 3) | /* High priority */ + _VAL2FLD(DMA_SxCR_MSIZE, 0x1) | /* 16 bit memory size */ + _VAL2FLD(DMA_SxCR_PSIZE, 0x1) | /* 16 bit peripheral size */ + DMA_SxCR_MINC | /* Memory-increment */ + _VAL2FLD(DMA_SxCR_DIR, 0x1) | /* Memory -> peripheral */ + DMA_SxCR_EN; /* Enable DMA */ +} + +static volatile uint16_t __attribute__((aligned(4))) +__attribute__((section(".dmadata"))) spi_rx_buffer[2][NUM_SPI_TX] = { 0 }; + +/* DMA is driven by SPI's RX and transfers the SPI result into a double buffer + * of results. The interrupt is also used for the ADC calculations */ +void setup_spi1_rx_dma(void) { + + /* Enable interrupt for dma completion */ + NVIC_SetPriority(DMA2_Stream0_IRQn, 3); + NVIC_EnableIRQ(DMA2_Stream0_IRQn); + + /* Use DMA2 Stream 0 Channel 3 (SPI1_RX) to read SPI_DR into the receive + * buffer */ + DMA2_Stream0->PAR = (uint32_t)&SPI1->DR; + DMA2_Stream0->M0AR = (uint32_t)&spi_rx_buffer[0][0]; + DMA2_Stream0->M1AR = (uint32_t)&spi_rx_buffer[1][0]; + DMA2_Stream0->NDTR = NUM_SPI_TX; + + DMA2_Stream0->CR = + _VAL2FLD(DMA_SxCR_CHSEL, 3) | /* SPI1_RX */ + DMA_SxCR_DBM | /* double buffer mode */ + _VAL2FLD(DMA_SxCR_PL, 2) | /* medium priority */ + _VAL2FLD(DMA_SxCR_MSIZE, 0x1) | /* 16 bit memory size */ + _VAL2FLD(DMA_SxCR_PSIZE, 0x1) | /* 16 bit peripheral size */ + DMA_SxCR_MINC | /* Memory-increment */ + _VAL2FLD(DMA_SxCR_DIR, 0x0) | /* peripheral -> memory*/ + DMA_SxCR_TCIE | /* completion interrupt enabled */ + DMA_SxCR_EN; /* Enable DMA */ +} + +void DMA2_Stream0_IRQHandler(void) { + if (DMA2->LISR & DMA_LISR_TCIF0) { + DMA2->LIFCR = DMA_LIFCR_CTCIF0; + uint32_t dma_current_buffer = _FLD2VAL(DMA_SxCR_CT, DMA2_Stream0->CR); + const uint16_t *sequence = (dma_current_buffer) + ? (const uint16_t *)spi_rx_buffer[1] + : (const uint16_t *)spi_rx_buffer[0]; + + bool fault = !adc_response_is_valid(sequence); + for (int i = 0; i < NUM_SENSORS; ++i) { + if (config.sensors[i].source == SENSOR_ADC) { + config.sensors[i].fault = fault ? FAULT_CONN : FAULT_NONE; + config.sensors[i].raw_value = + read_adc_pin(sequence, config.sensors[i].pin); + } + } + sensors_process(SENSOR_ADC); + process_knock_inputs(sequence); + } +} + +uint32_t platform_adc_samplerate(void) { + return ADC_SAMPLE_RATE; +} + +uint32_t platform_knock_samplerate(void) { + return KNOCK_SAMPLE_RATE; +} + +void stm32f4_configure_adc(void) { + setup_spi1(); + setup_spi1_tx_dma(); + setup_spi1_rx_dma(); + setup_tim1(); + + knock_configure(&config.knock_inputs[0]); + knock_configure(&config.knock_inputs[1]); +} diff --git a/src/platforms/stm32f4/stm32f4_usb.c b/src/platforms/stm32f4/stm32f4_usb.c new file mode 100644 index 00000000..7fdbc4af --- /dev/null +++ b/src/platforms/stm32f4/stm32f4_usb.c @@ -0,0 +1,339 @@ +#include + +#include "platform.h" +#include "stm32f4xx.h" +#include "usb.h" +#include "usb_cdc.h" + +#define CDC_EP0_SIZE 0x08 +#define CDC_RXD_EP 0x01 +#define CDC_TXD_EP 0x81 +#define CDC_DATA_SZ 0x40 +#define CDC_NTF_EP 0x82 +#define CDC_NTF_SZ 0x08 + +struct cdc_config { + struct usb_config_descriptor config; + struct usb_iad_descriptor comm_iad; + struct usb_interface_descriptor comm; + struct usb_cdc_header_desc cdc_hdr; + struct usb_cdc_call_mgmt_desc cdc_mgmt; + struct usb_cdc_acm_desc cdc_acm; + struct usb_cdc_union_desc cdc_union; + struct usb_endpoint_descriptor comm_ep; + struct usb_interface_descriptor data; + struct usb_endpoint_descriptor data_eprx; + struct usb_endpoint_descriptor data_eptx; +} __attribute__((packed)); + +/* Device descriptor */ +static const struct usb_device_descriptor device_desc = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_CLASS_IAD, + .bDeviceSubClass = USB_SUBCLASS_IAD, + .bDeviceProtocol = USB_PROTO_IAD, + .bMaxPacketSize0 = CDC_EP0_SIZE, + .idVendor = 0x1209, + .idProduct = 0x2041, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = INTSERIALNO_DESCRIPTOR, + .bNumConfigurations = 1, +}; + +/* Device configuration descriptor */ +static const struct cdc_config config_desc = { + .config = { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct cdc_config), + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .comm_iad = { + .bLength = sizeof(struct usb_iad_descriptor), + .bDescriptorType = USB_DTYPE_INTERFASEASSOC, + .bFirstInterface = 0, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_CDC, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_PROTO_NONE, + .iFunction = NO_DESCRIPTOR, + }, + .comm = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = NO_DESCRIPTOR, + }, + .cdc_hdr = { + .bFunctionLength = sizeof(struct usb_cdc_header_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_HEADER, + .bcdCDC = VERSION_BCD(1,1,0), + }, + .cdc_mgmt = { + .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, + .bmCapabilities = 0, + .bDataInterface = 1, + + }, + .cdc_acm = { + .bFunctionLength = sizeof(struct usb_cdc_acm_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_ACM, + .bmCapabilities = 0, + }, + .cdc_union = { + .bFunctionLength = sizeof(struct usb_cdc_union_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_UNION, + .bMasterInterface0 = 0, + .bSlaveInterface0 = 1, + }, + .comm_ep = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CDC_NTF_EP, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = CDC_NTF_SZ, + .bInterval = 0xFF, + }, + .data = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = USB_SUBCLASS_NONE, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = NO_DESCRIPTOR, + }, + .data_eprx = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CDC_RXD_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = CDC_DATA_SZ, + .bInterval = 0x01, + }, + .data_eptx = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CDC_TXD_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = CDC_DATA_SZ, + .bInterval = 0x01, + }, +}; + +const char *manu_str = "https://github.com/via/viaems/"; +const char *prod_str = "ViaEMS console"; + +usbd_device udev; +uint32_t ubuf[0x20]; + +#define USB_RX_BUF_LEN 1024 +static char usb_rx_buf[USB_RX_BUF_LEN]; +static _Atomic size_t usb_rx_len = 0; + +static struct usb_cdc_line_coding cdc_line = { + .dwDTERate = 115200, + .bCharFormat = USB_CDC_1_STOP_BITS, + .bParityType = USB_CDC_NO_PARITY, + .bDataBits = 8, +}; + +struct stringdesc { + uint8_t length; + uint8_t type; + uint16_t data[128]; +} __attribute__((packed)); +static struct stringdesc stringdesc; + +static void populate_string_descriptor(struct stringdesc *dest, + const char *str) { + size_t len = strlen(str); + dest->length = len * 2 + 2; + dest->type = 0x03; + + for (size_t pos = 0; pos < len; pos++) { + stringdesc.data[pos] = str[pos]; + } +} + +static usbd_respond cdc_getdesc(usbd_ctlreq *req, + void **address, + uint16_t *length) { + const uint8_t dtype = req->wValue >> 8; + const uint8_t dnumber = req->wValue & 0xFF; + const void *desc; + uint16_t len = 0; + switch (dtype) { + case USB_DTYPE_DEVICE: + desc = &device_desc; + break; + case USB_DTYPE_CONFIGURATION: + desc = &config_desc; + len = sizeof(config_desc); + break; + case USB_DTYPE_STRING: + switch (dnumber) { + case 0: + stringdesc = + (struct stringdesc){ .length = 4, .type = 0x03, .data = { 0x0409 } }; + break; + case 1: + populate_string_descriptor(&stringdesc, manu_str); + break; + case 2: + populate_string_descriptor(&stringdesc, prod_str); + break; + default: + return usbd_fail; + } + desc = &stringdesc; + break; + default: + return usbd_fail; + } + if (len == 0) { + len = ((struct usb_header_descriptor *)desc)->bLength; + } + *address = (void *)desc; + *length = len; + return usbd_ack; +} + +static usbd_respond cdc_control(usbd_device *dev, + usbd_ctlreq *req, + usbd_rqc_callback *callback) { + (void)callback; + if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + req->wIndex == 0) { + switch (req->bRequest) { + case USB_CDC_SET_CONTROL_LINE_STATE: + return usbd_ack; + case USB_CDC_SET_LINE_CODING: + memcpy(&cdc_line, req->data, sizeof(cdc_line)); + return usbd_ack; + case USB_CDC_GET_LINE_CODING: + dev->status.data_ptr = &cdc_line; + dev->status.data_count = sizeof(cdc_line); + return usbd_ack; + default: + return usbd_fail; + } + } + return usbd_fail; +} + +static void cdc_rx(usbd_device *dev, uint8_t event, uint8_t ep) { + (void)event; + if (ep != CDC_RXD_EP) { + return; + } + char buf[64]; + int ret = usbd_ep_read(dev, CDC_RXD_EP, buf, CDC_DATA_SZ); + if (ret < 0) { + return; + } + + if (USB_RX_BUF_LEN - usb_rx_len < (unsigned)ret) { + /* No space, just drop the packet */ + } else { + memcpy(usb_rx_buf + usb_rx_len, buf, ret); + usb_rx_len += ret; + } +} + +static usbd_respond cdc_setconf(usbd_device *dev, uint8_t cfg) { + switch (cfg) { + case 0: + /* deconfiguring device */ + usbd_ep_deconfig(dev, CDC_NTF_EP); + usbd_ep_deconfig(dev, CDC_TXD_EP); + usbd_ep_deconfig(dev, CDC_RXD_EP); + usbd_reg_endpoint(dev, CDC_RXD_EP, 0); + return usbd_ack; + case 1: + /* configuring device */ + usbd_ep_config( + dev, CDC_RXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ); + usbd_ep_config( + dev, CDC_TXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ); + usbd_ep_config(dev, CDC_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ); + usbd_reg_endpoint(dev, CDC_RXD_EP, cdc_rx); + usbd_ep_write(dev, CDC_TXD_EP, 0, 0); + return usbd_ack; + default: + return usbd_fail; + } +} + +static void cdc_init_usbd(void) { + usbd_init(&udev, &usbd_hw, CDC_EP0_SIZE, ubuf, sizeof(ubuf)); + usbd_reg_config(&udev, cdc_setconf); + usbd_reg_control(&udev, cdc_control); + usbd_reg_descr(&udev, cdc_getdesc); +} + +void OTG_FS_IRQHandler(void) { + usbd_poll(&udev); +} + +size_t console_read(void *ptr, size_t max) { + disable_interrupts(); + size_t amt = usb_rx_len > max ? max : usb_rx_len; + memcpy(ptr, usb_rx_buf, amt); + memmove(usb_rx_buf, usb_rx_buf + amt, usb_rx_len - amt); + usb_rx_len -= amt; + enable_interrupts(); + return amt; +} + +size_t console_write(const void *ptr, size_t max) { + int amt = max; + if (amt > CDC_DATA_SZ) { + amt = CDC_DATA_SZ; + } + + disable_interrupts(); + int written = usbd_ep_write(&udev, CDC_TXD_EP, (void *)ptr, amt); + enable_interrupts(); + if (written < 0) { + return 0; + } + return written; +} + +void stm32f4_configure_usb(void) { + GPIOA->MODER &= ~(GPIO_MODER_MODE11 | GPIO_MODER_MODE12); + GPIOA->MODER |= _VAL2FLD(GPIO_MODER_MODE11, 2) | + _VAL2FLD(GPIO_MODER_MODE12, 2); /* A11/A12 in AF mode */ + GPIOA->OSPEEDR |= + GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12; /* High speed */ + GPIOA->AFR[1] |= _VAL2FLD(GPIO_AFRH_AFSEL11, 10) | + _VAL2FLD(GPIO_AFRH_AFSEL12, 10); /* AF10 (USB FS)*/ + + cdc_init_usbd(); + NVIC_SetPriority(OTG_FS_IRQn, 15); + NVIC_EnableIRQ(OTG_FS_IRQn); + usbd_enable(&udev, true); + usbd_connect(&udev, true); +} diff --git a/src/platforms/stm32f4/stm32f4_vectors.s b/src/platforms/stm32f4/stm32f4_vectors.s new file mode 100644 index 00000000..5fafca7e --- /dev/null +++ b/src/platforms/stm32f4/stm32f4_vectors.s @@ -0,0 +1,460 @@ +/** + ****************************************************************************** + * @file startup_stm32f427xx.s + * @author MCD Application Team + * @brief STM32F427xx Devices vector table for GCC based toolchains. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M4 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + + .syntax unified + .cpu cortex-m4 + .fpu softvfp + .thumb + + + .section .text.Default_Handler,"ax",%progbits + .global Default_Handler +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler + +//.global g_pfnVectors +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word WWDG_IRQHandler /* Window WatchDog */ + .word PVD_IRQHandler /* PVD through EXTI Line detection */ + .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ + .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ + .word FLASH_IRQHandler /* FLASH */ + .word RCC_IRQHandler /* RCC */ + .word EXTI0_IRQHandler /* EXTI Line0 */ + .word EXTI1_IRQHandler /* EXTI Line1 */ + .word EXTI2_IRQHandler /* EXTI Line2 */ + .word EXTI3_IRQHandler /* EXTI Line3 */ + .word EXTI4_IRQHandler /* EXTI Line4 */ + .word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ + .word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ + .word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ + .word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ + .word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ + .word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ + .word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ + .word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ + .word CAN1_TX_IRQHandler /* CAN1 TX */ + .word CAN1_RX0_IRQHandler /* CAN1 RX0 */ + .word CAN1_RX1_IRQHandler /* CAN1 RX1 */ + .word CAN1_SCE_IRQHandler /* CAN1 SCE */ + .word EXTI9_5_IRQHandler /* External Line[9:5]s */ + .word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ + .word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ + .word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ + .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ + .word TIM2_IRQHandler /* TIM2 */ + .word TIM3_IRQHandler /* TIM3 */ + .word TIM4_IRQHandler /* TIM4 */ + .word I2C1_EV_IRQHandler /* I2C1 Event */ + .word I2C1_ER_IRQHandler /* I2C1 Error */ + .word I2C2_EV_IRQHandler /* I2C2 Event */ + .word I2C2_ER_IRQHandler /* I2C2 Error */ + .word SPI1_IRQHandler /* SPI1 */ + .word SPI2_IRQHandler /* SPI2 */ + .word USART1_IRQHandler /* USART1 */ + .word USART2_IRQHandler /* USART2 */ + .word USART3_IRQHandler /* USART3 */ + .word EXTI15_10_IRQHandler /* External Line[15:10]s */ + .word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ + .word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ + .word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ + .word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ + .word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ + .word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ + .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ + .word FMC_IRQHandler /* FMC */ + .word SDIO_IRQHandler /* SDIO */ + .word TIM5_IRQHandler /* TIM5 */ + .word SPI3_IRQHandler /* SPI3 */ + .word UART4_IRQHandler /* UART4 */ + .word UART5_IRQHandler /* UART5 */ + .word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ + .word TIM7_IRQHandler /* TIM7 */ + .word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ + .word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ + .word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ + .word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ + .word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ + .word ETH_IRQHandler /* Ethernet */ + .word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */ + .word CAN2_TX_IRQHandler /* CAN2 TX */ + .word CAN2_RX0_IRQHandler /* CAN2 RX0 */ + .word CAN2_RX1_IRQHandler /* CAN2 RX1 */ + .word CAN2_SCE_IRQHandler /* CAN2 SCE */ + .word OTG_FS_IRQHandler /* USB OTG FS */ + .word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ + .word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ + .word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ + .word USART6_IRQHandler /* USART6 */ + .word I2C3_EV_IRQHandler /* I2C3 event */ + .word I2C3_ER_IRQHandler /* I2C3 error */ + .word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ + .word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ + .word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ + .word OTG_HS_IRQHandler /* USB OTG HS */ + .word DCMI_IRQHandler /* DCMI */ + .word 0 /* Reserved */ + .word HASH_RNG_IRQHandler /* Hash and Rng */ + .word FPU_IRQHandler /* FPU */ + .word UART7_IRQHandler /* UART7 */ + .word UART8_IRQHandler /* UART8 */ + .word SPI4_IRQHandler /* SPI4 */ + .word SPI5_IRQHandler /* SPI5 */ + .word SPI6_IRQHandler /* SPI6 */ + .word SAI1_IRQHandler /* SAI1 */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word DMA2D_IRQHandler /* DMA2D */ +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler +// .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Stream0_IRQHandler + .thumb_set DMA1_Stream0_IRQHandler,Default_Handler + + .weak DMA1_Stream1_IRQHandler + .thumb_set DMA1_Stream1_IRQHandler,Default_Handler + + .weak DMA1_Stream2_IRQHandler + .thumb_set DMA1_Stream2_IRQHandler,Default_Handler + + .weak DMA1_Stream3_IRQHandler + .thumb_set DMA1_Stream3_IRQHandler,Default_Handler + + .weak DMA1_Stream4_IRQHandler + .thumb_set DMA1_Stream4_IRQHandler,Default_Handler + + .weak DMA1_Stream5_IRQHandler + .thumb_set DMA1_Stream5_IRQHandler,Default_Handler + + .weak DMA1_Stream6_IRQHandler + .thumb_set DMA1_Stream6_IRQHandler,Default_Handler + + .weak ADC_IRQHandler + .thumb_set ADC_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_IRQHandler,Default_Handler + + .weak FMC_IRQHandler + .thumb_set FMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Stream0_IRQHandler + .thumb_set DMA2_Stream0_IRQHandler,Default_Handler + + .weak DMA2_Stream1_IRQHandler + .thumb_set DMA2_Stream1_IRQHandler,Default_Handler + + .weak DMA2_Stream2_IRQHandler + .thumb_set DMA2_Stream2_IRQHandler,Default_Handler + + .weak DMA2_Stream3_IRQHandler + .thumb_set DMA2_Stream3_IRQHandler,Default_Handler + + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler,Default_Handler + + .weak DMA2_Stream5_IRQHandler + .thumb_set DMA2_Stream5_IRQHandler,Default_Handler + + .weak DMA2_Stream6_IRQHandler + .thumb_set DMA2_Stream6_IRQHandler,Default_Handler + + .weak DMA2_Stream7_IRQHandler + .thumb_set DMA2_Stream7_IRQHandler,Default_Handler + + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_OUT_IRQHandler + .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_IN_IRQHandler + .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler + + .weak OTG_HS_WKUP_IRQHandler + .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler + + .weak OTG_HS_IRQHandler + .thumb_set OTG_HS_IRQHandler,Default_Handler + + .weak DCMI_IRQHandler + .thumb_set DCMI_IRQHandler,Default_Handler + + .weak HASH_RNG_IRQHandler + .thumb_set HASH_RNG_IRQHandler,Default_Handler + + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + + .weak UART7_IRQHandler + .thumb_set UART7_IRQHandler,Default_Handler + + .weak UART8_IRQHandler + .thumb_set UART8_IRQHandler,Default_Handler + + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + + .weak SPI5_IRQHandler + .thumb_set SPI5_IRQHandler,Default_Handler + + .weak SPI6_IRQHandler + .thumb_set SPI6_IRQHandler,Default_Handler + + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler + + .weak DMA2D_IRQHandler + .thumb_set DMA2D_IRQHandler,Default_Handler diff --git a/targets/stm32f4.mk b/targets/stm32f4.mk index d7ae5012..b8324fde 100644 --- a/targets/stm32f4.mk +++ b/targets/stm32f4.mk @@ -1,22 +1,44 @@ CC=arm-none-eabi-gcc +AS=arm-none-eabi-as AR=arm-none-eabi-ar LD=arm-none-eabi-ld OBJCOPY=arm-none-eabi-objcopy -SPI_ADC?=AD7888 +SPI_ADC?=TLV2553 + +CMSIS=contrib/CMSIS_5/ +CMSISDEV=contrib/cmsis_device_f4/ +LIBUSB=contrib/libusb_stm32 + +VPATH=src/platforms/${PLATFORM} \ + contrib/libusb_stm32/src + +LIBUSB_OBJS= usbd_core.o \ + usbd_stm32f429_otgfs.o + +OBJS+= stm32f4.o \ + stm32f4_sched.o \ + stm32f4_vectors.o \ + stm32f4_usb.o \ + stm32f4_sensors.o \ + stm32f4_pwm.o \ + stm32_sched_buffers.o \ + ${LIBUSB_OBJS} -CM3_LIB=libopencm3_stm32f4.a -OBJS+= ${CM3_LIB} -OBJS+= stm32f4-discovery.o stm32_sched_buffers.o OBJS+= libssp.a libssp_nonshared.a -CFLAGS+= -D TICKRATE=4000000 -DNDEBUG -ffunction-sections -fdata-sections -CFLAGS+= -I${OPENCM3_DIR}/include -DSTM32F4 -O3 +CFLAGS= -DNDEBUG -ffunction-sections -fdata-sections -O3 CFLAGS+= -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mthumb -mcpu=cortex-m4 -CFLAGS+= -DSPI_${SPI_ADC} +CFLAGS+= -DSTM32F4 -DSTM32F4xx -DSTM32F427xx +CFLAGS+= -DPLATFORMIO -DUSBD_SOF_DISABLED + +CFLAGS+= -I${CMSIS}/CMSIS/Core/Include +CFLAGS+= -I${CMSISDEV}/Include +CFLAGS+= -I${LIBUSB}/inc +CFLAGS+= -DTICKRATE=4000000 -DSPI_${SPI_ADC} -LDFLAGS+= -lc -lnosys -L ${OBJDIR} -l:${CM3_LIB} -Wl,--gc-sections -LDFLAGS+= -T src/platforms/stm32f4-discovery.ld -nostartfiles +LDFLAGS+= -lc -lnosys -L ${OBJDIR} -nostartfiles -Wl,--gc-sections +LDFLAGS+= -T src/platforms/stm32f4/stm32f4.ld ${OBJDIR}/libssp.a: ${AR} rcs ${OBJDIR}/libssp.a @@ -24,12 +46,10 @@ ${OBJDIR}/libssp.a: ${OBJDIR}/libssp_nonshared.a: ${AR} rcs ${OBJDIR}/libssp_nonshared.a -${OBJDIR}/${CM3_LIB}: - $(MAKE) -C ${OPENCM3_DIR} ${MAKEFLAGS} - cp ${OPENCM3_DIR}/lib/${CM3_LIB} ${OBJDIR} - ${OBJDIR}/viaems.dfu: ${OBJDIR}/viaems - ${OBJCOPY} -O binary ${OBJDIR}/viaems ${OBJDIR}/viaems.dfu + ${OBJCOPY} -O ihex --remove-section=.configdata ${OBJDIR}/viaems ${OBJDIR}/viaems.hex + ${OBJCOPY} -O ihex --only-section=.configdata ${OBJDIR}/viaems ${OBJDIR}/viaems-config.hex + scripts/dfuse-pack.py -i ${OBJDIR}/viaems.hex -i ${OBJDIR}/viaems-config.hex ${OBJDIR}/viaems.dfu program: ${OBJDIR}/viaems.dfu - dfu-util -D ${OBJDIR}/viaems.dfu -a 0 -s 0x8000000:leave + dfu-util -D ${OBJDIR}/viaems.dfu -s :leave