I have an issue with my system in sleep mode.
Currently my MCU is driving a radio chip which send periodically messages over radio, I set the MCU in sleep mode when the radio chip is sending a message.
But sometimes I get an interrupt when the radio chip is sending data and that wake up my MCU and abort data sending.
I don't know how to find which irq is waking me up because my system is currently running with lots of tasks that can run irq "randomly" BUT none of the "random" irq is supposed to trig when the radio chip is sending a message.
When I try to break after __WFI() used in my sleep mode, the call stack is already refreshed with new data and I can't see what was the last instructions executed.
Do you know a method to determine which irq is waking me a sleeping system ?
Thanks for your help.
Examine the NVIC->ISPR[x] and the SCB->ICSR registers right after wakeup. If you can't break before the handler runs, replace __WFI() with this macro (or something equivalent if you are not using gcc)
uint32_t ispr0, ispr1, ispr2, icsr;
#define __WFI() ({ \
uint32_t primask = __get_PRIMASK(); \
__disable_irq(); \
asm volatile("wfi"); \
ispr0 = NVIC->ISPR[0]; \
ispr1 = NVIC->ISPR[1]; \
ispr2 = NVIC->ISPR[2]; \
icsr = SCB->ICSR; \
__set_PRIMASK(primask); \
})
to be able to comfortably examine the contents afterwards. I have taken the register names from the ST Cortex-M3 Programming Manual, the process should be similar for other cores.
Related
I have an ATSAMD21E18A micro that I am using with semi-hosting. In order for the semi-hosting to work, GDB needs to be "attached" before the first bkpt instruction. On the other hand, I have inexplicably found that the SysTick interrupt will not fire if GDB was already attached when I configured it. If I want to the SysTick interrupt to fire, I have to perform a reset (power-off via a button) and tell GDB to continue when it hasn't yet configured the micro (that is, it hasn't sent breakpoints over or anything else), and then hit Ctrl-C to initialize debugging mode after the SysTick configuration but before we get to initialise_monitor_handles.
I have verified that the start function is only copying over the relocatable data segment, zeroing the zero segment, and setting the right initial stack pointer value. We are writing our code without libraries like CMSIS.
Also I can confirm that I have no issues when the debugger is not attached (JLinkGDBServer through an Atmel SAM-ICE), besides needing to remove the semi-hosting stuff.
Also, the SysTick COUNT does still correctly count even when the interrupts themselves don't fire. Also the SysTick pending interrupt bit PENDSTSET in ICSR, is in fact set when this happens.
My code follows:
int main()
{
// enable system timer interrupt
SYS_TICK->STATUS = 0; // (CSR)
SYS_TICK->PERIOD = 48000; // (RVR) fire at 1khz for 48mhz clock
SYS_TICK->STATUS = 0b111; // use processor clock, w/ interrupt, and enabled
SYS_TICK->COUNT = 1; // (CVR) avoid high unknown value
// dumb busy loop
util_idle_ms(2000); // <<< I hit Ctrl-C to break here!
initialise_monitor_handles();
// ... more system initialization and everything else
}
I have seen some similar seeming questions here on StackOverflow, but they seemed to be too vague to get good answers.
Edit:
Here are possibly relevant register values taken during the busy loop for the run that doesn't call the SysTick handler (no hard reset, GDB attached before SysTick configured):
SYS_TICK_CSR/STATUS: 0x10007
SYS_TICK_RVR/PERIOD: 48000
SYS_TICK_CVR/COUNT: 5245 (varies of course)
NVC_ISER: 0 (and we expect this since SysTick is considered an exception, and not an interrupt)
DHCSR: 0x30003/0x1030003 (C_MASKINTS is not set; I've seen both values show up)
ICSR: 0x400f00f (it really wants to run the SysTick handler)
PRIMASK: 0
xPSR: 0x2100000f (IPSR is 0x0f/SysTick)
And for the run that calls the SysTick handler just fine (hard reset with GDB attaching after SysTick configuration):
SYS_TICK_CSR/STATUS: 0x10007
SYS_TICK_RVR/PERIOD: 48000
SYS_TICK_CVR/COUNT: 16892 (varies of course)
NVC_ISER: 0
DHCSR: 0x10003/0x1030003 (I've seen both values show up)
ICSR: 0 (SysTick handler already run)
PRIMASK: 0
xPSR: 0x2100000f
So the register values here do not yet seem to reveal anything new to me... Please help inform me of other potentially relevant registers to check!
Just for interest, the reason this is important to me is because I have gotten gprof to work on this chip, based on https://mcuoneclipse.com/2015/08/23/tutorial-using-gnu-profiling-gprof-with-arm-cortex-m/
And although I do have to hit Ctrl-C at just the right time after a hard reset, it does work like this!
Edit
I have found that I had a misunderstanding where I thought running load in GDB performed a soft reset. I have since found that although it returns execution to reset vector, various peripherals and other registers are not in fact reset. If I perform a soft reset in GDB with monitor reset then I don't need to Ctrl-C during a delay to attach GDB and both SysTick and SemiHosting will work.
The problem occurs when SysTick is configured and then load is run in GDB, without an explicit hard or soft reset. In this case, SysTick does not fire interrupts. Most of my debugging went like this, loading new code and immediately expecting it to work so I could evaluate it. Just running monitor reset is a better workaround than before, but I still would prefer to know the reason for SysTick's misbehavior!
I would visit the ARMĀ® v6-M Architecture Reference Manual and see if you can get some direction from that. https://static.docs.arm.com/ddi0419/d/DDI0419D_armv6m_arm.pdf
Observe that state of the registers related to the Systick that you didn't include in your question. If you can't figure out the problem based on those registers, edit your question and post the register values here (the NVIC ISER, all registers related to systick config, the DHCSR, and any others you think are related). They will be the key to getting more feedback.
The Debug Halting Control and Status Register (DHCSR) has the ability to mask interrupts including the systick. Maybe this is being set by the debugger?
bit 3 of the DHCSR looks relevant
I would also check that the SYST_RVR (Systick reload value register) is being set to something sane.
I don't have the rep to comment on your question, but I'm hoping this can get you going in a productive direction :)
I configure 4 gpio pins as pulse counters to read Hall-effect liquid flow sensors, which works fine, but when I try to use those same pins to wake up from a deep sleep, sleep ends immediately. If I don't configure the pins for PCNT, deep sleep works as expected.
I even went so far as setting a flag in RTC memory so that it sleeps for as long as 5 seconds, wakes up and does not configure PCNT before trying to go back to sleep. It still wakes from the second sleep immediately, so whatever it is about those pins is retained across deep sleep.
Is there a way to restore the default state of those pins without resetting the entire device?
The answer is, set both *_gpio_num members of pcnt_config_t to PCNT_PIN_NOT_USED and call pcnt_unit_config again:
pcnt_config_t pcnt_config = {
.pulse_gpio_num = PCNT_PIN_NOT_USED,
.ctrl_gpio_num = PCNT_PIN_NOT_USED,
.channel = PCNT_CHANNEL_0;
}
pcnt_unit_config(&pcnt_config);
I'm implementing a simple UART receive-transmit protocol on STM32F103, the library/boilerplate code I'm using here is LL, not HAL (as HAL includes insane amounts of overhead)
My problem is that after successfully entering the interrupt handler "USART1_IRQHandler" it keeps cycling on forever.
My code is here:
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
int ii = 0;
for(ii=0; ii<4; ii++){
LL_mDelay(40);
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
LL_mDelay(40);
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
}
uint8_t cc = LL_USART_ReceiveData8(USART1);
LL_USART_TransmitData8(USART1, cc);
LL_mDelay(130);
//LL_USART_ClearFlag_RXNE(USART1);
//NVIC_ClearPendingIRQ( USART1_IRQn );
/* USER CODE END USART1_IRQn 0 */
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
and in main.c I have:
LL_USART_EnableIT_RXNE(USART1);
while (1)
{
LL_mDelay(300);
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
LL_mDelay(300);
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
//LL_USART_EnableIT_TC(USART1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
The GPIO_Toggle commands are just there to blink an led so I know what's going on. Here's waht happens:
When I power the MC up, it enters the main loop and blinks slowly.
When I send something (~10 bytes) through UART, the led starts blinking fast, indicating that it has entered the interrupt handler. PProblem is that it never stops and keeps spinning around in the interrupt handler.
I've tried using the now commented functions
LL_USART_ClearFlag_RXNE(USART1);
NVIC_ClearPendingIRQ( USART1_IRQn );
either alone or in combination but they have absolutely no effect on anything.
What am I doing wrong?
How can I exit the handler?
Actually everything in your USART interrupt handler is wrong.
You do not check what has caused the interrupt.
If it is RXNE flag you should just load the value from the DR register. You do not need to clear any flags. If it is TXE flag you can store the data to the DR register. You cant clear this flag any other way. If you do not have any data to send you need to disable the TXE interrupt. Otherwise it will trigger continuously.
You can't just read and write the data register when you want to. You need to know if you are allowed to
You should also control the error statuses.
You must not use any delays in the interrupt routines. Keep it as fast as possible.
Do not touch NVIC exept for enabling and disabling the interrupts, as for now you do not know what it is for.
The system time utilised for controlling delays is updated by a periodic sysTick interrupt. If the RXNE interrupt has a higher priority than the sysTick interrupt it won't be handled while you're inside your RXNE IRQ handler, so the time will never increment and your delay end time will never be reached. Depending on how your delay is implemented, it may just put the CPU in a spinlock that can never exit.
I'd like to write an open-sourced core driver for controlling stepper motors in Linux. In this case, especially for 3D-Printers.
The basic idea is that the driver reserves pins on one IO port, and then manipulates those pins at once. It receives a buffer full of "toggle this, toggle that" values, and then emits those to the port, using a hardware timer.
Now the question is: Is there any way to handle a specific hardware interrupt as fast as possible?
The chip in question is an Allwinner H3, and I am using the TMR1 resource of said chip (IRQ 51). I can use it just fine, and it works as an interrupt as well:
static irqreturn_t stepCore_timer_interrupt(int irq, void *dev_id)
{
writel(TMR1_IRQ_PEND, TMR_IRQ_ST_VREG);
icnt++;
porta_state = readl(PA_VDAT);
porta_state &= porta_mask;
if(icnt & 0x00000001)
{
porta_state |= 0x00000001;
}
writel(porta_state, PA_VDAT);
return IRQ_HANDLED;
}
static struct irqaction stepCore_timer_irq = {
.name = "stepCore_timer",
.flags = IRQF_DISABLED | IRQF_NOBALANCING , IRQF_PERCPU,
.handler = stepCore_timer_interrupt,
.dev_id = NULL,
};
static void stepCore_timer_interrupt_setup(void)
{
int ret;
u32 val;
writel( 24000000, TMR1_INTV_VALUE_VREG );
writel( ( TMR1_MODE_CONTINUOUS | TMR1_CLK_PRES_1 | TMR1_CLK_SRC_OSC24M ), TMR1_CTRL_VREG );
ret = setup_irq(SUNXI_IRQ_TIMER1, &stepCore_timer_irq);
if (ret)
printk("%s: ERROR: failed to install irq %d\n", __func__, SUNXI_IRQ_TIMER1);
else
printk("%s: irq %d installed\n", __func__, SUNXI_IRQ_TIMER1);
ret = irq_set_affinity_hint(SUNXI_IRQ_TIMER1, cpumask_of(3));
if (ret)
printk("%s: ERROR: failed to set irq affinity for irq %d\n", __func__, SUNXI_IRQ_TIMER1);
else
printk("%s: set irq affinity for irq %d\n", __func__, SUNXI_IRQ_TIMER1);
/* Enable timer0 interrupt */
val = readl(TMR_IRQ_EN_VREG);
writel(val | TMR1_IRQ_EN, TMR_IRQ_EN_VREG);
}
TMR1 is otherwise unused (in fact, I had to add it myself) and so far works. However, there is quite some latency in handling the rather simple IRQ routine. Since I want to produce some code that is usable for a 3D printer, I very much like a more "stable" timer interrupt.
So, my question is: Is there any way to have a very short IRQ routine in Linux that has the highest possible priority? Or even doesn't care about the Linux scheduler at all, and just "does it's thing"? Basically a raw IRQ handler, ignoring what Linux thinks it should be?
The core it runs on is dedicated to just that task, anyways. The handler will be as short as possible: fetch an u32 from an array, write that to the port, done.
Preferably I would like to have something that just ignores the remainder of Linux all together. Yes, I know that that isn't the way to do it. But this is meant for a rather special case, so I have no qualms with adapting the regular kernel sources to suit those needs.
Oh, that reminds me, the kernel is 3.4.112 with the suitable preempt-rt patches.
Any help is greatly appreciated.
Greetings,
Chris
Here is a general solution to this issue. You can write a kernel module which will overwrite the existing interrupt handling routine and will be replaced by your own routine, where you can handle your irq of interest and redirect all the irq to the existing kernel interrupt handling routine. It's possible for x86 arch where you will get low level CPU instructions to get the existing address of interrupt description routine (lidt). I believe it should be possible for ARM too. Now, Linux has technique of CPU isolation isolcpus by utilizing this technique you can take a CPU out of scheduler domain i.e. no task will be scheduled on that particular CPU, until you specify a task to be run on that particular CPU (using taskset). After you take a CPU out of scheduler domain you can take help of the technique of affine a interrupt to that isolated CPU, you can do it via /proc/irq/IRQ_NUMBER/smp_affinity. Now all of your interrupt will be handled by this isolated CPU and 100% dedicated to that interrupt. And with your own IRQ routine you have full control over the interrupt handling.
Hopefully this will help!
Have you thought about using FIQ for that. We have a blog post about it:
http://free-electrons.com/blog/fiq-handlers-in-the-arm-linux-kernel/
I'm developing software for an NXP LPC1788 microcontroller, and I'm using the embOS RTOS. Whenever a message is received over USB, I want to use the OS_PutMailCond() function to store the USB message in a mailbox which a handler function is waiting on. In other words, I want to make message handling interrupt-driven.
The embOS user manual can be found here. Page 145 describes the OS_PutMailCond() function.
Whenever a USB message is received, it triggers the USB interrupt service routine on the LPC, but to let embOS know that it's an ISR I have to place OS_EnterInterrupt() and OS_LeaveInterrupt() at the start and end of the ISR respectively. This is necessary if I want to call embOS functions within it, including OS_PutMailCond().
The problem is that if I put OS_EnterInterrupt()/OS_LeaveInterrupt() anywhere within the USB ISR, the USB stops functioning properly and Windows informs me that the device has malfunctioned.
I have no idea why this is the case. We've tried something similar for handling messages over CAN, as shown below, and it works fine.
void CAN_IRQHandler(void)
{
OS_EnterInterrupt();
...
if (MBfieldCANframeInitialised)
OS_PutMailCond (&MBfieldCANframe, &recMessage);
OS_LeaveInterrupt();
}
OS_EnterInterrupt() and OS_LeaveInterrupt() are described on pages 252 and 253 of the linked manual. From the additional information section of the former:
If OS_EnterInterrupt() is used, it should be the first function to be
called in the interrupt handler. It must be used with
OS_LeaveInterrupt() as the last function called. The use of this
function has the following effects, it:
disables task switches
keeps interrupts in internal routines disabled
EDIT
I've investigated further and found out that using OS_EnterInterrupt() and OS_LeaveInterrupt() within the USB ISR (and other ISR's like the one for the GPIO when a rising or falling edge is detected on a pin) causes an OS error. The error value is 166, which means "OS-function called from ISR with high priority".
I'll update if I find out anything else.
Problem solved. It turns out the guy that made this work for the CAN ISR changed the code of one of the embOS source files to set the CAN ISR priority level from 0 to 29 (higher level = lower priority). I did the same thing for the USB ISR:
void OS_InitHW(void) {
OS_IncDI();
//
// We assume, the PLL and core clock was already set by the SystemInit() function
// which was called from the startup code
// Therefore, we don't have to initailize any hardware here,
// we just ensure that the system clock variable is updated and then
// set the periodic system timer tick for embOS.
//
SystemCoreClockUpdate(); // Update the system clock variable (might not have been set before)
if (SysTick_Config (OS_PCLK_TIMER / OS_TICK_FREQ)) { // Setup SysTick Timer for 1 msec interrupts
while (1); // Handle Error
}
//
// Initialize NVIC vector base address. Might be necessary for RAM targets or application not running from 0
//
NVIC_VTOR = (OS_U32)&__Vectors;
//
// Set the interrupt priority for the system timer to 2nd lowest level to ensure the timer can preempt PendSV handler
//
NVIC_SetPriority(SysTick_IRQn, (1u << __NVIC_PRIO_BITS) - 2u);
NVIC_SetPriority(CANActivity_IRQn, (1u << __NVIC_PRIO_BITS) - 3u);
NVIC_SetPriority(CAN_IRQn, (1u << __NVIC_PRIO_BITS) - 3u);
NVIC_SetPriority(USB_IRQn, (1u << __NVIC_PRIO_BITS) - 3u);
OS_COM_INIT();
OS_DecRI();
}
I found this in the embOS documentation:
Why can a high priority ISR not use the OS API ?
embOS disables low priority interrupts when embOS data structures are modified. During this time high priority ISR are enabled. If they would call an embOS function, which also modifies embOS data, the embOS data structures would be corrupted.