It would be nice to use the standard Ada features for declaring interrupt handler routines. Ideally this looks like
protected UART is procedure Receive_Handler; pragma Attach_Handler(Handler, Ada.Interrupts.Names.UTXR); end UART;
Unfortunately, we have not yet reached that easiness. But at least we managed to get a working interrupt handler by mimicking the gcc-C way of attaching an interrupt routine.
procedure Receive_Handler; pragma Machine_Attribute (Entity => Receive_Handler, Attribute_Name => "signal"); pragma Export (C, Receive_Handler, "__vector_11");
The interrupt routine must be a parameterless procedure at the library level. The both pragmas Machine_Attribute and Export must immediately follow the specification of the procedure. The pragma Machine_Attribute takes two parameters, the first one is the name of the interrupt handler routine, the second one is the string "interrupt" or "signal" including the quotes. The external name declared in pragma Export is built by adding the index number (-1) of the corresponding interrupt and the characters "__vector_". The interrupt index number has to be looked up in the data sheet of the controller in use.
Please note, the vector_-number is one less than the one in the data sheet. We use the avr-gcc linker scripts that start counting at 0!
For example you'd like to use the "Rx Complete" interrupt of the ATMega16. The data sheet reads:
Vector No. | Address | Source | Interrupt Definition |
---|---|---|---|
12 | $0016 | USART, RXC | USART, Rx Complete |
Your code should look like this:
procedure Receive_Handler; pragma Machine_Attribute (Entity => Receive_Handler, Attribute_Name => "signal"); pragma Export (C, Receive_Handler, "__vector_11"); -- OFF-BY-ONE! 12 - 1
The device description specs are generated directly from Atmel's XML files. Here we provide explicit names for all interrupts that match the interrupt names in the data sheets by prepending Sig_ and appending _String to the name. Then the code simplifies to
procedure Receive_Handler; pragma Machine_Attribute (Entity => Receive_Handler, Attribute_Name => "signal"); pragma Export (Convention => C, Entity => Receive_Handler, External_Name => MCU.Sig_USART0_Rx_String);
See the example in apps/pwm_int_demo that uses similar code.
Be careful when specifying the parameters for pragma Machine_Attribute. Even though you can use the named notation, the parser does not care. Entity must be the first parameter and the attribute string must be the second!
For including the interrupt routine in the final binary program you either have to “with“ it in your main program or you manually add the object file at the linker command.