add ADC/DAC and PWM

This commit is contained in:
Edward Emelianov
2025-10-06 23:27:30 +03:00
parent 17b4d714e0
commit 24ddc7e1a6
16 changed files with 477 additions and 932 deletions

View File

@@ -0,0 +1,50 @@
| Pin # | Pin name | function | settings | comment |
| --------- | ------------- | ------------ | ------------ | ------------ |
| 1 | VBAT | | | |
| 2 | PC13 | | | |
| 3 | PC14 | | | |
| 4 | PC15 | | | |
| 5 | OCS_IN | | | |
| 6 | OSC_OUT | | | |
| 7 | NRST | | | |
| 8 | VSSA | | | |
| 9 | VDDA | | | |
| 10 | PA0 | ADC1 IN1 | AIN | Text1 |
| 11 | PA1 | ADC1 IN2 | AIN | Text2 |
| 12 | PA2 | | | |
| 13 | PA3 | | | |
| 14 | PA4 | DAC OUT1 | AIN | int. heater |
| 15 | PA5 | ADC2 IN2 | AIN | DAC control |
| 16 | PA6 | PWM1 /TIM3.1 | AF2 | ext. heater |
| 17 | PA7 | PWM2 | AF2 | propto humid |
| 18 | PB0 | PWM3 | AF2 | propto Text |
| 19 | PB1 | PWM4 /TIM3.4 | AF2 | propto tsky |
| 20 | PB2 | SPI CS | PP | BME select |
| 21 | PB10 | | | |
| 22 | PB11 | | | |
| 23 | VSS1 | | | |
| 24 | VDD1 | | | |
| 25 | PB12 | | | |
| 26 | PB13 | | | |
| 27 | PB14 | | | |
| 28 | PB15 | | | |
| 29 | PA8 | | | |
| 30 | PA9 | USART1 TX | AF7 | USART for |
| 31 | PA10 | USART1 RX | AF7 | optical conn.|
| 32 | PA11 | USBDM | AF14 | |
| 33 | PA12 | USBDP | AF14 | |
| 34 | PA13 | SWDIO | AF0 | |
| 35 | VSS2 | | | |
| 36 | VDD2 | | | |
| 37 | PA14 | SWCLK | AF0 | |
| 38 | PA15 | USB PULLUP | PP | |
| 39 | PB3 | SPI SCK | AF5 | SPI for |
| 40 | PB4 | SPI MISO | AF5 | BME280 |
| 41 | PB5 | SPI MOSI | AF5 | sensor |
| 42 | PB6 | I2C SCL | AF4 | I2C for |
| 43 | PB7 | I2C SDA | AF4 | MLX90640 |
| 44 | BOOT0 | | | |
| 45 | PB8 | | | |
| 46 | PB9 | | | |
| 47 | VSS3 | | | |
| 48 | VDD3 | | | |

View File

@@ -0,0 +1,157 @@
/*
* This file is part of the ir-allsky project.
* Copyright 2025 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 "adc.h"
/**
* @brief ADCx_array - arrays for ADC channels with median filtering:
* ADC1:
* 0 - Ch0 - ADC1_IN1
* 1 - Ch1 - ADC1_IN2
* 2 - internal Tsens - ADC1_IN16
* 3 - Vref - ADC1_IN18
* ADC2:
* 4 - AIN5/DAC_OUT1 - PA4 - DAC1_OUT1 (onboard heater?), PA5 - ADC2_IN2 (DAC output control)
*/
static uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9];
TRUE_INLINE void calADC(ADC_TypeDef *chnl){
// calibration
// enable voltage regulator
chnl->CR = 0;
chnl->CR = ADC_CR_ADVREGEN_0;
// wait for 10us
uint16_t ctr = 0;
while(++ctr < 1000){nop();}
// ADCALDIF=0 (single channels)
if((chnl->CR & ADC_CR_ADEN)){
chnl->CR |= ADC_CR_ADSTP;
chnl->CR |= ADC_CR_ADDIS;
}
chnl->CR |= ADC_CR_ADCAL;
while((chnl->CR & ADC_CR_ADCAL) != 0 && ++ctr < 0xfff0){};
chnl->CR = ADC_CR_ADVREGEN_0;
// enable ADC
ctr = 0;
do{
chnl->CR |= ADC_CR_ADEN;
}while((chnl->ISR & ADC_ISR_ADRDY) == 0 && ++ctr < 0xfff0);
}
TRUE_INLINE void enADC(ADC_TypeDef *chnl){
// ADEN->1, wait ADRDY
chnl->CR |= ADC_CR_ADEN;
uint16_t ctr = 0;
while(!(chnl->ISR & ADC_ISR_ADRDY) && ++ctr < 0xffff){}
chnl->CR |= ADC_CR_ADSTART; /* start the ADC conversions */
}
/**
* ADC1 - DMA1_ch1
* ADC2 - DMA2_ch1
*/
// Setup ADC and DAC
void adc_setup(){
RCC->AHBENR |= RCC_AHBENR_ADC12EN; // Enable clocking
ADC12_COMMON->CCR = ADC_CCR_TSEN | ADC_CCR_VREFEN | ADC_CCR_CKMODE; // enable Tsens and Vref, HCLK/4
calADC(ADC1);
calADC(ADC2);
// ADC1: channels 1,2,16,18; ADC2: channel 2
ADC1->SMPR1 = ADC_SMPR1_SMP0 | ADC_SMPR1_SMP1;
ADC1->SMPR2 = ADC_SMPR2_SMP15 | ADC_SMPR2_SMP17;
// 4 conversions in group: 1->2->16->18
ADC1->SQR1 = (1<<6) | (2<<12) | (16<<18) | (18<<24) | (NUMBER_OF_ADC1_CHANNELS-1);
ADC2->SMPR1 = ADC_SMPR1_SMP1;
ADC2->SQR1 = (2<<6) | (NUMBER_OF_ADC2_CHANNELS-1);
// configure DMA for ADC
RCC->AHBENR |= RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN;
ADC1->CFGR = ADC_CFGR_CONT | ADC_CFGR_DMAEN | ADC_CFGR_DMACFG;
ADC2->CFGR = ADC_CFGR_CONT | ADC_CFGR_DMAEN | ADC_CFGR_DMACFG;
DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR));
DMA1_Channel1->CMAR = (uint32_t)(ADC_array);
DMA1_Channel1->CNDTR = NUMBER_OF_ADC1_CHANNELS * 9;
DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC;
DMA1_Channel1->CCR |= DMA_CCR_EN;
DMA2_Channel1->CPAR = (uint32_t) (&(ADC2->DR));
DMA2_Channel1->CMAR = (uint32_t)(&ADC_array[ADC2START]);
DMA2_Channel1->CNDTR = NUMBER_OF_ADC2_CHANNELS * 9;
DMA2_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC;
DMA2_Channel1->CCR |= DMA_CCR_EN;
enADC(ADC1);
enADC(ADC2);
// enable DAC
RCC->APB1ENR |= RCC_APB1ENR_DAC1EN;
// DAC simple throw out constant value: output buffer disable, DAC ch1 enable
DAC->CR = DAC_CR_BOFF1 | DAC_CR_EN1;
// starting value: 0
DAC1->DHR12R1 = 0;
}
/**
* @brief getADCval - calculate median value for `nch` channel
* @param nch - number of channel
* @return
*/
uint16_t getADCval(int 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];
int adval = (nch >= NUMBER_OF_ADC1_CHANNELS) ? NUMBER_OF_ADC2_CHANNELS : NUMBER_OF_ADC1_CHANNELS;
int addr = (nch >= NUMBER_OF_ADC1_CHANNELS) ? nch - NUMBER_OF_ADC2_CHANNELS + ADC2START: nch;
for(int i = 0; i < 9; ++i, addr += adval) // 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
}
// get voltage @input nch (V)
float getADCvoltage(int nch){
float v = getADCval(nch);
v *= getVdd();
v /= 4096.f; // 12bit ADC
return v;
}
// return MCU temperature (degrees of celsius)
float getMCUtemp(){
// make correction on Vdd value
int32_t ADval = getADCval(ADC_TS);
float temperature = (float) *TEMP30_CAL_ADDR - ADval;
temperature *= (110.f - 30.f);
temperature /= (float)(*TEMP30_CAL_ADDR - *TEMP110_CAL_ADDR);
temperature += 30.f;
return(temperature);
}
// return Vdd (V)
float getVdd(){
float vdd = ((float) *VREFINT_CAL_ADDR) * 3.3f; // 3.3V
vdd /= getADCval(ADC_VREF);
return vdd;
}

View File

@@ -0,0 +1,40 @@
/*
* This file is part of the ir-allsky project.
* Copyright 2025 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 <stm32f3.h>
#define NUMBER_OF_ADC1_CHANNELS (4)
#define NUMBER_OF_ADC2_CHANNELS (1)
// total number of channels - for array
#define NUMBER_OF_ADC_CHANNELS ((NUMBER_OF_ADC1_CHANNELS+NUMBER_OF_ADC2_CHANNELS))
// channels of ADC in array
#define ADC_AIN0 (0)
#define ADC_AIN1 (1)
#define ADC_TS (2)
#define ADC_VREF (3)
#define ADC_AIN5 (4)
// starting index of ADC2
#define ADC2START (9*NUMBER_OF_ADC1_CHANNELS)
void adc_setup();
float getMCUtemp();
float getVdd();
uint16_t getADCval(int nch);
float getADCvoltage(int nch);

Binary file not shown.

View File

@@ -18,6 +18,8 @@
#include "BMP280.h"
#include "hardware.h"
#include "mlxproc.h"
#include "mlx90640.h"
static bme280_t environment; // current measurements
@@ -49,17 +51,49 @@ TRUE_INLINE void iwdg_setup(){
TRUE_INLINE void gpio_setup(){
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; // for USART and LEDs
for(int i = 0; i < 10000; ++i) nop();
// USB - alternate function 14 @ pins PA11/PA12; SWD - AF0 @PA13/14
// USB - alternate function 14 @ pins PA11/PA12; SWD - AF0 @PA13/14; USB pullup - PA15
// PA6 - PWM for external heater (TIM3_CH1 or TIM16_CH1); PA7 - PWM propto (humidity - 50%)
GPIOA->AFR[0] = AFRf(2, 6) | AFRf(2, 7);
GPIOA->AFR[1] = AFRf(14, 11) | AFRf(14, 12);
GPIOA->MODER = MODER_AF(11) | MODER_AF(12) | MODER_AF(13) | MODER_AF(14) | MODER_O(15);
GPIOB->MODER = MODER_O(0) | MODER_O(1) | MODER_O(2);
GPIOA->MODER = MODER_AI(0) | MODER_AI(1) | MODER_AI(4) | MODER_AI(5) | MODER_AF(6) |
MODER_AF(7) | MODER_AF(11) | MODER_AF(12) | MODER_AF(13) | MODER_AF(14) | MODER_O(15);
// PB0 - PWM propto Text (<=20 - 0%, >=30 - 100%), PB1 - PWM propto (Text-Tsky) (<=-5 - 0%, >=+35 - 100%) PB2 - SPI_CS
GPIOB->AFR[0] = AFRf(2, 0) | AFRf(2, 1);
GPIOB->MODER = MODER_AF(0) | MODER_AF(1) | MODER_O(2);
pin_set(GPIOB, 1<<1);
SPI_CS_1();
}
// setup PWM (TIM3_CH1) for external heater management over optocoupler (active level - 0)
TRUE_INLINE void pwm_setup(){
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
TIM3->CR1 = TIM_CR1_ARPE;
TIM3->PSC = 71; // 72M/72 = 1MHz; PWMfreq=1M/100=10kHz
// PWM mode 1 (active -> inactive), enable buffering
TIM3->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE |
TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
TIM3->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE |
TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
TIM3->CCR1 = 0; TIM3->CCR2 = 0; TIM3->CCR3 = 0; TIM3->CCR4 = 0;
TIM3->ARR = PWM_CCR_MAX-1; // 8bit PWM from 0 to 99
TIM3->BDTR |= TIM_BDTR_MOE; // enable main output
// enable PWM output, all outputs active - low
TIM3->CCER = TIM_CCER_CC1P | TIM_CCER_CC1E | TIM_CCER_CC2P | TIM_CCER_CC2E |
TIM_CCER_CC3P | TIM_CCER_CC3E | TIM_CCER_CC4P | TIM_CCER_CC4E;
TIM3->CR1 |= TIM_CR1_CEN; // run timer
}
// change PWM value in percents; return 0 if `val` is bad or `ch` not 0..3
int setPWM(uint8_t ch, uint8_t val){
if(ch > 3 || val > PWM_CCR_MAX) return 0;
volatile uint32_t *CCRs = &(TIM3->CCR1);
CCRs[ch] = val;
return 1;
}
void hw_setup(){
gpio_setup();
pwm_setup();
#ifndef EBUG
iwdg_setup();
#endif
@@ -72,6 +106,21 @@ int bme_init(){
return 1;
}
// calculate mean sky temperature
static float Tsky(){
int nmeas = 0;
float Tacc = 0.f;
for(int n = 0; n < N_SENSORS; ++n){ // sum all sensors
float *im = mlx_getimage(n);
if(!im) continue;
for(int i = 0; i < MLX_PIXNO; ++i){
Tacc += im[i]; ++nmeas;
}
}
if(nmeas < MLX_PIXNO) return +1000.; // all very bad: no sensors found?
return (Tacc / (float)nmeas);
}
// process sensor and make measurements
void bme_process(){
static uint32_t Tmeas = 0;
@@ -87,7 +136,20 @@ void bme_process(){
environment.T = Temperature;
environment.P = Pressure;
environment.H = Humidity;
// set PWM duty propto humidity
float h = (Humidity - 50.f) * 2.f;
if(h < 0.f) h = 0.f; else if(h > 100.f) h = 100.f;
setPWM(PWM_CH_HUMIDITY, (uint8_t)h);
environment.Tmeas = Tms;
// set PWM duty propto external T
float t = (Temperature + 20.f) * 2.f;
if(t < 0.f) t = 0.f; else if(t > 100.f) t = 100.f;
setPWM(PWM_CH_TEXT, (uint8_t)t);
// set PWM propto skyqual
environment.Tsky = Tsky();
float q = (35.f - Temperature + environment.Tsky) * 2.5f;
if(q < 0.f) q = 0.f; else if(q > 100.f) q = 100.f;
setPWM(PWM_CH_TSKY, (uint8_t)q);
}
if(Tms - Tmeas > ENV_MEAS_PERIOD-1){
if(BMP280_start()) Tmeas = Tms;

View File

@@ -32,11 +32,26 @@
// interval of environment measurements, ms
#define ENV_MEAS_PERIOD (10000)
// External heater PWM: TIM3_CH1 or TIM16_CH1
// Max PWM CCR1 value (->1)
#define PWM_CCR_MAX (100)
// PWM channels (start from 0 - CH1)
// external heater
#define PWM_CH_HEATER (0)
// propto humidity (the higher - the brighter)
#define PWM_CH_HUMIDITY (1)
// propto external T (the higher - the brighter)
#define PWM_CH_TEXT (2)
// propto Tsky - Text (the higher - the brighter)
#define PWM_CH_TSKY (3)
typedef struct{
float T; // temperature, degC
float Tdew; // dew point, degC
float P; // pressure, Pa
float H; // humidity, percents
float Tsky; // mean Tsky, degC
// TODO: add here values of NTC on ADC channels 1/2
uint32_t Tmeas; // time of measurement
} bme280_t;
@@ -46,3 +61,4 @@ void hw_setup();
int bme_init();
void bme_process();
int get_environment(bme280_t *env);
int setPWM(uint8_t ch, uint8_t val);

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 17.0.1, 2025-10-05T00:09:36. -->
<!-- Written by QtCreator 17.0.1, 2025-10-06T23:24:05. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@@ -1,5 +1,7 @@
BMP280.c
BMP280.h
adc.c
adc.h
hardware.c
hardware.h
i2c.c

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adc.h"
#include "hardware.h"
#include "i2c.h"
#include "mlxproc.h"
@@ -44,12 +45,13 @@ int main(void){
}
USBPU_OFF();
hw_setup();
adc_setup();
i2c_setup(I2C_SPEED_400K);
bme_init();
USB_setup();
usart_setup(115200);
USBPU_ON();
uint32_t ctr = Tms, Tlastima[N_SESORS] = {0};
uint32_t ctr = Tms, Tlastima[N_SENSORS] = {0};
mlx_continue(); // init state machine
while(1){
IWDG->KR = IWDG_REFRESH;
@@ -77,7 +79,7 @@ int main(void){
}
}
mlx_process();
if(cartoon) for(int i = 0; i < N_SESORS; ++i){ // USB-only
if(cartoon) for(int i = 0; i < N_SENSORS; ++i){ // USB-only
uint32_t Tnow = mlx_lastimT(i);
if(Tnow != Tlastima[i]){
fp_t *im = mlx_getimage(i);

View File

@@ -41,15 +41,15 @@ extern volatile uint32_t Tms;
// current state and state before `stop` called
static mlx_state_t MLX_state = MLX_RELAX, MLX_oldstate = MLX_RELAX;
static int errctr = 0; // errors counter - cleared by mlx_continue
static uint32_t Tlastimage[N_SESORS] = {0};
static uint32_t Tlastimage[N_SENSORS] = {0};
// subpages and configs of all sensors
// 8320 bytes:
static int16_t imdata[N_SESORS][REG_IMAGEDATA_LEN];
static int16_t imdata[N_SENSORS][REG_IMAGEDATA_LEN];
// 8340 bytes:
static uint16_t confdata[N_SESORS][MLX_DMA_MAXLEN];
static uint8_t sens_addresses[N_SESORS] = {0x10<<1, 0x11<<1, 0x12<<1, 0x13<<1, 0x14<<1}; // addresses of all sensors (if 0 - omit this one)
static uint8_t sensaddr[N_SESORS];
static uint16_t confdata[N_SENSORS][MLX_DMA_MAXLEN];
static uint8_t sens_addresses[N_SENSORS] = {0x10<<1, 0x11<<1, 0x12<<1, 0x13<<1, 0x14<<1}; // addresses of all sensors (if 0 - omit this one)
static uint8_t sensaddr[N_SENSORS];
// get compile-time size: (gcc shows it in error message)
//char (*__kaboom)[sizeof( confdata )] = 1;
@@ -63,7 +63,7 @@ static int sensno = -1;
mlx_state_t mlx_state(){ return MLX_state; }
// set address
int mlx_setaddr(int n, uint8_t addr){
if(n < 0 || n > N_SESORS) return 0;
if(n < 0 || n > N_SENSORS) return 0;
if(addr > 0x7f) return 0;
sens_addresses[n] = addr << 1;
Tlastimage[n] = Tms; // refresh counter for autoreset I2C in case of error
@@ -106,8 +106,8 @@ static int nextsensno(int s){
return -1;
}
int next = s + 1;
for(; next < N_SESORS; ++next) if(sensaddr[next]) break;
if(next == N_SESORS) return nextsensno(-1); // roll to start
for(; next < N_SENSORS; ++next) if(sensaddr[next]) break;
if(next == N_SENSORS) return nextsensno(-1); // roll to start
D(i2str(next)); DB('('); D(i2str(s)); DB(')'); DN(" - new sensor number");
return next;
}
@@ -115,7 +115,7 @@ static int nextsensno(int s){
// count active sensors
int mlx_nactive(){
int N = 0;
for(int i = 0; i < N_SESORS; ++i) if(sensaddr[i]) ++N;
for(int i = 0; i < N_SENSORS; ++i) if(sensaddr[i]) ++N;
return N;
}
@@ -230,7 +230,7 @@ MLX90640_params *mlx_getparams(int n){
uint32_t mlx_lastimT(int n){ return Tlastimage[n]; }
fp_t *mlx_getimage(int n){
if(n < 0 || n >= N_SESORS || !sensaddr[n]) return NULL;
if(n < 0 || n >= N_SENSORS || !sensaddr[n]) return NULL;
MLX90640_params *p = get_parameters(confdata[n]);
if(!p) return NULL;
fp_t *ready_image = process_image(imdata[n]);

View File

@@ -23,7 +23,7 @@
#include "mlx90640.h"
// amount of sensors processing
#define N_SESORS (5)
#define N_SENSORS (5)
// maximal errors number to stop processing
#define MLX_MAX_ERRORS (11)

View File

@@ -20,6 +20,7 @@
#include <stm32f3.h>
#include <string.h>
#include "adc.h"
#include "hardware.h"
#include "i2c.h"
#include "mlxproc.h"
@@ -89,12 +90,16 @@ const char *const helpstring =
"i0..4 - setup I2C with speed 10k, 100k, 400k, 1M or 2M (experimental!)\n"
"p - pause MLX\n"
"s - stop MLX (and start from zero @ 'c')\n"
"A - get ADC values\n"
"C - \"cartoon\" mode on/off (show each new image) - USB only!!!\n"
"Dn - dump MLX parameters for sensor number n\n"
"Ia addr [n] - set device address for interactive work or (with n) change address of n'th sensor\n"
"Ir reg n - read n words from 16-bit register\n"
"Iw words - send words (hex/dec/oct/bin) to I2C\n"
"Is - scan I2C bus\n"
"M - get MCU temperature and Vdd value\n"
"O - set output of DAC (0..4095)\n"
"Px - set PWM output (0..100%) or get current value\n"
"Us - send string 's' to other interface\n"
;
@@ -140,7 +145,7 @@ static int getsensnum(const char *buf){
if(!buf || !*buf) return -1;
uint32_t num;
const char *nxt = getnum(buf, &num);
if(!nxt || nxt == buf || num >= N_SESORS) return -1;
if(!nxt || nxt == buf || num >= N_SENSORS) return -1;
return (int) num;
}
@@ -311,7 +316,7 @@ TRUE_INLINE void listactive(){
uint8_t *ids = mlx_activeids();
sendfun->S("Found "); sendfun->P('0'+N);
sendfun->S(" active sensors:"); N();
for(int i = 0; i < N_SESORS; ++i)
for(int i = 0; i < N_SENSORS; ++i)
if(ids[i]){
sendfun->S("SENSID");
sendfun->S(u2str(i)); sendfun->P('=');
@@ -331,6 +336,7 @@ TRUE_INLINE void getenv(){
bme280_t env;
if(!get_environment(&env)) sendfun->S("BADENVIRONMENT\n");
sendfun->S("TEMPERATURE="); sendfun->S(float2str(env.T, 2));
sendfun->S("\nSKYTEMPERATURE="); sendfun->S(float2str(env.Tsky, 2));
sendfun->S("\nPRESSURE_HPA="); sendfun->S(float2str(env.P/100.f, 2));
sendfun->S("\nPRESSURE_MM="); sendfun->S(float2str(env.P * 0.00750062f, 2));
sendfun->S("\nHUMIDITY="); sendfun->S(float2str(env.H, 2));
@@ -339,6 +345,43 @@ TRUE_INLINE void getenv(){
N();
}
TRUE_INLINE const char *DAC_chval(const char *buf){
uint32_t D;
const char *nxt = getnum(buf, &D);
if(!nxt || nxt == buf || D > 4095) return ERR;
DAC1->DHR12R1 = D;
return OK;
}
TRUE_INLINE void getADC(){
sendfun->S("AIN0="); sendfun->S(u2str(getADCval(ADC_AIN0)));
sendfun->S("\nAIN1="); sendfun->S(u2str(getADCval(ADC_AIN1)));
sendfun->S("\nAIN5="); sendfun->S(u2str(getADCval(ADC_AIN5)));
N();
}
TRUE_INLINE void getMCUvals(){
sendfun->S("MCUTEMP="); sendfun->S(float2str(getMCUtemp(), 2));
sendfun->S("\nMCUVDD="); sendfun->S(float2str(getVdd(), 2));
N();
}
TRUE_INLINE const char* setpwm(const char *buf){
uint32_t D;
if(!buf || !*buf){
sendfun->S("PWM1="); sendfun->S(u2str(TIM3->CCR1));
sendfun->S("\nPWM2="); sendfun->S(u2str(TIM3->CCR2));
sendfun->S("\nPWM3="); sendfun->S(u2str(TIM3->CCR3));
sendfun->S("\nPWM4="); sendfun->S(u2str(TIM3->CCR4));
N();
return NULL;
}
const char *nxt = getnum(buf, &D);
if(!nxt || nxt == buf || !setPWM(PWM_CH_HEATER, D)) return ERR;
return OK;
}
/**
* @brief parse_cmd - user string parser
* @param buf - user data
@@ -349,32 +392,32 @@ const char *parse_cmd(char *buf, int sendto){
if(!buf || !*buf) return NULL;
chsendfun(sendto);
if(buf[1]){
switch(*buf){ // "long" commands
switch(*buf++){ // "long" commands
case 'a':
return chhwaddr(buf + 1);
return chhwaddr(buf);
case 'd':
return drawimg(buf+1, 1);
return drawimg(buf, 1);
case 'g':
return drawimg(buf+1, 2);
return drawimg(buf, 2);
case 'i':
return setupI2C(buf + 1);
return setupI2C(buf);
case 'm':
return drawimg(buf+1, 0);
return drawimg(buf, 0);
case 't':
getimt(buf + 1); return NULL;
getimt(buf); return NULL;
case 'D':
dumpparams(buf + 1);
dumpparams(buf);
return NULL;
break;
case 'I':
buf = omit_spaces(buf + 1);
buf = omit_spaces(buf);
switch(*buf){
case 'a':
return chaddr(buf + 1);
return chaddr(buf);
case 'r':
return rdI2C(buf + 1);
return rdI2C(buf);
case 'w':
return wrI2C(buf + 1);
return wrI2C(buf);
case 's':
i2c_init_scan_mode();
return OK;
@@ -382,16 +425,23 @@ const char *parse_cmd(char *buf, int sendto){
return ERR;
}
break;
case 'O':
return DAC_chval(buf);
case 'P':
return setpwm(buf);
case 'U':
if(sendto == SEND_USB) chsendfun(SEND_USART);
else chsendfun(SEND_USB);
if(sendfun->S(buf + 1) && N()) return OK;
if(sendfun->S(buf) && N()) return OK;
return ERR;
default:
return ERR;
}
}
switch(*buf){ // "short" (one letter) commands
case 'A':
getADC();
break;
case 'c':
mlx_continue(); return OK;
break;
@@ -416,6 +466,11 @@ const char *parse_cmd(char *buf, int sendto){
case 'G':
getst();
break;
case 'M':
getMCUvals();
break;
case 'P':
return setpwm(NULL);
case 'R':
NVIC_SystemReset();
break;

View File

@@ -1,2 +1,2 @@
#define BUILD_NUMBER "36"
#define BUILD_DATE "2025-10-05"
#define BUILD_NUMBER "44"
#define BUILD_DATE "2025-10-06"

50
F3:F303/STM32F303C.md Normal file
View File

@@ -0,0 +1,50 @@
| Pin # | Pin name | function | settings | comment |
| --------- | ------------- | ------------ | ------------ | ------------ |
| 1 | VBAT | | | |
| 2 | PC13 | | | |
| 3 | PC14 | | | |
| 4 | PC15 | | | |
| 5 | OCS_IN | | | |
| 6 | OSC_OUT | | | |
| 7 | NRST | | | |
| 8 | VSSA | | | |
| 9 | VDDA | | | |
| 10 | PA0 | | | |
| 11 | PA1 | | | |
| 12 | PA2 | | | |
| 13 | PA3 | | | |
| 14 | PA4 | | | |
| 15 | PA5 | | | |
| 16 | PA6 | | | |
| 17 | PA7 | | | |
| 18 | PB0 | | | |
| 19 | PB1 | | | |
| 20 | PB2 | | | |
| 21 | PB10 | | | |
| 22 | PB11 | | | |
| 23 | VSS1 | | | |
| 24 | VDD1 | | | |
| 25 | PB12 | | | |
| 26 | PB13 | | | |
| 27 | PB14 | | | |
| 28 | PB15 | | | |
| 29 | PA8 | | | |
| 30 | PA9 | | | |
| 31 | PA10 | | | |
| 32 | PA11 | | | |
| 33 | PA12 | | | |
| 34 | PA13 | | | |
| 35 | VSS2 | | | |
| 36 | VDD2 | | | |
| 37 | PA14 | | | |
| 38 | PA15 | | | |
| 39 | PB3 | | | |
| 40 | PB4 | | | |
| 41 | PB5 | | | |
| 42 | PB6 | | | |
| 43 | PB7 | | | |
| 44 | BOOT0 | | | |
| 45 | PB8 | | | |
| 46 | PB9 | | | |
| 47 | VSS3 | | | |
| 48 | VDD3 | | | |