Non ASF-based port pin library

Discussions around product based on ARM Cortex M0+ core.

Moderator: nferre

pozz
Posts: 67
Joined: Fri Jun 13, 2014 2:55 pm

Non ASF-based port pin library

Wed Jul 02, 2014 4:32 pm

I implemented a very simple port pin library that doesn't use ASF, but only CMSIS header files that comes with ASF.
Of course, it is very simple: configure the pin as an output (without read-back... input buffer is disabled) or input (without any pull-up/down) or input with pull-up or input with pull-down.
What do you think?

Code: Select all

#include "samd20.h"  /* It's from ASF */
static inline int
pin_get_group(uint8_t pin)
{
	return pin / 32;
}

static inline int
pin_get_num(uint8_t pin)
{
	return pin % 32;
}

static inline void 
pin_set_output(uint8_t pin)
{
	uint8_t group = pin_get_group(pin);
	uint8_t pinnum = pin_get_num(pin);
	PORT->Group[group].PINCFG[pinnum].reg = 0;
	PORT->Group[group].DIRSET.reg = (1 << pinnum);
}
static inline void
pin_set_output_level(uint8_t pin, uint8_t level)
{
	uint8_t group = pin_get_group(pin);
	uint8_t pinnum = pin_get_num(pin);
	if (level) PORT->Group[group].OUTSET.reg = (1 << pinnum);
	else       PORT->Group[group].OUTCLR.reg = (1 << pinnum);
}

static inline void
pin_set_input(uint8_t pin)
{
	uint8_t group = pin_get_group(pin);
	uint8_t pinnum = pin_get_num(pin);
	PORT->Group[group].PINCFG[pinnum].reg = PORT_PINCFG_INEN;
	PORT->Group[group].DIRCLR.reg = (1 << pinnum);
}
static inline void
pin_set_input_pullup(uint8_t pin)
{
	uint8_t group = pin_get_group(pin);
	uint8_t pinnum = pin_get_num(pin);
	PORT->Group[group].PINCFG[pinnum].reg = PORT_PINCFG_INEN | PORT_PINCFG_PULLEN;
	PORT->Group[group].OUTSET.reg = (1 << pinnum);		/* Imposta pull-up */
	PORT->Group[group].DIRCLR.reg = (1 << pinnum);
}
static inline void
pin_set_input_pulldown(uint8_t pin)
{
	uint8_t group = pin_get_group(pin);
	uint8_t pinnum = pin_get_num(pin);
	PORT->Group[group].PINCFG[pinnum].reg = PORT_PINCFG_INEN | PORT_PINCFG_PULLEN;
	PORT->Group[group].OUTCLR.reg = (1 << pinnum);		/* Imposta pull-down */
	PORT->Group[group].DIRCLR.reg = (1 << pinnum);
}
static inline uint8_t
pin_get_input_level(uint8_t pin)
{
	uint8_t group = pin_get_group(pin);
	uint8_t pinnum = pin_get_num(pin);
	return 0 != (PORT->Group[group].IN.reg & (1 << pinnum));
}
pin parameter is 0 for PA0, 31 for PA31, 32 for PB0 and so on.
M14
Posts: 20
Joined: Thu Mar 27, 2014 10:51 am

Re: Non ASF-based port pin library

Thu Jul 03, 2014 2:24 pm

I did the same thing a while ago, with some differences :). In my opinion you should be able to configure a single pin to each configuration with a single function call.

I'm using the PORT_IOBUS when reading / writing an IO. I think you only should use PORT for low-power functionality.

Code: Select all


typedef enum {
	PD_NOT_USED = 0,
	PD_FLOATING = 0,
	PD_INPUT = 1,
	PD_OUTPUT = 2,
	PD_OUTPUT_READBACK = 3,
} E_portDirection;

typedef enum {
	PS_NOT_USED = 0,
	PS_FLOAT = 0,
	PS_PULL_DOWN = 1,
	PS_PULL_UP = 2,
} E_pullupSetting;

typedef enum {
	PM_A = 0,
	PM_B,
	PM_C,
	PM_D,
	PM_E,
	PM_F,
	PM_G,
	PM_H,
} E_pinMux;

typedef enum {
	PV_CLEAR = 0,
	PV_SET,
	PV_TOGGLE,			
} E_pinValue;



void Set_PortConfig(UINT8 pin, E_pinMux pinMux, E_portDirection portDirection, UINT8 pinVar, E_pullupSetting pullupSetting) {
	
	//unsigned char group = port / 32;
	
	if(pin > PIN_PB31) {
		// No valid port
		return;
	} else {
	
		PORT_WRCONFIG_Type wrconfig = { { 0 } };
		unsigned char group = pin / 32;
		
		if(group) {
			pin -= 32;
		}
		
		// Prepare WRCONFIG register data
		if(pin > 15) {
			// Upper 16 bits
			wrconfig.bit.HWSEL = 1;
			wrconfig.bit.PINMASK = (1 << (pin-16));
		} else {
			// Lower 16 bits
			wrconfig.bit.PINMASK = (1 << pin);
		}
		
		wrconfig.bit.WRPINCFG = 1;
		wrconfig.bit.WRPMUX = 1;
		
		if(pinMux == PM_A) {
			// Normal pinfunction
			wrconfig.bit.PMUX = PM_A;
			
			if(portDirection >= PD_OUTPUT) {
				// Set port as output
				Set_PortPin((pin + group*32), pinVar);
				PORT->Group[group].DIRSET.reg = (1 << pin);
			} else {
				// Set port as input
				PORT->Group[group].DIRCLR.reg = (1 << pin);
				
				// Enable input synchronizer. Necessary when using IO_BUS
				PORT->Group[group].CTRL.reg |= (1 << pin);
			
				// Set/clear pullup
				if(pullupSetting == PS_PULL_UP) {
					PORT->Group[group].OUTSET.reg = (1 << pin);
				} else {
					PORT->Group[group].OUTCLR.reg = (1 << pin);
				}
			}
			
		
			if(		(portDirection == PD_INPUT)
				||	(portDirection == PD_OUTPUT_READBACK) 
			) {
				// Enable input
				wrconfig.bit.INEN = 1;			
			}
		
			if(pullupSetting > PS_FLOAT) {
				// Enable pullup
				wrconfig.bit.PULLEN = 1;
			}
				
		} else {
			// Set other port function
			
			wrconfig.bit.PMUX = pinMux;
			wrconfig.bit.PMUXEN = 1;
		}
		
		PORT->Group[group].WRCONFIG = wrconfig;		
		
	}
}
/*****************************************************************************
**
*****************************************************************************/

void Set_PortPin(UINT8 pin, UINT8 pinValue) {
		
	if(pin > PIN_PB31) {
		return;
	} else {
		
		unsigned char group = pin / 32;
		
		if(group) {
			pin -= 32;
		}
		
		switch(pinValue) {
		case PV_CLEAR: 
			PORT_IOBUS->Group[group].OUTCLR.reg = (1 << pin);
			break;
		case PV_SET:
			PORT_IOBUS->Group[group].OUTSET.reg = (1 << pin);
			break;
		case PV_TOGGLE:
			PORT_IOBUS->Group[group].OUTTGL.reg = (1 << pin);
			break;
		default:
			PORT_IOBUS->Group[group].OUTCLR.reg = (1 << pin);
			break;
		}
	}
}
/*****************************************************************************
**
*****************************************************************************/

unsigned char Get_PortPin(UINT8 pin) {
	
	if(pin > PIN_PB31) {
		return 0;
		} else {
		
		unsigned char group = pin / 32;
		
		if(group) {
			pin -= 32;
		}
		
		return (PORT_IOBUS->Group[group].IN.reg >> pin) & 0x01;
	}	
}
/*****************************************************************************
**
*****************************************************************************/
I do have one question :). I put this code in a separate c/h file. When I want to declare the functions as an inline function, I get the error 'undefined reference'. How could I solve this?
pozz
Posts: 67
Joined: Fri Jun 13, 2014 2:55 pm

Re: Non ASF-based port pin library

Thu Jul 03, 2014 4:10 pm

M14 wrote:I did the same thing a while ago, with some differences :). In my opinion you should be able to configure a single pin to each configuration with a single function call.
It's a matter of tastes :-)
M14 wrote:I'm using the PORT_IOBUS when reading / writing an IO. I think you only should use PORT for low-power functionality.
In the past I read about the ARM single cycle instructions for low latency reading/writing of pins state, but I forgot it in my implementation.
Thank you for the suggestion, I'll try to implement IOBUS access in my library.
M14 wrote: I do have one question :). I put this code in a separate c/h file. When I want to declare the functions as an inline function, I get the error 'undefined reference'. How could I solve this?
If you want to compile all the functions as inline, you could put all of them in .h file. The compiler is able to generate inline instructions only if they are in the same translation unit, so the inline functions implementation should be in the .h file. You don't need .c file.

Take a look at ASF files where many inline functions are implemented.

Return to “SAM D20 Cortex-M0+ MCU”

Who is online

Users browsing this forum: No registered users and 1 guest