From 9529ff5bc2a894426980d082b23e27bacdc93d09 Mon Sep 17 00:00:00 2001 From: Bengt Ragnemalm <bengt.ragnemalm@liu.se> Date: Fri, 17 Dec 2021 21:02:48 +0100 Subject: [PATCH] macros much updated --- ArduinoZeroTemplate.cproj | 16 ++++ src/ArduinoZeroTemplate.c | 65 +++++++++++++--- src/ArduinoZeroTemplate.h | 19 ++--- src/macros.h | 156 +++++++++++++++++++++++++++++++------- 4 files changed, 202 insertions(+), 54 deletions(-) diff --git a/ArduinoZeroTemplate.cproj b/ArduinoZeroTemplate.cproj index 1e0c0d5..9ff2316 100644 --- a/ArduinoZeroTemplate.cproj +++ b/ArduinoZeroTemplate.cproj @@ -240,6 +240,22 @@ <board id="board.user_board.samd21g" value="Add" config="" content-id="Atmel.ASF" /> </framework-data> </AsfFrameworkConfig> + <avrtool>com.atmel.avrdbg.tool.atmelice</avrtool> + <avrtoolserialnumber>J41800033620</avrtoolserialnumber> + <avrdeviceexpectedsignature>0x10010305</avrdeviceexpectedsignature> + <avrtoolinterface>SWD</avrtoolinterface> + <com_atmel_avrdbg_tool_atmelice> + <ToolOptions> + <InterfaceProperties> + <SwdClock>2000000</SwdClock> + </InterfaceProperties> + <InterfaceName>SWD</InterfaceName> + </ToolOptions> + <ToolType>com.atmel.avrdbg.tool.atmelice</ToolType> + <ToolNumber>J41800033620</ToolNumber> + <ToolName>Atmel-ICE</ToolName> + </com_atmel_avrdbg_tool_atmelice> + <avrtoolinterfaceclock>2000000</avrtoolinterfaceclock> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <ToolchainSettings> diff --git a/src/ArduinoZeroTemplate.c b/src/ArduinoZeroTemplate.c index ad37bf4..803b1ec 100644 --- a/src/ArduinoZeroTemplate.c +++ b/src/ArduinoZeroTemplate.c @@ -1,8 +1,8 @@ /* * ArduinoZeroTemplate.c * - * Created: 2021-12-09 - * Author: Bengt Ragnemalm + * Created: 2021-11-04 14:11:05 + * Author: Bengt */ #include <asf.h> @@ -29,12 +29,53 @@ volatile uint8_t rx_buffer[MAX_RX_BUFFER_LENGTH]; static uint8_t write_buffer[DATA_LENGTH]; static uint8_t read_buffer[DATA_LENGTH]; */ +uint32_t readport; +uint32_t slask; void ArduinoZeroTemplate(void) { while (true) { - + /* + #define PORT_INPUT_PIN_EN(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINMASK(pin)].bit.INEN = 1 // Enable input buffer (needed to be able to read a pin) + #define PORT_SET_CTRLSAMPLING(pin) PORT->Group[GROUPNR(pin)].CTRL.reg |= PINMASK(pin); // Enable continuous sampling of a pin group (always in groups of 8) + #define PORT_CLR_CTRLSAMPLING(pin) PORT->Group[GROUPNR(pin)].CTRL.reg &= ~PINMASK(pin); // Remove continuous sampling of a pins group + #define PORT_PULL_PIN_EN(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINMASK(pin)].bit.PULLEN=1 // Enable pull on a pin (Typ 40 kohm). Data value defines pull-up (1) or pull-down (0). Not on PA24 and PA25 + #define PORT_PULL_PIN_DIS(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINMASK(pin)].bit.PULLEN=0 // Disable pull on a pin + #define PORT_PULL_PIN_DRIVE_STRONG(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINMASK(pin)].bit.DRVSTR=1 // Enable strong drive strength on a pin. (~3mA, twice for VCC=3.6V). Not on PA24 and PA25 + #define PORT_PULL_PIN_DRIVE_WEAK(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINMASK(pin)].bit.DRVSTR=1 // Disable strong drive strength on a pin. (~1mA, twice for VCC=3.6V) + */ + PORT_INPUT_PIN_EN(PIN_PB10); + PORT_SET_CTRLSAMPLING(PIN_PB10); + PORT_CLR_CTRLSAMPLING(PIN_PB10); +PORT->Group[GROUPNR(PIN_PB10)].CTRL.reg = 0; + PORT_PULL_PIN_EN(PIN_PB10); + PORT_PULL_PIN_DIS(PIN_PB10); + PORT_PULL_PIN_DRIVE_STRONG(PIN_PB10); + PORT_PULL_PIN_DRIVE_WEAK(PIN_PB10); + + /* + #define PORT_SET_PIN(pin) PORT->Group[GROUPNR(pin)].OUTSET.reg = PINMASK(pin) + #define PORT_CLR_PIN(pin) PORT->Group[GROUPNR(pin)].OUTCLR.reg = PINMASK(pin) + #define PORT_TOGGLE_PIN(pin) PORT->Group[GROUPNR(pin)].OUTTGL.reg = PINMASK(pin) + #define PORT_PIN_TO_OUTPUT(pin) PORT->Group[GROUPNR(pin)].DIRSET.reg = PINMASK(pin) + #define PORT_PIN_TO_INPUT(pin) PORT->Group[GROUPNR(pin)].DIRCLR.reg = PINMASK(pin) + #define PORT_PIN_TOGGLE_DIRECTION(pin) PORT->Group[GROUPNR(pin)].DIRTGL.reg = PINMASK(pin) + #define PORT_READ_PORT(pin) (PORT->Group[GROUPNR(pin)].IN.reg // Returns entire port (all pins) + #define PORT_READ_PIN1(pin) (PORT->Group[GROUPNR(pin)].IN.reg & PINMASK(pin)) >> (pin & 31) // Returns pin value 0 or 1. + #define PORT_READ_PIN(pin) (PORT->Group[GROUPNR(pin)].IN[PINMASK(pin)].bit) >> (pin & 31) // Returns pin value 0 or 1. + */ + PORT_SET_PIN(LED_0_PIN); + PORT_CLR_PIN(LED_0_PIN); + PORT_TOGGLE_PIN(LED_0_PIN); + PORT_PIN_TO_INPUT(LED_0_PIN); + PORT_PIN_TO_OUTPUT(LED_0_PIN); + PORT_PIN_TOGGLE_DIRECTION(LED_0_PIN); + readport=PORT_READ_PORT(PIN_PA00); + readport=PORT_READ_PIN1(PIN_PA00); +// readport=PORT_READ_PIN(PIN_PA00); + slask = readport; + slask++; }// end while (true) infinite loop }// end ArduinoZeroTemplate @@ -58,21 +99,21 @@ void ArduinoZeroTemplateInit(void) // Before writing to any port registers, enable constant input sampling on pins that shall be able to use the faster IOBUS. // Note: Only Data Output Value, Data Input Value and Pin Direction registers can be accessed through IOBUS operation. - PORT_SET_CTRLSAMPLING(LED0_PIN_PORTNR,LED_0_PIN); - PORT_SET_CTRLSAMPLING(MY_OUTPUT_PIN_PORTNR,MY_OUTPUT_PIN); - PORT_SET_CTRLSAMPLING(MY_INPUT_PIN_PORTNR,MY_INPUT_PIN); + PORT_SET_CTRLSAMPLING(LED_0_PIN); + PORT_SET_CTRLSAMPLING(MY_OUTPUT_PIN); + PORT_SET_CTRLSAMPLING(MY_INPUT_PIN); // Configure output pins // To in all circumstances avoid glitches, set the default value of the pin before setting the direction. - PORT_CLR_PIN(LED0_PIN_PORTNR,LED_0_PIN); // LED - PORT_OUTPUT_PIN(LED0_PIN_PORTNR,LED_0_PIN); - PORT_SET_PIN(MY_OUTPUT_PIN_PORTNR,MY_OUTPUT_PIN); // MY_OUTPUT - PORT_OUTPUT_PIN(MY_OUTPUT_PIN_PORTNR,MY_OUTPUT_PIN); + PORT_CLR_PIN(LED_0_PIN); // LED + PORT_PIN_TO_OUTPUT(LED_0_PIN); + PORT_SET_PIN(MY_OUTPUT_PIN); // MY_OUTPUT + PORT_PIN_TO_OUTPUT(MY_OUTPUT_PIN); // Configure input pins // All IN-buffers for pins used as inputs must be enabled. This is controlled through the PINCFG.INEN bit - PORT_INPUT_PIN(MY_INPUT_PIN_PORTNR,MY_INPUT_PIN); // MY_INPUT - PORT_INPUT_PIN_EN(MY_INPUT_PIN_PORTNR,MY_INPUT_PIN); +// PORT_PIN_TO_INPUT(MY_INPUT_PIN); // MY_INPUT +// PORT_INPUT_PIN_EN(MY_INPUT_PIN); // End I/O configuration diff --git a/src/ArduinoZeroTemplate.h b/src/ArduinoZeroTemplate.h index 5972c34..7f1ec71 100644 --- a/src/ArduinoZeroTemplate.h +++ b/src/ArduinoZeroTemplate.h @@ -102,19 +102,12 @@ Available for user // Definitions // I/O - // Definitions used for the pin group numbers needed if using the faster IOBUS I/O method. (Can be used for normal PORT as well) - // Group number for PORTA and PORTB - #define PORTA_NR 0 - #define PORTB_NR 1 - - #define LED_0_PIN PORT_PB08 // LED - #define LED0_PIN_PORTNR PORTB_NR - - #define MY_OUTPUT_PIN PORT_PA20 // MY_OUTPUT - #define MY_OUTPUT_PIN_PORTNR PORTA_NR - - #define MY_INPUT_PIN PORT_PB02 // MY_INPUT - #define MY_INPUT_PIN_PORTNR PORTB_NR + // When using the macros in macro.h all I/O is defined using the PIN nr definitions. (Ex PIN_PA00). + // These definitions are all pins through all ports in a serie. PA31=31 and PB00 = 32. + // If accessing a PORT, there are no definition for PORTA only. Use any of PINxx_PORT. + #define LED_0_PIN PIN_PB08 // LED + #define MY_OUTPUT_PIN PIN_PA20 // MY_OUTPUT + #define MY_INPUT_PIN PIN_PB02 // MY_INPUT // And so on for all pins... diff --git a/src/macros.h b/src/macros.h index 939eae1..022b532 100644 --- a/src/macros.h +++ b/src/macros.h @@ -3,38 +3,136 @@ * * Created: 2021-11-04 14:11:05 * Author: benra10 - */ +About writing to I/O +==================== +All I/O are placed in the PORT peripheral which has one or more groups 0, 1, 2... (which is the same as port A, B, C and so on) +and are therefore accessed (using pointers which should always be the case) something like this: +PORT->Group[GROUPNR].REGISTER.reg = PINMASK; +Which port to access (PA00, PB00, PC00...) is defined by the group number. +For PORTA,PA02 GROUPNR = 0 and PINMASK = b100. +For PORTA,PB02 GROUPNR = 1 and PINMASK = b100. + +To access a pin in a register there are also a pin union: +PORT->Group[0].PINCFG[1].bit.DRVSTR = 1; + +To make the code easy to read we want something like SET_PIN(pinnr); +But to do so we need to from the variable pinnr get both the group number and the pin mask. +By using the pin number for all ports sequentially (0-63 for PORTA and PORTB and so on) and not just pin 0-31 for each port +it is easy to get the group by just shifting the pin number 5 steps. (pin>>5) +Atmel has already defined such definitions (PIN_Pxyy) for example PIN_PA00. +But we also need to get the pin mask from the same definition. That is done like this: 1 << (PIN_Pxyy & 31) +For example for pin PB02: +PIN_PB02 = 34 +Group = 34>>5 = 1 +PINMASK = 1 << (34 & 31) = 1 << (2) = 2 +These numbers can now be put into the example above in place of GROUPNR and PINMASK +PORT->Group[GROUPNR].REGISTER.reg = PINMASK; +PORT->Group[PIN_PB02>>5].REGISTER.reg = 1 << (PIN_PB02 & 31); + +To make it easier to read the Pin Number and Pin Mask are made with two separate macros. + +#define GROUPNR(pin) (pin>>5) +#define PINMASK(pin) (1 << (pin & 31)) + +Then these two macros are used inside a set of macros that access the various registers. +For example to set a pin: +#define PORT_SET_PIN(pin) PORT->Group[GROUPNR(pin)].OUTSET.reg = PINMASK(pin) + +The only drawback with these macros is that you can't modify more than one pin or bit at a time. +Otherwise the pin mask can be a mask of many bits at once. For this reason the macro is named +SET_PIN and not OUTSET. + +================= +The above method is what I find the most general method of writing to PORT registers but there +are other ways to write to the PORT registers. + +Method 1. Method as described above that access the PORT and its groups using pointers. +Method 2. ARM Cortex M0+ has a local but between CPU and I/O called IOBUS that gives the +possibility to access as I/O registers in one clock cycle. The registers that can be +accessed through the IOBUS are: +Pin Direction (DIRCLR, DIRSET, DIRTGL), +Data Output Value (OUTCLR. OUTSET, OUTTGL) and +Data Input Value (IN) registers. +To read registers using IOBUS it is necessary to enable Continuous Sampling of all pins +(in groups of 8) that this method is used to in the CTRL register. This consumes some +power but many times you can enable Continuous Sampling, do I/O intensive parts of the code +and turn off constant sampling again. +To only use DIR and OUT registers Continuous Sampling is not needed!!! +Note that to make real use of the IOBUS some compiler optimization is needed or most +of the quicker access is wasted. +Syntax is the same but using the PORT_IOBUS definition. +PORT_IOBUS->Group[GROUPNR].REGISTER.reg = PINMASK + +Method 3. Group individual method. It uses the defined names REG_PORT_xxx to access individual +registers for each port. You can say that it bypasses the group structure of PORT and instead +defines separate names for each port group. It is not compatible with Cortex M0+ IOBUS. +Example for accessing register named REGISTER for the pin defined by PINMASK. +REG_PORT_REGISTERx = PINMASK; - x is the group number. +Example to set pin PB02: +REG_PORT_OUTSET1 = PORT_PB02; +It works but as you can see that you must yourself take care of the group because the macro +combined the port and the group in the same definition. +Recommendation: Use method nr 1 and 2. + +Read more here about writing to ports: +https://electronics.stackexchange.com/questions/139117/atmels-arm-programming-without-asf +https://community.atmel.com/forum/getting-started-arm-3 +https://www.avrfreaks.net/forum/considerations-using-iobus-port-access + +The PORT definitions are found in (example for SAMD21G18A) samd21g18au.h and port.h + +All PORT registers can be accessed using bit fields. Using those and a mask makes it possible to +set several pins at once. +*/ + +#define GROUPNR(pin) (pin>>5) +#define PINMASK(pin) (1 << (pin & 31)) +#define PINMASK_8_GROUP(pin) (0xFF << ( 8*((pin & 31)/8) )) // pin&32 => pin=0-32. Divide by 8 => pin=0-4. Skift FF up 8x that. +#define PINNR(pin) (pin & 31) + +// Basic PORT registers. These are not defined with bitfields in ASF and can only be accessed word-wise. +// These registers can also can be accessed using IOBUS. (IOBUS only exist in Cortex M0+) +#define PORT_SET_PIN(pin) PORT->Group[GROUPNR(pin)].OUTSET.reg = PINMASK(pin) +#define PORT_CLR_PIN(pin) PORT->Group[GROUPNR(pin)].OUTCLR.reg = PINMASK(pin) +#define PORT_TOGGLE_PIN(pin) PORT->Group[GROUPNR(pin)].OUTTGL.reg = PINMASK(pin) +#define PORT_PIN_TO_OUTPUT(pin) PORT->Group[GROUPNR(pin)].DIRSET.reg = PINMASK(pin) +#define PORT_PIN_TO_INPUT(pin) PORT->Group[GROUPNR(pin)].DIRCLR.reg = PINMASK(pin) +#define PORT_PIN_TOGGLE_DIRECTION(pin) PORT->Group[GROUPNR(pin)].DIRTGL.reg = PINMASK(pin) +#define PORT_READ_PORT(pin) PORT->Group[GROUPNR(pin)].IN.reg // Returns entire port (all pins) +#define PORT_READ_PIN1(pin) (PORT->Group[GROUPNR(pin)].IN.reg & PINMASK(pin)) >> (pin & 31) // Returns pin value 0 or 1. +#define PORT_READ_PIN(pin) PORT->Group[GROUPNR(pin)].IN.bit.IN[PINNR(pin)]=1 // Returns pin value 0 or 1. + +// Same as above but using IOBUS. (Only in Cortex M0+) +#ifdef PORT_IOBUS + #define PORT_IOB_SET_PIN(pin) PORT_IOBUS->Group[GROUPNR(pin)].OUTSET.reg = PINMASK(pin) // Set to 1; Time at 48 MHz is 190nsec + #define PORT_IOB_CLR_PIN(pin) PORT_IOBUS->Group[GROUPNR(pin)].OUTCLR.reg = PINMASK(pin) + #define PORT_IOB_TOGGLE_PIN(pin) PORT_IOBUS->Group[GROUPNR(pin)].OUTTGL.reg = PINMASK(pin) + #define PORT_IOB_PIN_TO_OUTPUT(pin) PORT_IOBUS->Group[GROUPNR(pin)].DIRSET.reg = PINMASK(pin) + #define PORT_IOB_PIN_TO_INPUT(pin) PORT_IOBUS->Group[GROUPNR(pin)].DIRCLR.reg = PINMASK(pin) + #define PORT_IOB_PIN_TOGGLE_DIRECTION(pin) PORT_IOBUS->Group[GROUPNR(pin)].DIRTGL.reg = PINMASK(pin) + #define PORT_IOB_READ_PIN1(pin) (PORT_IOBUS->Group[GROUPNR(pin)].IN.reg & PINMASK(pin)) >> (pin & 31) // Returns pin value 0 or 1. +#endif + +// The remaining PORT registers. All of these have the individual bit names defined as bit fields. +// Therefore they can be accessed bitwise or as a word. These registers can not be accessed using IOBUS. +#define PORT_INPUT_PIN_EN(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINNR(pin)].bit.INEN = 1 // Enable input buffer (needed to be able to read a pin) +#define PORT_SET_CTRLSAMPLING(pin) PORT->Group[GROUPNR(pin)].CTRL.reg |= PINMASK(pin); // Enable continuous sampling of a pin group the pin belongs to. (Always in groups of 8) +// To disable continous sampling, all eight bits/pins of that group must be cleared simultaneously. +#define PORT_CLR_CTRLSAMPLING(pin) PORT->Group[GROUPNR(pin)].CTRL.reg &= ~PINMASK_8_GROUP(pin); // Remove continuous sampling of a pins group +/* WRCONFIG must be handled with special �handling and is therfore not defined normally. +#define PORT_PULL_PIN_EN(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINNR(pin)].bit.PULLEN=1 // Enable pull on a pin (Typ 40 kohm). Data value defines pull-up (1) or pull-down (0). Not on PA24 and PA25 +#define PORT_PULL_PIN_DIS(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINNR(pin)].bit.PULLEN=0 // Disable pull on a pin +#define PORT_PULL_PIN_DRIVE_STRONG(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINNR(pin)].bit.DRVSTR=1 // Enable strong drive strength on a pin. (~3mA, twice for VCC=3.6V). Not on PA24 and PA25 +#define PORT_PULL_PIN_DRIVE_WEAK(pin) PORT->Group[GROUPNR(pin)].PINCFG[PINNR(pin)].bit.DRVSTR=0 // Disable strong drive strength on a pin. (~1mA, twice for VCC=3.6V) +*/ + +// Tip: Registers that has no groups are much easier to access. Example: SYSCTRL->VREG.bit.RUNSTDBY = 1 + #ifndef MACROS_H_ #define MACROS_H_ - -// Macros for writing to I/O: -// There are many ways to write to PORT registers. -// Method 1. Simplest PORT method. Example for setting PA20 on PORTA. Note PORT_PA20 and not PIN_PA20 -// PORTA.OUTSET.reg = PORT_PA20 -// Method 2. Quicker method of PORT. Not sure if it is much faster than method 1 but it is more "compatible" with method nr 3. -// port_nr is in this case 0 for PORTA, 1 for PORTB and so on. -// PORT->Group[port_nr].OUTSET.reg = PORT_PA20 -// Method nr 3. Much quicker method that uses the direct access IOBUS. (If any optimization is enabled, otherwise difference is smaller). -// PORT_IOBUS->Group[port_nr].OUTSET.reg = PORT_PA20 -// Drawback is that for using IOBUS the constant sampling of used pins (in groups of 8) must be enabled. -// This consumes some power. -// Recommendation: Use method nr 2 and 3. Nr 2 if need to use minimum of power and 3 if need for maximal speed. -// Note: Data sheet says that only Data Output Value, Data Input Value and Pin Direction registers can be accessed through IOBUS operation. -// But practically test has shown that all PORT register can be accessed. For example PINCFG. -// Read more here about writing to ports: -// https://electronics.stackexchange.com/questions/139117/atmels-arm-programming-without-asf -// https://community.atmel.com/forum/getting-started-arm-3 -// https://www.avrfreaks.net/forum/considerations-using-iobus-port-access - -#define PORT_SET_PIN(port_nr,port_pin) PORT_IOBUS->Group[port_nr].OUTSET.reg = port_pin // Set to 1; Time with 40MHz is 190nsec -#define PORT_CLR_PIN(port_nr,port_pin) PORT_IOBUS->Group[port_nr].OUTCLR.reg = port_pin // Set to 0 -#define PORT_TOGGLE_PIN(port_nr,port_pin) PORT_IOBUS->Group[port_nr].OUTTGL.reg = port_pin // Toggle pin -#define PORT_OUTPUT_PIN(port_nr,port_pin) PORT_IOBUS->Group[port_nr].DIRSET.reg = port_pin // Set pin to output -#define PORT_INPUT_PIN(port_nr,port_pin) PORT_IOBUS->Group[port_nr].DIRCLR.reg = port_pin // port.DIRCLR.reg = pin // Set pin to input -#define PORT_INPUT_PIN_EN(port_nr,port_pin) PORT->Group[port_nr].PINCFG[port_pin].bit.INEN = 1 // Enable input buffer (needed to be able to read a pin) -#define PORT_READ_PIN(port_nr,port_pin) PORT_IOBUS->Group[port_nr].IN[port_pin] // Read a pin +/* #define PORT_READ(port_nr) PORT->Group[port_nr].IN.reg // Read a port #define PORT_SET_CTRLSAMPLING(port_nr,port_pin) PORT->Group[port_nr].CTRL.reg |= port_pin; // Enable continuous sampling of a pin group (always in groups of 8) #define PORT_CLR_CTRLSAMPLING(port_nr,port_pin) PORT->Group[port_nr].CTRL.reg &= ~port_pin; // Remove continuous sampling of a pin group @@ -42,7 +140,7 @@ #define PORT_PULL_PIN_DIS(port_nr,port_pin) PORT->Group[port_nr].PINCFG[port_pin].PULLEN=0 // Disable pull on a pin #define PORT_PULL_PIN_DRIVE_STRONG(port_nr,port_pin) PORT->Group[port_nr].PINCFG[port_pin].DRVSTR=1 // Enable strong drive strength on a pin. (~3mA, twice for VCC=3.6V). Not on PA24 and PA25 #define PORT_PULL_PIN_DRIVE_WEAK(port_nr,port_pin) PORT->Group[port_nr].PINCFG[port_pin].DRVSTR=1 // Disable strong drive strength on a pin. (~1mA, twice for VCC=3.6V) - +*/ // Tip: Example of other register or variable fiddling: SYSCTRL->VREG.bit.RUNSTDBY = 1 #endif /* MACROS_H_ */ \ No newline at end of file -- GitLab