From 322b306bf2d59fbfbcf93784362aac8a695b1ca3 Mon Sep 17 00:00:00 2001
From: jaseg <git@jaseg.net>
Date: Sun, 10 Dec 2017 13:11:30 +0100
Subject: ADC properly triggering now

---
 fw/main.c      | 83 +++++++++++++++++++++++++++++++++++++++++++---------------
 fw/transpose.h |  1 +
 2 files changed, 63 insertions(+), 21 deletions(-)

diff --git a/fw/main.c b/fw/main.c
index 4e4ef36..108d3a1 100644
--- a/fw/main.c
+++ b/fw/main.c
@@ -162,8 +162,11 @@ void cfg_spi1() {
     /* FIXME maybe try w/o BIDI */
 }
 
+/* This is a lookup table mapping segments to present a standard segment order on the UART interface. This is converted
+ * into an internal representation once on startup in main(). The data type must be at least uint16. */
 uint32_t segment_map[8] = {5, 7, 6, 4, 1, 3, 0, 2};
 
+/* The value to be written into the aux register. This encompasses LED state as well as the current setting bits. */
 static volatile uint32_t aux_reg = 0;
 static volatile int frame_duration_us;
 volatile int nbits = MAX_BITS;
@@ -171,17 +174,34 @@ volatile int nbits = MAX_BITS;
 static unsigned int active_bit = 0;
 static int active_segment = 0;
 
-/* Bit timing base value. This is the lowes bit interval used */
+/* Bit timing base value. This is the lowes bit interval used in TIM1/TIM3 timer counts. */
 #define PERIOD_BASE 4
 
 /* This value is a constant offset added to every bit period to allow for the timer IRQ handler to execute. This is set
- * empirically using a debugger and a logic analyzer. */
+ * empirically using a debugger and a logic analyzer.
+ *
+ * This value is in TIM1/TIM3 timer counts. */
 #define TIMER_CYCLES_FOR_SPI_TRANSMISSIONS 9
 
+/* This value sets the point when the LED strobe is asserted after the begin of the current bit cycle and IRQ
+ * processing. This must be less than TIMER_CYCLES_FOR_SPI_TRANSMISSIONS but must be large enough to allow for the SPI
+ * transmission to reliably finish.
+ *
+ * This value is in TIM1/TIM3 timer counts. */
 #define TIMER_CYCLES_BEFORE_LED_STROBE 8
 
-#define AUX_SPI_PRETRIGGER 64
-#define ADC_PRETRIGGER 64
+/* This value sets how long the TIM1 CC IRQ used for AUX register setting etc. is triggered before the end of the
+ * longest cycle. This value should not be larger than PERIOD_BASE<<MIN_BITS to make sure the TIM1 CC IRQ does only
+ * trigger in the longest cycle no matter what nbits is set to.
+ *
+ * This value is in TIM1/TIM3 timer counts. */
+#define AUX_SPI_PRETRIGGER 64 /* trigger with about 24us margin to the end of cycle/next TIM3 IRQ */
+
+/* This value sets how long a batch of ADC conversions used for temperature measurement is started before the end of the
+ * longest cycle. Here too the above caveats apply.
+ *
+ * This value is in TIM1/TIM3 timer counts. */
+#define ADC_PRETRIGGER 150 /* trigger with about 12us margin to TIM1 CC IRQ */
 
 /* Defines for brevity */
 #define A TIMER_CYCLES_FOR_SPI_TRANSMISSIONS
@@ -230,7 +250,6 @@ void cfg_timers_led() {
      *  * Compare unit 1 triggers the interrupt handler only in the longest bit cycle. The IRQ handler
      *    * transmits the data to the auxiliary shift registers and
      *    * swaps the frame buffers if pending
-     *    * kicks off the ADC for (oversampled) temperature measurement
      *  * Compare unit 2 generates the led drivers' STROBE signal
      * 
      * The AUX_STROBE signal for the two auxiliary shift registers that deal with segment selection, current setting and
@@ -261,10 +280,12 @@ void cfg_timers_led() {
     /* Setup CC1 and CC2. CC2 generates the LED drivers' STROBE, CC1 triggers the IRQ handler */
     TIM1->BDTR  = TIM_BDTR_MOE;
     TIM1->CCMR1 = (6<<TIM_CCMR1_OC2M_Pos) | TIM_CCMR1_OC2PE; /* PWM Mode 1, enable CCR preload for AUX_STROBE */
-    TIM1->CCER  = TIM_CCER_CC1E | TIM_CCER_CC2E;
+    TIM1->CCMR2 = (6<<TIM_CCMR2_OC4M_Pos); /* PWM Mode 1 */
+    TIM1->CCER  = TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC4E;
     TIM1->CCR2  = TIMER_CYCLES_BEFORE_LED_STROBE;
     /* Trigger at the end of the longest bit cycle. This means this does not trigger in shorter bit cycles. */
     TIM1->CCR1  = timer_period_lookup[nbits-1] - AUX_SPI_PRETRIGGER;
+    TIM1->CCR4  = timer_period_lookup[nbits-1] - ADC_PRETRIGGER;
     TIM1->DIER  = TIM_DIER_CC1IE;
 
     TIM1->ARR   = 0xffff; /* This is as large as possible since TIM1 is reset by TIM3. */
@@ -284,6 +305,7 @@ void cfg_timers_led() {
 
 void TIM1_CC_IRQHandler() {
     /* This handler takes about 1.5us */
+    GPIOA->BSRR = GPIO_BSRR_BS_0; // Debug
 
     /* Set SPI baudrate to 12.5MBd for slow-ish 74HC(T)595. This is reset again in TIM3's IRQ handler.*/
     SPI1->CR1 |= (2<<SPI_CR1_BR_Pos);
@@ -312,15 +334,16 @@ void TIM1_CC_IRQHandler() {
     GPIOA->BSRR = GPIO_BSRR_BR_10;
     /* Send AUX register data */
     SPI1->DR = aux_reg | segment_map[active_segment];
-    /* Kick off ADC for (oversampled) temperature measurement */
-    ADC1->CR |= ADC_CR_ADSTART;
 
     /* Clear interrupt flag */
     TIM1->SR &= ~TIM_SR_CC1IF_Msk;
+
+    GPIOA->BSRR = GPIO_BSRR_BR_0; // Debug
 }
 
 void TIM3_IRQHandler() {
     /* This handler takes about 2.1us */
+    GPIOA->BSRR = GPIO_BSRR_BS_0; // Debug
 
     /* Reset SPI baudrate to 25MBd for fast MBI5026. Every couple of cycles, TIM1's ISR will set this to a slower value
      * for the slower AUX registers.*/
@@ -344,6 +367,8 @@ void TIM3_IRQHandler() {
 
     /* Clear interrupt flag */
     TIM3->SR &= ~TIM_SR_UIF_Msk;
+
+    GPIOA->BSRR = GPIO_BSRR_BR_0; // Debug
 }
 
 enum Command {
@@ -476,8 +501,6 @@ void USART1_IRQHandler(void) {
     /* COBS skip counter. During payload processing this contains the remaining non-null payload bytes */
     static int cobs_count = 0;
 
-    GPIOA->BSRR = GPIO_BSRR_BS_0; // Debug
-
     if (USART1->ISR & USART_ISR_ORE) { /* Overrun handling */
         overruns++;
         /* Reset and re-synchronize. Retry next frame. */
@@ -516,26 +539,30 @@ void USART1_IRQHandler(void) {
             }
         }
     }
-
-    GPIOA->BSRR = GPIO_BSRR_BR_0; // Debug
 }
 
 #define ADC_OVERSAMPLING 4
 uint32_t vsense;
 void DMA1_Channel1_IRQHandler(void) {
+    /* This interrupt takes either 1.2us or 13us. It can be pre-empted by the more timing-critical UART and LED timer
+     * interrupts. */
     GPIOA->BSRR = GPIO_BSRR_BS_4; // Debug
-    static int count = 0;
-    static uint32_t adc_aggregate[2] = {0, 0};
+    static int count = 0; /* oversampling accumulator sample count */
+    static uint32_t adc_aggregate[2] = {0, 0}; /* oversampling accumulator */
 
+    /* Clear the interrupt flag */
     DMA1->IFCR |= DMA_IFCR_CGIF1;
 
     adc_aggregate[0] += adc_buf[0];
     adc_aggregate[1] += adc_buf[1];
 
     if (count++ == (1<<ADC_OVERSAMPLING)) {
+        /* This has been cobbled together from online tutorials and ST documentation. The datasheet is pretty poor on
+         * this. */
         adc_vcc_mv = (3300 * VREFINT_CAL)/(adc_aggregate[0]>>ADC_OVERSAMPLING);
         vsense = ((adc_aggregate[1]>>ADC_OVERSAMPLING) * adc_vcc_mv)/4095 ;
         adc_temp_tenth_celsius = 300 - (((TS_CAL1*adc_vcc_mv/4095) - vsense)*100)/43;
+        /* Reset oversampling state */
         count = 0;
         adc_aggregate[0] = 0;
         adc_aggregate[1] = 0;
@@ -544,29 +571,43 @@ void DMA1_Channel1_IRQHandler(void) {
 }
 
 void adc_config(void) {
-    ADC1->CFGR1 = ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG;
-    ADC1->CFGR2 = (1<<ADC_CFGR2_CKMODE_Pos);
-    ADC1->SMPR = (7<<ADC_SMPR_SMP_Pos);
+    /* The ADC is used for temperature measurement. To compute the temperature from an ADC reading of the internal
+     * temperature sensor, the supply voltage must also be measured. Thus we are using two channels.
+     *
+     * The ADC is triggered by compare channel 4 of timer 1. The trigger is set to falling edge to trigger on compare
+     * match, not overflow.
+     */
+    ADC1->CFGR1 = ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | (2<<ADC_CFGR1_EXTEN_Pos) | (1<<ADC_CFGR1_EXTSEL_Pos);
+    /* Clock from PCLK/4 instead of the internal exclusive high-speed RC oscillator. */
+    ADC1->CFGR2 = (2<<ADC_CFGR2_CKMODE_Pos);
+    /* Use the slowest available sample rate */
+    ADC1->SMPR  = (7<<ADC_SMPR_SMP_Pos);
+    /* Internal VCC and temperature sensor channels */
     ADC1->CHSELR = ADC_CHSELR_CHSEL16 | ADC_CHSELR_CHSEL17;
+    /* Enable internal voltage reference and temperature sensor */
     ADC->CCR = ADC_CCR_TSEN | ADC_CCR_VREFEN;
+    /* Perform ADC calibration */
     ADC1->CR |= ADC_CR_ADCAL;
     while (ADC1->CR & ADC_CR_ADCAL)
         ;
+    /* Enable ADC */
     ADC1->CR |= ADC_CR_ADEN;
-    /* FIXME handle adc overrun */
+    ADC1->CR |= ADC_CR_ADSTART;
 
+    /* Configure DMA 1 Channel 1 to get rid of all the data */
     DMA1_Channel1->CPAR = (unsigned int)&ADC1->DR;
     DMA1_Channel1->CMAR = (unsigned int)&adc_buf;
     DMA1_Channel1->CNDTR = sizeof(adc_buf)/sizeof(adc_buf[0]);
     DMA1_Channel1->CCR = (0<<DMA_CCR_PL_Pos);
     DMA1_Channel1->CCR |=
-          DMA_CCR_CIRC
+          DMA_CCR_CIRC /* circular mode so we can leave it running indefinitely */
         | (1<<DMA_CCR_MSIZE_Pos) /* 16 bit */
         | (1<<DMA_CCR_PSIZE_Pos) /* 16 bit */
         | DMA_CCR_MINC
-        | DMA_CCR_TCIE;
-    DMA1_Channel1->CCR |= DMA_CCR_EN;
+        | DMA_CCR_TCIE; /* Enable transfer complete interrupt. */
+    DMA1_Channel1->CCR |= DMA_CCR_EN; /* Enable channel */
 
+    /* triggered on transfer completion. We use this to process the ADC data */
     NVIC_EnableIRQ(DMA1_Channel1_IRQn);
     NVIC_SetPriority(DMA1_Channel1_IRQn, 3);
 }
diff --git a/fw/transpose.h b/fw/transpose.h
index b1ca41b..389071c 100644
--- a/fw/transpose.h
+++ b/fw/transpose.h
@@ -8,6 +8,7 @@ enum {
     NROWS = 4,
     NCOLS = 8,
     MAX_BITS = 10,
+    MIN_BITS = 6,
 };
 
 enum {
-- 
cgit