Spyros Papanastasiou
/
General_purpose_timer_TIM2__custom_interrupt_handler
Using CMSIS NVIC functions to set custom timer interrupt handler.
Diff: main.cpp
- Revision:
- 2:7c45a714b991
- Parent:
- 1:8d34cf217c0a
- Child:
- 3:db424769ecca
--- a/main.cpp Fri Jul 26 12:32:55 2019 +0000 +++ b/main.cpp Thu Aug 01 07:53:05 2019 +0000 @@ -1,193 +1,59 @@ #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 () +void inline enable_timer () { -// *cr1 = 0x8D; - *cr1 = 5; + TIM2->CR1 = TIM_CR1_URS | TIM_CR1_CEN; } -/* -void inline disable_timer6 () +void inline enable_timer_interrupt () { - *cr1 |= 2; + cout << "Setting DIER..." << endl; + cout << "DIER was : 0x" << hex << TIM2->DIER << endl; + cout << "Status is : 0x" << hex << TIM2->SR << endl; + TIM2->DIER = TIM_DIER_UIE; + cout << "Set DIER." << endl; } -*/ -void inline enable_timer6_interrupt () +void inline clear_timer_status () { - *dier = 1; + TIM2->SR = 0; } -void inline clear_timer6_status () +void inline reset_timer () { - *sr = 0; -} - -void inline reset_timer6 () -{ - *egr = 1; - clear_timer6_status(); + TIM2->EGR = TIM_EGR_UG; + clear_timer_status(); } -void inline downscale_timer6_by (const unsigned short& v) +void inline downscale_timer_by (const unsigned short& v) { - *psc = v; + cout << "Downscaling counter by : 0x" << hex << 1 + v << endl; + TIM2->PSC = v; } -void inline downscale_timer6_max () +void inline downscale_timer_max () { - downscale_timer6_by(0xFFFF); + downscale_timer_by(0xFFFF); } -void inline limit_timer6_counter_to (const unsigned short& v) +void inline limit_timer_counter_to (const unsigned int& v) { - *arr = v; + cout << "Limiting counter to : 0x" << hex << v << endl; + TIM2->ARR = v; } void inline set_interrupt_period_to (const double& T) { - limit_timer6_counter_to(36000); - downscale_timer6_by(1999 + (T - 1) * 2e3); + limit_timer_counter_to(36000); + downscale_timer_by(1999 + (T - 1) * 2e3); // ^This allows : 500us <= T <= 32.768 // The ^former was found by solving : 0 <= 1999 + (T - 1) * 2e3 <= 0xFFFF, for the prescaler register. // - @@ -203,39 +69,41 @@ void init_LED () { - unsigned int tmp = *moder; - tmp |= 0x00000400U; - tmp &= 0xFFFFF7FFU; - // ^ Write 0,1 to bits 11,10. + GPIOA->MODER &= ~GPIO_MODER_MODER5; + // Set as output. // - - *moder = tmp; + GPIOA->MODER |= 1 << GPIO_MODER_MODER5_Pos; } void toggle_LED () { - *odr ^= 0x20; + GPIOA->ODR ^= GPIO_ODR_5; } // Other functions: // - -void inline enable_clock_for_timer6 () +void inline enable_clock_for_timer () { - *apb1enr |= 0x00000010U; + RCC->APB1RSTR |= RCC_APB1RSTR_TIM2RST; + RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM2RST; + RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } // NVIC functions: // - -extern "C" void TIM6_DAC1_IRQHandler (void) +extern "C" void TIM2_IRQHandler (void) { - clear_timer6_status(); + cout << "Interrupt hit!" << endl; + clear_timer_status(); toggle_LED(); } inline void enable_interrupt () { - NVIC_EnableIRQ(TIM6_DAC1_IRQn); + NVIC_SetPriority(TIM2_IRQn, 255); + NVIC_EnableIRQ(TIM2_IRQn); } // @@ -246,15 +114,25 @@ { 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); + enable_clock_for_timer(); + TIM2->ARR = 0xffffffff; + TIM2->CR1 = TIM2->CR2 = TIM2->SMCR = TIM2->DIER = TIM2->SR = TIM2->CCMR1 = TIM2->CCMR2 + = TIM2->CCER = TIM2->CNT = TIM2->PSC = TIM2->CCR1 = TIM2->CCR2 = TIM2->CCR3 + = TIM2->CCR4 = TIM2->DCR = TIM2->DMAR = 0; + downscale_timer_max(); + limit_timer_counter_to(0x1000); // ^ LED stays open for 0.5 and closed for 0.5 with a total of 1 sec. // - - enable_timer6_interrupt(); - enable_timer6(); + clear_timer_status(); +// enable_timer_interrupt(); + enable_timer(); + enable_interrupt(); cout << "Exiting main().." << endl; cout << endl; + while (true) + { + cout << hex + << "Status : 0x" << TIM2->SR << endl + << "Count : 0x" << TIM2->CNT << endl; + } } \ No newline at end of file