151 lines
4.1 KiB
C

/*
* steppers.c
*
* Copyright 2015 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "steppers.h"
#include "user_proto.h"
#include <libopencm3/stm32/timer.h>
volatile int32_t Glob_steps = 0;
volatile int Dir = 0; // 0 - stop, 1 - move to +, -1 - move to -
int32_t stepper_period = STEPPER_DEFAULT_PERIOD;
/**
* Init TIM2_CH3 (& remap to 5V tolerant PB10) [STEP] and PB11 [DIR]
*/
void steppers_init(){
// Turn off JTAG & SWD, remap TIM2_CH3 to PB10 (five tolerant)
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPBEN);
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF, AFIO_MAPR_TIM2_REMAP_PARTIAL_REMAP2);
// setup PB10 & PB11 - both opendrain
gpio_set_mode(GPIO_BANK_TIM2_PR2_CH3, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_TIM2_PR2_CH3);
// GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_TIM2_PR2_CH3);
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11);
rcc_periph_clock_enable(RCC_TIM2);
timer_reset(TIM2);
// timers have frequency of 1MHz -- 1us for one step
timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
// 36MHz of APB1 x2 = 72MHz
TIM2_PSC = STEPPER_TIM_DEFAULT_PRESCALER; // prescaler is (div - 1)
TIM2_CR1 = 0;
TIM2_CCER = 0;
TIM2_CCMR2 = 0;
TIM2_DIER = 0;
TIM2_SR = 0; // clear all flags
// PWM_OUT for TIM2_CH3
TIM2_CCMR2 = TIM_CCMR2_CC3S_OUT | TIM_CCMR2_OC3M_PWM1;
// active is low
TIM2_CCER = TIM_CCER_CC3E | TIM_CCER_CC3P;
// enable update IRQ
TIM2_DIER = TIM_DIER_UIE;
TIM2_ARR = STEPPER_DEFAULT_PERIOD;
TIM2_CCR3 = STEPPER_PULSE_LEN;
nvic_enable_irq(NVIC_TIM2_IRQ);
}
/**
* Set stepper period
*/
uint8_t set_stepper_speed(int32_t Period){
if(Period > STEPPER_MIN_PERIOD && Period < STEPPER_MAX_PERIOD){
P("Set stepper period to ");
if(Period > STEPPER_TIM_MAX_ARRVAL){ // redefine prescaler when value is too large
TIM2_PSC = STEPPER_TIM_HUNDR_PRESCALER;
Period /= 100;
print_int(Period*100);
TIM2_ARR = --Period;
}else{ // default prescaler - for 1MHz
print_int(Period);
TIM2_PSC = STEPPER_TIM_DEFAULT_PRESCALER;
TIM2_ARR = --Period;
}
stepper_period = Period;
newline();
}
return 0;
}
void stop_stepper(){
TIM2_CCR3 = 0;
TIM2_CR1 = 0;
Glob_steps = 0;
Dir = 0;
gpio_set(GPIOB, GPIO11); // clear "direction"
P("Stopped!\n");
}
/**
* move stepper motor to N steps
* if motor is moving - just add Nsteps to current counter
*/
uint8_t move_stepper(int32_t Nsteps){
int dir = 1;
if(!Nsteps) return 0;
if(TIM2_CR1){ // moving
if(!gpio_get(GPIOB, GPIO11)) dir = 0; // negative direction
Glob_steps += Nsteps;
if(Glob_steps < 0){
Glob_steps = -Glob_steps;
dir = !dir;
}
}else{ // start timer if not moving
if(Nsteps < 0){ // change direction
dir = 0;
Nsteps = -Nsteps;
}
Glob_steps = Nsteps;
TIM2_ARR = stepper_period;
TIM2_CCR3 = STEPPER_PULSE_LEN;
TIM2_CR1 = TIM_CR1_CEN;
}
// set/clear direction
P("Rotate ");
if(dir){
gpio_set(GPIOB, GPIO11);
}else{
gpio_clear(GPIOB, GPIO11);
P("counter-");
}
P("clockwise to ");
print_int(Glob_steps);
P(" steps\n");
return 0;
}
void tim2_isr(){
// Next step
if(TIM2_SR & TIM_SR_UIF){
if(--Glob_steps < 1){ // stop timer as steps ends
stop_stepper();
}
}
TIM2_SR = 0;
}
int32_t stepper_get_period(){
int32_t r = stepper_period + 1;
if(TIM2_PSC == STEPPER_TIM_DEFAULT_PRESCALER) return r;
else return r * 100;
}