From ecc478661f6010afc87001d3d5fc50646c03b478 Mon Sep 17 00:00:00 2001 From: true Date: Thu, 19 Oct 2023 16:20:41 -0700 Subject: [PATCH] WIP code ADC is sort of functioning, but not reliably. LEDs are sort of working. Buzzer isn't working right and is nearly always on as I can't set the PF2 mode. --- boards/generic_py32f030x6.json | 4 +- include/adc.h | 14 ++- include/flash.h | 20 ++++ include/led.h | 7 ++ include/testo.h | 6 ++ include/userio.h | 12 +++ lib/py32f0xx-ll/src/py32f0xx_ll_flash.c | 2 +- platformio.ini | 41 ++++----- py32f030x6.ld | 2 + pyocd_user.py | 13 +-- src/adc.c | 117 +++++++++++++++++------- src/flash.c | 75 +++++++++++++++ src/led.c | 15 +-- src/main.c | 48 +++++++--- src/userio.c | 26 ++++-- 15 files changed, 308 insertions(+), 94 deletions(-) create mode 100644 include/flash.h create mode 100644 include/userio.h create mode 100644 src/flash.c diff --git a/boards/generic_py32f030x6.json b/boards/generic_py32f030x6.json index 28d4456..4a0141b 100644 --- a/boards/generic_py32f030x6.json +++ b/boards/generic_py32f030x6.json @@ -1,7 +1,7 @@ { "build": { "cpu": "cortex-m0plus", - "extra_flags": "-DPY32F003x6", + "extra_flags": "-DPY32F030x6", "f_cpu": "8000000L", "mcu": "py32f030x6", "product_line": "PY32F030x6" @@ -21,7 +21,7 @@ "cmsis-dap" ] }, - "name": "Generic PY32F002/003/030 (configured as 32K PY32F030x6)", + "name": "Generic 16/32K PY32F002/003/030 (configured as 32K PY32F030x6)", "url": "https://github.com/trueserve", "vendor": "trueControl" } \ No newline at end of file diff --git a/include/adc.h b/include/adc.h index 2253999..f238d5d 100644 --- a/include/adc.h +++ b/include/adc.h @@ -5,6 +5,18 @@ * file creation: 20231016 0102 */ +#ifndef _INC_ADC_H +#define _INC_ADC_H + + +#include + + void adc_init(); -void adc_next(); \ No newline at end of file + +uint8_t adc_next(); + + + +#endif /* _INC_ADC_H */ \ No newline at end of file diff --git a/include/flash.h b/include/flash.h new file mode 100644 index 0000000..de81eda --- /dev/null +++ b/include/flash.h @@ -0,0 +1,20 @@ +/* + * flash.h: flashing user config and system option bytes + * + * file creation: 20231019 0129 + */ + +#ifndef _INC_FLASH_H +#define _INC_FLASH_H + + + +void flash_optr_checkfix(); + +void flash_optr_set(); + +void flash_init(); + + + +#endif /* _INC_FLASH_H */ \ No newline at end of file diff --git a/include/led.h b/include/led.h index e96c45f..9002b4e 100644 --- a/include/led.h +++ b/include/led.h @@ -5,6 +5,9 @@ * file creation: 20231015 0059 */ +#ifndef _INC_LED_H +#define _INC_LED_H + #include @@ -18,3 +21,7 @@ void led_mode_buzzer(); void led_setrgb(uint8_t i, uint16_t r, uint16_t g, uint16_t b); void led_buzz(uint8_t buzz); + + + +#endif /* _INC_LED_H */ \ No newline at end of file diff --git a/include/testo.h b/include/testo.h index 36c7f21..08ec726 100644 --- a/include/testo.h +++ b/include/testo.h @@ -4,6 +4,9 @@ * file creation: 20231015 0021 */ +#ifndef _INC_TESTO_H +#define _INC_TESTO_H + #include "py32f0xx_conf.h" @@ -78,3 +81,6 @@ #define ADC_VREF_EXT 0 #define ADC_THERM 4 + + +#endif /* _INC_TESTO_H */ \ No newline at end of file diff --git a/include/userio.h b/include/userio.h new file mode 100644 index 0000000..67d147f --- /dev/null +++ b/include/userio.h @@ -0,0 +1,12 @@ +/* + * userio.h: reacting when you fiddle with buttons, switches, and knobs + * + * file creation: 20231016 1505 + */ + + +#include + + + +void userio_parse(); diff --git a/lib/py32f0xx-ll/src/py32f0xx_ll_flash.c b/lib/py32f0xx-ll/src/py32f0xx_ll_flash.c index c3df089..4eaa3d0 100644 --- a/lib/py32f0xx-ll/src/py32f0xx_ll_flash.c +++ b/lib/py32f0xx-ll/src/py32f0xx_ll_flash.c @@ -871,7 +871,7 @@ ErrorStatus LL_FLASH_OBProgram(FLASH_OBProgramInitTypeDef *pOBInit) FLASH->CR|=FLASH_CR_OPTSTRT; /* set bit EOPIE */ - FLASH->CR|=FLASH_CR_EOPIE; + // FLASH->CR|=FLASH_CR_EOPIE; /* trigger program */ *((__IO uint32_t *)(0x40022080))=0xff; diff --git a/platformio.ini b/platformio.ini index 4f78b3d..aa315fe 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ [env] platform = nxplpc ; not actually this and we are not using a framework -build_type = debug ; setting platform = nxplpc makes everything happy + ; setting platform = nxplpc makes everything happy ; as we'll get nano specs and any other needed shit board = generic_py32f030x6 @@ -28,13 +28,17 @@ debug_extra_cmds = upload_protocol = custom upload_command = $PYTHONEXE ${platformio.packages_dir}/tool-pyocd/pyocd-flashtool.py --config $PROJECT_DIR/pyocd.yaml -t PY32F030x6 $SOURCE -build_flags = - -DUSE_FULL_LL_DRIVER - -L. - -Os - -std=gnu11 +platform_packages = + toolchain-gccarmnoneeabi@1.100301.220327 ; build issues with GCC12, use GCC10 for now + tool-pyocd ; user won't have to install it -debug_build_flags = + +[env:sc7-testobot-py32f-dbg_pyocd] +build_type = debug +board_build.ldscript = py32f030x6.ld + +debug_build_flags = + -Wl,-Map=.pio/output_dbg.map -DUSE_FULL_LL_DRIVER -L. -Os @@ -42,26 +46,15 @@ debug_build_flags = -Wl,-Map=.pio/output_dbg.map -g -platform_packages = - toolchain-gccarmnoneeabi@1.100301.220327 ; build issues with GCC12, use GCC10 for now - tool-pyocd ; user won't have to install it - - -[env:sc7-testobot-py32f-dbg_pyocd] -board_build.ldscript = py32f030x6.ld - -debug_build_flags = - -Wl,-Map=.pio/output_dbg.map - [env:sc7-testobot-py32f-rel_bl] +build_type = release board_build.ldscript = py32f030x6_bl.ld +board_upload.maximum_size = 30720 ; 2K reserved for bootloader build_flags = -Wl,-Map=.pio/output_rel.map - - - - - - + -DUSE_FULL_LL_DRIVER + -L. + -Os + -std=gnu11 diff --git a/py32f030x6.ld b/py32f030x6.ld index 28dd48a..08f87cc 100644 --- a/py32f030x6.ld +++ b/py32f030x6.ld @@ -31,6 +31,8 @@ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K + PERIPHERAL (rw): ORIGIN = 0x40000000, LENGTH = 512M + PPB (rw) : ORIGIN = 0xe0000000, LENGTH = 512M } /* Define output sections */ diff --git a/pyocd_user.py b/pyocd_user.py index 3b501dc..5d99db6 100644 --- a/pyocd_user.py +++ b/pyocd_user.py @@ -3,12 +3,13 @@ def will_connect(board): print("notice: adding peripheral memory map..."); - target.memory_map.add_region(DeviceRegion( - name="Peripheral", - start=0x40000000, - length=0x20000000, - access='rw' - )) +# target.memory_map.add_region(DeviceRegion( +# name="Peripheral", +# start=0x40000000, +# length=0x20000000, +# access='rw' +# )) + target.memory_map.add_region(DeviceRegion( name="PPB", start=0xE0000000, diff --git a/src/adc.c b/src/adc.c index 19b19cb..d60aedd 100644 --- a/src/adc.c +++ b/src/adc.c @@ -9,9 +9,9 @@ * voltage - only the analog supply can be the reference. * * because most inputs are high impedance, which is not ideal, - * sampling time is increased to the maximum. this results in - * a final ADC sample rate of ~33KHz, still well beyond - * what is needed in this application. + * sampling time is increased. this results in a final ADC sample rate + * of about 33KHz, still well beyond what is needed in this application. + * this value may change in the future. * * file creation: 20231015 0121 */ @@ -28,13 +28,16 @@ #define ADC_CHANNELS 6 #define ADC_HISTLEN 16 +#define CONF_CALIBRATE (1 << 0) +#define CONF_USEPROBE (1 << 1) + static uint16_t adc_read[ADC_CHANNELS]; static uint16_t adc_hist[ADC_CHANNELS][ADC_HISTLEN]; static uint16_t adc_avg[ADC_CHANNELS]; -static uint8_t adc_idx = ADC_HISTLEN; +static uint8_t adc_idx = ADC_HISTLEN + 1; static int16_t vref; static uint8_t calibrate = 0; @@ -52,39 +55,97 @@ void adc_init() // enable VREFINT and temperature sensor ADC->CCR = ADC_CCR_TSEN | ADC_CCR_VREFEN; - // configure scan channels, sampling time - ADC1->CHSELR = CONF_SET1_AN | PROBE_AN | - CONF_MODE_AN | CONF_SET0_AN | + // configure scan channels, sampling time (11 = temp, 12 = vrefint) + ADC1->CHSELR = CONF_SET1_AN | PROBE_AN | // note: SET1 and PROBE are + CONF_MODE_AN | CONF_SET0_AN | // shared and reflect one chan ADC_CHSELR_CHSEL11 | ADC_CHSELR_CHSEL12; ADC1->SMPR = SAMPLE_TIME; - // by default, DMA selection for all channels is ADC + // run in non-circular DMA mode, with wait mode enabled + ADC1->CFGR1 = ADC_CFGR1_OVRMOD | ADC_CFGR1_WAIT | LL_ADC_REG_DMA_TRANSFER_LIMITED; + + // by default, DMA selection for all channels is ADC // so all we need to do is set the peripheral address - DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR; + DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR; + DMA1_Channel1->CMAR = (uint32_t)adc_read; } -void adc_next() +/* + * internal function for stopping ADC conversion in progress. + */ +void adc_stop() +{ + if (ADC1->CR & ADC_CR_ADSTART) { + ADC1->CR |= ADC_CR_ADSTP; + while (ADC1->CR & ADC_CR_ADSTP); + } +} + +/* + * internal function for setting up the ADC for a read. + */ +void adc_go() +{ + // stop if conversion in progress (it shouldn't ever be) + adc_stop(); + + // configure and enable DMA + DMA1_Channel1->CCR = 0; + DMA1_Channel1->CNDTR = ADC_CHANNELS; + DMA1_Channel1->CCR = LL_DMA_PRIORITY_HIGH | + LL_DMA_MDATAALIGN_HALFWORD | + LL_DMA_PDATAALIGN_HALFWORD | + LL_DMA_MEMORY_INCREMENT | + LL_DMA_MODE_NORMAL | + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | + DMA_CCR_EN; + + // enable and start ADC + ADC1->ISR = 0x1e; // clear all interrupt flags (per DS; mistranslated) + ADC1->CR = ADC_CR_ADEN; + ADC1->CR |= ADC_CR_ADSTART; +} + +/* + * when switching AN0 to/from SET1 and VREFEXT, we need to clear + * old history stored for the first analog input as well as reset the + * history index to start over. finally we need to trigger the ADC + * to get fresh data. + */ +void adc_switch0() +{ + uint8_t i; + + adc_read[0] = 0; + adc_avg[0] = 0; + for (i = 0; i < ADC_HISTLEN; i++) { + adc_hist[0][i] = 0; + } + + adc_idx = 0; + + // force ADC read to get fresh data + adc_go(); +} + +uint8_t adc_next() { uint8_t i, j; int16_t w; - // start ADC calibration + // stop if conversion in progress (it shouldn't ever be) + adc_stop(); + + // start ADC calibration if (calibrate) { calibrate = 0; - // stop if conversion in progress (it shouldn't ever be) - if (ADC1->CR & ADC_CR_ADSTART) { - ADC1->CR |= ADC_CR_ADSTP; - while (ADC1->CR & ADC_CR_ADSTP); - } - // disable ADC and start calibration ADC1->CR &= ~(ADC_CR_ADEN); ADC1->CR |= ADC_CR_ADCAL; } // copy data to history from last read - adc_idx++; if (adc_idx > ADC_HISTLEN) { // nothing to copy when first starting adc_idx = 0; @@ -122,21 +183,9 @@ void adc_next() } // wait for calibration to complete, if started - while (ADC1->CR & ADC_CR_ADCAL); + while (ADC1->CR & ADC_CR_ADCAL) {}; - // configure and enable DMA - DMA1_Channel1->CNDTR = ADC_CHANNELS; - DMA1_Channel1->CMAR = (uint32_t)adc_read; - DMA1_Channel1->CCR = LL_DMA_PRIORITY_MEDIUM | - LL_DMA_MDATAALIGN_HALFWORD | - LL_DMA_PDATAALIGN_HALFWORD | - LL_DMA_MEMORY_INCREMENT | - LL_DMA_MODE_CIRCULAR | - LL_DMA_DIRECTION_PERIPH_TO_MEMORY | - DMA_CCR_EN; - - // enable and start ADC - ADC1->ISR = 0x8e; // clear all interrupt flags (per DS; mistranslated) - ADC1->CR = ADC_CR_ADEN; - ADC1->CR |= ADC_CR_ADSTART; + adc_go(); + + return adc_idx; } \ No newline at end of file diff --git a/src/flash.c b/src/flash.c new file mode 100644 index 0000000..67ac6e4 --- /dev/null +++ b/src/flash.c @@ -0,0 +1,75 @@ +/* + * flash.c: flashing user config and system option bytes + * + * by default, the pin used for PF2 is configured as a reset. + * this code will check that the no-reset bit is set. + * if it is not, this code will update the option bytes to + * set the BOR voltage, enable BOR, and enable the no-reset bit. + * + * file creation: 20231019 0126 + */ + + +#include "py32f0xx_conf.h" + + + +/* + * checks the flash option byte to determine if the reset pin + * is configured to be PF2. if not, sets this bit as well as + * the BOR values. if the bits were updated, they will be reloaded. + * + * only call this function during startup, before interrupts + * are enabled. + * + * note: this code caused me to lose MCU. consider it buggy. + */ +void flash_optr_checkfix() +{ + FLASH_OBProgramInitTypeDef opt; + + if ((FLASH->OPTR & FLASH_OPTR_NRST_MODE) == OB_RESET_MODE_RESET) { + // let's update this + opt.OptionType = OPTIONBYTE_RDP | OPTIONBYTE_USER; + opt.USERType = 0xff00; + opt.USERConfig = OB_BOR_ENABLE | OB_BOR_LEVEL_2p3_2p4 | OB_IWDG_SW | OB_WWDG_SW | OB_RESET_MODE_GPIO | OB_BOOT1_SYSTEM | OB_RDP_LEVEL_0; + + if (LL_FLASH_Unlock() != SUCCESS) return; + if (LL_FLASH_OB_Unlock() != SUCCESS) return; + + LL_FLASH_OBProgram(&opt); + + LL_FLASH_OB_Lock(); + LL_FLASH_Lock(); + + // reload newly programmed option bytes + LL_FLASH_OB_Launch(); + } +} + +/* + * manually load values into flash option byte, to configure + * the BOR and configure the reset pin to be PF2. + */ +void flash_optr_set() +{ + FLASH->OPTR &= (FLASH_OPTR_BOR_EN_Msk | FLASH_OPTR_BOR_LEV_Msk | FLASH_OPTR_NRST_MODE_Msk); + FLASH->OPTR |= OB_BOR_ENABLE | OB_BOR_LEVEL_2p3_2p4 | OB_RESET_MODE_GPIO; +} + +void flash_init() +{ + // load flash write timings for 8MHz operation + // (these are listed in the datasheet) + FLASH->TS0 = 0x3c; + FLASH->TS2P = 0x3c; + FLASH->TS3 = 0x3c; + + FLASH->TS1 = 0x90; + FLASH->TPS3 = 0x240; + + FLASH->PERTPE = 0x5dc0; + FLASH->SMERTPE = 0x5dc0; + FLASH->PRGTPE = 0x1f40; + FLASH->PRETPE = 0x640; +} \ No newline at end of file diff --git a/src/led.c b/src/led.c index bcdcb45..590811b 100644 --- a/src/led.c +++ b/src/led.c @@ -39,14 +39,14 @@ void led_next() led_sel &= 1; // clear outputs - RGBSEL0_PORT->BRR = (1 << RGBSEL0_PIN); - RGBSEL1_PORT->BRR = (1 << RGBSEL1_PIN); + RGBSEL0_PORT->BSRR = (1 << RGBSEL0_PIN); + RGBSEL1_PORT->BSRR = (1 << RGBSEL1_PIN); if (!led_sel || (led_mode == MODE_RGB)) { // set PWMs in RGB mode, or when right LED set PWM_RGB_R = rgb[led_sel][RED]; - PWM_RGB_R = rgb[led_sel][GRN]; - PWM_RGB_R = rgb[led_sel][BLU]; + PWM_RGB_G = rgb[led_sel][GRN]; + PWM_RGB_B = rgb[led_sel][BLU]; } else { // clear PWMs in piezo mode when piezo should be active PWM_RGB_R = PWM_RGB_G = PWM_RGB_B = 0; @@ -58,9 +58,9 @@ void led_next() // enable selected LED if (led_sel) { if ((led_mode == MODE_BUZZ) && !snd_buzz) return; - RGBSEL1_PORT->BSRR = (1 << RGBSEL1_PIN); + RGBSEL1_PORT->BRR = (1 << RGBSEL1_PIN); } else { - RGBSEL0_PORT->BSRR = (1 << RGBSEL0_PIN); + RGBSEL0_PORT->BRR = (1 << RGBSEL0_PIN); } } @@ -145,6 +145,9 @@ void led_init() led_mode = MODE_RGB; led_sel = 1; snd_buzz = 0; + + rgb[0][0] = 100; + rgb[1][1] = 100; // start outputting data led_next(); diff --git a/src/main.c b/src/main.c index e222442..4cbde96 100644 --- a/src/main.c +++ b/src/main.c @@ -10,7 +10,9 @@ #include "testo.h" #include "adc.h" +#include "flash.h" #include "led.h" +#include "userio.h" @@ -30,8 +32,8 @@ static inline void gpio_init() // enable pullups on I2C outputs GPIOA->PUPDR = 0x24000050; // datasheet doesn't say what the speeds are for GPIO... - // so set SWDIO to very high speed, all other outputs as high speed - GPIOA->OSPEEDR = 0x2E002AA0; + // so set SWDIO to very high speed, PA12 low speed, all other outputs as high speed + GPIOA->OSPEEDR = 0x2C002AA0; // alternate function select // PA6=T3C1, PA5=T3C2, PA4=T3C3, PA3=I2C_SCL, PA2=I2C_SDA GPIOA->AFR[0] = 0x01DDCC00; @@ -44,26 +46,27 @@ static inline void gpio_init() // PB1=OUT, PB0=AN - // unused pins are analog - GPIOB->OSPEEDR = 0x00000008; + // unused pins are analog, all outputs are low speed + GPIOB->OSPEEDR = 0x00000000; GPIOB->MODER = 0xfffffff7; - // PF2=OUT, low speed + // PF2=OUT(low), low speed // unused pins are analog + GPIOF->ODR = (1 << 2); GPIOF->MODER = 0xFFFFFCDF; } static inline void clk_init() { + // run all buses at core clock speed LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); - // enable GPIO clocks + // enable GPIO and peripheral clocks RCC->IOPENR = LL_IOP_GRP1_PERIPH_GPIOA | LL_IOP_GRP1_PERIPH_GPIOB | LL_IOP_GRP1_PERIPH_GPIOF; - // enable peripheral clocks RCC->AHBENR = LL_AHB1_GRP1_PERIPH_DMA1; RCC->APBENR1 = LL_APB1_GRP1_PERIPH_TIM3; RCC->APBENR2 = LL_APB1_GRP2_PERIPH_ADC1 | LL_APB1_GRP2_PERIPH_SYSCFG; @@ -78,6 +81,12 @@ static inline void systick_init() // configure timebase with interrupt at 4096Hz // this assumes we'll always be running at 8MHz SysTick_Config((8000000 / 4096) - 1); + + // configure unused DMA channels to not be on ADC + // (otherwise ADC can shows reads of 0... it's a hardware bug) + SYSCFG->CFGR3 = LL_SYSCFG_DMA_MAP_ADC | // ch0 + LL_SYSCFG_DMA_MAP_TIM17_UP << 8 | // ch1 + LL_SYSCFG_DMA_MAP_TIM17_UP << 16; // ch2 } /* @@ -88,6 +97,7 @@ int main() { // base hardware initialization clk_init(); + flash_init(); // also configures option bytes, if necessary gpio_init(); // peripheral initialization @@ -116,20 +126,36 @@ int main() */ void SysTick_Handler(void) { + uint16_t cs; + ctr++; // run LED programs quickly led_next(); - // limit counter to 4096 + // limit counter to 4096 counts ctr &= 0xfff; if (!ctr) { uptime++; } // run main logic at 1024Hz - if ((ctr & 0x3) == 0) { - // adc - adc_next(); + if (!(ctr & 0x3)) { + // shifted counter for use in the program + cs = ctr >> 2; + + // adc (512Hz/16 avg = 32Hz update rate) + if (cs & 1) { + if (!adc_next()) { + // adc has new computed results, + // so we can do userio, probe, etc + + // but the results are only valid + // after our first cycle + if (uptime || cs) { + userio_parse(); + } + } + } } } \ No newline at end of file diff --git a/src/userio.c b/src/userio.c index 99cf643..0ccd13c 100644 --- a/src/userio.c +++ b/src/userio.c @@ -1,5 +1,5 @@ /* - * adc.c: reacting when you fiddle with buttons, switches, and knobs + * userio.c: reacting when you fiddle with buttons, switches, and knobs * * mode switch: modes are changed only when the button is not being * pushed, as button being pushed negates the mode switch response. @@ -29,23 +29,26 @@ #define MODE_CONT 0 -#define MODE_CONT_TARGET 0x46d +#define MODE_CONT_TARGET (4096 * (1 - (1 / 4))) #define MODE_FUN 1 -#define MODE_FUN_TARGET 0xa2b +#define MODE_FUN_TARGET (4096 * (1 - (1 / 3))) #define MODE_DIODE 2 -#define MODE_DIODE_TARGET 0xf80 +#define MODE_DIODE_TARGET (4096 * (1 - (1 / 2))) -#define MODE_HYSTERESIS 20 +#define MODE_HYSTERESIS 60 // 3x worst case expected (1% tol) -#define MODE_ANALOG_MIN 0x80 +#define MODE_ANALOG_MIN 20 -uint8_t modesw; -uint8_t modesw_next; -uint8_t modesw_count; +uint8_t mode; +uint8_t mode_next; +uint8_t mode_count; +static const uint16_t mode_targets[] = { + MODE_CONT_TARGET, MODE_FUN_TARGET, MODE_DIODE_TARGET +}; uint8_t knob[2]; @@ -54,6 +57,11 @@ uint16_t btn_held = 0; +void userio_update() +{ + +} + void userio_parse() {