Add some more

This commit is contained in:
Edward Emelianov 2023-02-16 20:08:27 +03:00
parent 617daf62c7
commit 2cc06884b4
16 changed files with 758 additions and 28 deletions

View File

@ -217,5 +217,8 @@ _SW_ used as debugging/sewing; also (I remember about USB pullup only after end
| 11 | PF10 | M0 EN | slow out | l-s 0 or CS of SPI | | 11 | PF10 | M0 EN | slow out | l-s 0 or CS of SPI |
# Debug with SWD ## DMA usage
Press both _BTN0_ and _BTN1_ and turn on device. In this case the USB won't be activated and you can use SWD. If both these buttons not pressed on poweron, device will use _SWDIO_ as USB pullup, so SWD connection in this mode won't be available.
* ADC1 - DMA1_ch1
* ADC2 - DMA2_ch1

153
F3:F303/Multistepper/adc.c Normal file
View File

@ -0,0 +1,153 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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..3 - AIN0..3 (ADC1_IN1..4)
* 4 - internal Tsens - ADC1_IN16
* 5 - Vref - ADC1_IN18
* ADC2:
* 6 - AIN4 (ADC2_IN1)
* 7 - AIN5 (ADC2_IN10)
*/
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,3,4,16,18; ADC2: channels 1,10
ADC1->SMPR1 = ADC_SMPR1_SMP1 | ADC_SMPR1_SMP2 | ADC_SMPR1_SMP3 | ADC_SMPR1_SMP4;
ADC1->SMPR2 = ADC_SMPR2_SMP16 | ADC_SMPR2_SMP18;
// 4 conversions in group: 1->2->3->4->16->18
ADC1->SQR1 = (1<<6) | (2<<12) | (3<<18) | (4<<24) | (NUMBER_OF_ADC1_CHANNELS-1);
ADC1->SQR2 = (16<<6) | (18<<12);
ADC2->SMPR1 = ADC_SMPR1_SMP1;
ADC2->SMPR2 = ADC_SMPR2_SMP10;
ADC2->SQR1 = (1<<6) | (10<<12) | (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);
}
/**
* @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,45 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 (6)
#define NUMBER_OF_ADC2_CHANNELS (2)
// total number of channels - for array
#define NUMBER_OF_ADC_CHANNELS ((NUMBER_OF_ADC1_CHANNELS+NUMBER_OF_ADC2_CHANNELS))
// AIN0-3: ADC1_IN1..4; AIN4 - ADC2_IN1; AIN5 - ADC2_IN10
// channels of ADC in array
#define ADC_AIN0 (0)
#define ADC_AIN1 (1)
#define ADC_AIN2 (2)
#define ADC_AIN3 (3)
#define ADC_TS (4)
#define ADC_VREF (5)
#define ADC_AIN4 (6)
#define ADC_AIN5 (7)
// 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);

View File

@ -0,0 +1,90 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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 "buttons.h"
#include "hardware.h"
typedef struct{
keyevent event; // current key event
int16_t counter; // press/release counter
uint32_t lastTms; // time of last event change
} keybase;
static keybase allkeys[BTNSNO] = {0}; // array for buttons' states
uint32_t lastUnsleep = 0; // last keys activity time
void process_keys(){
static uint32_t lastT = 0;
if(Tms == lastT) return;
uint16_t d = (uint16_t)(Tms - lastT);
lastT = Tms;
for(int i = 0; i < BTNSNO; ++i){
keybase *k = &allkeys[i];
keyevent e = k->event;
if(BTN_state(i)){ // key is in pressed state
switch(e){
case EVT_NONE: // just pressed
case EVT_RELEASE:
if((k->counter += d) > PRESSTHRESHOLD){
k->event = EVT_PRESS;
}
break;
case EVT_PRESS: // hold
if((k->counter += d)> HOLDTHRESHOLD){
k->event = EVT_HOLD;
}
break;
default:
break;
}
}else{ // released
if(e == EVT_PRESS || e == EVT_HOLD){ // released
if(k->counter > PRESSTHRESHOLD) k->counter = PRESSTHRESHOLD;
else if((k->counter -= d) < 0){
k->event = EVT_RELEASE; // button released
}
}
}
if(e != k->event){
k->lastTms = Tms;
lastUnsleep = Tms;
}
}
}
/**
* @brief keystate - curent key state
* @param k - key number
* @param T - last event changing time
* @return key event
*/
keyevent keystate(uint8_t k, uint32_t *T){
if(k >= BTNSNO) return EVT_NONE;
keyevent evt = allkeys[k].event;
// change state `release` to `none` after 1st check
if(evt == EVT_RELEASE) allkeys[k].event = EVT_NONE;
if(T) *T = allkeys[k].lastTms;
return evt;
}
// getter of keyevent for allkeys[]
keyevent keyevt(uint8_t k){
if(k >= BTNSNO) return EVT_NONE;
return allkeys[k].event;
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the multistepper project.
* Copyright 2023 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>
// threshold in ms for press/hold
#define PRESSTHRESHOLD (9)
#define HOLDTHRESHOLD (199)
// events
typedef enum{
EVT_NONE, // no events with given key
EVT_PRESS, // pressed (hold more than PRESSTHRESHOLD ms)
EVT_HOLD, // hold more than HOLDTHRESHOLD ms
EVT_RELEASE // released after press or hold state
} keyevent;
extern uint32_t lastUnsleep; // last keys activity time
void process_keys();
keyevent keystate(uint8_t k, uint32_t *T);
keyevent keyevt(uint8_t k);

View File

@ -17,6 +17,8 @@
*/ */
#include "can.h" #include "can.h"
#include "commonproto.h"
#include "flash.h"
#include "hardware.h" #include "hardware.h"
#include "strfunc.h" #include "strfunc.h"
#include "usb.h" #include "usb.h"
@ -302,6 +304,66 @@ uint32_t CAN_speed(){
return oldspeed; return oldspeed;
} }
static void formerr(CAN_message *msg, errcodes err){
if(msg->length < 4) msg->length = 4;
msg->data[3] = (uint8_t)err;
}
/**
* @brief parseCANcommand - parser
* @param msg - incoming message @ my CANID
* FORMAT:
* 0 1 2 3 4 5 6 7
* [CMD][PAR][errcode][VALUE]
* CMD - uint16_t, PAR - uint8_t, errcode - one of CAN_errcodes, VALUE - int32_t
* `errcode` of incoming message doesn't matter
*/
TRUE_INLINE void parseCANcommand(CAN_message *msg){
int N = 1000;
// we don't check msg here as it cannot be NULL
#ifdef EBUG
DBG("Get data");
for(int i = 0; i < msg->length; ++i){
USB_sendstr(uhex2str(msg->data[i])); USB_putbyte(' ');
}
newline();
#endif
if(msg->length == 0) goto sendmessage; // PING
uint16_t Index = *(uint16_t*)msg->data;
#ifdef EBUG
USB_sendstr("Index = "); USB_sendstr(u2str(Index)); newline();
#endif
if(Index >= CCMD_AMOUNT){
formerr(msg, ERR_BADCMD);
goto sendmessage;
}
msg->data[3] = ERR_OK;
uint8_t par = msg->data[2];
if(par & 0x80){
formerr(msg, ERR_BADPAR);
goto sendmessage;
}
int32_t *val = (int32_t *)(&msg->data[4]);
if(msg->length == 8) par |= 0x80;
else if(msg->length == 2) par = CANMESG_NOPAR; // no parameter
else if(msg->length != 3){ // wrong length
formerr(msg, ERR_WRONGLEN);
goto sendmessage;
}
#ifdef EBUG
USB_sendstr("Run command\n");
#endif
errcodes ec = cancmdlist[Index](par, val);
if(ec != ERR_OK){
formerr(msg, ec);
}else{
msg->length = 8;
}
sendmessage:
while(CAN_BUSY == CAN_send(msg->data, msg->length, the_conf.CANID))
if(--N == 0) break;
}
static void can_process_fifo(uint8_t fifo_num){ static void can_process_fifo(uint8_t fifo_num){
if(fifo_num > 1) return; if(fifo_num > 1) return;
CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num]; CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num];
@ -348,6 +410,7 @@ static void can_process_fifo(uint8_t fifo_num){
dat[0] = lb & 0xff; dat[0] = lb & 0xff;
} }
} }
if(msg.ID == the_conf.CANID) parseCANcommand(&msg);
if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later
*RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message *RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message
} }

View File

@ -16,7 +16,10 @@
* 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 "buttons.h"
#include "commonproto.h" #include "commonproto.h"
#include "hardware.h"
#include "hdr.h" #include "hdr.h"
#include "proto.h" #include "proto.h"
#include "usb.h" #include "usb.h"
@ -45,16 +48,51 @@ static errcodes cu_time(uint8_t par, int32_t *val){
return ERR_OK; return ERR_OK;
} }
errcodes cu_mcut(uint8_t par, int32_t *val){
NOPARCHK(par);
float f = getMCUtemp();
*val = (uint32_t)(f*10.f);
return ERR_OK;
}
errcodes cu_mcuvdd(uint8_t par, int32_t *val){
NOPARCHK(par);
float f = getVdd();
*val = (uint32_t)(f*10.f);
return ERR_OK;
}
errcodes cu_adc(uint8_t par, int32_t *val){
uint8_t n = PARBASE(par);
if(n > NUMBER_OF_ADC_CHANNELS-1) return ERR_BADPAR;
*val = getADCval(n);
return ERR_OK;
}
// NON-STANDARD COMMAND!!!!!!!
// errcode == keystate, value = last time!!!!
errcodes cu_button(uint8_t par, int32_t *val){
uint8_t n = PARBASE(par);
if(n > BTNSNO-1){
*val = CANMESG_NOPAR; // the only chance to understand error
return ERR_BADPAR;
}
return (uint8_t) keystate(n, (uint32_t*)val);
}
// par - motor number, val - 0/1 for ESW0/1
errcodes cu_esw(uint8_t par, int32_t *val){
uint8_t n = PARBASE(par), l = *val;
if(n >= MOTORSNO || l > 1){
*val = CANMESG_NOPAR; // the only chance to understand error
return ERR_BADPAR;
}
*val = ESW_state(n, l);
return ERR_OK;
}
errcodes cu_abspos(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_abspos(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_accel(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_accel(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_adc(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_button(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_canid(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_diagn(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_diagn(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_emstop(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_emstop(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_eraseflash(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_eraseflash(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_esw(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_eswreact(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_eswreact(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_goto(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_goto(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_gotoz(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_gotoz(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
@ -62,8 +100,6 @@ errcodes cu_gpio(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_gpioconf(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_gpioconf(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_maxspeed(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_maxspeed(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_maxsteps(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_maxsteps(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_mcut(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_mcuvdd(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_microsteps(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_microsteps(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_minspeed(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_minspeed(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_motflags(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_motflags(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
@ -81,7 +117,7 @@ errcodes cu_udata(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
errcodes cu_usartstatus(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;} errcodes cu_usartstatus(_U_ uint8_t par, _U_ int32_t *val){return ERR_BADCMD;}
const fpointer cmdlist[CCMD_AMOUNT] = { const fpointer cancmdlist[CCMD_AMOUNT] = {
// different commands // different commands
[CCMD_PING] = cu_ping, [CCMD_PING] = cu_ping,
[CCMD_RELAY] = cu_nosuchfn, [CCMD_RELAY] = cu_nosuchfn,

View File

@ -16,17 +16,79 @@
* 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 "flash.h"
#include "hardware.h" #include "hardware.h"
// Buttons: PA9, PA10, PF6, PD3, PD4, PD5, pullup (active - 0) // Buttons: PA9, PA10, PF6, PD3, PD4, PD5, pullup (active - 0)
volatile GPIO_TypeDef* const BTNports[BTNSNO] = {GPIOA, GPIOA, GPIOF, GPIOD, GPIOD, GPIOD}; volatile GPIO_TypeDef* const BTNports[BTNSNO] = {GPIOA, GPIOA, GPIOF, GPIOD, GPIOD, GPIOD};
const uint32_t BTNpins[BTNSNO] = {1<<9, 1<<10, 1<<6, 1<<3, 1<<4, 1<<5}; const uint32_t BTNpins[BTNSNO] = {1<<9, 1<<10, 1<<6, 1<<3, 1<<4, 1<<5};
// Limit switches: 0:PC14/PC13, 1:PB9/PB7, 2:PD7/PD6, 3:PC10/PC11, 4:PC7/PD15,
// 5:PD11/PD10, 6:PE11/PE10, 7:PE7/PE8
volatile GPIO_TypeDef *ESWports[MOTORSNO][ESWNO] = {
{GPIOC, GPIOC}, // 0
{GPIOB, GPIOB},
{GPIOD, GPIOD}, // 2
{GPIOC, GPIOC},
{GPIOC, GPIOD}, // 4
{GPIOD, GPIOD},
{GPIOE, GPIOE}, // 6
{GPIOE, GPIOE},
};
const uint32_t ESWpins[MOTORSNO][ESWNO] = {
{1<<14, 1<<13},
{1<<9, 1<<7}, // 1
{1<<7, 1<<6},
{1<<10, 1<<11}, // 3
{1<<7, 1<<15},
{1<<11, 1<<10}, // 5
{1<<11, 1<<10},
{1<<7, 1<<8}, // 7
};
// motors: DIR/EN
// EN: 0:PF10, 1:PE1, 2:PB6, 3:PD2, 4:PC9, 5:PD14, 6:PE13, 7:PB1
volatile GPIO_TypeDef *ENports[MOTORSNO] = {
GPIOF, GPIOE, GPIOB, GPIOD, GPIOC, GPIOD, GPIOE, GPIOB};
const uint32_t ENpins[MOTORSNO] = {
1<<10, 1<<1, 1<<6, 1<<2, 1<<9, 1<<14, 1<<13, 1<<1};
// DIR: 0:PC15, 1:PE0, 2:PB4, 3:PC12, 4:PC8, 5:PD13, 6:PE12, 7:PB2
volatile GPIO_TypeDef *DIRports[MOTORSNO] = {
GPIOC, GPIOE, GPIOB, GPIOC, GPIOC, GPIOD, GPIOE, GPIOB};
const uint32_t DIRpins[MOTORSNO] = {
1<<15, 1<<0, 1<<4, 1<<12, 1<<8, 1<<13, 1<<12, 1<<2};
// timers for motors: 0:t15c1, 1:t16c1, 2:t17c1, 3:t2ch1, 4:t8ch1, 5:t4c1, 6:t1c1, 7:t3c3
volatile TIM_TypeDef *mottimers[MOTORSNO] = {
TIM15, TIM16, TIM17, TIM2, TIM8, TIM4, TIM1, TIM3};
const uint8_t mottchannels[MOTORSNO] = {1,1,1,1,1,1,1,3};
static IRQn_Type motirqs[MOTORSNO] = {
TIM15_IRQn, TIM16_IRQn, TIM17_IRQn, TIM2_IRQn, TIM8_CC_IRQn, TIM4_IRQn, TIM1_CC_IRQn, TIM3_IRQn};
// state 1 - pressed, 0 - released (pin active is zero)
uint8_t ESW_state(uint8_t MOTno, uint8_t ESWno){
uint8_t val = ((ESWports[MOTno][ESWno]->IDR & ESWpins[MOTno][ESWno]) ? 0 : 1);
if(the_conf.motflags[ESWno].eswinv) val = !val;
return val;
}
// calculate MSB position of value `val`
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
static const uint8_t bval[] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3};
uint8_t MSB(uint16_t val){
register uint8_t r = 0;
if(val & 0xff00){r += 8; val >>= 8;}
if(val & 0x00f0){r += 4; val >>= 4;}
return ((uint8_t)r + bval[val]);
}
// setup here ALL GPIO pins (due to table in Readme.md) // setup here ALL GPIO pins (due to table in Readme.md)
// leave SWD as default AF; high speed for CLK and some other AF; med speed for some another AF // leave SWD as default AF; high speed for CLK and some other AF; med speed for some another AF
TRUE_INLINE void gpio_setup(){ TRUE_INLINE void gpio_setup(){
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN
| RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOFEN; | RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOFEN;
// enable timers: 1,2,3,4,8,15,16,17
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM4EN;
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM8EN | RCC_APB2ENR_TIM15EN | RCC_APB2ENR_TIM16EN | RCC_APB2ENR_TIM17EN;
for(int i = 0; i < 10000; ++i) nop(); for(int i = 0; i < 10000; ++i) nop();
GPIOA->ODR = 0; GPIOA->ODR = 0;
GPIOA->AFR[0] = AFRf(5, 5) | AFRf(5, 6) | AFRf(5, 7); GPIOA->AFR[0] = AFRf(5, 5) | AFRf(5, 6) | AFRf(5, 7);
@ -111,10 +173,78 @@ TRUE_INLINE void iwdg_setup(){
} }
#endif #endif
// motor's PWM
static void setup_mpwm(int i){
volatile TIM_TypeDef *TIM = mottimers[i];
TIM->CR1 = TIM_CR1_ARPE; // buffered ARR
TIM->PSC = MOTORTIM_PSC; // 16MHz
// PWM mode 1 (active -> inactive)
uint8_t n = mottchannels[i];
switch(n){
case 1:
TIM->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;
break;
case 2:
TIM->CCMR1 = TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1;
break;
case 3:
TIM->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1;
break;
default:
TIM->CCMR2 = TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1;
}
#if MOTORTIM_ARRMIN < 5
#error "change the code!"
#endif
TIM->CCR1 = MOTORTIM_ARRMIN - 3; // ~10us for pulse duration
TIM->ARR = 0xffff;
// TIM->EGR = TIM_EGR_UG; // generate update to refresh ARR
TIM->BDTR |= TIM_BDTR_MOE; // enable main output
TIM->CCER = 1<<((n-1)*4); // turn it on, active high
TIM->DIER = 1<<n; // allow CC interrupt (we should count steps)
NVIC_EnableIRQ(motirqs[i]);
}
void hw_setup(){ void hw_setup(){
gpio_setup(); gpio_setup();
for(int i = 0; i < MOTORSNO; ++i) setup_mpwm(i);
#ifndef EBUG #ifndef EBUG
iwdg_setup(); iwdg_setup();
#endif #endif
} }
// timers for motors: 0:t15c1, 1:t16c1, 2:t17c1, 3:t2ch1, 4:t8ch1, 5:t4c1, 6:t1c1, 7:t3c3
void tim1_cc_isr(){
// addmicrostep(6);
TIM1->SR = 0;
}
void tim2_isr(){
// addmicrostep(3);
TIM2->SR = 0;
}
void tim3_isr(){
// addmicrostep(7);
TIM3->SR = 0;
}
void tim4_isr(){
// addmicrostep(5);
TIM4->SR = 0;
}
void tim8_cc_isr(){
// addmicrostep(4);
TIM8->SR = 0;
}
void tim1_brk_tim15_isr(){
// addmicrostep(0);
TIM15->SR = 0;
}
void tim1_up_tim16_isr(){
// addmicrostep(1);
TIM16->SR = 0;
}
void tim1_trg_com_tim17_isr(){
// addmicrostep(2);
TIM17->SR = 0;
}

View File

@ -20,6 +20,11 @@
#include <stm32f3.h> #include <stm32f3.h>
// motors' timer PSC = PCLK/Tfreq - 1, Tfreq=16MHz
#define MOTORTIM_PSC (2)
// minimal ARR value - 99 for 5000 steps per second @ 32 microsteps/step
#define MOTORTIM_ARRMIN (99)
// USB pullup: PA8 // USB pullup: PA8
#define USBPU_port GPIOA #define USBPU_port GPIOA
#define USBPU_pin (1<<8) #define USBPU_pin (1<<8)
@ -40,7 +45,16 @@ extern const uint32_t BTNpins[BTNSNO];
// motors amount // motors amount
#define MOTORSNO (8) #define MOTORSNO (8)
// Limit switches: 2 for each motor
#define ESWNO (2)
// ESW ports & pins
extern volatile GPIO_TypeDef *ESWports[MOTORSNO][ESWNO];
extern const uint32_t ESWpins[MOTORSNO][ESWNO];
extern volatile uint32_t Tms; extern volatile uint32_t Tms;
uint8_t ESW_state(uint8_t MOTno, uint8_t ESWno);
uint8_t MSB(uint16_t val);
void hw_setup(); void hw_setup();

View File

@ -16,6 +16,8 @@
* 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 "buttons.h"
#include "can.h" #include "can.h"
#include "flash.h" #include "flash.h"
#include "hardware.h" #include "hardware.h"
@ -42,8 +44,9 @@ int main(void){
hw_setup(); // GPIO, ADC, timers, watchdog etc. hw_setup(); // GPIO, ADC, timers, watchdog etc.
USBPU_OFF(); // make a reconnection USBPU_OFF(); // make a reconnection
USB_setup(); USB_setup();
USBPU_ON();
CAN_setup(the_conf.CANspeed); CAN_setup(the_conf.CANspeed);
adc_setup();
USBPU_ON();
uint32_t ctr = 0; uint32_t ctr = 0;
CAN_message *can_mesg; CAN_message *can_mesg;
while(1){ while(1){
@ -79,5 +82,6 @@ int main(void){
const char *ans = cmd_parser(inbuff); const char *ans = cmd_parser(inbuff);
if(ans) USB_sendstr(ans); if(ans) USB_sendstr(ans);
} }
process_keys();
} }
} }

View File

@ -1,3 +1,7 @@
adc.c
adc.h
buttons.c
buttons.h
can.c can.c
can.h can.h
commonproto.c commonproto.c

View File

@ -19,6 +19,8 @@
#include <stm32f3.h> #include <stm32f3.h>
#include <string.h> #include <string.h>
#include "adc.h"
#include "buttons.h"
#include "can.h" #include "can.h"
#include "flash.h" #include "flash.h"
#include "hardware.h" #include "hardware.h"
@ -417,11 +419,29 @@ int fn_dumperr(_U_ uint32_t hash, _U_ char *args){ // "dumperr" (1223989764)
return RET_GOOD; return RET_GOOD;
} }
int fn_canid(_U_ uint32_t hash, char *args){ // "canid" (2040257924)
if(args && *args){
int good = FALSE;
uint32_t N;
const char *eq = getnum(args, &N);
if(eq != args && N < 0xfff){
the_conf.CANID = (uint16_t)N;
CAN_reinit(the_conf.CANspeed);
good = TRUE;
}
if(!good) USB_sendstr("CANID setter format: `canid=ID`, ID is 11bit\n");
}
USB_sendstr("canid="); USB_sendstr(uhex2str(the_conf.CANID));
newline();
return RET_GOOD;
}
static int canusb_function(uint32_t hash, char *args){ static int canusb_function(uint32_t hash, char *args){
errcodes e = ERR_BADCMD; errcodes e = ERR_BADCMD;
uint32_t N; uint32_t N;
int32_t val = 0; int32_t val = 0;
uint8_t par = CANMESG_NOPAR; uint8_t par = CANMESG_NOPAR;
float f;
if(*args){ if(*args){
const char *n = getnum(args, &N); const char *n = getnum(args, &N);
if(n != args){ // get parameter if(n != args){ // get parameter
@ -444,6 +464,63 @@ static int canusb_function(uint32_t hash, char *args){
case CMD_PING: case CMD_PING:
e = cu_ping(par, &val); e = cu_ping(par, &val);
break; break;
case CMD_MCUT:
f = getMCUtemp();
USB_sendstr("T=");
USB_sendstr(float2str(f, 1));
newline();
return RET_GOOD;
break;
case CMD_MCUVDD:
f = getVdd();
USB_sendstr("VDD=");
USB_sendstr(float2str(f, 1));
newline();
return RET_GOOD;
break;
case CMD_ADC:
par = PARBASE(par);
if(par >= NUMBER_OF_ADC_CHANNELS){
USB_sendstr("Wrong channel number\n");
return RET_BAD;
}
USB_sendstr("ADC"); USB_putbyte('0'+par);
USB_putbyte('='); USB_sendstr(u2str(getADCval(par)));
f = getADCvoltage(par);
USB_sendstr("\nADCv");USB_putbyte('0'+par);
USB_putbyte('='); USB_sendstr(float2str(f, 1));
newline();
return RET_GOOD;
break;
case CMD_BUTTON:
e = cu_button(par, &val);
if(val == CANMESG_NOPAR){
USB_sendstr("Wrong button number\n");
return RET_BAD;
}
const char *kstate = "none";
switch(e){
case EVT_PRESS:
kstate = "press";
break;
case EVT_HOLD:
kstate = "hold";
break;
case EVT_RELEASE:
kstate = "release";
break;
default:
break;
}
USB_sendstr("KEY"); USB_putbyte('0'+PARBASE(par));
USB_putbyte('='); USB_sendstr(kstate);
USB_sendstr("KEYTIME="); USB_sendstr(u2str(val));
newline();
return RET_GOOD;
break;
case CMD_ESW:
e = cu_esw(par, &val);
break;
default: default:
break; break;
} }
@ -464,41 +541,40 @@ static int canusb_function(uint32_t hash, char *args){
#define AL __attribute__ ((alias ("canusb_function"))) #define AL __attribute__ ((alias ("canusb_function")))
// COMMON with CAN // COMMON with CAN
int fn_ping(_U_ uint32_t hash, _U_ char *args) AL; //* "ping" (10561715) int fn_ping(_U_ uint32_t hash, _U_ char *args) AL; // "ping" (10561715)
// not realized yet // not realized yet
int fn_abspos(_U_ uint32_t hash, _U_ char *args) AL; //* "abspos" (3056382221) int fn_abspos(_U_ uint32_t hash, _U_ char *args) AL; //* "abspos" (3056382221)
int fn_accel(_U_ uint32_t hash, _U_ char *args) AL; //* "accel" (1490521981) int fn_accel(_U_ uint32_t hash, _U_ char *args) AL; //* "accel" (1490521981)
int fn_adc(_U_ uint32_t hash, _U_ char *args) AL; //* "adc" (2963026093) int fn_adc(_U_ uint32_t hash, _U_ char *args) AL; // "adc" (2963026093)
int fn_button(_U_ uint32_t hash, _U_ char *args) AL; //* "button" (1093508897) int fn_button(_U_ uint32_t hash, _U_ char *args) AL; // "button" (1093508897)
int fn_canid(_U_ uint32_t hash, _U_ char *args) AL; // "canid" (2040257924) int fn_diagn(_U_ uint32_t hash, _U_ char *args) AL; //* "diagn" (2334137736)
int fn_diagn(_U_ uint32_t hash, _U_ char *args) AL; // "diagn" (2334137736)
int fn_emstop(_U_ uint32_t hash, _U_ char *args) AL; //* "emstop" (2965919005) int fn_emstop(_U_ uint32_t hash, _U_ char *args) AL; //* "emstop" (2965919005)
int fn_eraseflash(_U_ uint32_t hash, _U_ char *args) AL; // "eraseflash" (3177247267) int fn_eraseflash(_U_ uint32_t hash, _U_ char *args) AL; //* "eraseflash" (3177247267)
int fn_esw(_U_ uint32_t hash, _U_ char *args) AL; //* "esw" (2963094612) int fn_esw(_U_ uint32_t hash, _U_ char *args) AL; // "esw" (2963094612)
int fn_eswreact(_U_ uint32_t hash, _U_ char *args) AL; //* "eswreact" (1614224995) int fn_eswreact(_U_ uint32_t hash, _U_ char *args) AL; //* "eswreact" (1614224995)
int fn_goto(_U_ uint32_t hash, _U_ char *args) AL; // "goto" (4286309438) int fn_goto(_U_ uint32_t hash, _U_ char *args) AL; //* "goto" (4286309438)
int fn_gotoz(_U_ uint32_t hash, _U_ char *args) AL; //* "gotoz" (3178103736) int fn_gotoz(_U_ uint32_t hash, _U_ char *args) AL; //* "gotoz" (3178103736)
int fn_gpio(_U_ uint32_t hash, _U_ char *args) AL; //* "gpio" (4286324660) int fn_gpio(_U_ uint32_t hash, _U_ char *args) AL; //* "gpio" (4286324660)
int fn_gpioconf(_U_ uint32_t hash, _U_ char *args) AL; // "gpioconf" (1309721562) int fn_gpioconf(_U_ uint32_t hash, _U_ char *args) AL; //* "gpioconf" (1309721562)
int fn_maxspeed(_U_ uint32_t hash, _U_ char *args) AL; //* "maxspeed" (1498078812) int fn_maxspeed(_U_ uint32_t hash, _U_ char *args) AL; //* "maxspeed" (1498078812)
int fn_maxsteps(_U_ uint32_t hash, _U_ char *args) AL; //* "maxsteps" (1506667002) int fn_maxsteps(_U_ uint32_t hash, _U_ char *args) AL; //* "maxsteps" (1506667002)
int fn_mcut(_U_ uint32_t hash, _U_ char *args) AL; //* "mcut" (4022718) int fn_mcut(_U_ uint32_t hash, _U_ char *args) AL; // "mcut" (4022718)
int fn_mcuvdd(_U_ uint32_t hash, _U_ char *args) AL; //* "mcuvdd" (2517587080) int fn_mcuvdd(_U_ uint32_t hash, _U_ char *args) AL; // "mcuvdd" (2517587080)
int fn_microsteps(_U_ uint32_t hash, _U_ char *args) AL; //* "microsteps" (3974395854) int fn_microsteps(_U_ uint32_t hash, _U_ char *args) AL; //* "microsteps" (3974395854)
int fn_minspeed(_U_ uint32_t hash, _U_ char *args) AL; //* "minspeed" (3234848090) int fn_minspeed(_U_ uint32_t hash, _U_ char *args) AL; //* "minspeed" (3234848090)
int fn_motflags(_U_ uint32_t hash, _U_ char *args) AL; //* "motflags" (2153634658) int fn_motflags(_U_ uint32_t hash, _U_ char *args) AL; //* "motflags" (2153634658)
int fn_motmul(_U_ uint32_t hash, _U_ char *args) AL; // "motmul" (1543400099) int fn_motmul(_U_ uint32_t hash, _U_ char *args) AL; //* "motmul" (1543400099)
int fn_motreinit(_U_ uint32_t hash, _U_ char *args) AL; //* "motreinit" (199682784) int fn_motreinit(_U_ uint32_t hash, _U_ char *args) AL; //* "motreinit" (199682784)
int fn_relpos(_U_ uint32_t hash, _U_ char *args) AL; //* "relpos" (1278646042) int fn_relpos(_U_ uint32_t hash, _U_ char *args) AL; //* "relpos" (1278646042)
int fn_relslow(_U_ uint32_t hash, _U_ char *args) AL; //* "relslow" (1742971917) int fn_relslow(_U_ uint32_t hash, _U_ char *args) AL; //* "relslow" (1742971917)
int fn_saveconf(_U_ uint32_t hash, _U_ char *args) AL; //* "saveconf" (141102426) int fn_saveconf(_U_ uint32_t hash, _U_ char *args) AL; //* "saveconf" (141102426)
int fn_screen(_U_ uint32_t hash, _U_ char *args) AL; // "screen" (2100809349) int fn_screen(_U_ uint32_t hash, _U_ char *args) AL; //* "screen" (2100809349)
int fn_speedlimit(_U_ uint32_t hash, _U_ char *args) AL; //* "speedlimit" (1654184245) int fn_speedlimit(_U_ uint32_t hash, _U_ char *args) AL; //* "speedlimit" (1654184245)
int fn_state(_U_ uint32_t hash, _U_ char *args) AL; //* "state" (2216628902) int fn_state(_U_ uint32_t hash, _U_ char *args) AL; //* "state" (2216628902)
int fn_stop(_U_ uint32_t hash, _U_ char *args) AL; //* "stop" (17184971) int fn_stop(_U_ uint32_t hash, _U_ char *args) AL; //* "stop" (17184971)
int fn_tmcbus(_U_ uint32_t hash, _U_ char *args) AL; // "tmcbus" (1906135955) int fn_tmcbus(_U_ uint32_t hash, _U_ char *args) AL; //* "tmcbus" (1906135955)
int fn_udata(_U_ uint32_t hash, _U_ char *args) AL; // "udata" (2736127636) int fn_udata(_U_ uint32_t hash, _U_ char *args) AL; //* "udata" (2736127636)
int fn_usartstatus(_U_ uint32_t hash, _U_ char *args) AL; // "usartstatus" (4007098968) int fn_usartstatus(_U_ uint32_t hash, _U_ char *args) AL; //* "usartstatus" (4007098968)
/** /**

View File

@ -1,4 +1,6 @@
#include <stm32f3.h> #include <stm32f3.h>
#include <math.h>
#include <string.h>
/** /**
* @brief hexdump - dump hex array by 16 bytes in string * @brief hexdump - dump hex array by 16 bytes in string
@ -248,6 +250,75 @@ const char *getint(const char *txt, int32_t *I){
return nxt; return nxt;
} }
// be careful: if pow10 would be bigger you should change str[] size!
static const float pwr10[] = {1.f, 10.f, 100.f, 1000.f, 10000.f};
static const float rounds[] = {0.5f, 0.05f, 0.005f, 0.0005f, 0.00005f};
#define P10L (sizeof(pwr10)/sizeof(uint32_t) - 1)
const char *float2str(float x, uint8_t prec){
static char str[16] = {0}; // -117.5494E-36\0 - 14 symbols max!
if(prec > P10L) prec = P10L;
if(isnan(x)){ memcpy(str, "NAN", 4); return str;}
else{
int i = isinf(x);
if(i){memcpy(str, "-INF", 5); if(i == 1) return str+1; else return str;}
}
char *s = str + 14; // go to end of buffer
uint8_t minus = 0;
if(x < 0){
x = -x;
minus = 1;
}
int pow = 0; // xxxEpow
// now convert float to 1.xxxE3y
while(x > 1000.f){
x /= 1000.f;
pow += 3;
}
if(x > 0.) while(x < 1.){
x *= 1000.f;
pow -= 3;
}
// print Eyy
if(pow){
uint8_t m = 0;
if(pow < 0){pow = -pow; m = 1;}
while(pow){
register int p10 = pow/10;
*s-- = '0' + (pow - 10*p10);
pow = p10;
}
if(m) *s-- = '-';
*s-- = 'E';
}
// now our number is in [1, 1000]
uint32_t units;
if(prec){
units = (uint32_t) x;
uint32_t decimals = (uint32_t)((x-units+rounds[prec])*pwr10[prec]);
// print decimals
while(prec){
register int d10 = decimals / 10;
*s-- = '0' + (decimals - 10*d10);
decimals = d10;
--prec;
}
// decimal point
*s-- = '.';
}else{ // without decimal part
units = (uint32_t) (x + 0.5);
}
// print main units
if(units == 0) *s-- = '0';
else while(units){
register uint32_t u10 = units / 10;
*s-- = '0' + (units - 10*u10);
units = u10;
}
if(minus) *s-- = '-';
return s+1;
}
/* /*
void mymemcpy(char *dest, const char *src, int len){ void mymemcpy(char *dest, const char *src, int len){
if(len < 1) return; if(len < 1) return;

View File

@ -10,4 +10,5 @@ char *uhex2str(uint32_t val);
const char *getnum(const char *txt, uint32_t *N); const char *getnum(const char *txt, uint32_t *N);
const char *omit_spaces(const char *buf); const char *omit_spaces(const char *buf);
const char *getint(const char *txt, int32_t *I); const char *getint(const char *txt, int32_t *I);
const char *float2str(float x, uint8_t prec);
//void mymemcpy(char *dest, const char *src, int len); //void mymemcpy(char *dest, const char *src, int len);

View File

@ -1,2 +1,2 @@
#define BUILD_NUMBER "20" #define BUILD_NUMBER "30"
#define BUILD_DATE "2023-02-13" #define BUILD_DATE "2023-02-16"