Queries in Indigenous Communication Protocol Idea

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

Note: Using an Arduino Uno (AVR 328p) as master and Arduino Nano (AVR  328) as a slave. I'm using the Arduino IDE as my compiler.

 

I wanted to create my own communication protocol, similar to I2C, to see how things work.  
The basic guidelines I want to use are as follows:
 

1. There are two lines for data transmission, a data line and a clock line. The dataline will be set high when the system is idle, or when no data transmission will occur. It will be pulled low when data transmission will occur. 

2. The clock line will change so as to transmit the data. 

 

The way I plan for the above to occur is that I'll have two pins (say the SDA pins of both master and slave), connected in series on the breadboard. For the slave, the pin will merely be an output pin. When the master's SDA pin is pulled high, the slave's pin will also be pulled high. A similar thing will occur for the SCL pins of the master and slave. The slave only gives an output when it sends an ACK. 

 

Now, I define various basic functions, such as:
1. START: The clock line is pulled low for 20 ms.

2. The Address bits of the slave is 8 bits which are sent for 20ms each.

3. An ACK is sent if the data is received by setting the clock line low for 20ms

4. The Data byte is also sent similarly to the address bits, and ACK is given if received. 

5. STOP: By pulling the clock line high for 40 ms. 

 

I tried implementing it by doing the following for the master:

#include<avr/io.h>
#include<util/delay.h>

/* *************************************************************************************************************************** */
/********************************************FUNCTION DEFINITIONS***************************************************************/

/*
 * Note: Here, I'm using Pins B0, and Pin B1 as the two lines.
 * Pin B0 is the SDA line
 * Pin B1 is the SCL line
 *
 * The protocol is basically, that the SDA line is always high, unless there is a data transfer. (ie. when the system is idle, the SDA line is high)
 */

/*START Function is defined by the SCL going low for 20 ms, while the SDA line has to go low */
void proto_Start(void)
{
  PORTB &= ~(0x03); //Setting the clock line and the data line low (data line is to be kept low for data transfer)
  _delay_ms(20); //Keeping the signal the same for 20ms, so that the receiver MCU's will receive the START signal
}

/*STOP Function is defined by the SCL going high for 40 ms, while the SDA line remains high*/
void proto_Stop(void)
{
  PORTB &= ~(0x01); //Setting the data line low, so as to enable data transfer
  PORTB |= 0x02; //Setting the clock line high
  _delay_ms(40); //Keeping the signal the same for 40ms, so that the receiver MCU's will receive the STOP signal
}

/* TRANSMIT Function is defined by doing the following
 *  1. While keeping the SDA line low, the SCL line will be used to transmit the data
 *  2. From the given 8 bit data (from the master), we find the values of each bit
 *  3. The values of each bit is then saved in an array
 *  4. Using the array, we send the required bit, by setting the SCL pin high (for 1) or low (for 0).
 */

void proto_Transmit(unsigned char data)
{
  PORTB &= ~(0x01); //Setting the SDA line low, so that the system is not idle
  int i, k;
  int bits[8];

  /*Generally, to find the k-th bit of n, we use (n >> k) & 1
   * By right-shifting the desired bit into the least significant bit position, masking can be done with 1, to find the value.
   */

  for(k = 0; k < 8; k++)
  bits[k] = (data >> k) & 1;

  //I use this loop to send the data bits as per our requirement through the clock line. The clock line will be set high if the bit is high and vice versa.
  for (i=0; i<8; i++)
  {
    if (bits[i] == 1)
      {
        PORTB |= 0x02;  //Setting the clock line high
        _delay_ms(20);
      }
    else
      {
        PORTB &= ~(0x02); //Setting the clock line low
        _delay_ms(20);
      }
  }
}

/* *************************************************************************************************************************** */
int main (void)
{
  DDRB = 0xFF; //We set everything in Port D as output pins
  PORTB |= 0x01; //If they are outputs, we want them to be low. Except Pin B0, as it is the dataline pin which must be high for idle state.

  while(1)
  {
    proto_Start();
    proto_Transmit(0x01); //Address of first MCU
    // Do something here if we receive an ACK from the slave (maybe bit_is_clear?)
    if (bit_is_clear(PINB, 0) && bit_is_clear(PINB, 1)) //If ACK is sent, it will send set SDA and SCL low for 20ms
    {
      proto_Transmit('A');
      proto_Stop();
    }
  }
}

Is this the right way to go about implementing my protocol? Any ideas on how to improve this? Or is there any glaring issue?

Last Edited: Mon. Jun 19, 2017 - 10:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I thought the indigenous protocol was smoke signals.
What do you want to achieve? Why not use just one wire?

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

Hazed wrote:
Is this the right way to go about implementing my protocol? Any ideas on how to improve this?

 

That is hard to say, because you invented the protocol and we don't know your requirements, you are the best person to judge whether it is right or not.

 

For example, locking a task for 160 ms per byte while data is sent I would consider the "wrong way", because I expect to handle multiple tasks with my code. If the only task is to handle the comms protocol, then there is no problem. I would use a timer and a state machine to handle the protocol.

Bob.

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

Have you looked at I2c which has similar signals, probably addresses a number of issues that you may have overlooked in your proprietary protocol & most importantly is supported in hardware on the Mega328p you are using?

 

unless an academic exercise to gain deeper understanding of the principles involved wheel re-inventing is rarely a good idea.

 

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

Hazed wrote:
I wanted to create my own communication protocol, similar to I2C, to see how things work

So the first thing to do is to ensure that you have a thorough understanding of how I2C works.

 

Start here, with the Official Specification: 

UM10204: I2C-bus specification and user manual

Rev. 6 — 4 April 2014 

 

http://www.nxp.com/documents/use...

 

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

Designing a Prototcol has nothing specifically to do with AVR - this should be in 'General'

 

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

donotdespisethesnake wrote:
you invented the protocol and we don't know your requirements

 

Hazed wrote:
Is this the right way to go about implementing my protocol?
 

This is not the right way to go about anything!

 

You are making the classic mistake of jumping straight into implementation without have first done a proper - or, even, any - design.

 

You need to start by defining the requirements for your protocol.

 

Then you can move on to a design for your protocol.

 

Then - and only then - start thinking about implementation.

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

What are the issues with I2C?

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

dbrion0606 wrote:
What are the issues with I2C?

That's a really important question!

 

After identifying your requirements, one of the first things to do in any design should be to review the current state of the art (aka "prior art") - to see how other people are addressing the same or similar requirements...

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

You may want to take a step back and ask yourself why, out of all the potential ways to make a serial link between two devices almost all micros in the world have settled on a subset of pretty much just one-wire, I2C, SPI, UART with a few "odd balls" like LIN tacked on (oh, yeah, and this new fangled "USB" thing). If there was a "better way" then it's a pretty fair bet that would be what we'd all be using!

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

What happens if both devices decide to drive the bus? I2C manages this by using open collector drive so that such a collision is not a problem.

If we were to dissect the I2C protocol, we note the following:

1. it starts off as a simple synchronous protocol

2. they add protocol violations to implement the start and stop states

3. wire 'or' so that collisions can be managed and features like clock stretching can be implemented

4. specific states for device selection and data transfer

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

I think the OP indicates that it is a learning exercise for him, so I wouldn't worry too much about re-inventing wheel. In practice, there may be occasions when we need to bit bang a protocol, although I would only try to do that as a last resort.

 

As a generic exercise in "sending and receiving data", I think there are some useful aspects to learn which could apply to other programming problems, particularly in the typical applications for MCUs. I'm disappointed design methods like SDL rarely get a mention, they would help a lot.

Bob.

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

Wonder why this is done in such an inefficient way then:

  int bits[8];

  /*Generally, to find the k-th bit of n, we use (n >> k) & 1
   * By right-shifting the desired bit into the least significant bit position, masking can be done with 1, to find the value.
   */

  for(k = 0; k < 8; k++)
  bits[k] = (data >> k) & 1;

  //I use this loop to send the data bits as per our requirement through the clock line. The clock line will be set high if the bit is high and vice versa.
  for (i=0; i<8; i++)
  {
    if (bits[i] == 1)
      {
        PORTB |= 0x02;  //Setting the clock line high
        _delay_ms(20);
      }
    else
      {
        PORTB &= ~(0x02); //Setting the clock line low
        _delay_ms(20);
      }
  }

Why not simply:

  //I use this loop to send the data bits as per our requirement through the clock line. The clock line will be set high if the bit is high and vice versa.
  for (i=0; i<8; i++, data >>= 1)
  {
    if (data & 1)
      {
        PORTB |= 0x02;  //Setting the clock line high
        _delay_ms(20);
      }
    else
      {
        PORTB &= ~(0x02); //Setting the clock line low
        _delay_ms(20);
      }
  }

No need for bits[] at all. If you want the bits in the other order then:

  for (i=0; i<8; i++, data <<= 1)
  {
    if (data & 0x80)

EDIT: actually this:

  //I use this loop to send the data bits as per our requirement through the clock line. The clock line will be set high if the bit is high and vice versa.
  for (i=0; i<8; i++, data >>= 1)
  {
    if (data & 1)
      {
        PORTB |= 0x02;  //Setting the clock line high
        _delay_ms(20);
      }
    else
      {
        PORTB &= ~(0x02); //Setting the clock line low
        _delay_ms(20);
      }
  }

has _delay_ms() in both conditional statements so surely:

  //I use this loop to send the data bits as per our requirement through the clock line. The clock line will be set high if the bit is high and vice versa.
  for (i=0; i<8; i++, data >>= 1)
  {
    if (data & 1)
      {
        PORTB |= 0x02;  //Setting the clock line high
      }
    else
      {
        PORTB &= ~(0x02); //Setting the clock line low
      }
    _delay_ms(20);
  }

Last Edited: Mon. Jun 19, 2017 - 12:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At 20ms/bit it is about as slow as sending smoke signals.

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

clawson wrote:

You may want to take a step back and ask yourself why, out of all the potential ways to make a serial link between two devices almost all micros in the world have settled on a subset of pretty much just one-wire, I2C, SPI, UART with a few "odd balls" like LIN tacked on (oh, yeah, and this new fangled "USB" thing). If there was a "better way" then it's a pretty fair bet that would be what we'd all be using!

 

You're right, about how the code for that part could avoid using bits[], I've incorporated that as well. Thanks! 

 

Thank you all for all your comments and suggestions. There isn't any specific application to which I wish to try this protocol. It is purely an academic exercise. For testing, I'm trying to use it to send one character from one MCU to another. It will be a system of one master and multiple slaves. The slaves won't drive the system unless they are sending an ACK, so I don't think there is a situation wherein both the master and slave will both be trying to drive the system. I know that there are better, more efficient methods out there. But, I felt that designing a system by myself would help me learn a bit more since when I tried doing I2C or SPI, it merely required setting certain registers, and I didn't exactly get to see how the data moves -- bit by bit (not theoretically, but practically). 

 

However, I am facing issues when it came to designing a function to receive the signals of the master. I'll upload the code (what I've done till now) for the same as soon as possible.

Last Edited: Tue. Jun 20, 2017 - 03:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

UPDATE:

 

I've completed a rough sketch of the code for master and slave here. 
It kinda works however, I have a small issue. When the master sends the address of the slave, the received data on the slave MCU turns out to be double of the data sent. I assume it is because the data gets left shifted once, but I can't seem to find the exact reason why. I think it has something to do with the start function. Can anyone care to help?

 

I've attached the required files.

Attachment(s):