Using CMSIS NVIC functions to set custom timer interrupt handler.

Dependencies:   mbed

Revision:
0:12efa8652054
Child:
1:8d34cf217c0a
diff -r 000000000000 -r 12efa8652054 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Jul 24 15:08:28 2019 +0000
@@ -0,0 +1,257 @@
+#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;
+}
\ No newline at end of file