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)
Related
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
I want to control LED diod with PWM using bcm2835.h and pure C langueage. My code does not work. What am I missing?
I have tried "gpio" console command and it works fine, so I know that led is connected to the right ports. I can turn it on using console command:
gpio pwm 1 1024
My code:
#include <bcm2835.h>
#include <stdio.h>
// PWM output on RPi Plug P1 pin 12 (which is GPIO pin 18) in alt fun 5.
// Note that this is the _only_ PWM pin available on the RPi IO headers
#define PIN RPI_GPIO_P1_12
// and it is controlled by PWM channel 0
#define PWM_CHANNEL 0
// This controls the max range of the PWM signal
#define RANGE 1024
int main(int argc, char **argv)
{
if (!bcm2835_init())
{
return 1;
}
// Set the output pin to Alt Fun 5, to allow PWM channel 0 to be output there
bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_ALT5);
// Clock divider is set to 16.
// With a divider of 16 and a RANGE of 1024, in MARKSPACE mode,
// the pulse repetition frequency will be
// 1.2MHz/1024 = 1171.875Hz, suitable for driving a DC motor with PWM
bcm2835_pwm_set_clock(BCM2835_PWM_CLOCK_DIVIDER_16);
bcm2835_pwm_set_mode(PWM_CHANNEL, 1, 1);
bcm2835_pwm_set_range(PWM_CHANNEL, RANGE);
while(1)
{
bcm2835_pwm_set_data(PWM_CHANNEL, 1024);
bcm2835_delay(10);
}
bcm2835_close();
return 0;
}
I expect that my LED will turn on.
This is stupid, but after checking the math, code, wiring I discovered that app needs to be run with root privileges to have on-board access to the pins. It works fine. Topic can be closed.
I am using ATMEGA32 with Crystal 16MHz.
I have written following code to generate 1kHz wave.
I tried writing Low- 0xFF High-0x99
But I am unable to get 1kHz wave. I am getting 250Hz wave. instead of 1kHz.
Please help.
#define F_CPU 16000000UL
#include <avr/io.h> //io related
#include <util/delay.h> //delay
#include "lcd.h" //custom lib for lcd
#include <stdlib.h> //for string handling
#ifndef sbit_h
#define sbit_h
int main(void)
{
//All Declarations Go here.
OCR0=p*127/100; //set pwm1 duty cycle
OCR2=OCR0; //set pwm2 duty cycle (Output compare register)
while(1)
{
if ((bit_is_set(PIND, 3 )))
{
pinChange(LED8,1); //Turn on solenoid valve
pinChange(LED1,1);
//PORTC|=(1<<PC3);
_delay_ms(sq); //wait for squeeze time
TCCR0 |= (1<<WGM00)|(1<<COM01)|(1<<WGM01)|(1<<CS01)|(1<<CS00);
//initialise timer in PWM mode 1kHz//cs00 and cs02
_delay_us(500);//90 degree phase shift
TCCR2 |= (1<<WGM20)|(1<<COM21)|(1<<WGM21)|(1<<CS22); //initialise timer2 in PWM mode 1kHz//CS22 for 1kHz
_delay_ms(w1); //wait until weld time over
TCCR0=0x00; //stop PWM1
TCCR2=0x00; //Stop PWM2
_delay_ms(h); //wait for hold time
pinChange(LED8,0); //turn off solenoid valve
pinChange(LED1,0);
TCNT0=0x00; //Reset timer counter
TCNT2=0x00; //reset timer counter
}
}
Did you set clock selection fuse bits (and possibly clock divider) in the correct way? I'd start checking CKSEL0...3 and CKDIV8.
By my experience, getting an exact multiple of the desired time/frequency is almost always based on a wrong clock setting.
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!
I'm hitting a roadblock in porting over a program I previously wrote for the PIC10F200 (see this related SO question). Turns out another component on the board needed to be swapped out for something that needed to be communicated with through I2C, so ergo, I'm porting the program to the PIC12LF1552.
This is how the program currently works (at least on the PIC10F200)
Program starts at IDLE (no lights on)
Press the button, the T0CKI pin (in the PIC12LF1552's case, RA2) is already pulled up to VDD (~3.3V) by an external pull-up resistor, the button is connected to ground and the pin, ergo pulling T0CKI's signal to LO.
TMR0 increments (supposedly)
After a time period where PORTAbits.RA2 settles, state increments
switch block moves to turn the external LED on, in the previous version of the circuit, this would also activate the IC this pin is connected to.
Rinse repeat for the other 3 states.
I have re-verified this functionality through using MPLAB X's simulator where I fired T0CKI (again, configured as RA2, again confirmed by the simulator) to HI and then to LO to engage the TMR0 code. state increments, and everybody is happy.
When I program the device using either MPLAB X connected to my PICkit3 or the included standalone programmer MPLAB IPE, the program does not work as intended. I have re-verified all connections, the programming port is connected as it should be. The button is attached the right pin. The test LED is attached to the right pin. For the time being we are not considering any I2C communication, I just want to see my status LED (RA4) toggle.
I know that now that I've transitioned to a midrange PIC, I can use the interrupt features, but for the time being I want to get what I know has worked on a much simpler device working on the current one.
My question then is, why doesn't this program work when programmed to the PIC12LF1552, but yet works on the PIC12LF1552 simulator AND its previous incarnation worked on the PIC10F200 (both programmed and simulated)?
Thanks in advance everyone!
The following is the entire program:
#if defined(__XC)
#include <xc.h> /* XC8 General Include File */
#endif
#include <stdint.h> /* For uint8_t definition */
#include <stdbool.h> /* For true/false definition */
#include <stdio.h>
#include <stdlib.h>
/******************************************************************************/
/* Defines */
/******************************************************************************/
//#define SYS_FREQ 16000000L
//#define FCY SYS_FREQ/4
#define _XTAL_FREQ 500000
__CONFIG
(
MCLRE_ON &
CP_OFF &
BOREN_OFF &
WDTE_OFF &
PWRTE_OFF &
FOSC_INTOSC
);
void main(void)
{
TRISA = 0b101111;
OPTION_REG = 0b01111111;
APFCONbits.SDSEL = 1;
unsigned char state = 0;
unsigned char count = 0;
while(1)
{
switch (state)
{
case 0: // IDLE/OFF
if (LATAbits.LATA4) LATAbits.LATA4 = 0;
break;
case 1: // ON
if (!LATAbits.LATA4) LATAbits.LATA4 = 1;
break;
case 2: // BLINK (slow)
LATAbits.LATA4 = !LATAbits.LATA4;
__delay_ms(100);
break;
case 3: // BLINK (fast)
LATAbits.LATA4 = !LATAbits.LATA4;
__delay_ms(50);
break;
case 4: // BEAT DETECT
LATAbits.LATA4 = LATAbits.LATA5;
break;
default:
state = 0;
break;
}
if (TMR0 > 0)
{
while (count < 20)
{
if (!PORTAbits.RA2) count = 0;
__delay_ms(10);
count++;
}
TMR0 = 0;
state++;
}
}
}
Some of the pins may be configured as Analog Inputs.
From the Datasheet for this device
"The operation of pin RA4 as analog is selected by setting the ANS3
bit in the ANSEL register which is the default set-ting after a
Power-on Reset".
If you do not set the ANSEL register the pin cannot be used as output as it is configured as an analog input.
This applies to all the pins that can be A/D inputs.
I do not see any configuration bit setup in your code.
Setting ANSEL and ANSELH to 0 should do the trick.
According to the this documentation , on page 93 about the ANSELA register
"The ANSELA bits default to the Analog mode after Reset. To use any
pins as digital general purpose or peripheral inputs, the
corresponding ANSEL bits must be initialized to ‘0’ by user software."
If you don't plan to use analog inputs, you may add something like ANSELA=0;