/* Megumin LED display firmware * Copyright (C) 2018 Sebastian Götte * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "global.h" uint16_t adc_data[192]; bool sync_running = false; static void quicksort(uint16_t *head, uint16_t *tail); int main(void) { /* Configure clocks for 64 MHz system clock. * * HSI @ 16 MHz --[PLL x16 /4]--> PLL "R" clock @ 64 MHz */ /* Enable peripherals */ RCC->APBENR1 |= RCC_APBENR1_PWREN; /* Increase flash wait states to 2 required for operation above 48 MHz */ FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_PRFTEN | (FLASH->ACR & ~FLASH_ACR_LATENCY_Msk) | (2<ACR & FLASH_ACR_LATENCY_Msk) != (2<PLLCFGR = (16<CR |= RCC_CR_PLLON; while (!(RCC->CR & RCC_CR_PLLRDY)) { /* wait for PLL to stabilize. */ } /* Switch SYSCLK to PLL source. */ RCC->CFGR |= (2<CFGR & RCC_CFGR_SWS_Msk) != (2<AHBENR |= RCC_AHBENR_DMA1EN; RCC->APBENR1 |= RCC_APBENR1_TIM3EN | RCC_APBENR1_DBGEN; RCC->APBENR2 |= RCC_APBENR2_TIM1EN | RCC_APBENR2_ADCEN; RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN | RCC_IOPENR_GPIOCEN; TIM1->PSC = 1; TIM1->ARR = 32767; TIM1->DIER = TIM_DIER_UIE | TIM_DIER_CC1IE; TIM1->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN; TIM1->CCR1 = 3000; NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0); NVIC_EnableIRQ(TIM1_CC_IRQn); NVIC_SetPriority(TIM1_CC_IRQn, 0); TIM3->CR2 = (2<PSC = 0; /* We sample 32 times per 1 kHz AC cycle, and use 32 times oversampling. */ TIM3->ARR = 124; /* Output 64 MHz / 125 = 512 kHz signal */ TIM3->CR1 = TIM_CR1_CEN; DMAMUX1[0].CCR = 5; /* ADC */ DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR; DMA1_Channel1->CMAR = (uint32_t)(void *)adc_data; NVIC_EnableIRQ(DMA1_Channel1_IRQn); NVIC_SetPriority(DMA1_Channel1_IRQn, 64); ADC1->ISR = ADC_ISR_CCRDY | ADC_ISR_ADRDY; /* Clear CCRDY */ ADC1->CR = ADC_CR_ADVREGEN; delay_us(20); ADC1->CR = ADC_CR_ADCAL; while (ADC1->CR & ADC_CR_ADCAL) { /* wait. */ } ADC1->CFGR1 = (1<CFGR2 = (1<CHSELR = (1<<4); /* Enable input 4 -> PA4 (Vdiff)*/ while (!(ADC1->ISR & ADC_ISR_CCRDY)) { /* wait. */ } ADC1->ISR = ADC_ISR_CCRDY; /* Clear CCRDY */ ADC->CCR = ADC_CCR_TSEN | ADC_CCR_VREFEN; ADC1->CR = ADC_CR_ADVREGEN | ADC_CR_ADEN; while (!(ADC1->ISR & ADC_ISR_ADRDY)) { /* wait. */ } ADC1->CR |= ADC_CR_ADSTART; GPIOA->MODER = OUT(0) | IN(1) | OUT(2) | OUT(3) | ANALOG(4) | OUT(5) | OUT(6) | IN(7) | ANALOG(9) | ANALOG(10) | OUT(11) | ANALOG(12)| AF(13) | AF(14); GPIOB->MODER = ANALOG(0) | OUT(3) | ANALOG(1) | ANALOG(2) | ANALOG(4) | ANALOG(5) | ANALOG(6) | ANALOG(8) | OUT(7) | ANALOG(9); GPIOC->MODER = OUT(15) | ANALOG(14) | ANALOG(9); DBG->APBFZ1 |= DBG_APB_FZ1_DBG_TIM3_STOP; DBG->APBFZ2 |= DBG_APB_FZ2_DBG_TIM1_STOP; while (42) { } } void TIM1_BRK_UP_TRG_COM_IRQHandler(void) { TIM1->SR &= ~TIM_SR_UIF; GPIOB->BSRR = (1<<7); if (!sync_running) { while(DMA1_Channel1->CCR != 0) { DMA1_Channel1->CCR = 0; } DMA1_Channel1->CCR = (1<CNDTR = COUNT_OF(adc_data); DMA1_Channel1->CCR |= DMA_CCR_EN; sync_running = true; } } void TIM1_CC_IRQHandler(void) { TIM1->SR &= ~TIM_SR_CC1IF; GPIOB->BRR = (1<<7); } void DMA1_Channel1_IRQHandler(void) { static int32_t bottom = -1; static int32_t top = -1; DMA1->IFCR = DMA_IFCR_CTCIF1; if (bottom >= 0) { uint32_t amplitude = top - bottom; uint32_t middle = bottom + amplitude / 2; const uint32_t lower_thr = bottom + amplitude / 4; const uint32_t upper_thr = top - amplitude / 4; const uint32_t adc_clockdiv = 125 * 32; //const uint32_t adc_clockdiv = 1; /* FIXME DEBUG */ size_t num_edges = 0; ssize_t edge_indices[24]; int state = 0; ssize_t run_start = -1; int last_v = -1; for (ssize_t i=0; i lower_thr) { state = 1; run_start = i+1; } else if (a > upper_thr && b < upper_thr) { state = -1; run_start = i+1; } } else if (state == 1) { if (b < a) { state = 0; } else if (a < upper_thr && b > upper_thr) { /* run from run_start (incl.) to i (incl.) */ uint32_t v0 = adc_data[run_start]; int d = a - v0; int c = i - run_start; size_t intercept = run_start * adc_clockdiv + (middle - v0) * adc_clockdiv * c / d; if (num_edges < COUNT_OF(edge_indices)) { edge_indices[num_edges] = intercept; num_edges++; } state = 0; } } else if (state == -1) { if (b > a) { state = 0; } else if (a > lower_thr && b < lower_thr) { /* run from run_start (incl.) to i (incl.) */ uint32_t v0 = adc_data[run_start]; int d = a - v0; int c = i - run_start; size_t intercept = run_start * adc_clockdiv + (middle - v0) * adc_clockdiv * c / d; if (num_edges < COUNT_OF(edge_indices)) { edge_indices[num_edges] = intercept; num_edges++; } state = 0; } } } asm volatile ("bkpt"); } const int discard = 5; const int keep = 32; quicksort(adc_data, &adc_data[COUNT_OF(adc_data)-1]); for (size_t i=0; i= t) { break; } uint16_t x = h[0]; h[0] = t[0]; t[0] = x; } uint16_t x = h[0]; h[0] = tail[0]; tail[0] = x; // do the smaller recursive call first, to keep stack within O(log(N)) if (t - head < tail - h - 1) { quicksort(head, t); head = h + 1; } else { quicksort(h + 1, tail); tail = t; } } }