Psoc 5LP issues with SAR ADC at <1 volts measurements

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
AjSh_4344506
Level 1
Level 1
First like received

Hello Everyone,

These are some of my early days working with the Psoc 5LP CY8KIT -059 and I come from a mechanical engineering background so please bear with me.

I am using the SAR ADC to measure 0-5V from the terminal of the DC motor and reading this display on the LCD

image 1.JPG

image 2.JPG

I chose the single ended input range i.e from Vssa (GND) to Vdda (5.0V)

This arrangement only gives me a reliable reading from >1V - 5V. Any reading below <1V is displayed erroneously

I also tried switching input range from 0 to Vref*2 ( 0 to 2.048)  but this also did not help.

I am having trouble root causing this and would appreciate any input to help me through !

Thanks in advance.

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

I found the attached project from my HDD.

And the Tera Term Log of that day shows

000-TeraTerm-log.JPG

So this may be usable for you.

moto

View solution in original post

11 Replies
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

AjSh,

I have experience with designing MCUs to control BDC motors so I might be of assistance.

You're not very explicit as to what type of problem you are having.

I'm assuming the issue you have is reading the voltage at Pin0_4 when the FET is ON.  Again, I'm assuming the major issue you're having is that the readings are low voltage (<1V) but are "jumping" around.

If this is a correct assumption, this a normal effect of driving a BDC motor.  There are many things that cause an apparent erratic reading with the motor being driven.  (I apologize in advance if you are already aware of all the effects I'm mentioning.  Since this is a post in a forum, this might assist someone else looking to do what you are trying to do.)

  • Startup torque. The resistance of the motor is almost the DC resistance of the motor when it is first started. The way to determine that is the voltage across the motor divided by the stall current.
    The circuit resistance the sum of the Rmotordc@stall + Rds[of the FET] + Rcurrentsense.  Let's throw some numbers in Rmotordc@stall = Vdd/Istall = 3V/5A = 0.6 ohms.   Rds[@ full on] = 0.01 ohms  Rcurrentsense = 0.62.   You'll notice that the Rmotordc@stall almost equals the Rds and Rcurrentsense combined.  Therefore at startup V@Pin0_4 ~= Vdd/2 ~= 2.5V!
    Once the motor starts moving the backemf generated by the motor will reduce the effective resistance in the motor.  This will cause the voltage at Pin0_4 to go lower.
  • Motor Loading. The resistance of the motor changes with the mechanical loading.  The lower the load, the higher backemf generated the higher the effective resistance.  The higher the load, the less backemf and the lower the effective ressitance value.  In the case you stall the motor, you're back to the resistance listed in the first point.
  • Cogging torque.  Since applying current to a coil on the armature causes a magnetic field to be generated, it's position to the static magnetic field (due to the permanent magnets) will roughly determine the backemf. As the armature rotates, the position to the magnets change causing the effective resistance to vary with position.  Hence once a motor is rotating event with a little load, you're going to see a lot of current fluctuations.
  • Commutation switching. The motor's coils are on the moving armature.  To transfer current to the moving coils, a pair of graphite brushes are in contact with pairs of multiple copper plates on the armature.  Along with variations in brush resistance as they contact the plate, when the brushes bridge over to the next plate pair, there is actually two sets of coils being powered.  When the second set of coils are engaged they have current in them.  This causes a almost immediate surge in motor current which to your circuit looks like a significant drop in the resistance of the motor.
    When the brushes fully leave a plate pair, the current in that pair is almost immediate disconnected.
  • There are other effects I haven't mentioned here but are somewhat less significant.

From ALL the effects above you can see that you will not get a 'clean' motor reading.  I've often used a more immediate reading to determine if the motor is stalled and a highly averaged reading to determine the average loading effects.

Another set of thoughts:

When using an ADC, I prefer to use the system available Vref for the ADC reference.  Hence the 0 to Vref*2 ( 0 to 2.048) is a better choice.

This is especially true when driving a high loaded circuit element such as a motor from the same Voltage source as the CPU.  This is because if you use Vssa to Vdda, Vdda is derived from Vdd.  If Vdd drops due to a highly loaded motor (or due to startup stall currents), then your ratiometric result to the ADC is effected.  For example, using Vssa to Vdda where Vssa = 0 and Vdda = 5V,  then your ADC resolution is ratiometric to (Vdda-Vssa)/(1<<ADC_resolution) = (5V-0V)/(1<<10bits) = 4.88mV per ADC count.

If Vdd drops due to heavy loading, Vdda will drop proportionally.  If Vdda drops to 3.5V then (Vdda-Vssa)/(1<<ADC_resolution) = (3.5V-0V)/(1<<10bits) = 3.42mV per ADC count.

Using the system available Vref will be generally immune to Vdda drops.  Your readings will be more 'consistent' against Vdd variances.

I hope you're not using the Vdd derived from the KitProg part of the Cy8CKIT-059.  The Vdd is limited to 0.48A as per USB specifications and the Vdd voltage averages 4.75V lightly loaded.  When you place more load, (driving LEDs or a motor for example), Vdd drops.

I know I've been a bit 'wordy' but I hope with some completeness to my answer,it might explain what you are seeing or what to expect to see in future working with motor circuits.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Hi Len,

I tested the same using a potentiometer instead of the motor to eliminate the possible motor load/noises that could affect measurements at low voltage but it wasn't the case.

The Vdd i use is not from the Kitprog but from the USB port.

I may need to redo the steps by a separate battery source. Is there a reason for not using them as a power source but only as a logic driver?

I really appreciate your support and thank you for sharing your experience with motor controllers.

0 Likes
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

I found the attached project from my HDD.

And the Tera Term Log of that day shows

000-TeraTerm-log.JPG

So this may be usable for you.

moto

Thanks for sharing this moto!

AjSh,

You're welcome.

I'm glad to hear you've substituted a load resistor that can be measured in current to try to prove out the ADC conversion. It's a very good and solid idea.  It allows you to minimize outside influences while you debug the circuit.

In the motor drive controller products I've done for the automotive market, I use calibrated reference power resistors to calibrate the ADC circuit as well as functionally test the FET drivers in End-of-line testing.

The Vdd off the USB is basically the same specs as off the KitProg board.  <0.5A @ ~4.75V

There's another test you can perform if you have an adjustable DC voltage source.

  • Make sure the kit is powered but not driving the FET.
  • Place the "+" terminal of the power supply on Pin0_4
  • Place the "-" of the power supply on GND (VSS).
  • Adjust the voltage source from 0V to 4.0V slowly in increments.
  • At each stage of incrementing, note the output voltage (preferably using a calibrated meter) and determine the ADC results.
  • Create a table of the results and share it with us.  It might lend some clues as to what is going on.

Check out moto's project.  Maybe he's got some clues as to how to improve your design.

Len

PS:  Bravo to you!  I found it always best to perform cross-disciplinary engineering.

Len
"Engineering is an Art. The Art of Compromise."

Hi Len,

I will perform these steps and see how it turns out. I will also use a dedicated DC power source to remove any uncertainties coming from the MCU Vdd.

0 Likes

Hello,

I was kind of able to figure out the problem with  <1V measurement from the SAR_ADC. I think the problem was not with ADC but simply the conversion and display on the LCD. I saw Motoo's code and accordingly applied it in mine and was able to go as low as 0.1 V with the Vdd supply from PSoc

if (loop_counter>5000)

    {

       loop_counter=0;

        LCD_Position(0,0);

        LCD_PrintString("Voltage is:");

        LCD_Position(1,0);

        LCD_PrintNumber(Vir/1000);

        LCD_PrintString(".");

        LCD_PrintNumber(Vir%1000);

I have a new problem now, (portion of code below). I am trying to divide a type float static value(Rsense) into a digital (ADC) signal. Basically, the signal is voltage and I want to divide it by the Resistance (Rsense) to obtain current. The output clearly didn't work. Is there a way to achieve this?

Again, coming from a mechanical engineering background i may be missing some basic digital signal concept understanding and would really appreciate some guidance!

    ADC_SAR_2_StartConvert();

    ADC_SAR_2_IsEndConversion(ADC_SAR_1_WAIT_FOR_RESULT); // does not complete conversion until

    vir = ADC_SAR_2_GetResult16();  //get basic data from ADC

    Vir = ADC_SAR_2_CountsTo_mVolts(vir); // Convert this to mVolts

  Vsense = Vir                   -------( Vsense is of type float)

    Isense= Vir/Rsense;            ---------(Isense is of type float)

    Vr_motor= Isense*Rmotor;

0 Likes
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi

> The output clearly didn't work. Is there a way to achieve this?

"clearly didn't work" does mean almost nothing.

Explaining what you are seeing will help us to help you.

Having said that I modified my sample as below.

================

int main(void)

{

    static float Rsense = 123.456 ;

    uint16_t adc_count ;

    int16_t mV ;

    float isense ;

  

    init_hardware() ;

  

    splash() ;

    for(;;) {      

        ADC_StartConvert() ;

        ADC_IsEndConversion(ADC_WAIT_FOR_RESULT) ;

        adc_count = ADC_GetResult16(0) ;

        mV = ADC_CountsTo_mVolts(adc_count) ;

      

        if (mV < 0) {

            mV = -mV ;

            print("-") ;

        }

        snprintf(str, STR_LEN,  "%d.%03d V (0x%04X) : ", mV/1000, mV%1000, adc_count) ;

        print(str) ;

      

        isense = (float)mV / Rsense ;

      

        snprintf(str, STR_LEN, "%.2f mA\n\r", isense) ;

        print(str) ;

      

        CyDelay(1000) ;

    }

}

================

And the Tera Term log was

000-TeraTerm-log.JPG

So I hope that we can get current value.

BUT, there is/are some catch here.

Please refer to my old memo about printf with float

printf and float rhapsody (aka, yet another printf and floating topic)

To make above project work, I had to modify a couple of Project Settings.

(1) Enable float format in the library

From Project > Bulid Settings...

Select ARM GCC xxx > Linker > General

In the General pane, set "Use newlib-nano Float Formatting" to True

001-Build-settings.JPG

(2) Enlarge the heap size

From Workspace Explorer > <the project> > Design Wide Resources > System

The default Heap Size (bytes) is set to 0x80,

you need to set it larger, for my example I set it to 0x400

002-DWR-System-heap.JPG

But if your application needs speed, maybe using float is not a good idea,

using integer for 1000 x current value may serve your purpose without the cost of float (speed, and memory usage).

And last but not least, if our replies are useful, please make it "Like" or "Helpful".

moto

Hi Motoo,

Again many thanks for your support.

I don't have much knowledge of UART and basically had this project to run on an LCD display so I couldn't log values to show.

This project was to measure the back emf from a DC motor and using that to display the motor speed on an LCD.

I think the major struggle throughout this project for me was using basic conversions for display on the LCD. The highlighted pieces of code is what I corrected after taking your inputs to get my results.

    AMux_Select(0); //Channel 1 select for measuring Vir = I*Rsense

    ADC_SAR_1_StartConvert();

    ADC_SAR_1_IsEndConversion(ADC_SAR_1_WAIT_FOR_RESULT); // does not complete conversion until

    vir = ADC_SAR_1_GetResult16();  //get basic data from ADC

    Vir = ADC_SAR_1_CountsTo_mVolts(vir); // Convert this to mVolts

   Isense=(float) Vir*1.3;

  Vr_motor= (int16)Isense*Rmotor;

   Vb= Vs-Vr-Vr_motor; // Vb = V - (V-IR-Kb*w) - IR = Kb*w

    rpm = Vb*2885; // Using Kb=000331 from datasheet and converting to rpm 60/2*3.14

if (loop_counter>4000)

    {

       loop_counter=0;

        LCD_Position(0,0);

     

        if (Vb<=0)          // This condition is to prevent showing negative values on LCD which spoils display later

        {

        LCD_PrintString("Voltage is:");

        LCD_Position(1,0);

        LCD_PrintString("000000");

        LCD_Position(1,8);

        LCD_PrintString("rpm");

        }

        else

        {

        LCD_ClearDisplay();

        LCD_PrintString("Voltage is:");

        LCD_Position(1,0);

        LCD_PrintU32Number(rpm/1000);

        LCD_PrintString(".");

        LCD_PrintU32Number(rpm%10);

        LCD_Position(1,8);

        LCD_PrintString("rpm");

        }

}

    }

}

I'm trying to figure out how to attach files here btw.

Thank for you support.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

Just reading your code, I have a couple of points which I'd like to ask you to confirm,

(1) Can you put a break point at the line

            LCD_PrintU32Number(rpm/1000);

and read the value of rpm?

Is the value of rpm what you expected?

(2) To me following lines seem to be strange

        LCD_PrintU32Number(rpm/1000);

        LCD_PrintString(".");

        LCD_PrintU32Number(rpm%10);

Is rpm an integer or float?

If rpm is a float number, you need to write

        LCD_PrintU32Number((int)(rpm/1000)) ;

Meantime, let's assume rpm = 123456

rpm/1000 = 123

rpm%10 = 6

So you will get "123.6"

If you want to see the first place under decimal point

you need to do

(rpm/100)%10 = (123456/100)%10 = 1234 % 10 = 4

So to be safe (in case rpm is a float), I hope that doing the following will show you "123.4"

        LCD_PrintU32Number((int)(rpm/1000));

        LCD_PrintString(".");

        LCD_PrintU32Number(((int)(rpm/100))%10);

moto

0 Likes

Hi Motoo,

Yes, the rpm is what I expected. The variable type for 'rpm' is int32. I was careful enough there to not have a float variable printed in the LCD.

"(2) To me following lines seem to be strange

        LCD_PrintU32Number(rpm/1000);

        LCD_PrintString(".");

        LCD_PrintU32Number(rpm%10);

Is rpm an integer or float?

If rpm is a float number, you need to write

        LCD_PrintU32Number((int)(rpm/1000)) ;

Meantime, let's assume rpm = 123456

rpm/1000 = 123

rpm%10 = 6

So you will get "123.6"  "

Yes, this is exactly what i got with int32