mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-03-20 00:30:57 +03:00
add ADC & ADC monitoring
This commit is contained in:
@@ -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.
|
||||||
158
F0:F030,F042,F072/usbcan_gpio/adc.c
Normal file
158
F0:F030,F042,F072/usbcan_gpio/adc.c
Normal 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;
|
||||||
|
}
|
||||||
30
F0:F030,F042,F072/usbcan_gpio/adc.h
Normal file
30
F0:F030,F042,F072/usbcan_gpio/adc.h
Normal 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();
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,11 +276,22 @@ 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){
|
||||||
uint16_t curval = (GPIOx->IDR & curpinbit) ? 1 : 0;
|
int8_t chan = get_adc_channel(port, pin);
|
||||||
if(oldstate[pin] != curval){
|
if(chan < 0) continue; // can't be in normal case
|
||||||
oldstate[pin] = curval;
|
uint16_t cur = getADCval(chan);
|
||||||
alert |= curpinbit;
|
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;
|
return alert;
|
||||||
|
|||||||
@@ -78,14 +78,15 @@ typedef union{
|
|||||||
} funcvalues_t;
|
} funcvalues_t;
|
||||||
*/
|
*/
|
||||||
typedef struct{
|
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;
|
pinmode_t mode : 2;
|
||||||
pinpull_t pull : 2;
|
pinpull_t pull : 2;
|
||||||
pinout_t otype : 1;
|
pinout_t otype : 1;
|
||||||
pinspeed_t speed : 2;
|
pinspeed_t speed : 2;
|
||||||
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,
|
||||||
|
|||||||
@@ -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){
|
||||||
break;
|
case MISC_MONITOR:
|
||||||
|
monitor = true;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -16,22 +16,30 @@
|
|||||||
* 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);
|
||||||
GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER15)) |
|
GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER15)) |
|
||||||
GPIO_MODER_MODER15_O;
|
GPIO_MODER_MODER15_O;
|
||||||
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER8)) |
|
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER8)) |
|
||||||
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 */
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Binary file not shown.
@@ -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>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
adc.c
|
||||||
|
adc.h
|
||||||
can.c
|
can.c
|
||||||
can.h
|
can.h
|
||||||
canproto.c
|
canproto.c
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#define BUILD_NUMBER "125"
|
#define BUILD_NUMBER "132"
|
||||||
#define BUILD_DATE "2026-03-10"
|
#define BUILD_DATE "2026-03-10"
|
||||||
|
|||||||
Reference in New Issue
Block a user