aboutsummaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..7978b16
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,162 @@
+#include <stdbool.h>
+#include "stm32f091xc.h"
+#include "main.h"
+#include "tm1637.h"
+
+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 <num> of the I/O-shield. <num> should be the number
+ * of the LED to drive (0, 1, 2 or 3) and <on> 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);
+}
+
+void dumb_delay() {
+ for(unsigned long i = 0; i < 50e3; i++);
+}
+
+/**
+ * This function uses timer 3 to generate a blocking delay of <milliseconds>.
+ * 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
+}
+
+int main() {
+ shield_config();
+ tm1637_begin();
+ timer_delay(2000);
+
+ unsigned int minutes = 0;
+ unsigned int seconds = 0;
+
+ TM1637Sequence seq;
+ uint8_t dis_br_data[] = { 0b10001111 };
+ seq.data = dis_br_data;
+ seq.length = 1;
+ _tm1637_send(seq);
+
+ uint8_t dis_seg_data[] = { 0b11000001, 0b01111111 };
+ seq.data = dis_seg_data;
+ seq.length = 2;
+ _tm1637_send(seq);
+
+ while (1) {
+ timer_delay(1e3);
+ seconds++;
+ if (seconds >= 60) {
+ seconds = 0;
+ minutes++;
+ }
+ }
+}