# Sending and recieving 24 bit of ADC data in UART

#### Hiril Patel

Joined Dec 25, 2021
11
Hello respective members,

I need an advice for managing uart data so here is what i think i am doing :
1. I have 4 channel ADC out of which I use 3 channels.
2. I take one sample(24bit) from each channel sequestially via SPI with MCU MSP430f5529.
3. I transmit byte by byte to serial port in terminal or matlab.

So my question is how do i manage data the data coming in matlab contains 1 byte out of 4 byte from one channel and and then this is repeated for other two channels as well.

here is code for transmission of ADC data from MCU

Code:
//                   MSP430F552x
//                 -----------------
//            /|\ |                 |
//             |  |                 |
//    Master---+->|RST              |
//                |                 |
//                |             P3.0|-> Data Out (UCB0SIMO)
//                |                 |
//                |             P3.1|<- Data In (UCB0SOMI)
//                |                 |
//                |             P3.2|-> Serial Clock Out (UCB0CLK)
//
//

//******************************************************************************

#include <msp430.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

unsigned int j = 0x00;
unsigned int i;
unsigned int k;
unsigned char temp;
char string[32];
float voltage;
int channel;

static void putDataToUart(uint8_t byte);
static void putDataToUart(uint8_t byte){
UCA0TXBUF = byte;
while(!UCBUSY);
//  __delay_cycles(5000);
}

int main(void)
{

WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
//  P2DIR &= ~BIT6;         //DRDY_ pin

//******************SPI****************************//
P3SEL |= BIT0+BIT1+BIT2;                       // P3.3,4 option select
UCB0CTL1 |= UCSWRST;                      // **Put state machine in reset**
UCB0CTL0 |= UCSYNC+UCMSB+UCMST;          // 3-pin, 8-bit SPI Master, MSB
UCB0CTL1 |= UCSSEL_2;                    // SMCLK 16Mhz
UCB0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

//***************UART********************//

P3SEL |= BIT3+BIT4;                       // P3.3,4 = USCI_A0 TXD/RXD
UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**
UCA0CTL1 |= UCSSEL_2;                     // SMCLK
UCA0BR0 = 9;                              // 1MHz 115200 (see User's Guide)
UCA0BR1 = 0;                              // 1MHz 115200
UCA0MCTL |= UCBRS_1 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0
UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

channel |= 0;
volatile uint8_t tmp = 0;
__delay_cycles(1000);

int crChannel = 0;

while(1)
{
if(crChannel == 0)
{
UCA0TXBUF = '/';
while(!UCBUSY);
}
__delay_cycles(1000);
while(0 != P2IN & BIT6);
uint8_t tmp = ((adc_value1 >> 24) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the third byte data*/
tmp = ((adc_value1 >> 16) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the second byte data*/
tmp = ((adc_value1 >> 8) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the first byte data*/
tmp =  (uint8_t )(adc_value1 & 0x000000FF);
/*put the first byte to Uart*/
putDataToUart(tmp);

crChannel += 1;
crChannel %= 3;

}
}

#### ApacheKid

Joined Jan 12, 2015
720
Hello respective members,

I need an advice for managing uart data so here is what i think i am doing :
1. I have 4 channel ADC out of which I use 3 channels.
2. I take one sample(24bit) from each channel sequestially via SPI with MCU MSP430f5529.
3. I transmit byte by byte to serial port in terminal or matlab.

So my question is how do i manage data the data coming in matlab contains 1 byte out of 4 byte from one channel and and then this is repeated for other two channels as well.

here is code for transmission of ADC data from MCU

Code:
//                   MSP430F552x
//                 -----------------
//            /|\ |                 |
//             |  |                 |
//    Master---+->|RST              |
//                |                 |
//                |             P3.0|-> Data Out (UCB0SIMO)
//                |                 |
//                |             P3.1|<- Data In (UCB0SOMI)
//                |                 |
//                |             P3.2|-> Serial Clock Out (UCB0CLK)
//
//

//******************************************************************************

#include <msp430.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

unsigned int j = 0x00;
unsigned int i;
unsigned int k;
unsigned char temp;
char string[32];
float voltage;
int channel;

static void putDataToUart(uint8_t byte);
static void putDataToUart(uint8_t byte){
UCA0TXBUF = byte;
while(!UCBUSY);
//  __delay_cycles(5000);
}

int main(void)
{

WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
//  P2DIR &= ~BIT6;         //DRDY_ pin

//******************SPI****************************//
P3SEL |= BIT0+BIT1+BIT2;                       // P3.3,4 option select
UCB0CTL1 |= UCSWRST;                      // **Put state machine in reset**
UCB0CTL0 |= UCSYNC+UCMSB+UCMST;          // 3-pin, 8-bit SPI Master, MSB
UCB0CTL1 |= UCSSEL_2;                    // SMCLK 16Mhz
UCB0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

//***************UART********************//

P3SEL |= BIT3+BIT4;                       // P3.3,4 = USCI_A0 TXD/RXD
UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**
UCA0CTL1 |= UCSSEL_2;                     // SMCLK
UCA0BR0 = 9;                              // 1MHz 115200 (see User's Guide)
UCA0BR1 = 0;                              // 1MHz 115200
UCA0MCTL |= UCBRS_1 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0
UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

channel |= 0;
volatile uint8_t tmp = 0;
__delay_cycles(1000);

int crChannel = 0;

while(1)
{
if(crChannel == 0)
{
UCA0TXBUF = '/';
while(!UCBUSY);
}
__delay_cycles(1000);
while(0 != P2IN & BIT6);
uint8_t tmp = ((adc_value1 >> 24) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the third byte data*/
tmp = ((adc_value1 >> 16) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the second byte data*/
tmp = ((adc_value1 >> 8) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the first byte data*/
tmp =  (uint8_t )(adc_value1 & 0x000000FF);
/*put the first byte to Uart*/
putDataToUart(tmp);

crChannel += 1;
crChannel %= 3;

}
}
That MCU only offers a 10-bit and a 12-bit ADC resolution. A 24 bit resolution ADC sampling a max of say 5 volts, would equate to about 0.3 μV per bit, which seems incredibly low, likely to be swamped by noise unless this is a very special design.

Last edited:

#### nsaspook

Joined Aug 27, 2009
9,987

#### Hiril Patel

Joined Dec 25, 2021
11

It looks like he's sending 32-bits per 24-bit sample via the UART (putDataToUart(tmp) four times). If so then ADC channel information can be encoded in the spare bits in the 32-bit frame outside of the data bits.

yes, thats the only way i knew to transfer 24bit of data. Buy can you please suggest me how to encode channel information in it? Plus I am sending a my own made delimiter break which you can see on line 86 "/". this was done to use split command in matlab for the intial data spliting.

#### nsaspook

Joined Aug 27, 2009
9,987
Your delimiter could be any set of characters that also encode channel information like the ASCII characters [0..3].

#### Ian0

Joined Aug 7, 2020
5,510
If you have a standard 8-bit UART, send the first byte with bit 7 set, and the subsequent bytes with bit 7 clear.
That leaves you 28 bits in which to encode the data, 24 data bits and 4 channel bits.
Then when your receiving UART receives a byte, if bit 7 is set it knows it is the first of four data bytes, and you can use shift instructions to reassemble the data.
This is pretty much the same was as MIDI handles it.

#### Hiril Patel

Joined Dec 25, 2021
11
If you have a standard 8-bit UART, send the first byte with bit 7 set, and the subsequent bytes with bit 7 clear.
That leaves you 28 bits in which to encode the data, 24 data bits and 4 channel bits.
Then when your receiving UART receives a byte, if bit 7 is set it knows it is the first of four data bytes, and you can use shift instructions to reassemble the data.
This is pretty much the same was as MIDI handles it.
I am not totally sure how do i understand it. can you please rephrase it. I am really new to programming anything.

#### Ian0

Joined Aug 7, 2020
5,510
I am not totally sure how do i understand it. can you please rephrase it. I am really new to programming anything.
Look up the format for MIDI data.

#### nsaspook

Joined Aug 27, 2009
9,987
I am not totally sure how do i understand it. can you please rephrase it. I am really new to programming anything.
That will work but it takes extra programming to encode and decode data. If your data speed requirements are not critical it might be easier (for a new programmer) just to add a 5th byte to your data stream as the ADC selected number for MATLAB to decode as the data channel.
byte
0 delimiter "/"
1 channel "0..3" from the crChannel variable
2..4 24 bits of ADC data.

#### Ian0

Joined Aug 7, 2020
5,510
Worth mentioning that there has to be something unique about the first byte. If it is a binary value that can happen randomly as the output of the A/D then the receiver is going to get confused when that happens, and with 3 bytes of data, it's going to happen once in every 85 times the data is sent.
For example if you use "/" which is ASCII 47 to indicate the start of the data transmission, what will happen if the data you are sending happens to be 47?
If your micro has a 9-bit UART then you can set the 9th bit to indicate that it is the first byte of the transmission.

#### nsaspook

Joined Aug 27, 2009
9,987
Worth mentioning that there has to be something unique about the first byte. If it is a binary value that can happen randomly as the output of the A/D then the receiver is going to get confused when that happens, and with 3 bytes of data, it's going to happen once in every 85 times the data is sent.
For example if you use "/" which is ASCII 47 to indicate the start of the data transmission, what will happen if the data you are sending happens to be 47?
If your micro has a 9-bit UART then you can set the 9th bit to indicate that it is the first byte of the transmission.
I normally handle this with inter-character and inter-frame timing with async data transmission in simple protocols (with ack/nack) because you have distinct stop/start data barriers to reset start of frame and expected next data bytes. It's nice to have out of band signaling but it's not a absolute requirement for things like MODBUS.

https://forums.ni.com/t5/Instrument...ntermessage-delay-considerations/td-p/1993285

#### Ian0

Joined Aug 7, 2020
5,510
I normally handle this with inter-character and inter-frame timing with async data transmission in simple protocols (with ack/nack) because you have distinct stop/start data barriers to reset start of frame and expected next data bytes. It's nice to have out of band signaling but it's not a absolute requirement for things like MODBUS.

View attachment 269805
https://forums.ni.com/t5/Instrument...ntermessage-delay-considerations/td-p/1993285
Agreed - it also has something unique about the first byte - it is preceded by 3½ characters of silence.
DMX has a different solution - it starts with a line-break of >2 characters.
MIDI uses 7-bit data with the 8th bit set for the first byte and cleared for the remainder.

The TS is a novice programmer (by his own admission): timestamping each received byte and determining if it is >3.5 character times since the last one maybe isn't the easiest thing to program!

#### Hiril Patel

Joined Dec 25, 2021
11
Look up the format for MIDI data.

ok so I semi-followed your advice in a way and put unique delimiters before the transaction of each channel;:

channel 1 has delimiter = /A/
channel 2 has delimiter = /B/
channel 3 has delimiter = /C/
and now my code look like this

Code:
//                   MSP430F552x
//                 -----------------
//            /|\ |                 |
//             |  |                 |
//    Master---+->|RST              |
//                |                 |
//                |             P3.0|-> Data Out (UCB0SIMO)
//                |                 |
//                |             P3.1|<- Data In (UCB0SOMI)
//                |                 |
//                |             P3.2|-> Serial Clock Out (UCB0CLK)
//
//

//******************************************************************************

#include <msp430.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

unsigned int j = 0x00;
unsigned int i;
unsigned int k;
unsigned char temp;
char string[32];
float voltage;
int channel;

static void putDataToUart(uint8_t byte);

static void putDataToUart(uint8_t byte){

UCA0TXBUF = byte;
__delay_cycles(1000);
}

int main(void)
{

WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
//  P2DIR &= ~BIT6;         //DRDY_ pin

//******************SPI****************************//
P3SEL |= BIT0+BIT1+BIT2;                       // P3.3,4 option select
UCB0CTL1 |= UCSWRST;                      // **Put state machine in reset**
UCB0CTL0 |= UCSYNC+UCMSB+UCMST;          // 3-pin, 8-bit SPI Master, MSB
UCB0CTL1 |= UCSSEL_2;                    // SMCLK 16Mhz
UCB0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

//***************UART********************//

P3SEL |= BIT3+BIT4;                       // P3.3,4 = USCI_A0 TXD/RXD
UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**
UCA0CTL1 |= UCSSEL_2;                     // SMCLK
UCA0BR0 = 9;                              // 1MHz 115200 (see User's Guide)
UCA0BR1 = 0;                              // 1MHz 115200
UCA0MCTL |= UCBRS_1 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0
UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

channel |= 0;
volatile uint8_t tmp = 0;
__delay_cycles(1000);

int crChannel = 0;

while(1)
{
if(crChannel == 0)
{

UCA0TXBUF = '/';
__delay_cycles(1000);
UCA0TXBUF = 'A';
__delay_cycles(1000);
UCA0TXBUF = '/';
__delay_cycles(1000);

}
if(crChannel == 1)
{

UCA0TXBUF = '/';
__delay_cycles(1000);
UCA0TXBUF = 'B';
__delay_cycles(1000);
UCA0TXBUF = '/';
__delay_cycles(1000);
}
if(crChannel == 2)
{
UCA0TXBUF = '/';
__delay_cycles(1000);
UCA0TXBUF = 'C';
__delay_cycles(1000);
UCA0TXBUF = '/';
__delay_cycles(1000);

}
while(0 != P2IN & BIT6);
uint8_t tmp = ((adc_value1 >> 24) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the third byte data*/
tmp = ((adc_value1 >> 16) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the second byte data*/
tmp = ((adc_value1 >> 8) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the first byte data*/
tmp =  (uint8_t )(adc_value1 & 0x000000FF);
/*put the first byte to Uart*/
putDataToUart(tmp);

crChannel += 1;
crChannel %= 3;
}

is this any near to correct way? Since I am seperating this data in matlab using split command

it works but sometimes there is a miss in splitting data

#### nsaspook

Joined Aug 27, 2009
9,987
Agreed - it also has something unique about the first byte - it is preceded by 3½ characters of silence.
DMX has a different solution - it starts with a line-break of >2 characters.
MIDI uses 7-bit data with the 8th bit set for the first byte and cleared for the remainder.

The TS is a novice programmer (by his own admission): timestamping each received byte and determining if it is >3.5 character times since the last one maybe isn't the easiest thing to program!
The timing is not very critical for a simple data line of a few meters where the bit-error rate is practically zero (short distances with moderate bit-rates). I've run CRC checked comma delimited data streams in to the many billions of characters broadcasted with zero errors in an office environment. This works when the software properly detects both TX and RX buffer status and empty shift registers on both ends. Fixed delay timing for spacing each UART transmit is looking for trouble. The much better way is to actually check for clear to TX buffer status, before stuffing the buffer with more data if you're operating in polled mode.

Last edited:

#### nsaspook

Joined Aug 27, 2009
9,987
ok so I semi-followed your advice in a way and put unique delimiters before the transaction of each channel;:

channel 1 has delimiter = /A/
channel 2 has delimiter = /B/
channel 3 has delimiter = /C/
and now my code look like this

Code:
//                   MSP430F552x
//                 -----------------
//            /|\ |                 |
//             |  |                 |
//    Master---+->|RST              |
//                |                 |
//                |             P3.0|-> Data Out (UCB0SIMO)
//                |                 |
//                |             P3.1|<- Data In (UCB0SOMI)
//                |                 |
//                |             P3.2|-> Serial Clock Out (UCB0CLK)
//
//

//******************************************************************************

#include <msp430.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

unsigned int j = 0x00;
unsigned int i;
unsigned int k;
unsigned char temp;
char string[32];
float voltage;
int channel;

static void putDataToUart(uint8_t byte);

static void putDataToUart(uint8_t byte){

UCA0TXBUF = byte;
__delay_cycles(1000);
}

int main(void)
{

WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
//  P2DIR &= ~BIT6;         //DRDY_ pin

//******************SPI****************************//
P3SEL |= BIT0+BIT1+BIT2;                       // P3.3,4 option select
UCB0CTL1 |= UCSWRST;                      // **Put state machine in reset**
UCB0CTL0 |= UCSYNC+UCMSB+UCMST;          // 3-pin, 8-bit SPI Master, MSB
UCB0CTL1 |= UCSSEL_2;                    // SMCLK 16Mhz
UCB0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

//***************UART********************//

P3SEL |= BIT3+BIT4;                       // P3.3,4 = USCI_A0 TXD/RXD
UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**
UCA0CTL1 |= UCSSEL_2;                     // SMCLK
UCA0BR0 = 9;                              // 1MHz 115200 (see User's Guide)
UCA0BR1 = 0;                              // 1MHz 115200
UCA0MCTL |= UCBRS_1 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0
UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

channel |= 0;
volatile uint8_t tmp = 0;
__delay_cycles(1000);

int crChannel = 0;

while(1)
{
if(crChannel == 0)
{

UCA0TXBUF = '/';
__delay_cycles(1000);
UCA0TXBUF = 'A';
__delay_cycles(1000);
UCA0TXBUF = '/';
__delay_cycles(1000);

}
if(crChannel == 1)
{

UCA0TXBUF = '/';
__delay_cycles(1000);
UCA0TXBUF = 'B';
__delay_cycles(1000);
UCA0TXBUF = '/';
__delay_cycles(1000);
}
if(crChannel == 2)
{
UCA0TXBUF = '/';
__delay_cycles(1000);
UCA0TXBUF = 'C';
__delay_cycles(1000);
UCA0TXBUF = '/';
__delay_cycles(1000);

}
while(0 != P2IN & BIT6);
uint8_t tmp = ((adc_value1 >> 24) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the third byte data*/
tmp = ((adc_value1 >> 16) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the second byte data*/
tmp = ((adc_value1 >> 8) & 0xff);
/*put the first byte to Uart*/
putDataToUart(tmp);

/*prepare the first byte data*/
tmp =  (uint8_t )(adc_value1 & 0x000000FF);
/*put the first byte to Uart*/
putDataToUart(tmp);

crChannel += 1;
crChannel %= 3;
}

is this any near to correct way? Since I am seperating this data in matlab using split command

it works but sometimes there is a miss in splitting data
C:
     crChannel += 1;
crChannel %= 3;
For a 0,1,2 result for crChannel.

channel 1 has delimiter = /A/
channel 2 has delimiter = /B/
channel 3 has delimiter = /C/
0 usually the first channel number in C. Here you have four possible ADC channels of 0,1,2,3. It's good C programming to always handle all possibilities using 0 as the origin in code and comments

What happens to processing the delimiters when crChannel == 3 for 'D'? Your crChannel masking operator should be designed for a 0,1,2,3 result, so you should handle 0,1,2 AND 3 delimiter prefix as a code safety feature.

Change channels[3] to channels[4] and add the additional correct initializer for MUX_3.
C:
int channels[3] = {ADS1220_MUX_0_G, ADS1220_MUX_1_G, ADS1220_MUX_2_G};

Last edited:

#### Hiril Patel

Joined Dec 25, 2021
11
C:
     crChannel += 1;
crChannel %= 3;
For a 0,1,2 result for crChannel.

0 usually the first channel number in C. Here you have four possible ADC channels of 0,1,2,3. It's good C programming to always handle all possibilities using 0 as the origin in code and comments

What happens to processing the delimiters when crChannel == 3 for 'D'? Your crChannel masking operator should be designed for a 0,1,2,3 result, so you should handle 0,1,2 AND 3 delimiter prefix as a code safety feature.

Change channels[3] to channels[4] and add the additional correct initializer for MUX_3.
C:
int channels[3] = {ADS1220_MUX_0_G, ADS1220_MUX_1_G, ADS1220_MUX_2_G};

oh, sorry about that. i made a typing mistake in explanation, I use channel 0,1,2 in adc. Anyway it worked now, thank you all million times.

#### nsaspook

Joined Aug 27, 2009
9,987
That MCU only offers a 10-bit and a 12-bit ADC resolution. A 24 bit resolution ADC sampling a max of say 5 volts, would equate to about 0.3 μV per bit, which seems incredibly low, likely to be swamped by noise unless this is a very special design.
It's possible to get very good results from that chip without too much special design.

#### Hiril Patel

Joined Dec 25, 2021
11
Wow, I never found this work on ADS1220 till now but definitely bookmarking this for future references in the coming weeks.

I have one more question which is not the topic of the thread but I am wondering if you can advise me on this :

I got incoming serial data with above mentioned delimiters and I am using "adc_data = split(data,["/A/","/B/","/C/"]);" command to split them with delimiters but what it does is make a single column cell of splitted data.

here is the image:

Any suggestion how to deal with this?

#### nsaspook

Joined Aug 27, 2009
9,987
Sorry, not a MATLAB guy.

#### ag-123

Joined Apr 28, 2017
237
I'm not familiar with MATLAB. But if it is about encoding and decoding data. One of those things I'd commonly do is to pack my data as 00 ch3 ch2 ch1 in 32 bit words, assuming that I'd only need 8 bits per channel, then over at the receiving end, I'd unpack that same 32 bit in the same sequence. The benefit here is it is 'easier' in a sense that each set of 4 bytes typically fits a 32bit int machine word on the host. so it goes something like

Code:
ch3 = (word >> 16) & 0xff;
ch2 = (word >> 8) & 0xff;
ch1 = word & 0xff;
if it is 24 bits data for every sample, then i'd use the top 4 bits to indicate the channel and the rest is the adc data, ch (4 bits) data (24 bits)
hence for each sample it'd look like
Code:
ch = (word >> 24) & 0xff;
data = word & 0xffffff;
The easiest way to achieve sync is for the host side to issue a command, then the mcu start sampling and transmitting. This normally alleviate framing requirements. This is particularly true for usb transmissions, they are 'reliable' in the sense that the 'lower levels' already handles all that framing.
if it is plain usb (CDC (ACM) serial), it is normally 8 bit clean.

Last edited: