Spyros Papanastasiou
/
General_purpose_timer_TIM2__PWM_LED
Controlling PWM of LED through direct access of TIM2 timer's registers.
main.cpp@0:12efa8652054, 2019-07-24 (annotated)
- Committer:
- Ladon
- Date:
- Wed Jul 24 15:08:28 2019 +0000
- Revision:
- 0:12efa8652054
- Child:
- 1:8d34cf217c0a
Talking to Timer registers.; Enabling interrupt using NVIC functions.; Lighting LED for specified intervals.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Ladon | 0:12efa8652054 | 1 | #include <mbed.h> |
Ladon | 0:12efa8652054 | 2 | |
Ladon | 0:12efa8652054 | 3 | #include <iostream> |
Ladon | 0:12efa8652054 | 4 | // ^cout... |
Ladon | 0:12efa8652054 | 5 | // - |
Ladon | 0:12efa8652054 | 6 | #include <iomanip> |
Ladon | 0:12efa8652054 | 7 | // ^setw... |
Ladon | 0:12efa8652054 | 8 | // - |
Ladon | 0:12efa8652054 | 9 | |
Ladon | 0:12efa8652054 | 10 | using namespace std; |
Ladon | 0:12efa8652054 | 11 | |
Ladon | 0:12efa8652054 | 12 | // Board: NUCLEO-F303RE |
Ladon | 0:12efa8652054 | 13 | // - |
Ladon | 0:12efa8652054 | 14 | // Datasheet: |
Ladon | 0:12efa8652054 | 15 | // https://www.st.com/resource/en/datasheet/stm32f303re.pdf |
Ladon | 0:12efa8652054 | 16 | // Reference Manual: |
Ladon | 0:12efa8652054 | 17 | // https://www.st.com/resource/en/reference_manual/dm00043574.pdf |
Ladon | 0:12efa8652054 | 18 | // Programming Manual: |
Ladon | 0:12efa8652054 | 19 | // https://www.st.com/resource/en/programming_manual/dm00046982.pdf |
Ladon | 0:12efa8652054 | 20 | // (Some address #definitions): |
Ladon | 0:12efa8652054 | 21 | // https://raw.githubusercontent.com/ARMmbed/mbed-os/master/targets/TARGET_STM/TARGET_STM32F3/TARGET_STM32F303xE/device/stm32f303xe.h |
Ladon | 0:12efa8652054 | 22 | // - |
Ladon | 0:12efa8652054 | 23 | |
Ladon | 0:12efa8652054 | 24 | // Direct port access is shown in this example: |
Ladon | 0:12efa8652054 | 25 | // https://os.mbed.com/users/Ladon/code/Write_to_port_directly__LED/ |
Ladon | 0:12efa8652054 | 26 | // CPU hang discussion: |
Ladon | 0:12efa8652054 | 27 | // https://community.arm.com/developer/tools-software/tools/f/keil-forum/33581/cpu-crash-when-enable-nvic-for-timer/82797#82797 |
Ladon | 0:12efa8652054 | 28 | // Possible clock definition: |
Ladon | 0:12efa8652054 | 29 | // https://os.mbed.com/users/mbed_official/code/mbed-dev/file/f392fc9709a3/targets/TARGET_STM/TARGET_STM32F3/TARGET_STM32F303xE/TARGET_NUCLEO_F303RE/system_clock.c/ |
Ladon | 0:12efa8652054 | 30 | // We 're probably interested in APB1. |
Ladon | 0:12efa8652054 | 31 | // - |
Ladon | 0:12efa8652054 | 32 | |
Ladon | 0:12efa8652054 | 33 | // |
Ladon | 0:12efa8652054 | 34 | // * Let 's use a timer to blink the LED. |
Ladon | 0:12efa8652054 | 35 | // * Let 's use TIM6 or TIM7 from the datasheet. |
Ladon | 0:12efa8652054 | 36 | // * For TIM6 there is IRQ 54 (TIM6_DAC1), as seen in the Reference Manual. |
Ladon | 0:12efa8652054 | 37 | // * We find the appropriate alias (TIM6_DAC1_IRQn) in the "#definitions" ^^file above. |
Ladon | 0:12efa8652054 | 38 | // * We 'll use ^this and the TIM6_DAC1_IRQHandler one, with NVIC CMSIS instructions |
Ladon | 0:12efa8652054 | 39 | // like NVIC_EnableIRQ and NVIC_GetPendingIRQ, as seen in the ^^programming manual. |
Ladon | 0:12efa8652054 | 40 | // |
Ladon | 0:12efa8652054 | 41 | // * About the interrupt period: |
Ladon | 0:12efa8652054 | 42 | // * Assuming that the APB1 peripheral clock, which is responsible for TIM6, |
Ladon | 0:12efa8652054 | 43 | // runs at 36Mhz, we can limit the TIM6 to 36000 using the ARR (Auto Reload Register), |
Ladon | 0:12efa8652054 | 44 | // and downscale it by 1000. This leaves us with 1 full timer counter per second:) |
Ladon | 0:12efa8652054 | 45 | // * Truth is, input clock seems to be 72Mhz.. |
Ladon | 0:12efa8652054 | 46 | // * About setting arbitrary period: |
Ladon | 0:12efa8652054 | 47 | // * Knowing that the counter clock (probably 72Mhz) gets divided by (1+TIM6_PSC) we can: |
Ladon | 0:12efa8652054 | 48 | // * Divide by 1000 (1 + 999) and |
Ladon | 0:12efa8652054 | 49 | // * Limit Counter to 72000 which is not possible in 16 bits, but 72000 = 36000 * 2 so |
Ladon | 0:12efa8652054 | 50 | // let 's divide by 2 * 1000 instead. and limit to 36000. |
Ladon | 0:12efa8652054 | 51 | // That leaves us with the filling period of 1sec. In order to chose any other period, |
Ladon | 0:12efa8652054 | 52 | // we imagine the following equality: |
Ladon | 0:12efa8652054 | 53 | // 72Mhz 36000 steps |
Ladon | 0:12efa8652054 | 54 | // ---------------- = ------------- ==> 2000 + X = 2000 * T ==> X = 2000 * (T - 1) |
Ladon | 0:12efa8652054 | 55 | // (1 + 1999 + X) T secs |
Ladon | 0:12efa8652054 | 56 | // Thus, PSC (prescaler) is : 1999 + X = 1999 + 2000 * (T - 1), where T is in seconds. |
Ladon | 0:12efa8652054 | 57 | // - |
Ladon | 0:12efa8652054 | 58 | // From the Reference Manual: |
Ladon | 0:12efa8652054 | 59 | // |
Ladon | 0:12efa8652054 | 60 | // TIM6 base address: |
Ladon | 0:12efa8652054 | 61 | // 0x4000 1000 |
Ladon | 0:12efa8652054 | 62 | // - |
Ladon | 0:12efa8652054 | 63 | // TIM6_CR1: Control Register 1: |
Ladon | 0:12efa8652054 | 64 | // Address offset: 0x00. |
Ladon | 0:12efa8652054 | 65 | // 16bits. |
Ladon | 0:12efa8652054 | 66 | // Bit0: CEN: Counter Enable. |
Ladon | 0:12efa8652054 | 67 | // Enabled when 1. |
Ladon | 0:12efa8652054 | 68 | // Bit2: URS: Update Request Source. |
Ladon | 0:12efa8652054 | 69 | // 1: Only counter overflow/underflow generates an update interrupt. |
Ladon | 0:12efa8652054 | 70 | // |
Ladon | 0:12efa8652054 | 71 | // TIM6_DIER: DMA/ Interrupt Enable Register: |
Ladon | 0:12efa8652054 | 72 | // Address offset: 0x0C. |
Ladon | 0:12efa8652054 | 73 | // 16bits. |
Ladon | 0:12efa8652054 | 74 | // Bit0: UIE: Update Interrupt Enable. |
Ladon | 0:12efa8652054 | 75 | // 1: Enabled. |
Ladon | 0:12efa8652054 | 76 | // I 'm not sure what this does.. |
Ladon | 0:12efa8652054 | 77 | // Bit8: UDE: Update DMA Request Enable. |
Ladon | 0:12efa8652054 | 78 | // 1: Enabled. |
Ladon | 0:12efa8652054 | 79 | // |
Ladon | 0:12efa8652054 | 80 | // TIM6_SR: Status Register: |
Ladon | 0:12efa8652054 | 81 | // Address offset: 0x10. |
Ladon | 0:12efa8652054 | 82 | // 16bits. |
Ladon | 0:12efa8652054 | 83 | // Bit0: UIF: Update Interrupt Flag: |
Ladon | 0:12efa8652054 | 84 | // ?? |
Ladon | 0:12efa8652054 | 85 | // |
Ladon | 0:12efa8652054 | 86 | // TIM6_EGR: Event Generation Register: |
Ladon | 0:12efa8652054 | 87 | // Address offset: 0x14. |
Ladon | 0:12efa8652054 | 88 | // 16bits (1bit) w/o |
Ladon | 0:12efa8652054 | 89 | // Bit0: UG: Update Generation: |
Ladon | 0:12efa8652054 | 90 | // When set to 1, re-initializes the timer counter and generates an |
Ladon | 0:12efa8652054 | 91 | // update of the registers; it is automatically cleared by hardware. |
Ladon | 0:12efa8652054 | 92 | // Sounds like a reset button. |
Ladon | 0:12efa8652054 | 93 | // |
Ladon | 0:12efa8652054 | 94 | // TIM6_CNT: Counter: |
Ladon | 0:12efa8652054 | 95 | // Address offset: 0x24. |
Ladon | 0:12efa8652054 | 96 | // 32bits wide. |
Ladon | 0:12efa8652054 | 97 | // Bits 15..0: CNT: Counter value. |
Ladon | 0:12efa8652054 | 98 | // Bit31: UIFCPY: A read-only copy of UIF (update interrupt flag) bit from TIM6_SR (status register). |
Ladon | 0:12efa8652054 | 99 | // Note: In order to read the current count, mask the lower 16 bits. |
Ladon | 0:12efa8652054 | 100 | // |
Ladon | 0:12efa8652054 | 101 | // TIM6_PSC: Prescaler: |
Ladon | 0:12efa8652054 | 102 | // Address offset: 0x28. |
Ladon | 0:12efa8652054 | 103 | // 16bits. |
Ladon | 0:12efa8652054 | 104 | // The counter clock frequency CK_CNT is equal to f_{CK_PSC} / (PSC + 1). |
Ladon | 0:12efa8652054 | 105 | // |
Ladon | 0:12efa8652054 | 106 | // TIM6_ARR: Auto Reload Register: |
Ladon | 0:12efa8652054 | 107 | // Address Offset: 0x2C. |
Ladon | 0:12efa8652054 | 108 | // 16bits (r/w). |
Ladon | 0:12efa8652054 | 109 | // Info: The upper limit for the counter. |
Ladon | 0:12efa8652054 | 110 | // |
Ladon | 0:12efa8652054 | 111 | // RCC_APB1ENR: APB1 Peripheral Clock Enable Register: |
Ladon | 0:12efa8652054 | 112 | // Base register: (?)RCC at 0x40021000. |
Ladon | 0:12efa8652054 | 113 | // Address Offset: 0x1C. |
Ladon | 0:12efa8652054 | 114 | // 32bits. |
Ladon | 0:12efa8652054 | 115 | // Bit4: TIM6EN: Timer 6 clock Enable. |
Ladon | 0:12efa8652054 | 116 | // 0: Disable. |
Ladon | 0:12efa8652054 | 117 | // 1: Enable. |
Ladon | 0:12efa8652054 | 118 | // - |
Ladon | 0:12efa8652054 | 119 | |
Ladon | 0:12efa8652054 | 120 | // For TIM6 register: |
Ladon | 0:12efa8652054 | 121 | // - |
Ladon | 0:12efa8652054 | 122 | volatile unsigned short* cr1 = reinterpret_cast<volatile unsigned short*>(0x40001000U); |
Ladon | 0:12efa8652054 | 123 | volatile unsigned short* dier = reinterpret_cast<volatile unsigned short*>(0x4000100CU); |
Ladon | 0:12efa8652054 | 124 | volatile unsigned short* sr = reinterpret_cast<volatile unsigned short*>(0x40001010U); |
Ladon | 0:12efa8652054 | 125 | volatile unsigned short* egr = reinterpret_cast<volatile unsigned short*>(0x40001014U); |
Ladon | 0:12efa8652054 | 126 | volatile unsigned int* cnt = reinterpret_cast<volatile unsigned int*>( 0x40001024U); |
Ladon | 0:12efa8652054 | 127 | volatile unsigned short* psc = reinterpret_cast<volatile unsigned short*>(0x40001028U); |
Ladon | 0:12efa8652054 | 128 | volatile unsigned short* arr = reinterpret_cast<volatile unsigned short*>(0x4000102CU); |
Ladon | 0:12efa8652054 | 129 | |
Ladon | 0:12efa8652054 | 130 | // For RCC register: |
Ladon | 0:12efa8652054 | 131 | // - |
Ladon | 0:12efa8652054 | 132 | volatile unsigned int* apb1enr = reinterpret_cast<unsigned int*>(0x40021000U + 0x1CU); |
Ladon | 0:12efa8652054 | 133 | volatile unsigned int* apb1rstr = reinterpret_cast<unsigned int*>(0x40021000U + 0x10U); |
Ladon | 0:12efa8652054 | 134 | |
Ladon | 0:12efa8652054 | 135 | // For GPIOA register: |
Ladon | 0:12efa8652054 | 136 | // - |
Ladon | 0:12efa8652054 | 137 | volatile unsigned int* moder = reinterpret_cast<volatile unsigned int*>( 0x48000000U); |
Ladon | 0:12efa8652054 | 138 | volatile unsigned short* odr = reinterpret_cast<volatile unsigned short*>(0x48000014U); |
Ladon | 0:12efa8652054 | 139 | |
Ladon | 0:12efa8652054 | 140 | // Timer functions: |
Ladon | 0:12efa8652054 | 141 | // - |
Ladon | 0:12efa8652054 | 142 | |
Ladon | 0:12efa8652054 | 143 | void inline enable_timer6 () |
Ladon | 0:12efa8652054 | 144 | { |
Ladon | 0:12efa8652054 | 145 | // *cr1 = 0x8D; |
Ladon | 0:12efa8652054 | 146 | *cr1 = 5; |
Ladon | 0:12efa8652054 | 147 | } |
Ladon | 0:12efa8652054 | 148 | |
Ladon | 0:12efa8652054 | 149 | /* |
Ladon | 0:12efa8652054 | 150 | void inline disable_timer6 () |
Ladon | 0:12efa8652054 | 151 | { |
Ladon | 0:12efa8652054 | 152 | *cr1 |= 2; |
Ladon | 0:12efa8652054 | 153 | } |
Ladon | 0:12efa8652054 | 154 | */ |
Ladon | 0:12efa8652054 | 155 | |
Ladon | 0:12efa8652054 | 156 | void inline enable_timer6_interrupt () |
Ladon | 0:12efa8652054 | 157 | { |
Ladon | 0:12efa8652054 | 158 | *dier = 1; |
Ladon | 0:12efa8652054 | 159 | } |
Ladon | 0:12efa8652054 | 160 | |
Ladon | 0:12efa8652054 | 161 | void inline clear_timer6_status () |
Ladon | 0:12efa8652054 | 162 | { |
Ladon | 0:12efa8652054 | 163 | *sr = 0; |
Ladon | 0:12efa8652054 | 164 | } |
Ladon | 0:12efa8652054 | 165 | |
Ladon | 0:12efa8652054 | 166 | void inline reset_timer6 () |
Ladon | 0:12efa8652054 | 167 | { |
Ladon | 0:12efa8652054 | 168 | *egr = 1; |
Ladon | 0:12efa8652054 | 169 | clear_timer6_status(); |
Ladon | 0:12efa8652054 | 170 | } |
Ladon | 0:12efa8652054 | 171 | |
Ladon | 0:12efa8652054 | 172 | void inline downscale_timer6_by (const unsigned short& v) |
Ladon | 0:12efa8652054 | 173 | { |
Ladon | 0:12efa8652054 | 174 | *psc = v; |
Ladon | 0:12efa8652054 | 175 | } |
Ladon | 0:12efa8652054 | 176 | |
Ladon | 0:12efa8652054 | 177 | void inline downscale_timer6_max () |
Ladon | 0:12efa8652054 | 178 | { |
Ladon | 0:12efa8652054 | 179 | downscale_timer6_by(0xFFFF); |
Ladon | 0:12efa8652054 | 180 | } |
Ladon | 0:12efa8652054 | 181 | |
Ladon | 0:12efa8652054 | 182 | void inline limit_timer6_counter_to (const unsigned short& v) |
Ladon | 0:12efa8652054 | 183 | { |
Ladon | 0:12efa8652054 | 184 | *arr = v; |
Ladon | 0:12efa8652054 | 185 | } |
Ladon | 0:12efa8652054 | 186 | |
Ladon | 0:12efa8652054 | 187 | void inline set_interrupt_period_to (const double& T) |
Ladon | 0:12efa8652054 | 188 | { |
Ladon | 0:12efa8652054 | 189 | limit_timer6_counter_to(36000); |
Ladon | 0:12efa8652054 | 190 | downscale_timer6_by(1999 + (T - 1) * 2e3); |
Ladon | 0:12efa8652054 | 191 | } |
Ladon | 0:12efa8652054 | 192 | |
Ladon | 0:12efa8652054 | 193 | void inline set_interrupt_period_to_1sec () |
Ladon | 0:12efa8652054 | 194 | { |
Ladon | 0:12efa8652054 | 195 | set_interrupt_period_to(1); |
Ladon | 0:12efa8652054 | 196 | } |
Ladon | 0:12efa8652054 | 197 | |
Ladon | 0:12efa8652054 | 198 | // GPIOA functions: |
Ladon | 0:12efa8652054 | 199 | // - |
Ladon | 0:12efa8652054 | 200 | |
Ladon | 0:12efa8652054 | 201 | void init_LED () |
Ladon | 0:12efa8652054 | 202 | { |
Ladon | 0:12efa8652054 | 203 | unsigned int tmp = *moder; |
Ladon | 0:12efa8652054 | 204 | tmp |= 0x00000400U; |
Ladon | 0:12efa8652054 | 205 | tmp &= 0xFFFFF7FFU; |
Ladon | 0:12efa8652054 | 206 | // ^ Write 0,1 to bits 11,10. |
Ladon | 0:12efa8652054 | 207 | // - |
Ladon | 0:12efa8652054 | 208 | *moder = tmp; |
Ladon | 0:12efa8652054 | 209 | } |
Ladon | 0:12efa8652054 | 210 | |
Ladon | 0:12efa8652054 | 211 | void toggle_LED () |
Ladon | 0:12efa8652054 | 212 | { |
Ladon | 0:12efa8652054 | 213 | *odr ^= 0x20; |
Ladon | 0:12efa8652054 | 214 | } |
Ladon | 0:12efa8652054 | 215 | |
Ladon | 0:12efa8652054 | 216 | // Other functions: |
Ladon | 0:12efa8652054 | 217 | // - |
Ladon | 0:12efa8652054 | 218 | |
Ladon | 0:12efa8652054 | 219 | void inline enable_clock_for_timer6 () |
Ladon | 0:12efa8652054 | 220 | { |
Ladon | 0:12efa8652054 | 221 | *apb1enr |= 0x00000010U; |
Ladon | 0:12efa8652054 | 222 | } |
Ladon | 0:12efa8652054 | 223 | |
Ladon | 0:12efa8652054 | 224 | // NVIC functions: |
Ladon | 0:12efa8652054 | 225 | // - |
Ladon | 0:12efa8652054 | 226 | |
Ladon | 0:12efa8652054 | 227 | extern "C" void TIM6_DAC1_IRQHandler (void) |
Ladon | 0:12efa8652054 | 228 | { |
Ladon | 0:12efa8652054 | 229 | clear_timer6_status(); |
Ladon | 0:12efa8652054 | 230 | toggle_LED(); |
Ladon | 0:12efa8652054 | 231 | } |
Ladon | 0:12efa8652054 | 232 | |
Ladon | 0:12efa8652054 | 233 | inline void enable_interrupt () |
Ladon | 0:12efa8652054 | 234 | { |
Ladon | 0:12efa8652054 | 235 | NVIC_EnableIRQ(TIM6_DAC1_IRQn); |
Ladon | 0:12efa8652054 | 236 | } |
Ladon | 0:12efa8652054 | 237 | |
Ladon | 0:12efa8652054 | 238 | // |
Ladon | 0:12efa8652054 | 239 | // - |
Ladon | 0:12efa8652054 | 240 | // |
Ladon | 0:12efa8652054 | 241 | |
Ladon | 0:12efa8652054 | 242 | int main () |
Ladon | 0:12efa8652054 | 243 | { |
Ladon | 0:12efa8652054 | 244 | cout << "Entered main()." << endl; |
Ladon | 0:12efa8652054 | 245 | init_LED(); |
Ladon | 0:12efa8652054 | 246 | enable_interrupt(); |
Ladon | 0:12efa8652054 | 247 | enable_clock_for_timer6(); |
Ladon | 0:12efa8652054 | 248 | // ^ I think we need clock (?APB1?) running in order to setup the timer. |
Ladon | 0:12efa8652054 | 249 | // - |
Ladon | 0:12efa8652054 | 250 | set_interrupt_period_to(0.5); |
Ladon | 0:12efa8652054 | 251 | // ^ LED stays open for 0.5 and closed for 0.5 with a total of 1 sec. |
Ladon | 0:12efa8652054 | 252 | // - |
Ladon | 0:12efa8652054 | 253 | enable_timer6_interrupt(); |
Ladon | 0:12efa8652054 | 254 | enable_timer6(); |
Ladon | 0:12efa8652054 | 255 | cout << "Exiting main().." << endl; |
Ladon | 0:12efa8652054 | 256 | cout << endl; |
Ladon | 0:12efa8652054 | 257 | } |