#include "stm32f401xe.h"
#include "timer.h"
#include <stdint.h>
#include <stdbool.h>
#include <stm32f4xx.h>
#include "exti.h"
#include "pin.h"
#include "registers.h"
#include "display.h"
#include "delay.h"
void hard_fault_handler() {
while(1) {}
}
void usage_fault_handler() {
while(1) {}
}
void nmi_handler() {
while(1) {}
}
void bus_fault_handler() {
while(1) {}
}
/*----------------------------------------------------------------------------
* SystemCoreClockConfigure: configure SystemCoreClock using HSI
(HSE is not populated on Nucleo board)
*----------------------------------------------------------------------------*/
void SystemCoreClockSetHSI(void) {
RCC->CR |= ((uint32_t)RCC_CR_HSION); // Enable HSI
while ((RCC->CR & RCC_CR_HSIRDY) == 0); // Wait for HSI Ready
RCC->CFGR = RCC_CFGR_SW_HSI; // HSI is system clock
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI); // Wait for HSI used as system clock
FLASH->ACR = FLASH_ACR_PRFTEN; // Enable Prefetch Buffer
FLASH->ACR |= FLASH_ACR_ICEN; // Instruction cache enable
FLASH->ACR |= FLASH_ACR_DCEN; // Data cache enable
FLASH->ACR |= FLASH_ACR_LATENCY_5WS; // Flash 5 wait state
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // HCLK = SYSCLK
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 = HCLK/4
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 = HCLK/2
RCC->CR &= ~RCC_CR_PLLON; // Disable PLL
// HSI = 16 MHz
// PLL configuration: VCO = HSI/M * N, Sysclk = VCO/P
// => Sysclk = 48 MHz, APB1 = 12 MHz, APB2 = 24 MHz
// Since divider for APB1 is != 1, timer clock is 24 MHz
RCC->PLLCFGR = ( 16ul | // PLL_M = 16
(384ul << 6) | // PLL_N = 384
( 3ul << 16) | // PLL_P = 8
(RCC_PLLCFGR_PLLSRC_HSI) | // PLL_SRC = HSI
( 8ul << 24) ); // PLL_Q = 8
RCC->CR |= RCC_CR_PLLON; // Enable PLL
while((RCC->CR & RCC_CR_PLLRDY) == 0) __NOP(); // Wait till PLL is ready
RCC->CFGR &= ~RCC_CFGR_SW; // Select PLL as system clock source
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait till PLL is system clock src
}
display_t display;
timer_t stopwatch_timer;
timer_t display_timer;
timer_t startstop_debounce_timer;
exti_t startstop_exti;
pin_t startstop_button;
pin_t null_button;
bool counting;
void null_timer(void);
void toggle_timer(void);
void main()
{
// Setup
SystemCoreClockSetHSI();
systick_configure();
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM4EN;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
// The timer clocks are at 12 MHz.
// For 100 Hz, the prescaler would be 120 000. That is too big,
// so 200 Hz is chosen. From there it will have to be divided by 2 by application.
// For 6800 Hz, 1765 prescaler will be used, leading to 6798... Hz
// Initialize timer for display at interrupt frequency
// digit update frequency * digits * 34
// aim for 100 Hz * 4 * 17 = 6 800 Hz
/* exti_init(&null_button, 4, EXTI, SYSCFG); */
/* exti_external_interrupt(&null_button, EXTI_GPIOA); */
/* exti_rising_interrupt(&null_button); */
/* exti_enable_interrupt(&null_button); */
pin_init(&null_button, GPIOA, 4);
pin_into_input(&null_button);
pin_speed(&null_button, LOW_SPEED);
exti_init(&startstop_exti, 0, EXTI, SYSCFG);
exti_external_interrupt(&startstop_exti, EXTI_GPIOB);
exti_falling_interrupt(&startstop_exti);
exti_enable_interrupt(&startstop_exti);
pin_init(&startstop_button, GPIOB, 0);
pin_into_input(&startstop_button);
pin_speed(&startstop_button, LOW_SPEED);
{
pin_t pin_data;
pin_t pin_sftclk;
pin_t pin_strobe;
pin_init(&pin_data, GPIOA, 9);
pin_init(&pin_sftclk, GPIOA, 8);
pin_init(&pin_strobe, GPIOB, 5);
pin_into_output(&pin_data);
pin_into_output(&pin_sftclk);
pin_into_output(&pin_strobe);
display_init(&display, 4, pin_data, pin_sftclk, pin_strobe);
display_dots(&display, 1 << 1);
}
timer_init(&stopwatch_timer, TIM2, 2);
timer_set_counter(&stopwatch_timer, 0);
timer_configure(&stopwatch_timer, 0, 60000, 0);
timer_set_refresh(&stopwatch_timer, 10000*4);
{
pin_t pin_capture;
pin_init(&pin_capture, GPIOA, 1);
pin_into_alternate(&pin_capture, 1);
reg_write_bits(&stopwatch_timer.periph->CCMR1,
// TI2 on CCR1 no prescaler - capture every event sampling freq = fdts / 32, N = 8
// fdts = 24 MHz, fsample = 750kHz
(2 << TIM_CCMR1_CC1S_Pos) | (0 << TIM_CCMR1_IC1PSC_Pos) | (0xF << TIM_CCMR1_IC1F_Pos),
TIM_CCMR1_CC1S | TIM_CCMR1_IC1PSC | TIM_CCMR1_IC1F);
reg_write_bits(
&stopwatch_timer.periph->CCER,
// enable CC1 rising edge captures
(1 << TIM_CCER_CC1E_Pos) | (0 << TIM_CCER_CC1NP_Pos) | (1 << TIM_CCER_CC1P_Pos),
TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC1NP);
}
timer_init(&startstop_debounce_timer, TIM4, 4);
timer_configure(&startstop_debounce_timer, 0, 60000, 1);
timer_set_refresh(&startstop_debounce_timer, 10);
timer_enable_interrupt(&startstop_debounce_timer);
timer_init(&display_timer, TIM3, 3);
timer_configure(&display_timer, 0, 500, 0);
timer_set_refresh(&display_timer, 4);
timer_enable_interrupt(&display_timer);
timer_enable(&display_timer);
__enable_irq();
bool captured = false;
uint32_t capture = 0;
// Application
while (1) {
uint32_t count = 0;
if (!pin_read(&null_button)) {
null_timer();
captured = false;
} else {
count = timer_count(&stopwatch_timer) >> 2;
}
if (stopwatch_timer.periph->SR & TIM_SR_CC1IF) {
reg_clear_bits(&stopwatch_timer.periph->SR, TIM_SR_CC1IF);
capture = stopwatch_timer.periph->CCR1 >> 2;
captured = true;
} else if (!counting) {
captured = false;
}
if (captured && counting) {
// prevent capture wrapping
if (count < capture) {
count += 10000;
}
count -= capture;
}
display_enable_digit(&display, 0,
(count % display.max_value) >= 1000);
display_convert_number(&display, count);
/* DELAY_US(100); */
}
}
void null_timer(void) {
timer_set_counter(&stopwatch_timer, 0);
}
void toggle_timer(void) {
if (timer_is_enabled(&stopwatch_timer)) {
timer_disable(&stopwatch_timer);
counting = false;
} else {
timer_enable(&stopwatch_timer);
counting = true;
}
}
void EXTI0_handler(void) {
exti_clear_interrupt(&startstop_exti);
timer_set_counter(&startstop_debounce_timer, 0);
timer_enable(&startstop_debounce_timer);
}
/* void TIM2_handler(void) { */
/* // The stopwatch timer */
/* } */
void TIM3_handler(void) {
timer_clear_interrupt(&display_timer);
display_update(&display);
}
void TIM4_handler(void) {
timer_clear_interrupt(&startstop_debounce_timer);
if (!pin_read(&startstop_button)) {
toggle_timer();
}
}