Pages: [1]   Go Down
Author Topic: AVR USART Multi-Processor Mode  (Read 2113 times)
0 Members and 1 Guest are viewing this topic.
Maryland, USA
Offline Offline
Full Member
***
Karma: 0
Posts: 162
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Decided to break this out of the thread where I first mentioned it (http://arduino.cc/forum/index.php/topic,101823.0.html)

Apparently the hardware serial port has a mode where Single-Master, Multiple-Slave communication is available using a filtering system that helps the slaves ignore incoming I/O destined to a different slave.

ATmega48A/PA/88A/PA/168A/PA/328/P complete datasheet-- http://www.atmel.com/Images/doc8271.pdf
Page 195 (search "Multi-processor Communication Mode"):

Quote
20.9 Multi-processor Communication Mode
Setting the Multi-processor Communication mode (MPCMn) bit in UCSRnA enables a filtering
function of incoming frames received by the USART Receiver. Frames that do not contain
address information will be ignored and not put into the receive buffer. This effectively reduces
the number of incoming frames that has to be handled by the CPU, in a system with multiple
MCUs that communicate via the same serial bus. The Transmitter is unaffected by the MPCMn
setting, but has to be used differently when it is a part of a system utilizing the Multi-processor
Communication mode.
If the Receiver is set up to receive frames that contain 5 to 8 data bits, then the first stop bit indicates if the frame contains data or address information. If the Receiver is set up for frames with
nine data bits, then the ninth bit (RXB8n) is used for identifying address and data frames. When
the frame type bit (the first stop or the ninth bit) is one, the frame contains an address. When the
frame type bit is zero the frame is a data frame.
The Multi-processor Communication mode enables several slave MCUs to receive data from a
master MCU. This is done by first decoding an address frame to find out which MCU has been
addressed. If a particular slave MCU has been addressed, it will receive the following data
frames as normal, while the other slave MCUs will ignore the received frames until another
address frame is received.

20.9.1 Using MPCMn
For an MCU to act as a master MCU, it can use a 9-bit character frame format (UCSZn = 7). The
ninth bit (TXB8n) must be set when an address frame (TXB8n = 1) or cleared when a data frame
(TXB = 0) is being transmitted. The slave MCUs must in this case be set to use a 9-bit character
frame format.
The following procedure should be used to exchange data in Multi-processor Communication
mode:
1. All Slave MCUs are in Multi-processor Communication mode (MPCMn in
UCSRnA is set).
2. The Master MCU sends an address frame, and all slaves receive and read this frame. In
the Slave MCUs, the RXCn Flag in UCSRnA will be set as normal.
3. Each Slave MCU reads the UDRn Register and determines if it has been selected. If so,
it clears the MPCMn bit in UCSRnA, otherwise it waits for the next address byte and
keeps the MPCMn setting.
4. The addressed MCU will receive all data frames until a new address frame is received.
The other Slave MCUs, which still have the MPCMn bit set, will ignore the data frames.
5. When the last data frame is received by the addressed MCU, the addressed MCU sets
the MPCMn bit and waits for a new address frame from master. The process then
repeats from 2.
Using any of the 5- to 8-bit character frame formats is possible, but impractical since the
Receiver must change between using n and n+1 character frame formats. This makes fullduplex operation difficult since the Transmitter and Receiver uses the same character size setting. If 5- to 8-bit character frames are used, the Transmitter must be set to use two stop bit
(USBSn = 1) since the first stop bit is used for indicating the frame type.
Do not use Read-Modify-Write instructions (SBI and CBI) to set or clear the MPCMn bit. The
MPCMn bit shares the same I/O location as the TXCn Flag and this might accidentally be
cleared when using SBI or CBI instructions.


So from what I gather, you have the Master and Slaves all set to use a 9-bit serial mode, where the uppermost bit (bit#8) is set to indicate Address info and cleared to indicate Data info.  With MPCMx set on the slaves, they will only bother taking in data and interpreting it if bit#8 is set, then each slave can read the incoming data (meant to be an Address Frame, could be multiple bytes I guess) and determine for itself if it wishes to listen to the rest of the data (coming in with bit#8 cleared); as long as MPCMx is set, any incoming bytes with bit#8 cleared are ignored.

The intended slave, though, having seen its address sent in the sequence of bytes with bit#8 set, will then clear its MPCMx bit so that its USART will take in & notify the running sketch/firmware of the inbound data.

This continues on indefinitely until another address frame (1+ bytes with bit#8 set) are sent.  Seems to me like it would be VERY useful for supporting RS-485 multidrop networks!  But Arduino's HardwareSerial library doesn't directly support it, although I bet it wouldn't be hard at all to work around that...

Actually on that note, here's a brainstorm of how you'd handle the master & slave sides while continuing to use Serial.* functions on the ATmega328P:
(NOTE I HAVE NOT TESTED THIS CODE AT ALL!! ... might do it later when I'm at my workbench)

Master
Code:
void setup() {
  Serial.begin(9600);
  UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01) | (UCSZ02);  // Character Size = 9-bit
}

void loop() {
  // Send an Address Frame
  UCSR0B |= (1<<TXB80);  // Set bit#8
  Serial.write(0xF8);  // Send the address (in this example, a single-byte 8bit number);
  UCSR0B &= ~(1<<TXB80);  // Clear bit #8
  // With bit#8 cleared, all further bytes are Data and the slave not responding to address 0xF8 should ignore these
  Serial.println("Hello World!");
  delay(1000);
}

Slave
Code:
#define OUR_ADDRESS 0xF8

void setup() {
  Serial.begin(9600);
  UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01) | (UCSZ02);  // Character Size = 9-bit
  UCSR0A |= (1<<MPCM0);  // Enable Multi-Processor Slave Mode; only address-frame bytes will be read
}

void loop() {
  char buf[64];
  int i;

  while (!Serial.available())  // Waiting for the call...
    ;
  if (OUR_ADDRESS == Serial.read()) {
    // We've been called!
    // Clear MPCM0 so we're listening to data bytes
    UCSR0A &= (1<<MPCM0);
    // Ordinarily at this point we'd need some way to determine when the master is done sending to us, so we can raise MPCM0 again?
    // For this simple example I'll just read whatever comes through over the course of 250ms, then put MPCM0 back up.
    // A proper example would have some "end-of-frame" byte embedded in the protocol somehow.
    delay(250);  // give Serial library time to stuff its buffer with incoming contents
    i=0;
    while (Serial.available())
      buf[i++] = Serial.read();
    buf[i] = '\0';  // null-terminate the buffer
    // do something with buf (can't print it to screen, we're already using the USART!)
    // ...

    // Raise MPCM0
    UCSR0A |= (1<<MPCM0);
  }
}

I guess a point of ambiguity in the datasheet is what happens to a slave with MPCM0 disabled if the master suddenly sends an address frame (bit#8 set)?  Does it receive it?  Maybe that'd be a way to signify the end-of-packet -- an Address Frame is sent with value 0x00, no slaves respond to that address but the previously "called" slave will see it as a signal to stop reading & processing data and raise MPCM0, going back to "waiting to be called" mode.
Logged

Pages: [1]   Go Up
Jump to: