Controlling PWM of LED through direct access of TIM2 timer's registers.

Dependencies:   mbed

main.cpp

Committer:
Ladon
Date:
2019-08-02
Revision:
6:be045fed855f
Parent:
5:3ee6e0113b41
Child:
7:d93dcab712f1

File content as of revision 6:be045fed855f:

#include <mbed.h>
#include <iostream>

using namespace std;

void led_init ()
{
    // Led is at PA5.
    // -
    // Set PA5 as output.
    // -
    // Port Mode Register.
    // -
    GPIOA->MODER &= ~GPIO_MODER_MODER5;
    GPIOA->MODER |= 1 << GPIO_MODER_MODER5_Pos;
}

void inline reset_timer ()
{
    // Peripheral Reset Register.
    // -
    RCC->APB1RSTR |= RCC_APB1RSTR_TIM2RST;
    RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM2RST;
}

void inline clock_enable_for_timer ()
{
    // Peripheral Clock Enable Register.
    //     TIM2 Timer Clock Enable.
    // -
    reset_timer();
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
}

void inline timer_downscale_by (const unsigned short& v)
{
    // Prescaler.
    // -
    TIM2->PSC = v;
}

void inline timer_limit_counter_to (const unsigned int& v)
{
    // Auto-Reload Register.
    // -
    TIM2->ARR = v;
}

void inline timer_set_period_to (const double& T)
{
    // This function allows : 500us <= T <= 32.768
    //      The ^former was found by solving : 0 <= 1999 + (T - 1) * 2e3 <= 0xFFFF, for the prescaler register.
    // -
    timer_limit_counter_to(36000);
    timer_downscale_by(1999 + (T - 1) * 2e3);
}

void inline timer_enable_interrupt ()
{
    // DMA/ Interrupt Enable Register.
    //     Update Interrupt Enable.
    // -
    TIM2->DIER = TIM_DIER_UIE;
}

void inline timer_enable ()
{
    // Control Register 1.
    //     Update Request Source | Counter Enable.
    // -
    TIM2->CR1 = TIM_CR1_URS | TIM_CR1_CEN;
}

void inline timer_clear_status ()
{
    // Status Register.
    // -
    TIM2->SR = 0;
}

void led_toggle ()
{
    // Output Data Register.
    // -
    GPIOA->ODR ^= GPIO_ODR_5;
}

/* 
    // I was unable to use the following handler :
    // -
    extern "C" void TIM2_IRQHandler (void)
    {
        cout << "Interrupt hit!" << endl;
        clear_timer_status();
        toggle_LED();
    }
*/

// I couldn 't get TIM2_IRQHandler() to work; probably wrong name. Thus, I 'll use a custom handler
// and attach it using SetVector() from CMSIS : https://arm-software.github.io/CMSIS_5/Core/html/group__NVIC__gr.html.
// -
void interrupt_handler ()
{
    cout << "Interrupt hit!" << endl;
    timer_clear_status();
    led_toggle();
}

inline void interrupt_enable ()
{
//    NVIC_SetPriority(TIM2_IRQn, 250);
    NVIC_SetVector(TIM2_IRQn, (uint32_t) interrupt_handler);
    NVIC_EnableIRQ(TIM2_IRQn);
    // ^Definition of NVIC_SetVector() : https://arm-software.github.io/CMSIS_5/Core/html/group__NVIC__gr.html#gab43c1c59d5c081f1bc725237f4b1f916.
    // -
}

//
// -
//

int main ()
{
    cout << "Entered main()." << endl;
    led_init();
    clock_enable_for_timer();
    timer_set_period_to(1.3);
    timer_enable_interrupt();
    timer_enable();
    interrupt_enable();
    /*
        while (true)
        {
            cout << hex
                 << "Status : 0x" << TIM2->SR << endl
                 << "Count  : 0x" << TIM2->CNT << endl;
        }
    */
    cout << "Exiting main().." << endl;
}