1
\$\begingroup\$

How i can generate clock signal to L297 stepper motor controller with a PIC16f877 microcontroller?:

enter image description here

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

If you mean the CLOCK input, then for a single step, you can simply set the pin low for a short time (for a minimum of 0.5us according to the datasheet, so something like 10us would do) then set high again.

For a stream of pulses, then you could use the PWM (or Timer) peripheral. You could write a function that takes the number of steps and speed as the arguments, then sets the peripheral to pulse this number of times, incrementing the count in the Interrupt Service Routine (ISR)

This frees your main loop from doing all the work.

If you need a code example let me know and I'll post something.

Code Example

Here is some code based on the Timer 1 peripheral and Compare Peripheral.
The stepper_step(5, 50); sets up 5 pulses at 50Hz. Basically it sets the pin at the start of the Timer period (adjusted by the speed variable) then clears it using the Compare peripheral 1/10 of the period later. A count variable keeps track of the number of steps. I used the XC8 compiler:

/* 
 * File:   stepper_pulse_example_2.c
 * Author: Oli Glaser
 *
 * Created on 26 March 2013, 08:00
 */

#include <xc.h>
// Turn the Watchdog Timer off
#pragma config WDTE = OFF

/* Defines */
#define FOSC 4000000L
#define FCY (FOSC/4)L
#define TMR_MAX 65535
#define HZ 62500

/* Function definitions */
void delay(int d);
void init(void);
void stepper_step(unsigned int count, unsigned int speed);

/* Global variables */
unsigned int stepper_count = 0;
unsigned int stepper_speed = 0;
unsigned int stepper_pulse = 0;

/* Main */
int main(int argc, char** argv) {

    // Initialisation routine
    init(); 

    while(1) // Infinite loop
    {
        delay(1000);
        // Step 5 times at 50Hz
        stepper_step(5, 50);
    }
}

/* Interrupt Service Routine */
void interrupt int_routine(void)
{
    // Check it's Timer 1 that has interrupted
    if (PIE1bits.TMR1IE && PIR1bits.TMR1IF)
    {
        PIR1bits.TMR1IF = 0;            // Clear interrupt flag
        T1CONbits.TMR1ON = 0;           // Turn timer off to reset count register
        TMR1H = (stepper_speed >> 8);   // Set Timer registers
        TMR1L = (stepper_speed & 0x00FF);
        T1CONbits.TMR1ON = 1;           // Turn timer back on

        stepper_count--;        // decrement pulse count
        PIR1bits.CCP1IF = 0;    // Clear compare flag
        CCP1CON = 0x00;         // Reset Compare peripheral
        // Set for Compare mode: set pin and then clear pin on match
        if(stepper_count) CCP1CONbits.CCP1M = 0b1001; 
        CCPR1H = (stepper_pulse >> 8);  // Set clear pin timing
        CCPR1L = (stepper_pulse & 0x00FF);

        // Have pulses completed?
        if(stepper_count == 0)
        {
            T1CONbits.TMR1ON = 0;   // Turn Timer 1 off
            CCP1CON = 0x00;         // Reset Compare peripheral
            PORTCbits.RC5 = 0;      // Make sure pin is left low
        }
    }
    // process other interrupt sources here, if required
}

/* Initialisation routine */
void init(void)
{
    // Make all pins digital
    ANSEL = 0;
    ANSELH = 0;
    TRISCbits.TRISC5 = 1; // Disable RC5 output driver till PWM configured
    CCP1CON = 0x00; // Reset CCP Peripheral    
    PIR1bits.TMR1IF = 0;    // Clear Timer 1 interrupt flag
    PIR1bits.CCP1IF = 0;    // Clear Compare interrupt flag

    // Setup Timer 1 
    T1CON = 0;
    T1CONbits.T1CKPS0 = 1;  // T1CKPS = 11 = 1:8 prescaler
    T1CONbits.T1CKPS1 = 1;  // so timer clock = 1MHz / 8 = 125kHz
    /* Timer 1 clock is 125kHz, so for example if we want 2Hz,
     * we divide 125kHz by 2 = 62,500.
     * Then we subtract this from the rollover value of 65,535,
     * so 65,535 - 62,500 = 3035 which is 0x0BDB in hex.
     * Timer 1 has two 8-bit high and low registers,
     * so we put 0x0B in the high and 0xDB in the low */
    INTCONbits.GIE = 1;     // Enable global interrupts
    INTCONbits.PEIE = 1;    // Enable peripheral interrupts
    PIR1bits.TMR1IF = 0;    // Clear Timer 1 interrupt flag
    PIE1bits.TMR1IE = 1;    // Enable Timer 1 interrupt
    PORTCbits.RC5 = 0;      // Clear RC5
    TRISCbits.TRISC5 = 0;   // Sets pin RC5 to output
}

 /* This routine works well for slow steps
  * from 2Hz - 1kHz or so.
  * Above this using the PWM is better.
  * count = number of pulses
  * speed = pulse speed in Hz */
void stepper_step(unsigned int count, unsigned int speed)
{
    unsigned int temp_speed = HZ / (speed / 2);
    stepper_count = count;
    if(stepper_count < 1) stepper_count = 1;
    stepper_speed = (TMR_MAX - (temp_speed));
    // Make duty cycle 1:10
    stepper_pulse = (stepper_speed + ((temp_speed) / 10));
    // Split 16-bit integer over the two 8-bit Timer 1 registers
    TMR1H = (stepper_speed >> 8);
    TMR1L = (stepper_speed & 0x00FF);
    // Do the same for the compare registers
    CCPR1H = (stepper_pulse >> 8);
    CCPR1L = (stepper_pulse & 0x00FF);
    PIR1bits.CCP1IF = 0;
    CCP1CON = 0x00;             // Reset CCP Peripheral
    CCP1CONbits.CCP1M = 0b1001; // Set up for Compare mode, clear pin on match
    T1CONbits.TMR1ON = 1;
}

/* Rough delay routine */
void delay(int d)
{
    int i;  // Declare variable to be used in the loop

    while(d)    // While d > 0
    {
        i = 100;    // set i to 100 for inner loop
        while(i)    // while i > 0
        {
            i--;    // decrement i (e.g. i will equal 99, 98, 97,...)
        }
        d--;        // decrement d
    }
}

Here is a scope shot of the 5 pulses at 50Hz (notice the cursor measurement of 50Hz in the left hand box)

Scope Capture

\$\endgroup\$
4
  • \$\begingroup\$ I will need a stream of pulses so i will be very glad if you could post an example in order to understand it and change it for my project. I use assembly code, but if you dont have something in assembly i can change the C code. Thanks! \$\endgroup\$
    – 20317
    Commented Mar 25, 2013 at 12:35
  • 1
    \$\begingroup\$ @20317 - no problem, I'll post something in a few hours when I finish work. My assembly is a bit too rusty to do quickly, so it will be in C though (you can hopefully still get the idea - maybe compile and disassemble it) \$\endgroup\$
    – Oli Glaser
    Commented Mar 25, 2013 at 16:04
  • \$\begingroup\$ @20317 - Sorry it's been a while, didn't finish till late last night. I added a rough example using the Timer and Compare peripherals. You can also use the PWM peripheral (better at faster speeds) I will try and add another snippet for this later. \$\endgroup\$
    – Oli Glaser
    Commented Mar 26, 2013 at 9:59
  • \$\begingroup\$ You are the best! Thank you very much! I am going to understand and then change to assembly code. Much appreciated what you did! \$\endgroup\$
    – 20317
    Commented Mar 27, 2013 at 0:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.