Файл: C Programming for microcontrollers (Joe Pardue, 2005).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.06.2025
Просмотров: 2471
Скачиваний: 2
Chapter 7: Microcontroller Interrupts and Timers
// Demonstrator.c Motor Speed Control version
#include "PC_Comm.h" #include "Demonstrator.h"
#define PINB_MASK ((1<<PINB4)|(1<<PINB6)|(1<<PINB7)) #define PINE_MASK ((1<<PINE2)|(1<<PINE3))
unsigned char milliseconds = 0;
unsigned int second = 0; // count to 1000 and trigger one second event
unsigned int speed = 0; // IR detector count per second unsigned int lastspeed = 0; // IR detector count per second
void initializer()
{
//Calibrate the oscillator: OSCCAL_calibration();
//Initialize the USART USARTinit();
//Set for pin change on PINB0 PCMSK0 = (1 << PINB0); //
EIFR = (1 << 7); // flag for PCINT15-8 EIMSK = (1 << 7); // mask for PCINT15-8
DDRB = 0X00; // set PORTB for input
PORTB = 0xFF; // enable pullup on for input
// set PORTD for output //DDRD = 0xFF;
DDRD = (1 << PIND0); // set pin 0 to output
PORTD = (1 << PIND0); // set pin 0 to enable pullup milliSecInit(127); // 50% duty cycle 1kHz signal
// say hello
sendString("\rPC_Comm.c ready to communicate.\r"); // identify yourself specifically
sendString("You are talking to the Motor Speed Control demo.\r");
sendString("setxxx to set speed\r");
}
140
Chapter 7: Microcontroller Interrupts and Timers
void parseInput(char s[])
{
// parse first character switch (s[0])
{
case 's':
if( (s[1] == 'e') && (s[2] == 't')) parse_set(s);
break; case 'd':
if( (s[1] == 'e') && (s[2] == 'm') && (s[3] == 'o') && (s[4] == '?') )
sendString("You are talking to the Motor Speed Control
demo.\r");
break;
default:
sendString("\rYou sent: '"); sendChar(s[0]);
sendString("' - I don't understand.\r"); break;
}
s[0] = '\0';
}
int parse_set(char s[])
{
char set[11];
unsigned char i = 3, j = 0;
while( (s[i] != '\0') && (j <= 11) )
{
if( (s[i] >= '0') && (s[i] <= '9') )
{
set[j++] = s[i++];
}
else
{
sendString("Error - Parse_set received a non integer: ");
sendChar(s[i]);
sendChar('\r'); return 0;
}
}
141
Chapter 7: Microcontroller Interrupts and Timers
set[j] = '\0';
if(j>4)// must be < 256
{
sendString("Error - Parse_set number too large"); return 0;
}
else
{
set_speed(atoi(set));
}
return 1;
}
void set_speed(int count)
{
char speed[11];
sendString("Setting the Compare Timer Count to: "); itoa(count,speed,10);
sendString(speed);
sendChar('\r');
milliSecInit(count);
}
/*
The USART init set the system oscillator to 2 mHz. We set the Timer0 prescaler
to clk/8 which gives a 250 kHz input to the timer/counter. A compare of 250 throws
an interrupt every millisecond. */
void milliSecInit(unsigned char count)
{
// Enable timer0 compare interrupt TIMSK0 = (1<<OCIE0A);
//Sets the compare value setOCR0A(count);
//Set PWM Phase Correct mode, CLK/8 prescaler
142
Chapter 7: Microcontroller Interrupts and Timers
TCCR0A = (0<<FOC0A)|(0<<WGM01)|(1<<WGM00)|(1<<CS01);
}
void setOCR0A(unsigned char count)
{
// Sets the compare value OCR0A = count;
}
// Interrupt occurs twice per Millisec, timed for PWM SIGNAL(SIG_OUTPUT_COMPARE0)
{
// Toggle PORtD pin 0 if(PORTD &= 1) cbi(PORTD, 0); else sbi(PORTD, 0);
}
143
Chapter 7: Microcontroller Interrupts and Timers
Speedometer
We used an optoisolator to separate the motor power circuits from the Butterfly to help lessen the likelihood of blowing something up. A device similar to an optoisolator is an optointerrupter, which has an air channel between the IR light emitting diode and the IR detector transistor, see Figure 23. An opaque object passed between the diode and the detector causes the transistor to turn off thus ‘interrupting’ the current. We can tie the transistor to a pin on the Butterfly and detect the interruption. Did you notice the opening cut in the wheel in Figure 22? (when you cut out the slot, glue it just under the inner side of the slot to help keep the wheel balanced) If you rig up the motor base so that the wheel spins thru the slot in the optointerrupter, each time the opening passes; the transistor turns on and back off when the slot has passed. If we write our software so that a voltage change on the pin attached to the optointerrupter causes an interrupt in the Butterfly, we can count those interrupts. If we count for exactly one second we have the number of times the wheel rotates per second, which is the rotational speed in Hz. Cool!
Solder long wires to the optoisolator, and then add electrical tape to prevent the legs from shorting. Next carefully glue it to the motor base in a position so that the wheel rotates thru it. Make sure the wheel is balanced and will turn cleanly (easier said than done) and fully block and unblock the optoisolator slot as the wheel turns, Figure 22.
144
Chapter 7: Microcontroller Interrupts and Timers
Figure 23: Opto Interrupt Switch - H21A1
Figure 24: Opto Interrupter Glued on Motor Base
Wiring:
•Optoisolator pin 1 to +3v
•Optoisolator pin 2 to a 200 Ohm resistor
145
Chapter 7: Microcontroller Interrupts and Timers
•200 Ohm resistor to Butterfly GND
•Optoisolator pin 3 to PORTB pin 4 (remember counting starts at 0)
•Optoisolator pin 4 to Butterfly GND
You will notice that you learned the mechanical engineering skills needed for this project in kindergarten. Though most kindergarteners could probably do a more attractive job than I did, it works.
Figure 25: Speedometer
Create a Speedometer directory and copy the motor control software to it. Make the following changes in Demonstrator.c:
// Demonstrator.c Speedometer version
#include "PC_Comm.h" #include "Demonstrator.h"
146
Chapter 7: Microcontroller Interrupts and Timers
unsigned char milliseconds = 0;
unsigned int second = 0;//count to 1000 and trigger one second event
unsigned int speed = 0; // IR detector count per second unsigned int lastspeed = 0; // IR detector count per second
void initializer()
{
//Calibrate the oscillator: OSCCAL_calibration();
//Initialize the USART USARTinit();
//Init port pins
DDRB |= 0x08;
PORTB |= ((1<<PINB4));//|(1<<PINB6)|(1<<PINB7));
// Enable pin change interrupt on PORTB
PCMSK1 = ((1<<PINB4));//|(1<<PINB6)|(1<<PINB7)); EIFR = (1<<6)|(1<<7);
EIMSK = (1<<6)|(1<<7);
DDRD = 0xFF; // set PORTD for output
PORTD = 0XFF; // set LEDs off
milliSecInit(127); // 50% duty cycle 1kHz signal
// say hello
sendString("\rPC_Comm.c ready to communicate.\r"); // identify yourself specifically
sendString("You are talking to the Speedometer demo.\r"); sendString("'setxxx' to set speed\r'Hz' to get speed in
Hertz\r");
}
void parseInput(char s[])
{
// parse first character switch (s[0])
{
case 's':
if( (s[1] == 'e') && (s[2] == 't')) parse_set(s);
147
Chapter 7: Microcontroller Interrupts and Timers
break; case 'H':
if( (s[1] == 'z')) sendSpeed(); break;
case 'd':
if( (s[1] == 'e') && (s[2] == 'm') && (s[3] == 'o') && (s[4] == '?') )
sendString("You are talking to the Speedometer demo.\r");
break;
default:
sendString("\rYou sent: '"); sendChar(s[0]);
sendString("' - I don't understand.\r"); break;
}
s[0] = '\0';
}
void sendSpeed()
{
char spd[11];
sendString("Speed = "); itoa(lastspeed,spd,10); sendString(spd); sendChar('\r');
}
int parse_set(char s[])
{
char set[11];
unsigned char i = 3, j = 0;
while( (s[i] != '\0') && (j <= 11) )
{
if( (s[i] >= '0') && (s[i] <= '9') )
{
set[j++] = s[i++];
}
else
{
148
Chapter 7: Microcontroller Interrupts and Timers
sendString("Error - Parse_set received a
non integer: ");
sendChar(s[i]);
sendChar('\r'); return 0;
}
}
set[j] = '\0';
if(j>4)// must be < 256
{
sendString("Error - Parse_set number too large\r"); return 0;
}
else
{
set_speed(atoi(set));
}
return 1;
}
void set_speed(int count)
{
char speed[11];
sendString("Setting the Compare Timer Count to: "); itoa(count,speed,10);
sendString(speed);
sendChar('\r');
milliSecInit(count);
}
/*
The USART init set the system oscillator to 2 mHz. We set the Timer0 prescaler to clk/8 which gives a 250 kHz input to the timer/counter. A compare of 250 throws an interrupt every millisecond.
*/
void milliSecInit(unsigned char count)
{
// Enable timer0 compare interrupt TIMSK0 = (1<<OCIE0A);
149
Chapter 7: Microcontroller Interrupts and Timers
//Sets the compare value setOCR0A(count);
//Set PWM Phase Correct mode, CLK/8 prescaler TCCR0A = (0<<FOC0A)|(0<<WGM01)|(1<<WGM00)|(1<<CS01);
}
void setOCR0A(unsigned char count)
{
// Sets the compare value OCR0A = count;
}
// Interrupt occurs twice per Millisec, timed for PWM SIGNAL(SIG_OUTPUT_COMPARE0)
{
//Toggle PORTD pin 0 if(PORTD &= 1) cbi(PORTD, 0); else sbi(PORTD, 0);
//get the speed count once per second if(second++ >= 1000)
{
second = 0;
lastspeed = speed; // store most recent speed in Hz
speed = 0;
}
}
SIGNAL(SIG_PIN_CHANGE1)
{
speed++;
}
We’ve made a couple of simple changes. We reused the pin interrupt code from the joystick software and in the interrupt routine we increment a speed counter variable. Once per second we copy the speed counter value to ‘lastspeed’ variable, which we report as the speed in Hz when requested.
Compile and load, remembering to reset the AVRStudio programming tool to use the correct PC_Comm.hex, which I forgot AGAIN! Open HyperTerminal, toggle
150
Chapter 7: Microcontroller Interrupts and Timers
the power to the Butterfly, move the joystick to the up position for a moment, and you should see something like the following:
Play with it for a while and you’ll see that this isn’t particularly accurate. But what do you expect for cardboard and glue?
151