Assigned IRQ interrupt to digital pin

Status
Not open for further replies.
Hi again,

I guess the hard part for some of us to understand is exactly what your goal is. Yes I understand wanting to learn how it works...
I also know there are ones up here who dislike Arduino and want to write everything from scratch...

Again hard to really know how to help here. Example what board are you using again?
As for where your interrupt function pointer needs to be held in the _VectorsFlash array depends on processor!
Example Teensy 3.2 this is in 105
Teensy 3.0 -> 58
On LC (c and d or on 47)
Teensy 3.6 in 77
Teensy 3.5 also on 77

So you need to make sure you are matching which hardware you are using...
Much easier to:

a) Use the system ISR handler name for the port: void portc_isr(void)
All this does is use the linker to put the address in the correct location in the Interrupt vector

b) Use attachInterruptVector(...

Yes I understand you want to do everything yourself... But it is also hard to know where you are drawing the line. That is you are using the names of registers and the like. You could instead create your own names for them and have their addresses as #defines... You are using the NVIC enable and set priority functions...

So again my advice is start off simple. Simply put your code into the portc_isr function, and try your work to enable it and the like and see if it gets called.

When I am in doubt in cases like this and I don't have a logic analyzer sitting on my desk (I have a few), I would then do something simple, like have my
setup code call pinMode(13, OUTPUT);
And then I would have my first ISR do something like: digitalWriteFast(13, HIGH); At the start of it and see if the LED ever goes on...
 
@M.Melani - I see you visited debug post - and two folks have downloaded it. If you did and want to follow this path it will catch faults on bad code and dump the registers as Paul saw them that may help.


Doing this with that code in libraries will stop on fault and blink the LED. The deb_t3 won't print or do anything but record the LINE# calling order in some fashion to show if a FAULT occurs::
Code:
#include "debug_t3.h"

void setup() {
  // pinMode(LED_BUILTIN, OUTPUT); // not needed the code sets as output
  DebState( LED_BUILTIN) )

  // ...
  deb_t3( 1, ( 1 << 11 ) );
  SIM_SCGC5 = ( 1 << 11 ) ; // set for PORT C e /m Clock Gating Control Register 5 (SIM_SCGC5)
  deb_t3( 2, (( 1 << 19 ) | ( 1 << 17 ) | ( 1 << 8 ) | ( 1 << 1 ) | ( 1 << 0 )) );
  PORTC_PCR6 = ( 1 << 19 ) | ( 1 << 17 ) | ( 1 << 8 ) | ( 1 << 1 ) | ( 1 << 0 );// Type of interrupt and INPUT and Pull Up
  // ...

}


loop(){}

void My_F(){ // My Function 
  deb_t3( 9, x );
  PORTC_PCR6 &= ~(1 << ppul6); // clear the flag 
  deb_t3( 10, x );
  x +=1;  // increment my variable

} // end of My_F
 
Hi Kurt , thanks for your advise .
I am using Teensy 3.2 , and the Interrupt Vector is 105 , as the memory location should be 0x0000_01A4 .
And since this memory is fix ( if it was not remapped) , my goal is to write on it the address of my Handler function ( My_F) .
Regarding your points a) and b) , i already use it , and it work fine . I already implement the original function in the Kinetis H file extern void portc_isr(void);
But , later , when i start to study it , i wander if i can work directly with memory register . I am asking much ? I don't think so , it is only because i still don't use the proper way to set.
For sure , if i have to make a prototype devices i will use the ready functions , but at the final i know how they work .

so this way do not work :
#pragma abs_address:0x000001A4
void(* My_F_handler[])()={ My_F };
#pragma end_abs_address

I will change way .
 
@M.Melani - re your PM - the notes about the debug library were to have any FAULTS caused by your attempts to code this captured and reported out to the Serial Monitor.

I was assuming the Teensy is faulting and just hanging without useful information on where the problem occurred with the low level operations being attemtped. Including the library in the sketch keeps USB output online, allows software reprogramming without Button, and indicates the type of the fault.

If you really want to learn the fundamentals of the processor and details this might be informative given the code stack and registers Paul saw as relevant to expose in that code.

Post #52 was showing that if a call to deb_t3( a,b ) appears before a line that Faults - that line number in the code will be shown as well as some processor register details as Paul has captured and presented them.
>> Where Param 'a' is a number 0...9 for reference/index of a place in the code and Param 'b' is a local uint32_t value that may be of some use and is displayed on faulting.

This would allow you to pinpoint how far along the code gets before triggering the fault so it would be more indicative of where the problem is.

The example sketches are programmed to cause faults and trigger ASSERTS where known issues are present and one by one they can be commented out to allow the program to continue.
 
Hi again @M.Melani,

I will be honest and say I have no clue what this would actually do:
Code:
#pragma abs_address:0x000001A4
void(* My_F_handler[])()={ My_F };
#pragma end_abs_address

What is address 1A4? What do you expect to be stored there?
Is this part of the Interrupt Vector? If so how does the linker deal with you trying to put something there and the generated vector table?

Note: When I start trying to do things that are pretty tricky looking or I wonder what was actually generated, I then dump (disassemble) the actual generated binary and try to see what was actually generated. There is a standard tool called objdump that does this... Note: as there are different types of processors, and the like, there may be several different versions on a machine and often they will have other stuff as part of their name. In our case the actual program name looks like: arm-none-eabi-objdump

There is a thread where someone made it easier to do such things: https://forum.pjrc.com/threads/4169...Arduino-quot-Export-compiled-binary-quot-menu

Not sure if it would help or not
 
Following up on Kurt's last - in case I understand any of this - it seems like it is an attempt to prepopulate the RAM vector table to point to My_F()?

But that vector table is only enabled after the PJRC processor setup - before that the RAM table is not used as it comes from FLASH. After that setup by PJRC the table values area in memory may be overwritten during that setup - or by the compiler/linker?

Question - is PJRC's TeensyDuino IDE setup and compile code being used as a starting point? With that the vector table could be updated at runtime in setup - but even if this complie/link time operation were supported it may be made void with the processor setup or compiler/linker memory allocation and zeroing.

Using the PJRC code for IDE support wakes up the MCU clocks and many other critical parts to establish usable USB and other functionality that is not present after processor reset.

Some of that setup and IDE compatible code stack is what exposes the 'weak' FAULT _isr handlers used/needed in the debug library in the event running code faults the processor.
 
Kurt , what i am trying to do , ( in the wrong way for sure ) it is to read the address 0x000001A4 that it is the corresponding :
VECTOR_105 // 0x0000_01A4 105 or IRQ 89 Port control module Pin Detect (Port C) .

Base on how the ARM work , inside that memory address it should be write the assigned Function Handler ( My_F).

If you assign a function with void attachInterrupt( pin , My_F , mode ) , the address of the Function ( My_F) i made it should go in that address .
So when the interrupt 89 will happen , it will go to 0x0000_01A4 , and start My_F .

But why , if i read the &My_F it is different from the value in memory 0x0000_01A4 ??

Using this 3 lines force to store My_F to that memory location , but as Paul wrote , i can't use , it is a wrong way .
#pragma abs_address:0x000001A4
void(* My_F_handler[])()={ My_F };
#pragma end_abs_address

That it .
 
Sorry, I will leave some of those questions up to the C/C++ compiler/linker guru types to maybe answer some of this.

If this generates any code that tries to assign anything at run time to that memory address, my guess is it would fail and/or fault As that is not in the normal RAM memory space... And likewise if it is simply filling in memory at link time, well there is another object that fills in that memory at link time, so which one wins?

Did you try looking at what was actually generated like I mentioned in previous post using objdump? If I look at some simple program, the dump starts off like:
Code:
Disassembly of section .text:

00000000 <_VectorsFlash>:
       0:	00 80 00 20 bd 01 00 00 f5 19 00 00 ad 19 00 00     ... ............
      10:	ad 19 00 00 ad 19 00 00 ad 19 00 00 ad 19 00 00     ................
      20:	ad 19 00 00 ad 19 00 00 ad 19 00 00 f5 19 00 00     ................
      30:	f5 19 00 00 ad 19 00 00 35 1f 00 00 39 20 00 00     ........5...9 ..
      40:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      50:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      60:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      70:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      80:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      90:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      a0:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      b0:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      c0:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      d0:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      e0:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
      f0:	f5 19 00 00 a5 1a 00 00 f5 19 00 00 d1 1b 00 00     ................
     100:	f5 19 00 00 fd 1c 00 00 f5 19 00 00 f5 19 00 00     ................
     110:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     120:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     130:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     140:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     150:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     160:	f5 19 00 00 fd 07 00 00 f5 19 00 00 f5 19 00 00     ................
     170:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     180:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     190:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     1a0:	f5 19 00 00 f5 19 00 00 f5 19 00 00 f5 19 00 00     ................
     1b0:	f5 19 00 00 f5 19 00 00 f5 19 00 00                 ............
So 1A4 is initialized to the address: 0x19f5

For whatever reason these addresses have the lowest bit set, but if you search in the code for 19f4: you see:
Code:
000019f4 <unused_isr>:
	}
}

void unused_isr(void)
{
    19f4:	b508      	push	{r3, lr}
	fault_isr();
    19f6:	f7ff ffd9 	bl	19ac <fault_isr>
    19fa:	bf00      	nop
And so here is the unused_isr, which defaults for this vector and most of the others as you can see...

So after your magic three lines what is in your VectorFlash table?

Now again remember suppose you try to update this at run time. It will fail as this is FLASH memory AND this is NOT the vector table that is used by the system.
As shown by the code and mentioned earlier in this thread: https://forum.pjrc.com/threads/5329...to-digital-pin?p=185129&viewfull=1#post185129

In that posting, I showed that the system copied the table into a ram location and then updated the system register to point to this new location:
Code:
SCB_VTOR = (uint32_t)_VectorsRam;	// use vector table in RAM

So again if you wish to update it run time, you could simply call the function that uses the _VectorsRam array, or you cold reference the array yourself, OR
you could maybe look at the contents of SCB_VTOR and reference it's value to look up or store values in array. But warning I have not looked at the ARM reference manual to see if this register is readable...

But all of these methods are just making it more convoluted.

So again if you wish to have full control over this, you have the options of

Simply use that defined name for the vector...

Use the above methods to update it at run time,

Or keep trying different things like:
Edit the mk20dx128.c file and rename the function portc_isr to something else like My_f

I don't know if you could in your code if doing something like:
Code:
void My_f(void)		__attribute__ ((alias("portc_isr"))) {
    (your stuff)
}
Would work or not?

Good luck
 
Maybe best to give this a rest for a couple weeks? You really need to get Yiu's book to learn these finer points of the ARM Cortex-M architecture.
 
While I wait for the book.
I thought about using the IAR ARM version to understand the behavior of the registers in simulation mode.
And to make a first program to turn on and off an LED, with two FOR cycles to create a delay.
Apparently everything looks good in the simulation.
But when I do the Hex file, and I program it with the Teensy USB, the behavior is totally different, as if the FOR loop was not executed. It does not depend on the level optimization because I have tried all 4 possibilities (NONE-LOW-MEDIUM-HIGH), and the LED is always on.
With the registers I set mk20dx256 to 24Mhz.
And theoretically I also set the DEVICE specification in the IAR setup.
Does any of you have a simple program written with IAR that can help me understand what horrors I do?
 
Guys I need help .
After spending many hours reading a part of Joseph Yiu's book about M4 processor, I have to say that he often uses CMSIS-Core libraries in his examples, and completely ignores any form of Startup .s file. "Completely ignores" because hi use in his examples only ready Startup.s file .
The beginning of my Thread: Assigned IRQ interrupt to digital pin, I have finally discovered that memory spaces are set from the first startup.s file and loaded
*__vector_table, where the word WEAK is assigned to the IRQ NAME, allows it to be re-used in other forms.
After a huge effort (ignorance) trying to cut out the parts of startup.s files from examples found here and there, I managed to load a small program on IAR to turn on and off a Led with two while loops.
But I can not set any frequency of SYSTEM_CLOCK.
Comparing the same cycle of the 72Mhz MAIN loaded with TeensyDuino, what I do with the IAR program is much slower.
Setting the CPU Clock Frequency when it needs to be done?
I imagine immediately after the StartUp.s.
I can write it in C , and add as file with my main C , compiled just before it .


SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(1) | SIM_CLKDIV1_OUTDIV4(2);
SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(2) | SIM_CLKDIV2_USBFRAC_MASK;
OSC0->CR = OSC_CR_SC8P_MASK | OSC_CR_SC2P_MASK |OSC_CR_ERCLKEN_MASK ;
MCG->C7 = (uint8_t)0x00u;
MCG->C2 = MCG_C2_RANGE0(2) | MCG_C2_EREFS0_MASK;
MCG->C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(4) ;
MCG->C5 = MCG_C5_PRDIV0(7);
MCG_C6 |= (1u<<6) | (0x3<<0) ;

What is missing ? Where am I wrong?
I would use PLL to get 72MHz, with internal frequency reference.

What is the correct clock frequency setting process?

Please give me some Tips
 
Using T_3.2 as above?
PJRC code ...\hardware\teensy\avr\cores\teensy3\kinetis.h is a working solution for reference.
 
since I learned to use structures and enumerators, I tried to set up base on kinetis.h, and base on other MK20DX256.h files.
I believe that there must be a logical order of those instructions.
For example, if, first of all, do not block WDOG, nothing turns.
After it all you need to setup the processor clock frequency.
There are many combinations of possible frequency settings, but mine always seems to be the same or does not work.
Because there is a lot written in that book, but there is no concrete example of a startup.s with the details of why or how!
 
I would suggest the same as @defragster mentioned, in addition I would build example programs using Teensyduino and then use objdump to disassemble code and look to see the actual startup...
 
Back in msg #36 you said this was just for fun learning.

It is only for the pleasure to understand Learning do not have age .
It is not easy , I know it , but only because I didn’t get the gear jet .... but will become easy with the time .

Now you're asking for help specific to the IAR software.

After a huge effort (ignorance) trying to cut out the parts of startup.s files from examples found here and there, I managed to load a small program on IAR to turn on and off a Led with two while loops.
But I can not set any frequency of SYSTEM_CLOCK.
Comparing the same cycle of the 72Mhz MAIN loaded with TeensyDuino, what I do with the IAR program is much slower.

So I ask, did you actually buy the IAR software "only for the pleasure to understand Learning"?

My understanding is IAR is very expensive.

Or you're you're using a 30 day eval license? But really, who does that "only for the pleasure to understand" when the tool expires after only 1 month?
 
Thank you KurtE, I did not know this thing:
"" to use objdump to disassemble code and look to see the actual startup ... ""

Dear Poul, the most correct question would have been: Excuse me, what job do you do? Or what is your profession?
But I could also answer "The boy at the gas station (45 yesrs old boy)".
Like those I know who spend 1000 euros to build Drones alone and program the PID ...... return home.
Instead I am even simpler.
IAR offers the free version limited to 32K, but I can use as much as I want , do not expire .
I also had the opportunity to understand for example that GPIOC_PTOR in the simulator you see it change status only once and then never again. But that register in particular can only be read. Strange because the SIMULATOR also offers the possibility to change defaults. And confusing since you can't see ....so you can't see ! No even one time .......
But being ignorant, I get angry when I do not understand things , especially because I started from a simple function to enter into the vector table and now I find myself trapped because I can not make it run at the frequency that I want.
When I understand these rules, I go back to using TeensyDuino Software which is wonderful.
IAR- Keil - "Arduino" all have different systems to create a file of startup.s, different languages procedure, have differences between them.
I read a lot of that book you suggest , but I is not very useful for this moment problem , because those parts are not treated. The registers are described in detail, with the general operations, but then there is no mention of how to start Clock , not even in the example of the board hi use for his examples, nothing of the startup.s.
And Paul , that guy suggest to install IAR or Keil or GNU .
This are the reasons for all my questions.
Sorry, but if you want to do something, do you try it only once? Take a step back? Or do you try again and try again?
For example, looking, I found this file useful, that tomorrow I'll try to understand the way.

Code:
// If the internal load capacitors are being used, they should be selected
// before enabling the oscillator. Application specific. 16 pF and 8 pF selected
// in this example
 OSC_CR = OSC_CR_SC16P_MASK | OSC_CR_SC8P_MASK;
// Enabling the oscillator for 8 MHz crystal
// RANGE=1, should be set to match the frequency of the crystal being used
// HGO=1, high gain is selected, provides better noise immunity but does draw
// higher current
// EREFS=1, enable the external oscillator
// LP=0, low power mode not selected (not actually part of osc setup)
// IRCS=0, slow internal ref clock selected (not actually part of osc setup)
 MCG_C2 = MCG_C2_RANGE(1) | MCG_C2_HGO_MASK | MCG_C2_EREFS_MASK;
// Select ext oscillator, reference divider and clear IREFS to start ext osc
// CLKS=2, select the external clock source
// FRDIV=3, set the FLL ref divider to keep the ref clock in range
// (even if FLL is not being used) 8 MHz / 256 = 31.25 kHz
// IREFS=0, select the external clock
// IRCLKEN=0, disable IRCLK (can enable it if desired)
// IREFSTEN=0, disable IRC in stop mode (can keep it enabled in stop if desired)
 MCG_C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(3);
// wait for oscillator to initialize
 while (!(MCG_S & MCG_S_OSCINIT_MASK)){}
// wait for Reference clock to switch to external reference
 while (MCG_S & MCG_S_IREFST_MASK){}
// Wait for MCGOUT to switch over to the external reference clock
 while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2){}
// Now configure the PLL and move to PBE mode
// set the PRDIV field to generate a 4 MHz reference clock (8 MHz /2)
 MCG_C5 = MCG_C5_PRDIV(1); // PRDIV=1 selects a divide by 2
// set the VDIV field to 0, which is x24, giving 4 x 24 = 96 MHz
// the PLLS bit is set to enable the PLL
// the clock monitor is enabled, CME=1 to cause a reset if crystal fails
// LOLIE can be optionally set to enable the loss of lock interrupt

 MCG_C6 = MCG_C6_CME_MASK | MCG_C6_PLLS_MASK;
Clocking
Kinetis Quick Reference User Guide, Rev. 3, 05/2014
44 Freescale Semiconductor, Inc.
// wait until the source of the PLLS clock has switched to the PLL
 while (!(MCG_S & MCG_S_PLLST_MASK)){}
// wait until the PLL has achieved lock
 while (!(MCG_S & MCG_S_LOCK_MASK)){}
// set up the SIM clock dividers BEFORE switching to the PLL to ensure the
// system clock speeds are in spec.
// core = PLL (96 MHz), bus = PLL/2 (48 MHz), flexbus = PLL/2 (48 MHz), flash = PLL/4 (24
MHz)
 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(1)
 | SIM_CLKDIV1_OUTDIV3(1) | SIM_CLKDIV1_OUTDIV4(3);

// Transition into PEE by setting CLKS to 0
// previous MCG_C1 settings remain the same, just need to set CLKS to 0
 MCG_C1 &= ~MCG_C1_CLKS_MASK;
// Wait for MCGOUT to switch over to the PLL
 while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3){}
// The USB clock divider in the System Clock Divider Register 2 (SIM_CLKDIV2)
// should be configured to generate the 48 MHz USB clock before configuring
// the USB module.
 SIM_CLKDIV2 |= SIM_CLKDIV2_USBDIV(1); // sets USB divider to /2 assuming reset
 // state of the SIM_CLKDIV2 register


****************************************************************


If then this my asking disturbs.
I apologize
 
This approach is actually necessary if you don't use global variables to keep state. As I do more callback-based Arduino-style programming, I'm realizing that it's difficult, without jumping through lots of hoops, to make decisions based on program state because program state doesn't ever seem to be passed to the callback functions.

For example, I have an interrupt routine that needs to know which GPIO pin triggered it. This is hard to do using `attachInterrupt()` because it doesn't tell the callback which pin triggered it. Now, you might say that if you've set the ISR, you know the pin number. That's not true if the pin number is user-settable and unknown to the programmer creating the ISR.

So far, I'm using my own versions of `IntervalTimer` and `attachInterrupt()` for these reasons. An alternative is to use a different ISR for every possible pin. It gets ugly fast.
 
This approach is actually necessary if you don't use global variables to keep state. As I do more callback-based Arduino-style programming, I'm realizing that it's difficult, without jumping through lots of hoops, to make decisions based on program state because program state doesn't ever seem to be passed to the callback functions..
Might help to know which approach you are implying here...

And yes, there are several places in code that have had to deal with call backs wanting additional information, like which IO pin.

In some cases like this, you can try to grab the state of the different IO pins to try to figure out which one(s) were responsible.

Other times have done things like, setup some max number of IO pins that we will need to deal with. Then have a set of N ISR functions, and a table of N values. Could be pin numbers, or could be pointers to objects that contain the data, and when the Interrupt is triggered it then handles the simple ISR function, which then calls back to my Actual interrupt handler with the saved away data. (Or calls the ISR function of the stored away object...)

That way you don't have to make modified versions of lots of system functionality like IntervalTimer.
 
Status
Not open for further replies.
Back
Top