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. 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 txt - buffer with commands & data
* @param isUSB - == 1 if data got from USB * @param isUSB - == 1 if data got from USB
*/ */
static void cmd_parser(char *txt){ static void CommandParser(char *txt){
char _1st = txt[0]; char _1st = txt[0];
++txt; ++txt;
/* /*
@@ -479,5 +479,5 @@ void CANUSB_process(){
} }
int l = RECV(inbuff, MAXSTRLEN); int l = RECV(inbuff, MAXSTRLEN);
if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n"); 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 <stm32f0.h>
#include <string.h> #include <string.h>
#include "adc.h"
#include "flash.h" #include "flash.h"
#include "gpio.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) static uint16_t oldstates[2][16] = {0}; // previous state (16 bits - as some pins could be analog)
// strings for keywords // strings for keywords
@@ -134,6 +135,22 @@ int set_pinfunc(uint8_t port, uint8_t pin, pinconfig_t *pcfg){
return TRUE; 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 // reinit all GPIO registers due to config; also configure (if need) USART1/2, SPI1 and I2C1
int gpio_reinit(){ int gpio_reinit(){
bzero(monitor_mask, sizeof(monitor_mask)); bzero(monitor_mask, sizeof(monitor_mask));
@@ -167,7 +184,19 @@ int gpio_reinit(){
int shift4 = (pin - 8) << 4; int shift4 = (pin - 8) << 4;
gpio->AFR[1] = (gpio->AFR[1] & ~(0xf << shift4)) | (cfg->afno << shift4); 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 // 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; if(GPIOx->IDR & (1<<pin)) val = 1;
else val = 0; else val = 0;
break; break;
// case MODE_ANALOG: case MODE_ANALOG:{
default: // TODO: add ADC! int8_t chan = get_adc_channel(port, pin);
if(chan >= 0){
return (int16_t)getADCval(chan); // getADCval ×ÏÚ×ÒÁÝÁÅÔ uint16_t
}
}
break;
default:
break; break;
} }
return val; return val;
@@ -241,12 +276,23 @@ uint16_t gpio_alert(uint8_t port){
uint8_t curm = moder & 3; uint8_t curm = moder & 3;
if((curm == MODE_AF) || 0 == (monitor_mask[port] & curpinbit)) continue; // monitor also OUT (if OD) if((curm == MODE_AF) || 0 == (monitor_mask[port] & curpinbit)) continue; // monitor also OUT (if OD)
// TODO: add AIN // TODO: add AIN
if(curm == MODE_ANALOG) continue; 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; uint16_t curval = (GPIOx->IDR & curpinbit) ? 1 : 0;
if(oldstate[pin] != curval){ if(oldstate[pin] != curval){
oldstate[pin] = curval; oldstate[pin] = curval;
alert |= curpinbit; alert |= curpinbit;
} }
} }
}
return alert; return alert;
} }

View File

@@ -86,6 +86,7 @@ typedef struct{
uint8_t afno : 3; // alternate function number (only if mode == MODE_AF) uint8_t afno : 3; // alternate function number (only if mode == MODE_AF)
uint8_t af : 3; // alternate function name (`FuncNames`) uint8_t af : 3; // alternate function name (`FuncNames`)
uint8_t monitor : 1; // monitor changes uint8_t monitor : 1; // monitor changes
uint16_t threshold; // threshold for ADC measurement
} pinconfig_t; } pinconfig_t;
typedef struct{ typedef struct{
@@ -119,7 +120,8 @@ KW(OD) \
KW(USART) \ KW(USART) \
KW(SPI) \ KW(SPI) \
KW(I2C) \ KW(I2C) \
KW(MONITOR) KW(MONITOR) \
KW(THRESHOLD)
enum{ // indexes of string keywords enum{ // indexes of string keywords
#define KW(k) STR_ ## k, #define KW(k) STR_ ## k,

View File

@@ -22,6 +22,7 @@
extern "C"{ extern "C"{
#include <stm32f0.h> #include <stm32f0.h>
#include "adc.h"
#include "can.h" #include "can.h"
#include "flash.h" #include "flash.h"
#include "gpioproto.h" #include "gpioproto.h"
@@ -43,6 +44,7 @@ extern volatile uint32_t Tms;
COMMAND(dumpconf, "dump current configuration") \ COMMAND(dumpconf, "dump current configuration") \
COMMAND(eraseflash, "erase full flash storage") \ COMMAND(eraseflash, "erase full flash storage") \
COMMAND(help, "show this help") \ COMMAND(help, "show this help") \
COMMAND(mcutemp, "get MCU temperature (degC*10)") \
COMMAND(mcureset, "reset MCU") \ COMMAND(mcureset, "reset MCU") \
COMMAND(PA, "GPIOA setter/getter (type PA0=help for further info)") \ COMMAND(PA, "GPIOA setter/getter (type PA0=help for further info)") \
COMMAND(PB, "GPIOB setter/getter") \ COMMAND(PB, "GPIOB setter/getter") \
@@ -52,7 +54,8 @@ extern volatile uint32_t Tms;
COMMAND(sendcan, "send all after '=' to CAN USB interface") \ COMMAND(sendcan, "send all after '=' to CAN USB interface") \
COMMAND(setiface, "set/get name of interface x (0 - CAN, 1 - GPIO)") \ COMMAND(setiface, "set/get name of interface x (0 - CAN, 1 - GPIO)") \
COMMAND(storeconf, "save config to flash") \ 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(USART, "Read USART data or send (USART=hex)")
// COMMAND(usartconf, "set USART params (e.g. usartconf=115200 8N1)") // COMMAND(usartconf, "set USART params (e.g. usartconf=115200 8N1)")
@@ -93,6 +96,7 @@ enum KeywordGroup {
enum MiscValues{ enum MiscValues{
MISC_MONITOR = 1, MISC_MONITOR = 1,
MISC_THRESHOLD
}; };
static const Keyword keywords[] = { static const Keyword keywords[] = {
@@ -110,6 +114,7 @@ static const Keyword keywords[] = {
KEY(SPI, GROUP_FUNC, FUNC_SPI) KEY(SPI, GROUP_FUNC, FUNC_SPI)
KEY(I2C, GROUP_FUNC, FUNC_I2C) KEY(I2C, GROUP_FUNC, FUNC_I2C)
KEY(MONITOR, GROUP_MISC, MISC_MONITOR) KEY(MONITOR, GROUP_MISC, MISC_MONITOR)
KEY(THRESHOLD, GROUP_MISC, MISC_THRESHOLD)
#undef K #undef K
}; };
#define NUM_KEYWORDS (sizeof(keywords)/sizeof(keywords[0])) #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" " PULL: PU, PD or FL (pullup, pulldown, no pull - floating)\n"
" OTYPE: PP or OD (push-pull or open-drain)\n" " OTYPE: PP or OD (push-pull or open-drain)\n"
" FUNC: USART or SPI (enable alternate function and configure peripheal)\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 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` * @brief splitargs - get command parameter and setter from `args`
* @param args (i) - rest of string after command (like `1 = PU OD OUT`) * @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 // complex setter: parse properties
uint8_t mode_set = 0xFF, pull_set = 0xFF, otype_set = 0xFF, func_set = 0xFF; uint8_t mode_set = 0xFF, pull_set = 0xFF, otype_set = 0xFF, func_set = 0xFF;
bool monitor = false; 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); char *saveptr, *token = strtok_r(setter, " ,", &saveptr);
while(token){ 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; size_t i = 0;
for(; i < NUM_KEYWORDS; i++){ for(; i < NUM_KEYWORDS; i++){
if(strcmp(token, str_keywords[keywords[i].index]) == 0){ 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; break;
case GROUP_MISC: case GROUP_MISC:
DBG("GROUP_MISC\n"); DBG("GROUP_MISC\n");
if(keywords[i].value == MISC_MONITOR) monitor = true; switch(keywords[i].value){
case MISC_MONITOR:
monitor = true;
break; break;
case MISC_THRESHOLD:
pending_num = &curconf.threshold;
break;
}
} }
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 if(i == NUM_KEYWORDS) return ERR_BADVAL; // not found
token = strtok_r(NULL, " ,", &saveptr); 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(func_set != 0xFF) mode_set = MODE_AF;
if(mode_set == 0xFF) return ERR_BADVAL; // user forgot to set mode if(mode_set == 0xFF) return ERR_BADVAL; // user forgot to set mode
// set defaults // 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; if(otype_set == 0xFF) otype_set = OUTPUT_PP;
// can also do something with `speed_set`, then remove SPEED_MEDIUM from `curconfig` // can also do something with `speed_set`, then remove SPEED_MEDIUM from `curconfig`
// check that current parameters combination is acceptable for current pin // check that current parameters combination is acceptable for current pin
pinconfig_t curconf;
curconf.mode = static_cast <pinmode_t> (mode_set); curconf.mode = static_cast <pinmode_t> (mode_set);
curconf.pull = static_cast <pinpull_t> (pull_set); curconf.pull = static_cast <pinpull_t> (pull_set);
curconf.otype = static_cast <pinout_t> (otype_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; if(S < CAN_MIN_SPEED || S > CAN_MAX_SPEED) return ERR_BADVAL;
the_conf.CANspeed = S; the_conf.CANspeed = S;
} }
SEND(cmd); PUTCHAR('='); SENDn(u2str(the_conf.CANspeed)); CMDEQ();
SENDn(u2str(the_conf.CANspeed));
return ERR_AMOUNT; return ERR_AMOUNT;
} }
static errcodes_t cmd_curcanspeed(const char *cmd, char _U_ *args){ 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; return ERR_AMOUNT;
} }
@@ -350,6 +381,11 @@ static errcodes_t cmd_dumpconf(const char _U_ *cmd, char _U_ *args){
break; break;
case MODE_ANALOG: case MODE_ANALOG:
S(AIN); S(AIN);
if(p->threshold){
SP(THRESHOLD);
PUTCHAR(' ');
SEND(u2str(p->threshold));
}
break; break;
case MODE_AF: case MODE_AF:
switch(p->af){ switch(p->af){
@@ -430,7 +466,7 @@ static errcodes_t cmd_setiface(const char* cmd, char *args){
} }
} }
// getter // getter
SEND(cmd); PUTCHAR('='); CMDEQP(N);
char *ptr = (char*) the_conf.iInterface[N]; char *ptr = (char*) the_conf.iInterface[N];
int l = the_conf.iIlengths[N] / 2; int l = the_conf.iIlengths[N] / 2;
for(int j = 0; j < l; ++j){ 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); char *setter = splitargs(args, NULL);
if(!setter) return ERR_BADVAL; if(!setter) return ERR_BADVAL;
if(USB_sendstr(ICAN, setter)) return ERR_OK; if(USB_sendstr(ICAN, setter)) return ERR_OK;
USB_putbyte(ICAN, '\n');
return ERR_CANTRUN; return ERR_CANTRUN;
} }
static errcodes_t cmd_time(const char *cmd, char _U_ *args){ static errcodes_t cmd_time(const char *cmd, char _U_ *args){
SEND(cmd); PUTCHAR('='); SENDn(u2str(Tms)); CMDEQ();
SENDn(u2str(Tms));
return ERR_AMOUNT; return ERR_AMOUNT;
} }
@@ -474,6 +512,18 @@ static errcodes_t cmd_eraseflash(const char _U_ *cmd, char _U_ *args){
return ERR_OK; 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){ static errcodes_t cmd_help(const char _U_ *cmd, char _U_ *args){
SEND(REPOURL); SEND(REPOURL);
for(size_t i = 0; i < sizeof(cmdInfo)/sizeof(cmdInfo[0]); i++){ 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! // TODO: add checking real command length!
static const char *cmd_parser(char *str){ static const char *CommandParser(char *str){
char command[CMD_MAXLEN+1]; char command[CMD_MAXLEN+1];
int i = 0; int i = 0;
while(*str > '@' && i < CMD_MAXLEN){ command[i++] = *str++; } while(*str > '@' && i < CMD_MAXLEN){ command[i++] = *str++; }
@@ -524,7 +574,7 @@ void GPIO_process(){
if(l == 0) return; if(l == 0) return;
if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n"); if(l < 0) SEND("ERROR: USB buffer overflow or string was too long\n");
else{ else{
const char *ans = cmd_parser(inbuff); const char *ans = CommandParser(inbuff);
if(ans) SENDn(ans); if(ans) SENDn(ans);
} }
} }

View File

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

View File

@@ -16,13 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "adc.h"
#include "gpio.h"
#include "hardware.h" #include "hardware.h"
uint8_t ledsON = 0; uint8_t ledsON = 0;
void gpio_setup(void){ TRUE_INLINE void gpio_setup(){ // setup some common GPIO
// enable all active GPIO clocking
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
// Set LEDS (PB15/PA8) as output // Set LEDS (PB15/PA8) as output
pin_set(LED0_port, LED0_pin); // clear LEDs pin_set(LED0_port, LED0_pin); // clear LEDs
pin_set(LED1_port, LED1_pin); pin_set(LED1_port, LED1_pin);
@@ -32,6 +32,14 @@ void gpio_setup(void){
GPIO_MODER_MODER8_O; 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(){ void iwdg_setup(){
uint32_t tmout = 16000000; uint32_t tmout = 16000000;
/* Enable the peripheral clock RTC */ /* Enable the peripheral clock RTC */

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!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> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

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

View File

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