2024-11-20 08:16 AM - edited 2024-11-20 08:36 AM
I am writing an LPTIM driver without HAL or LL drivers on the B-L072Z-LRWAN1 board. I have been getting a number of odd behaviours here that I am hoping someone here can shed some light on.
1) cannot enable continuous start
-bit 2 of the CR register is never set. I added a bunch of __NOP();s in between enabling the timer and setting continuous start with no success. I have also tried setting both bits together with LPTIM->CR = 0b101U; with no success.
2) ISR only clears every now and then.
-using the IRQ handler function, the ISR is cleared the first time, and subsequently only clears about once every 5 times afterwards. the IRQ is being called constantly, when CNT = ARR should be true once a minute (Sysclock = MSI = 131072 Hz, PSC DIV = 128, ARR = 60963).
3) other ISR flags are set
-even though i only set bit 1 (ARRMIE) in the IER register, when the IRQ handler is called, the debugger is telling me that CMPM is also set when entering the IRQ handler.
Here is the relevant code:
extern bool TxTimerDone;
extern uint8_t LPTimRollOver;
void LPTIM_Init(LPTIM_TypeDef *LPTIM, lpTimerMode mode, uint16_t period)
{
uint32_t tmpcfg = 0;
uint16_t tmpier = 0;
//Select clock source as APB clock
RCC->CCIPR &= ~RCC_CCIPR_LPTIM1SEL;
//enable the oscillator for the peripheral
RCC->APB1ENR |= RCC_APB1ENR_LPTIM1EN;
//encoder mode disabled
tmpcfg &= ~LPTIM_CFGR_ENC;
//counter mode disabled
tmpcfg &= ~LPTIM_CFGR_COUNTMODE;
//preload ARR
tmpcfg |= LPTIM_CFGR_PRELOAD;
//set wave polarity.
tmpcfg &= ~LPTIM_CFGR_WAVPOL;
tmpcfg &= ~LPTIM_CFGR_WAVE;
//disable trigger event reset
tmpcfg &= ~LPTIM_CFGR_TIMOUT;
//disable hardware triggers
tmpcfg &= ~LPTIM_CFGR_TRIGEN;
//clear trigger selection
tmpcfg &= ~LPTIM_CFGR_TRIGSEL;
//set prescalar division
tmpcfg |= LPTIM_PRESCALER_DIV128;
//clear trig filter settings
tmpcfg &= ~LPTIM_CFGR_TRGFLT;
//external clock settings; clear.
tmpcfg &= ~LPTIM_CFGR_CKFLT;
//external source clock polarity. not relevant
tmpcfg &= ~LPTIM_CFGR_CKPOL;
//clock selection: internal clock
tmpcfg &= ~LPTIM_CFGR_CKSEL;
//write the configurations to the register
LPTIM->CFGR = tmpcfg;
//set the ARR before enabling interrupts, since an interrupt will be called right away.
//this mitigates the number of false interrupts called.
//enable the timer
LPTIM->CR = LPTIM_CR_ENABLE;
LPTIM->ARR = period;
//CMP flag in ISR is being set when it shouldn't be, so set the CMP register to ARR so it doesn't call the IRQ too often.
LPTIM->CMP = period;
//disable the timer
LPTIM->CR = 0;
//set interrupts to trigger on every ARR match
if(mode == lptim_mode_interrupt)
{
//NVIC_SetVector(LPTIM1_IRQn, (uint32_t)&LPTIM1_IRQ_Handler);
NVIC_SetPriority(LPTIM1_IRQn, 1);
NVIC_EnableIRQ(LPTIM1_IRQn);
//select the interrupt trigger sources
tmpier |= LPTIM_IER_ARRMIE;
//write the configurations to the register
LPTIM->IER = tmpier;
//set the interrupt vector
}
}
void LPTIM_Start(LPTIM_TypeDef *LPTIM, uint16_t period)
{
//enable the timer
LPTIM->CR = LPTIM_CR_ENABLE;
//enable continuous mode
LPTIM->CR |= LPTIM_CR_CNTSTRT;
}
void LPTIM_Stop(LPTIM_TypeDef *LPTIM)
{
//clear control register, disabling the timer.
LPTIM->CR &= ~(0b111U);
}
void LPTIM_Set_ARR(LPTIM_TypeDef *LPTIM, uint16_t period)
{
bool state = true;
//enable the timer if it is not
if((LPTIM->CR & LPTIM_CR_ENABLE) != LPTIM_CR_ENABLE)
{
//enable the timer
LPTIM->CR |= LPTIM_CR_ENABLE;
//toggle a status flag to show that LPTIMER was disabled
//upon entering this function
state = false;
}
//set the auto reload register
LPTIM->ARR = period;
//disable lptimer if it was disabled upon entering the function call.
if(state == false)
LPTIM->CR = 0;
}
//this function is called when the LP timer's CNT register = ARR
//in human terms, when the timer has counted up to its set point.
void LPTIM1_IRQHandler(void)
{
uint16_t timeout = 1000;
//read registers for debugging purposes
uint8_t CRRead = LPTIM1->CR;
uint8_t isrRead = LPTIM1->ISR;
if(LPTIM1->ISR & LPTIM_ISR_ARRM)
{
//clear the interrupt flag register.
LPTIM1->ICR = 127U;//LPTIM1->ISR;
//keep track of how many times (minutes) passed.
LPTimRollOver++;
//if 'mTxDelay' number of minutes have passed,
if(LPTimRollOver >= mTxDelay)
{
//toggle flag to indicate that the set number of minutes have passed
TxTimerDone = true;
LPTimRollOver = 0;
}
//if the timer disabled itself due to not being in CNTSRT mode, enable it again
if((LPTIM1->CR & LPTIM_CR_ENABLE) != LPTIM_CR_ENABLE)
LPTIM_Start(LPTIM1, LPTIMER_PERIOD);
}
//wait for interrupts to be cleared
while((LPTIM1->ISR != 0) && ((timeout) != 0))
timeout--;
//read ISR for debugging
isrRead = LPTIM1->ISR;
}
and then in the main I have
LPTIM_Init(LPTIM1, lptim_mode_interrupt, LPTIMER_PERIOD);
LPTIM_Start(LPTIM1, LPTIMER_PERIOD);
while(1)
{
}