| PrinceNai 
 
 
 Joined: 31 Oct 2016
 Posts: 554
 Location: Montenegro
 
 
			    
 
 | 
			
				| Stepper motor pulse train |  
				|  Posted: Wed Apr 12, 2023 1:01 pm |   |  
				| 
 |  
				| This is a code used to generate a pulse train to drive a stepper motor. It uses Tmr1 and CCP1 to do that. There are two possible ranges, 16-2040Hz or 16 - 4080Hz with a Timer1 resolution of 1us. It has an option to select a fixed number of equally spaced frequencies between  16Hz and Fmax. That was added because it is a part of a DIY motorized dolly attempt, where only a few speeds are needed from the stepper. On the oscilloscope it looks OK, still waiting for the controllers to see it in some real action   
  	  | Code: |  	  | // **************************************************************************
 // This is a very basic code that generates a pulse train for driving
 // a stepper motor. The idea is to vary the time between two
 // successive Timer1 interrupts and by doing that control the speed
 // of the stepper. In combination with CCP1 interrupt it is
 // capable of generating a pulse train with frequencies between
 // 16Hz and 2040Hz with basic steps of 8Hz or 16Hz - 4080Hz with 16. FREQUENCY_STEP
 // determines that. 8bit variable Freq_Control controls the frequency. Pot is
 // currently used to control it, but it might be anything else. There is also
 // a possibility to decide in how many steps the frequency will go from min. to
 // max. That is done via NUMBER_OF_FREQUENCIES define. The code assumes 1us
 // resolution of Timer1.
 // Pulse width is currently 36us, change CCPR1L value to make it wider or
 // narrower.
 // **************************************************************************
 
 #include <18F46k22.h>
 #FUSES NOWDT,NOPROTECT, NOPUT , NOLVP,  BROWNOUT
 #device ADC=8
 #use delay(internal=32000000)
 
 #byte CCPR1H = getenv("SFR:CCPR1H")                            // make visible the low and high bytes of the CCPR1 holding register
 #byte CCPR1L = getenv("SFR:CCPR1L")                            // to allow them being easily used in the code
 
 #define STEPPER_PIN PIN_C2                                     // pulses will be here
 
 // Frequency is controlled by an 8 bit variable (ADC, whatever).
 // Without scaling the frequency changes for 8 or 16Hz for every step
 // of that variable. With define below we set how "coarse" we want
 // this change of frequency to be. So with 10 we have ten steps
 // of 200Hz, with 25 twenty five steps of 80Hz and so on.
 
 #define NUMBER_OF_FREQUENCIES   10                             // how many steps there will be between 0 and max. frequency. 255 gives you no scaling.
 int8 Scaling_Factor = 255/NUMBER_OF_FREQUENCIES;               // used for the math behind it
 
 #define FREQUENCY_STEP 8                                       // this number determines the maximum frequency of pulses. 8 means 2040Hz, 16 means 4080Hz.
 //#define FREQUENCY_STEP 16                                    // It can be any number between these two. It also determines the step of the frequency
 // for each increment of Freq_Control variable
 
 int8 Freq_Control;                                             // variable that controls the frequency of pulses
 int16 DesiredFrequency;                                        // calculated from Freq_Control
 int32 Needed_Tmr1_Period;                                      // Timer1 period needed for the desired frequency
 int16 Tmr1_Preload;                                            // Timer1 preload to achieve the period needed
 
 // ************************************************************
 // ************************************************************
 #INT_TIMER1
 void  TIMER1_isr(void)
 {
 delay_cycles(7);                                            // fine tune when preloading (empirical value as measured in MPLAB SIM to get just the right frequencies)
 set_timer1(Tmr1_Preload);                                   // set Timer1 to repeatedly overflow with the desired frequency
 output_low(STEPPER_PIN);                                    // stop pulse on C2
 }
 // ------------------------------------------------------------
 #INT_CCP1
 void  CCP1_isr(void)
 {
 output_high(STEPPER_PIN);                                   // start pulse on C2
 }
 // ************************************************************
 // ************************************************************
 void main()
 {
 setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);                   // 1us resolution at 32Mhz
 setup_ccp1(CCP_COMPARE_INT);                                // setup CCP1 in compare mode, using Timer1
 
 setup_adc_ports(sAN0, VSS_VDD);                             // setup ADC on channel 0
 setup_adc(ADC_CLOCK_DIV_32 | ADC_TAD_MUL_20);
 set_adc_channel(0);
 
 enable_interrupts(INT_TIMER1);
 enable_interrupts(GLOBAL);
 
 CCPR1H = 0xFF;                                              // set CCP1 register to the desired trip point where Tmr1 count will cause interrupt, here on 65500.
 CCPR1L = 0XDC;                                              // At that point PIN_C2 goes high and starts the pulse. Tmr1 ends it in overflow interrupt.
 
 set_timer1(0);
 // ............................................................
 while(TRUE)
 {
 // read ADC, then scale or "trim" the reading as desired via NUMBER_OF_FREQUENCIES and Scaling_Factor.
 // Looks stupid and useless to first divide and then multiply by the same number. But because it is
 // an integer division, you lose everything behind theoretical "decimal point", making a division
 // of 25/25 or 49/25 both equal to 1, leaving you with the same desired frequency for a bunch of different starting values of Freq_Control.
 // Original code uses olympic averaging of ADC readings for stability.
 
 Freq_Control = read_adc()/Scaling_Factor;;
 Freq_Control = Freq_Control*Scaling_Factor;
 
 if(Freq_Control == 0){                                   // shut down pulse generation if ADC reading is right in the bottom range of the pot to stop the stepper
 disable_interrupts(INT_CCP1);
 output_low(STEPPER_PIN);
 delay_ms(50);
 }
 else{
 enable_interrupts(INT_CCP1);                           // enable interrupt
 
 DesiredFrequency = FREQUENCY_STEP*(int16)Freq_Control;// calculate the desired frequency and the period of Tmr1 from that.
 
 Needed_Tmr1_Period = 1000000/DesiredFrequency;        // 1.000.000/DesiredFrequency gives the result directly in us.
 Tmr1_Preload = 65536 - (int16)Needed_Tmr1_Period + 7;   // 7us are added because it takes cca. 7us for interrupt handler to arrive to the code
 // in the interrupt itself. It corrects the error in frequencies that causes. It would be quite big at high frequencies.
 // Tmr1_Preload determines the frequency of Tmr1 interrupts.
 delay_ms(50);
 }
 
 }         // while(TRUE)
 }            // main
 
 | 
 |  |