diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 162 | ||||
-rw-r--r-- | src/main.h | 26 | ||||
-rw-r--r-- | src/tm1637.c | 110 | ||||
-rw-r--r-- | src/tm1637.h | 29 |
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); |