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.
This commit is contained in:
true
2023-10-19 16:20:41 -07:00
parent 096131b87b
commit ecc478661f
15 changed files with 308 additions and 94 deletions

117
src/adc.c
View File

@@ -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;
}

75
src/flash.c Normal file
View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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();
}
}
}
}
}

View File

@@ -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()
{