write / send each bit of a char to a single PIN

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

Hi there,
I would like to write each bit of a char (could be anything else like int or double...) to a single pin almost like I would send it over a serial port... So far the only thing I can think about is

for(i=0;i<8,i++){
    temp = (((_BV(i) & char)>>i )<<5); // I want to write PINB5
    PORTB |= temp;
    _delay_us(1);
    PORTB ^= _BV(5);
    _delay_us(1);
}

Do you know a more efficient way to do that ?
I work with avr-libc, avrdude, atmega328p

Thanks in advance for your help

Olivier

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

We have another thread on this very subject going: https://www.avrfreaks.net/index.p...

Read that one first.

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

Quote:

We have another thread on this very subject going: https://www.avrfreaks.net/index.p... ... p;t=111615

Read that one first.


Well, that thread is about input, isn't it?

Olivier, perhaps you could expand just a bit on the use of this. Of what use is an arbitrary bit stream with no timing information? Usually there would also be a clock. Search for "bit-banged SPI", or you could use the SPI peripheral itself with a "dummy" clock on SCK. A forum search for "74hc595" might also uncover pertinent information.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Lee! OK, not the very same subject,perhaps. But.. In that thread there are discussions about how to get "each bit of a byte" out and do something with it.

I presented two variants, and Chuch tossed in a third.

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

Hi there,
yes the advised discussion is about input and I need output.
I will describe more precisely my needs, but first, the following works okay:

void send_char_to_pin(char c, int pinNumber){
  int i;
  for(i = 0; i < 8; i++){ 
    PORTB |= (((_BV(i) & c) >> i) << pinNumber);
    _delay_us(20);
    PORTB &= ~(_BV(pinNumber)); 
    _delay_us(20);
  }
}

I say okay as in not perfect or just works.
my need are the following. from a PC I send serial frames that have to be decoded on the uC. By decoded I mean information contained in serial frame have to be changed according to communication protocol requirements of my target device. So basically I do protocol conversion / translation. Speed is not of great concern since my target device (ASIC) works from very low data rates, say 100Hz, to 20MHz. Though I would not mind getting close to 20MHz.
The result of my translation is stored in variables and char are convenient since information send to my target device are one byte long.
The target protocol requires one dataline (DIN) and one local clock (STROBE), DIN is read by the ASIC at falling edge of STROBE as shown by the figure "Timing diagram".

Now, the above function as a main drawback, the bit shifting take way too much time and that does not fit to a "50% duty cycle" unless I use long delay, where the shift can neglected...

I hope this makes my problem more understandable and we'll find a way to get through this.

_____

Attachment(s): 

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

Quote:

Now, the above function as a main drawback, the bit shifting take way too much time

Can you put any numbers to that? I'd speculate that shifting would the bit of interest into bit position 0 should take on average 4 clocks + a fixed "base cost" of a few clocks. Worst case would be 8 clocks and the "base cost". So, lets say 20 clocks. If your AVR runs at 20 MHZ you'd be able to do 1 M of those per second. There is other stuff to be done too, but lets just stop a while and think about what 20 clocks, running the AVR at 20 MHZ means compared to the delay of 2 x 20 ms. Yes, about 5%. Is that messing it up for you?

As was described in the other thread, since you want to get the bits going from low to high you do not need to shift more than one step per loop.

void send_char_to_pin(char c, int pinNumber){ 
  int i; 
  char bitmask;
  bitmask = 1<<pinNumber;

  for(i = 0; i < 8; i++){
    if (c & 0x01)
    {
      PORTB |= bitmask; 
    }
    c >>= 1;
    _delay_us(20); 
    PORTB &= ~(bitmask); 
    _delay_us(20);
  } 
}

Apart from perhaps giving the compiler a better chance to optimize, I also think this code is more straight forward (it takes a while to get out of the dizziness after reading you _BV and shifting code line :wink:). Straight forward code is almost always A Good Thing [tm].

The single shift

c >>= 1;

will hopefully be translated to a single shift right instruction, taking only one clock.

The if statement/block will take a bit longer, but I would not expect more than 10 clocks - probably less. (Your variable left-shifting of the bit to the correct final position will probably be less efficient.

If you need to have a signal that has identical timing for bits being 1 and bits being zero you will really have to struggle/fight with the optimizer of the compiler. (E.g. you might think that you'd get a wee bit more "balanced" timing if you placed the shifting between the delays, but the compiler might very well move it if it sees fit.) For such fine control of timing I'd either try using a Timer/Counter or resort to coding this thing in assembler.

Final comment: Since the fastest AVRs run at 20 MHz max, the theoretical limit for a pulse train is 10 MHz. With logic imposed on the pulse train, I'd be impressed if you'd get past 1 MHz.

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

Quote:

Now, the above function as a main drawback, the bit shifting take way too much time and that does not fit to a "50% duty cycle" unless I use long delay, where the shift can neglected...

???
1) You complain about "way too much time", yet you have hard delays in the loop?!?
2) What part of the above timing diagram, or anywhere else in the thread is "50% duty cycle" a requirement?!? (And given the diagram with the clock I wouldn't think there would be such a requirement, beyond perhaps the minimum frequency you mentioned.)
3) Bit-banging SPI has come up before. The AVR instruction set doesn't lend itself to the fastest bit-banging, but have you looked for the 74hc595 threads as I suggested? 'Freaks have done clever things...

4) Ranting over :twisted: , why don't you just use SPI? You can get theoretically to AVR clk/2 (or is it clk/4?).

5) If you unroll the loop, I think you can do it in 5-8 AVR clocks. There is a clever implementation in a fairly recent extensive thread on this. You can shave a clock or two if you can dedicate a whole AVR port to the output of the data bit but probably not worth the effort when simple SPI will do.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:

1) You complain about "way too much time", yet you have hard delays in the loop?!?

Lee, I read his worthings and the code as this: "A 25 KHz signal is desired. Therefore I do two delays of 40 us for one period. But the logic of isolating the current bit and setting/clearing the actual signal pin disturbs the timing I want so that it is no longer 'a 50/50 duty cycle'."

I sort of implied two such disturbances: A fixed component, that could be remedied by tinkering with the delays. A variable component (due to variable number of shifts for consecutive iterations in the loop - these can be handled by i) getting rid of'em, or ii) using a Timer/Counter, or iii) coding the stuff in assembler where you will have full control, and can insert compensating code so that all code paths and iterations take a constant amout of time.

And yes, I stopped short of asking if 50/50 was actually a requirement. After all the stuff seems asynchronous, with a separate clock signal... (And yes, it is very likely that this is SPI, but you are actually the only one having mentioned that trigraph in this thread :wink:)

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

I can't seem to find the thread I was thinking of about doing the "fastest" bit-bang on an AVR. Perhaps the other 'freaks can recommend search words? IIRC the clever part involved doing XORs on the data byte and then conditionally using the AVR's bit-toggle feature. The end result was a fixed-cycles-per-bit sequence.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Hi there,
sorry to answer so late...
Actually I don't a very accurate 50% duty cycle, but I'd like to be close to it.
@JohanEkdahl: You were right, the discussion was a good advice. I succeeded in sending out a char. Now, I'd like to get rid of the _delay_xs macros and also finding a way to mirror a byte because I need to send from MSB to LSB.
I'll give more information soon...

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

Quote:

Actually I don't a very accurate 50% duty cycle, but I'd like to be close to it.

Why? Other than width limitations probably mentioned in ns, the device you are describing has no limitations such as that. That is how a clocked interface works--present the data bit, then present the strobe.

(You are making this harder than it is. Either use SPI, or use some of the 74HC595 code (adjusted) to bit-bang it.)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:
also finding a way to mirror a byte because I need to send from MSB to LSB

Why such a convoluted solution, when there is a straight-forward one? If you're not pressed for clock cycles then just test the most significant bit instead, and then rotate left instead of right.

Sketchy:

void send_char_to_pin(char c, int pinNumber){ 
  int i; 
  char bitmask; 
  bitmask = 1<<pinNumber; 

  for(i = 0; i < 8; i++){ 
    if (c & 0x80) 
    { 
      PORTB |= bitmask; 
    } 
    c <<= 1; 
    _delay_us(20); 
    PORTB &= ~(bitmask); 
    _delay_us(20); 
  } 
} 

If you really feel you want to mirror the byte, then search this site for several earlier discussions of that subject.

Quote:
I'd like to get rid of the _delay_xs macros

The alternative is to handle edges based on a timer, as I hinted at earlier. As long as you do not need those clock cycles for something else you might just leave the delay_xs calls in there. Unless this is a learning experience or a "challenge-just-for-fun" then don't do things that you do not need to do... :wink:

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

Quote:

If you're not pressed for clock cycles then just test the most significant bit instead, and then rotate left instead of right.

Like I have in my posted '595 routines? (a search for which I suggested earlier. I guess I'm out.)
https://www.avrfreaks.net/index.p...

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Hi there,
it's been a while...
I finnaly got it to work and I'm pretty happy with my work...On an atmega328p with à 16MHz quartz, I get 1.3MHz output frequency... Not that bad :)
Here is my code:

/** define a few global variable for ports **/
#define STRPORT		PORTB
#define DINPORT		PORTB
#define STR			PB4
#define DIN			PB5

/* define macro */
#define ASM_ONECYCLE __asm__ __volatile__ ("nop");


void send_frame(char data, uint8_t length, char directionReadout){
	int pos;
	char dir;
	if (directionReadout == 'L'){
	  dir = 0x01;
	}
	else{
	  dir = 0x80;
	}

	for (pos = 0; pos < length; pos++){
		// Strobe = 1
		STRPORT |= _BV(STR);
		ASM_ONECYCLE;
		// set DIN to one if needed
		if(data & dir){
		  DINPORT |= _BV(DIN);
		}

		else {
			DINPORT |= ~_BV(DIN); // dummy
		}
		// shift byte
		if(dir & 0x80){
			//blink(1);
			data <<= 1;
		}
		else {
			data >>= 1;
		}

		// set all signals to zero
		ASM_ONECYCLE;
		STRPORT &= ~_BV(STR);
		ASM_ONECYCLE;
		DINPORT &= ~_BV(DIN);
		ASM_ONECYCLE;
	}
}

Thank you for your help guys, and have a good one !

PS: code slightly modified and corrected on 08/12/2011 (dd/mm/yyyy)

Last Edited: Thu. Dec 8, 2011 - 08:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
      if(data & dir){
        DINPORT |= _BV(DIN);
      }

      else {
         DINPORT |= _BV(DIN); // dummy
      }

Is this one of those "spot the difference" competitions? My tired old eyes cannot make out any logical difference between the if() and the else() so as far as I can see the entire conditional is pointless?

Surely the else() case was supposed to be:

      else {
         DINPORT &= ~_BV(DIN); // dummy
      }

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

Quote:
Is this one of those "spot the difference" competitions?
I found the difference:

// dummy 

;)

Regards,
Steve A.

The Board helps those that help themselves.

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

Correction made... Thank you guys