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
}
}
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
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