Embedded Wednesdays

 

Week 8 - Spin with control.


This week we will be bring together stepper motors, timers, and interrupts to get position control.


As we saw in week 5, stepper motors are precision pieces that do exactly what you tell them to do, but we were only able to make them spin slowly and it was difficult to have them turn exactly a certain number of steps.


In week 6 we started using timers to generate PWM pulse trains to make our LEDs dim. Generating PWMs is a very useful thing to do, but it will just cause a stepper motor to spin until the power is removed, we can make the motors move but we need more position control.


What we need is another control method that is easy. I want to generate exactly N pulses with a particular timing. “N pulses” gives me a particular position, since each pulse gives one step. “Particular timing” gives me the means to step fast enough to satisfy the mechanicals of our system, not too slow to violate our real-time needs, and not too fast so that the motor stalls.


Making things easy ends up being very hard. We need to bring together timers and interrupts to generate exactly N pulses to drive our stepper motors.


The stepper motor is attached to pin PE11. PE11 can be configured to act as the output of timer 1, on channel 2. We know that we can generate PWMs on timers to dim LEDs, what we are going to do is reprogram timer 1 to generate a 50% PWM at a particular frequency.


The trick bit comes in when we use a thing called the repetition counter on timer 1. The repetition counter gets decremented every time a single PWM pulse is generated. When the repetition counter hits 0, an interrupt is generated.


The basic idea is to set up a PWM stream, with the frequency and pulse width that we want, and preset the repetition counter with the number of steps that we want the motor to move. When we start the PWM, the motor moves, once the number of steps has been generated, an interrupt fires. In the interrupt routine we turn off the PWM.


Due to the timer being a general purpose piece of hardware and not designed specifically for stepper motors, the repetition count register (RCR) is only 8 bits wide (wah-wa-wa), so we can only generate up to 255 pulses before we have to take an interrupt.


So, here’s the code:

Configure the HAL

Fire up an LED and turn it on.

We’re going to use port E for the motor and timer 1, so turn on their clocks to bring them to life.

Configure the motor control pins. PE9 is direction and PE11 is step.

The timer 1 interrupt that we are interested in is called updated, give it a priority of 0, and sub priority of 4.

Configure timer 1 to generate a PWM, using the internal clock source / 420  with a full wave width of 10 and a high time of 5, generate an update interrupt after 200 full waves.

Enable the update interrupt.

Start generating PWM on timer 1 using interrupts.


Until the world ends

    Delay one half second.

    Restart the PWM on timer 1 using interrupts.


When an interrupt happens,

    Turn off the PWM on timer 1

    Toggle the LED to show that interrupts are happening.





#include "stm32f4xx.h"

#include "stm32f401_discovery.h"


TIM_HandleTypeDef htim1;


const uint32_t prescaler = 420;


int main(void) {

GPIO_InitTypeDef GPIO_InitStruct;

TIM_OC_InitTypeDef sConfigOC;

TIM_ClockConfigTypeDef sClockSourceConfig;


HAL_Init();

BSP_LED_Init(LED3);

BSP_LED_On(LED3);


__GPIOE_CLK_ENABLE();

__TIM1_CLK_ENABLE();


GPIO_InitStruct.Pin = GPIO_PIN_9;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_FAST;


HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);


GPIO_InitStruct.Pin = GPIO_PIN_11;

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;

HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);


HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0, 4);

HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);


htim1.Instance = TIM1;

htim1.Init.Prescaler = prescaler;

htim1.Init.CounterMode = TIM_COUNTERMODE_DOWN;

htim1.Init.Period = 10;

htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

htim1.Init.RepetitionCounter = 200;

HAL_TIM_Base_Init(&htim1);


sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);

HAL_TIM_PWM_Init(&htim1);


sConfigOC.OCMode = TIM_OCMODE_PWM1;

sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;

sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;

sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;

sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;


sConfigOC.Pulse = 5;

HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2);


__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);

HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_2);


for (;;) {

HAL_Delay(500);

HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_2);

}

}


void TIM1_UP_TIM10_IRQHandler(void) {

HAL_TIM_IRQHandler(&htim1);

}


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) {

HAL_TIM_PWM_Stop_IT(htim, TIM_CHANNEL_2);

BSP_LED_Toggle(LED3);

}



Cool stuff

Why do we need those header files anyway?

When you breakup a C program into multiple files, the compiler will crunch up all of your code into object files and then link it all together into your final executable that does what you said. Maybe. If all of your code is in one file, the compiler has all of your code so it can do its best work, almost.

Why maybe and almost? Well, there is a stupid rule, the compiler can’t let you call a subroutine or use a data item unless it has been “declared” first. Declaring a subroutine or data item is your way of telling the compiler how that stuff is going to be used in the later code. The compiler goes from the top of the file to the bottom. You can’t use a variable and then later declare it. It has to be declared before you can use it.

That means that you have to declare all of your subroutines before you can call them. That kinda sucks because they take up space at the top of the file, and you have to scroll past them to get to the part of the file that is active. The body of a subroutine is called the definition, but by defining a subroutine you also declare it. “I will be using this subroutine at some point, and this is how it works.”

C makes this a bit easier. If you don’t declare a subroutine, it assumes that it is probably in another file and leaves space for it, declaring it like int function( int) . It assumes that your function will be returning an int and take an int as a parameter. Nice, except when you didn’t actually mean that.

Now you have a potential problem, the compiler assumes that you are going to take an int and return an int, but when you actually define your subroutine you are probably going to do something completely different. C is going to do what you said according to its rules. This is probably not what you meant.

Later versions of C gave us “prototypes” which are nothing more than a short declaration of a function but it has no body, the declaration is separated from the definition. We write:

void LED_Toggle( LED_T which);


int main( void) {

    for (;;) {

        LED_Toggle( LED4);

    }

}


void LED_Toggle( LED_T which) {

    BSP_LED_Toggle( which);

}


The first declaration of LED_Toggle is the prototype, it tells the compiler that we will be using a subroutine called LED_Toggle, it takes one parameter of type LED_T and returns nothing. That is quite different from the default. The second is the definition of LED_Toggle and since it matches the prototype, the compiler does not complain.

The people that wrote the compiler supplied a large library of code that you can use, like printf and the BSP code. This leads us to exactly the same place, all that we can do is assume that printf takes an int as a parameter and returns an int, just like it doesn’t. Well, that’s an issue.

The compiler people graciously put all of the prototypes for their subroutines in a file. These files are called header files and you use them by using the preprocessor #include command.

The preprocessor takes your C code and does file copies (#include), string replacements (#define), configuration management (#if and #ifdef), as well as stripping out comments before your code is passed on to the compiler. When the preprocessor sees a #include statement it is given the name of a file. The #include statement is then replaced by the text contained in that file.

Header files are named headers because they are put at the top of your code files so they can be used by your code that follows, and they contain all of the prototypes and defines that you need to use a separately compiled piece of code.

YOU DO NOT #INCLUDE EXECUTABLE C COMMANDS, that’s bad style. You include defines and prototypes, type defines, and enums. NOT CODE. Don’t include .c files, that leads to subroutines being defined twice or more and the compiler will just complain and you will get mad. The compiler will pick up the .c file in its normal processing, and then compile it again because it has been included, now everything is duplicated and broken. Plus it causes grief for future you.

The header file contains the publicly visible interface to a file of code, if it’s not in the header file, don’t use it. The code will be separately compiled, but the header file gives all of the information necessary to use the code.