I want to measure pulse duration using only one CCP model in capture mode with a pic 18f4550, so I try to detect the rising edge in first time, when a rising edge is detected the timer1 turn on and the capture mode change to falling edge, with this method I have to measure the pulse width, but the code I use doesn't work well!!
it was good when I used two CCP model.
if anyone could help, I will be grateful.
#include <stdio.h>
#include <stdlib.h>
#include "osc_config.h"
#include "LCD_8bit_file.h"
#include <string.h>
void main()
{
unsigned long comtage;
unsigned long DEPHASAGE[20];
float Deph_tempo;
TRISCbits.TRISC2=1;
IRCF0=1;
IRCF1=1;
IRCF2=1;
LCD_Init();
LCD_String_xy(0,0,"Deph.tempo");
PIE1bits.CCP1IE=1;
PIR1bits.CCP1IF=0;
CCP1CON=0b00000101;
CCPR1=0;
T1CONbits.RD16=1;
T1CKPS0=0;
T1CKPS1=0;
TMR1CS=0;
TMR1IF=0;
TMR1=0;
while(1)
{
if(PIR1bits.CCP1IF==1){
TMR1ON=1;
PIR1bits.CCP1IF=0;
CCP1CON=0b00000100;
while(!(PIR1bits.CCP1IF==1))
comtage= TMR1;
PIR1bits.CCP1IF=0;
Deph_tempo = (((float)comtage /30.518)/65536 );
sprintf(DEPHASAGE,"%.5f ",Deph_tempo);
LCD_String_xy(2,0,DEPHASAGE);
}
TMR1=0;
TMR1ON=0;
CCP1CON=0b00000101;
}
}
Reducing the number of instructions being exceuted while waiting for CCP1IF will increase precision. Have you tried this?
// ...
while (1)
{
CCP1CON = 0b00000101;
PIR1bits.CCP1IF = 0;
TMR1ON = 0;
TMR1 = 0;
// if your comms with the LCD use interrupts:
//
// disable interrupts here
while (!PIR1bits.CCP1IF)
;
TMR1ON = 1;
CCP1CON = 0b00000100;
PIR1bits.CCP1IF = 0;
while (!PIR1bits.CCP1IF)
;
compte = TMR1;
// if your comms with the LCD use interrupts:
//
// enable interrupts here
// refresh display
// if your comms with the LCD use interrupts:
//
// you may want to add a small delay here, to
// allow for comms to the LCD to end.
// this may not be necessary, depending on the
// signal frequency.
}
// ...
If that doesn't work, you should check that the LCD is NOT using interruots.
If it does, you should:
disable interrupts before reading a sample
keep interrupts disabled while timing
enable interrupts before updating the display
add a delay before taking the next sample, the delay should be long enough for the LCD buffer to empty.
That's for a solution without using interrupts... I think you'll get better results using pin change interrupts and a free running timer.
EDIT: After writing this solution, I found the bug in your code, around these lines of code:
while(!(PIR1bits.CCP1IF==1)) // this is missing a ;
comtage= TMR1; // this line gets executed in the loop
// and adds instructions to the
// loop, this probably more than
// halves the precision of your
// results.
// the imprecision is increased with
// your code that runs after the if block
Related
I am doing a major embedded project of making a GM counter. As a main microcontroller I am using a PIC32MK1024MCM064. I wont bore you with details, but I need to implement following algorithm:
When MCU turns on, 13 seconds delay has to pass
MCU waits for a button press, which triggers an interrupt
Button interrupt starts 60 seconds timer
During that 60 seconds, MCU input pin counts the impulses (Idle voltage state is high (3.3V) and low voltage state counts as an impulse (50-130 us duration))
After the timer has expired, MCU outputs the impulse number via 16x2 LCD screen
Project has many files included in it, but I have already verified all the peripheral code is written right:
I have already made sure, that my button interrupt and the timer interrupt work absolutely fine (used the oscilloscope, real timer and all the other stuff.)
I also verified that my I2C driver for the LCD screen works great as well.
I made sure I am indeed getting the low state impulses as defined (50-130 us duration.)
#include <xc.h>
#include "configurations_bits.h"
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "stdio.h"
#include <sys/attribs.h>
#include "delay.h"
#include "inter_integrated_circuit_protocol.h"
#include "liquid_crystal_display.h"
#include "pulse_width_modulation.h"
#include "timer.h"
#include "state_change_interrupts.h"
static int Particle_count = 0; //Variable for counting events
char Value[10]={0}; //Particle value string
int main(void) {
__builtin_disable_interrupts(); //Global interrupt disable
ANSELA = 0x00000000; //Enable PORT A digital inputs
ANSELG = 0x00000000; //Enable PORT G digital inputs
TRISEbits.TRISE12 = 0; //Output gpio of a led
TRISDbits.TRISD8 = 0; //Output gpio of a led
TRISGbits.TRISG9 = 0; //Output GPIO of GM ENABLE
LATGbits.LATG9 = 1; //Enable GM right away
TRISAbits.TRISA1 = 1; //Input for counting particles
TRISCbits.TRISC7 = 1; //Input for control button
Inter_Integrated_Circuit_Setup (); //I2C configuration
Inter_Integrated_Circuit_Enable (); //I2C enable
Liquid_Crystal_Display_initialization(); //LCD configuration
Liquid_Crystal_Display_Set_Cursor(1, 1);
Liquid_Crystal_Display_Write_String("Preparing for");
Liquid_Crystal_Display_Set_Cursor(2, 1);
Liquid_Crystal_Display_Write_String("Measurement");
Pulse_Width_Modulation_Setup(); //PWM setup
Pulse_Width_Modulation_Enable(); //Turn on high voltage generation
for(int i=0; i<13; i++){
delay_ms(1000);} //Waiting until the high voltage rail rises up to 400V (starting GM tube voltage)
Liquid_Crystal_Display_Clear(); //Clearing LCD
Liquid_Crystal_Display_Set_Cursor(1, 1);
Liquid_Crystal_Display_Write_String("Ready for");
Liquid_Crystal_Display_Set_Cursor(2, 1);
Liquid_Crystal_Display_Write_String("Measurement");
State_Change_Interrupts_Setup (); //Button interrupt setup
Timer2_Setup (); //Timer interrupt setup
__builtin_enable_interrupts(); //Global interrupt enable
while (1){
if(T2CONbits.ON){ //Condition, that the timer is on
Liquid_Crystal_Display_Clear(); //Clearing LCD
Liquid_Crystal_Display_Set_Cursor(1, 1);
Liquid_Crystal_Display_Write_String(" Measurement Is ");
Liquid_Crystal_Display_Set_Cursor(2, 1);
Liquid_Crystal_Display_Write_String(" In Progress ");
while(T2CONbits.ON){ //Execute this while cycle until the timer shuts off
if(PORTAbits.RA1==0){
Particle_count = Particle_count+1;
delay_us(100);} //GM tube dead time compensation
}
Liquid_Crystal_Display_Clear(); //Clearing LCD
sprintf(Value,"%05d CPM",Particle_count);
Particle_count=0; //Resetting the CPM value
Liquid_Crystal_Display_Set_Cursor(1, 1);
Liquid_Crystal_Display_Write_String(Value);//Printing radiation level in CPM notation
delay_ms(5000);
Liquid_Crystal_Display_Clear(); //Clearing LCD
}
}
return (EXIT_FAILURE);
}
When I press the button, my LCD outputs "Measurement Is In Progress" for a fraction of a second, then it immediately outputs "00000 CPM" which means it had counted zero impulses (In my application, I know for sure that I must capture at least 5 impulses per minute.) Looks like the while cycle has only one or few iterations (it should last for a minute). So my code problem is not a missing semicolon somewhere, but I feel like the whole architecture is not right. Have you got any observations or suggestions, how could I implement the mentioned algorithm? Want to thank you in advance.
(P.S I am running a 8 MHz internal FRC as the main clock)
I would like to measure a pulse using the pic 18f4550 in capture mode, this pulse is generated by the pic microcontroller itself, for this I use a function which plays the role of the XOR logic gate (you find the function that I've used below). with RC0 and RC2 the inputs and RC6 the signal output. the pulse leaving RC6 enters ccp2 to be measured.
The problem I found is that the ccp2 cannot detect the impulse generated by the microcontroller. I don't know if there are any conditions to connect the pins of the microcontroller or something.
If anyone has an answer or a hint to fix this, I will be grateful!
and if you have any questions feel free to ask .thanks !!
UPDATE: I changed some instructions in the code, now the RC6 output provides a signal. but my LCD does not display anything. the RC6 output is present below.
UPDATE 2: the while(1) in the xor() function blocking the rest of my program, so the program never get out of xor() and my LCD wont display anything. when I don't use the while loop in xor () my RC6 produce anything, the same for the LCD.
I don't know where the problem is, I did everything in my power to find the bug . but the system still not working!!!
I will leave the program as it is, so new readers can understand what I am talking about.
#include <stdio.h>
#include <stdlib.h>
#include "osc_config.h"
#include "LCD_8bit_file.h"
#include <string.h>
unsigned long comtage,capt0,x;
char pulse[20];
char cosinus[20];
float period,dephTempo,deph,phi;
void init (){
IRCF0 =1; /* set internal clock to 8MHz */
IRCF1 =1;
IRCF2 =1;
PIE2bits.CCP2IE=1;
PIR2bits.CCP2IF=0;
CCPR2 =0; /*CCPR1 is capture count Register which is cleared initially*/
T3CONbits.RD16=1;
T3CKPS0=0;
T3CKPS1=0;
TMR3CS=0;
TMR3IF=0;
T3CCP2=0; /*Timer3 is the capture clock source for CCP2*/
}
void xor()
{
while(1)
{
if (PORTCbits.RC0==PORTCbits.RC2)
{
PORTCbits.RC6=0;
}
else if (PORTCbits.RC0!=PORTCbits.RC2)
{
PORTCbits.RC6=1;
}
}
}
void main()
{
TRISCbits.TRISC0=1;
TRISCbits.TRISC2=1;
TRISCbits.TRISC6=0;
xor();
LCD_Init();
while(1)
{
CCP2CON = 0b00000101;
PIR2bits.CCP2IF = 0;
TMR3ON = 0;
TMR3 = 0;
while (!PIR2bits.CCP2IF);
TMR3ON = 1;
CCP2CON = 0b00000100;
PIR2bits.CCP2IF = 0;
while (!PIR2bits.CCP2IF);
comtage = CCPR2;
dephTempo = (((float)comtage /30.518)/65536 );
sprintf(pulse,"%.3f ",dephTempo);
LCD_String_xy(0,0,"the pulse width is : ");
LCD_String_xy(2,9,pulse);
}
}
I have 2 questions.
The first: I have a problem in the behavior of this code; when I run it in Proteus the program make flasher "repeat the code in the main function"
what should I do?
This is the code:
#include <p18f452.h>
#include <delays.h>
#include <io.h>
void main ()
{
TRISC=0x00;
PORTC=0xff;
Delay1KTCYx(900);
PORTC=0x00;
Delay1KTCYx(900);
while(1)
{
}
}
The second question: what is the proper delay function I can use? and how can I measure the delay time?
Is the watchdog disabled in simulation ? If it is enabled it will cause the repetition of the program.
Try adding this line after the includes.
#pragma config WDT = OFF
You only have code to generate one flash. Move the flash and delays into the loop:
for(;;)
{
PORTC = 0xff;
Delay1KTCYx(900);
PORTC = 0x00;
Delay1KTCYx(900);
}
Measuring roughly can be made manually by timing N flashes with a stopwatch. It's of course easier to use a measurement intrument (an oscilloscope is nice for this) if you have it.
Also, since your duty cycle is 50%, you can simplify the code:
PORTC = 0;
for (;;)
{
PORTC = ~PORTC;
Delay1KTCYx(900);
}
This uses bitwise not (~) to invert the bits of PORTC, which will make them toggle from one to zero and vice versa. Setting the entire port to 0 before the loop makes sure all pins are at a known state.
I am trying to get an Arduino UNO to read a 64cpr quadrature encoder. I specifically want to use Timer1 to measure of the frequency (and hence speed) of one of the encoder signals.
I ultimately want to store 10 measurements in an array to compute a moving average filter, but one thing at a time. I first need to be able to measure the clock cycles between two rising edges.
Here's what I've got so far. Any help or comments are appreciated:
#include <avr/io.h>
#include <avr/interrupt.h>
const int inputCapture = 8;
void setup(){
sei();
TCNT1 = 0;
TCCR1B = (1<<CS10)|(1<<ICES1); // No prescaling
TIFR1 = 1<<ICF1;
pinMode(inputCapture, INPUT);
Serial.begin(9600);
}
ISR(TIMER1_CAPT1_vect){
thisStep = ICR1;
TCNT1 = 0;
}
void loop(){
Serial.println(thisStep);
}
Right now I'm not even jumping into the ISR, which I don't understand. I think I have set everything up correctly. Interrupts are enabled. ICES1 should default to 0, or falling edge trigger, which is fine (just want to measure one period). I'm picking a pin on port B to receive the (input) signal, which should be fine. From Atmel's documentation, I think Timer1 is connected.
Any thoughts? Thanks in advance!
So I did this code for Arduino in C. It is for controlling a stepper motor. But every time I have to wait until the microcontroller begins a new loop so that it can take the value for the new variables, how can I create an interrupt so it will do it in any time of the program?
#include <avr/io.h>
#define F_CPU 4000000UL
#include <util/delay.h>
#include "IO/ioconfig.h"
#include "leebotones/leebotonesA.h"
#include "leebotones/leebotonesB.h"
#include "rutina/avanza.h"
#include "rutina/retrocede.h"
char variableA = 0;
char variableB = 0;
int main(void){
ioconfig();
while(1) {
if (leebotonesA()==1) {
variableA++;
} //Fin de if leebotonesA.
if (leebotonesB()==1) {
if (variableA==0) {
variableB=1;
}
else {
variableB=0;
}
}
if (variableA==2) {
variableA=0;
PORTD=0x00;
_delay_ms(10000);
} //Fin de if variableA.
if (variableA==1 && variableB==0) {
avanza();
} //Fin de if leebotonesA.
if (variableA==1 && variableB==1) {
retrocede();
}
_delay_ms(25);
}//End of while
}// End of main
A hardware interrupt on the Arduino occurs when one of the interrupt pins receives a change of state. The function to use, if you have access to the Arduino library, is attachInterrupt.
Example code to listen for an interrupt (derived from the documentation I linked to, I added comments to help explain):
// The Arduino has an LED configured at pin 13
int pin = 13;
// Holds the current state of the LED to handle toggling
volatile int state = LOW;
void setup()
{
pinMode(pin, OUTPUT);
// First Parameter:
// 0 references the interrupt number. On the Duemilanove, interrupt 0
// corresponds to digital pin 2 and interrupt 1 corresponds to digital pin
// 3. There are only two interrupt pins for the Duemilanove and I believe
// the Uno too.
// Second Parameter:
// blink is the name of the function to call when an interrupt is detected
// Third Parameter:
// CHANGE is the event that occurs on that pin. CHANGE implies the pin
// changed values. There is also LOW, RISING, and FALLING.
attachInterrupt(0, blink, CHANGE);
}
void loop()
{
// Turns the LED on or off depending on the state
digitalWrite(pin, state);
}
void blink()
{
// Toggles the state
state = !state;
}
There is also a concept of Pin Change Interrupts that is supported on every pin. See the bottom part of introduction to interrupts for more info.
However, sometimes a hardware interrupt can be avoided by refactoring your code. For example, keep your loop() running quickly --- mostly just reading inputs, limit the use of delay() --- and in the loop, call a function when the targeted inputted value is detected.
MSTimer2 is an Arduino library function to let you set timer interrupts.
Another alternative to delay, rather than interrupts, is to set a timer and check it each time through the loop. http://arduino.cc/en/Tutorial/BlinkWithoutDelay explains the concepts. The Metro library http://arduino.cc/playground/Code/Metro implements this and is easy to use. I've used it instead of delay() so that I can check for a button push while my robot is moving.