2
\$\begingroup\$

I have a device I'm trying to control with the Arduino.

It works like this.

  1. Arduino sets and holds 'ready line' to 5V high.
  2. Device sees ready line high and sends 16 clock pulses for two bytes of messages to receive.
  3. On each clock pulse the Arduino sets the data output to whatever the bit should be (0 or 1)
  4. When the two bytes have been sent, the 'ready line' is set back to 0v low.

The problem I have is getting the Arduino to sync to the clock pulses. Currently I have an if-statement that sets the data line if it sees the clock line high, but this does not seem to work. I think I should be looking for the rising edge instead.

How can I sync these two devices properly?

\$\endgroup\$
  • \$\begingroup\$ Sounds like SPI. \$\endgroup\$ – Ignacio Vazquez-Abrams Dec 1 '13 at 19:49
  • \$\begingroup\$ arduino.cc/en/Reference/SPI \$\endgroup\$ – jippie Dec 1 '13 at 19:51
  • 1
    \$\begingroup\$ Unfortunately it's not SPI. It's some proprietary controller interface for an old synthesizer \$\endgroup\$ – blarg Dec 1 '13 at 20:07
  • 1
    \$\begingroup\$ Still I suppose the concept is very similar, if not the same. \$\endgroup\$ – blarg Dec 1 '13 at 20:08
5
\$\begingroup\$

Add these steps to your algorithm: 0. Prepare first bit of data ... 3. Use external interrupt on rising edge to output prepared bit and put next bit into your bit holder variable. Count outputted bits. * Step 3 will be automatically repeated 16 times (external device will trigger it). 4. When bit counter reaches 16, disable ext interrupt and set ready line back.

hint: on atmega168/328 any pin may be used as ext interrupt source on incoming edge transition with quite simple technique.

I hope your external device is not driving clock pulses too fast. Arduino is not very good at reaction speeds faster than 1MHz. I'd say you should expect this approach to work reliably up to 100 kHz clock. On higher speeds either hardware SPI should be used to clock out your data or tight assembly coding. In any case several MHz is max you can do with bare AVR here.

\$\endgroup\$
  • 1
    \$\begingroup\$ The interval between pulses is ~120us, thus 1,000,000 / 120 = 8333, and speed is 8.3Khz? \$\endgroup\$ – blarg Dec 1 '13 at 21:49
  • 1
    \$\begingroup\$ Exactly. That will be perfectly fine. You also need to be careful if you want some other interrupts to be allowed during your data transfer. \$\endgroup\$ – x4mer Dec 1 '13 at 22:19
2
\$\begingroup\$

The SPI has two modes: Arduino Stock Library natively only supports master. Your scenario sounds exactly like SLAVE, in that the other end is driving the Clock.

full example

You can tie your Ready to the SS(as input) and setup the SPI with the correct mode of phase/polarity. Then set the SPCR as Slave. Enable your Ready and then wait for the 8 bit transfer to be complete, either by interrupt or polling. then release the Ready.

ATmega328 Datasheet note section 18.2 and 18.3.2

\$\endgroup\$
  • 1
    \$\begingroup\$ Nice and clear example. Although for this case software SPI is quite ok and allows to use any arduino pins not just those fixed to hw SPI. \$\endgroup\$ – x4mer Dec 3 '13 at 21:58
2
\$\begingroup\$

The problem here is all about timing, so let's all get our oscilloscopes out! Basically (and a little over simplified) most standard C flow control statements cannot make timing guarantees on a microcontroller. Hardware based interrupts guarantee the code will begin execution within a fixed number of clock cycles.

The Arduino (more specifically, the Arduino Uno with a ATmega328) has lots of ways to trigger hardware interrupts, called "interrupt vectors" sometimes (pg 57 of the ATmega Datasheet). Which one do we want? We want to execute code immediately after the cock pin transition, so we are looking for a Pin Change Interrupt or an external interrupt. Section 12 in the datasheet goes into way too much detail on this, but the key thing is each port of 8 pins shares an interrupt vector! ( arduino.cc/en/Hacking/PinMapping168 ) This makes it tricky, especially if you are new to microcontrollers. The Arduino community created a great library for just such a situation, called PinChangeInt. (Disclaimer: I contributed to PinChangeInt)

The builtin Arduino interrupt creator function (attachInterrupt(interrupt, ISR, mode)) and PinChangeInt's interrupt creator (PCintPort::attachInterrupt(PIN1, &ISRfunc, EDGE)) take very similar arguments. The pin and edge/mode arguments are self explanatory - The pin you want the interrupt on and the edge you are interested (most likely rising, but check the timing diagram for your clock). The function name you specify is attached as the Interrupt Service Routine (ISR). Whenever the hardware sees that interrupt, the ISR begins executing. This function (ISRfunct() above) takes no arguments and returns no values (type VOID). It can access global and other in scope variables like any function. To implement the logic you are describing you will need a global variable for your data, and one for the "index of transmission," or the last bit sent.

For more background on interrupts on the Arduino check out the links below:

ATmega168 Subsystem Diagrams

uchobby.com/index.php/2007/11/24/arduino-interrupts/

gonium.net/md/2006/12/20/handling-external-interrupts-with-arduino/

\$\endgroup\$
  • \$\begingroup\$ To do this though I would need to be able to tell the interrupt what state to output (0 or 1) and to increment the counter so it would know what place of the byte it was at. The Arduino interrupt function can't take any arguments. \$\endgroup\$ – blarg Dec 5 '13 at 13:47
  • \$\begingroup\$ I updated my solution to include a brief overview of ISR's on the Arduino. Essentially, you need a global or scope-wide variable to communicate data from an ISR to your main loop. \$\endgroup\$ – LexTalionis Dec 6 '13 at 23:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.