Spyros Papanastasiou
/
General_purpose_timer_TIM2__custom_interrupt_handler
Using CMSIS NVIC functions to set custom timer interrupt handler.
main.cpp
- Committer:
- Ladon
- Date:
- 2019-07-24
- Revision:
- 0:12efa8652054
- Child:
- 1:8d34cf217c0a
File content as of revision 0:12efa8652054:
#include <mbed.h> #include <iostream> // ^cout... // - #include <iomanip> // ^setw... // - using namespace std; // Board: NUCLEO-F303RE // - // Datasheet: // https://www.st.com/resource/en/datasheet/stm32f303re.pdf // Reference Manual: // https://www.st.com/resource/en/reference_manual/dm00043574.pdf // Programming Manual: // https://www.st.com/resource/en/programming_manual/dm00046982.pdf // (Some address #definitions): // https://raw.githubusercontent.com/ARMmbed/mbed-os/master/targets/TARGET_STM/TARGET_STM32F3/TARGET_STM32F303xE/device/stm32f303xe.h // - // Direct port access is shown in this example: // https://os.mbed.com/users/Ladon/code/Write_to_port_directly__LED/ // CPU hang discussion: // https://community.arm.com/developer/tools-software/tools/f/keil-forum/33581/cpu-crash-when-enable-nvic-for-timer/82797#82797 // Possible clock definition: // 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/ // We 're probably interested in APB1. // - // // * Let 's use a timer to blink the LED. // * Let 's use TIM6 or TIM7 from the datasheet. // * For TIM6 there is IRQ 54 (TIM6_DAC1), as seen in the Reference Manual. // * We find the appropriate alias (TIM6_DAC1_IRQn) in the "#definitions" ^^file above. // * We 'll use ^this and the TIM6_DAC1_IRQHandler one, with NVIC CMSIS instructions // like NVIC_EnableIRQ and NVIC_GetPendingIRQ, as seen in the ^^programming manual. // // * About the interrupt period: // * Assuming that the APB1 peripheral clock, which is responsible for TIM6, // runs at 36Mhz, we can limit the TIM6 to 36000 using the ARR (Auto Reload Register), // and downscale it by 1000. This leaves us with 1 full timer counter per second:) // * Truth is, input clock seems to be 72Mhz.. // * About setting arbitrary period: // * Knowing that the counter clock (probably 72Mhz) gets divided by (1+TIM6_PSC) we can: // * Divide by 1000 (1 + 999) and // * Limit Counter to 72000 which is not possible in 16 bits, but 72000 = 36000 * 2 so // let 's divide by 2 * 1000 instead. and limit to 36000. // That leaves us with the filling period of 1sec. In order to chose any other period, // we imagine the following equality: // 72Mhz 36000 steps // ---------------- = ------------- ==> 2000 + X = 2000 * T ==> X = 2000 * (T - 1) // (1 + 1999 + X) T secs // Thus, PSC (prescaler) is : 1999 + X = 1999 + 2000 * (T - 1), where T is in seconds. // - // From the Reference Manual: // // TIM6 base address: // 0x4000 1000 // - // TIM6_CR1: Control Register 1: // Address offset: 0x00. // 16bits. // Bit0: CEN: Counter Enable. // Enabled when 1. // Bit2: URS: Update Request Source. // 1: Only counter overflow/underflow generates an update interrupt. // // TIM6_DIER: DMA/ Interrupt Enable Register: // Address offset: 0x0C. // 16bits. // Bit0: UIE: Update Interrupt Enable. // 1: Enabled. // I 'm not sure what this does.. // Bit8: UDE: Update DMA Request Enable. // 1: Enabled. // // TIM6_SR: Status Register: // Address offset: 0x10. // 16bits. // Bit0: UIF: Update Interrupt Flag: // ?? // // TIM6_EGR: Event Generation Register: // Address offset: 0x14. // 16bits (1bit) w/o // Bit0: UG: Update Generation: // When set to 1, re-initializes the timer counter and generates an // update of the registers; it is automatically cleared by hardware. // Sounds like a reset button. // // TIM6_CNT: Counter: // Address offset: 0x24. // 32bits wide. // Bits 15..0: CNT: Counter value. // Bit31: UIFCPY: A read-only copy of UIF (update interrupt flag) bit from TIM6_SR (status register). // Note: In order to read the current count, mask the lower 16 bits. // // TIM6_PSC: Prescaler: // Address offset: 0x28. // 16bits. // The counter clock frequency CK_CNT is equal to f_{CK_PSC} / (PSC + 1). // // TIM6_ARR: Auto Reload Register: // Address Offset: 0x2C. // 16bits (r/w). // Info: The upper limit for the counter. // // RCC_APB1ENR: APB1 Peripheral Clock Enable Register: // Base register: (?)RCC at 0x40021000. // Address Offset: 0x1C. // 32bits. // Bit4: TIM6EN: Timer 6 clock Enable. // 0: Disable. // 1: Enable. // - // For TIM6 register: // - volatile unsigned short* cr1 = reinterpret_cast<volatile unsigned short*>(0x40001000U); volatile unsigned short* dier = reinterpret_cast<volatile unsigned short*>(0x4000100CU); volatile unsigned short* sr = reinterpret_cast<volatile unsigned short*>(0x40001010U); volatile unsigned short* egr = reinterpret_cast<volatile unsigned short*>(0x40001014U); volatile unsigned int* cnt = reinterpret_cast<volatile unsigned int*>( 0x40001024U); volatile unsigned short* psc = reinterpret_cast<volatile unsigned short*>(0x40001028U); volatile unsigned short* arr = reinterpret_cast<volatile unsigned short*>(0x4000102CU); // For RCC register: // - volatile unsigned int* apb1enr = reinterpret_cast<unsigned int*>(0x40021000U + 0x1CU); volatile unsigned int* apb1rstr = reinterpret_cast<unsigned int*>(0x40021000U + 0x10U); // For GPIOA register: // - volatile unsigned int* moder = reinterpret_cast<volatile unsigned int*>( 0x48000000U); volatile unsigned short* odr = reinterpret_cast<volatile unsigned short*>(0x48000014U); // Timer functions: // - void inline enable_timer6 () { // *cr1 = 0x8D; *cr1 = 5; } /* void inline disable_timer6 () { *cr1 |= 2; } */ void inline enable_timer6_interrupt () { *dier = 1; } void inline clear_timer6_status () { *sr = 0; } void inline reset_timer6 () { *egr = 1; clear_timer6_status(); } void inline downscale_timer6_by (const unsigned short& v) { *psc = v; } void inline downscale_timer6_max () { downscale_timer6_by(0xFFFF); } void inline limit_timer6_counter_to (const unsigned short& v) { *arr = v; } void inline set_interrupt_period_to (const double& T) { limit_timer6_counter_to(36000); downscale_timer6_by(1999 + (T - 1) * 2e3); } void inline set_interrupt_period_to_1sec () { set_interrupt_period_to(1); } // GPIOA functions: // - void init_LED () { unsigned int tmp = *moder; tmp |= 0x00000400U; tmp &= 0xFFFFF7FFU; // ^ Write 0,1 to bits 11,10. // - *moder = tmp; } void toggle_LED () { *odr ^= 0x20; } // Other functions: // - void inline enable_clock_for_timer6 () { *apb1enr |= 0x00000010U; } // NVIC functions: // - extern "C" void TIM6_DAC1_IRQHandler (void) { clear_timer6_status(); toggle_LED(); } inline void enable_interrupt () { NVIC_EnableIRQ(TIM6_DAC1_IRQn); } // // - // int main () { cout << "Entered main()." << endl; init_LED(); enable_interrupt(); enable_clock_for_timer6(); // ^ I think we need clock (?APB1?) running in order to setup the timer. // - set_interrupt_period_to(0.5); // ^ LED stays open for 0.5 and closed for 0.5 with a total of 1 sec. // - enable_timer6_interrupt(); enable_timer6(); cout << "Exiting main().." << endl; cout << endl; }