From a4144090c2674fce324dea631d6c979d08d3fe5b Mon Sep 17 00:00:00 2001 From: eddyem Date: Tue, 5 Feb 2019 11:52:00 +0300 Subject: [PATCH] modify firmware (fix some bugs) --- Doc_rus/Readme.pdf | Bin 7454365 -> 7455141 bytes Doc_rus/Readme.tex | 101 +++++++++------- STM32/steppers/Makefile | 9 +- STM32/steppers/Readme.md | 10 +- STM32/steppers/adc.c | 121 ++++++-------------- STM32/steppers/adc.h | 25 ++-- STM32/steppers/flash.c | 39 +------ STM32/steppers/flash.h | 45 +++++--- STM32/steppers/main.c | 31 ++--- STM32/steppers/proto.c | 210 +++++++++++++++++++++------------- STM32/steppers/proto.h | 8 +- STM32/steppers/rme.html | 10 +- STM32/steppers/steppers.bin | Bin 8008 -> 8496 bytes STM32/steppers/steppers.c | 65 ++++------- STM32/steppers/steppers.geany | 37 ------ STM32/steppers/steppers.h | 20 ++-- STM32/steppers/usart.c | 14 +-- STM32/steppers/usart.h | 18 ++- 18 files changed, 358 insertions(+), 405 deletions(-) delete mode 100644 STM32/steppers/steppers.geany diff --git a/Doc_rus/Readme.pdf b/Doc_rus/Readme.pdf index ba23c299cf43489a05a7cf81f05c22b0ab4be7a7..571f9aca32687451b35fd5f4599e3932e3e5fae5 100644 GIT binary patch delta 30261 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 delta 29502 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

`875+pcgKj?5-Zj8Lr&k-$GeB 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 $`ti%VI_1DKmX$wKoG`(407s4 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 DhHIUb diff --git a/Doc_rus/Readme.tex b/Doc_rus/Readme.tex index b825575..796c12c 100644 --- a/Doc_rus/Readme.tex +++ b/Doc_rus/Readme.tex @@ -12,7 +12,7 @@ \nocolon \def\Z{Цейсс--1000\xspace} \title{Многорежимный фотометр--поляриметр (MMPP) телескопа \Z. Техническая документация.} -\author{Емельянов~Э.В. \and Фатхуллин~Т.А.} +\author{Емельянов~Э.В. \and Москвитин~А.С.\and Фатхуллин~Т.А.} \graphicspath{{./imgs/}} \begin{document} \maketitle @@ -20,7 +20,6 @@ \section{Описание прибора} - MMPP (Multi-Mode Photometer-Polarimeter) "--- многорежимный фотометр-поляриметр телескопа \Z предназначен для проведения фотометрических и поляриметрических исследований. Прибор оснащен двумя турелями USB-HSFW (Edmund Optics) с пятью позициями для 50-мм фильтров, анализатором линейной поляризации и @@ -673,11 +672,11 @@ PF1 & PUPD & посылок экстренного останова, сброса и т.п.). После идентификатора контроллера следует текст команды и (опционально) ее аргументы. В случае, если команда -валидна, контроллер возвращает строку <>. Если команда не распознана, возвращается маркер ошибки +валидна, контроллер возвращает строку <>. Если команда не распознана, возвращается маркер ошибки <>. В случае же ошибок в аргументах команды возвращается маркер ошибки <>. Если команда -возвращает какую-либо информацию, она следует сразу за маркером <>. Данные, занимающие более одной +возвращает какую-либо информацию, она следует сразу за маркером <>. Данные, занимающие более одной строки текста, завершаются маркером <>. Если команда лишь требует выполнения определенного действия, -маркер <> возвращается после установления возможности выполнения данного действия. В силу синхронного +маркер <> возвращается после установления возможности выполнения данного действия. В силу синхронного характера интерфейса связи, команды, требующие длительного времени на исполнение (например, перемещение объекта) не выводят в случае ошибки или достижения заданного положения никаких данных, процесс их исполнения необходимо контролировать периодическим запросом состояния модуля. @@ -716,21 +715,24 @@ PF1 & PUPD & \item[\itm{C}] получение текущих значений параметров конфигурации, например, \begin{verbatim} CONFSZ=36 -DEVID=0 -V12NUM=1 -V12DEN=10 -I12NUM=1 -I12DEN=1 +DEVID=2 +V12NUM=605 +V12DEN=94 +I12NUM=3 +I12DEN=4 V33NUM=1 V33DEN=1 -ESWTHR=150 -MOT0SPD=60 -MOT1SPD=60 -USARTSPD=115200 +ESWTHR=500 +MOT0SPD=3 +MOT1SPD=2 +MAXSTEPS0=50000 +MAXSTEPS1=50000 +USARTSPD=9600 +INTPULLUP=1 REVERSE0=0 -REVERSE1=0 -MAXSTEPS0=0 -MAXSTEPS1=0 +REVERSE1=1 +USTEPS=16 +ACCDECSTEPS=50 DATAEND \end{verbatim} @@ -745,12 +747,12 @@ DATAEND \end{description} например, \begin{verbatim} -ADC[0]=4095 -ADC[1]=2340 -ADC[2]=4095 -ADC[3]=4087 -ADC[4]=1665 -ADC[5]=1532 +ADC[0]=189 +ADC[1]=2317 +ADC[2]=4088 +ADC[3]=4090 +ADC[4]=1703 +ADC[5]=1525 DATAEND \end{verbatim} @@ -837,25 +839,32 @@ ESW11=HALL проведенных изменений и их проверки. \begin{description}\def\itm#1{\rlap{#1}\phantom{M\#num}} +\item[\itm{A num}] установка количества шагов (\verb'ACCDECSTEPS'), в течение которого движение будет + производиться с ускорением (на старте) или замедлением (на финише); в случае, если требуется + переместить двигатель на меньшее количество шагов, движение будет производиться с минимальной + скоростью (равной произведению \verb'MOTxSPD' на значение макроса \verb'LOWEST_SPEED_DIV'); \item[\itm{C\#num}] изменение значения текущей скорости двигателя с номером \textbf{\#} на \textbf{num} (данное изменение действует лишь до окончания движения двигателя); -\item[\itm{Dxnum}] установка знаменателя (\textbf{d}enominator) величины \textbf{x} (D, I или M~-- в - соответствии с геттером значения измерений АЦП) в \textbf{num}; -\item[\itm{Exnum}] установка числителя (num\textbf{e}rator) (аналогично \textbf{Dxnum}); -\item[\itm{I num}] изменение значения идентификатора (целое число) контроллера; -\item[\itm{M\#num}] установка максимального диапазона (\textbf{num} от~1 до~65535) шагового двигателя - \textbf{\#}; -\item[\itm{P num}] включение (\textbf{num} равно нулю) или отключение (\textbf{num} отсутствует или любое, - кроме нуля) внутренней подтяжки на UART Tx; -\item[\itm{R\#num}] реверсивное движение двигателя \textbf{\#} (\textbf{num} равное нулю отключает реверс), - в режиме реверса меняется только направление вращения двигателя, но не обрабатываемые концевики; -\item[\itm{S\#num}] изменение значения максимальной скорости двигателя с номером \textbf{\#} на \textbf{num} - (максимальная скорость устанавливается после окончания движения с ускорением и не зависит от - текущей скорости, \textbf{C\#num}); -\item[\itm{T num}] изменение пороговых величин (\textbf{num} в ADU) для градации состояний концевика - двигателя~0 (0..num~-- датчик Холла, 2048-num..2048+num~-- пользовательская кнопка, +\item[\itm{Dvnum}] установка знаменателя (\textbf{d}enominator, \verb'xxxDEN') величины \textbf{v} (D, I или + M~-- в соответствии с геттером значения измерений АЦП) в \textbf{num}; +\item[\itm{Evnum}] установка числителя (num\textbf{e}rator, \verb'xxxNUM') (аналогично \textbf{Dvnum}); +\item[\itm{I num}] изменение значения идентификатора (\verb'DEVID', целое число) контроллера; +\item[\itm{M\#num}] установка максимального диапазона (\verb'MAXSTEPS#', \textbf{num} от~1 до~65535) шагового + двигателя \textbf{\#}; +\item[\itm{P num}] (\verb'INTPULLUP') включение (\textbf{num} равно нулю) или отключение (\textbf{num} + отсутствует или любое, кроме нуля) внутренней подтяжки на UART Tx; +\item[\itm{R\#num}] (\verb'REVERSE#') реверсивное движение двигателя \textbf{\#} (\textbf{num} равное нулю + отключает реверс), в режиме реверса меняется только направление вращения двигателя, но не + обрабатываемые концевики; +\item[\itm{S\#num}] изменение значения максимальной скорости (\verb'MOT#SPD') двигателя с номером + \textbf{\#} на \textbf{num} (максимальная скорость устанавливается после окончания движения с + ускорением и не зависит от текущей скорости, \textbf{C\#num}); +\item[\itm{T num}] изменение пороговых величин (\verb'ESWTHR', \textbf{num} в ADU) для градации состояний + концевика двигателя~0 (0..num~-- датчик Холла, 2048-num..2048+num~-- пользовательская кнопка, 4096-num..4095~-- свободное состояние); -\item[\itm{U num}] изменение скорости UART. +\item[\itm{U num}] изменение скорости UART (\verb'USARTSPD'); +\item[\itm{u num}] установка количества микрошагов (\verb'USTEPS') в одном шаге; это число должно быть + степенью двойки (до~32 включительно). \end{description} \paragraph{Сеттеры скорости двигателей.} @@ -925,10 +934,12 @@ MOT0SPD=3 MOT1SPD=5 MAXSTEPS0=50000 MAXSTEPS1=50000 +USARTSPD=9600 INTPULLUP=1 -USARTSPD=115200 REVERSE0=1 REVERSE1=0 +USTEPS=16 +ACCDECSTEPS=50 DATAEND MMPP_control -a 2GC @@ -947,10 +958,12 @@ MOT0SPD=3 MOT1SPD=2 MAXSTEPS0=50000 MAXSTEPS1=50000 +USARTSPD=9600 INTPULLUP=1 -USARTSPD=115200 REVERSE0=0 REVERSE1=1 +USTEPS=16 +ACCDECSTEPS=50 DATAEND \end{lstlisting} @@ -959,11 +972,13 @@ DATAEND изменение уровня на концевиках двигателя~0, \verb'MOTxSPD' (предельные скорости соответствующих двигателей), \verb'MAXSTEPSx' (максимальное количество шагов данного двигателя), \verb'INTPULLUP' (при отсутствии внешней подтяжки шины~Tx контроллеров отключенная подтяжка приведет к неработоспособности), \verb'USARTSPD' (скорость -шины UART) и \verb'REVERSEx' (направление вращения двигателя). +шины UART), \verb'REVERSEx' (направление вращения двигателя) и \verb'ACCDECSTEPS' (количество шагов для +ускорения\slash замедления движения). \paragraph{Значения предельных токов DRV8825.} \label{MotCurrents} Предельные токи определяются по уровню напряжения на подстроечных резисторах DRV8825. -\TODO[ТОКИ!] +Анализатор поляризации: M0 (подвижка) 0.5\,В, M1 (ротатор) 3.1\,В. +Фазовая пластина: M0 (подвижка) 0.9\,В, M1 (ротатор) 0.25\,В. \comment[ФОТО!]{добавить табличку со значениями напряжений + фотографию, как настраивать} \end{document} diff --git a/STM32/steppers/Makefile b/STM32/steppers/Makefile index a7b076b..69944e3 100644 --- a/STM32/steppers/Makefile +++ b/STM32/steppers/Makefile @@ -52,7 +52,7 @@ LIB_DIR := $(INC_DIR)/ld CFLAGS += -O2 -g -MD -D__thumb2__=1 CFLAGS += -Wall -Werror -Wextra -Wshadow -Wimplicit-function-declaration CFLAGS += -Wredundant-decls $(INCLUDE) -# -Wmissing-prototypes -Wstrict-prototypes +#-Wmissing-prototypes -Wstrict-prototypes CFLAGS += -fno-common -ffunction-sections -fdata-sections ############################################################################### @@ -71,10 +71,6 @@ LDLIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) DEFS += -DSTM32$(FAMILY) -DSTM32$(MCU) -#.SUFFIXES: .elf .bin .hex .srec .list .map .images -#.SECONDEXPANSION: -#.SECONDARY: - ELF := $(OBJDIR)/$(BINARY).elf LIST := $(OBJDIR)/$(BINARY).list BIN := $(BINARY).bin @@ -101,9 +97,6 @@ $(OBJDIR)/%.o: %.c @echo " CC $<" $(CC) $(CFLAGS) $(DEFS) $(INCLUDE) $(ARCH_FLAGS) -o $@ -c $< -#$(OBJDIR)/%.d: %.c $(OBJDIR) -# $(CC) -MM -MG $< | sed -e 's,^\([^:]*\)\.o[ ]*:,$(@D)/\1.o $(@D)/\1.d:,' >$@ - $(BIN): $(ELF) @echo " OBJCOPY $(BIN)" $(OBJCOPY) -Obinary $(ELF) $(BIN) diff --git a/STM32/steppers/Readme.md b/STM32/steppers/Readme.md index 2e9c4fa..3648940 100644 --- a/STM32/steppers/Readme.md +++ b/STM32/steppers/Readme.md @@ -148,7 +148,7 @@ Getters returning more than one field ends with `DATAEND` meaning that's all dat ### Motors' manipulation -Next char should be '0' or '1' --- motor's number. If wrong, `Num>1` answer would be returned. +Next char should be '0' or '1' --- motor's number. If wrong, `BADCMD` answer would be returned. There's only two commands in this section: * **Mnum** - move motor to *num* steps. Errors: @@ -164,9 +164,10 @@ There's only two commands in this section: Change of any setter takes place in MCU RAM immediately. To store them permanently run *write flash* command. +* **A num** - set value of ACCDECSTEPS (approximate amount of steps to do acceleration/deceleration) to *num* * **C#num** - set current *speed* to *num* for motor # -* **D num** - set *denominator* to number *num* -* **E num** - set *numerator* +* **D$num** - set *denominator $* (*D* - Vdd, *I* - current, *M* - 12V) to number *num* +* **E$num** - set *numerator $* * **I num** - set *device ID* * **M#num** - set maxsteps (*num* is 1..65535) for motor `#` * **P num** - properties of internal pullup (0 - disabled, other or without `num` - enabled) @@ -175,10 +176,11 @@ Change of any setter takes place in MCU RAM immediately. To store them permanent * **T num** - set *end-switches threshold* (in ADU, near 0 for Hall switch, 2048 for user button and 4096 for released state) * **U num** - set *USART speed* to *num* bits per second +* **u num** - set value of USTEPS (amount of microsteps per one step) ### Motor speed setters To set motor speed to **N** steps per second, give command `C` or `S` with argument equal to -3000/N. E.g. to set current speed for DevID=0, motor0 to 50 steps per second give command `0SC050`. +3000/N. E.g. to set current speed for DevID=0, motor0 to 50 steps per second give command `0SC060`. ### Denominator and numerator setters Have naxt letter similar to ADC getter (**D** - Vdd, **I** - motors' I, or **M** - motors' U). diff --git a/STM32/steppers/adc.c b/STM32/steppers/adc.c index 643c9b9..5848397 100644 --- a/STM32/steppers/adc.c +++ b/STM32/steppers/adc.c @@ -20,18 +20,18 @@ * MA 02110-1301, USA. * */ -#include "stm32f0.h" -#include "flash.h" #include "adc.h" +#include "flash.h" +#include "stm32f0.h" #include "usart.h" -extern volatile uint32_t Tms; // time counter for 1-second Vdd measurement static uint32_t lastVddtime = 0; // Tms value of last Vdd measurement static uint32_t VddValue = 0; // value of Vdd * 100 (for more precision measurements) // check time of last Vdd measurement & refresh it value -#define CHKVDDTIME() do{if(!VddValue || Tms < lastVddtime || Tms - lastVddtime > 999) getVdd();}while(0) +#define CHKVDDTIME() do{if(!VddValue || Tms - lastVddtime > 999) getVdd();}while(0) -/* +/** + * Array for ADC raw values (before median filter by 9 elements): * 0 - Steppers current * 1 - Input voltage 12V * 2 - EndSwitch2 of motor1 @@ -39,7 +39,32 @@ static uint32_t VddValue = 0; // value of Vdd * 100 (for more precision measurem * 4 - inner temperature * 5 - vref */ -uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS]; +uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9]; + +/** + * @brief getADCval - calculate median value for `nch` channel + * @param nch - number of channel + * @return + */ +uint16_t getADCval(int nch){ + int i, addr = nch; + register uint16_t temp; +#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); } +#define PIX_SWAP(a,b) { temp=(a);(a)=(b);(b)=temp; } + uint16_t p[9]; + for(i = 0; i < 9; ++i, addr += NUMBER_OF_ADC_CHANNELS) // first we should prepare array for optmed + p[i] = ADC_array[addr]; + PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; + PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ; + PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; + PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ; + PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ; + PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ; + PIX_SORT(p[4], p[2]) ; + return p[4]; +#undef PIX_SORT +#undef PIX_SWAP +} void adc_setup(){ // AIN: PA0..3, PA13, PA14. ADC_IN16 - inner temperature. ADC_IN17 - VREFINT @@ -86,7 +111,7 @@ void adc_setup(){ ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG; /* (2) */ DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); /* (3) */ DMA1_Channel1->CMAR = (uint32_t)(ADC_array); /* (4) */ - DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS; /* (5) */ + DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9; /* (5) */ DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC; /* (6) */ DMA1_Channel1->CCR |= DMA_CCR_EN; /* (7) */ ADC1->CR |= ADC_CR_ADSTART; /* start the ADC conversions */ @@ -96,90 +121,18 @@ void adc_setup(){ int32_t getTemp(){ CHKVDDTIME(); // make correction on Vdd value - int32_t temperature = (int32_t)ADC_array[4] * VddValue / 330; -/* -write2trbuf("getTemp()\ncal30="); -put_uint(*TEMP30_CAL_ADDR); -write2trbuf(", cal110="); -put_uint(*TEMP110_CAL_ADDR); -write2trbuf(", t="); -put_int(temperature); -SENDBUF(); -*/ + int32_t temperature = (int32_t)getADCval(4) * (int32_t)VddValue / 330; temperature = (int32_t) *TEMP30_CAL_ADDR - temperature; -/* -put_int(temperature); -SENDBUF(); -*/ temperature *= (int32_t)(1100 - 300); -/* -put_int(temperature); -SENDBUF(); -*/ temperature = temperature / (int32_t)(*TEMP30_CAL_ADDR - *TEMP110_CAL_ADDR); -/* -put_int(temperature); -SENDBUF(); -*/ temperature += 300; return(temperature); } // return Vdd * 100 (V) uint32_t getVdd(){ - #define ARRSZ (10) - static uint16_t arr[ARRSZ] = {0}; - static int arridx = 0; - uint32_t v = ADC_array[5]; - int i; -/* -write2trbuf("getVdd(), val="); -put_uint(v); -write2trbuf(", cal="); -put_uint(*VREFINT_CAL_ADDR); -SENDBUF(); -*/ - if(arr[0] == 0){ // first run - fill all with current data -/* -write2trbuf("1st run"); -SENDBUF(); -*/ - for(i = 0; i < ARRSZ; ++i) arr[i] = (uint16_t) v; - }else{ -/* -write2trbuf("arridx="); -put_int(arridx); -SENDBUF(); -*/ - arr[arridx++] = v; - v = 0; // now v is mean - if(arridx > ARRSZ-1) arridx = 0; - // calculate mean - for(i = 0; i < ARRSZ; ++i){ -/* -write2trbuf("arr["); put2trbuf('0'+i); write2trbuf("]="); -put_uint(arr[i]); -SENDBUF(); -*/ - v += arr[i]; - } - v /= ARRSZ; -/* -write2trbuf("mean value: "); -put_uint(v); -SENDBUF(); -*/ - } uint32_t vdd = ((uint32_t) *VREFINT_CAL_ADDR) * (uint32_t)330 * the_conf.v33numerator; // 3.3V -/* -put_uint(vdd); -SENDBUF(); -*/ - vdd /= v * the_conf.v33denominator; -/* -put_uint(vdd); -SENDBUF(); -*/ + vdd /= getADCval(5) * the_conf.v33denominator; lastVddtime = Tms; VddValue = vdd; return vdd; @@ -188,7 +141,7 @@ SENDBUF(); // return value of 12V * 100 (V) uint32_t getVmot(){ CHKVDDTIME(); - uint32_t vmot = ADC_array[1] * VddValue * the_conf.v12numerator; + uint32_t vmot = getADCval(1) * VddValue * the_conf.v12numerator; vmot >>= 12; vmot /= the_conf.v12denominator; return vmot; @@ -197,7 +150,7 @@ uint32_t getVmot(){ // return value of motors' current * 100 (A) uint32_t getImot(){ CHKVDDTIME(); - uint32_t vmot = ADC_array[0] * VddValue * the_conf.i12numerator; + uint32_t vmot = getADCval(0) * VddValue * the_conf.i12numerator; vmot >>= 12; vmot /= the_conf.i12denominator; return vmot; @@ -220,7 +173,7 @@ ESW_status eswStatus(int motnum, int eswnum){ if(eswnum) idx = 2; else idx = 3; } - uint16_t thres = the_conf.ESW_thres, val = ADC_array[idx]; + uint16_t thres = the_conf.ESW_thres, val = getADCval(idx); // low sighal: 0..threshold - Hall activated if(val < thres) return ESW_HALL; // high signal: (4096-thres)..4096 - pullup diff --git a/STM32/steppers/adc.h b/STM32/steppers/adc.h index 4503234..3f5c9eb 100644 --- a/STM32/steppers/adc.h +++ b/STM32/steppers/adc.h @@ -22,14 +22,15 @@ */ #pragma once -#ifndef __ADC_H__ -#define __ADC_H__ +#ifndef ADC_H__ +#define ADC_H__ -// 8 channels (including inttemp & vrefint) +#include + +// 6 channels (including inttemp & vrefint) #define NUMBER_OF_ADC_CHANNELS (6) -extern uint16_t ADC_array[]; -void adc_setup(); +extern volatile uint32_t Tms; // time counter for 1-second Vdd measurement typedef enum{ ESW_RELEASED, @@ -38,10 +39,12 @@ typedef enum{ ESW_ERROR } ESW_status; -int32_t getTemp(); -uint32_t getVdd(); -uint32_t getVmot(); -uint32_t getImot(); -ESW_status eswStatus(int motnum, int eswnum); +void adc_setup(void); +uint16_t getADCval(int nch); +int32_t getTemp(void); +uint32_t getVdd(void); +uint32_t getVmot(void); +uint32_t getImot(void); +ESW_status eswStatus(int motnum, int eswnum); -#endif // __ADC_H__ +#endif // ADC_H__ diff --git a/STM32/steppers/flash.c b/STM32/steppers/flash.c index 109e619..bf22aad 100644 --- a/STM32/steppers/flash.c +++ b/STM32/steppers/flash.c @@ -41,31 +41,23 @@ user_conf the_conf = { ,.v33denominator = 1 ,.v33numerator = 1 ,.ESW_thres = 500 - ,.usartspd = (uint32_t)115200 - ,.motspd = {10, 10} // max speed: 300 steps per second + ,.usartspd = (uint32_t)9600 + ,.motspd = {10, 10} // max speed: 300 steps per second, should be LESS than 1310! ,.maxsteps = {50000, 50000} // max steps from point to point ,.reverse = {0,0} // set DIR to this value when moving to '+' ,.intpullup = 1 // by default internal pullup @ Tx pin enabled + ,.usteps = 16 + ,.accdecsteps = 100 }; -static int erase_flash(); +static int erase_flash(void); static int get_gooddata(){ user_conf *c = (user_conf*) FLASH_CONF_START_ADDR; // have data - move it to `the_conf` int idx; -//write2trbuf("get_gooddata()\n"); for(idx = 0; idx < maxnum; ++idx){ // find current settings index - first good uint16_t sz = c[idx].userconf_sz; -/*write2trbuf("idx="); -put_int((int32_t) idx); -write2trbuf(", sz="); -put_uint((uint32_t) sz); -write2trbuf(", devID="); -put_uint((uint32_t) c[idx].devID); -write2trbuf(", ESW_thres="); -put_uint((uint32_t) c[idx].ESW_thres); -SENDBUF();*/ if(sz != sizeof(user_conf)){ if(sz == 0xffff) break; // first clear else{ @@ -93,9 +85,6 @@ int store_userconf(){ idx = 0; if(erase_flash()) return 1; }else ++idx; // take next data position -/*write2trbuf("store_userconf()\nidx="); -put_int((int32_t) idx); -SENDBUF();*/ if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash FLASH->KEYR = FLASH_FKEY1; FLASH->KEYR = FLASH_FKEY2; @@ -106,23 +95,12 @@ SENDBUF();*/ FLASH->CR |= FLASH_CR_PG; uint16_t *data = (uint16_t*) &the_conf; uint16_t *address = (uint16_t*) &c[idx]; - uint32_t i, count = sizeof(user_conf) / 2; + uint32_t i, count = (1 + sizeof(user_conf)) / 2; for (i = 0; i < count; ++i){ *(volatile uint16_t*)(address + i) = data[i]; while (FLASH->SR & FLASH_SR_BSY); if(FLASH->SR & FLASH_SR_PGERR) ret = 1; // program error - meet not 0xffff else while (!(FLASH->SR & FLASH_SR_EOP)); -/*write2trbuf("write byte "); -put_int((int32_t) i); -write2trbuf(", write value="); -put_uint(data[i]); -write2trbuf(", read value="); -put_uint(address[i]); -SENDBUF(); -if(ret){ -write2trbuf("PGERR"); -SENDBUF(); -}*/ FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR; } FLASH->CR &= ~(FLASH_CR_PG); @@ -132,17 +110,12 @@ SENDBUF(); static int erase_flash(){ int ret = 0; -/*write2trbuf("erase_flash()"); -SENDBUF();*/ /* (1) Wait till no operation is on going */ /* (2) Clear error & EOP bits */ /* (3) Check that the Flash is unlocked */ /* (4) Perform unlock sequence */ while ((FLASH->SR & FLASH_SR_BSY) != 0){} /* (1) */ FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR; /* (2) */ - /* if (FLASH->SR & FLASH_SR_EOP){ - FLASH->SR |= FLASH_SR_EOP; - }*/ if ((FLASH->CR & FLASH_CR_LOCK) != 0){ /* (3) */ FLASH->KEYR = FLASH_FKEY1; /* (4) */ FLASH->KEYR = FLASH_FKEY2; diff --git a/STM32/steppers/flash.h b/STM32/steppers/flash.h index 0efa755..fde7538 100644 --- a/STM32/steppers/flash.h +++ b/STM32/steppers/flash.h @@ -22,30 +22,41 @@ */ #pragma once -#ifndef __FLASH_H__ -#define __FLASH_H__ +#ifndef FLASH_H__ +#define FLASH_H__ + +#include typedef struct{ - uint16_t userconf_sz; // size of data - uint16_t devID; // device address (id) - uint16_t ESW_thres; // ADC threshold for end-switches/Hall sensors - // calibration values for current/voltage sensors - uint16_t v12numerator; // 12V to motors + uint16_t userconf_sz; // size of data + uint16_t devID; // device address (id) + + uint16_t ESW_thres; // ADC threshold for end-switches/Hall sensors + uint16_t v12numerator; // 12V to motors + uint16_t v12denominator; - uint16_t i12numerator; // motors' current + uint16_t i12numerator; // motors' current + uint16_t i12denominator; - uint16_t v33numerator; // 3.3V (vref) + uint16_t v33numerator; // 3.3V (vref) + + uint16_t motspd[2]; // max motor speed ([3000 / motspd] steps per second) + + uint16_t maxsteps[2]; // maximum amount of steps for each motor (0 - infinity) + + uint32_t usartspd; // usartspeed + uint16_t v33denominator; - uint32_t usartspd; // usartspeed - uint16_t motspd[2]; // max motor speed ([3000 / motspd] steps per second) - uint16_t maxsteps[2]; // maximum amount of steps for each motor (0 - infinity) - uint8_t reverse[2]; // == 1 if positive direction when DIR is low - uint8_t intpullup; // internal pullup @ Tx + uint8_t reverse[2]; // == 1 if positive direction when DIR is low + + uint8_t intpullup; // internal pullup @ Tx + uint8_t usteps; // amount of microsteps in each step + uint8_t accdecsteps; // amount of steps need for full acceleration/deceleration cycle } user_conf; extern user_conf the_conf; -void get_userconf(); -int store_userconf(); +void get_userconf(void); +int store_userconf(void); -#endif // __FLASH_H__ +#endif // FLASH_H__ diff --git a/STM32/steppers/main.c b/STM32/steppers/main.c index d668836..49cee6c 100644 --- a/STM32/steppers/main.c +++ b/STM32/steppers/main.c @@ -19,12 +19,12 @@ * MA 02110-1301, USA. */ -#include "stm32f0.h" -#include "usart.h" #include "adc.h" #include "flash.h" #include "proto.h" #include "steppers.h" +#include "stm32f0.h" +#include "usart.h" volatile uint32_t Tms = 0; @@ -85,13 +85,14 @@ void iwdg_setup(){ } int main(void){ - uint32_t lastT = 0; + //uint32_t lastT = 0; uint32_t ostctr = 0; #if 0 //def EBUG uint32_t msgctr = 0; #endif char *txt = NULL; + const char *ret = NULL; sysreset(); SysTick_Config(6000, 1); get_userconf(); @@ -103,17 +104,13 @@ int main(void){ //pin_set(GPIOA, 1<<5); // clear extern LED while (1){ IWDG->KR = IWDG_REFRESH; // refresh watchdog - if(lastT > Tms || Tms - lastT > 499){ -/* #ifdef EBUG - pin_toggle(GPIOA, 1<<4); // blink by onboard LED once per second - #endif -*/ + /*if(lastT > Tms || Tms - lastT > 499){ lastT = Tms; - } + }*/ if(usart1rx()){ // usart1 received data, store in in buffer if(usart1_getline(&txt)){ - txt = process_command(txt); - }else txt = NULL; // buffer overflow + ret = process_command(txt); + }else ret = NULL; // buffer overflow } #if 0 //def EBUG @@ -122,15 +119,9 @@ int main(void){ txt = "hello, I'm alive!\n"; } #endif - /* - if(trbufisfull()){ - write2trbuf("ERR"); - usart1_send_blocking(gettrbuf()); - } - cleartrbuf();*/ - if(txt){ // text waits for sending - if(ALL_OK == usart1_send(txt)){ - txt = NULL; + if(ret){ // text waits for sending + if(ALL_OK == usart1_send(ret)){ + ret = NULL; } } if(ostctr != Tms){ // check steppers not frequently than once in 1ms diff --git a/STM32/steppers/proto.c b/STM32/steppers/proto.c index 64a5666..b99306f 100644 --- a/STM32/steppers/proto.c +++ b/STM32/steppers/proto.c @@ -20,41 +20,43 @@ * MA 02110-1301, USA. * */ -#include "stm32f0.h" -#include "proto.h" #include "adc.h" #include "flash.h" +#include "proto.h" +#include "steppers.h" +#include "stm32f0.h" #include "string.h" #include "usart.h" -#include "steppers.h" static const char *eodata = "DATAEND"; static const char *badcmd = "BADCMD"; -static const char *allok = "ALL OK"; +static const char *allok = "ALLOK"; static const char *err = "ERR"; -#define EODATA ((char*)eodata) -#define BADCMD ((char*)badcmd) -#define ALLOK ((char*)allok) -#define ERR ((char*)err) +#define EODATA (eodata) +#define BADCMD (badcmd) +#define ALLOK (allok) +#define ERR (err) -static char *getnum(char *buf, int32_t *N); -static char *get_something(char *str); -static char *set_something(char *str); -static char *motor_cmd(char *str); +static const char *getnum(const char *buf, int32_t *N); +static const char *get_something(const char *str); +static const char *set_something(const char *str); +static const char *motor_cmd(const char *str); -static char *get_status(); -static char *get_conf(); -static char *get_raw_adc(); -static char *get_ADCval(char *str); -static char *get_temper(); +static const char *get_status(void); +static const char *get_conf(void); +static const char *get_raw_adc(void); +static const char *get_ADCval(const char *str); +static const char *get_temper(void); -static char *setDenEn(uint8_t De, char *str); -static char *setDevId(char *str); -static char *setESWthres(char *str); -static char *setUSARTspd(char *str); -static char *setmotvals(char v, char *str); -static char *setMotSpeed(int cur, char *str); +static const char *setDenEn(uint8_t De, const char *str); +static const char *setDevId(const char *str); +static const char *setESWthres(const char *str); +static const char *setUSARTspd(const char *str); +static const char *setUSTEPS(const char *str); +static const char *setACCDEC(const char *str); +static const char *setmotvals(char v, const char *str); +static const char *setMotSpeed(int cur, const char *str); #define omitwsp(str) do{register char nxt; while((nxt = *str)){if(nxt != ' ' && nxt != '\t') break; else ++str;}}while(0) @@ -62,9 +64,10 @@ static char *setMotSpeed(int cur, char *str); * get input buffer `cmdbuf`, parse it and change system state * @return message to send */ -char* process_command(char *cmdbuf){ +const char* process_command(const char *cmdbuf){ int32_t num; - char *str, c; + const char *str; + char c; #ifdef EBUG usart1_send_blocking(cmdbuf); #endif @@ -99,7 +102,7 @@ char* process_command(char *cmdbuf){ // read `buf` and get first integer `N` in it // @return pointer to first non-number if all OK or NULL if first symbol isn't a space or number -static char *getnum(char *buf, int32_t *N){ +static const char *getnum(const char *buf, int32_t *N){ char c; int positive = -1; int32_t val = 0; @@ -130,7 +133,7 @@ static char *getnum(char *buf, int32_t *N){ // get conf (uint16_t) number // @return 0 if all OK -static int getu16(char *buf, uint16_t *N){ +static int getu16(const char *buf, uint16_t *N){ int32_t N32; if(!getnum(buf, &N32)) return 1; if(N32 > 0xffff || N32 < 0) return 1; @@ -138,7 +141,7 @@ static int getu16(char *buf, uint16_t *N){ return 0; } -static char *get_something(char *str){ +static const char *get_something(const char *str){ switch(*str++){ case 'A': // get ADC value: voltage or current return get_ADCval(str); @@ -159,8 +162,7 @@ static char *get_something(char *str){ return BADCMD; } -static char *get_status(){ - int i, j; +static const char *get_status(){ char str[3] = {0, '=', 0}; if(RCC->CSR & RCC_CSR_IWDGRSTF){ // watchdog reset occured write2trbuf("WDGRESET=1\n"); @@ -169,7 +171,7 @@ static char *get_status(){ write2trbuf("SOFTRESET=1\n"); } RCC->CSR = RCC_CSR_RMVF; // clear reset flags - for(i = 0; i < 2; ++i){ + for(int8_t i = 0; i < 2; ++i){ write2trbuf("MOTOR"); str[0] = '0' + i; write2trbuf(str); stp_state stt = stp_getstate(i); @@ -212,7 +214,7 @@ static char *get_status(){ write2trbuf(str); put_int(stp_position(i)); SENDBUF(); - for(j = 0; j < 2; ++j){ + for(int8_t j = 0; j < 2; ++j){ write2trbuf("ESW"); put2trbuf('0' + i); put2trbuf('0' + j); put2trbuf('='); ESW_status stat = eswStatus(i, j); @@ -242,24 +244,38 @@ typedef struct{ const uint16_t *ptr; } user_conf_descr; +typedef struct{ + const char *fieldname; + const uint8_t *ptr; +} user_conf_descr8; + static const user_conf_descr descrarr[] = { - {"CONFSZ", &the_conf.userconf_sz}, - {"DEVID", &the_conf.devID}, - {"V12NUM", &the_conf.v12numerator}, - {"V12DEN", &the_conf.v12denominator}, - {"I12NUM", &the_conf.i12numerator}, - {"I12DEN", &the_conf.i12denominator}, - {"V33NUM", &the_conf.v33numerator}, - {"V33DEN", &the_conf.v33denominator}, - {"ESWTHR", &the_conf.ESW_thres}, - {"MOT0SPD",&the_conf.motspd[0]}, - {"MOT1SPD",&the_conf.motspd[1]}, - {"MAXSTEPS0",&the_conf.maxsteps[0]}, - {"MAXSTEPS1",&the_conf.maxsteps[1]}, + {"CONFSZ", &the_conf.userconf_sz}, + {"DEVID", &the_conf.devID}, + {"V12NUM", &the_conf.v12numerator}, + {"V12DEN", &the_conf.v12denominator}, + {"I12NUM", &the_conf.i12numerator}, + {"I12DEN", &the_conf.i12denominator}, + {"V33NUM", &the_conf.v33numerator}, + {"V33DEN", &the_conf.v33denominator}, + {"ESWTHR", &the_conf.ESW_thres}, + {"MOT0SPD", &the_conf.motspd[0]}, + {"MOT1SPD", &the_conf.motspd[1]}, + {"MAXSTEPS0", &the_conf.maxsteps[0]}, + {"MAXSTEPS1", &the_conf.maxsteps[1]}, {NULL, NULL} }; -static char *get_conf(){ +static const user_conf_descr8 descrarr8[] = { + {"INTPULLUP", &the_conf.intpullup}, + {"REVERSE0", &the_conf.reverse[0]}, + {"REVERSE1", &the_conf.reverse[1]}, + {"USTEPS", &the_conf.usteps}, + {"ACCDECSTEPS", &the_conf.accdecsteps}, + {NULL, NULL} +}; + +static const char *get_conf(){ const user_conf_descr *curdesc = descrarr; do{ write2trbuf(curdesc->fieldname); @@ -267,32 +283,31 @@ static char *get_conf(){ put_uint((uint32_t) *curdesc->ptr); SENDBUF(); }while((++curdesc)->fieldname); - write2trbuf("INTPULLUP="); - put2trbuf(the_conf.intpullup ? '1' : '0'); - write2trbuf("\nUSARTSPD="); + const user_conf_descr8 *curdesc8 = descrarr8; + write2trbuf("USARTSPD="); put_uint(the_conf.usartspd); SENDBUF(); - write2trbuf("REVERSE0="); - put_uint(the_conf.reverse[0]); - write2trbuf("\nREVERSE1="); - put_uint(the_conf.reverse[1]); - SENDBUF(); + do{ + write2trbuf(curdesc8->fieldname); + put2trbuf('='); + put_uint((uint32_t) *curdesc8->ptr); + SENDBUF(); + }while((++curdesc8)->fieldname); return EODATA; } -static char *get_raw_adc(){ - int i; - for(i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i){ +static const char *get_raw_adc(){ + for(int8_t i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i){ write2trbuf("ADC["); put2trbuf('0' + i); write2trbuf("]="); - put_uint((uint32_t) ADC_array[i]); + put_uint((uint32_t) getADCval(i)); SENDBUF(); } return EODATA; } -static char *get_ADCval(char *str){ +static const char *get_ADCval(const char *str){ uint32_t v; switch(*str){ case 'D': // vdd @@ -315,7 +330,7 @@ static char *get_ADCval(char *str){ return NULL; } -static char *get_temper(){ +static const char *get_temper(){ int32_t t = getTemp(); write2trbuf("TEMP="); put_int(t); @@ -323,8 +338,11 @@ static char *get_temper(){ return NULL; } -static char *set_something(char *str){ +static const char *set_something(const char *str){ switch(*str++){ + case 'A': // set accdecsteps + return setACCDEC(str); + break; case 'C': // set current speed return setMotSpeed(1, str); break; @@ -358,6 +376,9 @@ static char *set_something(char *str){ case 'U': // set USART speed return setUSARTspd(str); break; + case 'u': // set usteps + return setUSTEPS(str); + break; } return BADCMD; } @@ -367,7 +388,7 @@ static char *set_something(char *str){ * @param De == 1 for denominator, == 0 for numerator * @param str - rest of string */ -static char *setDenEn(uint8_t De, char *str){ +static const char *setDenEn(uint8_t De, const char *str){ uint16_t *targ = NULL; switch(*str++){ case 'D': @@ -387,19 +408,19 @@ static char *setDenEn(uint8_t De, char *str){ return ALLOK; } -static char *setDevId(char *str){ +static const char *setDevId(const char *str){ omitwsp(str); if(getu16(str, &the_conf.devID)) return BADCMD; return ALLOK; } -static char *setESWthres(char *str){ +static const char *setESWthres(const char *str){ omitwsp(str); if(getu16(str, &the_conf.ESW_thres)) return BADCMD; return ALLOK; } -static char *setUSARTspd(char *str){ +static const char *setUSARTspd(const char *str){ omitwsp(str); int32_t N32; if(!getnum(str, &N32)) return BADCMD; @@ -408,40 +429,40 @@ static char *setUSARTspd(char *str){ } // if cur == 1 set current speed else set global motspd -static char *setMotSpeed(int cur, char *str){ +static const char *setMotSpeed(int cur, const char *str){ omitwsp(str); - uint8_t Num = *str++ - '0'; + uint8_t Num = (uint8_t)(*str++ - '0'); if(Num > 1) return ERR; int32_t spd; omitwsp(str); - if(!getnum(str, &spd)) return ERR; - if(spd < 2 || spd > 6553) return "BadSpd"; + if(!getnum(str, &spd)) return BADCMD; + if(spd < 2 || spd > (0xffff/LOWEST_SPEED_DIV)) return ERR; if(cur){ // change current speed stp_chARR(Num, spd); }else{ - the_conf.motspd[Num] = spd; + the_conf.motspd[Num] = (uint16_t)spd; stp_chspd(); } return ALLOK; } // set other motor values -static char *setmotvals(char v, char *str){ +static const char *setmotvals(char v, const char *str){ omitwsp(str); - uint8_t Num = *str++ - '0'; + uint8_t Num = (uint8_t)(*str++ - '0'); if(Num > 1) return ERR; omitwsp(str); int32_t val; - if(!getnum(str, &val)) return ERR; - if(val < 0 || val > 0xffff) return "BadUINT16"; + if(!getnum(str, &val)) return BADCMD; + if(val < 0 || val > 0xffff) return ERR; switch(v){ case 'M': // maxsteps if(val == 0) return ERR; - the_conf.maxsteps[Num] = val; + the_conf.maxsteps[Num] = (uint16_t)val; break; case 'R': // reverse if(val && val != 1) return ERR; - the_conf.reverse[Num] = val; + the_conf.reverse[Num] = (uint8_t)val; break; default: return ERR; } @@ -449,12 +470,12 @@ static char *setmotvals(char v, char *str){ } // process motor command: start/stop -static char *motor_cmd(char *str){ +static const char *motor_cmd(const char *str){ omitwsp(str); - uint8_t Num = *str++ - '0'; + uint8_t Num = (uint8_t)(*str++ - '0'); int32_t steps; stp_status st; - if(Num > 1) return "Num>1"; + if(Num > 1) return ERR; omitwsp(str); switch(*str++){ case 'M': @@ -485,3 +506,34 @@ static char *motor_cmd(char *str){ } return ERR; } + +// set value of usteps +static const char *setUSTEPS(const char *str){ + omitwsp(str); + uint16_t x; + if(getu16(str, &x)) return BADCMD; + switch(x){ + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + the_conf.usteps = (uint8_t)x; + break; + default: + return ERR; + } + return ALLOK; +} + +// set value of accdecsteps +static const char *setACCDEC(const char *str){ + omitwsp(str); + uint16_t x; + if(getu16(str, &x)) return BADCMD; + if(x < ACCDECSTEPS_MIN || x > 255) return ERR; + the_conf.accdecsteps = (uint8_t)x; + stp_chspd(); + return ALLOK; +} diff --git a/STM32/steppers/proto.h b/STM32/steppers/proto.h index 405d7cf..d76e8f4 100644 --- a/STM32/steppers/proto.h +++ b/STM32/steppers/proto.h @@ -21,9 +21,9 @@ * */ #pragma once -#ifndef __PROTO_H__ -#define __PROTO_H__ +#ifndef PROTO_H__ +#define PROTO_H__ -char* process_command(char *cmdbuf); +const char* process_command(const char *cmdbuf); -#endif // __PROTO_H__ +#endif // PROTO_H__ diff --git a/STM32/steppers/rme.html b/STM32/steppers/rme.html index 21e9901..286f3ec 100644 --- a/STM32/steppers/rme.html +++ b/STM32/steppers/rme.html @@ -237,7 +237,7 @@ just ALL OK if succeed.

Motors' manipulation

-

Next char should be ‘0’ or ‘1’ — motor’s number. If wrong, Num>1 answer would be returned. +

Next char should be ‘0’ or ‘1’ — motor’s number. If wrong, BADCMD answer would be returned. There’s only two commands in this section:

    @@ -261,9 +261,10 @@ There’s only two commands in this section:

    write flash command.

      +
    • A num - set value of ACCDECSTEPS (approximate amount of steps to do acceleration/deceleration) to num
    • C#num - set current speed to num for motor #
    • -
    • D num - set denominator to number num
    • -
    • E num - set numerator
    • +
    • D$num - set denominator $ (D - Vdd, I - current, M - 12V) to number num
    • +
    • E$num - set numerator $
    • I num - set device ID
    • M#num - set maxsteps (num is 1..65535) for motor #
    • P num - properties of internal pullup (0 - disabled, other or without num - enabled)
    • @@ -272,13 +273,14 @@ There’s only two commands in this section:

    • T num - set end-switches threshold (in ADU, near 0 for Hall switch, 2048 for user button and 4096 for released state)
    • U num - set USART speed to num bits per second
    • +
    • u num - set value of USTEPS (amount of microsteps per one step)

    Motor speed setters

    To set motor speed to N steps per second, give command C or S with argument equal to -3000/N. E.g. to set current speed for DevID=0, motor0 to 50 steps per second give command 0SC050.

    +3000/N. E.g. to set current speed for DevID=0, motor0 to 50 steps per second give command 0SC060.

    Denominator and numerator setters

    diff --git a/STM32/steppers/steppers.bin b/STM32/steppers/steppers.bin index 39d0cede723bb02124d8e0975bbaca5a4396307a..ae9c948028334204171895213a7b8d4a1d85c1b9 100755 GIT binary patch delta 4853 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 G}=myf})H-TB^HZnjYIom+ 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 the_conf.maxsteps[nmotor]) return STPS_TOOBIG; int8_t d; if(steps < 0){ d = -1; steps = -steps; }else d = 1; // positive direction - if(the_conf.maxsteps[nmotor] && steps > the_conf.maxsteps[nmotor]) return STPS_TOOBIG; // check end-switches if(eswStatus(nmotor, 0) == ESW_HALL && d == -1) return STPS_ONESW; if(eswStatus(nmotor, 1) == ESW_HALL && d == 1) return STPS_ONESW; @@ -216,7 +193,7 @@ stp_status stp_move(int nmotor, int32_t steps){ } // turn on EN pin (0) pin_clear(MPORT[nmotor], MENPIN[nmotor]); - steps_left[nmotor] = steps; + steps_left[nmotor] = (uint32_t)steps; // setup timer & start it TIM_TypeDef *TIMx = nmotor ? TIM3 : TIM14; TIMx->ARR = stphigharr[nmotor]; @@ -231,7 +208,7 @@ stp_status stp_move(int nmotor, int32_t steps){ void stp_chARR(int n, int32_t val){ TIM_TypeDef *TIMx = n ? TIM3 : TIM14; if(val < 2) val = 2; - TIMx->ARR = val; + TIMx->ARR = (uint32_t)val; } void stp_stop(int n){ // stop motor by demand or @ end-switch @@ -265,25 +242,30 @@ static void stpr_int(int n){ }else return; switch(state[n]){ case STP_ACCEL: // acceleration phase - arrval = TIMx->ARR - stpsteparr[n]; + arrval = (uint16_t)TIMx->ARR - stpsteparr[n]; tmp = stplowarr[n]; if(arrval <= tmp || arrval > stphigharr[n]){ arrval = tmp; state[n] = STP_MOVE; // end of acceleration phase + DBG("AccEnd\n"); } TIMx->ARR = arrval; break; case STP_DECEL: // deceleration phase - arrval = TIMx->ARR + stpsteparr[n]; + arrval = (uint16_t)TIMx->ARR + stpsteparr[n]; tmp = stphigharr[n]; if(arrval >= tmp || arrval < stplowarr[n]){ arrval = tmp; state[n] = STP_MVSLOW; // end of deceleration phase, move @ lowest speed + DBG("DecEnd\n"); } TIMx->ARR = arrval; break; case STP_MOVE: // moving with constant speed phases - if(steps_left[n] <= ACCDECSTEPS) state[n] = STP_DECEL; // change moving status to decelerate + if(steps_left[n] <= ACCDECSTEPS){ + state[n] = STP_DECEL; // change moving status to decelerate + DBG("->Dec\n"); + } break; case STP_MVSLOW: // nothing to do here: all done before switch() @@ -296,6 +278,7 @@ static void stpr_int(int n){ dir[n] = 0; steps_left[n] = 0; state[n] = STP_SLEEP; + DBG("Stop\n"); break; } } diff --git a/STM32/steppers/steppers.geany b/STM32/steppers/steppers.geany deleted file mode 100644 index 7185bb8..0000000 --- a/STM32/steppers/steppers.geany +++ /dev/null @@ -1,37 +0,0 @@ -[editor] -line_wrapping=false -line_break_column=100 -auto_continue_multiline=true - -[file_prefs] -final_new_line=true -ensure_convert_new_lines=true -strip_trailing_spaces=true -replace_tabs=true - -[indentation] -indent_width=4 -indent_type=0 -indent_hard_tab_width=4 -detect_indent=false -detect_indent_width=false -indent_mode=3 - -[project] -name=Steppers -base_path=/home/eddy/Docs/SAO/Zeiss-1000/Simple_photometer/STM32/ - -[long line marker] -long_line_behaviour=1 -long_line_column=100 - -[files] -current_page=4 -FILE_NAME_0=0;C;0;EUTF-8;0;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2Fmain.c;0;4 -FILE_NAME_1=130;Make;0;EUTF-8;1;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2FMakefile;0;4 -FILE_NAME_2=0;Markdown;0;EUTF-8;0;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2FReadme.md;0;4 -FILE_NAME_3=0;C;0;EUTF-8;0;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2Fusart.c;0;4 -FILE_NAME_4=0;C;0;EUTF-8;0;1;0;%2Fhome%2Feddy%2FDocs%2FSAO%2FZeiss-1000%2FSimple_photometer%2FSTM32%2Fusart.h;0;4 - -[VTE] -last_dir=/home/eddy diff --git a/STM32/steppers/steppers.h b/STM32/steppers/steppers.h index f028a7b..1e89715 100644 --- a/STM32/steppers/steppers.h +++ b/STM32/steppers/steppers.h @@ -21,10 +21,16 @@ * */ #pragma once -#ifndef __STEPPERS_H__ -#define __STEPPERS_H__ +#ifndef STEPPERS_H__ +#define STEPPERS_H__ #include "stm32f0.h" +#include + +// the lowest speed equal to [max speed] / LOWEST_SPEED_DIV +#define LOWEST_SPEED_DIV (30) +// minimal value of ACCDECSTEPS +#define ACCDECSTEPS_MIN (30) extern int32_t mot_position[]; extern uint32_t steps_left[]; @@ -54,12 +60,12 @@ typedef enum{ #define stp_stepsleft(n) (steps_left[n]) stp_state stp_getstate(int motnum); -void stp_setup(); -void stp_chspd(); +void stp_setup(void); +void stp_chspd(void); stp_status stp_move(int nmotor, int32_t steps); void stp_stop(int n); -void stp_process(); -void stp_disable(); +void stp_process(void); +void stp_disable(void); void stp_chARR(int n, int32_t val); -#endif // __STEPPERS_H__ +#endif // STEPPERS_H__ diff --git a/STM32/steppers/usart.c b/STM32/steppers/usart.c index b1640b1..b635a3f 100644 --- a/STM32/steppers/usart.c +++ b/STM32/steppers/usart.c @@ -27,8 +27,8 @@ extern volatile uint32_t Tms; static int datalen[2] = {0,0}; // received data line length (including '\n') +static int dlen = 0; // length of data (including '\n') in current buffer int linerdy = 0, // received data ready - dlen = 0, // length of data (including '\n') in current buffer bufovr = 0, // input buffer overfull txrdy = 1 // transmission done ; @@ -113,7 +113,7 @@ void usart1_isr(){ if(!tmout) tmout = 1; // prevent 0 #endif // read RDR clears flag - uint8_t rb = USART1->RDR; + char rb = (char)USART1->RDR; if(datalen[rbufno] < UARTBUFSZ){ // put next char into buf rbuf[rbufno][datalen[rbufno]++] = rb; if(rb == '\n'){ // got newline - line ready @@ -177,13 +177,13 @@ TXstatus usart1_send(char *str){ return ALL_OK; }*/ -TXstatus usart1_send(char *str){ +TXstatus usart1_send(const char *str){ if(!txrdy) return LINE_BUSY; - int i; - for(i = 0; i < UARTBUFSZ; ++i){ + if(*str == 0) return ALL_OK; + for(int i = 0; i < UARTBUFSZ; ++i){ char c = *str++; - if(c == 0){ c = '\n'; i = UARTBUFSZ;} - USART1->TDR = c; + if(c == 0){c = '\n'; i = UARTBUFSZ;} + USART1->TDR = (uint16_t)c; while(!(USART1->ISR & USART_ISR_TXE)); } txrdy = 1; diff --git a/STM32/steppers/usart.h b/STM32/steppers/usart.h index a695020..02b92b0 100644 --- a/STM32/steppers/usart.h +++ b/STM32/steppers/usart.h @@ -19,10 +19,11 @@ * MA 02110-1301, USA. */ #pragma once -#ifndef __USART_H__ -#define __USART_H__ +#ifndef USART_H__ +#define USART_H__ #include "stm32f0.h" +#include // input and output buffers size #define UARTBUFSZ (64) @@ -43,19 +44,24 @@ typedef enum{ extern int linerdy, bufovr, txrdy; extern int trbufidx; -void USART1_config(); +void USART1_config(void); int usart1_getline(char **line); -TXstatus usart1_send(char *str); +TXstatus usart1_send(const char *str); #define usart1_send_blocking(str) do{}while(LINE_BUSY == usart1_send(str)) +#ifdef EBUG + #define DBG(str) usart1_send_blocking(str) +#else + #define DBG(str) +#endif #define SENDBUF() do{usart1_send_blocking(gettrbuf()); cleartrbuf();}while(0) #define cleartrbuf() do{trbufidx = 0;}while(0) #define trbufisfull() (trbufidx) int put2trbuf(char c); int write2trbuf(const char *str); -char *gettrbuf(); +char *gettrbuf(void); int put_int(int32_t N); int put_uint(uint32_t N); -#endif // __USART_H__ +#endif // USART_H__