ATSAMD10D13AS alternating GPIO INPUT/OUTPUT

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the function below I want to first set the pins as OUTPUTs and make them LOW wait 10s and then make all pins INPUTs, then set some pins as OUTPUTs and make those pins HIGH wait 10s and finally set all pins to INPUT mode. However once this line executes 

REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);

the ports get set to INPUTs and they are never swapped to OUTPUT mode. When I'm in debugger mode everything seems to work fine so maybe I'm not initializing some value somewhere? I know some of the registers are atomic like OUTSET and |= operations etc can have weird behaviour, but I followed this tutorial and they seem to use |= &= etc when interacting with the PORTs directly (DIR0). 

 

Complete code

 

struct pinSettings{
	
    uint8_t number[10] = {0b01111101, 0b00000101, 0b01011011, 0b01001111, 0b00100111, 0b01101110, 0b01111110, 0b01000101, 0b01111111, 0b01101111};
    uint8_t segmentPinOrder[7] = {5, 6, 7, 8, 9, 14, 15};
    uint8_t clear = 0x00;
    
}pinsettings;


bool toggleNumber(uint8_t numberToDisplay){ 
    // PWM is on PIN 16 and it's generated properly
    myPWM.begin();
    numberToDisplay = pinsettings.number[numberToDisplay];
	
	uint32_t pinDirections = 0, pinState = 0;
	
	for (uint8_t i = 0; i < 7; ++i) {
	    // make all pins OUTPUTs and set them to LOW
		pinDirections |= (1 << pinsettings.segmentPinOrder[i]);
		pinState |= (1 << pinsettings.segmentPinOrder[i]);
	}
	// make the pins OUTPUTs and set them to LOW
	REG_PORT_DIR0 |= pinDirections;
	REG_PORT_OUT0 &= ~(pinState);
	
	myPWM.setOnPercent(45);
		
	DelayMs(10000);
	// make all pins INPUTs
	REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);
	pinDirections = 0x00;
	pinState = 0x00;
	
    for (uint8_t i = 0; i < 7; ++i) {
        if ((numberToDisplay >> i) & 1) {
	        // set some pins to OUTPUT
		pinDirections |= (1 << pinsettings.segmentPinOrder[i]);
		pinState |= (1 << pinsettings.segmentPinOrder[i]);
        }
    }
   // set pins to HIGH  
   REG_PORT_DIR0 |= pinDirections;
   REG_PORT_OUT0 |= pinState;
   // PWM = 55 to turn the display ON (45 OFF)
   myPWM.setOnPercent(55);
   DelayMs(10000);
	
    // set all pins to inputs 
    REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);
    //REG_PORT_DIRCLR0 = (PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);
	//DelayMs(60000 * 15);

    return true;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What's your build and upload process?
Your symptoms are strange, but they could be explained if your "pinsettings" structure isn't actually copied into the binary and subsequently into RAM during initialization.

 

A quick check MIGHT be to change it to

const struct pinSettings{
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the response, I tried to change it but the problem remains. To be honest I'm not sure what the building process is I didn't change anything in atmel studio and I'm using atmel ice to upload the code, everything should be default, the only thing I changed was turning optimisations off because I thought it might optimize out some variable. Maybe I'm missing something in the PWM code, for the display to work PWM is needed and I made pin 16 the PWM pin, so maybe 

REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);

This line the right side of the expression is uint16_t and the rest of the bits get set to random values? However when Tried to save the the MSB half of the register

REG_PORT_DIR0 &= (~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15) | 0xffff0000);

The problem was the same, but when I saved PIN 15 aswell everything seems to work ok. I even tried to save ONLY the value of PIN 15 and everything is working as expected.
 

REG_PORT_DIR0 &= (~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15) | 0x00008000);

This is the complete code, I don't need to save the value when I make the pins inputs the second time, this is getting really confusing :D

bool toggleNumber(uint8_t numberToDisplay){
	// PWM is on PIN 16 and it's generated properly
	myPWM.begin();
	numberToDisplay = pinsettings.number[numberToDisplay];

	uint32_t pinDirections = 0, pinState = 0;

	for (uint8_t i = 0; i < 7; ++i) {
		// make all pins OUTPUTs and set them to LOW
		pinDirections |= (1 << pinsettings.segmentPinOrder[i]);
		pinState |= (1 << pinsettings.segmentPinOrder[i]);
	}
	// make the pins OUTPUTs and set them to LOW
	REG_PORT_DIR0 |= pinDirections;
	REG_PORT_OUT0 &= ~(pinState);

	myPWM.setOnPercent(45);

	DelayMs(10000);
	// make all pins INPUTs
	REG_PORT_DIR0 &= (~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15) | 0x00008000);
	pinDirections = 0x00;
	pinState = 0x00;

	for (uint8_t i = 0; i < 7; ++i) {
		if ((numberToDisplay >> i) & 1) {
			// set some pins to OUTPUT
			pinDirections |= (1 << pinsettings.segmentPinOrder[i]);
			pinState |= (1 << pinsettings.segmentPinOrder[i]);
		}
	}
	// set pins to HIGH
	REG_PORT_DIR0 |= pinDirections;
	REG_PORT_OUT0 |= pinState;
	// PWM = 55 to turn the display ON (45 OFF)
	myPWM.setOnPercent(55);
	DelayMs(10000);

	// set all pins to inputs
	REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);
	//REG_PORT_DIRCLR0 = (PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);
	//DelayMs(60000 * 15);

	return true;
}

Just for clarity I'll post the PWM code aswell maybe I'm missing something 

Complete PWM code:
 

PWM::PWM(int freq) {

	_freq = freq;
	_period = 8000000/freq;
}

void PWM::begin(void) {

	PM->APBCMASK.reg |= PM_APBCMASK_TCC0; // Enables the APB clock inside Pwr Management for TCC0

	GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC |			// Improve duty cycle
	GCLK_GENCTRL_GENEN |		// Enable gen clock
	GCLK_GENCTRL_SRC_OSC8M |	// Select prefered source (8 MHz oscilator in this case)
	GCLK_GENCTRL_ID(4);			// Select GCLK ID, in this case GCLK4

	while(GCLK->STATUS.bit.SYNCBUSY); // Wait until clock bus is synced

	GCLK->GENDIV.reg =	GCLK_GENDIV_DIV(1) |	// Divide clock by (in this case 1)
	GCLK_GENDIV_ID(4);		// Apply to selected clock

	while(GCLK->STATUS.bit.SYNCBUSY); // Wait until clock bus is synced

	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |		// Enable gen clock
	GCLK_CLKCTRL_GEN_GCLK4 |	// Select clock
	GCLK_CLKCTRL_ID_TCC0;	// Feed the clock to wanted TCC

	while(GCLK->STATUS.bit.SYNCBUSY); // Wait until clock bus is synced

	TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV1_Val); // Divide counter by 1 giving 8 MHz (125 ns) on each TCC0 tick
	TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Use "Normal PWM" (single-slope PWM): count up to PER, match on CC[n]

	while(TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization

	TCC0->PER.reg = _period; // Set the period (the number to count to (TOP) before resetting timer)

	while(TCC0->SYNCBUSY.bit.PER); // Wait for synchronization

	// Configure PA18 to be output
	PORT->Group[0].DIRSET.reg = PORT_PA16;
	PORT->Group[0].OUTCLR.reg = PORT_PA16;

	// Enable the port multiplexer
	PORT->Group[0].PINCFG[16].reg |= PORT_PINCFG_PMUXEN;

	// Connect TCC0 timer to PA18. Function F is TCC0/WO[2] for PA18.
	// Odd pin num (2*n + 1): use PMUXO
	// Even pin num (2*n): use PMUXE
	PORT->Group[0].PMUX[8].reg = PORT_PMUX_PMUXE_F;

	// Enable output (start PWM)
	TCC0->CTRLA.reg |= TCC_CTRLA_ENABLE;
	while(TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization

}

void PWM::setOnPercent(float onPercent){
	if((onPercent >= 0) && (onPercent <= 100)) {

		TCC0->CC[2].reg = _period*onPercent/100; // Set starting PWM signal
		while(TCC0->SYNCBUSY.bit.CC2); // Wait for synchronization
	}
	else {

		TCC0->CC[2].reg = 0; // Set starting PWM signal to 0%
		while(TCC0->SYNCBUSY.bit.CC2); // Wait for synchronization
	}
}

Is pin 15 somehow connected to these PWM settings? 

Last Edited: Fri. Mar 11, 2022 - 08:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wildernessbagel wrote:
This line the right side of the expression is uint16_t

No I would say, why do you think so?

 

wildernessbagel wrote:
When I'm in debugger mode everything seems to work fine

It's hard to ignore this, is it still true? Can you really run it in the debugger and everything works without the  | 0x00008000 ? Or do you have to single step?

Otherwise, the fact that you need to add 0x00008000 should mean that you don't get what you expect from this code:

    for (uint8_t i = 0; i < 7; ++i) {
        if ((numberToDisplay >> i) & 1) {
            // set some pins to OUTPUT
            pinDirections |= (1 << pinsettings.segmentPinOrder[i]);
            pinState |= (1 << pinsettings.segmentPinOrder[i]);
        }
    }
    // set pins to HIGH
    REG_PORT_DIR0 |= pinDirections;
    REG_PORT_OUT0 |= pinState;

So PA15 has to be an output for it to work? HIGH or LOW? Your pins are only made outputs when HIGH so again, something has to be wrong above.

wildernessbagel wrote:
Is pin 15 somehow connected to these PWM settings?

Not that I can see.
/Lars

 

Last Edited: Sat. Mar 12, 2022 - 12:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No I would say, why do you think so

Yeah it isn't I forgot that the pins are defined as unsigned long.

It's hard to ignore this, is it still true? Can you really run it in the debugger and everything works without the  | 0x00008000 ? Or do you have to single step?

You are correct it only works when I use single step, honestly I thought there is no difference? What does single step change?

So PA15 has to be an output for it to work? HIGH or LOW?

PLEASE READ EDIT BEFORE THIS PART
Yep the pin needs to be an output, it works both when I force the pin HIGH or LOW (only then PIN15 stays always OFF or ON, which is expected). I want to make them outputs only when HIGH because I want to turn only those segments ON (I tried making all of them OUTPUTs and setting the ones I want OFF to LOW, but the same thing happens).

The code below for example doesn't work. However as soon as I uncomment //REG_PORT_DIR0 |= ((uint32_t)1 << 15); everything is fine again, even if I remove the ELSE in the for loop and pinState |= (1 << 15);. Furthermore I tried replacing 15 by all the pin numbers and it never works, it's as if something else is impacted when setting pin 15.

	// the code above stayed the same as in the previous post
        REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);
	//REG_PORT_DIR0 |= (1 << 15);
	pinDirections = 0x00;
	pinState = 0x00;

	for (uint8_t i = 0; i < 7; ++i) {
		if ((numberToDisplay >> i) & 1) {
			// set some pins to OUTPUT
			pinDirections |= (1 << pinsettings.segmentPinOrder[i]);
			pinState |= (1 << pinsettings.segmentPinOrder[i]);
		}else{
			pinDirections |= (1 << pinsettings.segmentPinOrder[i]);
			pinState |= (1 << pinsettings.segmentPinOrder[i]);
		}
	}
	pinState |= (1 << 15);
	// set pins to HIGH
	REG_PORT_DIR0 |= pinDirections;
	REG_PORT_OUT0 |= pinState;
	// PWM = 55 to turn the display ON (45 OFF)
	myPWM.setOnPercent(55);
	DelayMs(10000);

	// set all pins to inputs
	REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);

EDIT: 

I was wrong about it working even when PIN 15 is low, it seemed like it works because PIN15 controls the top segment (and is HIGH for 8,9,0 and then LOW when showing 1). The code worked because it started from 8, but now I noticed it gets stuck on 1.

So PIN15 is supposed to be HIGH and an output for everything to work as expected.

 

For example this would work, however then the pin would be HIGH when showing the number 4 aka it would be just wrong. But if I don't set pin15 to INPUT then everything works REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 /*| PORT_PA15*/);, this would work without  REG_PORT_DIR0 |= pinDirections | (1 << 15); REG_PORT_OUT0 |= pinState | (1 << 15);.

REG_PORT_DIR0 &= ~(PORT_PA05 | PORT_PA06 | PORT_PA07 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA15);
	REG_PORT_DIR0 |= (1 << 15);
	pinDirections = 0x00;
	pinState = 0x00;

	for (uint8_t i = 0; i < 7; ++i) {
		if ((numberToDisplay >> i) & 1) {
			// set some pins to OUTPUT
			pinDirections |= (1 << pinsettings.segmentPinOrder[i]);
			pinState |= (1 << pinsettings.segmentPinOrder[i]);
		}
	}
	// set pins to HIGH
	REG_PORT_DIR0 |= pinDirections | (1 << 15);
	REG_PORT_OUT0 |= pinState | (1 << 15);

 

Last Edited: Mon. Mar 14, 2022 - 11:09 AM