CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to

Stepper motor pulse train

Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message

Joined: 31 Oct 2016
Posts: 462
Location: Montenegro

View user's profile Send private message

Stepper motor pulse train
PostPosted: Wed Apr 12, 2023 1:01 pm     Reply with quote

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 Very Happy

// **************************************************************************
// 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>
#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

// ************************************************************
// ************************************************************
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
// ------------------------------------------------------------
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);

   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.
// ............................................................
// 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
         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.
   }         // while(TRUE)
}            // main
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2001, 2005 phpBB Group