NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » multiple bits sequentially on single pin

April 15, 2011
by uml_12
uml_12's Avatar

Hi,

background: - I have two individual atmega168's (#1, and #2) and I need to connect them via a single pin to transfer up to 5 bits from one to the other. The first bit sent will be a 1 to indicate that #1 is ready to transfer the following data to #2. Fortunately #2's sole purpose is to receive this data, store it and analyze it following the transfer.


My basic understanding of this so far is:

  • I want to have #2 waiting for the first high bit on PCx.

  • When the first high is received from #1, place (shift, store in array?) the next bits (high or low) into some variable that can be saved while the unit maintains power (data is lost when you disconnect power)

  • I would imagine that there is a certain time that the first bit is high for. Then potentially a time of waiting before the next bit is written. then say the second bit is a 0, so #2 reads a low on PCx and stores the second bit in either a seperate variable (maybe easier?) or shifts it in the array previously designated for this information.

  • All the bits are transfered and after the 5th bit received, PCx reads low continuously and is no longer being used.


  • Can someone help me with an algorithm or reference (although actual code wouldn't hurt either ^_^ ) to do this .. say on PC5 or some random pin .. PCx if you like. As much as I would like to use spi for this transfer, I am unable and this particular knowledge would be supremely useful for the rest of my project as well.

Thanks !

April 15, 2011
by Noter
Noter's Avatar

Dallas one wire (DOW), sometimes called One Wire Interface (OWI), is a popular protocol. You can find lots of info on the web as well as various code examples too. You'll need an external pullup resistor (I think I use 5k) on the wire to make it work.

#define OWI_DDR             DDRB
#define OWI_PORT            PORTB
#define OWI_PIN             PINB
#define OWI_IO              PB1

#define OWI_DELAY_A     6 
#define OWI_DELAY_B     64
#define OWI_DELAY_C     60
#define OWI_DELAY_D     10
#define OWI_DELAY_E     9
#define OWI_DELAY_F     55
#define OWI_DELAY_G     0 
#define OWI_DELAY_H     480
#define OWI_DELAY_I     70 
#define OWI_DELAY_J     410

void owi_write_1(){
    // pull the bus low for 1 to 15 µs
    // then releases the bus for the rest of the time slot
    while(!(OWI_PIN & _BV(OWI_IO)));    // wait for high
    cli();
    OWI_DDR |= _BV(OWI_IO);             // set output
    OWI_PORT &= ~_BV(OWI_IO);           // pull low
    _delay_us(OWI_DELAY_A);
    OWI_PORT |= _BV(OWI_IO);                // raise high
    OWI_DDR &= ~_BV(OWI_IO);                // set input
    sei();
    _delay_us(OWI_DELAY_B);
    return;
}

void owi_write_0(){
    // pull the bus low for a period of at
    // least 60 µs, with a maximum length of 120 µs
    while(!(OWI_PIN & _BV(OWI_IO)));    // wait for high
    cli();
    OWI_DDR |= _BV(OWI_IO);             // set output
    OWI_PORT &= ~_BV(OWI_IO);           // pull low
    _delay_us(OWI_DELAY_C);
    OWI_PORT |= _BV(OWI_IO);            // raise high
    OWI_DDR &= ~_BV(OWI_IO);            // set input
    sei();
    _delay_us(OWI_DELAY_D);
    return;
}

uint8_t owi_read(){
    // The master pulls the bus low for 1 to 15 µs. The
    // slave then holds the bus low if it wants to send a ‘0’. 
    // If it wants to send a ‘1’, it simply releases the line. 
    // The bus should be sampled 15µs after the bus was pulled low.
    uint8_t bit_in;
    while(!(OWI_PIN & _BV(OWI_IO)));    // wait for high
    cli();
    OWI_DDR |= _BV(OWI_IO);             // set output
    OWI_PORT &= ~_BV(OWI_IO);           // pull low
    _delay_us(OWI_DELAY_A);
    OWI_PORT |= _BV(OWI_IO);            // raise high
    OWI_DDR &= ~_BV(OWI_IO);            // set input
    _delay_us(OWI_DELAY_E);
    bit_in = OWI_PIN & _BV(OWI_IO);
    sei();
    _delay_us(OWI_DELAY_F);
    return(bit_in?1:0);
}

void owi_write_byte(uint8_t data){
    uint8_t i;
    for(i=0;i<8;i++){
        data&_BV(i)?owi_write_1():owi_write_0();
    }
}

uint8_t owi_read_byte(){
    uint8_t i;
    uint8_t data;
    for(data=0,i=0;i<8;i++){
        data|=(owi_read()<<i);
    }
    return(data);
}
April 15, 2011
by Noter
Noter's Avatar

By the way, that code is for the master side. Changes may be in order for it to work as a slave. There should be only one master on the one wire buss.

April 15, 2011
by uml_12
uml_12's Avatar

Thanks ! I thoroughly appreciate the response. I will be reviewing that this weekend :)

April 15, 2011
by Ralphxyz
Ralphxyz's Avatar

See Paul, I told you you should do a DOW tutorial. I really think a lot of people would use if not at least reference it.

uml_12 was actually trying to make his own single wire protocol using a pin change, with just a pin change I wonder how that would work.

I can picture a two wire protocol.

Ralph

April 15, 2011
by uml_12
uml_12's Avatar

^ yes that's correct .. I am attempting to use a simple pin toggle for this experiment. Ralph .. are you saying the above mentioned procedure is not what I'm looking for ?

April 15, 2011
by Ralphxyz
Ralphxyz's Avatar

Oh no, but that will be up to you to decide if it would work.

I was just thinking of what you described using just a pin change for single wire communications.

You can use SPI or I2C to communicate between two mcus but they are not one wire protocols.

What exactly are you trying to accomplish?

Ralph

April 15, 2011
by Noter
Noter's Avatar

Probably you will need the reset function too. I have yet to code the slave side because so far I only use owi to read a temperature device but I think it should not be too difficult. The slave read function will start a timer when the pin goes low and then read it at the different durations to see if it is high again. The time frame when the pin goes high determines wether it's a 1, 0, or reset. Or use a pin change interrupt for exact timing and see what range it falls in. Maybe you don't need the reset, I haven't thought the slave side through all that well, but here it is just in case -

uint8_t owi_reset(){
    // The master pulls the bus low for at least 8 time slots, or 480µs and 
    // then releases it. This long low period is called the “Reset” signal.
    // If there is a slave present, it should then pull the bus low within 60µs 
    // after it was released by the master and hold it low for at least 60µs.
    // This response is called a “Presence” signal.
    uint8_t bit_in;
    while(!(OWI_PIN & _BV(OWI_IO)));    // wait for high
    DDRB |= _BV(OWI_IO);                // set output
    OWI_PORT &= ~_BV(OWI_IO);           // pull low
    _delay_us(OWI_DELAY_H);
    OWI_PORT |= _BV(OWI_IO);            // raise high
    DDRB &= ~_BV(OWI_IO);               // set input
    _delay_us(OWI_DELAY_I);
    bit_in = OWI_PIN & _BV(OWI_IO);
    _delay_us(OWI_DELAY_J);
    return(bit_in?0:1);
}
April 15, 2011
by Noter
Noter's Avatar

Probably you will need the reset function too. I have yet to code the slave side because so far I only use owi to read a temperature device but I think it should not be too difficult. The slave read function will start a timer when the pin goes low and then read it at the different durations to see if it is high again. The time frame when the pin goes high determines wether it's a 1, 0, or reset. Or use a pin change interrupt for exact timing and see what range it falls in. Maybe you don't need the reset, I haven't thought the slave side through all that well, but here it is just in case -

uint8_t owi_reset(){
    // The master pulls the bus low for at least 8 time slots, or 480µs and 
    // then releases it. This long low period is called the “Reset” signal.
    // If there is a slave present, it should then pull the bus low within 60µs 
    // after it was released by the master and hold it low for at least 60µs.
    // This response is called a “Presence” signal.
    uint8_t bit_in;
    while(!(OWI_PIN & _BV(OWI_IO)));    // wait for high
    DDRB |= _BV(OWI_IO);                // set output
    OWI_PORT &= ~_BV(OWI_IO);           // pull low
    _delay_us(OWI_DELAY_H);
    OWI_PORT |= _BV(OWI_IO);            // raise high
    DDRB &= ~_BV(OWI_IO);               // set input
    _delay_us(OWI_DELAY_I);
    bit_in = OWI_PIN & _BV(OWI_IO);
    _delay_us(OWI_DELAY_J);
    return(bit_in?0:1);
}
April 15, 2011
by Noter
Noter's Avatar

ATmel always has good info on just about everything - here's a better explaination that I could ever give.

AVR318: Dallas 1-Wire® master

There are several different one wire methods around and from what I've seen they are all similar. DOW is probably at the top of the food chain though because it implements a slave address in the full blown protocol and can handle 127 slaves on a single buss. Also you could use it someday on their devices like the DS18B20 temp sensor.

April 15, 2011
by Noter
Noter's Avatar

That's wierd, how did I post the same entry twice? I must be all thumbs tonight!

April 16, 2011
by uml_12
uml_12's Avatar

Thanks for all the information Noter. I'm going to both try to implement the one wire method in my own application as well as re-think my options here.

Ralph .. I'm making an electronic puzzle. My project:

a plywood/plastic base 12"x12". Plexi-glass is laid over the top of this box and underneath the plexi are 36 rgb leds creating nine 4"x4" puzzle positions. I am driving this array with 3 cascaded max7219's (spi) along with supportive transistors. Corresponding to each position, are nine individual cubes each containing their own atmega168's. Each cube has the ability to light up their own rgb leds.

The way things need to work: A master mcu stores information about each level for which it will illuminate the 36 led array based on each particular level number. The master also needs a way to send all 9 of the cube puzzle pieces information about the particular level or how to light their own leds in a mismatched way to begin each level. The user then removes the cubes and repositions them back onto the base. The base needs to be able to determine whether each cube is in the correct position. My original plan was to use a 168 slave inside the base to send the data to the cubes on 9 separate pins (each cube will be popping on and off the grid mind you). Along with sending data, an second 168 slave inside the base will be used to receive the data from the cubes on 9 separate pins which will each correspond to a particular position which is mapped out in my design.

This is a bit confusing, I understand, just because I'm having a tough time explaining this purely through text and with a small budget on time.

I'm also aware that this is not the only way to do this. This is mostly my initial idea for lack of a better understanding of how mcus receive data via spi protocol. If I were to use spi for all of this, I would also need to figure out how the 9 cube slaves can become the master in order to send data back (not really an issue surely). What I did just realize is that the each position can be a separate slave select line which is rendering my initial plan kind of useless. Not that it wouldn't be great to know.

I thank you for both your help and hopefully I can rethink this whole thing with the addition of more knowledge of mcu programming.

April 16, 2011
by Noter
Noter's Avatar

That sounds like a fun project. I've though about using the MAX7219/7221 before but they are more than double the price of an ATmega328 so I'd rather use another ATmega for additional pins and functionality. There are also various I2C port expanders but again their cost exceeds that of another processor.

Compared to one wire or spi, I think I2c is a better way to go for multiple mcu communication. I2C also allows multiple master's on the buss so it may better suit your application for that reason too. I've posted code for the I2c master/slave here. The latest version is near the bottom of the thread. And Rick has I2C code for a port expander here.

April 17, 2011
by uml_12
uml_12's Avatar

Cool .. yeah I'm looking at i2c and I already can see the benefits of using that over spi. However, that leads me to a design question:

If we imagine my puzzle as a 3x3 grid, each position will have a sda and scl line attached connecting these positions as slaves. Yet, it is only the positions that need to be addressed by the master. Each cube is not connected to the base. For example, the cube that is sitting in position 1,1 at the beginning of the level may wind up at position 2,3 at the conclusion of that particular level (since mismatched color codes are delivered at the start of each level and the user must replace the cubes to match a separate position).

Therefore, I need to:

  • Send the start sequence to all 9 positions

  • Address position 1 and write data from the master.

  • Address position 2 " "

  • write to p3, p4 ... ,p9.

  • (have the user remove the cubes and reposition them in correct/or sometimes incorrect positions)(some mechanism will be employed to tell the master when it is time to read data on a particular position as a cube has been placed in that spot)

  • (let's say position 6 needs to be read) Address position 6, and read data to the master from the slave (cube) placed in that spot.

  • use that data to identify if the cube is in the correct position. if not, do something.. (and repeat).

with spi, I would simply want to use a different latch pin for each position on the mcu that controls the sending of data. But I would require a separate mcu to receive the data as well because surely I would run out of GPIOs. Which is why I favor i2c over spi as of right now. But I'm sure there is some way around this issue for i2c .. just have not discovered it yet. Whatever the case .. I'm going to keep reading into it.

April 17, 2011
by Noter
Noter's Avatar

Sorry but I don't quite follow all that enough to understand what the question is?

April 18, 2011
by Ralphxyz
Ralphxyz's Avatar

One problem I see so far:

[Quote]"(let's say position 6 needs to be read) Address position 6, "[/Quote]

"If" I follow what you have said so far using I2C each cube would have a device ID.

But each "position" would not have a device ID so position 6 could not be addressed using I2C.

There would have to be ways to assign an address to a position which would not move (be dependent) with the cube move.

Ralph

April 18, 2011
by uml_12
uml_12's Avatar

I have two separate mcus needing to connect to a third mcu. the physical positioning of mcu #3 is static. The physical position of #1 and #2 can change. That is at the beginning of each level #1 and #2 are in positions 1 and 2. And at the end of the level, #1 will be in position 2 and #2 will be in position 1.

At the start of the level I must transfer a byte of data FROM #3 TO #1 and #2 to initiate the level. After both cubes (or mcus) have been removed from their respective positions .. at the moment that #1 has been placed into position 2, I need to read that byte of data again back TO #3 FROM mcu #1.

The question: i2c relies on an address to specify which chip the master (#3) is speaking to. So how do I specify the address of a location rather than the address of a specific mcu? The master (#3) is only going to be connected to the positions that #1 and #2 are inserted into. Therefore, it is the individual position I have to address, not the specific mcu inserted into it. hmm, the more I try to explain, the more I scratch my head about it. I might just have to give it some more thought.

Yes Ralph, giving each position would be the ideal scenario. I have a feeling that would require more mcus to identify positions and be separately addressed. I suppose I'm going back to the drawing board for a few hours. Thanks !

April 18, 2011
by Noter
Noter's Avatar

Hmm ... I think now I have a better understanding. One thing to consider with I2C is if you try to address a device that is not there, it get's stuck/hangs until it sees the device or reset. During the hang it continously transmits the device address looking for an ack from the device. I haven't tried plugging in a missing device when this happens so I'm not sure it would start working again anyway but in theory it should. Otherwise the mcu address does not have to remain static, you can change it as you change positions but I think you run the risk of the buss hang.

Probably a safer design would be to have a mcu for each position that is always there and can sense if anything is plugged in and which it is. Then the cubes become passive with only a few jumpers to identify them from each other.

April 18, 2011
by uml_12
uml_12's Avatar

Yes .. I forgot about the ack bit. SPI does not use the acknowledge bit .. I believe? Only a latch. I've decided to recycle my old design but revamp it with total spi communication using the latch on each pin of the receiving mcu to designate position (I'm not sure why I didn't do this in the first place .. then again, my mind is all over the place on a day to day basis with this design .. hmm)

So I will use the 328p as a master to send data to all the cubes, but will have a 168 receiving position information from the cubes to determine whether they are correct or not. The 168 will send a level complete bit to the 328p at then end of each level .. in which case the 328p will initiate the next level.

April 18, 2011
by Noter
Noter's Avatar

I think with spi you will read all 0's if the slave is missing and otherwise no way to tell it's not there. But it doesn't hang. May be a good time to develop and test your low level communication routines and be sure they work as needed before completing the overall design.

April 18, 2011
by uml_12
uml_12's Avatar

Will do. Say, if I had 2 masters and 1 slave connected either by spi. And I wanted to have 1 master transmitting data to the slave but the other master inactive. What do you think would happen? It is my thoughts that the inactive slave may hold the clk and DIN lines low, yet the active master is pulsing these lines. So are the lines able to be pulsed or are they continuously held low ?

April 18, 2011
by Noter
Noter's Avatar

SPI supports only one master for multiple slaves. As soon as you put another master on the buss you have two outputs on clock and dataout lines so neither master can put a signal on the line because it's state is held by the other. The slave only grabs it's output line when selected and otherwise lets it float. There is no chip select equivalent on the master so you'd have to do your own using another pin and disabling/enabling SPI transmit ports based on that pin state. The issue only applies for transmit type lines as many receivers can listen to a single transmitter of a signal. Probably better to use I2C with multiple masters if you must have them. You can have a single master that polls all the slaves for data many times a second. Maybe that would work ok and accomidate SPI requirements.

April 18, 2011
by uml_12
uml_12's Avatar

Ah yes, I believe the polling of each cube on each position may work for my design. Due to the fact that I don't have enough pins for the master to both have 9 chip selects as well as 9 servicing interrupts from each chip, I've decided that the best thing to do is to try use the chip select pin to select each position upon the sending of the data at the start of the level. Afterwards, I will change this pin on the cubes (slaves) to output instead of input and use them as service interrupts .. a similar change will occur on the master to accept the handling of the interrupts. Following this request, another change of the same pin will be issued to make it a chip select again as to accept input so that the data can be read from the cube to the master. I will use frequent changes of the pins to either output or input and (hopefully) have total control. Although, I'm merely assuming something like that would work. It's up to actual testing to see if it is possible .. or if you have experience with such things.

Curious, Noter, do you work with nerdkits to answer all my questions? Or are you simply a fellow enthusiast who enjoys answering design quieries? Because, I'm so glad you are offering your support. I would probably have gone way off in the wrong direction if I had your help .. only to realize after many hours of researching that my ideas were useless haha.

April 18, 2011
by Noter
Noter's Avatar

Just another fellow enthusiast ...

Post a Reply

Please log in to post a reply.

Did you know that multiple microcontrollers can communicate with each other? Learn more...