Вы вошли на сайт, как Гость
Регистрация

Контроль скорости вращения вентилятора

Программные примеры для микроконтроллеров 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) ;
}

Файлы проекта можно скачать здесь: