diff --git a/F3:F303/Multistepper/Readme.md b/F3:F303/Multistepper/Readme.md index 07a30e8..64abe75 100644 --- a/F3:F303/Multistepper/Readme.md +++ b/F3:F303/Multistepper/Readme.md @@ -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 | -# Debug with SWD -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. \ No newline at end of file +## DMA usage + +* ADC1 - DMA1_ch1 +* ADC2 - DMA2_ch1 + diff --git a/F3:F303/Multistepper/adc.c b/F3:F303/Multistepper/adc.c new file mode 100644 index 0000000..f9aa6e1 --- /dev/null +++ b/F3:F303/Multistepper/adc.c @@ -0,0 +1,153 @@ +/* + * This file is part of the multistepper project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#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; +} diff --git a/F3:F303/Multistepper/adc.h b/F3:F303/Multistepper/adc.h new file mode 100644 index 0000000..32711bd --- /dev/null +++ b/F3:F303/Multistepper/adc.h @@ -0,0 +1,45 @@ +/* + * This file is part of the multistepper project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#include + +#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); diff --git a/F3:F303/Multistepper/buttons.c b/F3:F303/Multistepper/buttons.c new file mode 100644 index 0000000..8544978 --- /dev/null +++ b/F3:F303/Multistepper/buttons.c @@ -0,0 +1,90 @@ +/* + * This file is part of the multistepper project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#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; +} diff --git a/F3:F303/Multistepper/buttons.h b/F3:F303/Multistepper/buttons.h new file mode 100644 index 0000000..896a2e4 --- /dev/null +++ b/F3:F303/Multistepper/buttons.h @@ -0,0 +1,40 @@ +/* + * This file is part of the multistepper project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include + +// 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); + diff --git a/F3:F303/Multistepper/can.c b/F3:F303/Multistepper/can.c index 9e87afc..e8284fb 100644 --- a/F3:F303/Multistepper/can.c +++ b/F3:F303/Multistepper/can.c @@ -17,6 +17,8 @@ */ #include "can.h" +#include "commonproto.h" +#include "flash.h" #include "hardware.h" #include "strfunc.h" #include "usb.h" @@ -302,6 +304,66 @@ uint32_t CAN_speed(){ 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){ if(fifo_num > 1) return; 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; } } + if(msg.ID == the_conf.CANID) parseCANcommand(&msg); if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later *RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message } diff --git a/F3:F303/Multistepper/commonproto.c b/F3:F303/Multistepper/commonproto.c index 3eedd1c..41df7bb 100644 --- a/F3:F303/Multistepper/commonproto.c +++ b/F3:F303/Multistepper/commonproto.c @@ -16,7 +16,10 @@ * along with this program. If not, see . */ +#include "adc.h" +#include "buttons.h" #include "commonproto.h" +#include "hardware.h" #include "hdr.h" #include "proto.h" #include "usb.h" @@ -45,16 +48,51 @@ static errcodes cu_time(uint8_t par, int32_t *val){ 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_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_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_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_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;} @@ -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_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_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_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;} @@ -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;} -const fpointer cmdlist[CCMD_AMOUNT] = { +const fpointer cancmdlist[CCMD_AMOUNT] = { // different commands [CCMD_PING] = cu_ping, [CCMD_RELAY] = cu_nosuchfn, diff --git a/F3:F303/Multistepper/hardware.c b/F3:F303/Multistepper/hardware.c index 14edb49..c79e38a 100644 --- a/F3:F303/Multistepper/hardware.c +++ b/F3:F303/Multistepper/hardware.c @@ -16,17 +16,79 @@ * along with this program. If not, see . */ +#include "flash.h" #include "hardware.h" // Buttons: PA9, PA10, PF6, PD3, PD4, PD5, pullup (active - 0) 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}; +// 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) // leave SWD as default AF; high speed for CLK and some other AF; med speed for some another AF TRUE_INLINE void gpio_setup(){ RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN | 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(); GPIOA->ODR = 0; GPIOA->AFR[0] = AFRf(5, 5) | AFRf(5, 6) | AFRf(5, 7); @@ -111,10 +173,78 @@ TRUE_INLINE void iwdg_setup(){ } #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<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; +} diff --git a/F3:F303/Multistepper/hardware.h b/F3:F303/Multistepper/hardware.h index b9c4222..365af27 100644 --- a/F3:F303/Multistepper/hardware.h +++ b/F3:F303/Multistepper/hardware.h @@ -20,6 +20,11 @@ #include +// 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 #define USBPU_port GPIOA #define USBPU_pin (1<<8) @@ -40,7 +45,16 @@ extern const uint32_t BTNpins[BTNSNO]; // motors amount #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; +uint8_t ESW_state(uint8_t MOTno, uint8_t ESWno); +uint8_t MSB(uint16_t val); void hw_setup(); diff --git a/F3:F303/Multistepper/main.c b/F3:F303/Multistepper/main.c index 4d3a56f..9153a29 100644 --- a/F3:F303/Multistepper/main.c +++ b/F3:F303/Multistepper/main.c @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#include "adc.h" +#include "buttons.h" #include "can.h" #include "flash.h" #include "hardware.h" @@ -42,8 +44,9 @@ int main(void){ hw_setup(); // GPIO, ADC, timers, watchdog etc. USBPU_OFF(); // make a reconnection USB_setup(); - USBPU_ON(); CAN_setup(the_conf.CANspeed); + adc_setup(); + USBPU_ON(); uint32_t ctr = 0; CAN_message *can_mesg; while(1){ @@ -79,5 +82,6 @@ int main(void){ const char *ans = cmd_parser(inbuff); if(ans) USB_sendstr(ans); } + process_keys(); } } diff --git a/F3:F303/Multistepper/multistepper.bin b/F3:F303/Multistepper/multistepper.bin index 457e329..2dec223 100755 Binary files a/F3:F303/Multistepper/multistepper.bin and b/F3:F303/Multistepper/multistepper.bin differ diff --git a/F3:F303/Multistepper/multistepper.files b/F3:F303/Multistepper/multistepper.files index 9527b27..a4821f4 100644 --- a/F3:F303/Multistepper/multistepper.files +++ b/F3:F303/Multistepper/multistepper.files @@ -1,3 +1,7 @@ +adc.c +adc.h +buttons.c +buttons.h can.c can.h commonproto.c diff --git a/F3:F303/Multistepper/proto.c b/F3:F303/Multistepper/proto.c index b2ce29d..14b22d0 100644 --- a/F3:F303/Multistepper/proto.c +++ b/F3:F303/Multistepper/proto.c @@ -19,6 +19,8 @@ #include #include +#include "adc.h" +#include "buttons.h" #include "can.h" #include "flash.h" #include "hardware.h" @@ -417,11 +419,29 @@ int fn_dumperr(_U_ uint32_t hash, _U_ char *args){ // "dumperr" (1223989764) 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){ errcodes e = ERR_BADCMD; uint32_t N; int32_t val = 0; uint8_t par = CANMESG_NOPAR; + float f; if(*args){ const char *n = getnum(args, &N); if(n != args){ // get parameter @@ -444,6 +464,63 @@ static int canusb_function(uint32_t hash, char *args){ case CMD_PING: e = cu_ping(par, &val); 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: break; } @@ -464,41 +541,40 @@ static int canusb_function(uint32_t hash, char *args){ #define AL __attribute__ ((alias ("canusb_function"))) // 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 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_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_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_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_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_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_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_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_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_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_mcuvdd(_U_ uint32_t hash, _U_ char *args) AL; //* "mcuvdd" (2517587080) +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_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_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_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_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_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_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_usartstatus(_U_ uint32_t hash, _U_ char *args) AL; // "usartstatus" (4007098968) +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_usartstatus(_U_ uint32_t hash, _U_ char *args) AL; //* "usartstatus" (4007098968) /** diff --git a/F3:F303/Multistepper/strfunc.c b/F3:F303/Multistepper/strfunc.c index c006e59..13ba940 100644 --- a/F3:F303/Multistepper/strfunc.c +++ b/F3:F303/Multistepper/strfunc.c @@ -1,4 +1,6 @@ #include +#include +#include /** * @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; } +// 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){ if(len < 1) return; diff --git a/F3:F303/Multistepper/strfunc.h b/F3:F303/Multistepper/strfunc.h index a067ff8..7712bf7 100644 --- a/F3:F303/Multistepper/strfunc.h +++ b/F3:F303/Multistepper/strfunc.h @@ -10,4 +10,5 @@ char *uhex2str(uint32_t val); const char *getnum(const char *txt, uint32_t *N); const char *omit_spaces(const char *buf); 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); diff --git a/F3:F303/Multistepper/version.inc b/F3:F303/Multistepper/version.inc index 5141e3b..f3c69a2 100644 --- a/F3:F303/Multistepper/version.inc +++ b/F3:F303/Multistepper/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "20" -#define BUILD_DATE "2023-02-13" +#define BUILD_NUMBER "30" +#define BUILD_DATE "2023-02-16"