Spyros Papanastasiou
/
General_purpose_timer_TIM2__PWM_LED
Controlling PWM of LED through direct access of TIM2 timer's registers.
main.cpp@11:42d5cbbd0f3e, 2019-08-02 (annotated)
- Committer:
- Ladon
- Date:
- Fri Aug 02 13:30:49 2019 +0000
- Revision:
- 11:42d5cbbd0f3e
- Parent:
- 10:f80370dd55f8
Controlling PWD of LED through direct access of TIM2 timer's registers.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Ladon | 5:3ee6e0113b41 | 1 | #include <mbed.h> |
Ladon | 0:12efa8652054 | 2 | #include <iostream> |
Ladon | 0:12efa8652054 | 3 | |
Ladon | 0:12efa8652054 | 4 | using namespace std; |
Ladon | 0:12efa8652054 | 5 | |
Ladon | 10:f80370dd55f8 | 6 | void inline led_init () |
Ladon | 0:12efa8652054 | 7 | { |
Ladon | 5:3ee6e0113b41 | 8 | // Led is at PA5. |
Ladon | 5:3ee6e0113b41 | 9 | // - |
Ladon | 9:e65dd1ad537e | 10 | // Set PA5 as AF1 (TIM2_CH1). |
Ladon | 8:6464500cc838 | 11 | // - |
Ladon | 11:42d5cbbd0f3e | 12 | // Alternate Function Low Register. |
Ladon | 11:42d5cbbd0f3e | 13 | // - |
Ladon | 8:6464500cc838 | 14 | GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL5; |
Ladon | 8:6464500cc838 | 15 | GPIOA->AFR[0] |= 1 << GPIO_AFRL_AFRL5_Pos; |
Ladon | 11:42d5cbbd0f3e | 16 | // Port Output Speed Register. |
Ladon | 11:42d5cbbd0f3e | 17 | // - |
Ladon | 8:6464500cc838 | 18 | GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5; |
Ladon | 11:42d5cbbd0f3e | 19 | // Port Mode Register. |
Ladon | 11:42d5cbbd0f3e | 20 | // - |
Ladon | 5:3ee6e0113b41 | 21 | GPIOA->MODER &= ~GPIO_MODER_MODER5; |
Ladon | 8:6464500cc838 | 22 | GPIOA->MODER |= 2 << GPIO_MODER_MODER5_Pos; |
Ladon | 0:12efa8652054 | 23 | } |
Ladon | 0:12efa8652054 | 24 | |
Ladon | 5:3ee6e0113b41 | 25 | void inline reset_timer () |
Ladon | 0:12efa8652054 | 26 | { |
Ladon | 5:3ee6e0113b41 | 27 | // Peripheral Reset Register. |
Ladon | 11:42d5cbbd0f3e | 28 | // TIM2 timer reset. |
Ladon | 5:3ee6e0113b41 | 29 | // - |
Ladon | 5:3ee6e0113b41 | 30 | RCC->APB1RSTR |= RCC_APB1RSTR_TIM2RST; |
Ladon | 5:3ee6e0113b41 | 31 | RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM2RST; |
Ladon | 0:12efa8652054 | 32 | } |
Ladon | 0:12efa8652054 | 33 | |
Ladon | 5:3ee6e0113b41 | 34 | void inline clock_enable_for_timer () |
Ladon | 0:12efa8652054 | 35 | { |
Ladon | 11:42d5cbbd0f3e | 36 | reset_timer(); |
Ladon | 5:3ee6e0113b41 | 37 | // Peripheral Clock Enable Register. |
Ladon | 5:3ee6e0113b41 | 38 | // TIM2 Timer Clock Enable. |
Ladon | 5:3ee6e0113b41 | 39 | // - |
Ladon | 5:3ee6e0113b41 | 40 | RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; |
Ladon | 0:12efa8652054 | 41 | } |
Ladon | 0:12efa8652054 | 42 | |
Ladon | 11:42d5cbbd0f3e | 43 | void inline timer_setup_pwm () |
Ladon | 11:42d5cbbd0f3e | 44 | { |
Ladon | 11:42d5cbbd0f3e | 45 | const double duty_cycle = 0.6; |
Ladon | 11:42d5cbbd0f3e | 46 | // Capture/ Compare Register 1. |
Ladon | 11:42d5cbbd0f3e | 47 | // - |
Ladon | 11:42d5cbbd0f3e | 48 | TIM2->CCR1 = TIM2->ARR * duty_cycle; |
Ladon | 11:42d5cbbd0f3e | 49 | // Capture/ Compare Mode Register 1. |
Ladon | 11:42d5cbbd0f3e | 50 | // Output Compare 1 Mode. |
Ladon | 11:42d5cbbd0f3e | 51 | // Output Compare 1 Preload Enable. |
Ladon | 11:42d5cbbd0f3e | 52 | // Output Compare 1 Fast Enable. |
Ladon | 11:42d5cbbd0f3e | 53 | // - |
Ladon | 11:42d5cbbd0f3e | 54 | TIM2->CCMR1 = (0b111 << TIM_CCMR1_OC1M_Pos) | TIM_CCMR1_OC1PE | TIM_CCMR1_OC1FE; |
Ladon | 11:42d5cbbd0f3e | 55 | // ^ 0b111 : PWM Mode 2 : On while CCR < ARR, OFF while >=. |
Ladon | 11:42d5cbbd0f3e | 56 | // - |
Ladon | 11:42d5cbbd0f3e | 57 | } |
Ladon | 11:42d5cbbd0f3e | 58 | |
Ladon | 8:6464500cc838 | 59 | void inline timer_enable_pwm () |
Ladon | 8:6464500cc838 | 60 | { |
Ladon | 11:42d5cbbd0f3e | 61 | timer_setup_pwm(); |
Ladon | 11:42d5cbbd0f3e | 62 | // Capture/ Compare Enable Register. |
Ladon | 11:42d5cbbd0f3e | 63 | // Capture/ Compare 1 Output Enable. |
Ladon | 11:42d5cbbd0f3e | 64 | // - |
Ladon | 10:f80370dd55f8 | 65 | TIM2->CCER = TIM_CCER_CC1E; |
Ladon | 8:6464500cc838 | 66 | } |
Ladon | 8:6464500cc838 | 67 | |
Ladon | 4:c52637c8d084 | 68 | void inline timer_downscale_by (const unsigned short& v) |
Ladon | 0:12efa8652054 | 69 | { |
Ladon | 5:3ee6e0113b41 | 70 | // Prescaler. |
Ladon | 5:3ee6e0113b41 | 71 | // - |
Ladon | 2:7c45a714b991 | 72 | TIM2->PSC = v; |
Ladon | 0:12efa8652054 | 73 | } |
Ladon | 0:12efa8652054 | 74 | |
Ladon | 4:c52637c8d084 | 75 | void inline timer_limit_counter_to (const unsigned int& v) |
Ladon | 0:12efa8652054 | 76 | { |
Ladon | 5:3ee6e0113b41 | 77 | // Auto-Reload Register. |
Ladon | 5:3ee6e0113b41 | 78 | // - |
Ladon | 2:7c45a714b991 | 79 | TIM2->ARR = v; |
Ladon | 0:12efa8652054 | 80 | } |
Ladon | 0:12efa8652054 | 81 | |
Ladon | 5:3ee6e0113b41 | 82 | void inline timer_set_period_to (const double& T) |
Ladon | 0:12efa8652054 | 83 | { |
Ladon | 11:42d5cbbd0f3e | 84 | // Precondition : T <= 59 (= (2^32 - 1) / 72e6). |
Ladon | 5:3ee6e0113b41 | 85 | // - |
Ladon | 8:6464500cc838 | 86 | timer_limit_counter_to(72000000 * T); |
Ladon | 11:42d5cbbd0f3e | 87 | // ^Explanation : By limiting ARR to 72e6, the counter gets filled in 1sec (at 72Mhz). |
Ladon | 11:42d5cbbd0f3e | 88 | // - |
Ladon | 0:12efa8652054 | 89 | } |
Ladon | 0:12efa8652054 | 90 | |
Ladon | 5:3ee6e0113b41 | 91 | void inline timer_enable_interrupt () |
Ladon | 5:3ee6e0113b41 | 92 | { |
Ladon | 5:3ee6e0113b41 | 93 | // DMA/ Interrupt Enable Register. |
Ladon | 5:3ee6e0113b41 | 94 | // Update Interrupt Enable. |
Ladon | 5:3ee6e0113b41 | 95 | // - |
Ladon | 10:f80370dd55f8 | 96 | TIM2->DIER = TIM_DIER_UIE; |
Ladon | 5:3ee6e0113b41 | 97 | } |
Ladon | 0:12efa8652054 | 98 | |
Ladon | 5:3ee6e0113b41 | 99 | void inline timer_enable () |
Ladon | 0:12efa8652054 | 100 | { |
Ladon | 5:3ee6e0113b41 | 101 | // Control Register 1. |
Ladon | 5:3ee6e0113b41 | 102 | // Update Request Source | Counter Enable. |
Ladon | 0:12efa8652054 | 103 | // - |
Ladon | 11:42d5cbbd0f3e | 104 | TIM2->CR1 = TIM_CR1_URS | TIM_CR1_CEN; |
Ladon | 5:3ee6e0113b41 | 105 | } |
Ladon | 5:3ee6e0113b41 | 106 | |
Ladon | 5:3ee6e0113b41 | 107 | void inline timer_clear_status () |
Ladon | 5:3ee6e0113b41 | 108 | { |
Ladon | 5:3ee6e0113b41 | 109 | // Status Register. |
Ladon | 5:3ee6e0113b41 | 110 | // - |
Ladon | 5:3ee6e0113b41 | 111 | TIM2->SR = 0; |
Ladon | 0:12efa8652054 | 112 | } |
Ladon | 0:12efa8652054 | 113 | |
Ladon | 5:3ee6e0113b41 | 114 | /* |
Ladon | 5:3ee6e0113b41 | 115 | // I was unable to use the following handler : |
Ladon | 5:3ee6e0113b41 | 116 | // - |
Ladon | 5:3ee6e0113b41 | 117 | extern "C" void TIM2_IRQHandler (void) |
Ladon | 5:3ee6e0113b41 | 118 | { |
Ladon | 5:3ee6e0113b41 | 119 | cout << "Interrupt hit!" << endl; |
Ladon | 5:3ee6e0113b41 | 120 | clear_timer_status(); |
Ladon | 5:3ee6e0113b41 | 121 | toggle_LED(); |
Ladon | 5:3ee6e0113b41 | 122 | } |
Ladon | 5:3ee6e0113b41 | 123 | */ |
Ladon | 0:12efa8652054 | 124 | |
Ladon | 5:3ee6e0113b41 | 125 | // I couldn 't get TIM2_IRQHandler() to work; probably wrong name. Thus, I 'll use a custom handler |
Ladon | 5:3ee6e0113b41 | 126 | // and attach it using SetVector() from CMSIS : https://arm-software.github.io/CMSIS_5/Core/html/group__NVIC__gr.html. |
Ladon | 4:c52637c8d084 | 127 | // - |
Ladon | 4:c52637c8d084 | 128 | void interrupt_handler () |
Ladon | 3:db424769ecca | 129 | { |
Ladon | 4:c52637c8d084 | 130 | timer_clear_status(); |
Ladon | 3:db424769ecca | 131 | } |
Ladon | 3:db424769ecca | 132 | |
Ladon | 10:f80370dd55f8 | 133 | void inline interrupt_enable () |
Ladon | 0:12efa8652054 | 134 | { |
Ladon | 4:c52637c8d084 | 135 | // NVIC_SetPriority(TIM2_IRQn, 250); |
Ladon | 7:d93dcab712f1 | 136 | NVIC_SetVector(TIM2_IRQn, reinterpret_cast<uint32_t>(interrupt_handler)); |
Ladon | 2:7c45a714b991 | 137 | NVIC_EnableIRQ(TIM2_IRQn); |
Ladon | 5:3ee6e0113b41 | 138 | // ^Definition of NVIC_SetVector() : https://arm-software.github.io/CMSIS_5/Core/html/group__NVIC__gr.html#gab43c1c59d5c081f1bc725237f4b1f916. |
Ladon | 5:3ee6e0113b41 | 139 | // - |
Ladon | 0:12efa8652054 | 140 | } |
Ladon | 0:12efa8652054 | 141 | |
Ladon | 11:42d5cbbd0f3e | 142 | void inline timer_init () |
Ladon | 11:42d5cbbd0f3e | 143 | { |
Ladon | 11:42d5cbbd0f3e | 144 | clock_enable_for_timer(); |
Ladon | 11:42d5cbbd0f3e | 145 | timer_set_period_to(0.1); |
Ladon | 11:42d5cbbd0f3e | 146 | timer_enable_interrupt(); |
Ladon | 11:42d5cbbd0f3e | 147 | timer_enable_pwm(); |
Ladon | 11:42d5cbbd0f3e | 148 | timer_enable(); |
Ladon | 11:42d5cbbd0f3e | 149 | } |
Ladon | 0:12efa8652054 | 150 | |
Ladon | 0:12efa8652054 | 151 | int main () |
Ladon | 0:12efa8652054 | 152 | { |
Ladon | 0:12efa8652054 | 153 | cout << "Entered main()." << endl; |
Ladon | 4:c52637c8d084 | 154 | led_init(); |
Ladon | 11:42d5cbbd0f3e | 155 | timer_init(); |
Ladon | 4:c52637c8d084 | 156 | interrupt_enable(); |
Ladon | 0:12efa8652054 | 157 | cout << "Exiting main().." << endl; |
Ladon | 0:12efa8652054 | 158 | } |