#include #include "stm32f091xc.h" #include "main.h" #include "tm1637.h" #define SECOND (1e3) #define LONG_PRESS_DURATION (300) const unsigned short leds[] = {PINOUT_LED_1, PINOUT_LED_2, PINOUT_LED_3, PINOUT_LED_4}; volatile bool led_direction = false; /* * This function configures the I/O-ports that are used by the I/O-shield. It * uses register RCC_AHBENR to enable the clocks for the ports that are used, * register GPIOx_MODER to configure pins as input, analog or output and * GPIOx_PUPDR to configure pull-up and pull-down resistors. */ void shield_config() { // enable internal clocks RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; // GPIO A and B RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // ADC1 peripheral RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // TIM3 // clear mode register configuration bits GPIOB->MODER &= ~((0b11 << (PINOUT_LED_1 * 2)) | (0b11 << (PINOUT_LED_2 * 2)) | (0b11 << (PINOUT_LED_3 * 2)) | (0b11 << (PINOUT_LED_4 * 2)) | (0b11 << (PINOUT_BTN * 2))); GPIOA->MODER &= ~(0b11 << (PINOUT_POT * 2)); // set output mode register configuration bits // 0b00 -> input mode (reset state) // 0b01 -> general purpose output mode // 0b10 -> alternate function mode // 0b11 -> analog mode GPIOB->MODER |= (0b01 << (PINOUT_LED_1 * 2)) | (0b01 << (PINOUT_LED_2 * 2)) | (0b01 << (PINOUT_LED_3 * 2)) | (0b01 << (PINOUT_LED_4 * 2)) | (0b00 << (PINOUT_BTN * 2)); GPIOA->MODER |= (0b11 << (PINOUT_POT * 2)); // pull-up resistor for button GPIOB->PUPDR &= ~(0b11 << (PINOUT_BTN * 2)); GPIOB->PUPDR |= (0b01 << (PINOUT_BTN * 2)); // calirate ADC1 ADC1->CR |= ADC_CR_ADCAL; while (ADC1->CR & ADC_CR_ADCAL); ADC1->CR |= ADC_CR_ADEN; // enable ADC1 // TIM3 setup TIM3->PSC = 8000 - 1; // prescaler set so each timer tick is 1ms TIM3->CCMR1 |= TIM_CCMR1_OC1M_0; TIM3->EGR |= TIM_EGR_UG; // generate update event TIM3->CR1 |= TIM_CR1_CEN; // enable counter } void toggle_direction() { led_direction = !led_direction; } void EXTI4_15_IRQHandler(void) { // toggle_direction(); led_direction = !led_direction; } void interrupt_setup() { RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // RCC->APB2ENR |= RCC_APB2ENR_SYSCFGCOMPEN; SYSCFG->EXTICR[PINOUT_BTN / 4] &= ~(0xF << ((PINOUT_BTN % 4) * 4)); SYSCFG->EXTICR[PINOUT_BTN / 4] |= (0x1 << ((PINOUT_BTN % 4) * 4)); EXTI->IMR |= (1 << PINOUT_BTN); EXTI->RTSR &= ~(1 << PINOUT_BTN); EXTI->FTSR |= (1 << PINOUT_BTN); NVIC_SetPriority(EXTI4_15_IRQn, 3); NVIC_EnableIRQ(EXTI4_15_IRQn); } /* * This function drives led of the I/O-shield. should be the number * of the LED to drive (0, 1, 2 or 3) and should be an integer with value * 0 or 1 which indicates if the LED should be turned on (1) or off (0). */ void led_write(int num, int on) { GPIOB->ODR &= ~(1 << leds[num]); GPIOB->ODR |= (on << leds[num]); } /* * This function returns the status of the pushbutton of the I/O-shield. * Return value 1 indicates that the button is currently pressed and return * value 0 indicates that the button is currently not pressed. */ int button_read() { return (GPIOB->IDR & (1 << PINOUT_BTN)) == 0; } /* * Read potmeter value as analog value between 0 and 2^12 - 1 */ unsigned int pot_read() { ADC1->CHSELR = (1 << PINOUT_POT); ADC1->CR |= ADC_CR_ADSTART; while (ADC1->CR & ADC_CR_ADSTART); uint16_t result = ADC1->DR; ADC1->CR |= ADC_CR_ADSTP; return result; } void next_led() { static uint8_t led = 0; for(int j = 0; j < 4; j++) led_write(j, 0); // clear all leds led = (led + led_direction * 2 - 1) & 0b11; // calculate next led index led_write(led, 1); } /** * This function uses timer 3 to generate a blocking delay of . * It uses the channel 1 capture/compare register to check if the time (in * milliseconds) has expired. */ void timer_delay(unsigned short millis) { TIM3->CCR1 = millis; // set delay amount TIM3->CNT = 0; // reset timer TIM3->SR &= ~(TIM_SR_CC1IF); // reset capture/compare output while ((TIM3->SR & TIM_SR_CC1IF) == 0); // wait until c/c goes high } void timer_display(unsigned int minutes, unsigned int seconds, bool colon) { tm1637_dispcfg(pot_read() / 512, 1); // 512 = 2**12 / 8; [0,4096]->[0,8] tm1637_segmentsend(0, tm1637_font[minutes / 10]); tm1637_segmentsend(1, tm1637_font[minutes % 10] | (colon * TM1637_COLON)); tm1637_segmentsend(2, tm1637_font[seconds / 10]); tm1637_segmentsend(3, tm1637_font[seconds % 10]); } int main() { shield_config(); tm1637_begin(); timer_delay(100); unsigned int minutes = 0; unsigned int seconds = 0; bool pause = false; button_state state = up_idle; bool reverse = false; unsigned short led = 0; // timers unsigned long millis_stopwatch = 0; unsigned long millis_looplicht = 0; unsigned long millis_button_dn = 0; bool long_press = false; tm1637_dispcfg(7, 1); while (1) { uint8_t button_now = button_read(); switch(state) { case up_idle: { state = button_now ? down_edge : up_idle; millis_button_dn = 0; break; } case down_edge: { state = button_now ? down_idle : up_idle; millis_button_dn = 1; break; } case down_idle: { state = button_now ? down_idle : up_edge; millis_button_dn++; break; } case up_edge: { state = button_now ? down_idle : up_idle; long_press = millis_button_dn >= LONG_PRESS_DURATION; if (!long_press) pause = !pause; else { pause = true; minutes = 0; seconds = 0; millis_stopwatch = 0; } break; } } if (millis_stopwatch % 50 == 0) timer_display(minutes, seconds, millis_stopwatch > (SECOND / 2)); if (millis_stopwatch == SECOND) { seconds++; if (seconds >= 60) { seconds = 0; minutes++; if (minutes >= 100) minutes = 0; } millis_stopwatch = 0; } unsigned int delay_amount = 50 + pot_read(); if (millis_looplicht >= delay_amount) { for(int j = 0; j < 4; j++) led_write(j, 0); // calculate next led index led = (led + ((2 * reverse) - 1)) & 0b11; led_write(led, 1); millis_looplicht = 0; } timer_delay(1); millis_looplicht++; if (!pause) millis_stopwatch++; } }