diff options
-rw-r--r-- | src/main.c | 167 |
1 files changed, 165 insertions, 2 deletions
@@ -1,11 +1,174 @@ -#define PINOUT_BTN (8) +#ifdef __cplusplus +extern "C" { +#endif + +#include "stm32f091xc.h" + +// GPIO A +#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) + const unsigned short leds[] = {PINOUT_LED_1, PINOUT_LED_2, PINOUT_LED_3, PINOUT_LED_4}; +#define PINOUT_BTN (8) + +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_DISP_CLK * 2)) | + (0b11 << (PINOUT_DISP_DIO * 2)) | + (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 |= (0b01 << (PINOUT_DISP_CLK * 2)) | + (0b01 << (PINOUT_DISP_DIO * 2)) | + (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; + return; +} + +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. + */ +int 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() { - return 0; + shield_config(); + interrupt_setup(); + + while (1) { + // if ((TIM3->SR & TIM_SR_CC1IF) > 0) { + // toggle_direction(); + // TIM3->CNT = 0; + // TIM3->SR &= ~(TIM_SR_CC1IF); + // TIM3->EGR |= TIM_EGR_UG; + // } + timer_delay(100); + next_led(); + } +} + +#ifdef __cplusplus } +#endif |