LPTMR interrupt is not generated by KL03

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPTMR interrupt is not generated by KL03

1,355 Views
andymurray
Contributor II

Hi,

I am using KL03 and KDS. At first I used PE to configure the LPTMR. When the interrupt was generated by the timer, I read the ADC and sent the information through the LPUART. Everything worked fine.


I am trying to do the same algorithm without PE (for many reasons, including errors found in the generated code for VLPR and power up time), and I have something missing in the LPTMR configuration that doesn't take me to the interrupt to read my ADC. For debugging purposes, currently the ADC acquisition instructions are in the main loop, and I have a PTOR instruction for PORTB10 to be able to know if I got to the LPTMR0_IRQHandler (visualization with LED and oscilloscope).

The code that follows (main.c) shows the LPTMR configuration, including CSR, PSR, the priority of the interrupt (NVIC) and the interrupt enable (NVIC). After initilization, I enable global interrupts, set the value for the timer, and enable the timer. I am constantly looking at the value in CNR (which increments as expected), but doesn't take me to the LPTMR0_IRQHandler.

Any ideas of what am I missing in the configuration? I really appreciate any help you can provide. It most be something very silly but I have been stucked in this for days... 

Best,
Andy

//  Includes

#include "MKL03Z4.h"                         // KL03 M0+ register definitions

 

//Power modes

unsigned int power_mode_status = 0;

unsigned int dummyread;

//ADC, LPUART and LPTMR

unsigned int result_mask = 0;

uint32_t result_ADC;

uint32_t status_LPUART = 0;

uint32_t status_LPTMR = 0;

 

void MCU_Init(void);                               // initializes MCU

 

 

void MCU_Init(void)

{

                __asm__("CPSID i");                           // Disable interrupts

 

                // System Registers

                SIM->COPC=0x00;                // Disable COP watchdog; RefMan2017 Pag. 173 and 196

                SIM->SCGC5|=(SIM_SCGC5_LPUART0_MASK | SIM_SCGC5_PORTB_MASK | SIM_SCGC5_LPTMR_MASK);  // Enable clock gate for LPUART, PORTB, LPTMR registers. RefMan2017 Pag. 188

                SIM_SCGC6 |= SIM_SCGC6_ADC0_MASK; // Enable ADC0 clock gate control. RefMan2017 Pag. 189

                SIM_SOPT2 |= SIM_SOPT2_LPUART0SRC_MASK; // Set MCGIRCLK clock as LPUART0 transmit and receive clock. RefMan2017 Pag. 179

 

                // Errata 8068 fix: https://community.nxp.com/thread/350259 and https://community.nxp.com/message/902536

                SIM->SCGC6 |= SIM_SCGC6_RTC_MASK;           // enable clock to RTC

                RTC->SR = 0b00001; // Time counter disabled - RTC status register

                RTC->TSR = 0x00;     // dummy write to RTC TSR per errata 8068

                SIM->SCGC6 &= ~SIM_SCGC6_RTC_MASK;          // disable clock to RTC

 

                // System Tick Init

                SysTick->CTRL=0;        // Disable the SysTick Timer

                SysTick->LOAD=48000;  // Core Clock/1=48MHz, Period=1ms/(1/48000000Hz)=48000

                SysTick->VAL=0;  // Clear the current counter value to 0

                SysTick->CTRL=(SysTick_CTRL_ENABLE_Msk

                                                                |SysTick_CTRL_CLKSOURCE_Msk);   // Enable SYS TICK timer and set clock source to processor clock (core clock)

 

                // System clock initialization MCG_Lite

                MCG_C1 = 0b01000010U; // LIRC clock is main clock source, LIRC enabled. RefMan2017 Pag. 317

                MCG_C2 = 0b00000000U; // LIRC is in 2 MHz mode. RefMan Pag. 318

 

                //System integration module. RefMan2017 Pag 191

                SIM_CLKDIV1 &=~ (SIM_CLKDIV1_OUTDIV1(1));      // Set Core Clock/1= 2 MHz

                SIM_CLKDIV1 |= (SIM_CLKDIV1_OUTDIV4(2));       // Set Bus Clock/3= 0.666 MHz

 

                // System mode controller SMC - RefMan2017 Pag. 199

                SMC_PMPROT=SMC_PMPROT_AVLP_MASK; // Write-once register: Allow Low Leakage Power Modes (VLPR,VLPW,VLPS,LLS); Pag. 202 RefMan2017

                // Goes to VLPR before VLPW - required

                power_mode_status  = SMC_PMSTAT;                //Pag. 206 RefMan2017

                SMC_PMCTRL = SMC_PMCTRL_RUNM(2);       //VLPR mode register Pag. 204 RefMan, Pag. 211 RefMan

                dummyread = SMC_PMCTRL;

                (void)dummyread;

                SCB->SCR &= ~ SCB_SCR_SLEEPDEEP_Msk; //Sleep Deep mask cleared. From sarah's code: https://community.nxp.com/message/902536

                power_mode_status  = SMC_PMSTAT; //Pag. 206 RefMan2017

 

                // GPIO Initialization

                //Pin Mux configuration: RefMan2017 Pag. 155

                PORTB->PCR[0] = PORT_PCR_MUX(1);            // Set pin MUX for GPIO on PORTB pin 0

                PORTB->PCR[1] = PORT_PCR_MUX(2);            // Set pin MUX for Alternative 2 

                PORTB->PCR[2] = PORT_PCR_MUX(2);            // Set pin MUX for Alternative 2 

                PORTB->PCR[5] = PORT_PCR_MUX(0);            // Set pin MUX for default on PORTB pin 5

                PORTB->PCR[6] = PORT_PCR_MUX(1);            // Set pin MUX for GPIO on PORTB pin 6

                PORTB->PCR[10] = PORT_PCR_MUX(1);           // Set pin MUX for GPIO on PORTB pin 10

                PORTB->PCR[11] = PORT_PCR_MUX(1);           // Set pin MUX for GPIO on PORTB pin 11

                PORTB->PCR[13] = PORT_PCR_MUX(1);           // Set pin MUX for GPIO on PORTB pin 13

                PTB->PDDR = 0b00110001000000;                                                     // Set pins 6, 10 and 11 as outputs. RefMan2017 Pag. 167

 

                // ADC configuration

                ADC0_CFG1 = 0b10001000; //ADC config register 1. RefMan2017 Pag. 387

                                 //Bit7: low power = 1

                                 //Bit6-5: ADCK input clock / 1 = 00

                                 //Bit4: short sample time = 0

                                 //Bit3-2: single end-10 bit = 10

                                 //Bit1-0: bus clock = 00

                ADC0_SC1A = 0b00000001;  //ADC status and control. refMan2017 Pag. 383

                                 //Bit 7 COCO 0 Read-only flag which is set when a conversion completes.

                                 //Bit 6 AIEN 0 Conversion complete interrupt disabled.

                                 //Bit 4:0 ADCH 00001 Input channel ADC0_SE1 == PTB5 selected as ADC input channel.

 

                //LPUART Configuration

                LPUART0_BAUD |= LPUART_BAUD_M10_MASK;                             //Set 10-bit data characters for TX and RX

                LPUART0_CTRL |= (LPUART_CTRL_TXDIR_MASK | LPUART_CTRL_TXINV_MASK | LPUART_CTRL_TE_MASK |LPUART_CTRL_LOOPS_MASK |LPUART_CTRL_RSRC_MASK);

                                               //RefMan 2017 pag. 586

                                               //Bit 29: TXpin is an output in single-wire mode

                                               //Bit 28: Transmit data inverted

                                               //Bit 19: Tx enabled

                                               //Bit 7: Loops >> single-wire mode where TX is internally connected to RX input

                                               //Bit 5: RSRC >> Single-wire mode TX pin connected to TX output and RX input

 

                //LPTMR0 Configuration

                LPTMR0_CSR = 0b1000000;                 //RefMan2017 Pag. 488

                               // TIE (6): Set Timer Interrupt Enable, RefMan2017 Pag. 488.

                               // TFC (2): CNR resets when Timer Compare Flag (TCF) is set (0)

                               // TMS (1): Time counter mode (0)

                               // TEN (0): timer disabled

                LPTMR0_PSR = 0b0001100;                 //RefMan2017 Pag. 490

                               // PRESCALE (6:3): CLK/4 (0001)

                               // PBYP (2): Prescaler filter is bypassed (set). The selected prescaler clk directly clocks the CNR.

                               // PCS (1:0): Prescaler clock select MCGIRCLK (00)

                NVIC_SetPriority(28, 1U);                     //Sets priority to 1 (0 is highest)

                NVIC_EnableIRQ(28);                           // Enables an interrupt for a given IRQ number

}

 

//              INTERRUPT HANDLERS

void LPTMR0_IRQHandler(void) { // From https://community.nxp.com/message/929302

                LPTMR0_CSR |= LPTMR_CSR_TCF_MASK;                      // Clear interrupt flag, CNR resets

                PTB->PTOR|=(1<<10);                                                                                         // Toggle pin 10

                // Should read ADC here

}

 

 

// MAIN

int main(void)

{

                MCU_Init();

                power_mode_status  = SMC_PMSTAT;                //Pag. 206 RefMan2017

                __asm__("CPSIE i");                                             // Enable interrupts

 

                LPTMR0_CMR = 1999;                                          // Set value for Timer - Compare value. RefMan2017 Pag. 491. CNR increments. 1999 == 1 ms

                LPTMR0_CSR |= LPTMR_CSR_TEN_MASK;      //Timer enable, RefMan2017 Pag. 489

 

                while (1){

                               PTB->PSOR|=(1<<11);

                               LPTMR0->CNR = 0x11;         //Write value to be able to read: https://community.nxp.com/message/989760

                               status_LPTMR = LPTMR0_CNR;          //Value of counter of LPTMR

 

                               ADC0_SC1A = 0b00000001;                 // Initiate conversion again with interrupt disabled

                               while (result_mask == 0){

                                               //Result is NOT ready to be read

                                               result_mask = ADC0_SC1A && ADC_SC1_COCO_MASK;

                               }

                               //Result is ready to be read

                               result_mask = 0;                                     //Updates Result's mask

                               result_ADC = ADC0_RA;                       // Saves EMG in result_ADC

                               //status_LPUART = LPUART0_STAT && LPUART_STAT_TDRE_MASK;//debug

                               //status_LPUART = LPUART0_STAT && LPUART_STAT_TC_MASK;//debug

                               //LPUART0_DATA = 0b10101010;                                        // Load data to transmit

                }

}

Tags (2)
0 Kudos
2 Replies

843 Views
mjbcswitzerland
Specialist V

Hi Andy

I didn't actually spot a reason why your interrupt is not firing. The only (slight) error I noted is that you are bypassing the pre-scaler but calculating the match value as if the pre-scaler were enabled. (2ms should be 3999 and not 1999).

Attached is a binary for the FRDM-KL03Z that uses the LPTMR to toggle the same port (PTB10) every 2ms. You can use the debug menu on the OpenSDA VCOM at 19200 Baud to view the settings as follows:

     Main menu
===================
1              Configure LAN interface
2              Configure serial interface
3              Go to I/O menu
4              Go to administration menu
5              Go to overview/statistics menu
6              Go to USB menu
7              Go to I2C menu
8              Go to utFAT disk interface
9              FTP/TELNET commands
a              CAN commands
help           Display menu specific help
quit           Leave command mode
3


 Input/Output menu
===================
up             go to main menu
md             Memory Display [address] [<l>|<w>|<b>] [num]
mm             Memory Modify [address] [<l>|<w>|<b>] [val]
mf             Memory Fill [address] [<l>|<w>|<b>] [val] [num]
set_ddr        Set port type [1..4] [<i>|<o>
get_ddr        Get data direction [1..4]
read_port      Read port input [1..4]
write_port     Set port output [1..4] [0/1]
lp_cnt         Read LPTMR CNT
help           Display menu specific help
quit           Leave command mode
md 40040000 l 4
Memory Display
0x40040000     00000041 00000004 00000f9f 00000000  ...A............

Note that the counter value needs to be read specially - it can't be read with a simple memory access but needs a write to proceed it. The command "lp_cnt" can be used for this as follows:

#lp_cnt
0x0000086b

lp_cnt
0x00000955

lp_cnt
0x00000d04

You can compare these with your register settings to be sure that you have an operating LPTMR.

These are then the NVIC settings so that you can compare with those that you have.

md e000e100 l 1
Memory Display
0xe000e100     10001000  ....

the 0x10000000 is the LPTRM's interrupt enable in the NVIC


md e000e410 l 4
Memory Display
0xe000e410     00000000 00000000 00000000 00000040  ...............@

The 0x00000040 is the priority 1 of the interrupt.

Again you can compare these with what your code has set.


Finally, this is the (low level) code that I use.

_CONFIG_DRIVE_PORT_OUTPUT_VALUE(B, (PORTB_BIT10), (PORTB_BIT10), (PORT_SRE_SLOW | PORT_DSE_HIGH));
POWER_UP_ATOMIC(5, LPTMR0);                         // ensure that the LP timer can be accessed
LPTMR0_CSR = 0;                                     // reset the timer and ensure no pending interrupts
MCG_C2 &= ~MCG_C2_IRCS;                             // select slow internal reference clock
MCG_C1 |= MCG_C1_IRCLKEN;                           // enable IRC
LPTMR0_PSR = (LPTMR_PSR_PCS_MCGIRCLK | LPTMR_PSR_PBYP); // select 2MHz IRC clock (MCGIRCLK)
fnEnterInterrupt(irq_LPTMR0_ID, PRIORITY_3, (void(*)(void))_LPTMR0_handler); // enter interrupt handler
LPTMR0_CSR |= LPTMR_CSR_TIE;                        // enable timer interrupt
LPTMR0_CMR = 1999;                                  // set the match value (2ms)
LPTMR0_CSR |= LPTMR_CSR_TEN;                        // enable the timer

The handler is:
static __interrupt void _LPTMR0_handler(void)
{
    LPTMR0_CSR = LPTMR0_CSR;                        // clear pending interrupt
    _TOGGLE_PORT(B, PORTB_BIT10);                   // toggle port on each interrupt
}

Regards

Mark

Kinetis: http://www.utasker.com/kinetis.html
Kinetis KL02/KL03/KL05:
- http://www.utasker.com/kinetis/FRDM-KL02Z.html
- http://www.utasker.com/kinetis/FRDM-KL03Z.html
- http://www.utasker.com/kinetis/FRDM-KL05Z.html


Free Open Source solution: https://github.com/uTasker/uTasker-Kinetis
Working project in 15 minutes video: https://youtu.be/K8ScSgpgQ6M

For better, faster, cheaper product developments consider the uTasker developer's version, professional Kinetis support, one-on-one training and complete fast-track project solutions to set you apart from the herd : http://www.utasker.com/support.html

843 Views
andymurray
Contributor II

Mark, 

I checked your message and decided to do slight changes in the order in which I configured the registers of the LPTMR, and everything is working fine now. Thanks also for the comment on the bypass of the prescaler. 

I want to thank you for this reply, and for the multiple replies that you have given in NXP community. It seems that your work is excelling over that of NXP support. Because of the help you have given to many KL03 users, it is much easier for the newbies to start working with this microcontroller. 

Best Regards,

Andy

0 Kudos