HardFault exception (configurable-priority exception to HardFault) - arm

stack contents I have written a simple IO interrupt routine to test IO pin in ARM cortex m4 (cm408F). Code is below and very simple and populates vector table (also includes pragma weak and other stuff).
I force the interrupt by setting the corresponding bits in NVIC_ISER0 and NVIC_ISPR0. The moment interrupt is issued the processor gives me the following hardfault exception and get stuck in a loop in L1 boot ROM.
The processor has escalated a configurable-priority exception to HardFault.
An instruction executed with an invalid EPSR.T or EPSR.IT field (CFSR.INVSTATE).
Exception occured at PC = 0xffffffff, LR = 0x0
In the call stack window I see:
__iar_systems$$modulde + 0x1451
Could this help?
I also added a while(1) loop for HardFault_Handler.
So if the processor actually asserts HardFault_Handler it should go to this endless loop but it never goes there. It does not matter which interrupt is activated (through NVIC_ISER0 and NVIC_ISPR0) the same problem happens (upon receiving the interrupt it jumps and get stuck in a loop in L1 boot ROM line: 1452!).
I have attached a snapshot of stack once the interrupt is raised. Before raising the interrupt I have changed
the contents of R12 and R0-R3 (0x1238 .... 0x1234) to realize them better in the stack. As I said
when the interrupt is raised the program never returns so I paused it and looked at the stack (attached picture).
It seems that the first push is fine; we can see xPSR, PC, LR, R12, R0 to R3 are all stacked correctly (FPU is disabled).
But in the second push into stack PC is zero (LR is fine)! I guess this shows the problem. PC should not be zero.
Why it does not return from the interrupt by pushing the the correct return address address to PC. I guess the third
push into stack is the result of this problem.
Before interrupt: SP=0x2005FFF0
After interrupt: SP=0x2005FFA4
............................
// My code is very simple as follows.
// main.c
#include <intrinsics.h>
int main()
{
int k1=123;
k1=k1+2*k1;
while(1)
{
k1=k1;
}
return 0;
}
// =========================================
// my_int_Routines.c
void PINT0_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
void PINT1_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
void PINT2_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
void PINT3_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
void PINT4_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
// =====================================
// my_startup.c
// This is ARM standard cstartup.c in IAR folder. I only added the relevant lines
// (marked as Reza)
/**************************************************
*
* This file contains an interrupt vector for Cortex-M written in C.
* The actual interrupt functions must be provided by the application developer.
*
* Copyright 2007-2017 IAR Systems AB.
*
* $Revision: 112610 $
*
**************************************************/
#pragma language=extended
#pragma segment="CSTACK"
extern void __iar_program_start( void );
extern void NMI_Handler( void );
extern void HardFault_Handler( void );
extern void MemManage_Handler( void );
extern void BusFault_Handler( void );
extern void UsageFault_Handler( void );
extern void SVC_Handler( void );
extern void DebugMon_Handler( void );
extern void PendSV_Handler( void );
extern void SysTick_Handler( void );
extern void PINT0_BLOCK_Int_Handler(void); // 18 Pin Interrupt Block Reza
extern void PINT1_BLOCK_Int_Handler(void); // 19 Pin Interrupt Block Reza
extern void PINT2_BLOCK_Int_Handler(void); // 20 Pin Interrupt Block Reza
extern void PINT3_BLOCK_Int_Handler(void); // 21 Pin Interrupt Block Reza
extern void PINT4_BLOCK_Int_Handler(void); // 22 Pin Interrupt Block Reza
typedef void( *intfunc )( void );
typedef union { intfunc __fun; void * __ptr; } intvec_elem;
// The vector table is normally located at address 0.
// When debugging in RAM, it can be located in RAM, aligned to at least 2^6.
// If you need to define interrupt service routines,
// make a copy of this file and include it in your project.
// The name "__vector_table" has special meaning for C-SPY, which
// is where to find the SP start value.
// If vector table is not located at address 0, the user has to initialize
// the NVIC vector table register (VTOR) before using interrupts.
#pragma location = ".intvec"
const intvec_elem __vector_table[] =
{
{ .__ptr = __sfe( "CSTACK" ) },
__iar_program_start,
NMI_Handler,
HardFault_Handler,
MemManage_Handler,
BusFault_Handler,
UsageFault_Handler,
0,
0,
0,
0,
SVC_Handler,
DebugMon_Handler,
0,
PendSV_Handler,
SysTick_Handler,
// ******* Reza (all zeros below)
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
PINT0_BLOCK_Int_Handler, // 18 Pin Interrupt Block Reza
PINT1_BLOCK_Int_Handler, // 19 Pin Interrupt Block Reza
PINT2_BLOCK_Int_Handler, // 20 Pin Interrupt Block Reza
PINT3_BLOCK_Int_Handler, // 21 Pin Interrupt Block Reza
PINT4_BLOCK_Int_Handler // 22 Pin Interrupt Block Reza
};
#pragma call_graph_root = "interrupt"
__weak void NMI_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void HardFault_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void MemManage_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void BusFault_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void UsageFault_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void SVC_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void DebugMon_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PendSV_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void SysTick_Handler( void ) { while (1) {} }
// ====================== Reza
#pragma call_graph_root = "interrupt"
__weak void PINT0_BLOCK_Int_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PINT1_BLOCK_Int_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PINT2_BLOCK_Int_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PINT3_BLOCK_Int_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PINT4_BLOCK_Int_Handler( void ) { while (1) {} }
void __cmain( void );
__weak void __iar_init_core( void );
__weak void __iar_init_vfp( void );
#pragma required=__vector_table
void __iar_program_start( void )
{
__iar_init_core();
__iar_init_vfp();
__cmain();
}

I am referring to the specification,
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0439b/DDI0439B_cortex_m4_r0p0_trm.pdf
Here it is mentioned that the default location of vector table is 0x00000000, it has to be mentioned in VTOR register.
If that register is not modified then after getting an interrupt the CPU will read the memory address of LOC1: 0x00000000 + some offset corresponding to the interrupt number, and will jump to LOC1.
Now my guess is, the ISR is not located properly and LOC1 is not properly set, instead it contains some garbage value 0x00001452.
Now CPU reads and jumps into that location.
I think you can solve this problem by,
Configuring the linker in such a way that the ISR is located in proper location and LOC1 gets populated with proper value.Also you may need to configure VTOR register with some customized value.

In Cortex-M, it is not actually possible for the PC to be 0xffffffff, there are physically only bits [31:1] in the register. When you observe execution which appears to be at address 0xfffffffe, this is the 'LOCKUP' address, which is an archtectural livelock state (being an invalid fetch address, it forces the PC to the lockup address, which is an invalid fetch address).
Once in the lockup state at the lockup address, the only way out is to use a debugger to change the PC, or to reset the core.
To debug Cortex-M lockup scenarios, it it important to look at the stack, but you can't be sure what was last successfully stacked. You also can't be sure about the sequence of faults, but there is a reasonable assumption that a fault occurred during a fault handler (LOCKUP means that the exception model is out of other options).
The first thing to check is what these stacked PC values relate to in your code, and where that relates to you attempting to trigger the ISR. Maybe your main() has already returned (it does nothing), and this 0x1452 is a UNDEF. The toolchain has 3 options for 'after main()', Loop forever, crash, or just carry on running through arbitrary instructions. Unless you disassemble/step through the image, this won't be obvious.

Related

C undeclared(first use in this function) error

I am writing a C program on NIOS II SBT for Eclplise to deal with Pushbuttons interrupts on a DE2 board, not that it matters but I keep running into this error 'keys_irq' undeclared(first use in this function) error. I dont know what I am doing wrong.
volatile int keys_edge_capture;
static void keys_int_init() {
void* keys_edge_capture_ptr = (void*) &keys_edge_capture;
// Enable all three keys as interrupt
IOWR_ALTERA_AVALON_PIO_IRQ_MASK(PUSH_BUTTONS_BASE, 0x0F);
// Reset edge capture register
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PUSH_BUTTONS_BASE, 0x00);
// Register ISR and place it on the interrupt table
alt_ic_isr_register(PUSH_BUTTONS_IRQ_INTERRUPT_CONTROLLER_ID, PUSH_BUTTONS_IRQ,keys_irq, keys_edge_capture_ptr, 0x00);
}
void keys_irq(void* context) {
// Recast context to keys_edge_capture type
volatile int* keys_edge_capture_ptr = (volatile int*) context;
// Read the edge capture to determine what triggered the interrupt
*keys_edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(PUSH_BUTTONS_BASE);
if (*keys_edge_capture_ptr & 0b0100) // extract KEY2
*(red_leds) = *(switches);
else if (*keys_edge_capture_ptr & 0b1000) { // extract KEY3
//do something
}
// clear the edge capture register
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PUSH_BUTTONS_BASE, 0x00);
// dummy instruction to synchronize the handler
IORD_ALTERA_AVALON_PIO_EDGE_CAP(PUSH_BUTTONS_BASE);
return;
}
int main()
{
int SW_Value,Keys_Val;
int mask = 0xF;
while(1){
SW_Value = *(switches) & mask;
*(green_leds) = SW_Value;
keys_int_init();
}
return 0;
}
The function keys_int_init is calling a reference to keys_irq. Since keys_irq is not yet defined, the compiler doesn't know what the function is. In order to avoid this, you can add something called a prototype at the beginning of the file, before keys_int_init is defined.
void keys_irq(void* context);
This tells the compiler what type the function is so it knows how to handle it.

Program stop working as I activated ADC interrupt on STM32

I wrote code for generation square pulse. Everything works fine till I activate ADC Interrupt by NVIC commands. Is ADC IRQ Handler wrote correctly?(I am specious on Handler). ADC Sets on continuous mode by only one channel is using. End_of_Conversion and End_of_Sequence Interrupt flag have been set.
#include <stdint.h>
#include "stm32f0xx.h"
//static __IO uint32_t RPM;
//uint32_t RPM=1000;
#define DUTY_CYCLE 0.1
void SysTick_Handler(void);
void ADC1_IRQHandler(void);
void Delay_10us(__IO uint32_t nTime);
static __IO uint32_t TimingDelay;
static __IO uint32_t adcValue;
//int calibration_factor;
uint32_t rpm;
int global_counter=0;
int main(void)
{
// Set SysTick for create 10 microsecond delay
if(SysTick_Config(80))
{
while(1);
}
//CLOCK
RCC->CR|=RCC_CR_HSEON;
while(!(RCC->CR&RCC_CR_HSERDY));
RCC->CR|=RCC_CR2_HSI14ON;
while(!(RCC->CR2|=RCC_CR2_HSI14RDY));
RCC->CFGR|=RCC_CFGR_SW_HSE;
while(!(RCC->CFGR&=RCC_CFGR_SWS_HSE));
RCC->APB2ENR|=RCC_APB2ENR_ADCEN;
// ADC1->CFGR2|=ADC_CFGR2_CKMODE_1;
RCC->AHBENR|=RCC_AHBENR_GPIOAEN;
//GPIO
GPIOA->MODER|=GPIO_MODER_MODER4_0;
GPIOA->PUPDR|=GPIO_PUPDR_PUPDR4_0;
GPIOA->ODR|=GPIO_ODR_4;
GPIOA->MODER|=GPIO_MODER_MODER5;
//NVIC
NVIC_EnableIRQ(ADC1_COMP_IRQn);/*if I comment this two lines everything works*/
NVIC_SetPriority(ADC1_COMP_IRQn,0);/*if I comment this two lines everything works*/
//ADC
ADC1->CR&=~ADC_CR_ADEN;
ADC1->SMPR|=ADC_SMPR1_SMPR;
ADC1->CFGR1|=ADC_CFGR1_CONT;
ADC1->IER|=ADC_IER_EOSIE|ADC_IER_EOCIE;
ADC1->CHSELR|=ADC_CHSELR_CHSEL5;
// for(int i=0;i<10;i++);
// ADC1->CR|=ADC_CR_ADCAL;
// while(ADC1->CR&ADC_CR_ADCAL);
// calibration_factor=ADC1->DR;
for(int i=0;i<10;i++);
ADC1->ISR|=ADC_ISR_ADRDY;
ADC1->CR|=ADC_CR_ADEN;
while(!(ADC1->ISR&=ADC_ISR_ADRDY));
ADC1->CR|=ADC_CR_ADSTART;
/* Loop forever */
int counter=0;
while(1){
if(rpm<250){rpm=250;}
int T=10000/rpm;
int OnDelay=T*DUTY_CYCLE;
int OffDelay=T*(1-DUTY_CYCLE);
GPIOA->ODR &=~GPIO_ODR_4;
Delay_10us(OffDelay);
GPIOA->ODR|=GPIO_ODR_4;
Delay_10us(OnDelay);
counter++;
if(counter==59)
{
GPIOA->ODR &=~GPIO_ODR_4;
Delay_10us(2*T);
counter=0;
continue;
}
}
}
void Delay_10us(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
void SysTick_Handler(void){
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
void ADC1_IRQHandler()
{
//
}
Your program enters ADC1_IRQHandler and stays there forever, since the handler is empty.
If you've enabled an interrupt, you must handle it - at least clear the interrupt source. And don't look at the SysTick_Handler it is a special case with autoreset. For other interrupts the corresponding status bit must be reset, or a special sequence of operations performed. Details are usually found in the description of a status register, in your case it is ADC_ISR.
With End_Of_Conversion and End_Of_Sequence enabled, the interrupt could be triggered by the EOC and EOSEQ status bits. Both could be cleared by writing 1 to it. The handler should look something like this:
void ADC1_IRQHandler()
{
if(ADC1->ISR & ADC_ISR_EOC)
{
//Handle end of conversion
ADC1->ISR = ADC_ISR_EOC;
}
if(ADC1->ISR & ADC_ISR_EOSEQ)
{
//Handle end of sequence
ADC1->ISR = ADC_ISR_EOSEQ;
}
}
The name of a handler depends on the startup used, it must match name of a placeholder in the interrupt vector table of the startup file. In Cube-generated projects this is an assembler file (.s) in Startup folder (eg. /Core/Startup/startup_stm32f407zgtx.s)

Map interrupt handlers for SAM L10 device

I have been attempting to set up the I2C on a system which uses the SAM L10 MCU. I have been able to build my project on MPLAB X as a standalone project without having to use MPLAB Harmony v3 code configurator. My problem is I can't seem to get the interrupt handlers to be mapped correctly.
Here are the references I am using:
https://github.com/Microchip-MPLAB-Harmony/csp_apps_sam_l10_l11/tree/master/apps/sercom/i2c/master/i2c_eeprom
and
https://microchipdeveloper.com/32arm:samd21-code-gcc-nvic-example
In both cases, there seems to be a table which assigns function pointers for the interrupt handlers, called exception_table. I do not know the correct way to incorporate this in my code, in fact, I don't understand how this code actually gets executed as it is simply assigning the function pointers.
Here is what I have for my code:
init.c
#include <xc.h>
#include "peripheral/clock/plib_clock.h"
#include "peripheral/nvic/plib_nvic.h"
#include "peripheral/sercom/plib_sercom0_i2c_master.h"
#pragma config NVMCTRL_NSULCK = 0x7
#pragma config BOD33_LEVEL = 0x6
#pragma config BOD33_DIS = CLEAR
#pragma config BOD33_ACTION = 0x1
#pragma config WDT_RUNSTDBY = CLEAR
#pragma config WDT_ENABLE = SET
#pragma config WDT_ALWAYSON = CLEAR
#pragma config WDT_PER = 0x9
#pragma config WDT_WINDOW = 0xB
#pragma config WDT_EWOFFSET = 0xB
#pragma config WDT_WEN = CLEAR
#pragma config BOD33_HYST = CLEAR
void port_init (void)
{
/* Configure SERCOM0 I2C on PA22 and PA23, Peripheral function C */
PORT_REGS->GROUP[0].PORT_PINCFG[22] = 0x1;
PORT_REGS->GROUP[0].PORT_PINCFG[23] = 0x1;
PORT_REGS->GROUP[0].PORT_PMUX[11] = 0x22;
/* Configure PA25 as an output driven high */
PORT_REGS->GROUP[0].PORT_DIR = 0x02000000;
PORT_REGS->GROUP[0].PORT_OUT = 0x02000000;
PORT_REGS->GROUP[0].PORT_PINCFG[27] = 0x0;
}
void sys_initialise (void)
{
CLOCK_Initialize ();
port_init ();
SERCOM0_I2C_Initialize ();
NVIC_Initialize ();
}
main.c
#include <xc.h>
#include "init.h"
#include "peripheral/sercom/plib_sercom0_i2c_master.h"
void main (void)
{
sys_initialise ();
uint8_t reg[1];
reg[0] = 0xD0;
SERCOM0_I2C_Write (0x76, &reg[0], 2);
}
I used the GPIO for my debugging. I thought setting up the NVIC would be enough but it only enables the interrupt themselves, the interrupt handler is still not mapped. I can see it because from my debugging the handler never gets called when I try to send something over I2C. I can also see the I2C work correctly for sending the first part of the transaction, which is the device address up to the ACK bit. From this point on, I can see on the scope that the clock is held low but the SDA comes back to high. I believe it is because the interrupt handler SERCOM0_I2C_InterruptHandler from https://github.com/Microchip-MPLAB-Harmony/csp_apps_sam_l10_l11/blob/master/apps/sercom/i2c/master/i2c_eeprom/firmware/src/config/sam_l10_xpro/peripheral/sercom/i2c_master/plib_sercom0_i2c_master.c does not get called, as this is responsible for sending the rest of the data.
I found the answer to this in the user guide for the xc32 compiler which can be accessed here: https://ww1.microchip.com/downloads/en/DeviceDoc/XC32_PICC_UG_DS50002895A.pdf
I was missing the call to NVIC_SetVector() - section 14.3 shows how to associate the interrupt handler with an exception.

RTC on an mcu - function pointers and callbacks

The code below is an example of how to use the real time clock on an mcu.
My question is in relation to callbacks and function pointers.
I have included the struct declaration for rtc_config_t below.
My question is, on the line cfg.callback = rtc_example_callback
Why is the & sign not used before rtc_example_callback.
Why is not necessary to pass the arguments to rtc_example_callback?
The last struct memeber void *callback_data; is set to NULL, I don't understand what this does?
When or what would you want to return?
Many tanks for your inputs
#include "rtc.h"
#include "interrupt.h"
#include "isr.h"
#define ALARM (QM_RTC_ALARM_MINUTE / 6)
#define MAX_RTC_FIRINGS (5)
void rtc_example_callback(void *);
static volatile uint32_t rtc_fired = 0;
/* RTC app example */
int main(void)
{
/* Variables */
rtc_config_t cfg; //create a struct variable to configure the RTC
PRINTF("Starting: RTC\n");
/* Initialise RTC configuration */
cfg.init_val = 0;
cfg.alarm_en = true;
cfg.alarm_val = ALARM;
cfg.callback = rtc_example_callback;
cfg.callback_data = NULL;
irq_request(IRQ_RTC_0, rtc_isr_0); //submit the RTC to the interrupt service routine
clk_periph_enable(CLK_PERIPH_RTC_REGISTER | CLK_PERIPH_CLK); //switch on RTC and Periphal clock
rtc_set_config(RTC_0, &cfg); //Set the RTC configuration
/* Wait for RTC to fire 5 times and then finish. */
while (rtc_fired < MAX_RTC_FIRINGS) {
}
PRINTF("Finished: RTC\n");
clk_periph_disable(CLK_PERIPH_RTC_REGISTER | CLK_PERIPH_CLK); //turn off the clocks
return 0;
}
void rtc_example_callback(void *data)
{
PUTS("Alarm!!\n");
qm_rtc_set_alarm(RTC_0, (RTC[RTC_0].rtc_ccvr + ALARM));
rtc_fired++;
}
-----------------------------------------------------------------------
/**
* RTC configuration type.
*/
typedef struct {
uint32_t init_val; /**< Initial value in RTC clocks. */
bool alarm_en; /**< Alarm enable. */
uint32_t alarm_val; /**< Alarm value in RTC clocks. */
/**
* User callback.
*
* #param[in] data User defined data.
*/
void (*callback)(void *data);
void *callback_data; /**< Callback user data. */
} rtc_config_t;
name of the function is a pointer to the function
function will be called with arguments from rtc library, you are not invoking it (you cannot pass arguments here).
I guess that NULL assigned to custom_callback will not call custom method from library (default function or no function will be called), just assign NULL if you dont want to use custom callback.
usually library code looks like:
if(custom_callback)
{
custom_callback(some_parameters);
}
else
{
default_callback(some_parameters);
}

accurate stopwatch by embedded C

I would like to make a stopwatch by embedded C to program on ST STM32F407VG.
I know how I should write a clock program to make a hundredth of sec,but I know it will not be accurate,when I use a delay of 10ms.
what should I do
While the STM32 is endowed with a number of hardware timers, all Cortex-M devices have a common SYSTICK timer, so the simplest and most portable method is to use that.
The following creates a 1 millisecond period free-running counter msTicks:
#include "stm32f4xx.h"
#include <stdint.h>
static volatile uint32_t msTicks = 0; // Milliseconds
int main(void)
{
SysTick_Config(SystemCoreClock / 1000) ; // 1ms interrupt
for(;;)
{
... // your code here
}
}
// SYSTICK interrupt handler
void SysTick_Handler(void)
{
msTicks++ ;
}
Then your stopwatch can be implemented with two functions thus:
static uint32_t start_time ;
void start( void )
{
start_time = msTicks ;
}
uint32_t getMillisecondsSinceStart( void )
{
return msTicks - start_time ;
}
For example:
bool running = false ; // needs <stdbool.h> included
uint32_t time_millisec = 0 ;
for(;;)
{
if( startButtonPressed() )
{
running = true ;
start() ;
}
if( stopButtonPressed() )
{
running = false ;
}
if( running )
{
time_millisec = getMillisecondsSinceStart() ;
}
displayTime( time_millisec ) ;
}
Accuracy will be entirely dependent on whatever is driving the core clock - normally an external crystal or oscillator. The internal RC oscillator will be less precise - it is factory trimmed to +/- 1%. Your particular part also has a 32KHz RC oscillator for driving the RTC that can be calibrated, but that cannot be used to drive the core clock (systick), and would be less portable, less precise and more complex that the method described above.
The best option is to use the internal timer modules of that microcontroller. About which you can explore in the datasheet.

Resources