I am trying to generate a 1 second delay, using a timer module.
The value in the timer register is 61 DEC, which will generate an interrupt every ~50ms, which will increment the value of a variable named value.
After 20 interrupts ~1 sec will have passed, which will alter the state of an LED. But this doesn't seem to work as intended. The oscillator frequency is 4MHz, the prescaler value of the timer module is 256 and the targeted microcontroller is PIC16F72.
Here is the code:
char value;
bit state;
void Interrupt(){
if(TMR0IF_bit){
value++;
TMR0 = 61;
TMR0IF_bit = 0;
}
}
void main(){
value = 0;
state = 0;
TRISB.RB0 = 0;
OPTION_REG = 0x87;
TMR0 = 61;
INTCON = 0xA0;
while(1){
if(value == 20){
value=0;
state=~state;
PORTB.RB0=state;
}
}
}
The declaration of the ISR is wrong
Switch:
void Interrupt(){
to:
void interrupt(){
And don't forget the volatile
volatile char value;
Related
I'm trying to blink an led without using the delay function.
I came across using the timer interrupt and I tried it, and it compiles fine. But the output is fixed on PORTA = 0x01; so, I believe the ISR function is not working. Is there anything I am missing in the code? Thanks.
#include <asf.h>
#include <avr/interrupt.h>
volatile unsigned int count=0;
volatile unsigned int tTime=0;
void port_init(void) {
PORTA = 0xff;
}
ISR(TIMER0_OVF_vect)
{
TCNT0 = 206;
count++;
if (count < 625) {
PORTA=0x01;
}
else if ((count > 625) && (count < 1250)) {
PORTA=0x00;
}
else {
count=0;
}
}
int main (void)
{
board_init();
port_init();
TCCR0 = 0x06; //setting dispensing ratio
TCNT0 = 206; //initial value of register
TIMSK = 0x01; //enabling interrupt
SREG=0x80;
DDRA=0xff;
while(1){
}
}
You have a logic error in your ISR() function. Once count hits 625 the first two if/else-if clauses will be false (because 625 is neither less than, nor greater than, 625), thus the final else clause will execute and reset count to zero. The result of this is that the else-if clause that sets PORTA=0x00 will never execute.
To fix, change the first if from < to <=, like so:
if (count <= 625) {
I'm programming my C code onto a PIC board. My question is how to get my program to count how long a button (RB0) is pressed down. It then displays the time it took and display it on an LCD Display. It counts in ms. Below is my code so far.
// Global Variables
unsigned int COUNTER;
// Subroutine Declarations
#include <pic18.h>
#include "lcd_portd.c"
//LCD routine, modified from previous examples
void LCD_Out(unsigned int STUFF, unsigned char A)
{
unsigned char C[5], i;
for (i=0; i<5; i++)
{
C[i] = STUFF % 10;
STUFF = STUFF / 10;
}
for (i=5; i>0; i--)
{
if (i == A) LCD_Write('.');
LCD_Write(C[i-1] + '0');
}
}
void interrupt IntServe(void)
{
if (TMR2IF)
{
RB0 = !RB0;
TMR2IF = 0;
COUNTER = COUNTER + 1;
}
}
// Main Routine
void main(void)
{
//Instantiate all Ports to ready for Timers and pin sets
LCD_Init();
TRISA = 0;
TRISB = 0;
TRISC = 0;
TRISD = 0;
TRISE = 0;
ADCON1 = 0x0F;
//Timer Interrupt for 1 ms, A = 9, C = 4, B = 250
//Which means PR2 = 249, and # is x1001101 = 0x4D
T2CON = 0x4D;
PR2 = 249;
TMR2IE = 1;
PEIE = 1;
TMR2ON = 1;
TMR2IP = 1;
GIE = 1;
// While Loop displays length of Wait through Counter
while(1)
{
LCD_Move(0,0);
LCD_Out(COUNTER,3);
}
}
you need figure out how your button is connected to your microcontroller and declare that port in you main function. Make sure you are able to read the state of the port (i.e. button up or button down).
Then it your IntServe interrupt check if the button is down, if it is increment COUNTER.
The COUNTER is an integer, so it will only be able to count upto 65,535 ms (about 65 seconds), you may want to implement a second count variable for overflow, so that you can count button presses longer than 65 seconds.
I need to program a PIC16F883 to blink / light up LED's at the same time. The oscillator is running at 3,2768, and I'm using TIMER0 to help me with the timing.
Right now, I have a prescaler set to 1:256, so I get an interrupt every 50ms, and I have a variable that is calculated from that, to display how many seconds has gone.
If the input is changed, the seconds variable is of course reset again.
Here is the assignment from my teacher:
If Input is 0 (Closed):
The Red And The Green LED should be turned on at the same time for 15 seconds. After this the green LED should be turned
off completely, and the red LED should blink every fifth second for 10 minutes
If input is 1 (Opened):
The red LED should be turned off completely, and the green LED should be turned on for 10 minutes, and after that
it should be turned off too.
My timer is working fine. I have tested that. The program runs fine too and keeps the 2 LED's turned off for 15 seconds, then turns them off, but my red LED isn't blinking. I have been sitting at my desk all day desperately trying to find the error in my code.
Picture of the print:
Here is my C Code. I am using MPLab and the HI-TECH C compiler :)
#include <pic.h>
//Variabler
int B = 0; //Definerer variablen B, used as a flag
int A = 0; //Definerer veriablen A, used as a flag
int E = 0;
int savedstatus = 1; //Definere variablen savedstatus used to check last status for input
int millicounter = 0; //Variabel to calculate seconds
int sec = 0; //Variabel holding seconds gone
int count = 0; //For counting seconds passed, used in input0 subroutine
int onesec = 0; //Used to counting seconds for blinking LED in input0 subroutine
int scount = 0;
//Variabler slut
void interrupt jesper(void)
{
T0IF = 0x00;
TMR0 = 96;
millicounter++;
if(millicounter == 20)
{
sec++;
millicounter = 0;
}
}
//Subrutines
void input0()
{
if(sec<=15 && E==0)
{
PORTA = 0x21;
}
else if(A==0)
{
scount = 0;
sec = 0;
count = sec;
A = 1;
E = 1;
}
else if(sec<=600 && sec>count)
{
count++;
if((scount+5)>=count)
{
if(B==0)
{
onesec = sec;
B = 1;
PORTA = 0x01;
}
else if(sec>onesec)
{
PORTA = 0x00;
B = 0;
scount = count;
scount;
}
else
{
PORTA = 0x01;
}
}
else
{
PORTA = 0x00;
}
}
else PORTA = 0x00;
}
void input1()
{
if(sec<=600)
{
PORTA = 0x20;
}
else
{
PORTA = 0x00;
}
}
//Subrutines over
int main(void)
{
TRISA = 0x00; //Sets all A-PORTS to output
TRISB = 0x01; //Sets all PORTB to output with the exception of BIT0
TRISC = 0x00; //Sets All PORTC to output
ANSEL = 0x00; //Disable Analog ports
ANSELH = 0x00; //Disable Analog ports
//Timer Config
PSA = 0x00;
PS0 = 0x01;
PS1 = 0x01;
PS2 = 0x01;
TMR0 = 0x60;
GIE = 0x01;
T0IE = 0x01;
T0IF = 0x00;
T0CS = 0x00;
//Timer Config Over
while(0x01)
{
if(savedstatus != RB0)
{
savedstatus = RB0;
sec = 0;
E = 0;
A = 0;
}
if(savedstatus == 1)
{
input1();
}
else
{
input0();
}
}
}
I really hope that you can help me here :)
Here is my flowchart for the subroutine input0 where my problem is. Btw some of my variables have different names in the code itself, but it shouldn't be hard to see.
Your main() calls input0() as often as possible. When input0() is in the flashing state it uses the conditional sec > count. I suspect that your intention is that input0() should only change the LED state at intervals of a second. But then on the else side of that conditional you turn both LEDs off. This else side is executing many times because main() is calling input0() so often. Try deleting the else condition where you turn the LEDs off.
void input0()
{
<snip>
else if(sec<=600 && sec>count)
{
<snip>
}
else PORTA = 0x00; // <-- delete this line so you're not always turning the LED off
}
I am trying to make a servo controller that have a higher resolution than the ATtiny85 8-bit timer/counter. So far I have managed to get about 2000 positions on my servo (1µs/step) within a time frame of 21'000 µs. I have also managed to move 5 servos sequential and with different speed, but now I want to move them synchronous.
My biggest problem is that I don't get how I should make it happen! I have looked around on other servo codes including servo8bit library and tried to find a way. It seems that most of the examples uses compare match ISR to move the servos "at the same time", my problem is that I have a 16-bit integer that I want to compare.
Is there a way to do some magic so I can use the 8-bit compare match ISR with my 16-bit integer? Or does anyone of you have some other suggestions on how I can move my servos synchronous without using compare match ISR?
I hope my questions make sense!
Since I don't really have any code to show yet (only flawed attempts without compar match ISR that makes no sense) I post the link to my TinyServo code if it helps.
EDIT 1:
Here is the part of the code I mentioned and didn't post the first time:
void servoMove(void)
{
uint16_t nextPulse = hPulse[0];
timerSetup (); //16-bit setup for counter
for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
if ( (oTime > nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) ) //check if HIGH pulse (pos) is done
{
PORTB &= ~(1 << servo[channel]);
if (i+1 < sizeof(hPulse)/sizeof(hPulse[0]))
{
nextPulse += hPulse[i+1];
}
channel++;
}
else
{
channel = 0;
oTime = 0; //resets 16-bit variable
tot_overflow = 0; //resets tot_overflow variable
TIFR |= (1 << TOV1); // clear counter1 overflow-flag
TCNT1 = 0; //resets Timer/Counter1
}
}
for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
if ( (oTime > tPulse - nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) ) //check if LOW pulse (period) is done
{
PORTB |= (1 << servo[channel]);
nextPulse -= hPulse[i];
channel++;
}
}
}
void servoPosSet(volatile uint16_t pos[], uint8_t size)
{
for (i = 0; i < size; i++)
{
hPulse[i] = pos[i];
}
}
int main(void)
{
TCCR1 |= (1 << CS12); //set Timer/Counter1 prescaler to increment every 1 µs (PCK/8)
for (channel = 0; channel < size); channel++)
{
DDRB |= (1 << servo[channel]); //sets PB0-PB4 as output pins
}
channel = 0;
uint16_t pos[] = {2000, 1500, 1900, 1300, 1700};
uint8_t size = 5;
while(1)
{
servoPosSet(pos);
servoMove();
}
}
EDIT 2:
This is an illustration of how I think the code should work:
...but it does not!
If you have nothing else to do during the pulse, you could use a busy
loop instead of interrupts:
#include <avr/io.h>
#include <util/delay_basic.h>
/* Send a pulse of width = 4*count cycles. */
void pulse(uint16_t count, uint8_t channel)
{
uint8_t mask = 1 << channel,
old_port = PORTB,
high = old_port | mask,
low = old_port & ~mask;
PORTB = high;
_delay_loop_2(count);
PORTB = low;
}
This will give you a resolution of 4 clock cycles, or 0.5 µs with a
8 MHz clock.
Sending the pulses to the 5 servos should take at most 10 ms. Since
you repeat the pulse train every 21 ms, this leaves you 11 ms
to compute the next set of positions, which should be plenty. You could
program a timer to wake you up every 21 ms, then your main() may
look like:
int main(void)
{
static uint16_t pos[] = {4000, 3000, 3800, 2600, 3400};
uint8_t i;
/* Wake up every 21 ms. */
setup_timer();
sleep_enable();
for (;;) {
/* Update the servos. */
for (i = 0; i < 5; i++) pulse(pos[i], i);
/* Compute the next set of positions. */
...
/* Wait for timer interrupt. */
sleep_cpu();
}
}
I'm using the pic18F4550 with microchip v8.63 and with the C 18 compiler. I'm using a LDR that retrieve the value of the led (not on my picdem board) (red, green and blue) these values are stored in a variable after each conversion. Afther that when I press the button S2, I come into the method ISR: this part works.
But now: I try to compare the variable red, green and blue in the if's: but I think that it not happen, he just go to my 'else' (led RB3 on my picdem board burns).
#include <p18f4550.h>
/** V E C T O R R E M A P P I N G *******************************************/
extern void _startup (void); // See c018i.c in your C18 compiler dir
#pragma code _RESET_INTERRUPT_VECTOR = 0x001000
void _reset (void)
{
_asm goto _startup _endasm
}
#pragma code
void ISR (void);
#pragma code _HIGH_INTERRUPT_VECTOR = 0x001008
void _high_ISR (void)
{
_asm goto ISR _endasm
}
#pragma code _LOW_INTERRUPT_VECTOR = 0x001018
void _low_ISR (void)
{
;
}
#pragma code
/******************************************************************************/
// global variable, value off LDR.
unsigned int var1ADRESH = 0x00;
unsigned int color_red = 0;
unsigned int color_green = 0;
unsigned int color_blue = 0;
void main (void)
{
TRISD = 0x00; // PORTD als uitgang
RCONbits.IPEN = 0; // prioriteit uit
INTCONbits.GIE = 1; // enable interrupt
INTCONbits.RBIE = 1; // interrupt portB aan
//= set up port =
TRISAbits.TRISA0 = 1; // Set RA0/AN0 to input
//leds
TRISAbits.TRISA3 = 0;
TRISAbits.TRISA4 = 0;
TRISAbits.TRISA5 = 0;
LATAbits.LATA3 = 1;
LATAbits.LATA4 = 1;
LATAbits.LATA5 = 1;
ADCON0 = 0b00000000; // Set channel select to AN0
ADCON1 = 0b00001110; // Configure RA0/AN0 as analogue
ADCON2 = 0b10101010; // Right justified result
// TAD 12 and FOSC 32 - may need to adjust this
// depending on your clock frequency (see datasheet)
while(1)
{
_asm sleep _endasm
}
}
#pragma interrupt ISR
void ISR (void)
{
if (INTCONbits.RBIF==1) {
//conversie blauw
LATAbits.LATA3 = 0;
ADCON0bits.ADON = 1; // Enable ADC
// read LDR value.
ADCON0bits.GO = 1; // Set the GO bit of the ADCON0 register to start the conversion.
while (ADCON0bits.GO); // Wait until the conversion is complete.
ADCON2bits.ADFM = 0; // read result as 8-bit. (dus data in ADRESH) !
//= read data in ADRESH =
var1ADRESH = ADRESH; // reading value from LDR
color_blue = ADRESH; //waarde in blauw
//conversie rood
LATAbits.LATA3 = 1;
LATAbits.LATA4 = 0;
ADCON0bits.ADON = 1; // Enable ADC
// read LDR value.
ADCON0bits.GO = 1; // Set the GO bit of the ADCON0 register to start the conversion.
while (ADCON0bits.GO); // Wait until the conversion is complete.
ADCON2bits.ADFM = 0; // read result as 8-bit. (dus data in ADRESH) !
//= read data in ADRESH =
var1ADRESH = ADRESH; // reading value from LDR
color_red = ADRESH; //waarde in blauwe steken
//conversie groen
LATAbits.LATA4 = 1;
LATAbits.LATA5 = 0;
ADCON0bits.ADON = 1; // Enable ADC
// read LDR value.
ADCON0bits.GO = 1; // Set the GO bit of the ADCON0 register to start the conversion.
while (ADCON0bits.GO); // Wait until the conversion is complete.
ADCON2bits.ADFM = 0; // read result as 8-bit. (dus data in ADRESH) !
//= read data in ADRESH =
var1ADRESH = ADRESH; // reading value from LDR
color_green = ADRESH; //waarde in blauwe steken
// alles uitzetten
//PORTB = 0b1111111;
LATAbits.LATA5 = 1;
if(color_blue > color_red && color_blue > color_green){
//blauw
LATDbits.LATD0 = 1;
}
if(color_red > color_blue && color_red > color_green){
//rood
LATDbits.LATD1 = 1;
}
if(color_green > color_red && color_green > color_blue){
//groen
LATDbits.LATD2 = 1;
}
else {
LATDbits.LATD3 = 1;
}
}
INTCONbits.RBIF = 0;
}
Try to set all three of LATA3,4,5 before every readout?
Set TRIS for D0..2
I detect a few weird things (but I confess none might be the source).
Once you set the LATA bits, does the LDR immediately changes val? Of course not, but is it a matter of ns, us or ms?... I don't see any delays for LDR settling times.
You are always sleeping so it's probably required to set the ADON=1 (check DS). However, check the device DS for how long does the AD needs to get it's internals up and ready after setting ADON=1. On that subject, once ADON=1 you shouldn't and don't need to set it over again the the next two acquisitions.
The same thing about the format of the result. Set it once, and do it before the GO=1. Don't re-set every conversion.
If you're using only 8bit results, why the unsigned int? (btw, int is pretty dangerous in microcontrollers. Use short and chars and always explicit signdness).
Why the var1ADRESH assignment then color_xxx? when do the LATD bits eventually get =0?
Your If else only works on the last branch. I believe you intended a if{} else if{} else if... structure.