Hi Jeff,
Please find the code below. It is the complete software (.ino file) as is. I think the comments are pretty self-explanatory.
I used the STM32 library functions which are used by the Photon formware as well.
You can find a library description at http://www.han-ese.nl/STM32/stm32f2stdlibrary/html/index.html
and the STM32 documents UM1061Description of STM32F2xx Standard Peripheral Library, RM0033 Reference manual, AN4013 Application note, and numerous others come in very handy. Just google for STM32F2X and timer, adc and so on
I had some lines removed, due to max size of posts…
Have fun!
Ray
// ======================================================================
#include "Particle.h"
#define __NO_DEBUG_IRQ5
#define __NO_DEBUG_IRQ3
#define __NO_DEBUG_IRQ4
#define __NO_DEBUG_IRQ_ADC
// Definition of pins
#define ADR0 D4
#define ADR1 D5
#define ADR2 D6
#define ADR3 D7
// Clock frequency of the timers, needed for timing calculations
#define TIMCLK 30000000UL
#define NROFAISAMPLES 60
#define BASECYCLE 1.25E-3
#define HALF40KHZPERIOD 12.50E-6
#define NROFHALFPULSES 16
#define ENDPEAKDETECTPERIOD 1.10E-3
#define ENABLERECEIVER 0.30E-3
// Zero Crossing to peak is one quarter of the 40kHz period time = 6.25 us
// For some reason, this value can not be very small: the STM locks
// too short time between interrupts?
// When using IRQ for debugging, this value must be 10 times bigger
#define ZCD2PEAKDELAY 6.25E-6
// Pulse width of ADC Start conversion. Not critical, so 1 us
#define ADCSTARTPULSEWIDTH 1E-6
// Constant to reduce TIM5 ISR processing time
const uint16_t g_Half40kHzCnt = HALF40KHZPERIOD * TIMCLK;
const uint16_t g_End40kHzCnt = 10 * g_Half40kHzCnt;
const uint16_t g_EndPDPeriod = ENDPEAKDETECTPERIOD * TIMCLK;
const uint16_t g_EnableReceiver = ENABLERECEIVER * TIMCLK;
const uint16_t g_TIM4_Period = (ZCD2PEAKDELAY + ADCSTARTPULSEWIDTH) * TIMCLK;
const uint16_t g_TIM4_Pulse = ZCD2PEAKDELAY * TIMCLK;
// Temporary: for testing only
uint16_t g_TIM3CNT = 0;
uint g_NewTIM3CNT = 0;
uint16_t g_TIM4CNT = 0;
uint g_NewTIM4CNT = 0;
uint16_t g_TIM8CNT = 0;
uint g_NewTIM8CNT = 0;
uint g_ADCValue = 0;
uint16_t g_ADCCNT = 0;
uint g_ADC = 0;
// ======================================================================
void setup() {
for (int l_Cnt = 0; l_Cnt<NROFAISAMPLES; l_Cnt++)
{
g_CCZeroDValue[l_Cnt] = 2;
g_RawPeakValue[l_Cnt] = 1;
}
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
ADC_CommonInitTypeDef ADC_CommonInitStruct;
ADC_InitTypeDef ADC_InitStruct;
// ---------------------------------------------
// TIM_TimeBaseInit writes registers:
// - TIMx->CR1 : DIR,CMS,CounterMode
// - TIMx->ARR : Period
// - TIMx->PSC : Prescaler
// - TIMx->EGR : PreloadMode Immediate
// TIM_ICInit writes registers:
// - TIMx->CCMR1 : CC1S,IC1F,ICSelection,ICFIlter
// - TIMx->CCER : CC1P,CC1NP,ICPolarity,CC1E
// TIM_OC1Init writes registers:
// - TIMx->CCMR1 : OCMode,
// - TIMx->CCR1 : Pulse
// - TIMx->CCER : CC1E, OCPolarity,OutputState
// TIM_OC1PreloadConfig writes registers:
// - TIMx->CCMR1 : OC1PE,OCPreload
// TIM_ARRPreloadConfig writes registers:
// - TIMx->CR1 : ARPE
// TIM_SelectOnePulseMode writes registers:
// - TIMx->CR1 : OPMode
// TIM_GenerateEvent writes registers:
// - TIMx->EGR : (argument)
// TIM_SelectOutputTrigger writes registers:
// - TIMx->CR2 : MMS
// TIM_SelectMasterSlaveMode writes registers:
// - TIMx->SMCR : MSM
// TIM_SelectInputTrigger writes registers:
// - TIMx->SMCR : TS
// TIM_SelectSlaveMode writes registers:
// - TIMx->SMCR : SMS
// ---------------------------------------------
// ADC_CommonInit writes registers:
// - ADC->CCR : MULTI, DELAY, DMA, ADCPRE
// ADC_Init writes registers:
// - ADC->CR1 : RES, SCAN
// - ADC->CR2 : CONT, ALIGN, EXTEN, EXTSEL
// - ADC->SQR1 : L
// ADC_RegularChannelConfig writes registers:
// - ADC->SMPR1/2 :
// - ADC->SQR1/2/3 :
// ADC_DMARequestAfterLastTransferCmd writes registers:
// - ADC->CR2 : ADC_CR2_DDS
// ADC_DMACmd writes registers:
// - ADC->CR2 : ADC_CR2_DMA
// ADC_ClearITPendingBit writes registers:
// - ADC->SR : IT mask
// ADC_ITConfig writes registers:
// - ADC->CR1 : IT mask
// ADC_Cmd writes registers:
// - ADC->CR2 : ADON
// Timer 5: - pulse train of 40 kHz
// - cyclic trigger 10 ms
// Timer 3: - input capture for zero detector
// Timer 4: - delay between input capture and ADC conversion
// ADC1: - sample analog peak of signal
// DMA1: - input capture counter value to memory
// - ADC conversion result to memory
// Setup of timers according:
// UM1601 Description of STM32F2xx Standard Peripheral Library
// - Input capture, section 25.2.3, page 525
// Enable periherals
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
// Set pins IO mode
HAL_Pin_Mode(ADR0,OUTPUT);
HAL_Pin_Mode(ADR1,OUTPUT);
HAL_Pin_Mode(ADR2,OUTPUT);
HAL_Pin_Mode(ADR3,OUTPUT);
//Assign pins to their alternate functions
// Pin WKP, TIM5_Ch1: 40 kHz pulse
HAL_Pin_Mode(WKP,AF_OUTPUT_PUSHPULL);
// Alternate Function AF2 on pin PA0-WKUP
GPIO_PinAFConfig(PIN_MAP[WKP].gpio_peripheral,PIN_MAP[WKP].gpio_pin_source,GPIO_AF_TIM5);
// Pin D3, TIM3_Ch1: Zero Cross Detector
HAL_Pin_Mode(D3,INPUT);
// Set the input pin D3 to GPIO_Mode_AF
// D3 (B4) PINMAP: GPIOB, GPIO_Pin_4, GPIO_PinSource4, NONE, NONE, TIM3, TIM_Channel_1, PIN_MODE_NONE, 0, 0 },
GPIO_InitStructure.GPIO_Pin = PIN_MAP[D3].gpio_pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// Alternate Function AF on pin PB4-D3
GPIO_PinAFConfig(PIN_MAP[D3].gpio_peripheral,PIN_MAP[D3].gpio_pin_source,GPIO_AF_TIM3);
// Pin D2, TIM3_Ch2: Breather LED
HAL_Pin_Mode(D2,AF_OUTPUT_PUSHPULL);
// Set the input pin D2 to GPIO_Mode_AF
// D2 (B5) PINMAP: GPIOB, GPIO_Pin_5, GPIO_PinSource5, NONE, NONE, TIM3, TIM_Channel_2, PIN_MODE_NONE, 0, 0 },
// GPIO_InitStructure.GPIO_Pin = PIN_MAP[D2].gpio_pin;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
// GPIO_Init(GPIOB, &GPIO_InitStructure);
// Alternate Function AF on pin PB4-D3
GPIO_PinAFConfig(PIN_MAP[D2].gpio_peripheral,PIN_MAP[D2].gpio_pin_source,GPIO_AF_TIM3);
// Pin A0, ADC1: Peak signal
HAL_Pin_Mode(A0,AN_INPUT);
// A0 (C5) PINMAP: GPIOC, GPIO_Pin_5, GPIO_PinSource5, ADC_Channel_15, NONE, NULL, NONE, PIN_MODE_NONE, 0, 0 },
// ======================================================================
// == Configuration of DMA 1, channel 0 (connected to the TIM3.CC1) ==
// ======================================================================
// - used for transfering Capture Counter of TIM3 to a buffer in memory
// - each zero detect leads to TIM3.CC1
// - on this event, the DMA transfers the contents of the dataregister TIM3.CC1
// - to the g_CCZeroDValue buffer in memory, incrementing the pointer
// - no burst (=> single)
// - no FIFO, since data (half word, 16 bits) is generated every 25 us only
// - enable of TIM3.CC1 after 40 kHz pulses are generated (trigger by TIM5.CC2)
// - disable after a period of i.e. 1 ms, pulse are dimished by then (trigger by TIM5.CC3)
DMA_InitStruct.DMA_Channel = DMA_Channel_5;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStruct.DMA_PeripheralBaseAddr = 0x40000434; // TIM3->CCR1; // 0x40000434; // TIM3 CCR1 address
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)g_CCZeroDValue;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStruct.DMA_BufferSize = NROFAISAMPLES;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_Init(DMA1_Stream4, &DMA_InitStruct);
// ======================================================================
// == Configuration of DMA 2, stream 0, channel 0 (connected to ADC) ==
// ======================================================================
// - used for transfering ADC conversion data to a buffer in memory
// - each peak is sampled by the ADC
// - once EoC, the DMA transfers the contents of the dataregister ADC->DR
// - to the g_RawPeakValue buffer in memory, incrementing the pointer
// - no burst (=> single)
// - no FIFO, since data (half word, 16 bits) is generated every 25 us only
// - enable of ADC/DMA after 40 kHz pulses are generated (trigger by TIM5.CC2)
// - disable after a period of i.e. 1 ms, pulse are dimished by then (trigger by TIM5.CC3)
DMA_InitStruct.DMA_Channel = DMA_Channel_0;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStruct.DMA_PeripheralBaseAddr = 0x4001204C; // ADC1->DR; // 0x4001204C; // DR ADDRESS; ADC1
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)g_RawPeakValue;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStruct.DMA_BufferSize = NROFAISAMPLES;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_Init(DMA2_Stream0, &DMA_InitStruct);
// DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
// Do not enable DMA now, but in ISR
// DMA_Cmd(DMA2_Stream0, ENABLE);
// ======================================================================
// == Configuration of Timer 5 ==
// ======================================================================
// - used for generating 40kHz pulse and cyclic timing of measurement
// and calculations
// - resets the counter of TIM3 at an exact moment in the timing
// - IRQ to handle:
// - 40 kHz pulse generation
// - wait for measurements
// - start calculations
// - handle overall measurement sequence and calculation
// - Timer in Output Compare mode, with a period (eg 1 ms) a multitude of
// one 40kHz period.
// - OC1 toggles every 12.5 us and is reprogrammed for the next period
// After a fixed number of times (eg 8 pulses), OC1 is reprogrammed for
// the next period of 1 ms
// - OC2 is programmed to signal TIM3 to reset the counter of the input
// capture
// - OC3 can be used to interrupt and signal a calculation task after the
// measuring period. It can also be used to disable the input capturing
// - OC4 can be used to pulse a external pin, as to trigger a scope.
// - First enable interrupt for TIM5
NVIC_InitStruct.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
// - max counter resolution, clock = 30 Mhz
// - 30000000UL = 30 MHz Timer Clock = HCLK / 4
// - counting up, measure time from zero count
// - Period length 1.25 ms: 1.25E-3 * 30000000 = 37500
// - OC1 every 12.5 us interrupt: 1.25E-5 * 30000000 = 375
// 16 times an interrupt at n * 375 an interrupt delivers 8 pulses
// - OC2 signals TIM3 at the end of the OC1 pulse train.
TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
TIM_TimeBaseStruct.TIM_Period = (BASECYCLE * TIMCLK);
TIM_TimeBaseStruct.TIM_Prescaler = 1;
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStruct);
// - OC1: Output Compare mode with toggle when creating a pulse.
// - The first interrupt takes place at half the 40 kHz pulse
// - Polarity low: pulses start high and go low, this enables use of 74ls139 (not)G input
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = NextOC1Capture(g_HalfPulseCnt);
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM5, &TIM_OCInitStruct);
// - OC2: Output Compare mode with PWM2 (first low, then high when CC2 is reached).
// - Force OC output low, also to be done at the start of each 1.2 ms cycle
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = g_End40kHzCnt;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM5, &TIM_OCInitStruct);
// - OC3: Output Compare mode with PWM2 (first low, then high when CC3 is reached).
// - Force OC output low, also to be done at the start of each 1.2 ms cycle
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = g_EndPDPeriod;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM5, &TIM_OCInitStruct);
// - OC4: Output Compare mode with PWM2 (first low, then high when CC3 is reached).
// - Force OC output low, also to be done at the start of each 1.2 ms cycle
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = g_EnableReceiver;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC4Init(TIM5, &TIM_OCInitStruct);
// - Take over new capture value immediately (it is written in the interrupt handler)
TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Disable);
// - Output TRGO signal to next timer when a capture takes place
// - OC2 (end of 40kHz pulses) triggers TRGO
// - enable master/slave operation between timer and slave timer
TIM_SelectOutputTrigger(TIM5, TIM_TRGOSource_OC2Ref);
TIM_SelectMasterSlaveMode(TIM5, TIM_MasterSlaveMode_Enable);
// - clear and release Update and CC1 interrupts
TIM_ClearITPendingBit(TIM5,TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4);
TIM_ITConfig(TIM5,TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4,ENABLE);
// TIM enable counter
TIM_Cmd(TIM5, ENABLE);
// ======================================================================
// == Configuration of Timer 3 ==
// ======================================================================
// - input capture on Zero Cross Detector input TIM_Ch1, pin D3
// - max counter resolution, clock = 30 Mhz
// - 30000000UL = 30 MHz Timer Clock = HCLK / 4
// With maximum time of 1.25 ms, the 16-bit counter will reach 37500
// Timer period is set to maximum
// - counting up, measure time from zero count
TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
TIM_TimeBaseStruct.TIM_Period = 65535;
TIM_TimeBaseStruct.TIM_Prescaler = 1;
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);
// - capture rising edge on TIM3_Ch1, pin D3
// - no input capture filters or prescalers
// - direct connection: Channel1 to TI1
TIM_ICStructInit(&TIM_ICInitStruct);
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICFilter = 0x0;
TIM_ICInit(TIM3, &TIM_ICInitStruct);
// - OC2: Output Compare mode with PWM2 (first low, then high when CC2 is reached).
// - Force OC output low, also to be done at the start of each 1.2 ms cycle
// - max count = 37500 - TIM5->TIM3 delay.
// - At 1.25 ms cycle start, output = low
// - Only after TIM3 start, setting high is possible.
// - CC2 value ranges from 0 (max LED) to 37500-TIM5->TIM3 delay (LED off)
// - Initially LED is set off
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 37500;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStruct);
// - Take over new capture value immediately (it is written in the interrupt handler)
// TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
// - counter enabled when gated from TIM5 (OC2Ref)
// - (counter is reset at start of TIM5 1.25 ms period by TIM5 ISR)
// - counter is running when TIM5_OC2 reaches its CC value,
// so TIM3 capture counter is always referenced to TIM5 1.25 ms period
TIM_SelectInputTrigger(TIM3, TIM_TS_ITR2);
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated);
// - Output TRGO signal to next timer when a capture takes place
// - enable master/slave operation between timer and slave timer
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_OC1);
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);
// - Enable DMA trigger on CC1
TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);
#ifdef __DEBUG_IRQ3
// - clear and release CC1 interrupts
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);
TIM_ITConfig(TIM3,TIM_IT_CC1,ENABLE);
#endif
// TIM enable counter
TIM_Cmd(TIM3, ENABLE);
// ======================================================================
// == Configuration of Timer 4 ==
// ======================================================================
// - to be triggered by an rising edge on TIM3 TRGO output
// - after 6.25 us, TIM4 OC4Ref must be set
// - 1us later, the OC4Ref output can drop
// - input capture on Zero Cross Detector input TIM_Ch1, pin D3
// - max counter resolution, clock = 30 Mhz
// - 30000000UL = 30 MHz Timer Clock = HCLK / 4
// - counting up, measure time from zero count
// - Pulse length: CCR register (TIM_OCInitStructure.TIM_Pulse): 6.25E-6 * 30000000 = 187
// - Pulse+Delay length: The ARR register (TIM_TimeBaseStructure.TIM_Period): 7.25E-6 * 30000000 = 217
TIM_TimeBaseStruct.TIM_Period = g_TIM4_Period;
TIM_TimeBaseStruct.TIM_Prescaler = 1;
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStruct);
// - One Pulse is a special case of PWM mode
// - Set CC4 output enable (CC4E), to trigger ADC
// - Set CC4 output polarity active high (CC4P), to trigger ADC
// - PWM2: first low, then high
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = g_TIM4_Pulse;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC4Init(TIM4, &TIM_OCInitStruct);
// - One single pulse when triggered
TIM_SelectOnePulseMode(TIM4, TIM_OPMode_Single);
// - counter starts when triggered from TIM3 TRGO
TIM_SelectInputTrigger(TIM4, TIM_TS_ITR2);
TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Trigger);
#ifdef __DEBUG_IRQ4
// clear and release CC4 interrupt
NVIC_InitStruct.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_ClearITPendingBit(TIM4,TIM_IT_CC4);
TIM_ITConfig(TIM4,TIM_IT_CC4,ENABLE);
#endif
// TIM enable counter
TIM_Cmd(TIM4, ENABLE);
// ======================================================================
// == Configuration of ADC ==
// ======================================================================
// - to be triggered by a rising edge of TIM4_Ch4 output
// - sample A0 analog input
// - at EOC, DMA stores the result in memory
// - Single ADC mode
// - Sample at 30MHz clock (prescaler = 2)
// - For now, no DMA
// - No delay between ADC1 and ADC2 required
ADC_DeInit();
ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStruct);
// - 12 bits resolution (=> 15 clock cycles = 0.5 us)
// - Single (one channel) scan mode
// - Single scan, not continuous
// - Trigger converion on a rising edge
// - Trigger from TIM4_CC4
// - Right aligned data, no scaling
// - 1 conversion
ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStruct.ADC_ScanConvMode = ENABLE;
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStruct);
// - Setup A0 input channel as single channel, with highest speed sampling
// PIN_MAP[A0] - 10 { GPIOC, GPIO_Pin_5, GPIO_PinSource5, ADC_Channel_15, NONE, NULL, NONE, PIN_MODE_NONE, 0, 0 },
ADC_RegularChannelConfig(ADC1, PIN_MAP[A0].adc_channel, 1, ADC_SampleTime_15Cycles);
#ifdef __DEBUG_IRQ_ADC
// clear and release EoC interrupt
NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);
ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);
#else
// Enable DMA
// only possible when no interrupts are generated
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
#endif
// Do not enable ADC now, but in the TIM5.CC2 interrupt routine
// ADC_Cmd(ADC1, ENABLE);
pinMode(D7, OUTPUT);
attachSystemInterrupt(SysInterrupt_TIM5_IRQ, TIM5_IRQHandler);
#ifdef __DEBUG_IRQ3
attachSystemInterrupt(SysInterrupt_TIM3_IRQ, TIM3_IRQHandler);
#endif
#ifdef __DEBUG_IRQ4
attachSystemInterrupt(SysInterrupt_TIM4_IRQ, TIM4_IRQHandler);
#endif
#ifdef __DEBUG_IRQ_ADC
attachSystemInterrupt(SysInterrupt_ADC_IRQ, ADC_IRQHandler);
#endif
// Debugging
Serial.begin(9600); // Open serial over USB.
delay(5000);
Serial.println("Spark Core or Photon says Hello over USB! ");
}
// ======================================================================
void TIM5_IRQHandler(void)
{
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)
{
#ifdef __DEBUG_IRQ5
g_CCR[g_HalfPulseCnt] = TIM3->CNT;
g_BaseCycleSema++;
#endif
// This fires every 12.5 us (HALF40KHZPERIOD define), but only when OC1 is captured
// Set new capture value
g_HalfPulseCnt++;
TIM_SetCompare1(TIM5,NextOC1Capture(g_HalfPulseCnt));
// Clear interrupt
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1);
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM5, TIM_IT_CC2);
// Start of Pulse Detect Period, reset and then enable DMA
DMA_ClearFlag(DMA1_Stream4, DMA_IT_TCIF4);
DMA_ClearFlag(DMA2_Stream0, DMA_IT_TCIF0);
DMA_Cmd(DMA1_Stream4,ENABLE);
DMA_Cmd(DMA2_Stream0,ENABLE);
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC3) != RESET)
{
#ifdef __DEBUG_IRQ5
g_CC3 = TIM3->CNT;
g_CC3Sema++;
#endif
TIM_ClearITPendingBit(TIM5, TIM_IT_CC3);
// End of the Pulse Detect Period, disable DMA
DMA_Cmd(DMA1_Stream4,DISABLE);
DMA_Cmd(DMA2_Stream0,DISABLE);
DMA_ClearFlag(DMA1_Stream4, DMA_IT_TCIF4);
DMA_ClearFlag(DMA2_Stream0, DMA_IT_TCIF0);
// Now process raw captured data
ProcessRawCapturedData();
// and prepare new measurement run
PrepareMeasurement();
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC4) != RESET)
{
TIM_ClearITPendingBit(TIM5, TIM_IT_CC4);
// Enable receiver
digitalWriteFast(ADR2,g_RxChannel[g_ScanChannel] & 0x01);
digitalWriteFast(ADR3,g_RxChannel[g_ScanChannel] & 0x02);
}
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
#ifdef __DEBUG_IRQ5
g_CCR[0] = TIM3->CNT;
#endif
// This fires every 1.25 ms (BASECYCLE define)
// Set new capture value
g_HalfPulseCnt=1;
TIM_SetCompare1(TIM5,NextOC1Capture(g_HalfPulseCnt));
// - Set TIM3 counter to zero
TIM_SetCounter(TIM3,0);
// Clear interrupt
TIM_ClearITPendingBit(TIM5, TIM_IT_Update);
ProcessSystemTick();
g_BaseCycleFlag=true;
}
}
// ======================================================================
#ifdef __DEBUG_IRQ3
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
// Captured!, read value
g_TIM3CNT = TIM_GetCapture1(TIM3);
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
g_NewTIM3CNT++;
}
}
#endif
// ======================================================================
#ifdef __DEBUG_IRQ4
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_CC4) != RESET)
{
// Captured!, read value
g_TIM4CNT = TIM3->CNT;
TIM_ClearITPendingBit(TIM4, TIM_IT_CC4);
g_NewTIM4CNT++;
}
}
#endif
// ======================================================================
#ifdef __DEBUG_IRQ_ADC
void ADC_IRQHandler(void)
{
if (ADC_GetITStatus(ADC1, ADC_IT_EOC) != RESET)
{
// EoC!, read value
g_ADCCNT = TIM3->CNT;
g_ADCArr[g_ADCArrCnt] = g_ADCCNT;
if (g_ADCArrCnt<14)
{
g_ADCArrCnt++;
}
// g_ADCValue = ADC_GetConversionValue(ADC1);
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
g_ADC++;
}
}
#endif
// ======================================================================