View previous topic :: View next topic |
Author |
Message |
emelyjose
Joined: 13 Nov 2016 Posts: 5
|
Millis() function in CCS |
Posted: Sun Nov 13, 2016 5:06 pm |
|
|
Hi how is it going. I'm new to the forum and programming and I really would like you to give me an idea or a program to be able to monitor how much time a program actually runs in microseconds from start to finish and save it in a variable.
Really what I want is to implement a code of a PID to control the temperature of an oven but the code is written for the Arduino compiler. And the only break I have is that I found a function called millis() and not really how to measure the execution time of my program.
If it helps someone here, I leave them the code of the program so that they can analyze it and then tell me that they think.
I once saw that this can be done using a Timer but I have searched and searched in many parts of the internet and I do not find anything similar to the Millis () function implemented in CCS.
Really the code is missing a lot but my main priority is how to learn to use millis () in ccs.
Code: |
#include <18F4550.h>
#device ADC=10
#use delay(crystal=20000000)
#FUSES PUT //Power Up Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOVREGEN //USB voltage regulator disabled
#FUSES NOPBADEN //PORTB pins are configured as digital I/O on RESET
#FUSES NOLPT1OSC //Timer1 configured for higher power operation
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST //Extended instruction set disabled
#define LCD_RS_PIN PIN_D0
#define LCD_RW_PIN PIN_D1
#define LCD_ENABLE_PIN PIN_D2
#define LCD_DATA4 PIN_D3
#define LCD_DATA5 PIN_D4
#define LCD_DATA6 PIN_D5
#define LCD_DATA7 PIN_D6
#include <lcd.c>
int timeChange, SampleTime = 100; // Seteamos el tiempo de muestreo en 100 Mili Segundo
int NewSampleTime=0;// A modificar......
int16 control=0;
unsigned long lastTime, now;
double Temp=0, salida, Setpoint, ITerm, lastInput,kp, ki, kd, outMin,outMax, Min, Max;
void main()
{
setup_timer_2(t2_div_by_4,249,1); //Configuracion de Timer 2 para establecer frec. PWM a 1kHz
setup_ccp1(ccp_pwm); //Configurar modulo CCP1 en modo PWM
set_pwm1_duty(0); //Dejo en cero la salida PWM
setup_adc_ports(AN0); //Configurar ADC (Lectura de temperatura) setup_adc_ports(AN0||VSS_VREF);
setup_adc(adc_clock_internal); //Reloj interno para la conversion analoga digital)
set_adc_channel(0); //Seleccionar Canal 0 para sensor de Temperatura
LCD_INIT(); //Inicializo el LCD
LCD_PUTC("\f"); //Limpio el LCD
while(TRUE)
{
Temp=read_adc()*5000.0/1024.0;
lcd_gotoxy(1,1);
printf(lcd_putc,"Temp: %f",Temp/10);
unsigned long now = millis(); // xxx duracion del programa viene del timer 0 unsigned long now = millis();
double timeChange = (double)(now - lastTime);
if(timeChange>=SampleTime)// Determina si hay que ejecutar el PID o retornar de la función.
{
/* Calculamos todas las variables de error. */
double error = Setpoint - Temp;
ITerm += (ki * error);
if(ITerm> outMax) ITerm= outMax;
else if(ITerm< outMin) ITerm= outMin;
double dInput = (Temp - lastInput);
/* Calculamos la función de salida del PID. */
salida = kp * error + ITerm - kd * dInput;
if(salida > outMax) salida = outMax;
else if(salida < outMin) salida = outMin;
lcd_gotoxy(1,4);
printf(lcd_putc,"u(ley Con): %ld ",control);
/* Guardamos el valor de algunas variables para el próximo ciclo de cálculo. */
lastInput = Temp;
lastTime = now;
}
/* Establecemos los valores de las constantes para la sintonización. */
double SampleTimeInSec = (double) SampleTime /100;
kp = Kp;
ki = Ki * SampleTimeInSec;
kd = Kd / SampleTimeInSec;
/* si el usuario decide cambiar el tiempo de muestreo durante el funcionamiento, Ki y Kd tendrán
que ajustarse para reflejar este cambio. */
if (NewSampleTime > 0)
{
double ratio = (double)NewSampleTime / (double)SampleTime;
ki *= ratio;
kd /= ratio;
SampleTime = (unsigned long)NewSampleTime;
}
if(Min > Max) return;
{
outMin = Min;
outMax = Max;
}
if(salida > outMax) salida = outMax;
else if(salida < outMin) salida = outMin;
if(ITerm> outMax) ITerm= outMax;
else if(ITerm< outMin) ITerm= outMin;
}
delay_ms(1000);
//---------------------------------------------------------------------------//
/*lcd_gotoxy(1,2);
printf(lcd_putc,"SetPoint: %2.2f C ",R/10);
lcd_gotoxy(1,3);
printf(lcd_putc,"Error: %2.2f C ",e/10);*/
/* Cuanto tiempo pasó desde el último cálculo. */
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Mon Nov 14, 2016 3:46 am |
|
|
Before looking at time, there are some problems elsewhere. For instand 'double' does not exist. The largest type that exists in CCS for the PIC18, is a single. It won't error, but it'll just replace double with single. Honestly though you also need to understand 'why'. Even single precision maths is slow. On your chip a single precision division takes nearly 300uSec at your clock rate. Imagine how much worse it'd be using double. Also there is almost nothing in the world that needs this type of precision. Learn to reduce your task to be practical in the chip....
Then your adc setting is wrong. ADC_CLOCK_INTERNAL, is not recommended above 1MHz, unless you stop the chip to take teh reading. Look at the data sheet.
Generally most LCD's need some delay before starting. The PIC will start to run before the LCD has actually started. Put some extra delay at the start of the code before trying to wake the LCD, or you are likely to find this unreliable.
Then there are some 'insanities' in your code. You take an integer, and convert this to a float value ((double)(now - lastTime)). Ugh. Stick to integers.
Then for your question, look at the CCS #USE TIMER. and it's associated functions. In particular get_ticks. Millis is not a generic function (pretty much only exists for the Arduino). Get_ticks, in it's standard form is equivalent to the Arduino 'micros' function. However you can reprogram the #use to work in mSec if you require, and then get_ticks would be equivalent to get_millis. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Mon Nov 14, 2016 6:03 am |
|
|
couple of comments regarding...
Quote: | Temp=read_adc()*5000.0/1024.0; |
Floating point division takes 'forever' on a PIC. It is better to just do
Temp=read_adc()*4.88 IF you use floating point, though using all integer math IS the way to go...
Also "5000.0" looks like you're using VDD as the Vref. If this is true, please note that VDD is NOT stable enough for Vref, it's also NOT 5.000 volts AND WILL vary depending on the current demands. Whenever an I/O pin it turned on,the VDD will drop. When doing 'calculations' as opposed to being idle, the VDD will drop.
You also talk about an 'oven control', this implies a high current relay and that WILL drop VDD and/or induce EMI spikes onto VDD. Since you're using VDD as Vref, Vref WILL 'see' higher of lower voltages so the ADC will not report the real Vin.
It's best to
1) use a stable voltage reference device for Vref
2) sample 16* then take average(fast with a PIC)
3) use a LOT of bypass caps on the PCB
4) use heavy Vdd and Gnds on the PCB
5) use R-C-R-C filter for the temp sensor input to PCB.
Jay |
|
|
emelyjose
Joined: 13 Nov 2016 Posts: 5
|
|
Posted: Mon Nov 14, 2016 4:49 pm |
|
|
Hi, thanks for your comments and the tips that you are giving me, I will take them into account. As I said this is an original code for Arduino and I I am in the process of migrating it to CCS.
But my main question is how do I measure the instructional time of the program from start to finish ?
If you could give me an example of how to do it. The rest of the code I am adjusting as I organize.
Function millis() of Arduino, migrated to CCS pic - how it is performed ? Please help me. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Mon Nov 14, 2016 5:58 pm |
|
|
I'm not to sure how or why you want the ardunio function millis(). It only shows how long a program has run for since the PIC was started. It should be easy to setup a timer and 'emulate' the millis() function, I'm just at a loss to understand what benefit it is.
A PID controller doesn't need it !
However I'm thinking now, perhaps you may want to know how long the PIC has turned on the heater vs off time?
Jay |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Nov 15, 2016 3:05 am |
|
|
Why?
Please tell us what you want time in ms for, and the required precision.
Mike |
|
|
emelyjose
Joined: 13 Nov 2016 Posts: 5
|
|
Posted: Tue Nov 15, 2016 6:59 am |
|
|
I need to implement the millis() Arduino function in ccs to get the time it takes to run the program from the beginning to the endl in milliseconds and save this time in a variable. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Tue Nov 15, 2016 12:22 pm |
|
|
OK, have a look at the 'stopwatch' program that CCS supplies in the examples folder (stwt.c or something like that, I'm going from memory here..)
At the beginning of your program you'll need to 'start' the task, then when your program is done, 'stop' the task, then printout the value stored.
If you setup the timer correctly, each bit will be 1 millisecond......
Jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Tue Nov 15, 2016 12:35 pm |
|
|
As I have already said, #USE TIMER, then 'get_ticks' by default gives a direct equivalent of the Arduino 'micros' function. Divide by 1000, and you have millis. You can also specify the tick in the #USE TIMER setup, so could set this to give 1mSec. However this may not be possible because of the limitations of the hardware timers, so division is easier. |
|
|
emelyjose
Joined: 13 Nov 2016 Posts: 5
|
|
Posted: Wed Nov 16, 2016 1:55 am |
|
|
As I said I'm new to programming and I do not know how to implement ticks.
Could give me an example. Thank you. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Wed Nov 16, 2016 12:24 pm |
|
|
emelyjose wrote: | I need to implement the millis() Arduino function in ccs to get the time it takes to run the program from the beginning to the endl in milliseconds and save this time in a variable. |
But you are still not telling us why you must have the information.
However to generate 1ms ticks you can:-
1) Set up a spare timer to generate an interrupt every 1ms.
2) In the associated ISR you increment a tick_counter.
3) Then at the start of your routine make a note of the tick_counter value.
4) At the end of the routine make second note of the tick_counter value.
5) Perform the subtraction and job's done.
Mike |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
|