# Is it better on two closely connected microcontrollers to send nibbles with an ack or a byte with a delay for an ack?

I'm trying to figure out the best approach to transfer high-speed data one way between two microcontrollers (with their own crystals of the same speed) of the same variety (between AT89C4051 and AT89S52) from an electrical standpoint.

I have them wired up as follows:

AT89S52 P0 is connected to AT89C4051 P1
AT89S52 P1.1 is connected to AT89C4051 P3.7


The AT89C4051 has 6 bytes of data that the AT89S52 needs, and the AT89S52 always initiates the download.

My options are as follows:

** OPTION 1. Transfer byte-wide. **

Code in AT89C4051 (transmitter) will be this:

mov R1,#DATALOCATION
jb P3.7,$;Wait for falling edge mov P1,@R1 ;send out byte inc R1 ;Increment pointer jnb P3.7,$ ;Wait for rising edge
mov P1,@R1
inc R1
;and this code (minus first line) repeats twice for remaining bytes


And the AT89S52 will have this code:

mov R0,#DATASPACE
mov P0,#0FFh ;Make ports accept data
clr P1.1 ;lower line to get first byte
mov @R0,P1 ;Load next byte in
inc R0     ;increment pointer
nop        ;waste cycles to let AT89C4051
nop        ;be ready for next byte. Is this wait time enough under worst
;case scenarios???
setb P1.1  ;raise line to get next byte
mov @R0,P1 ;Load next byte in
inc R0     ;increment pointer
nop        ;waste cycles to let AT89C4051
nop        ;be ready for next byte
;and this code (minus first line) repeats twice for remaining bytes


That is my 8-bit approach which seems fast, but I didn't have enough pins free to use one for an acknowledgement pin as the rest are used.

** OPTION 2. Transfer nibble-wide **

Code in AT89C4051 (transmitter) will be this:

mov R1,#DATALOCATION ;pointer = start of data
jb P3.7,$;Wait for falling edge (but this means stalls which I don't like) mov A,@R1 ;get byte orl A,#0F0h ;and accept lower nibble. clr ACC.7 ;Make P1.7 our ack bit (ack=0) mov P1,A ;return data in lower nibble with P1.7=0 as ack jnb P3.7,$  ;Wait for rising edge
mov A,@R1   ;get byte
orl A,#0Fh  ;and accept high nibble. (ack=1)
swap A      ;and put it in our low nibble slot
mov P1,A    ;return data in lower nibble with P1.7=1 as ack
inc R1
;and this code (minus first line) repeats 5x for remaining bytes


Code in AT89S52 (receiver) will be like this:

mov R0,#DATALOCATION ;pointer = start of data
orl A,#0F0h ;set our nibble and make rest of lines high to receive ack
mov P1,A  ;and show it
clr P1.1  ;lower clock
jb P1.7,$;wait till remote is ready mov A,@R0 ;Load byte again orl A,#0Fh ;set our nibble and make rest of lines high to receive ack swap A ;swap nibbles so we get right nibble mov P1,A ;show data setb P1.1 ;raise clock jnb P1.7,$ ;wait till remote is ready
inc R0
;and this code (minus first line) repeats 5x for remaining bytes


which is best?

But the part that makes me concerned is timing and hardware.

The micros are connected no more than 10cm away from each other and all PCB traces are 12mils wide with 12mils clearance. When I run any microcontroller circuit, if my hand touches both leads of the crystal, then the speed of operation seems to vary (probably because human resistance affects crystal frequency?)

Given all environments the micros can be exposed to (except water), which of my two ideas is best to ensure I get data at the highest speed possible? and I only have 9 I/O lines to play with here.

So do I resort to the nibble method and wait for acknowledgements even if the response takes a while? or am I safe to use the byte method?

Remember, we have to assume worst-case scenarios. fingers touching crystal area (as a test), weak batteries, etc. because the last thing I want to happen is data loss.

For clarification, each crystal lead is connected to 33pF ceramic capacitors which are also grounded. (I'm using the standard microcontroller crystal setup). and my ground planes are large.

UPDATE

As requested, I included the important connections. Both crystals are 22.1184Mhz. The capacitor and resistor connected to the reset pin is 47nF and 100K. All other capacitors are 33pF.

I also included a basic timing diagram. I had to use paint to draw the lines in because I have no program on my computer to do professional diagrams.

• How often does this happen, and how high-speed does it need to be? – BeB00 Aug 29 '18 at 16:56
• I think this question is actually independent of the processor instruction set. Can you provide timing diagrams instead of code? A block diagram would also be better than your written description of the connections. – Elliot Alderson Aug 29 '18 at 17:06
• I think timing diagrams are more of what I need based on all environments the micro is exposed to. But the crystals on both are 22.1184Mhz and many instructions execute at 1/12th of a clock speed (equals 1 cycle)(about 0.54uS) but some need 2 cycles to execute. But I'm asking this question more in terms of hardware. Like for example, would nearby interference affect how my code works? I completely understand the execution time of each instruction in itself. – Mike Aug 29 '18 at 17:16
• This happens very frequently and this fragment alone should execute in under 50uS (ideal) but no more than 100uS. Just as an example of timing, I want to have this executed no more than the time required to transmit a byte down the serial line at 56kbps with a typical 8N1 uart setting. and Elliot, I included images for your reference. – Mike Aug 29 '18 at 17:42
• Can you run one crystal output to both chips? I foresee clock de-synchronization using separate clocks. – rdtsc Aug 29 '18 at 17:51

First of all, you need to put your project in an enclosure that prevents human fingers (or anything else) from getting so close to the crystal that it has a major effect on the frequency.

Once you have the clocks under control, the first method should be fine, assuming that you have accounted for the worst-case delays through the I/O synchronizers on both chips. Keep in mind that although the two processors are at the same nominal frequency, their actual frequencies will vary slightly and the phase relationship between them can be anything at all.

You'll probably need a few more nop instructions on the master side (the one generating the clock). Also, those nop instructions should come before the instruction that reads the data — this gives the slave procesor the time it needs to recognize the clock transition and actually put the data on the 8-bit bus.

• Yes I agree with your 1st statement. That of course I will do. Now that's two people telling me frequencies from each crystal can differ slightly. Because of the differences and my bad luck, I think I'm safer to use the nibble approach with acks rather than reprogramming the chip hundreds of times with the same program only to add a nop for each new time it is programmed. – Mike Aug 29 '18 at 20:21
• I will also wait to see if anyone else offers me answers here that help before I just accept an answer. – Mike Aug 29 '18 at 20:22
• Why would you need to reprogram the chip "hundreds of times"? You should be able to work out the required number of nops from the documentation in one go. – Dave Tweed Aug 29 '18 at 20:34
• well maybe not hundreds of times but I could be inaccurate because of clock drift which probably wont be listed in the documentation. – Mike Aug 30 '18 at 15:08
• The point is, you can easily calculate the worst-case conditions, and allow a margin beyond that. There's no need to adjust anything if conditions change, as long as they don't exceed your original worst case. – Dave Tweed Aug 30 '18 at 15:10

OK, let's see if I got this right. You are trying to achieve high-speed data transfer and you are doing bit-banging? Furthermore, you are literally counting CPU ticks for each operation, meaning your MCU is running at 100% load continuously.

There are only two possible outcomes here. Either you run out of RAM for incoming data, or you have to stop somewhere and process it. I'll assume it is the latter, and the processing takes much longer than communication. Let's say at least 5 times longer.

Now, funny thing about baud rates is that the volume of data transferred at 1 Mbps 1/6 of the time is exactly the same as the volume transferred at 170 kbps continuously. You seem to understand this, as in your comment "serial line at 56kbps with a typical 8N1".

Furthermore, by using hardware communication you can do data transfer and processing simultaneously. So, why don't you use UART hardware available in both MCUs and perfectly capable of higher speeds than you quote?

Now, just in case you need those UARTs for some other use, take a look at application note 3524 from Microchip. It is bit-banging implementation of SPI that is supposed to achieve 2Mbps transmission (at 32Mhz clock).

• I already am using the hardware UART for data transfer and I have an interrupt specifically for it. Except transfer and processing simultaneously is impossible. Even all my 8051 emulators agree with me because once the byte is received/sent, my program that has to prepare the correct bytes that the serial routine needs is interrupted and therefore garbage will be processed because my transmit routine transmits the next byte once the last one is transmitted. – Mike Aug 30 '18 at 15:07
• I don't see the use of UART hardware in your code samples. Of course transfer and processing simultaneously is possible, that is how embedded software works. Once the byte is sent all your program has to do is move next byte into transmit buffer, that's just a few OP codes. Then it can continue preparing data while the transmission is happening. If you don't have data ready, you do not send anything. On receive side you push received byte into buffer and continue processing. If no data in the buffer you simply wait for it. Why some "garbage" will be processed? – Maple Aug 30 '18 at 15:40