I little while back I started a thread about how to measure frequencies up to 30MHz with 1kHz resolution using a STM32F401 Nucleo board.
After a lot of experimentation I was successful, but have now run into another problem.
I suspect by default I'm using the internal 16MHz RC oscillator which is marginal in terms of stability.
A better reference would be to use the 8MHz xtal drived MCO signal coming from the ST link section.
Below is my code where I've tried to select an external 16MHz xtal, but despite the xtal running ok it always uses the internal 16MHz RC osc.
The code itself works fine - just has an issue with reference selection. Input signal is applied to PD2.
Apologies in advance for my primitive coding style and not using register defines etc.
There's a reason why I'm using only a 16 bit counter - hardware limitations elsewhere.
Here's hoping the code displays ok and makes sense !
Regards Tim
Code: Select all
void setup() {
Serial2.begin (115200); // Initialise the serial interface
Serial.println("start") ;
//RCC->CR &= ~(1<<0); // turn off hsi
RCC->CR |= 1<<16; // start xtal osc and wait for it to settle
while (!(RCC->CR & (1<<17)));
RCC->APB1ENR |= 1<<28; // turn on power clock
PWR->CR |= 3<<14; // power mode 2
FLASH->ACR = (1<<8) | (1<<9)| (1<<10)| (5<<0);
// set up prescalers
RCC->CFGR &= ~(1<<4); // bits 7:4 all 0s
RCC->CFGR |= (4<<10); // bits 12:10 100 or 4 into 10th position means /2
RCC->CFGR &= ~(4<<13); // set bit 14 to zero for no divide
// configure main PLL
#define PLL_M 16
#define PLL_N 336
#define PLL_P 4
RCC->PLLCFGR = (PLL_M <<0) | (PLL_N << 6) | (PLL_P <<16) | (1<<22);
// enable PLL and wait for it to become ready
RCC->CR |= (1<<24); // ok
while (!(RCC->CR & (1<<25))); // ok
// select PLL clock source and wait for it to be set
RCC->CFGR |= (2<<0);
//RCC->CFGR |= (3<<0); // not allowed
while (!(RCC->CFGR & (2<<2)));
setup_freq_timers() ;
}
void loop() {
Serial.println(measure_freq());
delay(300) ;
}
void setup_freq_timers (void) // generates timebase signal signal on CH1
{
// set up Timer 2 for gating signal
// Enable clock access to tim2
RCC->APB1ENR |= (1U<<0);
// Enable clock access to GPIOA for time gate debug
RCC->AHB1ENR |= (1U<<0) ;
//Set prescaler value
TIM2->PSC = 84 - 1 ; // 84MHz clock done to 1MHz
//Set auto-reload value
TIM2->ARR = 8000 - 1; // 8ms Gate
//Set output compare toggle mode
TIM2->CCMR1 = ((1U<<4) | (1U<<5));
//Enable tim2 ch1 in compare mode
TIM2->CCER |= (1U<<0);
// MMS=010 bit 5 evnt on TRG01 on each event
TIM2->CR2 |= (1U<<5 ) ;
//Clear counter
TIM2->CNT = 0;
///////////////////////////////////////////
//set up timer 2 PD2 input
//////////////////////////////////////////
//Enable clock access to GPIOD
RCC->AHB1ENR |= (1U<<3);
//Set PD2 mode into alternate function mode
GPIOD->MODER &=~(1U<<0);
GPIOD->MODER |=(1U<<5);
//Set PD2 alternate function type to TIM3_CH3 (AF02)
GPIOD->AFR[0]|= (1U<<9) ; // 0b0010 = AF2
//Enable clock access to tim3
RCC->APB1ENR |= (1U<<1);
/*Set Prescaler*/
TIM3->PSC = 0 ;
//Set CH3 to input capture from ITR1 via TRC
TIM3->CCMR2 |= (1U<<0) | (1U<<1 ) ; // mapped to TRC to trigger, needs TS bit in SMCR
TIM3->SMCR |= (1U<<4) ; // slecting ITR1 in TS bit 4
// set slave mode reset
TIM3->SMCR |= (1U<<2) ; // set sms reset mode
TIM3->ARR = 0xffff ;
TIM3->CCER = 0x0100 ; // cap compare 3 output enable
TIM3->SMCR |= TIM_SMCR_ECE; // switch to ETR2 input on PD2
TIM3->SMCR |= TIM_SMCR_ETPS; // divide by 8
}
int measure_freq () // returns int of freq in kHz
{
int i = 0 ;
int freq_temp = 0 ;
TIM3->CCR3 = 0 ;
TIM3->CR1 = (1U<<0); // start tim3
TIM2->CR1 = (1U<<0); // start tim2
while(!(TIM3->SR & (1U<<3))){} // dummy read to clear junk ch3 capture compare irq
freq_temp = (TIM3->CCR3);
freq_temp = 0 ;
i = 0 ;
while (i <=7) // average over 4 readings // was 3
{
while(!(TIM3->SR & (1U<<3))){} // (1U<<3) ch3 capture compare irq
freq_temp = freq_temp + (TIM3->CCR3);
i++ ;
}
TIM3->CR1 = 0 ; // stop timers
TIM2->CR1 = 0 ;
return freq_temp / 8 ;
}