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.
This commit is contained in:
117
src/adc.c
117
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;
|
||||
}
|
||||
75
src/flash.c
Normal file
75
src/flash.c
Normal 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;
|
||||
}
|
||||
15
src/led.c
15
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();
|
||||
|
||||
48
src/main.c
48
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/userio.c
26
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()
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user