aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.c162
-rw-r--r--src/main.h26
-rw-r--r--src/tm1637.c110
-rw-r--r--src/tm1637.h29
4 files changed, 327 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++;
+ }
+ }
+}
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 0000000..0714611
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,26 @@
+#pragma once
+#define PINOUT_DISP_CLK (5)
+#define PINOUT_DISP_DIO (6)
+
+#define PINOUT_POT (7)
+
+// GPIO B
+#define PINOUT_LED_1 (3)
+#define PINOUT_LED_2 (5)
+#define PINOUT_LED_3 (4)
+#define PINOUT_LED_4 (6)
+
+#define PINOUT_BTN (8)
+
+void shield_config();
+void toggle_direction();
+void EXTI4_15_IRQHandler(void);
+void interrupt_setup();
+void led_write(int num, int on);
+int button_read();
+unsigned int pot_read();
+void next_led();
+void dumb_delay();
+void timer_delay(unsigned short millis);
+int main();
+
diff --git a/src/tm1637.c b/src/tm1637.c
new file mode 100644
index 0000000..4e6e68c
--- /dev/null
+++ b/src/tm1637.c
@@ -0,0 +1,110 @@
+#include "main.h"
+#include "tm1637.h"
+#include "stm32f091xc.h"
+
+void _tm1637_micro_delay() {
+ timer_delay(10);
+ // for(unsigned int x = 0; x < 10e4; x++);
+}
+
+/** @brief reset gpio mode registers for dio */
+void _tm1637_GPIOMODE_reset() {
+ GPIOA->MODER &= ~(0b11 << (PINOUT_DISP_DIO * 2));
+ GPIOA->PUPDR &= ~(0b11 << (PINOUT_DISP_DIO * 2));
+ GPIOA->OTYPER &= ~(0b1 << PINOUT_DISP_DIO);
+}
+
+/** @brief set dio to general-purpose output mode */
+void _tm1637_GPIOMODE_gp_output() {
+ _tm1637_GPIOMODE_reset();
+
+ GPIOA->MODER |= (0b01 << (PINOUT_DISP_DIO * 2));
+}
+
+/** @brief set dio to open drain input mode */
+void _tm1637_GPIOMODE_open_drain() {
+ _tm1637_GPIOMODE_reset();
+
+ GPIOA->MODER |= (0b10 << (PINOUT_DISP_DIO * 2));
+ GPIOA->OTYPER |= (0b1 << PINOUT_DISP_DIO);
+}
+
+/**
+ * @brief gpio write abstraction function
+ * @param pin pin to write
+ * @param state state to set `pin` to
+ */
+void _tm1637_pin_write(uint32_t pin, bool state) {
+ GPIOA->ODR ^= (((GPIOA->ODR & (1 << pin)) >> pin) ^ state) << pin;
+
+ if (pin == PINOUT_DISP_CLK) led_write(0, state);
+ if (pin == PINOUT_DISP_DIO) led_write(1, state);
+}
+
+void tm1637_begin() {
+ _tm1637_GPIOMODE_gp_output();
+
+ // general purpose output mode for clk
+ GPIOA->MODER &= ~(0b11 << (PINOUT_DISP_CLK * 2));
+ GPIOA->MODER |= (0b01 << (PINOUT_DISP_CLK * 2));
+
+ _tm1637_pin_write(PINOUT_DISP_CLK, 1);
+ _tm1637_pin_write(PINOUT_DISP_DIO, 1);
+
+ _tm1637_micro_delay();
+}
+
+void _tm1637_start() {
+ _tm1637_pin_write(PINOUT_DISP_DIO, 0);
+ _tm1637_micro_delay();
+}
+
+void _tm1637_stop() {
+ _tm1637_pin_write(PINOUT_DISP_CLK, 1);
+ _tm1637_pin_write(PINOUT_DISP_DIO, 1);
+ _tm1637_micro_delay();
+}
+
+bool _tm1637_ack() {
+ _tm1637_pin_write(PINOUT_DISP_CLK, 0);
+ _tm1637_pin_write(PINOUT_DISP_DIO, 0);
+ _tm1637_GPIOMODE_open_drain();
+ _tm1637_micro_delay();
+ _tm1637_pin_write(PINOUT_DISP_CLK, 1);
+ _tm1637_micro_delay();
+ _tm1637_pin_write(PINOUT_DISP_CLK, 0);
+ _tm1637_GPIOMODE_gp_output();
+ _tm1637_micro_delay();
+ return true;
+}
+
+/**
+ * @brief send data to TM1637
+ * @param command command to send
+ * @return response data as TM1637Response struct
+ */
+TM1637Sequence _tm1637_send(TM1637Sequence command) {
+ TM1637Sequence response = { .data = 0, .length = 0 };
+
+ _tm1637_start();
+
+ for (uint32_t byte = 0; byte < command.length; byte++) {
+ for (uint8_t bit = 0; bit < 8; bit++) {
+ uint8_t rev_bit = 7 - bit;
+ _tm1637_pin_write(PINOUT_DISP_CLK, 0);
+ _tm1637_pin_write(PINOUT_DISP_DIO, (command.data[byte] & (1 << rev_bit)) >> rev_bit);
+ _tm1637_micro_delay();
+ _tm1637_pin_write(PINOUT_DISP_CLK, 1);
+ _tm1637_micro_delay();
+ }
+ //TODO: confirm ack
+ /*bool ack =*/ _tm1637_ack();
+ }
+
+ _tm1637_micro_delay();
+
+ // stop condition
+ _tm1637_stop();
+
+ return response;
+}
diff --git a/src/tm1637.h b/src/tm1637.h
new file mode 100644
index 0000000..b9fa42c
--- /dev/null
+++ b/src/tm1637.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct {
+ uint8_t* data;
+ uint32_t length;
+} TM1637Sequence;
+
+/** @brief configure registers for tm1637 */
+void tm1637_begin();
+
+/**
+ * @brief configure display brightness
+ * @param brightness display brightness from 0 (dim) to 7 (bright)
+ * @param on whether the display is on
+ */
+void tm1637_dispcfg(uint8_t brightness, bool on);
+
+/**
+ * @brief send segment data to display
+ * @param segment address segment 0-3
+ * @param data data to set to segment where LSB=A and MSB-1=G. on segment 1 MSB=colon
+ */
+void tm1637_segmentsend(uint8_t segment, uint8_t data);
+
+
+TM1637Sequence _tm1637_send(TM1637Sequence command);