Embedded Wednesdays
Embedded Wednesdays
Week 7 - Time has come today.
This week we will be working with the timer modules of our processors.
First, what is a timer? It is important to understand that modern computers are electronic circuits that are built to step from one state to another with a clock circuit. You’ve heard of the clock circuit, that’s the part of your computer that the manufacturers use to sell the system. The 3.2 GHz portion that makes it so much better than last years 3.0 GHz section. What this is is an oscillator, much like the quartz crystal in your watch, except much faster.
Your watch runs off of a 32768 cycles per second (hertz (Hz)) quartz crystal that is thermally stabilized by your arm. The crystal on your PC does not buzz along at 3.2 GHz, they put on a low frequency crystal, like 25MHz that is multiplied using the black magic of phase locked loops (PLL) up to the frequencies needed for the processor, USB, video, Ethernet, and so forth.
Our processor does the same thing, the board comes with an 8MHz crystal that is PLL’d up to 84MHz. This 84MHz signal is a 50% duty cycle 3.3 volt square wave that is used to set the timing on everything, the UARTs, the SPI, the bits going out to the LEDs, and the processor core. Back in Lab1 when we set the LED output bit high, that instruction was being executed at 84MHz (with lots of hand waving), and the signal got put onto the output pin at the next edge of the 84MHz clock, roughly.
The processor manufacturer included a bunch of “timers” in the processor. These are not timers like you have in you kitchen to time your rice cooking, those would be considered real-time clocks. These are sections of the device that run, somewhat, independent of the processor core. They provide input and output timing services, they count and generate pulses.
Then timers are configured to do particular duties, counting input pulses, generating PWM signals, generating pulses, and so on. The basic function is taking some signal on a pin and using the 84MHz system clock to do something useful.
A really simple example would be to generate an interrupt exactly 1000 times per second. This example doesn’t use any pins, input or output, it just uses the timer registers.
The 84MHz system clock is distributed to the various peripherals and busses, but it is way too fast for most of our uses, since the timers typically only deal with 16 bit values and counting to 64K at 84MHz doesn’t take long at all. A “prescaler” is provided to divide the bus clock down to a more reasonable range.
The timer input clock that we are using is the system bus clock. You have to check the datasheet for the processor to see which of the two internal busses the timer is attached to. Timers 2,3,4, and 5 are attached to the 42MHz internal bus, timers 1, 9, 10, and 11 are attached to the 84MHz bus.
From here on the timers get complex. They are really highly configurable and can do a bazillion functions, so continuing on means having to hit the data sheets, application notes, and example code.
Since you don’t have stepper motors and drivers yet, let’s see what we can do with our LEDs. On our circuit boards, the LEDs just happen to be connected to pins that hook into Timer 4. This timer has four subchannels, each connected to pins for input out output. Our board is laid out so that we have:
PD12 - Timer 4 Channel 1 - Green LED
PD13 - Timer 4 Channel 2 - Orange LED
PD14 - Timer 4 Channel 3 - Red LED
PD15 - Timer 4 Channel 4 - Blue LED
#include "stm32f4xx.h"
#include "stm32f401_discovery.h"
TIM_HandleTypeDef htim4;
int main(void) {
GPIO_InitTypeDef GPIO_InitStruct;
TIM_OC_InitTypeDef sConfigOC;
uint32_t period = 20;
/*
* Initialize the hardware abstaction layer.
* Turn on the clock to port D so we can control the output pins
* Initialize PD12, 13, 14, and 15 as a GPIO output.
* Turn on the clock to timer 4
* Initialize PE11 to be a PWM output, prescaler=42, high for 10 counts out of 20, on channel 2.
* Start the PWM on channel 2.
*/
HAL_Init();
__GPIOD_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM4;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
__TIM4_CLK_ENABLE();
htim4.Instance = TIM4;
htim4.Init.Prescaler = 42;
htim4.Init.CounterMode = TIM_COUNTERMODE_DOWN;
htim4.Init.Period = period;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.RepetitionCounter = 0;
HAL_TIM_PWM_Init(&htim4);
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 = period/2;
HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1);
sConfigOC.Pulse = period/3;
HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2);
sConfigOC.Pulse = period/4;
HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3);
sConfigOC.Pulse = period/5;
HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_4);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
for (;;) {
HAL_Delay(1);
}
}
For our project we will be using stepper motors. The processor’s interface to the stepper motors consists of two signals; a DIO bit that will be either high or low, indicating clockwise or counter-clockwise rotation, and a series of pulses, one per step, that have a minimum high time of 1 microsecond (1 millionth of a second) and a minimum low time of 1 microsecond.
As we saw in week 6, we can generate pulses on the step signal line, by toggling a DIO bit. The problem is that, by using HAL_Delay, I was only able to get a 500Hz output signal. Set the output high, wait 1mS., set the output low, wait 1mS. repeat. That gives my motor, with a driver set to 1/16 step, one rotation every 6.4 seconds. That’s pretty slow.
How can we speed it up? If we have the timer generate 3200 pulses per second (the motor has 200 steps per rotation and is 1/16 stepped, so 3200 steps per rotation), then we will get 1 rotation per second. More useful.
Let’s see how to do that.
#include "stm32f4xx.h"
#include "stm32f401_discovery.h"
TIM_HandleTypeDef htim1;
int main(void) {
GPIO_InitTypeDef GPIO_InitStruct;
TIM_OC_InitTypeDef sConfigOC;
uint32_t period = 20;
/*
* The stepper motor controller's step line is connected to PE11.
* Direction output is on PE9
*/
/*
* Initialize the hardware abstaction layer.
* Turn on the clock to port E
* Initialize PE9 as a GPIO output, and set it low.
* Turn on the clock to timer 1
* Initialize PE11 to be a PWM output, prescaler=84, high for 10 counts out of 20, on channel 2.
* Start the PWM on channel 2.
*/
HAL_Init();
__GPIOE_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);
HAL_GPIO_WritePin( GPIOE, GPIO_PIN_9, GPIO_PIN_RESET);
__TIM1_CLK_ENABLE();
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pin = GPIO_PIN_11;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
htim1.Instance = TIM1;
htim1.Init.Prescaler = 84;
htim1.Init.CounterMode = TIM_COUNTERMODE_DOWN;
htim1.Init.Period = period;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
HAL_TIM_PWM_Init(&htim1);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = period/2;
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;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
for (;;) {
HAL_Delay(1);
}
}
Cool stuff
Object oriented on the cheap.
Over the years I have come up with a way of organizing my programs that a lot of people find very understandable and provides a lot of the useful bits of object oriented programming. All of the functions and data that deal with a particular device are kept together. Visibility of the internal workings of these functions is minimized giving an approximation of public/private keywords in C++ providing information hiding.
C programs are organized like an office, some have everything heaped up in one cardboard box, others with everything sorted and put away in hanging folders. Using one or the other doesn’t really matter, it just comes into play in the future when you need to find something. By doing a small of extra work now, you are saving yourself time and aggravation in the future.
C compilers don’t care if your program is all in one file (cardboard box) or split amongst multiple files (folders), but giving your code some organization helps you in the future when you have to alter your program.
C requires that you call the initial subroutine in your code “main” (I will write main()). Historically main() has been kept in a file called main.c. If you take over someone’s C program, you start looking in main.c to see how the thing works. The filenames are reasonably arbitrary, you could call them 1.c fu.c and 3.c, but where is main() kept? I have no idea and I will think less of you for breaking with convention without good reason, just put it in main.c.