Using CMSIS NVIC functions to set custom timer interrupt handler.

Dependencies:   mbed

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