0

Accessing individual registers on the SAMD51
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Accessing individual registers on the SAMD51

by Warlock31415 on Fri Jul 05, 2019 3:46 pm

Hey, I have the Grand Central Metro M4 and I wanted to know if there was a SAMD51 equivalent to AVR's DDRA and PORTA. If so how exactly do you set the data direction and the pin state?

Thanks

Warlock31415
 
Posts: 7
Joined: Sun Jun 04, 2017 7:16 am

Re: Accessing individual registers on the SAMD51

by mckenney on Sat Jul 06, 2019 11:17 am

You're probably looking for

PORT->Group[0].OUT.reg
PORT->Group[0].DIR.reg

where "0" refers to port A. ("1" would be port B, and so on.)

There are also SET/DIR/Toggle registers. [See also Data Sheet (01507B) Section 32.7]

mckenney
 
Posts: 13
Joined: Sun Sep 30, 2018 11:09 pm

Re: Accessing individual registers on the SAMD51

by Warlock31415 on Sun Jul 07, 2019 12:42 pm

That worked. Thanks!

Are there any code examples? How did you figure it out? I've only done avr programming and the avr datasheet is very clear on how to access registers with code examples. The SAMD51 datasheet has no such thing

Warlock31415
 
Posts: 7
Joined: Sun Jun 04, 2017 7:16 am

Re: Accessing individual registers on the SAMD51

by mckenney on Sat Jul 13, 2019 11:42 pm

Atmel Studio (7) has a collection of examples. (Keyword "START").

The definitions follow CMSIS conventions. "atsamd51g19a.h" gives you the top level names, then right-click "Go to Implementation" to get the structure(s) in e.g. Port.h. Eventually you get to the names in the Data Sheet.

mckenney
 
Posts: 13
Joined: Sun Sep 30, 2018 11:09 pm

Re: Accessing individual registers on the SAMD51

by westfw on Sun Jul 14, 2019 8:17 pm

The definitions follow CMSIS conventions.


CMSIS is an ARM standard, the most important parts of which are:

  • Peripheral registers are accessed as memory (ARM doesn't have any separate "peripheral" instructions, so everything is located somewhere inside the 32bit address space.)
  • The memory space for each particular peripheral is described by a C "structure" whose start address is at the beginning peripheral. This is a very common technique for accesses memory-mapped peripherals, but may seem strange if you haven't seen it before. See http://www.open-std.org/jtc1/sc22/wg21/ ... _paper.pdf for example. The newer AVR chips are also setting things up like this.
  • ARM "standard" peripherals get names and content defined by ARM. This is things like the Systick timer and the Interrupt Controller.
  • Individual vendors have a lot of latitude on the details of their vendor-specific peripherals. Atmel has the convention that each peripheral register has a .reg and .bits union for each register - use the .reg to access the "full" register at once, use .bits to access individual bitfields within the register. (accessing bitfields can be somewhat prone to unexpected behavior, since the chip itself will need to access full registers.) Sometimes vendors do a poor (IMO) job.

All this usually involves some pretty ugly C code that is somewhat compiler dependent and causes Language experts to groan, but in general end users don't need to worry about that - the necessary include files for a particular compiler are provided, and they'll work.

So for PORT->Group[0].DIR.reg:
PORT is where all of the SAMD gpio ports start, defined in ...tools/CMSIS-Atmel/1.2.0/CMSIS/Device/ATMEL/samd51/include/samd51j19a.h:
Code: Select all | TOGGLE FULL SIZE
#define PORT              ((Port     *)0x41008000UL) /**< \brief (PORT) APB Base Address */

They're evenly spaced, and implemented as an array of PortGroup members (one for each of PORTA, etc), in
.../tools/CMSIS-Atmel/1.2.0/CMSIS/Device/ATMEL/samd51/include/component/port.h:
Code: Select all | TOGGLE FULL SIZE
typedef struct {
       PortGroup                 Group[4];    /**< \brief Offset: 0x00 PortGroup groups [GROUPS] */
} Port;

(Yeah: PORT is actually 4 ports, a single port is called a PortGroup, and what's in the datasheet as PORTA should be referred to as PORT.Group[0]. That's the sort of thing I meant by "Poor descisions.")

PortGroup is defined (also in port.h) with the individual registers:
Code: Select all | TOGGLE FULL SIZE
typedef struct {
  __IO PORT_DIR_Type             DIR;         /**< \brief Offset: 0x00 (R/W 32) Data Direction */
  __IO PORT_DIRCLR_Type          DIRCLR;      /**< \brief Offset: 0x04 (R/W 32) Data Direction Clear */
  __IO PORT_DIRSET_Type          DIRSET;      /**< \brief Offset: 0x08 (R/W 32) Data Direction Set */
  __IO PORT_DIRTGL_Type          DIRTGL;      /**< \brief Offset: 0x0C (R/W 32) Data Direction Toggle */
  __IO PORT_OUT_Type             OUT;         /**< \brief Offset: 0x10 (R/W 32) Data Output Value */
  __IO PORT_OUTCLR_Type          OUTCLR;      /**< \brief Offset: 0x14 (R/W 32) Data Output Value Clear */
  __IO PORT_OUTSET_Type          OUTSET;      /**< \brief Offset: 0x18 (R/W 32) Data Output Value Set */
  __IO PORT_OUTTGL_Type          OUTTGL;      /**< \brief Offset: 0x1C (R/W 32) Data Output Value Toggle */
  __I  PORT_IN_Type              IN;          /**< \brief Offset: 0x20 (R/  32) Data Input Value */
  __IO PORT_CTRL_Type            CTRL;        /**< \brief Offset: 0x24 (R/W 32) Control */
  __O  PORT_WRCONFIG_Type        WRCONFIG;    /**< \brief Offset: 0x28 ( /W 32) Write Configuration */
  __IO PORT_EVCTRL_Type          EVCTRL;      /**< \brief Offset: 0x2C (R/W 32) Event Input Control */
  __IO PORT_PMUX_Type            PMUX[16];    /**< \brief Offset: 0x30 (R/W  8) Peripheral Multiplexing */
  __IO PORT_PINCFG_Type          PINCFG[32];  /**< \brief Offset: 0x40 (R/W  8) Pin Configuration */
       RoReg8                    Reserved1[0x20];
} PortGroup;

And then each register is also defined in port.h (the __IO vs __I is elsewhere, though.)

Also note the comparatively huge number of individual registers assigned to each peripheral. And the unallocated space ("Reserved") that makes the arrays come out even. You can get pretty creative when you have a couple of gigabytes of address space to throw at things...

This all works pretty well, in the end. Note that the compiler will normally optimize away what looks like it might be inefficient ("array accesses just to find the port??"), so the resulting binary code is about as good as it could be if you did it any other way...
westfw
 
Posts: 1559
Joined: Fri Apr 27, 2007 1:01 pm
Location: SF Bay area

Please be positive and constructive with your questions and comments.