|
Контроль скорости вращения вентилятора
Программные примеры для микроконтроллеров AVR - AVRStudio
(Контроль скорости вращения вентилятора с учетом
показаний датчика температуры)
Самый простой способ замера температуры — с помощью термистора (терморезистора).
Такие устройства выпускаются для широкого диапазона температур
и, при правильном подборе, не требуют специального усиления или предварительной
обработки сигналов.
Существует также и множество других температурных сенсоров, включая биметаллические
термопары и резистивные температурные устройства (RTD, Resistive
Temperature Device). Стоит также упомянуть о специальных микросхемах, наподобие
LM35 от компании National Semiconductor, — калиброванные, стабильные
в измерениях устройства, выдающие определенный уровень напряжения на
градус Цельсия. Например, для LM35 выходное значение составляет 10 мВ/°С.
Тем не менее, несмотря на все свои достоинства, подобные устройства более дорогостоящие
и требуют дополнительных схем.
Для опроса температуры в нашей программе выберем
термистор с отрицательным температурным коэффициентом
(NTC, Negative Temperature Coefficient).
Поскольку термистор выдает значения в виде процентов
сопротивления на градусы Цельсия, для измерения
температуры можно воспользоваться резистивным делителем.
Диапазон выходных напряжений делителя
также можно приспособить к диапазону АЦП, обеспечив
таким образом наилучшее разрешение.
В микроконтроллерах AVR ко входу АЦП обычно
подключают цепи с небольшим выходным импедансом
(около 5 кОм или ниже). Выберем резистор на 4,7 кОм
для формирования делителя с термистором 2 кОм
(приблизительное сопротивление при 25°С) (рис. 4.4).
Выбранный термистор изменяет величину на 3,83% на каждый 1°С. Это обеспечивает
диапазон напряжения в АЦП от 0,84 В при -40°С до 4,5 В при 61°С.
Термистор размещен в верхнем плече делителя, поскольку его отрицательный импеданс
изменяется с температурой. В результате с повышением температуры будет
повышаться и напряжение на входе АЦП.
Разрешение АЦП составляет 10 разрядов,
В нашем примере в качестве аналогового входа, к которому подключен выход
резистивного делителя с термистором, будем использовать вывод 3 порта А. Для
приведения в действие вентилятора охлаждения воспользуемся, к примеру, электродвигателем
на 9 В.
Исходя из этой схемы, управлять скоростью вращения можно в режиме ШИМ
Т/С1. Напомним, что в этом режиме Т/С1 работает как суммирующий и вычитающий
счетчик, осуществляя циклические переходы от 0x000 0 к максимальному
значению и затем снова возвращаясь к 0x0000. Когда значение счетного регистра
совпадает со значением регистра OCR1A, то на выводе микроконтроллера ОС1А
устанавливается высокий или низкий уровень сигнала (определяется состоянием
разрядов СОМ1А1 и СОМ 1 АО регистра TCCR1A). Таким образом, чем меньше
значение хранится в регистре сравнения OCR1A, тем реже поступают управляющие
импульсы, включающие двигатель. Примем, что температуре 30 С и ниже
двигатель вообще отключен (OCR1A = 0), а при температуре 60 С — вращается
с максимальной скоростью (OCR1A = 1022). Отсюда, формула для определения
текущего значения регистра OCR1А в зависимости от температуры имеет вид:
OCR1 А = (t - 30) • 1022 / 30°С.
Данные о текущей температуре и относительной скорости вращения вентилятора
будут отображены на ЖК-дисплее.
Примечание: при компиляции проекта требуется добавить в проект следующие библиотеки: "avr/io.h","avr/interrupt.h","avr/signal.h"
,"string.h","avr/delay.h"
#define RD_1 PORTB |= 4
#define RS_1 PORTB |= 2
#define E_1 PORTB |= 1
#define RD_0 PORTB &= 3
#define RS_0 PORTB &= 5
#define E_0 PORTB &= 6
const unsigned char addLUT[4] = {0x80, 0xC0, 0x94, 0xD4};
unsigned char LCD_Address, LCD_Line;
char buffer[15];
void WriteNibble(unsigned char data)
{
RD_0;
E_1;
PORTC = (data & 0x0F);
E_0;
RD_1;
_delay_loop_2(3000);
}
void WriteByte(unsigned char data)
{
RD_0;
E_1;
PORTC = (data >> 4);
E_0;
E_1;
PORTC = (data & 0xF);
E_0;
RD_1;
_delay_loop_2(3000);
}
void GoToLine(char LineNum)
{
RS_0;
LCD_Address = addLUT[LineNum-1];
WriteByte(LCD_Address);
RS_1;
LCD_Address = 0;
LCD_Line = LineNum;
}
void SetLCDPosition(char row, char col)
{
RS_0;
LCD_Address = addLUT[row-1] + col;
WriteByte(LCD_Address);
RS_1;
LCD_Line = row;
}
void ShowChar(unsigned char c)
{
RS_1;
WriteByte(c);
LCD_Address++;
switch (LCD_Address)
{
case 20: GoToLine(2); break;
case 40: GoToLine(3); break;
case 60: GoToLine(4); break;
case 80: GoToLine(1); break;
}
}
void ShowStr(unsigned char *s)
{
while (*s != 0) ShowChar(*s++);
}
void InitLCD(void)
{
unsigned char title1[] = "Temperature: ";
unsigned char title2[] = "Motor velocity: ";
RD_1;
E_0;
RS_0;
_delay_loop_2(50000);
WriteNibble(0x33);
WriteNibble(0x33);
WriteNibble(0x33);
WriteNibble(0x22);
WriteByte(0x28);
WriteByte(0x01);
WriteByte(0x10);
WriteByte(0x06);
WriteByte(0x0C);
for (char i=0x40; i<0x5F; i++)
{
_delay_loop_2(10000);
RS_0;
WriteByte(i);
_delay_loop_2(10000);
ShowChar(0);
}
RS_1;
SetLCDPosition(1, 4);
ShowStr(title1);
SetLCDPosition(3, 2);
ShowStr(title2);
}
INTERRUPT(SIG_ADC)
{
unsigned char CurrentTemp, CurrentVel;
CurrentTemp = ((long)101 * (long)ADCW) / (long) 1023 - (long)40;
CurrentVel = (CurrentTemp - 30) * 1022 / 30;
sprintf(buffer, "%02d C", CurrentTemp);
SetLCDPosition(2, 8);
ShowStr(buffer);
sprintf(buffer, "%04d", CurrentVel);
SetLCDPosition(4, 8);
ShowStr(buffer);
OCR1A = CurrentVel;
}
void main (void)
{
DDRA = 0;
DDRB = 0x07;
DDRC = 0x0F;
DDRD = _BV(PD5);
TIMSK = _BV(TOIE1);
TCCR1A = _BV(PWM10) | _BV(PWM11) | _BV(COM1A1);
TCCR1B = _BV(CS10);
TCNT1 = 0;
OCR1A = 0;
ACSR = _BV(ACD);
ADMUX = 3;
ADCSR = _BV(ADEN) | _BV(ADSC) | _BV(ADFR) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1);
InitLCD();
sei();
while(1) ;
}
|
Файлы проекта можно скачать здесь:
|
|
|