PIC16F1574 current measurement and display on multiplexed 2-digit 7-segment

Thread Starter

Travm

Joined Aug 16, 2016
363
Ive been designing a circuit to control a load, with current feedback and a dual 7 segment display.
I dont have a diagram of the circuit (that makes sense) at this point, so i cant really share, but the circuit appears to work properly.
I believe i have a code problem, and i'm brand new at C programming, and microcontrollers so I dont know what is wrong exactly.

I have a timer set to increment a couple variables by interrupt every 1ms.
First it reads the pot value, and sets the value of of ADC reading to a variable called dutycycle.
Then it reads the current value, and sets the value of the ADC reading to a variable called current.

I believe the error is in the code below.
Code:
 while (1)
    {
       // Add your application code
        if (ADCclk>120)
        {
            if (ADCsel==1) // sets ADC channel to read pot input
            {
           // PrevResult = ((ADRESH << 8) + ADRESL);
                ADCON0bits.CHS = channel_AN7;
                __delay_us(10);
                ADCON0bits.GO = 1;
            }
            if (ADCsel==2)  // sets ADC channel to read current feedback circuit
            {
                ADCON0bits.CHS = channel_AN3;
                __delay_us(10);
                ADCON0bits.GO = 1;
            }
            ADCclk=0;
        }            
    PWM1DCL = DutyCycle;
    PWM1DCH = (DutyCycle >> 8);
    PWM1LDCON = 0x80;
   // p = (current*10 / 1024); //Multiplying current by 10, so that integer math works, and gives an integer value between 0 and 9.  Needs adjustment yet for proper rounding (add one half of a point)
      //p is the value to display on 7 segment display
    //seg7set[p](); // calling the 7 segment value specified by p

   
   if (digitsel>1)
   {
       seg7set[10](); // clear 7 segment display to prevent ghosting
       if (IO_RA5_PORT==1)
      {
          IO_RA5_SetLow(); 
          p=(current*10 / 1024);  //get tens value from adc reading
          seg7set[p]();
      }
      else if (IO_RA5_PORT==0)
      {
              IO_RA5_SetHigh();
              p=((current*100 / 1024)%10); //get ones value from adc reading
              seg7set[p]();
      }
       digitsel=0;
   }
   }
   
}
This works perfectly as the display goes from 00 to 65, as i smoothly turn the pot, the display value goes up smoothly. When i get to 65, it jumps from there directly to 75, then counts backwards from 75-70, then jumps to 85, then backwards to 80, then jumps to 85.
The maximum current reading is 88, which is exactly as expected.
The odd display behaviour between 65 and 88 i dont understand, especially since it works at the lower values.
any thoughts?
 

MrChips

Joined Oct 2, 2009
30,714
Your problem is not about programming in C. Your problem is basic math.

If you have a value x that you want to display as tens and units, the math is

x = tens * 10 + units

Therefore, using integer arithmetic,

tens = x/10

units = x - tens * 10
 

Kjeldgaard

Joined Apr 7, 2016
476
The problem with the crazy ones, looks like arithmetic overflow in the line:
p = ((Current * 100/1024)% 10); // get ones value from adc reading

When current is full scale, the first interim result to be 1023 * 100 = 102300, and it gives an overflow if the calculation is done as 16-bit arithmetic. And it also fits the error occurs just above a display of 65 (2^16/100 = 65536/100 ~ 65.5)

A solution could be to divide the first two constants with 4:
p = ((Current * 25/256)% 10); // get ones value from adc reading

It should give the correct result, but has only an intermediate result of 25575.
 

Thread Starter

Travm

Joined Aug 16, 2016
363
Your problem is not about programming in C. Your problem is basic math.

If you have a value x that you want to display as tens and units, the math is

x = tens * 10 + units

Therefore, using integer arithmetic,

tens = x/10

units = x - tens * 10
I may be wrong, but i dont think so.
I am struggling with base 2 numbers, bitwise operators, and all the other stuff you dont learn at mechanical engineering school, but i think the basic math i've got sorted.
The mathematical operation i'm doing;
current is read as a 10bit number (1024 being 100%)
multiplication by 10 prior to dividing by 1024 as required for integer math, division by 1024 gives the percentage as an integer (i'm trying to convert this ratio into a 0-99 number to display on my 7-segment display)
to get the ones, I am multiplying by 100, then getting the remainder of divide by 10.

The 16-bit overflow is likely whats happening. will report back if making those adjustments work.

Appreciate the help. you guys are great.
 

Thread Starter

Travm

Joined Aug 16, 2016
363
The problem with the crazy ones, looks like arithmetic overflow in the line:
p = ((Current * 100/1024)% 10); // get ones value from adc reading

When current is full scale, the first interim result to be 1023 * 100 = 102300, and it gives an overflow if the calculation is done as 16-bit arithmetic. And it also fits the error occurs just above a display of 65 (2^16/100 = 65536/100 ~ 65.5)

A solution could be to divide the first two constants with 4:
p = ((Current * 25/256)% 10); // get ones value from adc reading

It should give the correct result, but has only an intermediate result of 25575.
Thanks
this solved it.
I have some ongoing issues with the analog voltages bouncing around, but i think at this point that is because i have 0 decoupling caps on the circuit, and about 1/3 of it is on a breadboard. Still it only bounces around by 2-3% so it cant be that bad.
 

djsfantasi

Joined Apr 11, 2010
9,156
ADC values bounce around naturally. Given a supposedly non-changing voltage as input, the ADC will return values which bounce around the input voltage.

Temperature changes, current variations and a laundry list of parameters affect the reading. So what do you do?

I don't know what YOU are going to do, but I use a rolling average. Keep an array of the last n values in a circular list. Then, for each reading, you replace the oldest entry and average the values. Depending on the variation, I use 3-10 entries.
 

Thread Starter

Travm

Joined Aug 16, 2016
363
ADC values bounce around naturally. Given a supposedly non-changing voltage as input, the ADC will return values which bounce around the input voltage.

Temperature changes, current variations and a laundry list of parameters affect the reading. So what do you do?

I don't know what YOU are going to do, but I use a rolling average. Keep an array of the last n values in a circular list. Then, for each reading, you replace the oldest entry and average the values. Depending on the variation, I use 3-10 entries.
I was planning on creating some code that would keep the last n values somehow, and then average them using a division operation.
Ive been googling circular list, circular array, linked list, in C to death. I cant find anything that i can make sense of. Any pointers on using a circular list or array for this application?
Otherwise I guess i'll just jump in and hack at it till it works, doubtful i'll end up with an elegant solution.
Mind you thats how i came up with my 7-segment code and it turns out that i did pretty well with it.
When I'm all finished with this I'm planning on making a thread in projects going over what i've done, everything i've learned, and how.
 

MrChips

Joined Oct 2, 2009
30,714
You don't need a circular list.

You can calculate a running average on the fly.
Create a running sum which will be n-times larger than the average, computed as follows:

sum = sum + (value - avg)
avg = sum/n

where value is the newest value
n = number of points to average
 

Thread Starter

Travm

Joined Aug 16, 2016
363
You don't need a circular list.

You can calculate a running average on the fly.
Create a running sum which will be n-times larger than the average, computed as follows:

sum = sum + (value - avg)
avg = sum/n

where value is the newest value
n = number of points to average
Something like that is likely how i would have tried to do it.
is either more efficient in C programming for PIC?
I feel like the circular list would be more cumbersome to code, I certainly didnt find any 2 or 3 line examples of circular list code. Also didnt find any examples of circular list code written for Pic controllers.
 

MrChips

Joined Oct 2, 2009
30,714
Something like that is likely how i would have tried to do it.
is either more efficient in C programming for PIC?
I feel like the circular list would be more cumbersome to code, I certainly didnt find any 2 or 3 line examples of circular list code. Also didnt find any examples of circular list code written for Pic controllers.
You tell me. All it takes is two additions.
If you choose n = 4, 8, 16, etc, division becomes right-shift operations.
 

Thread Starter

Travm

Joined Aug 16, 2016
363
You tell me. All it takes is two additions.
If you choose n = 4, 8, 16, etc, division becomes right-shift operations.
bah, that bitwise operations again. Between that and getting my head around the fact i actually have a limited amount of numbers available when i do math....
I'm so used to having all the numbers available to me, and division being easier on base 10!
I even made a mental note to remember that all powers of 2 divide with bit shifts!

I still dont understand enough about circular lists, or Array's in C in general to have much idea on their usefulness. I have used 1 array in my program just to make the program much simpler to write. Which to me, appears to be their primary use.
 

MrChips

Joined Oct 2, 2009
30,714
Why is bitwise shifts so difficult to understand?

To multiply by 10, you shift all digits to the left (add a zero at the right).
To divide by 10, you move all digits to the right by one digit (radix 10).

To multiply by 2, you shift left by 1 bit.
To divide by 2, you shift right by 1 bit (radix 2).

In other words, the math is the same. You are multiplying or dividing by the radix.
 

Thread Starter

Travm

Joined Aug 16, 2016
363
Why is bitwise shifts so difficult to understand?

To multiply by 10, you shift all digits to the left (add a zero at the right).
To divide by 10, you move all digits to the right by one digit (radix 10).
radix is a new word for me (just now).
but yes, that is exactly how math in my head at base 10 works. My head does this automagically every time I do math, without thinking.
To multiply by 2, you shift left by 1 bit.
To divide by 2, you shift right by 1 bit (radix 2).

In other words, the math is the same. You are multiplying or dividing by the radix.
The math is not the same, I cant read binary, so this is like saying; Comment ca va? and How are you? are the same... they arent. One is French.

I fully understand how this works, but in practice I'm not seeing opportunities to simplify the math (for the MCU) because my head doesnt work in base 2 yet.
Every time i hit a silly question like this one, that reminds me MCU's dont do math like I do, the better understanding I will get about it.
 

MrChips

Joined Oct 2, 2009
30,714
Surely you've seen my signature:

There are 10 types of people in the world: those who understand binary and those who don't.

The quicker you start seeing in binary, the quicker you will be at mastering computer programming.
 

Thread Starter

Travm

Joined Aug 16, 2016
363
Surely you've seen my signature:

There are 10 types of people in the world: those who understand binary and those who don't.

The quicker you start seeing in binary, the quicker you will be at mastering computer programming.
Yes I have, its excellent.
I understand binary at the moment like I understand french. I can use a couple words, make 2-3 sentences, and google the rest!
If only i had more time, I would love to build a small mechanical computer, maybe marble powered. Not to do anything practical, just for the learning, and its a beautiful mesh of my mechanical background, and aspirations to learn more about electronics.

It also doesnt help that in the past few months ive been learning binary, hexadecimal, frequency/time relationships, and pretty much everything about electronics except ohms law (i already knew that one). Still a long way to go, but I think i'm doing pretty good.
 
Top