#include "stm32f401xe.h" #include "timer.h" #include #include #include #include "exti.h" #include "pin.h" #include "registers.h" #include "display.h" #include "delay.h" #include "uart.h" #include "adc.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 } #define DIGITS 4 display_t display; timer_t display_timer; uart_t uart; adc_t adc; timer_t adc_timer; typedef struct { char digits[4]; uint8_t digits_count; uint8_t dot; bool error; } converted_number_t; converted_number_t convert_number(char* data); void main() { // Setup SystemCoreClockSetHSI(); systick_configure(); RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN; RCC->APB1ENR |= RCC_APB1ENR_TIM3EN | RCC_APB1ENR_USART2EN | RCC_APB1ENR_TIM2EN; RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_ADC1EN; // 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 { 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, DIGITS, pin_data, pin_sftclk, pin_strobe); display_dots(&display, 1 << 0); 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); } { pin_t pin_rx; pin_t pin_tx; pin_init(&pin_tx, GPIOA, 2); pin_init(&pin_rx, GPIOA, 3); pin_into_alternate(&pin_rx, 7); pin_into_alternate(&pin_tx, 7); usart_init(&uart, USART2, 2); usart_configure_speed(&uart, 12000000, 9600); usart_configure_uart(&uart, true, USART_WORD_9_BITS, true, USART_PARITY_EVEN, USART_STOP_BIT_ONE); usart_configure_transmitter(&uart, true); usart_configure_receiver(&uart, true); } { pin_t adc_pin; pin_init(&adc_pin, GPIOA, 0); pin_mode(&adc_pin, ANALOG); adc_init(&adc, ADC1); adc_configure(&adc, ADC_RES_12BIT, ADC_ALIGN_RIGHT, false); adc_sequence(&adc, 1); adc_select(&adc, 0, 0); adc_external_trigger(&adc, ADC_EXT_RISING_EDGE, ADC_EXT_REG_TIM2_TRGO); adc_enable(&adc, true); timer_init(&adc_timer, TIM2, 2); timer_configure(&adc_timer, 0, 60000, 0); timer_set_refresh(&adc_timer, 200); // once per 200 ms timer_master_mode(&adc_timer, TIMER_MMS_UPDATE); timer_enable(&adc_timer); } __enable_irq(); // Application while (1) { if (!adc_is_captured(&adc)) { continue; } uint32_t value = adc_captured_value(&adc); uint32_t voltage = (value * 3300) / 4096; display_convert_number(&display, voltage); for (uint8_t digit = 0; digit < display.digits_count; digit++) { usart_transmit(&uart, &display.digits[digit], 1); if (digit == 0) { usart_transmit(&uart, ".", 1); } } usart_transmit(&uart, " V\r\n", 4); } } converted_number_t convert_number(char* data) { converted_number_t number; bool found_dot = false; uint8_t curr_digit = 0; number.dot = 0; number.error = false; while (*data != '\0' && curr_digit < DIGITS) { char curr = *(data++); // handle first dot if (curr == '.' && !found_dot) { found_dot = true; // if dot first, assume zero. if (curr_digit == 0) { number.digits[curr_digit++] = '0'; } number.dot = 1 << (curr_digit - 1); continue; // handle digit } else if (curr >= '0' && curr <= '9') { number.digits[curr_digit++] = curr; // handle others } else { number.error = true; break; } } if (*data != '\0') { number.error = true; } number.digits_count = curr_digit; return number; } /* void TIM2_handler(void) { */ /* // The stopwatch timer */ /* } */ void TIM3_handler(void) { timer_clear_interrupt(&display_timer); display_update(&display); }