LadyAda LCD SPI library and serial baud rate

Greets,

I've been trying to run a Modbus at 57.6KBaud with the LiquidCrystal library that supports the LCD Backpack from Ada Fruit. The result has been entirely too many Modbus timeouts. My guess is that the UART in the ATmega 328 has no buffering and the software SPI interface is just plain slow?

I had been sending the characters to the LCD one at a time, but that was dog-slow. That's the only way I see I can run the Modbus at 57.6KBaud -- send a byte, check Serial.available(), send a byte, repeat. But I have a feeling that I'd still lose characters because 1 character time at that baud rate is about 175uS and my guess is that it takes a lot longer than that to send a character to the LCD display.

Any suggestions, other than sticking with 9600 Baud?

By the way, the reason I want to use 57.6KBaud is because it's going over XBee and setting them to 57.6KBaud allows a Fio to be reflashed over XBee.

This may be moot because the Fio doesn't seem to have 5 volts on the board, even though it runs from USB. I'm gonna go read me a schematic ...

(Edited to add ...)

The schematic shows that Vin from the USB goes directly to a Max1555 Lithium battery charger. And no where else. Which means all I have is 3.3 volts to work with. Which means I'm probably going to have to re-think this thing.

The UART has a single output buffer and a double input buffer from memory. SPI should be faster than serial by quite a ways.

I don't quite understand why this is so slow, you should be able to send 20 characters in 3 mS at that rate.

I haven't used Modbus, but a quick Google seems to indicate it is a protocol for sending packets. There would be an overhead for small packet lengths.

I developed a simple protocol myself (I'm not suggesting it is better than Modbus) for sending serial data over a link where errors might occur. It is mentioned on my page where I worked on a remote-controlled car:

http://www.gammon.com.au/forum/?id=11506

Also further down that page is a discussion about Manchester encoding which may or may not be relevant in your case.

Sounds like there is more than to your problem than meets the eye, I would be trying to narrow it down to communications reliability, and any inefficiencies in the length of the packets you are sending. Some debugging prints on the receiving end might help to show whether or not large numbers of packets are discarded due to communications errors.

Nick,

I have to use Modbus since these devices are used in environments where everything else is Modbus.

Modbus is a client/server protocol used in industrial control environments. Most of the packets succeed, but most of the packets are very short commands to return some value -- read a bunch of analog data, tell me whether a pin is "true" or "false", turn on a pin, turn off a pin. The packets that are failing are "write multiple registers" and are about 30 bytes long. So the Arduino is receiving 30 bytes in under 6ms. Which is probably the problem -- if SPI is blocking the UART interrupts (or whatever) for more than 350uS, I'm going to drop bytes.

I'm going to change the LCD update code so it only updates a byte at a time and see if that fixes the problem with the packets being lost.

Hardware serial, and hardware SPI should work together reasonably well. The serial at least has double buffering so it can tolerate a delay of two incoming bytes. Hardware serial uses interrupts, so sending or receiving a byte using SPI should not be an issue, as that takes around 3 uS.

To say more I would have to see the code, however if there is a point at which you turn interrupts off, that could likely be the culprit.

Nick,

I've not looked at LadyAda's LiquidCrystal library, but it doesn't use hardware SPI -- you can use any three pins you want, so I'm assuming she's bit-banging the pins. And I'm guessing that she has interrupts turned off while she's doing that.

I'm not doing anything with interrupts on my end, so I can't blame myself -- I have to blame someone else ;)

Hmm, Adafruit, eh? This sounds vaguely familiar.

When I responded to you, I looked at the inbuilt LiquidCrystal library, not the Adafruit one (despite your subject line, sorry).

Check out this thread:

http://forums.adafruit.com/viewtopic.php?f=19&t=19079&start=15

When I suggested in the above thread they consider using hardware SPI, I got pretty short shift. I haven't posted much on their forums since (if anything).

They seemed to think that being able to use "any pins" warranted code that ran 50 times as slowly. Well whatever works for them, I guess.

The code changes I suggested to them may well be able to be applied in your case for a substantial speed improvement.

One of their responses was:

... few people need fast updates ...

Perhaps you are one of those "few people" that actually want quick updates?

[quote author=Nick Gammon link=topic=93487.msg704903#msg704903 date=1330233412] One of their responses was:

... few people need fast updates ...

Perhaps you are one of those "few people" that actually want quick updates? [/quote]

Heh. I'm also one of those "few people" who won't mind changing the code so it uses hardware SPI if the "right' pins are used.

The code is in Git. I think I'll see if I can make some code changes so it =conditionally= uses hardware SPI. A transfer rate of 150uS per byte is a maximum data transfer rate of 3,300 cps by serial before the two character buffering is overrun. My guess is they are sending more than one byte per write() call and disabling interrupts while doing so?

I can't see their code disabling interrupts anywhere. However digitalWrite does so briefly, and if you are doing a lot of them (as you would if you are doing 8 per byte) then maybe that accounts for it.

It looks like one user on that Adafruit forum was happy with your mods Nick even if the Adafruit people think nobody needed the speed.

I had trouble with them once to and haven't been back since :)


Rob

[quote author=Nick Gammon link=topic=93487.msg705418#msg705418 date=1330279313] I can't see their code disabling interrupts anywhere. However digitalWrite does so briefly, and if you are doing a lot of them (as you would if you are doing 8 per byte) then maybe that accounts for it. [/quote]

That likely depends on how the ATmega handles interrupts and how a missed interrupt is handled by the ISR.

Every pin on the '328 can produce a pin-change interrupt, and the pin-change interrupts are grouped by port. I'm using pins 2, 3 and 4 for the LCD SPI connection, and obvious I'm using pins 0 and 1 for the serial I/O. That means I've got my fair share of pins on that port and I probably need to move the LCD to a different batch of pins so I'm not pounding on the same port with PCI0 interrupts.

The flag indicating that the pin-change interrupt happened on that port is cleared when the ISR is started, which means the ISR is responsible for servicing =all= of the pins that changed.

Anyway, I'm not as familiar with the architecture as I probably need to be. I'll see if moving the LCD to a different port helps with the problem.

Problem solved!

I made Nick's hardware SPI change, which made a huge difference. Then I had issues with the Modbus stack timing out, so I changed the 3.5 character time from 1ms to 3ms and that fixed the last of the problems. I now have the display running at 57.6KBaud.

I'm going to go back to LadyAda's forum and reading them the riot act. The change I made was maybe four lines and it adaptively uses SPI depending on the pins that are given when the device is open, so it's still "flexible" and it's also "fast". VERY fast :)

Let us know how you get on. :-)

Some people say, "hey, good idea, thanks!" and others, well ... don't.

[quote author=Nick Gammon link=topic=93487.msg706675#msg706675 date=1330372416] Let us know how you get on. :-)

Some people say, "hey, good idea, thanks!" and others, well ... don't. [/quote]

I'll probably send Lady Ada a more private mail basically saying, if you want to keep your reputation around professionals, you need to take advice from professionals.