Spyros Papanastasiou
/
General_purpose_timer_TIM2__custom_interrupt_handler
Using CMSIS NVIC functions to set custom timer interrupt handler.
Diff: main.cpp
- 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