Been searching for an answer for this for a few days now. I'm trying to create this simple verification program where if I push a button it drives the voltage on RC0 to 5 volts, and when the button is unpushed, a pulldown resistor pulls the pin down to 0. The problem is, the code will only work the first time I push the button. After that it will not enter the if (RC0 == 1) loop. Here is my code snippet. What am I missing here?
void main(void){
/* Configure the oscillator for the device */
ConfigureOscillator();
/* Initialize I/O and Peripherals for application */
InitApp();
TRISB = 0x0F; // turn on RB7 for use as output
TRISC = 0xFF; // turn on RC0 for use as input
ANSEL = 0b11101111; // use RC0 as digital input
unsigned char time = 0;
while (1) {
//delay for 10 seconds
if (PORTCbits.RC0 == 1) {
while (time < 10) {
/* TODO */
RB7 = 1;
__delay_ms(100);
RB7 = 0;
__delay_ms(100);
time++;
}
}
if (PORTCbits.RC0 == 0){ //This if statement will always execute when
//RC0 == 0, but will not execute when I push
//the button. This really confuses me.
//Can RCO be given a value different than 1?
__delay_ms(2000);
}
RB7 = 0;
__delay_ms(500);
RB7 = 1;
__delay_ms(500);
}
}
Related
I have a PIC12F1822 of which there is an LED and relay as an output and a trigger as an input.
When the trigger is equal to one, I would like the to have the LED and relay stay turned on for 90 minutes.
The problem is that I cannot use a delay since __delayms has a limit.
How can this be done?
#include <xc.h>
#define _XTAL_FREQ 4000000
#define LED PORTAbits.RA5
#define RELAY PORTAbits.RA4
#define TRIGGER PORTAbits.RA2
int main()
{
// OSCCON = 0b01101000 ;
OSCCONbits.IRCF = 0b1101 ;
TRISAbits.TRISA5 = 0; //LED Output PIN
TRISAbits.TRISA4 = 0; //Output for Relay
TRISAbits.TRISA2 = 1; //INPUT trigger from comparator
while(1)
{
if (TRIGGER == 1)
{
LED = 1; // LED ON
__delay_ms(1000); // 1 Second Delay
LED = 0; // LED OFF
__delay_ms(1000); // 1 Second Delay
}
else
{
LED = 0; // LED OFF
}
}
return 0;
}
why not wrap the 1s delay in a function providing a delay_s(uint32_t seconds) and in a function providing a delay_m(uint32_t minutes)
void delay_s(uint32_t seconds)
{
while(seconds){
__delay_ms(1000);
seconds--;
}
}
void delay_m(uint32_t minutes)
{
while(minutes){
delay_s(60);
minutes--;
}
}
But:
You should be aware that this totally blocks your µC's program.
It will not react on any key press. This may be ok but may be you want the delay to be reset to 90min if the key is pressed during the delay.
I would suggest to implement the whole thing with interrupts and timers.
Set up a timer that triggers once every second and countdown a global variable (volatile!) within the isr that was set by your main loop. If the counter reaches 0 your isr may disable your output.
This way your µC can process other things in the mainloop and is not blocked by a __delay-function.
Simple. State machines and timer interrupts and a minute countdown. Something like this:
Disclaimer - coded blindly
#define MINUTES_90 5400UL /* 90 seconds */
typedef enum
{
/** Code operational */
p_state_idle,
/** Code waiting for 90 minutes */
p_state_waiting
}p_state_t;
static unsigned long gSecondsRemaining = MINUTES_90;
int main()
{
p_state_t gState = p_state_running;
OPTION_REGbits.PSA = 0; /* Prescaler assigned */
OPTION_REGbits.PS = 0b111; /* 256 prescaler */
OPTION_REGbits.TMR0CS = 0; /* Fosc / 4 */
INTCONbits.TMR0IE = 1; /* Timer 0 interrupt enabled */
INTCONbits.PEIE = 1; /* Peripheral interrupts enabled */
INTCONbits.GIE = 1; /* Global interrupts enabled */
/** Default LED off */
LED = 0;
while (1)
{
switch (gState)
{
case p_state_idle:
if (TRIGGER == 1)
{
LED = 1; // LED ON
gSecondsRemaining = MINUTES_90; /* Reset timer countdown */
gState = p_state_waiting;
}
break;
case p_state_waiting:
/** can sleep here */
if (gSecondsRemaining == 0)
{
gState = p_state_idle;
LED = 0;
}
break;
}
}
}
void interrupt ISR()
{
static unsigned char gSecond = 15;
/** approx 15 Hz ? */
if (INTCONbits.TMR0IF)
{
INTCONbits.TMR0IF = 0;
if (gSecond > 0)
gSecond--;
if (gSecond == 0)
{
if (gSecondsRemaining > 0)
gSecondsRemaining--;
gSecond = 15;
}
}
}
You could solve this with an inline assembly function with some loops. Have a look here: example: 30 min delay
I am facing a problem while implementing a timer based interrupt in mikroC for PIC.
I want to toggle a port pin for 8 times if there is a keypress at PORTC.F0 and there should be a delay of say 100ms between the toggles.
Normally this would be very easy using a delay function
for (i=0;i<=8;i++)
{
PORTB.F0=~PORTB.F0;
Delay_ms(100);
}
But during the period, any other keypresses are missed by the system. So I thought of implementing the solution using interrupts.
#define SW PORTC.F0
char ttime,i;
volatile flag;
void Inittimer()
{
T1CON = 0x01;
TMR1IF_bit = 0;
TMR1H = 0x06;
TMR1L = 0x00;
TMR1IE_bit = 1;
INTCON = 0xC0;
}
void Interrupt()
{
if (TMR1IF_bit)
{
TMR1IF_bit = 0;
TMR1H = 0x06;
TMR1L = 0x00;
ttime--;
if (ttime==0)
{
flag=1;
}
}
}
void main()
{
Inittimer1();
TRISB = 0;
TRISC.F0 = 1;
PORTB = 0x00;
while(1)
{
if (SW==0)
{
ttime=3;
}
if (flag==1)
{
for (i=0;i<=8;i++)
{
PORTB=~PORTB;
flag=0;
}
}
}
}
Nothing is working. Can somebody please help me to correct the code?
Well this doesn't look right:
if (flag==1)
{
for (i=0;i<=8;i++)
{
PORTB=~PORTB;
flag=0;
}
}
When you first see that flag is set, you immediately loop and toggle the output 8 times, without waiting for flag to turn back to 1. That's not right, it's overly simplified.
You need to look for the flag, then toggle the output and clear the flag, and wait for it to to get set again, maintaining the counter in parallel. The for loop is not the proper structure for this, since it will "lock out" the rest of the program and might cause keypresses to be missed.
When you initialize your timer:
void Inittimer()
{
T1CON = 0x01;
TMR1IF_bit = 0;
TMR1H = 0x06; // No prescaler? I doubt your clock speed is 40-some KHz!
TMR1L = 0x00;
TMR1IE_bit = 1;
INTCON = 0xC0;
}
Why don't you control the LED directly from the ISR ?
if (ttime)
PORTB.F0 = (--ttime & 1); // ttime is not decremented when led is not blinking.
else
PORTB.F0 = 0; // ensures the LED is off.
To start blinking 8 times:
if (SW==0)
{
PORTB.F0 = 1;
ttime = 16;
}
Note that with a 100ms clock interrupt, the first 'blink' of the LED may last up to 200ms... This is why many like to work with a faster timer interrupt (this has usually other uses as well), controlling the led would require adding a soft post-scaler
if (blinking)
{
if (--blinkTimer == 0)
{
blinkTimer = BLINK_DELAY; // whatever number it takes for 100ms.
PORTB.F0 = (--blinking & 1);
}
}
else
{
PORTB.F0 = 0
}
To start blinking:
if (SW==0)
{
blinking = (2 * BLINKS) - 1;
blinkTimer = BLINK_DELAY;
PORTB.F0 = 1;
}
This should get you a more even first blink.
#include "stm32f30x_conf.h"
uint16_t read_pos(void);
void PC_Conf(void);
uint8_t get_bit(uint8_t, uint8_t);
// PROCESSORTACT = 64 MHz
// AHB Prescaler = 1
// APB1 Prescaler = 2
// APB2 Prescaler = 1
long pin = 1;
long dir = 1;
char recvd;
int main(void){
/*
*/
//Definitions
GPIO_InitTypeDef GPIO_Initstructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_NVICInitStructure;
//ENABLE CLOCK
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//GPIO
GPIO_Initstructure.GPIO_Mode=GPIO_Mode_OUT;
GPIO_Initstructure.GPIO_OType=GPIO_OType_PP;
GPIO_Initstructure.GPIO_Pin=GPIO_Pin_All;
GPIO_Initstructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_Initstructure);
//TIMER
TIM_TimeBaseInitStructure.TIM_Prescaler = (64000-1); //TIM2 cycle to 1kHz
TIM_TimeBaseInitStructure.TIM_Period = 500; //Every 500ms
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//NVIC
NVIC_NVICInitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_NVICInitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_NVICInitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_NVICInitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_NVICInitStructure);
//Enable everything
TIM_Cmd(TIM2, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
/* Infinite loop */
while (1)
{
}
}
void TIM2_IRQHandler(void)
{
GPIOB->ODR &= 0;
GPIOB->ODR ^=1<<pin;
//Pin to new value
if(dir == 1)
{
if(pin == 5)
pin=11;
else if(pin == 15)
{ pin-=1;
dir = 0;
}
else
pin+=1;
}
else
{
if(pin == 11)
pin = 5;
else if(pin == 1)
{
pin+=1;
dir = 1;
}
else
pin-=1;
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
I have trouble understanding this code. First part is okay, but I don't understand the last function.
Can someone explain me simply what is going on in the last part of this code in TIM2_IRQHandler(void)?
I am new in this, any help would be welcome.
In many interrupts you need to clear the interrupt pending flag. If you don't when you exit from the handler, the interrupt will be called again. Every interrupt is described in the Reference manual of your micro. For example SPI interrupt pending flag is cleared by writing or reading from the DR register. Another one requires that bit to be cleared by the programmer.
The rule of thumb: clear the flag as soon as you enter the interrupt.
If the interrupt can be triggered by more than one event, the programmer should check which one was the source of the exception.
I'm able to receive with the following code, but unfortunately, nothing is sent back. What am I doing wrong?
#include <pic18f25k80.h>
#include "config.h"
#include <usart.h>
int i = 0;
unsigned char MessageBuffer[200];
void main() {
OSCCONbits.IRCF = 0b110; // 8MHz
TRISB6 = 0; // TX set as output
TRISB7 = 0; // RX set as output
// Clear TX interrupt
// Set RX interrupt
// 8-bit Asynch. mode
// BRGH = 1 = high baud mode
// 51 = ((8MHz/baud)/16)-1 with baud = 9600
Open2USART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE
& USART_EIGHT_BIT & USART_BRGH_HIGH, 51 );
RC2IF = 0; // reset RX2 flag
RC2IP = 0; // not high priority
RC2IE = 1; // Eneble RX2 interrupt
INTCONbits.PEIE = 1; // enable peripheral interrupts
INTCONbits.GIE = 1; // enable interrupts
RCSTA2bits.SPEN = 1; // enable USART
while(1){
}
}
void interrupt ISR () {
if(PIR3bits.RC2IF == 1) {
if(i<200) { // buffer size
MessageBuffer[i] = Read2USART(); // read byte from RX reg
if (MessageBuffer[i] == 0x0D) { // check for return key
puts2USART(MessageBuffer);
for(;i>0;i--)
MessageBuffer[i] = 0x00; // clear array
i=0;
return;
}
i++;
RC2IF = 0; // clear RX flag
} else {
puts2USART(MessageBuffer);
for(;i>0;i--)
MessageBuffer[i] = 0x00; // clear array
i = 0;
return;
}
}
}
I'm transmitting the 0x41 hex code, I checked with the scope and see that is is being received. And according to the code I have, an echo of the received data should be sent back. When I check the TX pin, nothing is happening.
Add USART_CONT_RX to Open2USART to enable continuous receive.
Also, it's a good idea to do the minimum necessary in the interrupt service routine. Consider something like:
void interrupt ISR () {
char data;
if(PIR3bits.RC2IF == 1) {
data = Read2USART(); // always read byte from RX reg (clears RC2IF)
if(i<200) { // buffer size
MessageBuffer[i] = data; // read byte from RX reg
i++;
}
else{
// flag buffer full error
}
}
}
and doing the rest of what you are doing in the while(1) loop.
I've been doing a project about home automation in which I have to use timer interrupts with 8051 microcontroller. I've constructed the following code, however I couldn't manage to get interrupt working. It seems that the program does not go into timer ISR at all. I use a buton to simulate PIR input, therefore lampControl is triggered, no worries there. I use as a library.
Any ideas or help will be greately appreciated:
void timer0_isr(void) interrupt 1 //Timer 0 Interrupt
{
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
if (++lamp_interrupt_count == 6000)
{
sendCharacterShowAsHex(0x8F);
lamp_interrupt_count = 0;
TR0 = 0;
}
}
void main()
{
unsigned char chr;
IE = 0x93;
while(1)
{
serialInput();
if (getPIRInput() == 0x00)
{
lampControl(0x80);
}
....
....
....
}
void lampControl(unsigned char serial_data_in)
{
if (serial_data_in == 0x80)
{
sendCharacterShowAsHex(0x80);
//enable interrupts
IE = 0x93;
device_interrupt = 2; //Lamp
TMOD = 0x21; // Timer0 Gate=0, Mode 1, 16bit timer
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
}
else if(serial_data_in == 0x8F)
{
sendCharacterShowAsHex(0x8F);
}
}
You need to configure the timer and interrupts before you can use them.
In main() you need at least the following configuration bits set in order to be able to turn
the timer on with "TR0 = 1;" :
Set those bits first thing in main() and this should do the trick:
TMOD = 0x01; // 16-bit no auto reload
TH0 = 0xDC; //Set high and low bits to count 0xFFFF - 0xDC00 = 0x23FF counts
TL0 = 0x00;
ET0 = 1; // Enable timer0 interrupt
EA = 1; // Enable all interrupts
//TR0 = 1; //Enable Timer0 immediately
The rest of your code should run fine.
Note: you could change your interrupt function definition to:
"void timer0_isr(void) interrupt 1 using 1" to force it to use register bank 1 for the interrupt function operation.