NASA MUX 16 Channel Firmware
Serial Control Protocol
The serial control interface is a basic Rx/Tx UART with the following settings:
- Baudrate: 9600
- Data Bits: 8
- Stop Bits: 1
- Flow Control: None
- Format: Bytes (as apposed to ASCII)
- 16 bit CRC using the CRC16-IBM polynomial (example code provided)
The serial protocol is inspired by Modbus. Do not follow the Modbus specification, instead follow the one layed down here.
The basic packet format is as follows in Table 1:
Name | Bytes | Description |
---|---|---|
START | 1 | The start byte is the FRAME_START magic byte (0x81) |
CMD | 1 | The command to be executed (ACK, WR_REG, ERR, etc) |
DATA | n | The length n and the actual content of data is determined by CMD |
CRC | 2 | The CRC is a 16-bit CRC which uses the CRC16-IBM polynomial. the CRC is sent with the low byte first. |
END | 1 | The end byte is the FRAME_END magic byte (0x82) |
There are magic bytes which effect the packets:
Name | Magic Byte | Description |
---|---|---|
ESCAPE | 0x80 | Escapes magic bytes to deactivates their magical properties. |
FRAME_START | 0x81 | Indicates the start and the end of a packet, unless escaped. |
FRAME_END | 0x82 | Indicates the start and the end of a packet, unless escaped. |
The magic bytes must be escaped if they must occur in data. For example, if I wanted to send a 0x81 in the data then I will need to escape it and send two bytes instead of one.
I would then send 0x80 0x81
.
Magic bytes, unless they are escaped, are not included in the CRC calculation.
Commands
FIXME: Insert Command Table for quick reference.
ACK: Acknowledge (0x83)
ACK is just an acknowledgement, ACK should only be sent as a response. The contents of ACK depend on the command sent. For example, ACK will have a data length of 0 for WR_REG or WR_BLOCK commands. However, For RD_REG and RD_BLOCK commands (read commands in general) it will hold the requested information in the data section.
Data Format (n = ?): Data packet in ACK specified by command.
Example Packet:
Time | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
Packet | 0x81 | 0xf0 | 0xBF | 0x04 | 0x82 |
Response: ACK with n = 0.
ERR: Error (0x84)
ERR returns an error Data Format (n = 1):
Name | Bytes | Description |
---|---|---|
Type | 1 | Describes the error type |
Error Types:
Name | Byte | Description |
---|---|---|
GEN | 0x00 | General Error |
CRC | 0x01 | CRC Not Valid |
BAD_PACKET | 0x02 | Packet has a bad format at some point |
BAD_ADDRESS | 0x03 | Packet has an invalid address |
FRAME | 0x04 | Unexpected or spurious FRAME_START byte |
Response: No response expected to ERR.
Example Packet:
Time | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
Packet | 0x81 | 0xf0 | 0xBF | 0x04 | 0x82 |
WR_REG: Write Register (0x85)
Data Format (n = 3):
Name | Bytes | Description |
---|---|---|
Address | 1 | Address MSB First |
Data | 2 | Data sent MSB first |
Response: ACK with data length = 0.
Example Packet (Turn off LED):
Time | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
Packet | 0x81 | 0x85 | 0x00 | 0x00 | 0x00 | 0x29 | 0x28 | 0x82 |
READ_REG: Read Register (0x86)
Data Format (n = 1):
Name | Bytes | Description |
---|---|---|
Address | 1 | Address MSB First |
Response: ACK with data length = 2, which contains the 16 bits in the register. MSB first.
Example Packet (Read value from 0x10 [AD5504 Channel 1]):
Time | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
Packet | 0x81 | 0x86 | 0x10 | 0x62 | 0x1C | 0x82 |
WR_BLOCK: Write Block (0x86) [NOT IMPLEMENTED]
READ_BLOCK: Read Block (0x87) [NOT IMPLEMENTED]
DISABLE_CRC: Disable CRC Checks on the MSP430 (0xf0) [USE WITH CAUTION]
CAUTION By disabling CRC you still need to send the CRC bytes, they just are not checked. The suggestion is that you send the CRC 0x0000 or 0xDEAD.
Data Format (n = 0): No data sent
Response: ACK with data length = 2, which is 0xDEAD. MSB first.
Example Packet:
Time | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
Packet | 0x81 | 0xf0 | 0xBF | 0x04 | 0x82 |
If you send the packed 0x81f0BF0482 (send MSB first) you should be able to disable the CRC and not worry about calculating the correct CRC values.
ENABLE_CRC: Enables CRC Checks on the MSP430 (0xf1) [DEFAULT]
Data Format (n = 0): No data sent
Response: ACK with data length = 2, which is 0xBEEF. MSB first.
CRC
The CRC used is the CRC-16 used by MODBUS and implemented the same way. While all data is sent Most Significant Byte first the CRC must be sent Least Signigicant Byte First. This is do to the way that the CRC algorithm is computed on the MSP430. You can type in a message here and see what the CRC is (look at CRC-16 (Modbus)
).
IMPORTANT: Unescaped magic bytes are not included in CRCs (this includes an unescaped ESCAPE byte). This is to simplify processing where the message can be processed without ESCAPE_ bytes which are then added in where needed at send time rather than while forming the packet.
Included here is some example CRC code for using the CRC-16 table I implemented. Code is written in C.
Library (src/crc16/crc16.c
and include/crc16/crc16.h
)
#include "crc16/crc16.h"
const uint16_t crc16_ibm_table[] = {
0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };
void crc_init(uint16_t *crc) {
*crc = CRC_INIT_VALUE;
}
void crc_add_byte(uint16_t *crc, uint8_t byte) {
uint8_t temp = (*crc) ^ byte;
(*crc) >>= 8;
(*crc) ^= CRC_TABLE[temp];
}
char crc_check(uint16_t crc) {
return (crc == CRC_FINAL_VALUE);
}
Example Use (Checking CRC):
#include <stdint.h>
#include "crc16/crc16.h"
#define LEN 6
// When this message is run through the CRC algorithm it should result in a 0.
uint8_t message[LEN] = {0x85, 0x00, 0x00, 0x00, 0x29, 0x28};
uint16_t crc;
crc_init(&crc)
// Initialize the crc
for (int i = 0; i < LEN; i++) {
crc_add_byte(&crc, message[i]);
}
if (crc == 0) {
printf("Success\n");
} else {
printf("Failure\n");
}
Example Use (Generating CRC):
#include <stdint.h>
#include "crc16/crc16.h"
#define LEN 4
// When this message is run through the CRC algorithm it should result in a 0x2829.
uint8_t message[LEN] = {0x85, 0x00, 0x00, 0x00};
uint16_t crc;
// Initialize the crc
crc_init(&crc)
for (int i = 0; i < LEN; i++) {
crc_add_byte(&crc, message[i]);
}
printf("CRC-16 (Modbus): 0x%04x\n", crc)
printf("Formatted to be sent: { 0x%02x, 0x%02x }", crc&0xff, crc>>8)
Address Table
Name | Address | Description |
---|---|---|
Settings | 0x00 | Settings Register: Bit 0 controls the Red LED on the MSP430 |
AD5504 #1 | 0x10 | The bottom 12 bits control the value of AD5504 1 (R1C1) |
AD5504 #2 | 0x11 | The bottom 12 bits control the value of AD5504 2 (R1C2) |
AD5504 #3 | 0x12 | The bottom 12 bits control the value of AD5504 3 (R1C3) |
AD5504 #4 | 0x13 | The bottom 12 bits control the value of AD5504 4 (R1C4) |
AD5504 #5 | 0x14 | The bottom 12 bits control the value of AD5504 5 (R2C1) |
AD5504 #6 | 0x15 | The bottom 12 bits control the value of AD5504 6 (R2C2) |
AD5504 #7 | 0x16 | The bottom 12 bits control the value of AD5504 7 (R2C3) |
AD5504 #8 | 0x17 | The bottom 12 bits control the value of AD5504 8 (R2C4) |
AD5504 #9 | 0x18 | The bottom 12 bits control the value of AD5504 9 (R3C1) |
AD5504 #10 | 0x19 | The bottom 12 bits control the value of AD5504 10 (R3C2) |
AD5504 #11 | 0x1a | The bottom 12 bits control the value of AD5504 11 (R3C3) |
AD5504 #12 | 0x1b | The bottom 12 bits control the value of AD5504 12 (R3C4) |
AD5504 #13 | 0x1c | The bottom 12 bits control the value of AD5504 13 (R4C1) |
AD5504 #14 | 0x1d | The bottom 12 bits control the value of AD5504 14 (R4C2) |
AD5504 #15 | 0x1e | The bottom 12 bits control the value of AD5504 15 (R4C3) |
AD5504 #16 | 0x1f | The bottom 12 bits control the value of AD5504 16 (R4C4) |
DAC7512 #1 | 0x20 | The bottom 12 bits control the value of DAC7512 1 (R1C1) |
DAC7512 #2 | 0x21 | The bottom 12 bits control the value of DAC7512 2 (R1C2) |
DAC7512 #3 | 0x22 | The bottom 12 bits control the value of DAC7512 3 (R1C3) |
DAC7512 #4 | 0x23 | The bottom 12 bits control the value of DAC7512 4 (R1C4) |
DAC7512 #5 | 0x24 | The bottom 12 bits control the value of DAC7512 5 (R2C1) |
DAC7512 #6 | 0x25 | The bottom 12 bits control the value of DAC7512 6 (R2C2) |
DAC7512 #7 | 0x26 | The bottom 12 bits control the value of DAC7512 7 (R2C3) |
DAC7512 #8 | 0x27 | The bottom 12 bits control the value of DAC7512 8 (R2C4) |
DAC7512 #9 | 0x28 | The bottom 12 bits control the value of DAC7512 9 (R3C1) |
DAC7512 #10 | 0x29 | The bottom 12 bits control the value of DAC7512 10 (R3C2) |
DAC7512 #11 | 0x2a | The bottom 12 bits control the value of DAC7512 11 (R3C3) |
DAC7512 #12 | 0x2b | The bottom 12 bits control the value of DAC7512 12 (R3C4) |
DAC7512 #13 | 0x2c | The bottom 12 bits control the value of DAC7512 13 (R4C1) |
DAC7512 #14 | 0x2d | The bottom 12 bits control the value of DAC7512 14 (R4C2) |
DAC7512 #15 | 0x2e | The bottom 12 bits control the value of DAC7512 15 (R4C3) |
DAC7512 #16 | 0x2f | The bottom 12 bits control the value of DAC7512 16 (R4C4) |
DAC7512 #1 Step Interval | 0x30 | Sets the step interval of the DAC7512 channel 1 R1C1 when 0x40 is set to a value greater than 1. |
DAC7512 #1 Counter | 0x40 | Sets the frequency of the steps of the DAC7512 channel 1 on R1C1. Active only when the value is greater than 1. (2-0xffff) |
IMPORTANT: For register 0x40 the frequency calculation can be approximated as follows:
f = 40kHz/(DAC7512 #1 COUNTER VALUE), where DAC7512 #1 COUNTER VALUE > 2
For reliable operation I suggest you run f near 5kHz (DAC7512 #1 Counter Value = 0x0008
)
Implementation Concerns
The current implementation on the MSP430 uses a state machine for processing which contains 3 state: IDLE, MESSAGE, ESCAPE. In the IDLE state you are not currently in a packet and are awaiting to see a start of packet FRAME_START (0x81). In the MESSAGE state you add the bytes you get to the CRC (except ESCAPE bytes), when you see the FRAME_END you check the CRC (if CRC is enabled) and then process the packet on hand. If a spurious FRAME_START comes along you clear the CRC, clear the buffer and send a FRAME_ERROR, you also try to sync into the next packet (currently problematic). If an ESCAPE byte comes during the MESSAGE state you don't CRC it or add it to the buffer and move to the ESCAPE state which simply adds the next byte to the buffer and CRC regardless of its magical properties.
+--------[FRAME_END (0x82)]-----------+
| |
V |
+------+ +-------+
| IDLE | ---[FRAME_START (0x81)]---->|MESSAGE|<---------------+
+------+ +-------+ |
| |
[ESCAPE (0x80)] |
| |
V |
+--------+ |
| ESCAPE |---[ANY BYTE]---+
+--------+