/* * geany_encoding=koi8-r * stepper.c * * Copyright 2017 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 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 "includes.h" /* * Half-step mode: * D |----|----| | | | | |----| * D | | |----|----|----|----|----| | * C | |----|----|----| | | | | * C |----| | | |----|----|----|----| * B | | | |----|----|----| | | * B |----|----|----| | | |----|----| * A | | | | | |----|----|----| * A |----|----|----|----|----| | | | * * In full-step mode pulse rises sequentally: D->C->B->A * 0 0000 * 1 0001 * 2 0010 * 3 0011 * 4 0100 * 5 0101 * 6 0110 * 7 0111 * 8 1000 * 9 1001 *10 1010 *11 1011 *12 1100 *13 1101 *14 1110 *15 1111 */ // winding1: [13], winding2: [24] // microsteps: [1234] = 1000, 1100, 0100, 0110, 0010, 0011, 0001, 1001 -- for ULN // [1324] = 1000, 1010, 0010, 0110, 0100, 0101, 0001, 1001 - bipolar // 1000, 1010, 0010, 0110, 0100, 0101, 0001, 1001 - half-step // 1010, 0110, 0101, 1001 - full step //static const uint8_t usteps[8] = {8, 12, 4, 6, 2, 3, 1, 9}; // ULN - unipolar, active 1 //static const uint8_t usteps[8] = {7, 3, 11, 9, 13, 12, 14, 6}; // unipolar, active is 0 static const uint8_t usteps_matrix[8][8] = { {0b1000, 0b1100, 0b0100, 0b0110, 0b0010, 0b0011, 0b0001, 0b1001}, // [1234] {0b0010, 0b0110, 0b0100, 0b1100, 0b1000, 0b1001, 0b0001, 0b0011}, // [3214] {0b1000, 0b1001, 0b0001, 0b0011, 0b0010, 0b0110, 0b0100, 0b1100}, // [1432] {0b1000, 0b1010, 0b0010, 0b0110, 0b0100, 0b0101, 0b0001, 0b1001}, // [1324] // inversion: cat | sed -e 's/0b/x/g' -e 's/0/y/g' -e 's/1/0/g' -e 's/y/1/g' -e s'/x/0b/g' {0b0111, 0b0011, 0b1011, 0b1001, 0b1101, 0b1100, 0b1110, 0b0110}, // [1234] {0b1101, 0b1001, 0b1011, 0b0011, 0b0111, 0b0110, 0b1110, 0b1100}, // [3214] {0b0111, 0b0110, 0b1110, 0b1100, 0b1101, 0b1001, 0b1011, 0b0011}, // [1432] {0b0111, 0b0101, 0b1101, 0b1001, 0b1011, 0b1010, 0b1110, 0b0110}, // [1324] }; uint8_t const *usteps = usteps_matrix[1]; static int8_t Ustep = 0; // current microstep count uint16_t Steps_left; // steps left to proceed (absolute value) static uint8_t direction = 0; // ==1 if rotate CCW static uint8_t cur_motor = 0; // current motor number volatile uint8_t stepper_pulse = 0; // interrupt flag, used in main.c void stepper_setup(){ TCCR1B |= _BV(WGM12); // configure timer1 for CTC mode, TOP is OCR1A OCR1A = 1000; // set the CTC compare value - 1kHz (125steps per second) TCCR1B |= _BV(CS11); // start the timer at 8MHz/8 = 1MHz TIMSK |= _BV(OCIE1A); // enable the CTC interrupt } /** * Change TIM1 speed * Period = 8 * 65535/(spd + 10) microseconds */ uint8_t stepper_ch_speed(char *spd){ int16_t newval; if(readInt(spd, &newval)){ if(newval > -9 && newval < 0x7fff){ uint16_t O = 0xffff / (newval + 10); //TIMSK &= ~_BV(OCIE1A); // disable timer interrupt OCR1A = O; //TCNT1 = 0; // reset counter //TIMSK |= _BV(OCIE1A); }else{ DBG("Bad speed value\n"); return 1; } } return 0; } /** * Check endswitches * @return 0 if none pressed, 1 if "-", 2 if "+" */ static uint8_t check_endsw(uint8_t Nmotor){ // PA1 - "-" - 1, PA0 - "+" - 2 uint8_t pc = PINA, sw = 0; // if(0 == (pc & _BV(0))) sw = 1; // if(0 == (pc & _BV(1))) sw = 2; if(0 == (pc & _BV(0))) sw = 2; if(0 == (pc & _BV(1))) sw = 1; return sw; } /* Steppers: 7 - turret1 6 - turret2 5 - analisator 4 - collimator */ /** * get end-switches state for all motors or only Nth * @param Nmotor - number of given motor */ void stepper_get_esw(uint8_t Nmotor){ if(Nmotor < 1 || Nmotor > 4) return; // no running motor // [1 x St=y] char str[] = "[1 0 St=0]\n"; // 3 - motor number, 8 - endswitch (3 if none) str[3] = Nmotor + '0'; uint8_t sw = check_endsw(Nmotor); if(sw == 0) sw = 3; str[8] = sw + '0'; usart_send(str); } /** * move stepper number Nmotor by Nsteps steps * @return 0 if all OK, 1 if error occured */ uint8_t stepper_move(uint8_t Nmotor, int16_t Nsteps){ if(Nmotor < 1 || Nmotor > 4 || Steps_left) return 1; TIMSK &= ~_BV(OCIE1A); // disable timer interrupt // turn all OFF STPRS_OFF(); // turn on the motor we need PORTC |= (1 << (Nmotor+3)); cur_motor = Nmotor; uint8_t c = check_endsw(Nmotor); if(!Nsteps){ // check endswitches stepper_get_esw(Nmotor); STPRS_OFF(); cur_motor = 0; return 1; } if(c){ if(c == 1){if(Nsteps > 0) c = 0;} else if(Nsteps < 0) c = 0; } if(c){ stop_motors(); return 1; // already at end-switch in given direction } if(Nsteps < 0){ // CCW Nsteps = -Nsteps; direction = 1; }else direction = 0; // CW Steps_left = Nsteps; TCNT1 = 0; // reset counter TIMSK |= _BV(OCIE1A); return 0; } void stop_motors(){ stepper_get_esw(cur_motor); // turn off all pulses to place motor in free state & prevent undesirable behaviour STPRS_OFF(); TIMSK &= ~_BV(OCIE1A); // disable timer interrupt stepper_pulse = 0; Steps_left = 0; Ustep = 0; cur_motor = 0; } /** * process stepper pulses generation @ timer event */ void stepper_process(){ stepper_pulse = 0; uint8_t port = PORTC & 0xf0; // save old port state & clear clocking PORTC = port | usteps[Ustep]; uint8_t sw = check_endsw(cur_motor); // 1 - "-", 2 - "+", 0 - none if(direction){ // CCW if(--Ustep < 0){ Ustep = 7; --Steps_left; } if(sw == 1){ stop_motors(); return; } }else{ // CW if(++Ustep > 7){ Ustep = 0; --Steps_left; } if(sw == 2){ stop_motors(); return; } } if(Steps_left == 0) stop_motors(); } /** * User can change current stepper phases table * N - position in table from 'a' (0) to 'h' (7) * return 0 if all OK */ uint8_t chk_stpr_cmd(char N){ if(N < '0' || N > '7') return 1; usteps = usteps_matrix[N-'0']; return 0; } /** * Timer 1 used to generate stepper pulses */ ISR(TIMER1_COMPA_vect){ stepper_pulse = 1; // say that we can generate next microstep }