add ADC & ADC monitoring

This commit is contained in:
Edward Emelianov
2026-03-10 22:40:45 +03:00
parent 8e78a12f06
commit 3802f7d2cb
15 changed files with 338 additions and 36 deletions

View File

@@ -10,3 +10,6 @@ The old USB-CAN is available as earlier by /dev/USB-CANx, also you can see new d
New interface allows you to configure GPIO and use it's base functions: in/out/ADC.
## DMA channels
DMA1 channel1: ADC.

View File

@@ -0,0 +1,158 @@
/*
* This file is part of the usbcangpio project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stm32f0.h>
#include "adc.h"
/**
* @brief ADC_array - array for ADC channels with median filtering:
* 0..3 - external channels
* 4 - internal Tsens
* 5 - Vref
*/
#define TSENS_CHAN (NUM_EXT_ADC_CH)
#define VREF_CHAN (NUM_EXT_ADC_CH + 1)
static uint16_t ADC_array[MAX_ADC_CHANNELS*9];
/*
* ADC channels:
* IN0 - V12
* IN1 - V5
* IN16- temperature sensor
* IN17- vref
*/
void adc_setup(){
uint16_t ctr = 0; // 0xfff0 - more than 1.3ms
// Enable clocking
/* (1) Enable the peripheral clock of the ADC */
/* (2) Start HSI14 RC oscillator */
/* (3) Wait HSI14 is ready */
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; /* (1) */
RCC->CR2 |= RCC_CR2_HSI14ON; /* (2) */
while ((RCC->CR2 & RCC_CR2_HSI14RDY) == 0 && ++ctr < 0xfff0){}; /* (3) */
// calibration
/* (1) Ensure that ADEN = 0 */
/* (2) Clear ADEN */
/* (3) Launch the calibration by setting ADCAL */
/* (4) Wait until ADCAL=0 */
if ((ADC1->CR & ADC_CR_ADEN) != 0){ /* (1) */
ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); /* (2) */
}
ADC1->CR |= ADC_CR_ADCAL; /* (3) */
ctr = 0; // ADC calibration time is 5.9us
while ((ADC1->CR & ADC_CR_ADCAL) != 0 && ++ctr < 0xfff0){}; /* (4) */
// enable ADC
ctr = 0;
do{
ADC1->CR |= ADC_CR_ADEN;
}while ((ADC1->ISR & ADC_ISR_ADRDY) == 0 && ++ctr < 0xfff0);
// configure ADC
/* (1) Select HSI14 by writing 00 in CKMODE (reset value) */
/* (2) Select the continuous mode */
/* (3) Select CHSEL0-9 - all ADC inputs, 16,17 - t. sensor and vref */
/* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */
/* (5) Wake-up the VREFINT and Temperature sensor (only for VBAT, Temp sensor and VRefInt) */
// ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_CONT; /* (2)*/
ADC1->CHSELR = (0x3FF << 0) | ADC_CHSELR_CHSEL16 | ADC_CHSELR_CHSEL17; /* (3)*/
ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; /* (4) */
ADC->CCR |= ADC_CCR_TSEN | ADC_CCR_VREFEN; /* (5) */
// configure DMA for ADC
// DMA for AIN
/* (1) Enable the peripheral clock on DMA */
/* (2) Enable DMA transfer on ADC and circular mode */
/* (3) Configure the peripheral data register address */
/* (4) Configure the memory address */
/* (5) Configure the number of DMA tranfer to be performs on DMA channel 1 */
/* (6) Configure increment, size, interrupts and circular mode */
/* (7) Enable DMA Channel 1 */
RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG; /* (2) */
DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); /* (3) */
DMA1_Channel1->CMAR = (uint32_t)(ADC_array); /* (4) */
DMA1_Channel1->CNDTR = MAX_ADC_CHANNELS * 9; /* (5) */
DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC; /* (6) */
DMA1_Channel1->CCR |= DMA_CCR_EN; /* (7) */
ADC1->CR |= ADC_CR_ADSTART; /* start the ADC conversions */
}
/**
* @brief getADCval - calculate median value for `nch` channel
* @param nch - number of channel
* @return
*/
uint16_t getADCval(int nch){
int i, addr = nch;
register uint16_t temp;
#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
#define PIX_SWAP(a,b) { temp=(a);(a)=(b);(b)=temp; }
uint16_t p[9];
for(i = 0; i < 9; ++i, addr += MAX_ADC_CHANNELS) // first we should prepare array for optmed
p[i] = ADC_array[addr];
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ;
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ;
PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ;
PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ;
PIX_SORT(p[4], p[2]) ;
return p[4];
#undef PIX_SORT
#undef PIX_SWAP
}
// return MCU temperature (degrees of celsius * 10)
int32_t getMCUtemp(){
int32_t ADval = getADCval(TSENS_CHAN);
int32_t temperature = (int32_t) *TEMP30_CAL_ADDR - ADval;
temperature *= (int32_t)(1100 - 300);
temperature /= (int32_t)(*TEMP30_CAL_ADDR - *TEMP110_CAL_ADDR);
temperature += 300;
return(temperature);
}
// return Vdd * 100 (V)
uint32_t getVdd(){
uint32_t vdd = ((uint32_t) *VREFINT_CAL_ADDR) * (uint32_t)330; // 3.3V
vdd /= getADCval(VREF_CHAN);
return vdd;
}
static inline uint32_t Ufromadu(uint8_t nch, uint32_t vdd){
uint32_t ADU = getADCval(nch);
ADU *= vdd;
ADU >>= 12; // /4096
return ADU;
}
/**
* @brief getUval - calculate U12/U5
* @return array with members:
* 0 - V12 * 100V (U12 = 12Vin/4.93)
* 1 - V5 * 100V (U5 = 5Vin /2)
*/
uint16_t *getUval(){
static uint16_t Uval[4];
uint32_t vdd = getVdd();
uint32_t val = Ufromadu(0, vdd) * 493;
Uval[0] = (uint16_t)(val / 100);
Uval[1] = (uint16_t)(Ufromadu(1, vdd) << 1);
return Uval;
}

View File

@@ -0,0 +1,30 @@
/*
* This file is part of the usbcangpio project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
// 10 ADC ins + VDD + MCUt
#define NUM_EXT_ADC_CH 10
#define MAX_ADC_CHANNELS (NUM_EXT_ADC_CH+2)
int32_t getMCUtemp();
uint32_t getVdd();
uint16_t getADCval(int nch);
void adc_setup();
uint16_t *getUval();

View File

@@ -337,11 +337,11 @@ TRUE_INLINE void getcanstat(){
}
/**
* @brief cmd_parser - command parsing
* @brief CommandParser - command parsing
* @param txt - buffer with commands & data
* @param isUSB - == 1 if data got from USB
*/
static void cmd_parser(char *txt){
static void CommandParser(char *txt){
char _1st = txt[0];
++txt;
/*
@@ -479,5 +479,5 @@ void CANUSB_process(){
}
int l = RECV(inbuff, MAXSTRLEN);
if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n");
else if(l) cmd_parser(inbuff);
else if(l) CommandParser(inbuff);
}

View File

@@ -19,10 +19,11 @@
#include <stm32f0.h>
#include <string.h>
#include "adc.h"
#include "flash.h"
#include "gpio.h"
static uint16_t monitor_mask[2] = {0}; // pins to monitor == 1 (ONLY GPIO!!!)
static uint16_t monitor_mask[2] = {0}; // pins to monitor == 1 (ONLY GPIO and ADC)
static uint16_t oldstates[2][16] = {0}; // previous state (16 bits - as some pins could be analog)
// strings for keywords
@@ -134,6 +135,22 @@ int set_pinfunc(uint8_t port, uint8_t pin, pinconfig_t *pcfg){
return TRUE;
}
/**
* @brief get_adc_channel - get ADC channel number for given pin
* @param port - 0/1 (GPIOA/GPIOB)
* @param pin - 0..16
* @return ADC channel number or -1
*/
TRUE_INLINE int8_t get_adc_channel(uint8_t port, uint8_t pin){
if(port == 0){ // GPIOA
if (pin <= 7) return pin; // PA0..PA7 -> IN0..IN7
}else{ // GPIOB
if(pin == 0) return 8; // PB0 -> IN8
if(pin == 1) return 9; // PB1 -> IN9
}
return -1; // No ADC channel on this pin
}
// reinit all GPIO registers due to config; also configure (if need) USART1/2, SPI1 and I2C1
int gpio_reinit(){
bzero(monitor_mask, sizeof(monitor_mask));
@@ -167,7 +184,19 @@ int gpio_reinit(){
int shift4 = (pin - 8) << 4;
gpio->AFR[1] = (gpio->AFR[1] & ~(0xf << shift4)) | (cfg->afno << shift4);
}
if(cfg->monitor && cfg->mode != MODE_AF) monitor_mask[port] |= (1 << pin);
if(cfg->monitor && cfg->mode != MODE_AF){
monitor_mask[port] |= (1 << pin);
if(cfg->mode == MODE_ANALOG){
if(cfg->threshold > 4095) cfg->threshold = 4095; // no threshold
int8_t chan = get_adc_channel(port, pin);
if(chan >= 0){
oldstates[port][pin] = getADCval(chan);
}
}else{
// ÃÉÆÒÏ×ÏÊ ÒÅÖÉÍ  ÓÏÈÒÁÎÑÅÍ ÔÅËÕÝÅÅ ÓÏÓÔÏÑÎÉÅ IDR
oldstates[port][pin] = (gpio->IDR >> pin) & 1;
}
}
}
}
// TODO: configure USART, SPI etc
@@ -216,8 +245,14 @@ int16_t pin_in(uint8_t port, uint8_t pin){
if(GPIOx->IDR & (1<<pin)) val = 1;
else val = 0;
break;
// case MODE_ANALOG:
default: // TODO: add ADC!
case MODE_ANALOG:{
int8_t chan = get_adc_channel(port, pin);
if(chan >= 0){
return (int16_t)getADCval(chan); // getADCval ×ÏÚ×ÒÁÝÁÅÔ uint16_t
}
}
break;
default:
break;
}
return val;
@@ -241,11 +276,22 @@ uint16_t gpio_alert(uint8_t port){
uint8_t curm = moder & 3;
if((curm == MODE_AF) || 0 == (monitor_mask[port] & curpinbit)) continue; // monitor also OUT (if OD)
// TODO: add AIN
if(curm == MODE_ANALOG) continue;
uint16_t curval = (GPIOx->IDR & curpinbit) ? 1 : 0;
if(oldstate[pin] != curval){
oldstate[pin] = curval;
alert |= curpinbit;
if(curm == MODE_ANALOG){
int8_t chan = get_adc_channel(port, pin);
if(chan < 0) continue; // can't be in normal case
uint16_t cur = getADCval(chan);
uint16_t thresh = the_conf.pinconfig[port][pin].threshold;
uint16_t diff = (cur > oldstate[pin]) ? (cur - oldstate[pin]) : (oldstate[pin] - cur);
if(diff > thresh){
oldstate[pin] = cur;
alert |= curpinbit;
}
}else{
uint16_t curval = (GPIOx->IDR & curpinbit) ? 1 : 0;
if(oldstate[pin] != curval){
oldstate[pin] = curval;
alert |= curpinbit;
}
}
}
return alert;

View File

@@ -78,14 +78,15 @@ typedef union{
} funcvalues_t;
*/
typedef struct{
uint8_t enable : 1; // [immutable!] pin config avialable (==1 for PA0-PA3, PA5-PA7, PA9, PA10, PB0-PB7, PB10, PB11, ==0 for rest)
uint8_t enable : 1; // [immutable!] pin config avialable (==1 for PA0-PA3, PA5-PA7, PA9, PA10, PB0-PB7, PB10, PB11, ==0 for rest)
pinmode_t mode : 2;
pinpull_t pull : 2;
pinout_t otype : 1;
pinspeed_t speed : 2;
uint8_t afno : 3; // alternate function number (only if mode == MODE_AF)
uint8_t af : 3; // alternate function name (`FuncNames`)
uint8_t monitor : 1; // monitor changes
uint8_t afno : 3; // alternate function number (only if mode == MODE_AF)
uint8_t af : 3; // alternate function name (`FuncNames`)
uint8_t monitor : 1; // monitor changes
uint16_t threshold; // threshold for ADC measurement
} pinconfig_t;
typedef struct{
@@ -119,7 +120,8 @@ KW(OD) \
KW(USART) \
KW(SPI) \
KW(I2C) \
KW(MONITOR)
KW(MONITOR) \
KW(THRESHOLD)
enum{ // indexes of string keywords
#define KW(k) STR_ ## k,

View File

@@ -22,6 +22,7 @@
extern "C"{
#include <stm32f0.h>
#include "adc.h"
#include "can.h"
#include "flash.h"
#include "gpioproto.h"
@@ -43,6 +44,7 @@ extern volatile uint32_t Tms;
COMMAND(dumpconf, "dump current configuration") \
COMMAND(eraseflash, "erase full flash storage") \
COMMAND(help, "show this help") \
COMMAND(mcutemp, "get MCU temperature (degC*10)") \
COMMAND(mcureset, "reset MCU") \
COMMAND(PA, "GPIOA setter/getter (type PA0=help for further info)") \
COMMAND(PB, "GPIOB setter/getter") \
@@ -52,7 +54,8 @@ extern volatile uint32_t Tms;
COMMAND(sendcan, "send all after '=' to CAN USB interface") \
COMMAND(setiface, "set/get name of interface x (0 - CAN, 1 - GPIO)") \
COMMAND(storeconf, "save config to flash") \
COMMAND(time, "show current time (ms)")
COMMAND(time, "show current time (ms)") \
COMMAND(vdd, "get approx Vdd value (V*100)")
// COMMAND(USART, "Read USART data or send (USART=hex)")
// COMMAND(usartconf, "set USART params (e.g. usartconf=115200 8N1)")
@@ -93,6 +96,7 @@ enum KeywordGroup {
enum MiscValues{
MISC_MONITOR = 1,
MISC_THRESHOLD
};
static const Keyword keywords[] = {
@@ -110,6 +114,7 @@ static const Keyword keywords[] = {
KEY(SPI, GROUP_FUNC, FUNC_SPI)
KEY(I2C, GROUP_FUNC, FUNC_I2C)
KEY(MONITOR, GROUP_MISC, MISC_MONITOR)
KEY(THRESHOLD, GROUP_MISC, MISC_THRESHOLD)
#undef K
};
#define NUM_KEYWORDS (sizeof(keywords)/sizeof(keywords[0]))
@@ -129,11 +134,18 @@ static const char *pinhelp =
" PULL: PU, PD or FL (pullup, pulldown, no pull - floating)\n"
" OTYPE: PP or OD (push-pull or open-drain)\n"
" FUNC: USART or SPI (enable alternate function and configure peripheal)\n"
" MISC: MONITOR (send data by USB as only state changed)\n\n"
" MISC: MONITOR - send data by USB as only state changed\n"
" THRESHOLD (ADC only) - monitoring threshold, ADU\n"
"\n"
;
static const char *EQ = " = "; // equal sign for getters
// send `command = `
#define CMDEQ() do{SEND(cmd); SEND(EQ);}while(0)
// send `commandXXX = `
#define CMDEQP(x) do{SEND(cmd); SEND(u2str((uint32_t)x)); SEND(EQ);}while(0)
/**
* @brief splitargs - get command parameter and setter from `args`
* @param args (i) - rest of string after command (like `1 = PU OD OUT`)
@@ -201,8 +213,19 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){
// complex setter: parse properties
uint8_t mode_set = 0xFF, pull_set = 0xFF, otype_set = 0xFF, func_set = 0xFF;
bool monitor = false;
uint16_t *pending_num = NULL; // pointer to UINT16 value, if !NULL, next token should be a number
pinconfig_t curconf = the_conf.pinconfig[port][pin]; // copy old config
char *saveptr, *token = strtok_r(setter, " ,", &saveptr);
while(token){
if(pending_num){
int32_t val;
char *end = getint(token, &val);
if(end == token || val < 0 || val > 0xFFFF) return ERR_BADVAL;
*pending_num = (uint16_t)val;
pending_num = NULL; // reset
token = strtok_r(NULL, " ,", &saveptr);
continue;
}
size_t i = 0;
for(; i < NUM_KEYWORDS; i++){
if(strcmp(token, str_keywords[keywords[i].index]) == 0){
@@ -229,8 +252,14 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){
break;
case GROUP_MISC:
DBG("GROUP_MISC\n");
if(keywords[i].value == MISC_MONITOR) monitor = true;
break;
switch(keywords[i].value){
case MISC_MONITOR:
monitor = true;
break;
case MISC_THRESHOLD:
pending_num = &curconf.threshold;
break;
}
}
break;
}
@@ -238,6 +267,7 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){
if(i == NUM_KEYWORDS) return ERR_BADVAL; // not found
token = strtok_r(NULL, " ,", &saveptr);
}
if(pending_num) return ERR_BADVAL; // no number that we waiting for
if(func_set != 0xFF) mode_set = MODE_AF;
if(mode_set == 0xFF) return ERR_BADVAL; // user forgot to set mode
// set defaults
@@ -245,7 +275,6 @@ static errcodes_t pin_setter(uint8_t port, uint8_t pin, char *setter){
if(otype_set == 0xFF) otype_set = OUTPUT_PP;
// can also do something with `speed_set`, then remove SPEED_MEDIUM from `curconfig`
// check that current parameters combination is acceptable for current pin
pinconfig_t curconf;
curconf.mode = static_cast <pinmode_t> (mode_set);
curconf.pull = static_cast <pinpull_t> (pull_set);
curconf.otype = static_cast <pinout_t> (otype_set);
@@ -301,12 +330,14 @@ static errcodes_t cmd_canspeed(const char *cmd, char *args){
if(S < CAN_MIN_SPEED || S > CAN_MAX_SPEED) return ERR_BADVAL;
the_conf.CANspeed = S;
}
SEND(cmd); PUTCHAR('='); SENDn(u2str(the_conf.CANspeed));
CMDEQ();
SENDn(u2str(the_conf.CANspeed));
return ERR_AMOUNT;
}
static errcodes_t cmd_curcanspeed(const char *cmd, char _U_ *args){
SEND(cmd); PUTCHAR('='); SENDn(u2str(CAN_getspeed()));
CMDEQ();
SENDn(u2str(CAN_getspeed()));
return ERR_AMOUNT;
}
@@ -350,6 +381,11 @@ static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){
break;
case MODE_ANALOG:
S(AIN);
if(p->threshold){
SP(THRESHOLD);
PUTCHAR(' ');
SEND(u2str(p->threshold));
}
break;
case MODE_AF:
switch(p->af){
@@ -430,7 +466,7 @@ static errcodes_t cmd_setiface(const char* cmd, char *args){
}
}
// getter
SEND(cmd); PUTCHAR('=');
CMDEQP(N);
char *ptr = (char*) the_conf.iInterface[N];
int l = the_conf.iIlengths[N] / 2;
for(int j = 0; j < l; ++j){
@@ -446,11 +482,13 @@ static errcodes_t cmd_sendcan(const char _U_ *cmd, char *args){
char *setter = splitargs(args, NULL);
if(!setter) return ERR_BADVAL;
if(USB_sendstr(ICAN, setter)) return ERR_OK;
USB_putbyte(ICAN, '\n');
return ERR_CANTRUN;
}
static errcodes_t cmd_time(const char *cmd, char _U_ *args){
SEND(cmd); PUTCHAR('='); SENDn(u2str(Tms));
CMDEQ();
SENDn(u2str(Tms));
return ERR_AMOUNT;
}
@@ -474,6 +512,18 @@ static errcodes_t cmd_eraseflash(const char _U_ *cmd, char _U_ *args){
return ERR_OK;
}
static errcodes_t cmd_mcutemp(const char *cmd, char _U_ *args){
CMDEQ();
SENDn(i2str(getMCUtemp()));
return ERR_AMOUNT;
}
static errcodes_t cmd_vdd(const char *cmd, char _U_ *args){
CMDEQ();
SENDn(u2str(getVdd()));
return ERR_AMOUNT;
}
static errcodes_t cmd_help(const char _U_ *cmd, char _U_ *args){
SEND(REPOURL);
for(size_t i = 0; i < sizeof(cmdInfo)/sizeof(cmdInfo[0]); i++){
@@ -490,7 +540,7 @@ constexpr uint32_t hash(const char* str, uint32_t h = 0){
// TODO: add checking real command length!
static const char *cmd_parser(char *str){
static const char *CommandParser(char *str){
char command[CMD_MAXLEN+1];
int i = 0;
while(*str > '@' && i < CMD_MAXLEN){ command[i++] = *str++; }
@@ -524,7 +574,7 @@ void GPIO_process(){
if(l == 0) return;
if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n");
else{
const char *ans = cmd_parser(inbuff);
const char *ans = CommandParser(inbuff);
if(ans) SENDn(ans);
}
}

View File

@@ -34,4 +34,7 @@ typedef enum{
// maximal available parameter number
#define MAXPARNO 255
// default threshold for monitoring
#define ADC_THRES_DEFAULT 100
void GPIO_process();

View File

@@ -16,22 +16,30 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adc.h"
#include "gpio.h"
#include "hardware.h"
uint8_t ledsON = 0;
void gpio_setup(void){
// enable all active GPIO clocking
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
TRUE_INLINE void gpio_setup(){ // setup some common GPIO
// Set LEDS (PB15/PA8) as output
pin_set(LED0_port, LED0_pin); // clear LEDs
pin_set(LED1_port, LED1_pin);
GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER15)) |
GPIO_MODER_MODER15_O;
GPIO_MODER_MODER15_O;
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER8)) |
GPIO_MODER_MODER8_O;
}
void hardware_setup(){
// enable all active GPIO clocking
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
gpio_setup();
//gpio_reinit();
adc_setup();
}
void iwdg_setup(){
uint32_t tmout = 16000000;
/* Enable the peripheral clock RTC */

View File

@@ -44,7 +44,7 @@ extern volatile uint32_t Tms;
extern uint8_t ledsON;
void gpio_setup(void);
void hardware_setup();
void iwdg_setup();
#ifdef STM32F072xB
void Jump2Boot();

View File

@@ -33,7 +33,7 @@ int main(void){
sysreset();
SysTick_Config(6000, 1);
flashstorage_init();
gpio_setup();
hardware_setup();
USB_setup();
CAN_setup(the_conf.CANspeed);
RCC->CSR |= RCC_CSR_RMVF; // remove reset flags

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 18.0.2, 2026-03-10T00:17:06. -->
<!-- Written by QtCreator 18.0.2, 2026-03-10T22:39:58. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@@ -1,3 +1,5 @@
adc.c
adc.h
can.c
can.h
canproto.c

View File

@@ -1,2 +1,2 @@
#define BUILD_NUMBER "125"
#define BUILD_NUMBER "132"
#define BUILD_DATE "2026-03-10"