Skip to content
Snippets Groups Projects
Commit 9529ff5b authored by Bengt Ragnemalm's avatar Bengt Ragnemalm
Browse files

macros much updated

parent ffb82665
Branches
No related tags found
No related merge requests found
......@@ -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>
......
/*
* 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
......
......@@ -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...
......
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment