[ATSAM] Interupt handling from scratch - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: August 27, 2019, 11:48:36 am
content: So having finally worked out how to address memory with pointers (thankyou to those that helped) I can now forgoe the manufacturers header files and use my own defines to just write/read the register memory locations.So the next hurdle in this series is interupts. These too are address specific, so how do I get the compiler to put my interupt servicing routine at the right address?

author: ataradov
author_rank: Super Contributor
timestamp: August 27, 2019, 05:28:18 pm
content: You do it in a linker script. Pretty minimal example herehttps://github.com/ataradov/mcu-starter-projects/tree/master/samc21, as usual. Actual functions can br located anywhere, but the vector table is fixed.Also, not using manufacturer header files (or generated manually from SVD files) is a really bad idea.

author: Simon
author_rank: Unknown
timestamp: August 27, 2019, 05:39:56 pm
content: OK, thank you but what is the linker and how did you create one?

author: ataradov
author_rank: Super Contributor
timestamp: August 27, 2019, 07:19:16 pm
content: https://en.wikipedia.org/wiki/GNU_linkerThere is plenty of documentation on that stuff. You can start with the site mentioned in the article.The linker script is herehttps://github.com/ataradov/mcu-starter-projects/blob/master/samc21/linker/samc21j18.ldMine is derived from the stock one.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 06:45:35 am
content: OK so what have you actually done as I see most of the files are as supplied. How are your examples different from a fresh project in AS. Basically I am trying to have a project were I can setup my own accesses to the registers (memory) without clasting with existing naming in the atmel files.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 08:18:26 am
content: Accessing registers and implementing interrupts are totally independent. Yes, I'm using vendor supplied header files for the code, because why would not I?The vector stuff is located in the startup file and a linker script. Those files are created from scratch.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 09:07:11 am
content: Yes so what I am trying to do is avoid the register definitions as I'm finding it easy enough to just err, address the registers by address. But I need to be able to do interrupts.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 10:24:36 am
content: So what is the actual problem? Pointers to the interrupt vectors are located in the table "void (* const vectors[])(void)". This table is placed at the beginning of the flash memory using a linker script (samc21j18.ld). None of this requires any header files.You will need to re-implement functions NVIC_*() to actually work with the interrupt controller itself, if you don't want ARM core headers either.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 11:26:46 am
content: How do you mean work with the interrupt controller itself? I just need to be able to handle the various interrupts.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 11:32:13 am
content: You need to first understand how interrupts work on ARM controllers. You have a vector table, which lists pointers to all the interrupt handler functions. They are simple C functions, nothing fancy. This table has to be located at the beginning of the flash memory. You can relocate it, but don't worry about that now, just assume that it has to be at the beginning.The interrupt controller will fetch those vectors when it needs to execute an interrupt.The interrupt controller itself looks just like any other peripheral (TC or SERCOM, for example). You can work with it using functions you normally use to access registers.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 12:04:36 pm
content: Well all the data sheet does is list the interrupt line numbers and there is only one per periphery so how different events on the same periphery trigger a different interrupt is a mystery to me.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 12:06:25 pm
content: Yes, there is only one handler per peripheral. It is up to the application to read the interrupt status register and figure out what specific event caused the interrupt. That's what INTFLAG register is for.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 12:08:24 pm
content: So either way I do it I would always have to check the status register and execute whatever code is relevant?

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 12:10:07 pm
content: If you only enable one interrupt source (via INTENSET), then you don't have to check it, you will know that only one thing would have caused the interrupt. But generally, yes, you need to look at all possibly enabled flags and see if one (or multiple) of them are active.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 12:44:09 pm
content: OK, so basically it's just a case of getting the compiler to put the code for each handler in the right address. What is NVIC_*() ? Can't I just copy out the interrupt handling stuff?

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 01:40:04 pm
content: No, the actual code can go anywhere you or compiler likes. All the pointers to the handlers are collected in one table.NVIC_*() are standard functions for working with NVIC. You can look at what they do and copy that code, of course. Again, you are going against industry best practices, but ultimately it is up to you.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 02:57:57 pm
content: Well what is the magic code for accessing registers. These are not published and the headers are double Dutch to me as there are so many levels.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 03:00:20 pm
content: It is the same code we discussed in the other thread in great detail.There is no magic, they are memory mapped registers, you access them the same way as you do now for the peripherals.NVIC registers are published and documented in the core documentation. They are a part of the core.But you will be better off learning how to work with header files.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 03:32:52 pm
content: The AVR mega 0 series use struct variables that loosely follow the data sheet. "Peripheral name""instance number"."register name".The SAMC does not follow this logic. Indeed, it takes a massive debate to work out what has been done and given that I suspect that the headers are for ASF and not human consumption they all have very long-winded and non memorable names. If they have really done that then I'd prefer my own names that I can at least remember. But yes it would be nice to be able to just use the headers. 1.5 days trying to figure out what they have done. 0.5 days to figure out pointer memory addressing and get pins toggling. Here and now with what Atmel/Microchip offer I can't even figure out how to toggle a pin!

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 03:43:04 pm
content: We may be talking about different things or you are looking at some other header files.Atmel header files are best in the industry, IMO.And they follow the same general structure as for AVRs.No idea why you are having so many problems with them.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 04:51:29 pm
content: well PORTA.DIR would work on AVR mega 0 series. It does not on SAMC, that's all I am saying.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 04:55:29 pm
content: You are fixating on ports too much. They are described differently. It is not a big deal.But we are going in circles.

author: SiliconWizard
author_rank: Super Contributor
timestamp: August 28, 2019, 04:58:27 pm
content: Maybe you should try Forth instead...

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 05:02:28 pm
content: Quote from: ataradov on August 28, 2019, 04:55:29 pmWell Ports is where anyone would start. The easiest way af knowing if I have got the chip working at the most basic level is to toggle a pin. So where as on AVR all the registers follow the same format the SAMC ones have two style? so how are the ports refered to?

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 05:07:29 pm
content: Again, did not we discuss them to death already?I offered you to use my macros for working with ports. The way those macros are implemented show you how to use ports. It is syntactically different, but basically the same as with any other MCU. It will be hard to do anything if you are that picky about small details like that.Also, you came up with your own macros. What happened to them? Why is this an issue again?Don't like how header files describe ports? Don't use that part. But use them for other peripherals that are more traditional. Including NVIC itself.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 05:14:30 pm
content: ideally I would like to understand how the atmel headers work but after over a day of looking i am still confused. The AVR had one header file that put all registers into periphery structs. There are dozens of header files and I'm struggling.If i write my own macro's/headers I will unwitingly clash with something pre existing so I guess I am wasting my time writing my own stuff as I'd have to rewrite everything.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 05:19:40 pm
content: There is one file per peripheral. This file describes the peripheral registers. Those pretty struts. They are independent of any addresses or locations.Then there is one file for each instance of the peripheral. This one describes a particular instance, so it has addresses in it.And then there is a file per device that pulls all of that together.Seems pretty logical to me. I really don't understand why you are having so much trouble figuring that out.And once you get used to that, you don't even nerd to read those files. Most of the names just match names from the datasheet.

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 05:23:55 pm
content: Well As is probably obvious I'm not that skilled in programming but I won't become so by reading books about coding for x86 platforms.So in the SAC wu have: REG_PORT_DIR0 sure, it's there and i can use it, it I remember it. The port is now 0 instead of A, i guess i can try and remember that. and it starts with REG, i can see why but I would not have bothered. The 0 is on the end of DIR, so that reads as the DIR "0" register intead of the DIR register of port "0".But yea i can use those.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 05:29:25 pm
content: Why do you need those REG_xxx definitions? I never used them in my life.Just use structure format, and it will be much more readable and logical: "PORT->Group.DIR.reg". Yes, you need to remember that 0 is A. You can create your own defines for that, and the code will read "PORT->Group.DIR.reg".And PORT is really the only peripheral that is this "illogical".

author: Simon
author_rank: Unknown
timestamp: August 28, 2019, 05:47:07 pm
content: Quote from: ataradov on August 28, 2019, 05:29:25 pmWell where do i find anything different?, I followed the links through form the SAMC21.h header file.

author: ataradov
author_rank: Super Contributor
timestamp: August 28, 2019, 05:57:17 pm
content: Just so we are talking about the same stuff. Here is a set of files I'm talking abouthttps://github.com/ataradov/mcu-starter-projects/tree/master/samc21/includeHere you see samc21.h, which includes one of the part-specific files. Each part specific file includes header files from "components" and "instance" directories.And later you see it actually define all the peripherals. For example here is how PORT is defined:Code:#define PORT ((Port *)0x41000000UL)Here you can see an address cast to "Port" type. The definition of that type is located in the "component/port.h" file included earlier.There you can see that Port is a structure that contains an array of PortGroup members:Code:typedef struct {PortGroup Group; /**< \brief Offset: 0x00 PortGroup groups  */} Port;. And you can follow that to see what PortGroup is. It is actually the structure that defines all the PORT registers.

author: techman-001
author_rank: Frequent Contributor
timestamp: August 29, 2019, 02:13:43 am
content: Quote from: SiliconWizard on August 28, 2019, 04:58:27 pmI'm not aware of any decent Open Source Forth for ATSAM because the flash controller only supports 256-bytes-at-once writes. The designer of Mecrisp-Stellaris informed me of this last March when I asked if he had considered porting to ATSAM.Note: Mecrisp-Stellaris internally uses individual 2 byte writes (=opcode size!) on STM32 Flash.Sample Forth interrupt handler code for Mecrisp-Stellaris on STM32F0----------------------------------------------------------------------------------------------: tim6.int.handlerdo.stuff;['] tim6.int.handler irq-tim6_dac ! \ Tie the interrupt handler to the interruptSTM32F051 interrupt list reference------------------------------------------------interrupt exti0_1interrupt exti2_3interrupt exti4_15interrupt tscinterrupt dma_ch1interrupt dma_ch2_3interrupt dma_ch4_5interrupt adcinterrupt tim1_upinterrupt tim1_ccinterrupt tim2interrupt tim3interrupt tim6_dacinterrupt tim14interrupt tim15interrupt tim16interrupt tim17interrupt i2c1interrupt i2c2interrupt spi1interrupt spi2interrupt usart1interrupt usart2interrupt cec_can

author: ataradov
author_rank: Super Contributor
timestamp: August 29, 2019, 04:59:12 am
content: Quote from: techman-001 on August 29, 2019, 02:13:43 amThis is not true. It has to erase 256-byte row at once, but it can program any number of bytes. This is the same as with any other flash-based MCU.There is a limit that only 8 writes can happen into a row before erase is necessary, but this can mostly be skipped for debugging. I don't know why Forth needs to write things into Flash, but hopefully it does not do that after application development is done.

author: westfw
author_rank: Super Contributor
timestamp: August 29, 2019, 06:03:53 am
content: Interrupts on a CortexM3 are "vectored" through a vector table at the beginning of memory. It has a separate vector for each interrupt that can occur.The vector table is usually filled in by the startup file, startup_samc21.h, which will be included in builds automatically unless you take steps to prevent it.By default, the table is full of "weak" symbols to a dummy default interrupt function.So far, this is pretty muchexactlythe way that the AVR interrupts work (they are also vectored, by a table in the startup code, to default functions.)The difference is that on an AVR, you have to use the special ISR macro, because interrupt service routines need some extra code to save processor state. On an ARM, it turns out that the hardware will save exactly the same state when an interrupt occurs as the function call ABI requires, so an ISR function isexactly the sameas a normal function!So all you have to do to create an ISR function on ATSAM is create a function with the correct name. The compiler and linker will do the rest.As for ENABLING an interrupt, there is an extra level that needs to be done.Enable interrupts at the CPU level. Use the__enable_irq()function that is provided. This is the same assei()on the AVR.Enable the interrupts in the specific peripheral you're using. Each peripheral has anINTENSETandINTENCLRregister defined in those structures we've been trying to get you to use, or aREG_periph_INTENSET/etc symbol is the less desirable "instance/*.h files. This is the same as setting the xxIEn bit in some peripheral status register on an AVR (except instead of a couple bits in a single status register, you probably have 3 separate registers that do nothing but manipulate or read the status of interrupt enables.)(this is the part that is new on the ARM) You must also enable the interrupt within the NVIC (Nested Vectored Interrupt Controller.) The NVIC is part of the ARM definition of the CPU, so it won't appear in the Atmel include files, and probably not in the Atmel Documentation, either. It has a bunch of 32bit registers, with each bit corrosponding to a particular vector, and some additional registers for setting priorities (which you can mostly ignore.) The NVIC doesn't know about peripherals at all, only interrupt numbers. So while the NVIC registers are defined in the ARM include files, the interrupt numbers are defined in the samc21xxx.h file (periperalname_IRQn) There are also some standard functions for manipulating the NVIC.An ATSAM chip really has WAY too many registers to define all their values manually. You're looking at 61 register for the PORT (gpio), and some more to fiddle with the NVIC and clock for that port. You really do need to figure out how to use the pre-defined symbols...Which particular interrupt are you trying to configure? pin change interrupts, since you were talking about gpio?

author: techman-001
author_rank: Frequent Contributor
timestamp: August 29, 2019, 06:17:37 am
content: Quote from: ataradov on August 29, 2019, 04:59:12 am"This is not true. It has to erase 256-byte row at once, but it can program any number of bytes."Thank you for the feedback Ataradov, I'll pass that along to the Mecrisp-Stellaris Forth author. Does this apply to all SAM versions and would you mind giving a Assembly example of a 2 byte write if you have the time ?"I don't know why Forth needs to write things into Flash"Mecrisp-Stellaris Forth compiles to Ram or to Flash as the programmer decides.My workflow is to develop in Ram until I'm satisfied with the function I'm creating, then I'll 'compiletoflash' so that after a MCU reset I don't have to reload everything again. If a function I'm developing in Ram needs modification I just reset the MCU and upload again ... and again until it's right, then I put it in Flash.This technique is very fast as the serial source uploads at 460800 Baud and uses hardware handshaking. Everything is orchestrated from my VIM editor make button.There really is no such thing as a generic "Forth" because in embedded they are all different, even Mecrisp-Stellaris varies between different MCU's of the same manufacturer depending on hardware capabilities, particularly Flash.The premier Open Source Forth for PIC (in my opinion), FlashForth can only compile to Ram for instance."hopefully it does not do that after application development is done: ?It certainly can, that's up to the developer. Perhaps they may require data be saved in the Flash for some reason. Mecrisp-Stellaris can easily check the remaining Flash to see if there is adequate room before performing such a write to Flash. It does this normally anyway when the developer "compilestoflash".

author: westfw
author_rank: Super Contributor
timestamp: August 29, 2019, 06:25:04 am
content: QuoteRemember that Forth is an interpreter. You can define new words and things after the main part of your application development is done, and they'll be written to flash to save them.QuoteAre you sure? I looked at the SAMC datasheet, and it's certainly possible to read it such that the 256byte-row-at-a-time, or at best 64byte-page-at-a-time writes are necessary. It's not clear whether leaving bytes set to 0xFF when you write a page will permit those bytes to be written with non-FF values later (or exactly how you'd do that WRT "write page" command.)

author: ataradov
author_rank: Super Contributor
timestamp: August 29, 2019, 07:21:40 am
content: Yes, I'm 100% sure. You can write individual bytes or bits. Just write 0xff where you don't want things to be changed.Otherwise the note about 8 writes into a row would not make any sense.

author: techman-001
author_rank: Frequent Contributor
timestamp: August 29, 2019, 09:52:47 am
content: Quote from: ataradov on August 29, 2019, 07:21:40 amI passed your feedback to the Mecrisp-Stellaris designer and he replied with the following questions:Must the Flash always be written in ascending or descending order ?Can it not be mixed by writing to random locations in Atmel SAME70 ?I would be glad to know which Atmel SAM... chips may suffer from this limitations and which do not.

author: ataradov
author_rank: Super Contributor
timestamp: August 29, 2019, 10:05:52 am
content: You need to write 64 bytes for a write page command, but if you set bytes that you want to keep to 0xff, it will work.SAM E70 is a bit different. It has ECC, so you must write in 64-bit chunks and with 64-bit alignment. But you can still do partial writes keeping in mind this limitation. This is explicitly described in the datasheet in the section about partial writes.

author: techman-001
author_rank: Frequent Contributor
timestamp: August 30, 2019, 06:35:56 am
content: Quote from: ataradov on August 29, 2019, 10:05:52 amThanks again, turns out that the SAM he was using to port his Forth to was a E70!He tells me that he may dig out some alternative SAMS and have another go, so there may yet be a Mecrisp-Stellaris Forth for SAM, just not the E70