This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

UART and Sampling

Other Parts Discussed in Thread: MSP430F5438A

Hi, 

I am facing following problems 

1. The  debugger does not go inside the " while (ADC12BUSY & ADC12CTL1 == 1) " at all. I do not know why? Plus the debugger shows me the correct value in Register " ADC12MEM0" but does not show the correct value of variable "SavedADC12MEM0". 

2.  I want to send the ADC sampled values to Tera Terminal. I can communicate with tera terminal well but unable to see the decimal values of the samples. 

 

My code is as follows 

 

void Port_Init();
void adc_init(void);
void adc_start_Read(void);
void acc_read_with_offset();
void UART(int , int);

volatile int SavedADC12MEM0, SavedADC12MEM1 ;
unsigned int adc_index = 0;
//extern unsigned int SavedADC12MEM0[];

int main(void)
{
WDTCTL = WDTPW+WDTHOLD;

Port_Init();

while (1)
{
adc_init();
adc_start_Read();
// UART();
}
}


void Port_Init(void)
{
P3SEL = 0x30; // P3.4,5 = USCI_A0 TXD/RXD
P6SEL |= 0x80;
P6DIR |= 0x00;
P1DIR |= 0x01;

}


void adc_init(void)
{

REFCTL0 &= ~REFVSEL_3; //Page 693 of data sheet
REFCTL0 |= REFVSEL_2 + REFON; // Configure internal 2.5V reference
__delay_cycles(1900); // delay for reference to settle
ADC12CTL0 &= ~ADC12ENC; // Ensure ENC is clear
ADC12CTL0 |= ADC12ON | ADC12SHT0_15 | ADC12MSC ;//| ADC12REFON | ADC12REF2_5V;
ADC12CTL1 |= ADC12SHP | ADC12CONSEQ_2 |ADC12SSEL_0;
ADC12CTL2 = ADC12RES_2;
ADC12MCTL0 |= ADC12SREF_1 | ADC12INCH_7;
ADC12MCTL1 = ADC12SREF_1 | ADC12EOS;
__delay_cycles(1900);

}

void adc_start_Read(void)
{

REFCTL0 &= ~REFVSEL_3; //Page 693 of data sheet
REFCTL0 |= REFVSEL_2 + REFON; // Configure internal 2.5V reference
__delay_cycles(1900); // delay for reference to settle
ADC12CTL0 &= ~ADC12ENC; // Ensure ENC is clear
ADC12CTL0 |= ADC12ON | ADC12SHT0_15 ;//| ADC12REFON | ADC12REF2_5V;| ADC12MSC_0
ADC12CTL1 |= ADC12SHP | ADC12CONSEQ_1 |ADC12SSEL_0;
ADC12CTL2 = ADC12RES_2;
ADC12MCTL0 |= ADC12SREF_1 | ADC12INCH_7;
ADC12MCTL1 = ADC12SREF_1 | ADC12EOS;
__delay_cycles(1900);

P1DIR |= 0x01;
P1OUT = (uint8_t) (ADC12CTL1 & ADC12BUSY);
for(;;)
{
ADC12CTL0 |= ADC12ENC | ADC12SC;

while (ADC12BUSY & ADC12CTL1 == 0)
{
P1DIR |= 0x01;
P1OUT ^= 0x00;
SavedADC12MEM0 = ADC12MEM0;
SavedADC12MEM1 = ADC12MEM1;

}
SavedADC12MEM0 = 1234;
UART(SavedADC12MEM0, ADC12MEM1);
}

}
void UART(int a , int b)
{
volatile int buffer;
UCA0CTL1 |= UCSWRST; // **Put state matchine in reset**
UCA0CTL1 |= UCSSEL_3; // SMCLK
UCA0BR0 = 6; // 1MHz 9600 (see User's Guide)
UCA0BR1 = 0; // 1MHz 9600
UCA0MCTL = UCBRS_0 + UCBRF_13 + UCOS16; // Modln UCBRSx=0, UCBRFx=0,
// over sampling
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA0IE |= UCRXIE; // Enable USCI_A0 RX interrupt
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA0IE |= UCRXIE; // Enable USCI_A0 RX interrupt
buffer = a;

while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF = buffer; // TX -> RXed character
}

void adc_start_Read(void)
{

REFCTL0 &= ~REFVSEL_3; //Page 693 of data sheet
REFCTL0 |= REFVSEL_2 + REFON; // Configure internal 2.5V reference
__delay_cycles(1900); // delay for reference to settle
ADC12CTL0 &= ~ADC12ENC; // Ensure ENC is clear
ADC12CTL0 |= ADC12ON | ADC12SHT0_15 ;//| ADC12REFON | ADC12REF2_5V;| ADC12MSC_0
ADC12CTL1 |= ADC12SHP | ADC12CONSEQ_1 |ADC12SSEL_0;
ADC12CTL2 = ADC12RES_2;
ADC12MCTL0 |= ADC12SREF_1 | ADC12INCH_7;
ADC12MCTL1 = ADC12SREF_1 | ADC12EOS;
__delay_cycles(1900);

P1DIR |= 0x01;
P1OUT = (uint8_t) (ADC12CTL1 & ADC12BUSY);
for(;;)
{
ADC12CTL0 |= ADC12ENC | ADC12SC;

while (ADC12BUSY & ADC12CTL1 == 0)
{
P1DIR |= 0x01;
P1OUT ^= 0x00;
SavedADC12MEM0 = ADC12MEM0;
SavedADC12MEM1 = ADC12MEM1;

}
SavedADC12MEM0 = 1234;
UART(SavedADC12MEM0, ADC12MEM1);
}

}
void UART(int a , int b)
{
volatile int buffer;
UCA0CTL1 |= UCSWRST; // **Put state matchine in reset**
UCA0CTL1 |= UCSSEL_3; // SMCLK
UCA0BR0 = 6; // 1MHz 9600 (see User's Guide)
UCA0BR1 = 0; // 1MHz 9600
UCA0MCTL = UCBRS_0 + UCBRF_13 + UCOS16; // Modln UCBRSx=0, UCBRFx=0,
// over sampling
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA0IE |= UCRXIE; // Enable USCI_A0 RX interrupt
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA0IE |= UCRXIE; // Enable USCI_A0 RX interrupt
buffer = a;while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF = buffer; // TX -> RXed character
}

  • 1) "while (ADC12BUSY & ADC12CTL1 == 0)" Due to C operator precedence, this is always false. Try "while ((ADC12BUSY & ADC12CTL1) != 0)" [I also reversed the sense since I think that's what you have in mind.]

    2) Your UART() function resets the UART every time it sends data. Since it's probably called more than once per millisecond, the Tx byte never has a chance to get out to the wire. I suggest you move the reset code to a separate "UART_init()" function and call it only once. [Did you finally succeed in sending a known byte e.g. 'Z' or 'U' the other day?]

    3) You're writing the raw ADC value to the UART; this is fine, but keep in mind that it will probably not be "read" able to a human without a "hex display" feature in your terminal program (I don't know if TeraTerm has that feature or not.)

  • Thanks. Your reply helped alot. I am learning how to program a microcontroller for the first time.  

    Yes, I did send the alphabet to the Tera Terminal the other day. Tera Terminal does display hex. All you need to do is to click on its screen and press " Shift + Esc". 

    I am unable to understand one thing that in the following expression from my code 

     UCA0TXBUF = buffer; 

    "UCA0TXBUF" is 8 bit wide and "buffer" is defined as an 16 bit integer. But compiler does not give me an error. Why?

    Melissa

  • Short (slightly glib) answer: Because C is like that. It allows you to truncate a 16-bit variable into 8 bits on the assumption that you know what you're doing. ("You'd like to Shoot Yourself In The Foot? Allow me to assist.")

    Should the compiler issue a warning? Given 40 years of history using this idiom, probably not. But that question is an excellent way to start a, um, "lively discussion".

  • Hi, 

    I am trying to see what exactly is the sampling rate at which my code is sampling the data.  The formula given in the User Guide is as follows

    tsample > (RS + RI) × ln(2n+1) × CI + 800 ns

    I tried to toggle the LED to monitor ABUSY signal but either it changes so quickly that I can not see it changing on the scope though I can see it changing in the "Register" section of the compiler. 

    I am using MCLK ( I do not know the frequency of MCLK plus I do not know how to see it on the scope). I am also using 1024 ADC clock cycles. I think that the total sampling time would be 

    tsample + tsync +tconvert = Total Sampling time ; Am I Right?

    void adc_start_Read(void)

    {

    REFCTL0 &= ~REFVSEL_3; //Page 693 of data sheet
    REFCTL0 |= REFVSEL_2 + REFON; // Configure internal 2.5V reference
    __delay_cycles(1900); // delay for reference to settle
    ADC12CTL0 &= ~ADC12ENC; // Ensure ENC is clear
    ADC12CTL0 |= ADC12ON | ADC12SHT0_15 | ADC12MSC ;//| ADC12REFON | ADC12REF2_5V;
    ADC12CTL1 |= ADC12SHP | ADC12CONSEQ_2 |ADC12SSEL_0;
    ADC12CTL2 = ADC12RES_2;
    ADC12MCTL0 |= ADC12SREF_1 | ADC12INCH_7;
    ADC12MCTL1 = ADC12SREF_1 | ADC12EOS;
    __delay_cycles(1900);

    for(;;)
    {
    ADC12CTL0 |= ADC12ENC | ADC12SC;

    while ((ADC12BUSY & ADC12CTL1)!=0)
    {
    P1DIR |= 0x01;
    P1OUT = (uint8_t) (ADC12CTL1 & ADC12BUSY);
    ADC12CTL0 &= ~ADC12ENC;

    SavedADC12MEM0 = ADC12MEM0;
    SavedADC12MEM1 = ADC12MEM1;
    P1OUT = (uint8_t) (ADC12CTL0 & ADC12ENC);
    UART_SEND (SavedADC12MEM0, ADC12MEM1);

    }

    }
    }

  • 1) SSEL_0 requests MODCLK, which is roughly 5MHz.

    2) The second formula should give you the full conversion time for a single channel. Tsample+Tsync+Tconvert = (1024+1+13)/5MHz, or roughly 208 usec. Then multiply by 2 since you're sampling 2 channels.

    3) The P1OUT trick is clever, but it doesn't scale, since it relies on the fact that ADC12BUSY == 1 == BIT0. It doesn't e.g. extend to ADC12ENC. I suggest simple if() statements that explicitly set P1.0 high or low.

    4) Sending a single UART byte at 9600bps takes about 1ms, more than twice the time you should expect the ADC to be busy. Inside the wait-for-completion loop this will distort your timings.

    I suggest simplifying this:

    A) Use CONSEQ_1 so the ADC stops after 2 channels. Leave MSC=1 so you get both channels.

    B) Set: P1OUT |= 0x01;   // LED on

    C) Set: ADC12CTL0 |= ADC12SC;  // Start conversion sequence

    D) Wait for completion: while ((ADC12CTL1 & ADC12BUSY)!=0) /*EMPTY*/;  // spin until complete

    E) Clear: P1OUT &= ~0x01; // LED off

    F) Send the MEM0/MEM1 values through the UART. This will have the side-effect of giving you a visible "idle" space on your scope.

    Expect the LED to only be on for about 416 usec; you won't be able to see it on the LED, but should be able to see it on a scope.

  • Hi, 

    Questions: 

    1. I am unable to find the maximum clock frequency for  MSP430F5438A . What is the maximum clock frequency this processor can work on?

    2. How can I check MODCLK with scope? I can see MCLK, ACLK and SMCLK.

    3. How did you know that single UART byte will take 1msec?

    4. Is there any command that I can use to write the ADC sampled data into a file? Like I used to do in C by using fopen() function.

    5. I changed the code to see the sampling rate on the scope but no luck so far. I made the following change . 

    Melissa

    void adc_start_Read(void)
    {
    for(;;)
    {
    ADC12CTL0 |= ADC12ENC | ADC12SC;

    while ((ADC12BUSY & ADC12CTL1)!=0)
    {
    P1DIR |= 0x01;
    P1OUT |= 0x01;
    ADC12CTL0 &= ~ADC12ENC;
    SavedADC12MEM0 = ADC12MEM0;
    SavedADC12MEM1 = ADC12MEM1;
    }

    if ((ADC12BUSY & ADC12CTL1)==0)
    {
    P1OUT &=~ 0x01;
    UART_SEND (SavedADC12MEM0, ADC12MEM1);
    }
    }
    }

  • melissa walraven said:
    1. I am unable to find the maximum clock frequency for  MSP430F5438A . What is the maximum clock frequency this processor can work on?

    Usually to find such data, you shall read datasheet of the chip in question which is first download link in the product page. You shall read page 40, "Recommended Operating Conditions" table where you will find data you are looking for.

  • 1) The first page of the data sheet (slas655d) says "Up to 25 MHz system clock". Achieving this is not quite as simple as flipping a bit.

    2) I don't think you can get MODCLK on a pin, but since you're using it for ADC12CLK, you should be able to get it on the ADC12CLK pin (P2.7). The recipe is in slas655d Table 45 (P2DIR.7=1, P2SEL.7=1).

    3) A UART byte (8N1) is 10 bits (i.e. add start and stop bits). So 9600bps is (9600/10)=960 byte/sec. 1/960 byte/sec is just over 1ms/byte.

    4) The short answer: No. The MCU provides no large-scale data storage. One common way to do this is by attaching an SD card. Integrating this would be a project (hardware+software) in itself. For now I suggest sending the samples over the UART and storing them on the host (PC).

    5) What does "no luck" mean precisely? Does P1.0 stay high? stay low? What does the debugger tell you?

  • Hi, 

    It stays low most of the time. It goes high but very fast that I can not even see it on the scope. I am not getting your predicted period of 416usec. 

    The debugger is not a good tool to monitor the status of ADC12BUSY or SAMPCON or SHI. 

    I will be interfacing MSP with the SD Card soon. 

    Melissa

  • I'm not sure I understand: How do you know the P1.0 pin goes high if you can't see it on the scope?

    Here's another suggestion: Just before setting ADC12SC, add:

           TA0CTL = TASSEL_2 | MC_2 | TACLR; // SMCLK, Continuous, Clear (,start)

    then right after the loop that waits for ADC12BUSY to go to 0, add:

            TA0CTL = 0;                // Stop timer

            volatile unsigned conversion_time_in_usec = TA0R;

    then look at that variable in the debugger. This uses a timer(A) running from SMCLK (1MHz) to time the operation.

  • Hi, 

    I do not understand how am I sampling two channels. I thought, I am only sampling channel 7. 

    Melissa

  • I changed the code. It looks like this now 

    void adc_start_Read(void)
    {
    for(;;)
    {
    ADC12CTL0 |= ADC12ENC | ADC12SC;

    while ((ADC12BUSY & ADC12CTL1)!=0)
    {

    P1OUT |= 0x01;
    ADC12CTL0 &= ~ADC12ENC;
    }

    P1OUT &= ~0x01;

     

    I tired two following settings 

    1. MCLK = 1MHz : ADC12SHT0 = 64 ADC12CLK Cycles

    (13 + 1 + 64) / 1MHz = 78usec = 12KHz 

    The Scope shows 6KHz

    2. MCLK = 1MHz : ADC12SHT0 = 32 ADC12CLK Cycles

    (13 + 1 + 32) / 1MHz = 46usec = 21KHz

    The scope shows appoximately 11KHz. 

    I am trying to sample only one channel (P6.7) .  I do not know why scope is showing me half values. Where am I making the mistake?

    Melissa

     

  • melissa walraven said:
    ADC12CTL0 &= ~ADC12ENC;

    Why do you effectively disable ADC after each conversion? Try removing ENC bit clear and see results.

  • I removed the ENC bit clear. Same results!

    Melissa

  • melissa walraven said:
    I removed the ENC bit clear. Same results!

    Dunno. As you do not "reset" ADC12CTL0 and ADC12CTL1 to known state at the beginning of initialization but use OR/NOR math, perhaps ADC clock divider bits are set and in fact it is running slower clock?

  • I can see the clock running at 1MHz. I am using MCLK. 

  • melissa walraven said:
    I can see the clock running at 1MHz. I am using MCLK. 

    Do you know value of ADC12DIVx bits too? As I said - you shall start init of ADC register with write, not OR or NOR op

  • I tried to follow the examples given with the Code Composer. Can you give me an example how to initialize the ADC?Melissa

  • melissa walraven said:
    I tried to follow the examples given with the Code Composer. Can you give me an example how to initialize the ADC?Melissa

    Showing example ADC12 init to give main idea:

    Wrong way:

    int main(void)
    {
      WDTCTL = WDTPW+WDTHOLD;
      ADC12CTL0 |= ADC12ON+SHT0_8+MSC;
      ADC12CTL1 |= SHP+CONSEQ_2;
      ADC12CTL0 |= ENC;
      ADC12CTL0 |= ADC12SC;
    

    Right way:

    int main(void)
    {
      WDTCTL = WDTPW+WDTHOLD;
      ADC12CTL0 = ADC12ON+SHT0_8+MSC;
      ADC12CTL1 = SHP+CONSEQ_2;
      ADC12CTL0 |= ENC;
      ADC12CTL0 |= ADC12SC;
    

  • What is CONSEQ set to? CONSEQ, MSC, EOS, and (in its own way) ENC all interact. Most of your code seems to suggest you want to sample 2 channels, but different versions of the code have requested (based on these settings) either 1 or 2, and I'm losing track.

    More generally, turning off ENC during a conversion is an unusual action. It has well-defined results, but it sidetracks the ADC from doing what you originally asked it to do. I suggest you avoid it for now.

  • I wanted to sample one channel Channel# 7 (P6.7). 

    CONSEQ = 1

    MSC = 1

    EOS = 0  

  • Try:

    ADC12MCTL0  = ADC12SREF_1 | ADC12INCH_7|ADC12EOS;

    Your "sequence" will only be 1 channel, but that's fine. (You may want to add more later.)

  • Bruce McKenney47378 said:
    Should the compiler issue a warning?

    Assigning a 16bit value to an 8 bit register should give a “conversion may lose significant bits” warning, but not an error. However, not all warnings are enabled on every compiler by default. To suppress the warning (if enabled at all) on an intentional conversion, the 16bit value can be preceded by an explicit cast “(unsigned char)”. Then the compiler knows it is intentional and suppresses the warning.

    melissa walraven said:
    the debugger shows me the correct value in Register " ADC12MEM0" but does not show the correct value of variable "SavedADC12MEM0". 

    Register updates in the debugger register section are only done every now and then, because the update process is very slow and stops the CPU.

    However, reading registers with the debugger is the same as reading registers with the CPU, and may have side-effects, e.g. clearing interrupt flags. If the debugger reads ADC12MEM0 to update the register display, this will clear the ADC12IFG0 interrupt flag. So be careful what you do with your debugger. (you don’t use interrupts, so it isn’t  a problem for you now)

**Attention** This is a public forum