CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Using CT For Measuring AC Current With 16F886

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
mcmsat



Joined: 25 May 2018
Posts: 51
Location: Nigeria

View user's profile Send private message

Using CT For Measuring AC Current With 16F886
PostPosted: Sat Jul 09, 2022 1:36 am     Reply with quote

First of all I apologize for posting this topic in "Code Library". I noticed it lately and deleted it and bringing it here.

Please I was fiddling with a Current transformer to measure RMS Current. I used Arduino code without a driver header file. It is working in Arduino platform. But as I wanted to convert the code to CCS C platform I became confused in some areas, especially on:

Code:

int16_t GetpeakTopeak() {
  int16_t ctValueHigh = 0;
  int16_t ctValueLow = ADC_RESOLUTION;

  for (uint8_t i = 0; i < 40; i++) {
    if (analogRead(CTmonitor) > ctValueHigh)
      ctValueHigh = analogRead(CTmonitor);
    if (analogRead(CTmonitor) < ctValueLow)
      ctValueLow = analogRead(CTmonitor);
    delayMicroseconds(500);
  }
//    Serial.print("High : ");  Serial.print(ctValueHigh);
//    Serial.print('\t');
//    Serial.print("Low : ");  Serial.println(ctValueLow);
  return (ctValueHigh - ctValueLow);
}


The above part of the code is entirely out of "setup" and "loop". As a low level apprentice in CCS C, I am confused as how to place it here. I believe this is the reason it fail to work for me in CCS C platform. Please Can someone correct me where i make mistakes in the conversion.


Code:
#include <16F886.h>
#device ADC = 10
#fuses INTRC_IO,NOWDT,MCLR,NOPROTECT
#use delay(clock = 8000000)
#use rs232(baud = 9600,xmit=PIN_C6,DISABLE_INTS,ERRORS)

#include <Flex_LCD2004.c>

#define ADC_RESOLUTION    1024

float avgResult=0;
float currentInmA=0;
long CTmonitor;

void init()
{
  setup_adc(ADC_CLOCK_INTERNAL);           
  setup_adc_ports( sAN0 |sAN1 | VSS_VDD);// Select Analog Inputs
  setup_comparator(NC_NC_NC_NC);
  set_adc_channel(0); //Select AN0 as ADC Input for CTmonitor

}

  int16 GetpeakTopeak() {
  int16 ctValueHigh = 0;
  int16 ctValueLow = ADC_RESOLUTION;

  for (int i = 0; i < 40; i++) {
    if (CTmonitor > ctValueHigh)
      ctValueHigh = CTmonitor;
    if (CTmonitor < ctValueLow)
      ctValueLow = CTmonitor;
    delay_ms(500);
  }
  return (ctValueHigh - ctValueLow);

for(int i=0;i<10;i++){
  avgResult += GetpeakTopeak();
}
  avgResult = avgResult/10.00;
//  currentInmA = 0.0045 * avgResult;
  currentInmA = 0.053 * avgResult;
  avgResult = 0;
  }

void main()
{
  init();                           // Configure peripherals/hardware
  lcd_init();                    // Initialize LCD module
  lcd_gotoxy(1,1);   //colum 1 row 1
  lcd_putc("CURRENT TEST");
  delay_ms(1);            //Wait 1 ms
  CTmonitor = read_adc(); //Read AN3 and store in MS2
  delay_ms(1);            //Wait 1 ms   

  while(TRUE)
{

   lcd_gotoxy(5,2);  // colum5 row 2
   printf(lcd_putc,"%3.1g ",currentInmA);

 printf("CURRENT = %3.1g   \n\r",currentInmA);
 
   delay_ms(500);
}

}



The original Arduino code is this:

Code:

#include "Arduino.h"
#include "LiquidCrystalWired.h"

#define LCD_ADDRESS (0x7c >> 1)
#define ROW_COUNT   2
#define COL_COUNT   16


#define ADC_RESOLUTION    1024
#define CTmonitor A1
unsigned long previousMillis = 0;// will store last time LCD was updated
const long interval = 1000;// interval at which to refresh (milliseconds)
LiquidCrystalWired lcd = LiquidCrystalWired(
        ROW_COUNT, COL_COUNT, FONT_SIZE_5x8, BITMODE_8_BIT);

float avgResult=0;
float currentInmA=0;
void setup() {
  // put your setup code here, to run once:
    Serial.begin(115200);
    lcd.begin(LCD_ADDRESS, &Wire);
    lcd.turnOn();
}

void loop() {
  unsigned long currentMillis = millis();
for(int i=0;i<10;i++){
  avgResult += GetpeakTopeak();
}
  avgResult = avgResult/10.00;
//  currentInmA = 0.0045 * avgResult;
  currentInmA = 0.053 * avgResult;
//  Serial.println(currentInmA);
  avgResult = 0;

     if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

//111111111111111111111111111111111111111111111111111111111
  lcd.setCursorPosition(0, 0);
  lcd.print("L1:");
  lcd.setCursorPosition(0, 3);
  lcd.print(currentInmA,2);
//*******************************
if(currentInmA >= 100)
  lcd.setCursorPosition(0, 9);
  lcd.print("A      ");
//*******************************100
if(currentInmA > 10 && currentInmA < 100)
  lcd.setCursorPosition(0, 8);
  lcd.print("A       ");
//*******************************10
if(currentInmA < 10)
  lcd.setCursorPosition(0, 7);
  lcd.print("A        ");
//*******************************1
            Serial.print(" The Current RMS value is: ");
            Serial.print(currentInmA,2);
            Serial.println(" V ");
     }   
}

int16_t GetpeakTopeak() {
  int16_t ctValueHigh = 0;
  int16_t ctValueLow = ADC_RESOLUTION;

  for (uint8_t i = 0; i < 40; i++) {
    if (analogRead(CTmonitor) > ctValueHigh)
      ctValueHigh = analogRead(CTmonitor);
    if (analogRead(CTmonitor) < ctValueLow)
      ctValueLow = analogRead(CTmonitor);
    delayMicroseconds(500);
  }
//    Serial.print("High : ");  Serial.print(ctValueHigh);
//    Serial.print('\t');
//    Serial.print("Low : ");  Serial.println(ctValueLow);
  return (ctValueHigh - ctValueLow);
}

_________________
All is well even in the well!
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Sat Jul 09, 2022 7:13 am     Reply with quote

There are some problems with the way you translated it. For example, you wrote:
Code:

  int16 GetpeakTopeak() {
  int16 ctValueHigh = 0;
  int16 ctValueLow = ADC_RESOLUTION;

  for (int i = 0; i < 40; i++) {
    if (CTmonitor > ctValueHigh)
      ctValueHigh = CTmonitor;
    if (CTmonitor < ctValueLow)
      ctValueLow = CTmonitor;
    delay_ms(500);
  }
  return (ctValueHigh - ctValueLow);

for(int i=0;i<10;i++){
  avgResult += GetpeakTopeak();
}
  avgResult = avgResult/10.00;
//  currentInmA = 0.0045 * avgResult;
  currentInmA = 0.053 * avgResult;
  avgResult = 0;
  }

Code after return never gets executed. This part
Code:

for(int i=0;i<10;i++){
  avgResult += GetpeakTopeak();
}
  avgResult = avgResult/10.00;
//  currentInmA = 0.0045 * avgResult;
  currentInmA = 0.053 * avgResult;
  avgResult = 0;

is in the main loop of the original code. Second, one of your delays is half a second long and you are executing that 40 times. Third, in the original code ADC channel gets read in GetpeakTopeak() function, which your code doesn't do, you read it only once.

I confess that it is unclear to me why ADC channel is read so many times, but you said it works in Arduino so i guess it must be so. I took the liberty to rearrange your code a bit. It is not tested.
Code:

#include <16F886.h>
#device ADC = 10
#fuses INTRC_IO,NOWDT,MCLR,NOPROTECT
#use delay(clock = 8000000)
#use rs232(baud = 9600,xmit=PIN_C6,DISABLE_INTS,ERRORS)

#include <Flex_LCD2004.c>

#define ADC_RESOLUTION    1024   // max. ADC output

float avgResult=0;
float currentInmA=0;
long CTmonitor;               // in Arduino this is a pin or analog channel definition

void init()
{
  setup_adc(ADC_CLOCK_INTERNAL);           
  setup_adc_ports( sAN0 |sAN1 | VSS_VDD);// Select Analog Inputs
  setup_comparator(NC_NC_NC_NC);
  set_adc_channel(0); //Select AN0 as ADC Input for CTmonitor

}

// function to read ADC channell 0
  int16 GetpeakTopeak() {
   int16 ctValueHigh = 0;               // strange, 0 is high and 1024 is low
   int16 ctValueLow = ADC_RESOLUTION;
   int16 Analog_Result = 0;            //variable to store analog reads
   set_adc_channel(0);                  // probably not even needed, as the ADC channell was already set
   

   for (int i = 0; i < 40; i++) {
      Analog_Result = read_adc();         // read ADC every time
      if (Analog_Result > ctValueHigh)
         ctValueHigh = Analog_Result;
      if (Analog_Result < ctValueLow)
         ctValueLow = Analog_Result;
//      delay_ms(500);                  // you wait more than 20s here!!!
      delay_us(500);
   }
   return (ctValueHigh - ctValueLow);      // retutn value that was somehow averaged
  }

void main()
{
  init();                           // Configure peripherals/hardware
  lcd_init();                    // Initialize LCD module
  lcd_gotoxy(1,1);   //colum 1 row 1
  lcd_putc("CURRENT TEST");
  delay_ms(1);            //Wait 1 ms
//  CTmonitor = read_adc(); //Read AN3 and store in MS2.
  delay_ms(1);            //Wait 1 ms 

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

  while(TRUE){
   for(int i=0;i<10;i++){               // you are reading ADC 400x!!!
     avgResult += GetpeakTopeak();
   }
     avgResult = avgResult/10.00;
     currentInmA = 0.053 * avgResult;
     avgResult = 0;

   
   lcd_gotoxy(5,2);  // colum5 row 2
   printf(lcd_putc,"%3.1g ",currentInmA);
   printf("CURRENT = %3.1g   \n\r",currentInmA);
 
   delay_ms(500);
  }

}
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Sat Jul 09, 2022 9:51 am     Reply with quote

A suggestion. Get rid of the floats. By declaring float avgResult=0 as unsigned int16 the compiled code (without LCD parts) is hugely smaller. With float ROM 12%, RAM 9%, with int16 ROM 5%, RAM 7%. Understand that when reading ADC (or anything else inside a PIC) you are reading, writing and dealing with discrete numbers, in this case numbers from 0 - 1023. Even if you add them together 10 times, they still nicely fit into a 16 bit integer. So for all the mathematics with your displayed resolution floats are not needed.

It should work with while true() like this:
Code:

  while(TRUE){
   for(int i=0;i<8;i++){               // you are reading ADC 400x!!!
     avgResult += GetpeakTopeak();
   }
     avgResult = avgResult/8;
//      avgResult = avgResult >> 3;
      currentInmA = 0.053 * (float)avgResult;
      avgResult = 0;

   
//   lcd_gotoxy(5,2);  // colum5 row 2
//   printf(lcd_putc,"%3.1g ",currentInmA);
//   printf("CURRENT = %3.1g   \n\r",currentInmA);
 
   delay_ms(500);
  }


Note that I changed i to 8, because it translates to only 6 RRF instructions in the final code (division by 8 and shifting produce exactly the same code) and is again faster than division by ten. Old hands here will tell if my casting is OK. It would also be nice to know what kind of signal you are sampling. The rate of change. GetpeakTopeak() function takes 20+ ms to return a result, then you do it 10 times before displaying the end result. So 200+ ms for one result plus 500ms waiting... If the signal change rate isn't very, very slow I have much doubt in the end result. If it's a 50 or 60Hz signal, the whole approach is wrong. But than again, I might have misread the code or the philosophy behind it. You are measuring AC signal, otherwise peak to peak has no meaning. With AC phase comes into play, but that doesn't matter in this case. But if I remember my lessons from more than 20 years ago, Nyquist and Shannon proved that if you want to reconstruct your signal, a sampling rate of at least twice the frequency is needed. Per period, of course.
mcmsat



Joined: 25 May 2018
Posts: 51
Location: Nigeria

View user's profile Send private message

PostPosted: Sat Jul 09, 2022 1:12 pm     Reply with quote

I am sorry for my late reply. It was a natural factor. I thank the contributor to my questions. let me check it out now
_________________
All is well even in the well!
mcmsat



Joined: 25 May 2018
Posts: 51
Location: Nigeria

View user's profile Send private message

PostPosted: Sat Jul 09, 2022 2:00 pm     Reply with quote

@ PrinceNai, I beg you to help me translate this code the way it should be so that I will study it as part of my lessons. everything goes to Arduino but my fun started with CCS C. Though not as a professional. I do some small things in my garden as a hobby.

This is the original Arduino that I have compiled in my Arduino IDE and it is working on my Uno R3. My curiosity is how can I make it work on this 16F886 I have it's board laying around.

Code:
#define ADC_RESOLUTION    1024
#define CTmonitor A1

float avgResult=0;
float currentInmA=0;

void setup() {
  // put your setup code here, to run once:
    Serial.begin(115200);
}

void loop() {
  unsigned long currentMillis = millis();
for(int i=0;i<10;i++){
  avgResult += GetpeakTopeak();
}
  avgResult = avgResult/10.00;
  currentInmA = 0.053 * avgResult;
  Serial.println(currentInmA);
  avgResult = 0;
   
}

int16_t GetpeakTopeak() {
  int16_t ctValueHigh = 0;
  int16_t ctValueLow = ADC_RESOLUTION;

  for (uint8_t i = 0; i < 40; i++) {
    if (analogRead(CTmonitor) > ctValueHigh)
      ctValueHigh = analogRead(CTmonitor);
    if (analogRead(CTmonitor) < ctValueLow)
      ctValueLow = analogRead(CTmonitor);
    delayMicroseconds(500);
  }
//    Serial.print("High : ");  Serial.print(ctValueHigh);
//    Serial.print('\t');
//    Serial.print("Low : ");  Serial.println(ctValueLow);
  return (ctValueHigh - ctValueLow);
}


Please any one who can translate it here for me
_________________
All is well even in the well!
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Sat Jul 09, 2022 5:54 pm     Reply with quote

Some three posts back there is a complete code. That compiles without any problem.
mcmsat



Joined: 25 May 2018
Posts: 51
Location: Nigeria

View user's profile Send private message

PostPosted: Sat Jul 09, 2022 11:36 pm     Reply with quote

PrinceNai wrote:
Some three posts back there is a complete code. That compiles without any problem.


@PrinceNai, the code is working now! So much thanks! I am so much happy for this!

Now, I have to study the code to see my mistakes in case for another translation. Thanks Again!

The Code works exactly like this:
Code:

#include <16F886.h>
#device ADC = 10
#fuses INTRC_IO,NOWDT,MCLR,NOPROTECT
#use delay(clock = 8000000)
#use rs232(baud = 9600,xmit=PIN_C6,DISABLE_INTS,ERRORS)

#include <Flex_LCD2004.c>

#define ADC_RESOLUTION    1024   // max. ADC output

float avgResult=0;
float currentInmA=0;
long CTmonitor;               // in Arduino this is a pin or analog channel definition

void init()
{
  setup_adc(ADC_CLOCK_INTERNAL);           
  setup_adc_ports( sAN0 |sAN1 | VSS_VDD);// Select Analog Inputs
  setup_comparator(NC_NC_NC_NC);
  set_adc_channel(0); //Select AN0 as ADC Input for CTmonitor

}

// function to read ADC channell 0
  int16 GetpeakTopeak() {
   int16 ctValueHigh = 0;               // strange, 0 is high and 1024 is low
   int16 ctValueLow = ADC_RESOLUTION;
   int16 Analog_Result = 0;            //variable to store analog reads
   set_adc_channel(0);                  // probably not even needed, as the ADC channell was already set
   

   for (int i = 0; i < 40; i++) {
      Analog_Result = read_adc();         // read ADC every time
      if (Analog_Result > ctValueHigh)
         ctValueHigh = Analog_Result;
      if (Analog_Result < ctValueLow)
         ctValueLow = Analog_Result;
//      delay_ms(500);                  // you wait more than 20s here!!!
      delay_us(500);
   }
   return (ctValueHigh - ctValueLow);      // retutn value that was somehow averaged
  }

void main()
{
  init();                           // Configure peripherals/hardware
  lcd_init();                    // Initialize LCD module
  lcd_gotoxy(1,1);   //colum 1 row 1
  lcd_putc("CURRENT TEST");
  delay_ms(1);            //Wait 1 ms
//  CTmonitor = read_adc(); //Read AN3 and store in MS2.
  delay_ms(1);            //Wait 1 ms

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

  while(TRUE){
   for(int i=0;i<10;i++){               // you are reading ADC 400x!!!
     avgResult += GetpeakTopeak();
   }
     avgResult = avgResult/10.00;
     currentInmA = 0.053 * avgResult;
     avgResult = 0;

   
   lcd_gotoxy(5,2);  // colum5 row 2
   printf(lcd_putc,"%3.1g ",currentInmA);
   printf("CURRENT = %3.1g   \n\r",currentInmA);
 
   delay_ms(500);
  }

}


You later said this:
Code:

Code:

  while(TRUE){
   for(int i=0;i<8;i++){               // you are reading ADC 400x!!!
     avgResult += GetpeakTopeak();
   }
     avgResult = avgResult/8;
//      avgResult = avgResult >> 3;
      currentInmA = 0.053 * (float)avgResult;
      avgResult = 0;

   
//   lcd_gotoxy(5,2);  // colum5 row 2
//   printf(lcd_putc,"%3.1g ",currentInmA);
//   printf("CURRENT = %3.1g   \n\r",currentInmA);
 
   delay_ms(500);
  }

As part of learning I will still do this to see something for myself.
_________________
All is well even in the well!
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Sun Jul 10, 2022 8:33 am     Reply with quote

I'm glad it works. I suggested division by 8 because it is slightly faster. But the biggest difference is defining avgResult as unsigned int 16. That way you always manipulate only two bytes instead of four (float variable takes four bytes).
mcmsat



Joined: 25 May 2018
Posts: 51
Location: Nigeria

View user's profile Send private message

PostPosted: Sun Jul 10, 2022 9:24 am     Reply with quote

PrinceNai wrote:
I'm glad it works. I suggested division by 8 because it is slightly faster. But the biggest difference is defining avgResult as unsigned int 16. That way you always manipulate only two bytes instead of four (float variable takes four bytes).


I thank you for your help PrinceNai. You are such a good concerning my request. I have refined some definitions and adapted it for ZMPT101B AC voltage sensor and it worked like magic. It's better for me than Emonlib in Arduino. I control. I don't know if it can be used with ADS1115 unlike Emonlib in Arduino in which ADC pin is static in the header file itself. I will try the ADS1115 as I have 2pcs of the module.
Thanks so much.
_________________
All is well even in the well!
Ttelmah



Joined: 11 Mar 2010
Posts: 19513

View user's profile Send private message

PostPosted: Mon Jul 11, 2022 1:55 am     Reply with quote

Well done.

Change the title of the thread to include (solved), if you are happy. Very Happy
PrinceNai



Joined: 31 Oct 2016
Posts: 479
Location: Montenegro

View user's profile Send private message

PostPosted: Mon Jul 11, 2022 9:46 am     Reply with quote

I have to correct myself when I said the end result won't be good. Went through the code again and for a 50Hz signal it makes perfect sense. GetpeakTopeak() function scans the analog input for exactly one period (40 x 500us equals 20ms, which is one whole period at 50Hz) and finds out the difference between highest and lowest sample. Then this difference is averaged from 10 results and converted to current .
mcmsat



Joined: 25 May 2018
Posts: 51
Location: Nigeria

View user's profile Send private message

Solved
PostPosted: Mon Jul 11, 2022 2:47 pm     Reply with quote

I thank PrinceNai again.
_________________
All is well even in the well!
Ttelmah



Joined: 11 Mar 2010
Posts: 19513

View user's profile Send private message

PostPosted: Tue Jul 12, 2022 12:19 am     Reply with quote

There are a couple of tweaks/comments.
First on the PIC, you should not use ADC_CLOCK_INTERNAL above 1MHz
CPU clock. Doing so results in poor ADC accuracy. You should instead be
using the master oscillator divided to give the correct sample time. The
datasheet has a table of the recommended divisions for different master
clock rates.
The second thing is that the timing of the sampling is going to be slightly
slow. The ADC takes time to read, and the loop itself takes time, so as
currently written that sample loop does not take 20mSec, instead it is
sampling for more like 22mSec. This will potentially give slight errors in
the result.
So. Change the ADC to use a fixed divisor, and then work out how long
the ADC readings will take. Then subtract this (and a few uSec more for
the loop), from the delays for the loop.
This should improve the results slightly.
mcmsat



Joined: 25 May 2018
Posts: 51
Location: Nigeria

View user's profile Send private message

PostPosted: Tue Jul 12, 2022 12:28 am     Reply with quote

Ttelmah wrote:
There are a couple of tweaks/comments.
First on the PIC, you should not use ADC_CLOCK_INTERNAL above 1MHz
CPU clock. Doing so results in poor ADC accuracy. You should instead be
using the master oscillator divided to give the correct sample time. The
datasheet has a table of the recommended divisions for different master
clock rates.
The second thing is that the timing of the sampling is going to be slightly
slow. The ADC takes time to read, and the loop itself takes time, so as
currently written that sample loop does not take 20mSec, instead it is
sampling for more like 22mSec. This will potentially give slight errors in
the result.
So. Change the ADC to use a fixed divisor, and then work out how long
the ADC readings will take. Then subtract this (and a few uSec more for
the loop), from the delays for the loop.
This should improve the results slightly.

Thanks Ttelmah for the advice. I am doing every thing I could to learn pic in CCS C.
_________________
All is well even in the well!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group