Go Down

Topic: SSI Clock Signal for Rotary Encoder (Read 2647 times) previous topic - next topic

Hi Everyone,

First post! :) I'm very new to the Arduino/microcontrollers in general (got my first Uno in the Sparkfun Inventor's kit via mail two days ago) and I'm loving it so far.  My background isn't in EE/embedded hardware, so I was pleasantly surprised at how easy the Arduino is to program.  Since then, I've worked through the tutorials and understand the basics of analogue & digital I/O on the Arduino, but now I have a question I can't seem to find an answer to.

One of the reasons I originally became interested in programming the Arduino is that I want to use it to read data from this, a 13bit absolute rotary encoder that uses the SSI protocol.  For my project, I need 12+ encoders, and the Arduino seems like a great way of keeping the hardware physically small and the setup portable without breaking the bank. 

After some Googling, I came across this post from 2006, which seems to try to do the same thing I'm looking to do.  The only hang-up is that there are no mentions of Clock +/- signals, which are necessary for timing.  I'm assuming that the OP was getting his clock signal from another piece of hardware, but I would love to get this encoder to read using only the Arduino, since I do not really know what I would need to generate a clock signal.  Is this possible to do?

After some additional searching, someone suggested that it would be possible to "fake" a clock signal by doing something like in the following code, but this did not seem to work.

Code: [Select]

void loop()
{
   if (digitalRead(CLOCK_MINUS_PIN)==LOW && digitalRead(CLOCK_PLUS_PIN)==HIGH)
   {
     digitalWrite(CLOCK_MINUS_PIN,HIGH);
     digitalWrite(CLOCK_PLUS_PIN,LOW);
   } 
   else
   {
     digitalWrite(CLOCK_MINUS_PIN,LOW);
     digitalWrite(CLOCK_PLUS_PIN,HIGH);
   }
   delayMicroseconds(10);


I also found the Arduino SPI library, but the problem is that the SPI protocol seems to lump everything into 1 clock instead of 2.

Does anyone have any suggestions as to how this project can be done?  Any help would be greatly appreciated!

Thanks!
arduinoRobo

MarkT

#1
Feb 18, 2012, 03:21 pm Last Edit: Feb 18, 2012, 03:38 pm by MarkT Reason: 1
You'd be better off doing direct port manipulation for two reasons:  

Firstly speed, the device probably wants faster clocking than 50kHz.

Secondly you want the clock lines to change simulataneously, and using two pins on the same port means you can change them simultaneously (which the RS422A interface specifications need)

Another approach would be to derive clk- from clk+ with an inverter  (74HC04 perhaps).  But ideally use proper RS422 line driver and receiver pair (quick search revealed SN65HVD179, but there will be others).

[edit, yes a few more part numbers of note:  AM26LS32ACN, SN75ALS194N, SN75ALS181]
[ I won't respond to messages, use the forum please ]

#2
Feb 18, 2012, 04:19 pm Last Edit: Feb 18, 2012, 04:47 pm by arduinoRobo Reason: 1
Hi MarkT,

Thanks for your reply!  You are correct, according to the Wikipedia article, SSI needs a clock speed somewhere between 100kHz and 2MHz.  (Just out of curiosity, since you mention 50kHz: is this the speed at which the Arduino loop() method executes?  From the specs, I see that the Arduino has a 16MHz clock speed.  As such, I would have thought that trying to "fake" a clock as I did above would work, since I am trying to switch at 100kHz.  Since the Arduino library has a delayMicroseconds() method, I would have expected that loop() runs pretty fast..?)

Thanks for tipping me off to direct port manipulation!  I found this handy article on the Arduino site and will try to see what I can do.  After reading this article and your post, I have a few more questions about direct port manipulation, if you don't mind:

1.) From your above statement, the advantage to direct port manipulation is that it is possible to "cut out" some of the overhead from predefined methods like digitalWrite(), for example, by manually setting the pins.  How much am I saving by doing direct port manipulation?  (This question is essentially the same one as above.)

2.) To implement the clock, would I manually make a clock signal by setting two of the Atmega168 digital pins to HIGH/LOW and delay for some amount of time, or could I simply connect the encoder's clock to the Atmega's crystal pins?

3.) When you say using 2 pins on the same port, do you mean that I should hook up both the encoder's CLOCK+ and CLOCK- pins to one digital out?  Could I use the clock signal from the SPI library for this?

4.) Since timing is important, if I wanted to implement a clock using direct port manipulation, would you suggest that I only have the clock code on one Arduino and nothing else?  Or is it possible to have code which reads the encoder/converts the grey code running on the same Arduino?  You mention a line driver and receiver pair as an alternative method to derive the clock signals using additional hardware. Is this different from something like a VCXO?  When looking up the SN65HVD179, I see that both components look the same, but just want to double check. :)

5.) Is it possible to brick my Arduino by doing direct port manipulation and making an error, or would I simply have to upload another program to it and everything would be back to normal?

Thank you so much for your help and for answering my questions :).
I really appreciate it,

arduinoRobo

MarkT

1) Direct port manipulation is perhaps 2 clock cycles, not 5us or such
2) No: Crystal oscillator runs at low voltage, not logic voltage, doesn't tolerate much capacitance either
3) No, a port is upto 8 pins.  ATmega328 has ports B, C and D.  You can change upto 8 pins simultaneously with one instruction.
4) Use a timer interrupt to drive your pins?  Least interference with rest of system potentially (although you probably need to dedicate a port, which is limiting, only 3 ports).  No, a crystal oscillator is not the solution, you need to clock a fixed number of clock pulses every so often, not continuously (how are you going to tell which result bit is which otherwise?)  RS422 drivers deal with the differential signals the encoder is using in hardware, so the microcontroller only gets to deal with one pin per signal, not two.
[ I won't respond to messages, use the forum please ]

Hi MarkT,

Thank you for getting back to me so quickly.  I now have a better understanding of what is meant by "ports" and the port manipulation article makes more sense to me now. 

I will give this a shot and post my results!

Thanks again,
arduinoRobo

After working my way through the port manipulation article more carefully, I think that I have implemented a clock, but am getting some strange results. 

Via my code below, I want to simultaneously set pins 12 and 11 HIGH, delay 10 microseconds (100kHZ), and then set pins 12 and 11 LOW. 

After each "set" operation, I am using pins 3 and 2 to read the value of PORTB using PIND, and printing the resulting number to serial.

Given the ports of PIND I connected the jumpers to, I would expect PIND to read the following binary numbers during each iteration:

B00000000
B00001100

which would be printed as

0 and 12 via Serial.println(), respectively. 

However, examining my serial output stream, I instead get alternating 15s and 7s, where the 15 is sometimes replaced by a 13.

This would suggest that PIND is reading the following binary numbers:

7 = B00000111;
15 = B00001111;
13 = B00001101;

These results are strange to me, because this suggests that the value of pin 2 is never changing, since it is constantly 1...  :~ 

The two most far-right 1s and 0s of PIND are RX & TX from the documentation.  Given this, I can understand why I would not get 0 and 12, as these would be set to 0 and 1 for send/receive.  However, I do not understand why Rx would ever be set to 1 as this is input... since I am not sending code, I would not expect it to ever receive anything.  Conversely, I do not understand why Tx would ever be 0, as it should always be sending..?

I imagine I am still doing something incorrectly.  Am I interpreting the binary wrong?  Could someone please point me in the right direction? :)

For your reference, my code is:

Code: [Select]

/* Arduino Port Manipulation to Generate Clock
*/

   void setup()
{
   Serial.begin(115200); //Set serial baud rate
}

void loop()
{
  PORTB = B00011000; //Set Port B pins 11,12 HIGH, others LOW
  Serial.println(PIND); //Read all Port D pins (0-7) simultaneously and print
  delayMicroseconds(10); //Delay for 10 microseconds for timing
  PORTB = B00000000; //Set all Port B pins LOW
  Serial.println(PIND); //Read all Port D pins (0-7) simultaneously and print
}


EDIT: Alternatively, is there a way to simply read from 2 pins instead of all of PIND, but do so simultaneously?  This would eliminate the ambiguity with what Px and TX may be doing.  Still doesn't explain why pin 2 is always 1, though.

Thank you again for your help!
arduinoRobo

MarkT

Remember to call pinMode() as appropriate - yes I think you have the bit positions right.  Stick a multimeter on a pin to know what state its in, then you'll know (rather than speculating...)

Code: [Select]

void setup()
{
   Serial.begin(115200); //Set serial baud rate
   pinMode (11, OUTPUT) ;
   pinMode (12, OUTPUT) ;
}
[ I won't respond to messages, use the forum please ]

#7
Feb 18, 2012, 10:02 pm Last Edit: Feb 18, 2012, 10:14 pm by arduinoRobo Reason: 1
EDIT: Didn't see MarkT had already posted the answer to the top half of this post while I was writing it.

Reexamining my code, I thought that a possible error may have come from the fact that I never set my pin data directions.

As such, I changed my setup() method to:

Code: [Select]


 void setup()
{
  DDRB = (1<<PORTB4); //PORTB4 is digital pin No 12 -- OUTPUT
  DDRB = (1<<PORTB3); //PORTB3 is digital pin No 11 -- OUTPUT
  DDRD = (0<<PORTB2);
  DDRD = (0<<PORTB3);
 delay(500);
 Serial.begin(115200);
}



Now I am getting 15s and 3s in my Serial output, where the 3 is sometimes replaced by an 11 or a 1.

This suggests the following binary numbers:

B00000001 = 1
B00000011 = 3
B00001011 = 11
B00001111 = 15

Therefore, both input ports are now changing, the mistake being that I did not set them for reading.

Looks like I am getting closer, which is exciting!  This is fun!  :smiley-mr-green:

This output leads me to 4 more questions:

1.) Unfortunately, since I am turning both pins on and off simultaneously, I would never expect to receive an 11 in my Serial output... does anyone know what could be causing this?  

2.) Is there a way to only read from only two pins simultaneously, instead of reading from all pins on a port?

3.) From this example code, I found that it is possible to set individual DDRB values using the "<<" operator.  But from the BitMath documentation, I see that << is a bit shift.  Why can I not use = instead?

4.) As I wrote above, I still don't understand the changing Rx and Tx values.  If someone could explain this to me, I would appreciate it :).

Thank you again!
arduinoRobo

Sorry MarkT!

As I was typing my latest post, I saw that you responded before I could finish my own reply!  :D  Man you are fast!  Thank you so much for your help :D 

Indeed, you are correct, I forgot to set the pinMode.  This result led to the new questions/observations above. :)

#9
Feb 18, 2012, 10:29 pm Last Edit: Feb 18, 2012, 10:58 pm by arduinoRobo Reason: 1
Hi Guys,

Thanks to MarkT, my code is getting better and better.  Here is my code for creating/testing a clock for your reference.  I am using pins 2 and 3 to read the data.  The only issue now is that I am sometimes receiving numbers other than 3 and 15, depending on the state of Rx and Tx.  However, it should work fine when connecting it directly to an SSI device to use as a clock.  For consistent output, maybe I can use a bit mask?  Excited!   :smiley-mr-green:

Code: [Select]

/* Arduino Port Manipulation to Generate Clock Signal
* 02/18/2012 
*/

/*Please see the "Port Manipulation" article here: http://www.arduino.cc/playground/Learning/PortManipulation
   *NOTE: The ATmega328 (used on the UNO) has an identical pin configuration to the ATmega168 (described in the Port Manipulation documentation). http://arduino.cc/en/Main/arduinoBoardUno
   *ATmega328 has three ports: D - Digital Pins 0-7, B - Digital Pins 8-13, C - Analog Pins 0-5
   *For safety, do not use:
   *Port D - Pins 0 & 1 are used for serial communications for programming and bebugging the Arduino.  This can mess up the chip's send and receive.
   *
   *DDR is the direction register and sets whether the port is input or output
   *To set input/output use DDR<LETTER> - 0 is input, 1 is output
   *Alternatively, since it is only run once during setup(), it is satisfactory to use pinMode(pinNumber, OUTPUT);
   *
   *PORT is the register for the state of the outputs.  It sets whether the value of an output pin is high (1) or low (0)
   *To set state of a specific pin on the Arduino UNO, use PORT<LETTER><PIN_NUMBER>
   *
   *PIN is the input register variable.  It will read all of the digital input pins at the same time
   *To read the pins on a specific port use PIN<Letter>
   *   
*/

int DATA_MINUS_PIN = 2; //Pin for reading minus signal
int DATA_PLUS_PIN = 3; //Pin for reading plus signal

int CLOCK_MINUS_PIN = 11; //Pin for clock minus signal
int CLOCK_PLUS_PIN = 12; //Pin for clock plus signal

void setup()
{
   pinMode(CLOCK_MINUS_PIN, OUTPUT); //Set clock minus pin as output
   pinMode(CLOCK_PLUS_PIN, OUTPUT); //Set clock plus pin as output
   
   pinMode(DATA_MINUS_PIN, INPUT); //Set data minus pin as input
   pinMode(DATA_PLUS_PIN, INPUT); //Set data plus pin as input
   
   delay(500);
   Serial.begin(115200); //Set serial baud rate
}

void loop()
{
  PORTB = B00011000; //Set Port B pins 11,12 HIGH, others LOW
  Serial.println(PIND); //Read all Port D pins (0-7) simultaneously and print
  delayMicroseconds(10); //Delay for 10 microseconds for timing
  PORTB = B00000000; //Set all Port B pins LOW
  Serial.println(PIND); //Read all Port D pins (0-7) simultaneously and print
}


Now on to reading and decoding the grey code from the encoder.  Hopefully I will have something soon.

Thanks again!
arduinoRobo

Go Up