#include <stdint.h>
#include <stm32f4xx.h>
#include "exti.h"
#include "pin.h"
#include "timer.h"
#include "registers.h"
#include "stm32f401xe.h"
#define LED_PIN 5
#define LED_GPIO GPIOA
#define BUTTON_PIN 13
#define BUTTON_GPIO GPIOC
#define BUTTON_GPIO_ID 2 // A 0 B 1 C 2
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
}
void main()
{
SystemCoreClockSetHSI();
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOCEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
// Initialize timer for display at interrupt frequency
// digit update frequency * digits * 34
// aim for 100 Hz * 4 * 34 = 13 600 Hz
__enable_irq();
while(1) { __WFI(); }
}
#define CYCLES 17
typedef struct {
// Display board configuration
uint16_t digits;
pin_t pin_data;
pin_t pin_sftclk;
pin_t pin_strobe;
// Current application data
uint16_t number;
uint8_t digit_dots;
// Current application state
uint8_t digits_en;
// Current internal state of the display
uint16_t current_cycle;
uint32_t current_shifter;
uint8_t current_digit;
uint16_t max_value;
} display_t;
typedef enum {
DISPLAY_UPDATE_NEW_DIGIT,
DISPLAY_UPDATE_DISPLAY_DIGIT,
DISPLAY_UPDATE_SHIFT,
} display_update_state_t;
const char numbers_seven_segments[11] = {
0x3F, /* 0 */
0x06, /* 1 */
0x5B, /* 2 */
0x4F, /* 3 */
0x66, /* 4 */
0x6D, /* 5 */
0x7D, /* 6 */
0x07, /* 7 */
0x7F, /* 8 */
0x6F, /* 9 */
0x40, /* - */
};
/**
* @brief Convert a digit from a regular number to seven segments
* @param[in] number The number to convert
* @param[in] digit The digit from least significant, starting with 0
* @return Description
*/
uint16_t seven_segment_convert(uint16_t number, uint16_t digit);
/**
* @brief Converts a number to serial strem to send via the SIPO register.
* @param[in] number The number to convert to the values for shift register
* @param[in] digit The digit from the least significant, starting with 0
* @return Description
*/
uint16_t convert_num_digit(uint16_t number, uint8_t digit);
void display_number_set(display_t* display, uint16_t number);
void display_number_increment(display_t* display);
void display_enable_digit(display_t* display, uint8_t digit, uint8_t enable);
display_update_state_t display_update(display_t* display);
uint16_t seven_segment_convert(uint16_t number, uint16_t digit) {
for (int i = 0; i < digit; i++) {
number /= 10;
}
return numbers_seven_segments[number % 10];
}
uint16_t convert_num_digit(uint16_t number, uint8_t digit) {
uint16_t seven_segments = seven_segment_convert(number, digit);
uint16_t digits = 1 << digit;
return (seven_segments << 8) | digits;
}
void display_number_set(display_t *display, uint16_t number) {
display->number = number;
}
void display_number_increment(display_t *display) {
display->number = (display->number + 1) % display->max_value;
}
void display_enable_digit(display_t *display, uint8_t digit, uint8_t enable) {
uint16_t digits = 1 << digit;
if (enable != 0) {
display->digits_en |= digits;
} else {
display->digits_en &= ~digits;
}
}
display_update_state_t display_update(display_t *display) {
display_update_state_t state = DISPLAY_UPDATE_SHIFT;
if (display->current_cycle == 0) {
// Set up.
state = DISPLAY_UPDATE_NEW_DIGIT;
}
if (display->current_cycle < CYCLES - 1) {
pin_write(&display->pin_data,
reg_read_bits_pos(&display->current_shifter,
16 - display->current_cycle,
1));
pin_set(&display->pin_sftclk);
pin_reset(&display->pin_sftclk);
} else {
// Send strobe
pin_set(&display->pin_strobe);
pin_reset(&display->pin_strobe);
state = DISPLAY_UPDATE_DISPLAY_DIGIT;
}
display->current_cycle = (display->current_cycle + 1) % CYCLES;
return state;
}