SAMD21 switching a pin direction from input to output

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

I've been working on this issue for 2 days now. I am trying to use a DS1302 RTC with a SAMD21 xplained pro. The DS1302 has a 3-wire protocol (CE, Data line & Clock line). I seem to be having an issue with reading the data line. I suspect the issue is when I change the port pin from output in input. Are there any special considerations when switching a pin direction from input to output?


void set_data_port_direction(direction_t dir){
	
	if(dir == INPUT){  	  
	 
	  PORT->Group[1].PINCFG[1].reg = PORT_PINCFG_INEN | PORT_PINCFG_PULLEN;
		
	} else {
	  REG_PORT_DIRSET1 = DATA;
	}

 }

The DS1302 has an Arduino Library, so hooking it up to my logic analyzer, I see a good read looks like this:

 

 

 

This topic has a solution.

"When all else fails, read the directions"

Last Edited: Sat. Jul 15, 2017 - 02:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

What is "DATA"?
Also, I imagine there should be a write to DIRCLR1 to change a pin from output to input.

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

ezharkov wrote:
What is "DATA"?

 #define DATA	PORT_PB01

ezharkov wrote:
Also, I imagine there should be a write to DIRCLR1 to change a pin from output to input.

 

I changed the code to:

 

void set_data_port_direction(direction_t dir){
	
	if(dir == INPUT){
	  	  
	  REG_PORT_OUTCLR1 = DATA;
	  PORT->Group[1].PINCFG[1].reg = PORT_PINCFG_INEN | PORT_PINCFG_PULLEN;
	  	
	} else {

		REG_PORT_DIRSET1 = DATA;
	}
 }

My output looks like this:

 

 

 

The read cycle should return a value of 0x19, but is different every time.

 

Here is reading from an Arduino (working).

 

 

Here us my read function:

 

uint8_t _readByte()
{	 
	set_data_port_direction(INPUT);
	
	uint8_t value = 0;
	uint8_t currentBit = 0;
	
	for (int i = 0; i < 8; ++i)
	{
		currentBit = (PORT->Group[1].IN.reg & PORT_PB01);
		value |= (currentBit << i);		
			
		REG_PORT_OUTSET1 = SCLK; 
		delay_us(1);			
		REG_PORT_OUTCLR1 = SCLK; 
			
	}
	return value;
}

Thanks for the help.

"When all else fails, read the directions"

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

I don't think that you need to change PINCFG at all to change pin direction; you can enable the pullup and the input buffer all the time, and JUST change the DIR register.

I'd feel better if you were consistent WRT "REG_" vs "PORT->" notations for accessing the port registers, but they look OK.

But this doesn't explain why it's not working...

Do you need another delay_us(1) ?  Your currently don't have much time between dropping the clock and reading the next bit.

I'd think that the fact that the data is wiggling while you're doing the read IS a sign that you've switched port direction...

 

 

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

westfw wrote:
I don't think that you need to change PINCFG at all to change pin direction;
 

Are you sure? When I simply set the pin direction, the SAMD21 no longer reads the pin value. However I don't need to enable the pull up.

PORT->Group[1].PINCFG[1].reg = PORT_PINCFG_INEN ;// works

I am getting closer. I am able to confirm that I can set the date and time but can not read correctly. I set the date and time to an odd date on the SAMD21 and then when I connect the DS1302 to an arduino, the date is what I set it too.

So it seems, I am back to my read issue. As you suggested I screwed with the delays which when I change definitely effects the read values. I've been looks at the data sheet...

"When all else fails, read the directions"

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

Got it working...well partially. I narrowed down the issue to this function:

 

uint8_t _readByte()
{
    set_data_port_direction(INPUT);

    uint8_t value = 0;
    uint8_t currentBit = 0;

    for (int i = 0; i < 8; ++i)
    {
        currentBit = (PORT->Group[1].IN.reg & DATA) > 0;
        value |= (currentBit << i);
        REG_PORT_OUTSET1 = SCLK;
        delay_us(10);
        REG_PORT_OUTCLR1 = SCLK;
    }    

    delay_us(10);  // weird!  

    return value;
}

I have to add a 10 ~us delay to the clock and after I do 8 readings (burst). Here is the strange bit. I have 2 DS1302s.  One of the DS1302s likes the delay:

 

    delay_us(10);  // weird!

And the other one doesnt. If I remove it, one of the DS1302s work and vice versa. Strange! Any thoughts?

"When all else fails, read the directions"

Last Edited: Sun. Jul 9, 2017 - 11:07 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Are you sure?

You'd have to have INEN *ON* all the time, and no, I'm not sure :-(  The arduino pinMode() function does as you are doing, for instance.

 

I have to add a 10 ~us delay

The way I read the datasheet, any delays needed should be less than one microsecond.  In your photos, is the timescale actually the same on the Arduino vs yourCode versions?  It looks like they're operating at about the same speed, while I'd expect your code to be significantly faster (the Arduino code uses digitalWrite(), which is ... not fast.)

       delay_us(10);
        REG_PORT_OUTCLR1 = SCLK;
       delay_us(1);  // are you sure you don't need a delay here?
    }

 

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

westfw wrote:
In your photos, is the timescale actually the same on the Arduino vs yourCode versions?

Its close but not exact.

 

westfw wrote:
delay_us(1); // are you sure you don't need a delay here?

 

I tried your suggestions but the result was the same (e.g worked for one DS1302 and not the other). I will keep working on it and post my findings.

 

Thx for the Help!

"When all else fails, read the directions"

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

westfw wrote:
delay_us(1); // are you sure you don't need a delay here?

Ultimately you were correct, the clock needed to have a nice even smooth pulse.. The devil was in the timing, which as expected needed to be pretty spot on. In addition, I didn't need to set the DATA pin to low when changing pin directions. According to my measurements, the call to set the pin LOW was about ~8us. This was causing the read timing issue to be off just enough to make me loose my mind.

After making these adjustments, everything worked perfect. I was able to port the code over to my SAM4S xplained pro. All I needed to do was change the pins and register calls.

SAMD21 Example

ds1302 files can be found here on my github account.

An example implementation can be found here.

SAM4S Example

ds1302 files can be found here on my github account

An example implementation can be found here

 

"When all else fails, read the directions"

Last Edited: Sun. Jul 16, 2017 - 05:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

great!  thanks for providing "closure."