Monday, December 30, 2013

Working with STM32F4 Temperature Sensor

Working with the on-board Temperature Sensor


Now that we have worked with the Timer peripheral on the STM32F4 Discovery board, I will move on to setting up the sampling of the on board temperature sensor.

To start open up your existing project in CooCox CoIDE.  In my case I am going to create a new project called Blog_Example_3 and copy the contents from Blog_Example_2.  As I stated in the last blog, I will just keep adding to the existing code, this way we can see if we encounter any conflicts with the code.  When we created the project before, we came to a tab labeled "Repository".  If this tab is not open you can either click on "Browse in Repository" under the main tab or go to the View -> Repository in the toolbar.  Once you open the "Repository" you will see the items you selected from the first example.  In this example we are going to use the analog to digital converter so select the ADC peripheral and you will see the source file and header file added to your project.


Now that we have the files added, we can start configuring our ADC in the main.c file.  Near the top of the page, under the "Includes" section add the following lines:

// Must be included to setup the A/D
#include "stm32f4xx_adc.h"

After the includes, we need to create our label for the ADC structure, similar to the way we labeled the GPIO structure and TIM structure.   The following code should be just before the main loop.  


/**********************************************************************************
 *
 * The ADC_InitTypeDef is a structure defined in the stm32f4xx_adc.h file.
 * The ADC_CommonInitTypeDef is a structure defined in the stm32f4xx_adc.h file.
 *
 **********************************************************************************/
ADC_InitTypeDef ADC_InitStruct;

ADC_CommonInitTypeDef ADC_CommonInitStruct

Next we are going to de-initialize all ADCs peripherals registers to their default reset values.  Add the following line of code.

ADC_DeInit();

Next we need to configure the ADC peripheral.  Just like the GPIO and TIM there is an RCC associated with with ADC peripheral.  If you look back at the datasheet for the STM32F407VG processor you will find that the ADC1, ADC2 and ADC3 are on the APB2 bus.  Therefore we need to enable the High Speed APB2 peripheral clock.  After the Timer 2 configuration add the following line of code.

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

After this the "Common" ADC settings needs to be configured.  Then the ADC initial settings need to be configured.  These settings include the resolution, scan mode, continuous mode, external trigger configuration, data alignment and number of conversions.  See code below:

ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div8;
ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStruct);

ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStruct.ADC_ScanConvMode = DISABLE;
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStruct);

The next statement configures the ADC1 channel, rank and sample time

ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 1, ADC_SampleTime_144Cycles);

Then we enable the internal temperature sensor

ADC_TempSensorVrefintCmd(ENABLE);

The next statement enables ADC1

ADC_Cmd(ADC1, ENABLE);

Now that we have the ADC configured, we need to read the temperature.  For now we will just use the debugger to read the value of the sensor.  But first we need to read the data then convert it to a temperature.  To find the formula for the conversion of the data, we can look at the reference manual for the STM32F4.  The formula is

Temperature (in °C) = {(VSENSE – V25) / Avg_Slope} + 25

Where:
V25 = VSENSE value for 25° C
Avg_Slope = average slope of the temperature vs. VSENSE curve (given in mV/°C or μV/°C)

Looking at the data sheet for the specific STM32F407VG processor the following values are found

V25 = 0.76V
Avg_Slope = 2.5mV/°C

We will create a float variable called TemperatureValue.  This will be located just after the program main.  I have highlighted the new variable we have created.

int main(void)
{

        unsigned int _btn_count = 0;
        float TemperatureValue = 0;

Now we need to write the logic the read the temperature input and convert it using the formula listed above.

ADC_SoftwareStartConv(ADC1); //Start the conversion
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
     ; //Processing the conversion
TemperatureValue = ADC_GetConversionValue(ADC1); //Return the converted data
TemperatureValue *= 3300;
TemperatureValue /= 0xfff; //Reading in mV
TemperatureValue /= 1000.0; //Reading in Volts
TemperatureValue -= 0.760; // Subtract the reference voltage at 25°C
TemperatureValue /= .0025; // Divide by slope 2.5mV

TemperatureValue += 25.0; // Add the 25°C

To read the value of the sensor make sure the variables window is open to see the actual converted value of the temperature sensor.


Run the debugger and watch the temperature.

The full main.c code is listed below


/* Includes ------------------------------------------------------------------*/
// Must be included if using STM32F4 Discovery board or processor
#include "stm32f4xx.h"

// Must be included if using GPIO (General purpose I/O) peripheral
#include "stm32f4xx_gpio.h"

// Must be included to setup general purpose I/O
// Some peripherals require the setup of RCC (Reset and clock controller)
#include "stm32f4xx_rcc.h"

// Must be included to setup timers
#include "stm32f4xx_tim.h"

// Must be included to setup the A/D
#include "stm32f4xx_adc.h"

void Delay(__IO uint32_t nCount)
{
  while(nCount--)
  {
  }
}

/**********************************************************************************
 *
 * The GPIO_InitTypeDef is a structure defined in the stm32f4xx_gpio.h file.
 * This is a simple way to use ST's library to configure the I/O.
 *
 * This is a common setup for the other peripherals as well.  Each peripheral will have
 * different properties which can be found in the header files and source files.
 *
**********************************************************************************/

GPIO_InitTypeDef  GPIO_InitStruct;


/**********************************************************************************
 *
 * The TIM_TimeBaseInitTypeDef is a structure defined in the stm32f4xx_tim.h file.
 *
**********************************************************************************/

TIM_TimeBaseInitTypeDef TIM_InitStruct;


/**********************************************************************************
 *
 * The ADC_InitTypeDef is a structure defined in the stm32f4xx_adc.h file.
 * The ADC_CommonInitTypeDef is a structure defined in the stm32f4xx_adc.h file.
 *
 **********************************************************************************/

ADC_InitTypeDef ADC_InitStruct;
ADC_CommonInitTypeDef ADC_CommonInitStruct;



int main(void)
{


        unsigned int _btn_count = 0;
        float TemperatureValue = 0;

    /**********************************************************************************
     *
     * This enables the peripheral clock to the GPIOD module.  This is stated in
     * the beginning of the stm32f4xx.gpio.c source file.
     *
    **********************************************************************************/

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    /**********************************************************************************
     *
     * This block of code defines the properties of the GPIO port.
     * The different options of each item can be found in the stm32f4xx_gpio.h header file
     * Every GPIO configuration should have the following initialized
     *
     * GPIO_InitStruct.GPIO_Pin
     * GPIO_InitStruct.GPIO_Mode
     * GPIO_InitStruct.GPIO_Speed
     * GPIO_InitStruct.GPIO_OType
     * GPIO_InitStruct.GPIO_PuPd
     * GPIO_Init(GPIOx, &GPIO_InitStruct); (x represents port - A, B, C, D, E, F, G, H, I)
     *
    **********************************************************************************/

    //GPIO_InitStruct.GPIO_Pin configures the pins that will be used.
    //In this case we will use the LED's off of the discovery board which are on
    //PortD pins 12, 13, 14 and 15
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_14 | GPIO_Pin_13 | GPIO_Pin_12;

    //PIO_InitStruct.GPIO_Mode configures the pin mode the options are as follows
    // GPIO_Mode_IN (Input Mode)
    // GPIO_Mode_OUT (Output Mode)
    // GPIO_Mode_AF (Alternate Function)
    // GPIO_Mode_AN (Analog Mode)
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;

    //GPIO_InitStruct.GPIO_Speed configures the clock speed, options are as follows
    // GPIO_Speed_2MHz
    // GPIO_Speed_25MHz
    // GPIO_Speed_50MHz
    // GPIO_Speed_100MHz
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    //GPIO_InitStruct.GPIO_OType configures the pin type, options are as follows
    // GPIO_OType_PP (Push/Pull)
    // GPIO_OType_OD (Open Drain)
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

    //Configures pullup / pulldown resistors on pin, options are as follows
    // GPIO_PuPd_NOPULL (Disables internal pullup and pulldown resistors)
    // GPIO_PuPd_UP (Enables internal pullup resistors)
    // GPIO_PuPd_DOWN (Enables internal pulldown resistors)
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

    //This finally passes all the values to the GPIO_Init function
    //which takes care of setting the corresponding bits.
    GPIO_Init(GPIOD, &GPIO_InitStruct);

    /**********************************************************************************
     *
     * This enables the peripheral clock to the GPIOA module.  This is stated in
     * the beginning of the stm32f4xx.gpio.c source file.
     *
    **********************************************************************************/
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    /**********************************************************************************
     *
     * This block of code defines the properties of the GPIOA port.
     * We are defining Pin 0 as a digital input with a pulldown resistor
     * to detect a high level.  Pin 0 is connected to the 3.3V source
     *
   **********************************************************************************/

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);


/**********************************************************************************
*
* This enables the peripheral clock to the TIM2 (Timer 2) module.
* The TIM2 module clock is at 84MHz.  The prescaler will be set
* to 42000-1 which will configure the clock for 84MHz/42kHz = 2kHz
* Then we will use the period to define as 2000-1.  This will trigger
* an event every 1 second (or it should).
*
* Since we are using the STM32F4 Discovery board that has a 8MHz crystal
* we need to adjust the following:
*
* 1) Use the clock configuration tool to setup the system_stm32f4xx.c file
*
* 2) In stm32f4xx.h file
*   a) Set the #define HSE_VALUE to the following
*   #define HSE_VALUE    ((uint32_t)8000000)
*
* 3) In startup_stm32f4xx.c file
*   a) Uncomment the line
*   extern void SystemInit(void);
*
*   b) Add the following line under Default_Reset_Handler()
*   just before the line of code that jumps to main();
*   SystemInit();
*
* **********************************************************************************/

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM_InitStruct.TIM_Prescaler = 42000 - 1; // This will configure the clock to 2 kHz
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; // Count-up timer mode
TIM_InitStruct.TIM_Period = 2000 - 1; // 2 kHz down to 1 Hz = 1 second
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // Divide clock by 1
TIM_InitStruct.TIM_RepetitionCounter = 0; // Set to 0, not used
TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);


/**********************************************************************************
*
* This enables the A/D converter for sampling the on board
* temperature sensor.
*
* You must first enable the CommonInitStructure
* then enable the specific InitStructure for AD1, AD2 or AD3
*
* Review reference manual RM0090 for register details.
*
* 1) Deinitialize the ADC
* 2) Set the ADC1 peripheral clock
* 2) Set the Common Structure of ADC
* a) Mode = Configures the ADC to operate in independent or multi mode.
* b) Prescaler = Select the frequency of the clock to the ADC. The clock is common for all the ADCs.
* ADCCLK = PCLK2/Prescaler
* c) DMA = Configures the Direct memory access mode for multi ADC mode.
* d) Two Sampling Delay = Configures the Delay between 2 sampling phases.
* # * Time(ADCCLK) where # is between 5 and 20
* 3) Configure The ADC Initialization Settings
* 4) Configure the channels for for ADC1, internal temperature sensor is on channel 16
* 5) Enable the internal temperature sensor
* 6) Enable the A/D conversion for ADC1
*
* **********************************************************************************/

ADC_DeInit();

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div8;
ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStruct);

ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStruct.ADC_ScanConvMode = DISABLE;
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStruct);

// ADC1 Configuration, ADC_Channel_TempSensor is actual channel 16
ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 1,
ADC_SampleTime_144Cycles);

// Enable internal temperature sensor
ADC_TempSensorVrefintCmd(ENABLE);

// Enable ADC conversion
ADC_Cmd(ADC1, ENABLE);

    /**********************************************************************************
     *
     * This block of code blinks all four LED's on initial startup
     *
     * **********************************************************************************/
    GPIO_SetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
    Delay(0xFFFFF);
    GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);

    while(1)
    {


        //Reads the status of the push-button on the Discovery board
        //If button is pressed blue LED is toggled

        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
        {
            _btn_count++;
                if (_btn_count > 0xFFFF)
                {
                    GPIO_ToggleBits(GPIOD, GPIO_Pin_15);
                    _btn_count = 0;
                }
        }

        //Monitors the TIM2 flag status.  If the TIM2 period is reached
        //The TIM_FLAG_Update = 1.  If this is true, we clear the flag
        //and toggle the green LED.

   if (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != RESET)
   {
     TIM_ClearFlag(TIM2, TIM_IT_Update);
     GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
   }


// ADC Conversion to read temperature sensor
// Temperature (in °C) = ((Vsense – V25) / Avg_Slope) + 25
// Vense = Voltage Reading From Temperature Sensor
// V25 = Voltage at 25°C, for STM32F407 = 0.76V
// Avg_Slope = 2.5mV/°C
// This data can be found in the STM32F407VF Data Sheet

ADC_SoftwareStartConv(ADC1); //Start the conversion
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
; //Processing the conversion
TemperatureValue = ADC_GetConversionValue(ADC1); //Return the converted data
TemperatureValue *= 3300;
TemperatureValue /= 0xfff; //Reading in mV
TemperatureValue /= 1000.0; //Reading in Volts
TemperatureValue -= 0.760; // Subtract the reference voltage at 25°C
TemperatureValue /= .0025; // Divide by slope 2.5mV
TemperatureValue += 25.0; // Add the 25°C

    } //End of while(1) loop
} //End of main loop


I will work next on writing this data to an SD card.  If you have any questions feel free to comment.  I apologize for the delay in this post.  If has been quite crazy around here.  I look forward to your questions and comments.

Thanks,

Mike

Sunday, June 2, 2013

Working with STM32F4 Timers

Working with Timers

Now that we have worked with the GPIO peripheral on the STM32F4 Discovery board, I will move on to setting up a simple timer to blink an LED.  Since we are using the blue LED with the push-button, I will choose the green LED to blink with the timer.

To start open up your existing project in CooCox CoIDE.  In my case I am going to create a new project called Blog_Example_2 and copy the contents from Blog_Example_1.  As I stated in the last blog, I will just keep adding to the existing code, this way we can see if we encounter any conflicts with the code.  When we created the project before, we came to a tab labeled "Repository".  If this tab is not open you can either click on "Browse in Repository" under the main tab or go to the View -> Repository in the toolbar.  Once you open the "Repository" you will see the items you selected from the first example.  In this example we are going to use a timer so select the TIM peripheral and you will see the source file and header file added to your project.



Now that we have the files added, we can start configuring the timer in our main.c file.  In this example, I plan on using the the Timer 2 (TIM2) to blink the green LED every 1 second.  Before we start writing the code we need to understand the system clock configuration. By default the configuration uses the internal RC oscillator which provides a system clock time of 16MHz.  This can be configured as high as 168MHz.  To easily configure the clock configuration you can use an excel file that was created by ST.  The excel file will automatically generate the source file for you to use and has a nice graphical view for the different clock sources.  Below is a link to the page where the file can be downloaded.  The part number is stsw-stm32091.

http://www.st.com/stonline/stappl/productcatalog/app?page=partNumberSearchPage&levelid=LN11&parentid=1533&resourcetype=SW

Once you have this loaded open up the excel file.  I am going to show the configuration using the external 8MHz crystal on the Discovery board to generate a 168MHz system clock frequency.

Set the  following to configure

  1. In the bottom left corner set the following
    • Configuration Mode = Expert
    • VDD = 3.3
    • Main Regulator Output Voltage = Scale1 Mode
    • Flash wait state = 5
    • Prefetch Buffer = OFF
    • Instruction Cache = ON
    • Data Cache = ON
    • Leave Enable I2S Clock Unchecked
  2. In the graphical portion from left left to right
    • Set HSE OSC = 8 MHz
    • Check HSE as the PLL Source
    • Set PLL_M = 8
    • Set PLL_N = 336
    • PLL_Q = 7
    • PLL_P = 2
    • AHBx Prescaler = 1
    • APB1 Prescaler = 4
    • APB2 Prescaler = 2


 


Once you have all the settings properly configured, press the "Generate" button at the bottom of the screen.  This will generate the system_stm32f4xx.c file in the same folder the excel file is located in.  Replace the file in your project with this file.  Also, if you look at the note just under the text box you entered 8 for the HSE OSC it states "Modify the "HSE_VALUE" in stm32f4xx.h.  If you open this file in your project, you can see it defaults to 25MHz (25000000).  Change this value to 8MHz (8000000).

Section of code is stm32f4xx.h file:

#if !defined  (HSE_VALUE)
      //This value was originally set to 25000000 (25MHz)
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */



Then there is one more step we need to do.  In the startup_stm32f4xx.c file there is a function defined as below:

//extern void SystemInit(void);

This by default is commented out and never called.  To use your clock configuration, uncomment this line of code and add the line of code to call SystemInit(); under the Default_Reset_Handler function in the same start_stm32f4xx.c file.  This should be inserted just before the call to the main(); function.  The code should look like the following.  I have highlighted the code that needs to be added. 


void Default_Reset_Handler(void)
{
  /* Initialize data and bss */
  unsigned long *pulSrc, *pulDest;

  /* Copy the data segment initializers from flash to SRAM */
  pulSrc = &_sidata;

  for(pulDest = &_sdata; pulDest < &_edata; )
  {
    *(pulDest++) = *(pulSrc++);
  }
 
  /* Zero fill the bss segment.  This is done with inline assembly since this
     will clear the value of pulDest if it is not kept in a register. */
  __asm("  ldr     r0, =_sbss\n"
        "  ldr     r1, =_ebss\n"
        "  mov     r2, #0\n"
        "  .thumb_func\n"
        "zero_loop:\n"
        "    cmp     r0, r1\n"
        "    it      lt\n"
        "    strlt   r2, [r0], #4\n"
        "    blt     zero_loop");
#ifdef __FPU_USED
  /* Enable FPU.*/
  __asm("  LDR.W R0, =0xE000ED88\n"
        "  LDR R1, [R0]\n"
        "  ORR R1, R1, #(0xF << 20)\n"
        "  STR R1, [R0]");
#endif   
 
  // Call SystemInit function for external clock configuration
  SystemInit();

 
  /* Call the application's entry point.*/
  main();
}


With all of these changes, the system clock will be configured for 168MHz.  If you look at the excel file where we configured the clock source, you see on the right hand side the text

TIM2,3,4,5,6,7,12,13,14

This is set to 84MHz.  This is important to know when we start configuring the Timer in our main.c file.  Now that we have configured our startup code let's start updating our main.c file.  The first step, just like configuring the GPIO is to include the header file.  So now the top of your main file should look like the following:

/* Includes ------------------------------------------------------------------*/
// Must be included if using STM32F4 Discovery board or processor
#include "stm32f4xx.h"

// Must be included if using GPIO (General purpose I/O) peripheral
#include "stm32f4xx_gpio.h"

// Must be included to setup general purpose I/O
// Some peripherals require the setup of RCC (Reset and clock controller)
#include "stm32f4xx_rcc.h"

// Must be included to setup timers
#include "stm32f4xx_tim.h"


After the includes, we need to create our label for the TIM2 structure, similar to the way we labled the GPIO structure.   The following code should be just before the main loop.  I have highlighted the new code that has been added.

GPIO_InitTypeDef  GPIO_InitStruct;
TIM_TimeBaseInitTypeDef    TIM_InitStruct; 

Next we need to configure the TIM2 peripheral.  Just like the GPIO there is an RCC associated with Timers.  If you look back at the excel spreadsheet, visually this will show you that RCC_APB1 is associated with TIM2.  Next we need to configure the timer to trigger every 1 second.  To do this we will configure a Count Up mode.  As you can see in the code below we have set the Prescaler to 42000-1 which will configure the clock frequency to 84 MHz/42 kHz = 2kHz.  Then we will configure the Count Up timer to trigger on a period of 2000 cycles.  Taking our Timer frequency of 2kHz / 2000 cycles = 1 second.  Then we load the values and enable the TIM2 Timer.  Initialization code is shown below.

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM_InitStruct.TIM_Prescaler = 42000 - 1;                // This will configure the clock to 2 kHz
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;     // Count-up timer mode
TIM_InitStruct.TIM_Period = 2000 - 1;                    // 2 kHz down to 1 Hz = 1 second
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;        // Divide clock by 1
TIM_InitStruct.TIM_RepetitionCounter = 0;                // Set to 0, not used
TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);


We already have the GPIO for the green LED configured from our previous example.  Now all we need to do is write some code to monitor the TIM2 Timer so once the Period expires we toggle the green LED.  In this case we could use an interrupt, but I will just monitor the status of the TIM2 flag.  If TIM_FLAG_Update of TIM2 Timer = 1 we know the period of 2000 has been reached.  At this time we can clear the flag and toggle the green LED (if you recall this was PortD Pin12 on the Discover board).   

if (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != RESET)
{
TIM_ClearFlag(TIM2, TIM_IT_Update);
GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
}


And that is it.  Timer is configured.

At this point, we have configured the STM32F4 Discovery board to control inputs, outputs and a simple timer.  The key component is to understand the clock sources and their configuration.  When I first started writing this code, I did not realize the the setup was using the internal RC oscillator.  This caused quite a bit of confusion with the timing of the flashing LED.

Next I plan on monitoring the internal temperature sensor on the STM32F4 microprocessor.  If you have any feedback feel free to e-mail or post your comments.






Complete main.c code for this example is shown below.  In the future I plan on sharing the entire code on an external site.


/* Includes ------------------------------------------------------------------*/
// Must be included if using STM32F4 Discovery board or processor
#include "stm32f4xx.h"

// Must be included if using GPIO (General purpose I/O) peripheral
#include "stm32f4xx_gpio.h"

// Must be included to setup general purpose I/O
// Some peripherals require the setup of RCC (Reset and clock controller)
#include "stm32f4xx_rcc.h"

// Must be included to setup timers
#include "stm32f4xx_tim.h"

void Delay(__IO uint32_t nCount)
{
  while(nCount--)
  {
  }
}

/**********************************************************************************
 *
 * The GPIO_InitTypeDef is a structure defined in the stm32f4xx_gpio.h file.
 * This is a simple way to use ST's library to configure the I/O.
 *
 * This is a common setup for the other peripherals as well.  Each peripheral will have
 * different properties which can be found in the header files and source files.
 *
**********************************************************************************/

GPIO_InitTypeDef  GPIO_InitStruct;


/**********************************************************************************
 *
 * The TIM_TimeBaseInitTypeDef is a structure defined in the stm32f4xx_tim.h file.
 *
**********************************************************************************/

TIM_TimeBaseInitTypeDef    TIM_InitStruct;


int main(void)
{


        unsigned int _btn_count = 0;

    /**********************************************************************************
     *
     * This enables the peripheral clock to the GPIOD module.  This is stated in
     * the beginning of the stm32f4xx.gpio.c source file.
     *
    **********************************************************************************/

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    /**********************************************************************************
     *
     * This block of code defines the properties of the GPIO port.
     * The different options of each item can be found in the stm32f4xx_gpio.h header file
     * Every GPIO configuration should have the following initialized
     *
     * GPIO_InitStruct.GPIO_Pin
     * GPIO_InitStruct.GPIO_Mode
     * GPIO_InitStruct.GPIO_Speed
     * GPIO_InitStruct.GPIO_OType
     * GPIO_InitStruct.GPIO_PuPd
     * GPIO_Init(GPIOx, &GPIO_InitStruct); (x represents port - A, B, C, D, E, F, G, H, I)
     *
    **********************************************************************************/

    //GPIO_InitStruct.GPIO_Pin configures the pins that will be used.
    //In this case we will use the LED's off of the discovery board which are on
    //PortD pins 12, 13, 14 and 15
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_14 | GPIO_Pin_13 | GPIO_Pin_12;

    //PIO_InitStruct.GPIO_Mode configures the pin mode the options are as follows
    // GPIO_Mode_IN (Input Mode)
    // GPIO_Mode_OUT (Output Mode)
    // GPIO_Mode_AF (Alternate Function)
    // GPIO_Mode_AN (Analog Mode)
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;

    //GPIO_InitStruct.GPIO_Speed configures the clock speed, options are as follows
    // GPIO_Speed_2MHz
    // GPIO_Speed_25MHz
    // GPIO_Speed_50MHz
    // GPIO_Speed_100MHz
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    //GPIO_InitStruct.GPIO_OType configures the pin type, options are as follows
    // GPIO_OType_PP (Push/Pull)
    // GPIO_OType_OD (Open Drain)
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

    //Configures pullup / pulldown resistors on pin, options are as follows
    // GPIO_PuPd_NOPULL (Disables internal pullup and pulldown resistors)
    // GPIO_PuPd_UP (Enables internal pullup resistors)
    // GPIO_PuPd_DOWN (Enables internal pulldown resistors)
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

    //This finally passes all the values to the GPIO_Init function
    //which takes care of setting the corresponding bits.
    GPIO_Init(GPIOD, &GPIO_InitStruct);

    /**********************************************************************************
     *
     * This enables the peripheral clock to the GPIOA module.  This is stated in
     * the beginning of the stm32f4xx.gpio.c source file.
     *
    **********************************************************************************/
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    /**********************************************************************************
     *
     * This block of code defines the properties of the GPIOA port.
     * We are defining Pin 0 as a digital input with a pulldown resistor
     * to detect a high level.  Pin 0 is connected to the 3.3V source
     *
   **********************************************************************************/

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);


    /**********************************************************************************
     *
     * This enables the peripheral clock to the TIM2 (Timer 2) module.
     * The TIM2 module clock is at 84MHz.  The prescaler will be set
     * to 42000-1 which will configure the clock for 84MHz/42kHz = 2kHz
     * Then we will use the period to define as 2000-1.  This will trigger
     * an event every 1 second (or it should).
     *
     * Since we are using the STM32F4 Discovery board that has a 8MHz crystal
     * we need to adjust the following:
     *
     * 1)     Use the clock configuration tool to setup the system_stm32f4xx.c file
     *
     * 2)    In stm32f4xx.h file
     *          a)    Set the #define HSE_VALUE to the following
     *               #define HSE_VALUE    ((uint32_t)8000000)
     *
     * 3)    In startup_stm32f4xx.c file
     *          a)     Uncomment the line
     *               extern void SystemInit(void);
     *
     *          b)     Add the following line under Default_Reset_Handler()
     *               just before the line of code that jumps to main();
     *               SystemInit();
     *
     * **********************************************************************************/

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_InitStruct.TIM_Prescaler = 42000 - 1;                // This will configure the clock to 2 kHz
    TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;     // Count-up timer mode
    TIM_InitStruct.TIM_Period = 2000 - 1;                    // 2 kHz down to 1 Hz = 1 second
    TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;        // Divide clock by 1
    TIM_InitStruct.TIM_RepetitionCounter = 0;                // Set to 0, not used
    TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
    /* TIM2 enable counter */
    TIM_Cmd(TIM2, ENABLE);

    /**********************************************************************************
     *
     * This block of code blinks all four LED's on initial startup
     *
     * **********************************************************************************/
    GPIO_SetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
    Delay(0xFFFFF);
    GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);

    while(1)
    {


        //Reads the status of the push-button on the Discovery board
        //If button is pressed blue LED is toggled

        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
        {
            _btn_count++;
                if (_btn_count > 0xFFFF)
                {
                    GPIO_ToggleBits(GPIOD, GPIO_Pin_15);
                    _btn_count = 0;
                }
        }

        //Monitors the TIM2 flag status.  If the TIM2 period is reached
        //The TIM_FLAG_Update = 1.  If this is true, we clear the flag
        //and toggle the green LED.

        if (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != RESET)
        {
          TIM_ClearFlag(TIM2, TIM_IT_Update);
          GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
        }

    } //End of while(1) loop
} //End of main loop