diff --git a/CMakeLists.txt b/CMakeLists.txt index ce1f2a5..ba28846 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ add_target(HGLRC1 STM32F051 DEAD_TIME=26 COMP_MAP=123 SENS_MAP=0xA6 VOLT_MUL=210 add_target(IFLIGHT1 STM32F051 DEAD_TIME=26 COMP_MAP=321 SENS_MAP=0xA3A6 VOLT_MUL=110 CURR_MUL=20 LED_MAP=0xB8B5B3 LED_INV) add_target(IFLIGHT2 STM32G071 DEAD_TIME=35 COMP_MAP=213 SENS_MAP=0xA5A4 VOLT_MUL=110 CURR_MUL=20 LED_WS2812 IO_PA6) add_target(IFLIGHT3 STM32G071 DEAD_TIME=35 COMP_MAP=132 SENS_MAP=0xA0 VOLT_MUL=110 LED_WS2812) +add_target(LUMENIER1 GD32F350 DEAD_TIME=57 COMP_MAP=321 SENS_MAP=0xA3 VOLT_MUL=110) add_target(NEUTRONRC1 STM32G071 DEAD_TIME=35 COMP_MAP=123 SENS_MAP=0xA6A4 VOLT_MUL=210 CURR_MUL=50 LED_WS2812) add_target(NEUTRONRC2 AT32F421 DEAD_TIME=66 COMP_MAP=321) add_target(SKYSTARS1 GD32E230 DEAD_TIME=40 COMP_MAP=321 SENS_MAP=0xA3 VOLT_MUL=110 LED_MAP=0xB5B3A15) diff --git a/README.md b/README.md index aff1b8a..e472005 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Features + Analog/serial/iBUS/SBUS input mode + KISS/iBUS/S.Port telemetry + DSHOT 3D mode, turtle mode, beacon, LED, programming -+ Sine startup mode (crawler mode) ++ Sine startup mode (crawler mode), brushed mode + Proportional brake, drag brake + Temperature/voltage/current protection + Variable PWM frequency, active freewheeling diff --git a/boot/src/io.c b/boot/src/io.c index 544c7fe..9d0e8fd 100644 --- a/boot/src/io.c +++ b/boot/src/io.c @@ -35,13 +35,13 @@ void initio(void) { USART2_CR3 = USART_CR3_HDSEL; USART2_CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; #else + TIM3_SMCR = TIM_SMCR_SMS_RM | TIM_SMCR_TS_TI1F_ED; // Reset on any edge on TI1 + TIM3_CCMR1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC1M_PWM2 | TIM_CCMR1_CC2S_IN_TI1 | TIM_CCMR1_IC2F_CK_INT_N_8; + TIM3_CCER = TIM_CCER_CC2E | TIM_CCER_CC2P; // IC2 on falling edge on TI1 TIM3_ARR = CLK_CNT(38400) - 1; // Bit time TIM3_CCR1 = CLK_CNT(76800); // Half-bit time TIM3_EGR = TIM_EGR_UG; TIM3_CR1 = TIM_CR1_CEN; - TIM3_SMCR = TIM_SMCR_SMS_RM | TIM_SMCR_TS_TI1F_ED; // Reset on any edge on TI1 - TIM3_CCMR1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC1M_PWM2 | TIM_CCMR1_CC2S_IN_TI1 | TIM_CCMR1_IC2F_CK_INT_N_8; - TIM3_CCER = TIM_CCER_CC2E | TIM_CCER_CC2P; // IC2 on falling edge on TI1 #endif TIM14_PSC = CLK / 10000 - 1; // 0.1ms resolution TIM14_ARR = 4999; // 500ms I/O timeout diff --git a/mcu/GD32F350/config.c b/mcu/GD32F350/config.c new file mode 100644 index 0000000..90599a6 --- /dev/null +++ b/mcu/GD32F350/config.c @@ -0,0 +1,194 @@ +/* +** Copyright (C) 2022-2023 Arseny Vakhrushev +** +** This firmware 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 firmware 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 firmware. If not, see . +*/ + +#include +#include +#include "common.h" + +#define ADC1_BASE ADC_BASE +#define COMP_CSR MMIO32(SYSCFG_COMP_BASE + 0x1c) + +static char len, ain; +static uint16_t buf[5]; + +void init(void) { + RCC_CFGR &= ~RCC_CFGR_SW_PLL; + while (RCC_CFGR & RCC_CFGR_SWS_PLL); + RCC_CR &= ~RCC_CR_PLLON; + RCC_CFGR = 0x8240000; // PLLMUL=11001 (x26) + RCC_CR |= RCC_CR_PLLON; + while (!(RCC_CR & RCC_CR_PLLRDY)); + RCC_CFGR |= RCC_CFGR_SW_PLL; + + RCC_APB2RSTR = -1; + RCC_APB1RSTR = -1; + RCC_APB2RSTR = 0; + RCC_APB1RSTR = 0; + RCC_AHBENR = RCC_AHBENR_DMAEN | RCC_AHBENR_SRAMEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; + RCC_APB2ENR = RCC_APB2ENR_SYSCFGCOMPEN | RCC_APB2ENR_ADCEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_TIM17EN; + RCC_APB1ENR = RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM14EN | RCC_APB1ENR_WWDGEN; + SCB_VTOR = (uint32_t)_rom; // Set vector table address + + // Default GPIO state - analog input + GPIOA_AFRL = 0x20000000; // A7 (TIM1_CH1N) + GPIOA_AFRH = 0x00000222; // A8 (TIM1_CH1), A9 (TIM1_CH2), A10 (TIM1_CH3) + GPIOB_AFRL = 0x00000022; // B0 (TIM1_CH2N), B1 (TIM1_CH3N) + GPIOB_AFRH = 0x00000000; + GPIOA_PUPDR = 0x24000000; + GPIOB_PUPDR = 0x00001000; // B6 (pull-up) + GPIOA_MODER = 0xebeabfff; // A7 (TIM1_CH1N), A8 (TIM1_CH1), A9 (TIM1_CH2), A10 (TIM1_CH3) + GPIOB_MODER = 0xffffeffa; // B0 (TIM1_CH2N), B1 (TIM1_CH3N), B6 (USART1_TX) +#ifndef ANALOG +#ifdef IO_PA2 + RCC_APB2ENR |= RCC_APB2ENR_TIM15EN; + GPIOA_PUPDR |= 0x10; // A2 (pull-up) + GPIOA_MODER &= ~0x10; // A2 (TIM15_CH1) +#else + RCC_APB1ENR |= RCC_APB1ENR_TIM3EN; + GPIOB_AFRL |= 0x10000; // B4 (TIM3_CH1) + GPIOB_PUPDR |= 0x100; // B4 (pull-up) + GPIOB_MODER &= ~0x100; // B4 (TIM3_CH1) +#endif +#endif + + nvic_set_priority(NVIC_TIM3_IRQ, 0x40); + nvic_set_priority(NVIC_TIM15_IRQ, 0x40); + nvic_set_priority(NVIC_USART1_IRQ, 0x80); + nvic_set_priority(NVIC_USART2_IRQ, 0x40); + nvic_set_priority(NVIC_DMA1_CHANNEL1_IRQ, 0x80); // ADC + nvic_set_priority(NVIC_DMA1_CHANNEL2_3_DMA2_CHANNEL1_2_IRQ, 0x80); // USART1 + nvic_set_priority(NVIC_DMA1_CHANNEL4_7_DMA2_CHANNEL3_5_IRQ, 0x40); // TIM3 or TIM15 or USART2 + + nvic_enable_irq(NVIC_TIM1_BRK_UP_TRG_COM_IRQ); + nvic_enable_irq(NVIC_TIM1_CC_IRQ); + nvic_enable_irq(NVIC_TIM2_IRQ); + nvic_enable_irq(NVIC_TIM3_IRQ); + nvic_enable_irq(NVIC_TIM15_IRQ); + nvic_enable_irq(NVIC_USART1_IRQ); + nvic_enable_irq(NVIC_USART2_IRQ); + nvic_enable_irq(NVIC_DMA1_CHANNEL1_IRQ); + nvic_enable_irq(NVIC_DMA1_CHANNEL2_3_DMA2_CHANNEL1_2_IRQ); + nvic_enable_irq(NVIC_DMA1_CHANNEL4_7_DMA2_CHANNEL3_5_IRQ); + + RCC_CR2 |= RCC_CR2_HSI14ON; // Enable IRC28M + while (!(RCC_CR2 & RCC_CR2_HSI14RDY)); + ADC1_CR2 = ADC_CR2_ADON | ADC_CR2_TSVREFE; + TIM17_ARR = CLK_MHZ - 1; + TIM17_CR1 = TIM_CR1_CEN | TIM_CR1_OPM; + while (TIM17_CR1 & TIM_CR1_CEN); // Wait for 1us (RM 11.4.1) + ADC1_CR2 |= ADC_CR2_CAL; + while (ADC1_CR2 & ADC_CR2_CAL); + ADC1_CR1 = ADC_CR1_SCAN; + ADC1_SMPR1 = -1; // Sampling time ~17us @ IRC28M/2=14Mhz + ADC1_SMPR2 = -1; + ADC1_SQR3 = SENS_CHAN; + len = SENS_CNT; + if (IO_ANALOG) { + ADC1_SQR3 |= THROT_CHAN << (len++ * 5); + ain = 1; + } + ADC1_SQR3 |= 0x230 << (len * 5); // CH16 (temp), CH17 (vref) + len += 2; + ADC1_SQR1 = (len - 1) << ADC_SQR1_L_LSB; + DMA1_CPAR(1) = (uint32_t)&ADC1_DR; + DMA1_CMAR(1) = (uint32_t)buf; + + TIM1_DIER = TIM_DIER_UIE | TIM_DIER_CC1IE | TIM_DIER_CC4IE; // Software comparator blanking, ADC trigger + TIM1_SMCR = TIM_SMCR_TS_ITR1; // TRGI=TIM2 + TIM2_CR2 = TIM_CR2_MMS_COMPARE_OC1REF; // TRGO=OC1REF + TIM2_CCMR1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC1M_PWM2; // Inverted PWM on OC1 + TIM2_CCMR2 = TIM_CCMR2_CC4S_IN_TI4 | TIM_CCMR2_IC4F_DTF_DIV_16_N_8; + TIM2_CCER = TIM_CCER_CC4E; // IC4 on rising edge on TI4 (COMP1_OUT) +} + +void compctl(int x) { + int cr = 0; + switch (x & 3) { + case COMP_IN1: + cr = 0x61; // A1>A0 + break; + case COMP_IN2: + cr = 0x41; // A1>A4 + break; + case COMP_IN3: + cr = 0x51; // A1>A5 + break; + } + if (x & 4) cr |= 0x800; // Change polarity + COMP_CSR = cr; +} + +void io_serial(void) { + TIM15_DIER = 0; + nvic_clear_pending_irq(NVIC_TIM15_IRQ); + RCC_APB2ENR &= ~RCC_APB2ENR_TIM15EN; + RCC_APB1ENR |= RCC_APB1ENR_USART2EN; + GPIOA_AFRL |= 0x100; // A2 (USART2_TX) +} + +void io_analog(void) { + TIM15_DIER = 0; + nvic_clear_pending_irq(NVIC_TIM15_IRQ); + RCC_APB2ENR &= ~RCC_APB2ENR_TIM15EN; + GPIOA_PUPDR &= ~0x30; // A2 (no pull-up/pull-down) + GPIOA_MODER |= 0x30; // A2 (analog) +} + +void adc_trig(void) { + if (DMA1_CCR(1) & DMA_CCR_EN) return; + DMA1_CNDTR(1) = len; + DMA1_CCR(1) = DMA_CCR_EN | DMA_CCR_TCIE | DMA_CCR_MINC | DMA_CCR_PSIZE_16BIT | DMA_CCR_MSIZE_16BIT; + ADC1_CR2 = ADC_CR2_ADON | ADC_CR2_TSVREFE | ADC_CR2_DMA | ADC_CR2_EXTTRIG | ADC_CR2_EXTSEL_SWSTART; +} + +void tim1_brk_up_trg_com_isr(void) { + int sr = TIM1_SR; + if (sr & TIM_SR_UIF) { + TIM1_SR = ~TIM_SR_UIF; + if (TIM1_CCR4) COMP_CSR &= ~0x700; // COMP_OUT off + } + if (sr & TIM_SR_COMIF) tim1_com_isr(); +} + +void tim1_cc_isr(void) { + int sr = TIM1_SR; + if (sr & TIM_SR_CC4IF) { + TIM1_SR = ~TIM_SR_CC4IF; + COMP_CSR |= 0x400; // COMP1_OUT=TIM2_IC4 + } + if (!(sr & TIM_SR_CC1IF)) return; + TIM1_SR = ~TIM_SR_CC1IF; + if (!(ADC1_CR2 & ADC_CR2_DMA)) return; + ADC1_CR2 = ADC_CR2_ADON | ADC_CR2_TSVREFE | ADC_CR2_DMA | ADC_CR2_EXTTRIG | ADC_CR2_EXTSEL_SWSTART | ADC_CR2_SWSTART; +} + +void dma1_channel1_isr(void) { + DMA1_IFCR = DMA_IFCR_CTCIF(1); + DMA1_CCR(1) = 0; + ADC1_CR2 = ADC_CR2_ADON | ADC_CR2_TSVREFE; + int i = 0, v = 0, c = 0, x = 0; +#if SENS_CNT == 2 + c = buf[i++]; +#endif +#if SENS_CNT >= 1 + v = buf[i++]; +#endif + if (ain) x = buf[i++]; + int r = 4914000 / buf[i + 1]; + int t = (1450 - (buf[i] * r >> 12)) * 10 / 43 + 25; + adc_data(t, v * r >> 12, c * r >> 12, x); +} diff --git a/mcu/GD32F350/config.cmake b/mcu/GD32F350/config.cmake new file mode 100644 index 0000000..76365b9 --- /dev/null +++ b/mcu/GD32F350/config.cmake @@ -0,0 +1,3 @@ +set(opts -mcpu=cortex-m4 -mthumb) +set(libs opencm3_stm32f0) +set(defs STM32F0) diff --git a/mcu/GD32F350/config.h b/mcu/GD32F350/config.h new file mode 100644 index 0000000..4c60c75 --- /dev/null +++ b/mcu/GD32F350/config.h @@ -0,0 +1,59 @@ +/* +** Copyright (C) 2022-2023 Arseny Vakhrushev +** +** This firmware 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 firmware 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 firmware. If not, see . +*/ + +#pragma once + +#if SENS_MAP == 0xA3 // A3 (volt) +#define SENS_CNT 1 +#define SENS_CHAN 0x3 +#endif + +#ifndef THROT_CHAN +#define THROT_CHAN 2 // A2 +#endif + +#define CLK 104000000 + +#define IFTIM TIM2 +#define IFTIM_ICF 128 +#define IFTIM_ICE TIM_DIER_CC4IE +#define IFTIM_ICR TIM2_CCR4 +#define IFTIM_OCR TIM2_CCR1 +#define iftim_isr tim2_isr + +#ifdef IO_PA2 +#define IO_TYPE 0 +#define IOTIM TIM15 +#define IOTIM_IDR (GPIOA_IDR & 0x4) // A2 +#define IOTIM_DMA 5 +#define iotim_isr tim15_isr +#else +#define IO_TYPE 2 +#define IOTIM TIM3 +#define IOTIM_IDR (GPIOB_IDR & 0x10) // B4 +#define IOTIM_DMA 4 +#define iotim_isr tim3_isr +#endif +#define iodma_isr dma1_channel4_7_dma2_channel3_5_isr + +#define USART1_TX_DMA 2 +#define USART1_RX_DMA 3 +#define usart1_dma_isr dma1_channel2_3_dma2_channel1_2_isr + +#define USART2_RX_DMA 5 + +void tim1_com_isr(void); diff --git a/mcu/GD32F350/config.ld b/mcu/GD32F350/config.ld new file mode 100644 index 0000000..b6e9392 --- /dev/null +++ b/mcu/GD32F350/config.ld @@ -0,0 +1,6 @@ +MEMORY { + boot (rx) : ORIGIN = 0x8000000, LENGTH = 4K + cfg (rx) : ORIGIN = ORIGIN(boot) + LENGTH(boot), LENGTH = 1K + rom (rx) : ORIGIN = ORIGIN(cfg) + LENGTH(cfg), LENGTH = 32K - LENGTH(boot) - LENGTH(cfg) + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 8K +} diff --git a/mcu/GD32F350/preset.c b/mcu/GD32F350/preset.c new file mode 100644 index 0000000..32a3d5e --- /dev/null +++ b/mcu/GD32F350/preset.c @@ -0,0 +1,33 @@ +/* +** Copyright (C) 2022-2023 Arseny Vakhrushev +** +** This firmware 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 firmware 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 firmware. If not, see . +*/ + +#include "common.h" + +const uint16_t sinedata[] = { + 346, 352, 358, 364, 370, 376, 382, 388, 394, 400, 406, 412, 418, 424, 430, 436, 441, 447, 453, 459, 464, 470, 476, 481, 487, 492, 498, 503, 508, 514, + 519, 524, 529, 534, 539, 544, 549, 554, 559, 564, 568, 573, 578, 582, 586, 591, 595, 599, 603, 607, 611, 615, 619, 622, 626, 629, 633, 636, 639, 643, + 646, 649, 651, 654, 657, 660, 662, 664, 667, 669, 671, 673, 675, 677, 679, 680, 682, 683, 684, 686, 687, 688, 689, 689, 690, 691, 691, 692, 692, 692, + 692, 692, 692, 692, 691, 691, 690, 689, 689, 688, 687, 686, 684, 683, 682, 680, 679, 677, 675, 673, 671, 669, 667, 664, 662, 660, 657, 654, 651, 649, + 646, 643, 639, 636, 633, 629, 626, 622, 619, 615, 611, 607, 603, 599, 595, 591, 586, 582, 578, 573, 568, 564, 559, 554, 549, 544, 539, 534, 529, 524, + 519, 514, 508, 503, 498, 492, 487, 481, 476, 470, 464, 459, 453, 447, 441, 436, 430, 424, 418, 412, 406, 400, 394, 388, 382, 376, 370, 364, 358, 352, + 346, 340, 334, 328, 322, 316, 310, 304, 298, 292, 286, 280, 274, 268, 262, 256, 251, 245, 239, 233, 228, 222, 216, 211, 205, 200, 194, 189, 184, 178, + 173, 168, 163, 158, 153, 148, 143, 138, 133, 128, 124, 119, 114, 110, 106, 101, 97, 93, 89, 85, 81, 77, 73, 70, 66, 63, 59, 56, 53, 49, + 46, 43, 41, 38, 35, 32, 30, 28, 25, 23, 21, 19, 17, 15, 13, 12, 10, 9, 8, 6, 5, 4, 3, 3, 2, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 6, 8, 9, 10, 12, 13, 15, 17, 19, 21, 23, 25, 28, 30, 32, 35, 38, 41, 43, + 46, 49, 53, 56, 59, 63, 66, 70, 73, 77, 81, 85, 89, 93, 97, 101, 106, 110, 114, 119, 124, 128, 133, 138, 143, 148, 153, 158, 163, 168, + 173, 178, 184, 189, 194, 200, 205, 211, 216, 222, 228, 233, 239, 245, 251, 256, 262, 268, 274, 280, 286, 292, 298, 304, 310, 316, 322, 328, 334, 340, +}; diff --git a/src/cli.c b/src/cli.c index f7c17e4..3859d80 100644 --- a/src/cli.c +++ b/src/cli.c @@ -49,6 +49,7 @@ XX(28, val, volume) \ XX(29, val, beacon) \ XX(30, val, led) \ + XX(31, val, brushed) \ static int beep = -1; diff --git a/src/common.h b/src/common.h index fee9dbc..77eafdc 100644 --- a/src/common.h +++ b/src/common.h @@ -86,6 +86,7 @@ typedef struct { char volume; char beacon; char led; + char brushed; } Cfg; typedef struct { diff --git a/src/defs.h b/src/defs.h index 9a8fffe..b475da1 100644 --- a/src/defs.h +++ b/src/defs.h @@ -168,7 +168,7 @@ #define DUTY_RAMP 25 #endif #ifndef DUTY_DRAG -#define DUTY_DRAG 0 +#define DUTY_DRAG 75 #endif #ifndef THROT_MODE #define THROT_MODE 0 @@ -228,3 +228,6 @@ #ifndef LED #define LED 0 #endif +#ifndef BRUSHED +#define BRUSHED 0 +#endif diff --git a/src/io.c b/src/io.c index b366864..a6015c4 100644 --- a/src/io.c +++ b/src/io.c @@ -242,12 +242,10 @@ static void dshotdma(void) { static int cmd, cnt, rep; if (DMA1_CCR(IOTIM_DMA) & DMA_CCR_DIR) { #ifdef AT32F421 // Errata 1.5.1 - int psc = TIM15_PSC; RCC_APB2RSTR = RCC_APB2RSTR_TIM15RST; RCC_APB2RSTR = 0; TIM15_BDTR = TIM_BDTR_MOE; TIM15_CR1 = TIM_CR1_CEN | TIM_CR1_ARPE; - TIM15_PSC = psc; #else TIM_CCER(IOTIM) = 0; TIM_DIER(IOTIM) = 0; @@ -285,6 +283,7 @@ static void dshotdma(void) { TIM_SMCR(IOTIM) = 0; TIM_CCMR1(IOTIM) = 0; // Disable OC before enabling PWM to force OC1REF update (RM: OC1M, note #2) TIM_CCMR1(IOTIM) = TIM_CCMR1_OC1PE | TIM_CCMR1_OC1M_PWM2; + TIM_CCER(IOTIM) = TIM_CCER_CC1E; // Enable output as soon as possible (GD32F350 makes a twitch) TIM_CR2(IOTIM) = TIM_CR2_CCDS; // CC1 DMA request on UEV using the same DMA channel DMA1_CCR(IOTIM_DMA) = 0; DMA1_CNDTR(IOTIM_DMA) = 23; @@ -313,7 +312,6 @@ static void dshotdma(void) { TIM_CCR1(IOTIM) = 0; // Preload high level TIM_EGR(IOTIM) = TIM_EGR_UG; // Update registers and trigger DMA to preload the first bit TIM_ARR(IOTIM) = dshotarr2; // Preload bit time - TIM_CCER(IOTIM) = TIM_CCER_CC1E; // Enable output __enable_irq(); if (!rep || !--rep) dshotval = 0; } @@ -411,15 +409,17 @@ static void dshotdma(void) { cfg.led &= ~8; break; #endif - case 40: // Increase timing + case 40: // Select timing if (cnt != 6) break; - if ((x = cfg.timing) < 7) ++x; + if ((x = cfg.timing + 1) > 7) x = 1; beepval = cfg.timing = x; break; - case 41: // Decrease timing + case 41: // Select PWM frequency if (cnt != 6) break; - if ((x = cfg.timing) > 1) --x; - beepval = cfg.timing = x; + if ((x = (cfg.freq_min >> 2) + 1) > 12 || x < 6) x = 6; + cfg.freq_min = x << 2; + cfg.freq_max = x << 3; + beepval = x - 5; break; case 42: // Increase acceleration ramping if (cnt != 6) break; @@ -563,6 +563,10 @@ static void cliirq(void) { } if (USART2_ISR & USART_ISR_FE || i == sizeof iobuf - 1) reset(); // Data error char b = USART2_RDR; // Clear RXNE + if (b == '\b' || b == 0x7f) { // Backspace + if (i > 0) --i; + return; + } iobuf[i++] = b; if (i == 2 && iobuf[0] == 0x00 && iobuf[1] == 0xff) scb_reset_system(); // Reboot into bootloader if (b != '\n') return; @@ -618,6 +622,10 @@ static void cliirq(void) { TIM3_SR = ~TIM_SR_CC2IF; TIM3_DIER = TIM_DIER_CC2IE; n = 0; + if (b == '\b' || b == 0x7f) { // Backspace + if (i > 0) --i; + break; + } iobuf[i++] = b; if (i == 2 && iobuf[0] == 0x00 && iobuf[1] == 0xff) scb_reset_system(); // Reboot into bootloader if (b != '\n') break; diff --git a/src/main.c b/src/main.c index ad45cc5..10545d4 100644 --- a/src/main.c +++ b/src/main.c @@ -17,7 +17,7 @@ #include "common.h" -#define REVISION 5 +#define REVISION 6 const Cfg cfgdata = { .id = 0x32ea, @@ -55,6 +55,7 @@ const Cfg cfgdata = { .volume = VOLUME, // Sound volume (%) [0..100] .beacon = BEACON, // Beacon volume (%) [0..100] .led = LED, // LED bits + .brushed = BRUSHED, // Brushed mode }; __attribute__((__section__(".cfg"))) @@ -73,7 +74,7 @@ static char prep, accl, tick, reverse, ready; TIM1_CCMR1 = TIM_CCMR1_OC1M_FORCE_LOW | TIM_CCMR1_OC2M_FORCE_LOW; \ TIM1_CCMR2 = TIM_CCMR2_OC3M_FORCE_LOW; \ TIM1_EGR = TIM_EGR_COMG; \ - for (;;) WWDG_CR = 0xff; \ + for (;;); \ } #endif @@ -89,6 +90,32 @@ static char prep, accl, tick, reverse, ready; */ static void nextstep(void) { + if (cfg.brushed) { + int m1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; + int m2 = TIM_CCMR2_OC3PE; +#ifdef INVERTED_HIGH + int er = TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P; +#else + int er = 0; +#endif + if (reverse) { + m1 |= TIM_CCMR1_OC1M_FORCE_HIGH | TIM_CCMR1_OC2M_PWM1; + m2 |= TIM_CCMR2_OC3M_FORCE_HIGH; + er |= TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC3NE; + if (cfg.damp) er |= TIM_CCER_CC2NE; + } else { + m1 |= TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC2M_FORCE_HIGH; + m2 |= TIM_CCMR2_OC3M_PWM1; + er |= TIM_CCER_CC1E | TIM_CCER_CC2NE | TIM_CCER_CC3E; + if (cfg.damp) er |= TIM_CCER_CC1NE | TIM_CCER_CC3NE; + } + TIM1_CCMR1 = m1; + TIM1_CCMR2 = m2; + TIM1_CCER = er; + step = 1; + sync = 6; + return; + } #ifndef SENSORED if (sine) { // Sine startup IFTIM_OCR = sine; @@ -110,8 +137,8 @@ static void nextstep(void) { TIM1_CCR3 = DEAD_TIME + (sinedata[c] * cfg.sine_power >> 4); TIM1_CR1 = TIM_CR1_CEN | TIM_CR1_ARPE; if (prep) return; - TIM1_CCMR1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE | TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC2M_PWM1; - TIM1_CCMR2 = TIM_CCMR2_OC3PE | TIM_CCMR2_OC4PE | TIM_CCMR2_OC3M_PWM1; + TIM1_CCMR1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC2PE | TIM_CCMR1_OC2M_PWM1; + TIM1_CCMR2 = TIM_CCMR2_OC3PE | TIM_CCMR2_OC3M_PWM1; #ifdef INVERTED_HIGH TIM1_CCER = TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE | TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P; #else @@ -232,12 +259,19 @@ static void nextstep(void) { } static void laststep(void) { - TIM1_CCMR1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE | TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC2M_PWM1; - TIM1_CCMR2 = TIM_CCMR2_OC3PE | TIM_CCMR2_OC4PE | TIM_CCMR2_OC3M_PWM1; + TIM1_CCMR1 = TIM_CCMR1_OC1M_FORCE_LOW | TIM_CCMR1_OC2M_FORCE_LOW; + TIM1_CCMR2 = TIM_CCMR2_OC3M_FORCE_LOW; + TIM1_EGR = TIM_EGR_COMG; + TIM1_CCMR1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC2PE | TIM_CCMR1_OC2M_PWM1; + TIM1_CCMR2 = TIM_CCMR2_OC3PE | TIM_CCMR2_OC3M_PWM1; #ifdef INVERTED_HIGH TIM1_CCER = TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE | TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P; #else TIM1_CCER = TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE; +#endif + TIM1_EGR = TIM_EGR_UG | TIM_EGR_COMG; +#ifndef SENSORED + compctl(0); #endif } @@ -397,10 +431,7 @@ void main(void) { int cells = cfg.prot_cells; #if SENS_CNT > 0 - while (!ready) { // Wait for sensors - WWDG_CR = 0xff; - __WFI(); - } + while (!ready) __WFI(); // Wait for sensors if (!cells) cells = (volt + 439) / 440; // Assume maximum 4.4V per battery cell #endif #ifndef ANALOG @@ -418,7 +449,6 @@ void main(void) { TIM14_CR1 = TIM_CR1_CEN | TIM_CR1_URS; throt = 1; while (!(TIM14_SR & TIM_SR_UIF)) { // Wait for 250ms zero throttle - WWDG_CR = 0xff; __WFI(); beep(); if (!throt) continue; @@ -430,11 +460,9 @@ void main(void) { } #endif laststep(); - TIM1_EGR = TIM_EGR_COMG; PID curpid = {.Kp = 400, .Ki = 0, .Kd = 600}; for (int curduty = 0, running = 0, braking = 2, choke = 0, r = 0, v = 0;;) { SCB_SCR = SCB_SCR_SLEEPONEXIT; // Suspend main loop - WWDG_CR = 0xff; __WFI(); int input = throt; int range = cfg.sine_range * 20; @@ -505,7 +533,7 @@ void main(void) { if (++r == 8) r = 0; if (curduty >= newduty || (curduty += b) > newduty) curduty = newduty; // Acceleration ramping } - int ccr = scale(curduty, 0, 2000, running && cfg.damp ? DEAD_TIME : 0, arr); + int ccr = scale(curduty, 0, 2000, running && cfg.damp ? DEAD_TIME : 0, cfg.brushed ? arr - (CLK_MHZ * 3 >> 1) : arr); TIM1_CR1 = TIM_CR1_CEN | TIM_CR1_ARPE | TIM_CR1_UDIS; TIM1_ARR = arr; TIM1_CCR1 = ccr; @@ -532,15 +560,11 @@ void main(void) { __enable_irq(); } else if (!running && step) { // Stop motor __disable_irq(); - laststep(); - TIM1_EGR = TIM_EGR_UG | TIM_EGR_COMG; TIM1_DIER &= ~TIM_DIER_COMIE; TIM_ARR(IFTIM) = 0; TIM_EGR(IFTIM) = TIM_EGR_UG; TIM_DIER(IFTIM) = 0; -#ifndef SENSORED - compctl(0); -#endif + laststep(); step = 0; sine = 0; prep = 0; diff --git a/src/util.c b/src/util.c index 3865e9e..c7157cc 100644 --- a/src/util.c +++ b/src/util.c @@ -63,7 +63,7 @@ void checkcfg(void) { cfg.sine_power = 0; #else cfg.timing = clamp(cfg.timing, 1, 7); - cfg.sine_range = cfg.damp && cfg.sine_range ? clamp(cfg.sine_range, 5, 25) : 0; + cfg.sine_range = cfg.damp && cfg.sine_range && !cfg.brushed ? clamp(cfg.sine_range, 5, 25) : 0; cfg.sine_power = clamp(cfg.sine_power, 1, 15); #endif cfg.freq_min = clamp(cfg.freq_min, 16, 48); @@ -110,6 +110,7 @@ void checkcfg(void) { #else cfg.led = 0; #endif + cfg.brushed = !!cfg.brushed; #ifndef ANALOG if (!IO_ANALOG) return; cfg.arm = 1; // Ensure low level on startup @@ -120,8 +121,6 @@ void checkcfg(void) { int savecfg(void) { if (ertm) return 0; __disable_irq(); - WWDG_CFR = ~WWDG_CFR_EWI; // Slow down watchdog - WWDG_CR = 0xff; FLASH_KEYR = FLASH_KEYR_KEY1; FLASH_KEYR = FLASH_KEYR_KEY2; FLASH_SR = -1; // Clear errors @@ -132,7 +131,7 @@ int savecfg(void) { FLASH_AR = (uint32_t)_cfg; FLASH_CR = FLASH_CR_PER | FLASH_CR_STRT; // Erase page #endif - while (FLASH_SR & FLASH_SR_BSY) WWDG_CR = 0xff; + while (FLASH_SR & FLASH_SR_BSY); FLASH_CR = FLASH_CR_PG; #ifdef STM32G0 #define T uint32_t @@ -148,10 +147,9 @@ int savecfg(void) { #ifdef STM32G0 *dst++ = *src++; #endif - while (FLASH_SR & FLASH_SR_BSY) WWDG_CR = 0xff; + while (FLASH_SR & FLASH_SR_BSY); } FLASH_CR = FLASH_CR_LOCK; - WWDG_CFR = 0x7f; // Resume watchdog __enable_irq(); #ifdef STM32G0 if (FLASH_SR & (FLASH_SR_PROGERR | FLASH_SR_WRPERR)) return 0; @@ -174,12 +172,12 @@ int playmusic(const char *str, int vol) { if (!vol || ertm || flag) return 0; flag = 1; vol <<= 1; - TIM1_CCMR1 = TIM_CCMR1_OC1M_FORCE_HIGH | TIM_CCMR1_OC2M_FORCE_LOW; - TIM1_CCMR2 = TIM_CCMR2_OC3PE | TIM_CCMR2_OC3M_PWM1; + TIM1_CCMR1 = TIM_CCMR1_OC1M_FORCE_HIGH | TIM_CCMR1_OC2PE | TIM_CCMR1_OC2M_PWM1; + TIM1_CCMR2 = TIM_CCMR2_OC3M_FORCE_HIGH; #ifdef INVERTED_HIGH - TIM1_CCER = TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE | TIM_CCER_CC3E | TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P; + TIM1_CCER = TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE | TIM_CCER_CC2E | TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P; #else - TIM1_CCER = TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE | TIM_CCER_CC3E; + TIM1_CCER = TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE | TIM_CCER_CC2E; #endif TIM1_PSC = CLK_MHZ / 8 - 1; // 8MHz for (int a, b; (a = *str++);) { @@ -191,7 +189,7 @@ int playmusic(const char *str, int vol) { if (a > 4) --a; if (*str == '#') ++a, ++str; TIM1_ARR = arr[a] >> b; // Frequency - TIM1_CCR3 = vol; // Volume + TIM1_CCR2 = vol; // Volume set: TIM1_EGR = TIM_EGR_UG | TIM_EGR_COMG; a = *str; @@ -200,9 +198,8 @@ int playmusic(const char *str, int vol) { for (uint32_t t = tickms + a * 125; t != tickms;) { // Duration 125*X ms if (TIM14_CR1 & TIM_CR1_CEN) TIM14_EGR = TIM_EGR_UG; // Reset arming timeout IWDG_KR = IWDG_KR_RESET; - WWDG_CR = 0xff; } - TIM1_CCR3 = 0; // Preload silence + TIM1_CCR2 = 0; // Preload silence } TIM1_CCMR1 = TIM_CCMR1_OC1M_FORCE_LOW | TIM_CCMR1_OC2M_FORCE_LOW; TIM1_CCMR2 = TIM_CCMR2_OC3M_FORCE_LOW;