Passing pin to a function

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

Is there a way to tell a function which pin I want to use so I can change it's state inside of the function?

Basically, I want to mimic BASCOMs SOUND function. It is defined in BASCOM as SOUND pin, duration, pulses.
So far I've done the code below, and it is working (it sounds the same as the function from bascom), but I've hardwired the output pin (Spkr).

typedef struct {
  unsigned char bit0:1;
  unsigned char bit1:1;
  unsigned char bit2:1;
  unsigned char bit3:1;
  unsigned char bit4:1;
  unsigned char bit5:1;
  unsigned char bit6:1;
  unsigned char bit7:1;
} io_reg;

#define Spkr				( (volatile io_reg*)(&PORTD) )->bit7

// mimics BASCOM SOUND function
void sound(uint16_t duration, uint16_t pulses) {

	// Delay function based on AVR-libc "_delay_loop_2"
	// with 2 NOPs, so it sounds the same as the sound function of BASCOM
	void my_delay(uint16_t __count) {
		__asm__ volatile (
			"1: sbiw %0,1"	"\n\t"
			"nop"			"\n\t"
			"nop"			"\n\t"
			"brne 1b"
			: "=w" (__count)
			: "0" (__count)
		);}

	while (duration-- > 0) {
		Spkr = 1;
		my_delay(pulses);
		Spkr = 0;
		my_delay(pulses);
	}
}

So, how can I make a C function like this?

void sound(one pin, uint16_t duration, uint16_t pulses) {
...
	while (duration-- > 0) {
		pin = 1;
		my_delay(pulses);
		pin = 0;
		my_delay(pulses);
	}
}

Thank you

Felipe Maimon

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

You cannot pass a pointer to a bit... so the best you can do is pass the bit reference (0-7, or mask), and a pointer to the port (or the port's address). The function will have to de-reference it from there.

you could encode the port address, and bit reference into a single value if you liked.

ONEPIN = (((unsigned int)(&PORTn))<<3) + BITy

you will then need to decode this in your function back into the port address, and pin reference

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

You will need to pass both the port and the pin number to the function. The latter is easy as that can be an ordinary integer parameter (eg a char). Passing a port as a parameter to a function is readily described in the documentation: http://www.nongnu.org/avr-libc/u...

While you're there, why not browse through the whole FAQ? Knowing a little about whats in it will help you immensely in your coming endeavours. Someone put lot of energy into writing it just for you ;)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

So it might be better to stay how it is right now with the output pin hardwired.

Thank you glitch

Felipe Maimon

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

To expand on glitch's post, you could also pass a structure that wraps the pin number and port address, something like,

typedef struct Pin{
uint8_t pinNo;
volatile uint8_t* portAddr;
};

Then recode the function to process the information,

void sound(Pin pin, uint16_t duration, uint16_t pulses) {
...
   while (duration-- > 0) {
      *pin.portAddr |= 1 << pin.pinNo;
      my_delay(pulses);
      *pin.portAddr &= ~( 1 << pin.pinNo );
      my_delay(pulses);
   }
} 

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

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

@Johan

Lol. I've been just reading the next item in the FAQ (What registers are used by the C compiler), and must have missed this one... Thank you for pointing it out.

Felipe Maimon

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

Most welcome!

BTW, I wasn't being sarcastic (or playing the *sshole game). The FAQ is really quite good and well worth a read from beginning to end.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

That's quite a good idea, mckeemj! But how can I pass it without creating a Pin variable?

Can I call it with something like this?

void sound((Pin){&PORTD,7}, 25, 100)

Felipe Maimon

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

fmaimon wrote:
That's quite a good idea, mckeemj! But how can I pass it without creating a Pin variable?

Can I call it with something like this?

void sound((Pin){&PORTD,7}, 25, 100)

if you're going to pass it like that, then why not just write the function to take 2 parameters, and pass PORT and PIN individually. It will actually be more efficient.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

So I did it. Now its like this:

// mimics BASCOM SOUND function
void sound(volatile uint8_t* port, uint8_t mask, uint16_t duration, uint16_t pulses) {

	// Delay function based on AVR-libc "_delay_loop_2"
	// with 2 NOPs included to match BASCOMs delay inside
	// SOUND function
	void my_delay(uint16_t __count) {
		__asm__ volatile (
			"1: sbiw %0,1"	"\n\t"
			"nop"			"\n\t"
			"nop"			"\n\t"
			"brne 1b"
			: "=w" (__count)
			: "0" (__count)
		);}

	while (duration-- > 0) {
		*port |= (mask);
		my_delay(pulses);
		*port &= ~(mask); 
		my_delay(pulses);
	}
}

And I defined the speaker pin as:

#define Speaker_pin			&PORTC, _BV(PC4)

So I call it like this:

	sound(Speaker_pin, 12, 5760);

:lol:

Thank you all!

Felipe Maimon

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

Certainly creating a structure leads to a pin variable. However, that isn't really much different than a #define. One advantage is that the code is a bit clearer, or I think so. With a function declared to take four arguments and called with what appears to be three, it seems there is the possibility of confusion.

That said, glad you got things figured out. The most important thing is that the code does what you want, followed closely by it being understandable to who ever has to maintain it.

Martin Jay McKee

P.S. With regards to efficiency, although I've never done this with C, I've done some similar things with C++ and the compiler has done an excellent job optimizing it down to sbi/cbi instructions. It may help to use some const declarations and making the struct variable static would be a good step, but I would expect the same efficiency here.

As with most things in engineering, the answer is an unabashed, "It depends."