Поиск по этому блогу

20 янв. 2014 г.

О практическом использовании микроконтроллеров AVR

В настоящее время при построении систем управления различным технологическим оборудованием все чаще стали использовать микроконтроллеры. Причем наиболее распространенными из них являются микроконтроллеры фирмы ATMEL. В работе рассматривается один из подходов практического использования микроконтроллера ATmega8 для решения трех практических задач:
1.Определение температуры с использованием датчика DS18B20 и в зависимости от ее значения моделирование управлением оборудованием.
2.Измерение постоянного напряжения в диапазоне от 0 до 15 вольт.
3. Измерение времени (цифровые часы).
         Принципиальная схема устройства для решения перечисленных задач представлена на рис.1.
Рис.1
         Для отображения информации о времени, измеряемом напряжении, температуре, используется жидкокристаллический индикатор с числом столбцов и строк  16х2 соответственно (LCD 16x20), который поддерживает систему команд, совместимую с контроллером HD44780 (фирма Hitachi) и его аналогами, например, KS0066 (фирма Samsung), SED1278 (фирма Epson), ST7066 (фирма Sitronix). В качестве микроконтроллера выбран ATmega8-16 илиATmega8L-8. Последняя модель отличается меньшей тактовой частотой (8МГц) и меньшим энергопотреблением. Для измерения температуры используется цифровой датчик DS18B20 температуры с точностью до 0,0625 C (при разрешающей способности температурного преобразователя 12 бит) и диапазоном от -55 до +125 С. Датчик с микроконтроллером сопрягается по протоколу 1wire. Иначе 1wire – это однопроводной интерфейс, позволяющий подключить к одной линии данных практически неограниченное количество периферии. Каждое устройство имеет уникальный 64-х битный идентификационный номер.
         При включении устройство на индикаторе отображает в течение 5 секунд свое назначение и далее в бесконечном цикле отображает время, постоянное напряжение любого подключенного источника питания в пределах от 0 до 15 вольт и температуру. Если температура превысит 32 градуса C (по представленной программе), загорается  светодиод (моделирование управлением оборудования). Если температура опускается ниже 32 градусов, светодиод гаснет (оборудование отключается).
         После изготовления устройства по схеме на рис.1. в микроконтроллер должна быть загружена программа с помощью программатора. В качестве программатора  был использован простой, но надежно работающий адаптер для COM порта (рис.2) и программа PonyProg ( http://www.lancos.com/prog.html )
 
Рис.2
         Программирование устройства (рис.1) выполнялось на языке Си. И если учесть, что в архитектуру AVR изначально были заложены принципы оптимизации Си-процедур, то альтернативы этому алгоритмическому языку нет.
         Для программирования микроконтроллера ATMega8 воспользуемся компилятором языка Си CodeVision AVR фирмы  HP InfoTech, Румыния (http://www.hpinfotech.ro/html/cvavr.htm).  Он популярен тем, что включает в себя
- Кросс-компилятор Си, обеспечивающий выполнение почти всех элементов языка Си, разрешенных архитектурой AVR;
- Интегрированную среду разработки, в состав которой входит программатор чипов AVR для автоматической пересылки кодов после компиляции в микроконтроллерный чип через адаптер, а также набор библиотек Си для работы с LCD модулями, температурными датчиками,  часами реального времени и т.д.;
- Автоматический генератор программ CodeWizardAVR, позволяющий написать за несколько минут весь код необходимый для выполнения таких функций, как инициализация таймеров/счетчиков, инициализация LCD, инициализация АЦП и др.
         Рассмотрим последовательно этапы программирования микроконтроллера для решения представленной выше задачи. Предположим, что пакет CodeVision AVR установлен на компьютере. В работе в качестве примера используется CodeVision AVR Version  1.25.8 Professional. Перед запуском программы CodeVision AVR создаем каталог, где будут храниться файлы.
1. Запускаем CodeVision AVR и выбераем пункт CodeWizardAVR:
2.Выбираем тип микроконтроллера и частоту:

3.Выбираем LCD и подключаем его к порту D:

Видно, что автоматически устанавливается соответствие между подключаемыми битами микроконтроллера и выводами LCD модуля.
4.Инициализируем шину 1Wire и температурный датчик DS1820, хотя по заданию используется датчик DS18B20. Это делается для подсказки вызова необходимых библиотек. Впоследствии в программе будут произведены необходимые замены. Датчик устанавливаем на 3-й бит порта C:

5.Заходим во вкладку Timers. Выбираем для ATMegaTimer 1:

В качестве источника тактовой частоты (Clock Source) выбираем System Clock, т.е. таймер будет тактироваться с частотой, на которой работает микроконтроллер (8MHz). Выберем тактовую частоту для таймера-счетчика (ClockValue) равную 7,813kHz. Она получается делением частоты работы микроконтроллера на коэффициент деления(1024). Включаем прерывание (Interrupt on), которое должно произойти, если в регистре A  появиться  значение, указанное в поле Comp.A. Поле Value  определяет начальное значение таймера-счетчика. Таким образом, наш таймер должен «тикнуть» 7813 раз, чтобы прошла ровно 1 секунда, если кварцевый резонатор идеально настроен на частоту 8 MHz. Однако при делении 8 MHz на делитель частоты 1024 получается 7.8125kHz. Поэтому в поле Comp.A необходимо поставить или 7812 или 7813 тактов (1e84 или 1e85 для шестнадцатеричной системы исчисления). Однако, как показал опыт с практически используемым кварцевым резонатором на 8 MHz, значение 7811 тактов оказалось наилучшим, что привело к опережению времени только на 1 секунду в сутки. Эта величина и установлена в программе после практической корректировки. Для получения наибольшей точности часов можно программно через каждые 24 часа от полученного времени отнимать 1 секунду.
6.Заходим последовательно в вкладки File -> GenerateSave and Exit:
Даем имена проекту и исходному файлу. В результате появится окно с текстом программы на языке Си:

7. Корректируем программу в окне текстом программы, который представлен ниже:
#include
#include
#include
#include
// Вставка ассемблерного кода (1 wire)
#asm
   .equ __w1_port=0x15 ;PORTC
   .equ __w1_bit=3
#endasm
#include <1wire .h="">
// Вставка ассемблерного кода (LCD)
#asm
   .equ __lcd_port=0x12 ;PORTD
#endasm
#include
#include // подключаем библиотеку задержки
#define VREF 5000 // Определяем опорное напряжение в миливольтах
unsigned int s; // переменная сек.
unsigned int m; // пересенная мин.
unsigned int h; // переменная часов
// Вызов функции обработки прерывания Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
// Увеличиваем секунду на 1 в случае появления в регистре A числа 7813(изм. на 7811)
 TCNT1H=0;
 TCNT1L=0;
  s++;
}
void main(void)
{
// Declare your local variables here
int temp,temp3;       
float temp1,temp2,volt1;
unsigned long volt;
char lcd_volt[8],znak[2],lcd_buffer[16];
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0xFF;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x03;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
TCCR0=0x00;
TCNT0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 7,813 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x05;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x1E; //  Частота работы таймера
OCR1AL=0x84; // Скорректирована на 7812 (1E84)
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x10;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
// LCD module initialization
lcd_init(16);    // Инициализация LCD
// Глобальное разрешение прерываний
#asm("sei")  
w1_init(); // Инициализация линии 1 wire    
lcd_gotoxy(0,0);   // Позиционируем вывод на начало 1-й строки
lcd_putsf(" Time  Voltmetr"); // Выполняем вывод
lcd_gotoxy(0,1); // Позиционируем на начало 2-й строки   
lcd_putsf(" Termoregulator");  // Выполняем вывод
 DDRB.0=1;      // Переводим в режим вывода 0-й бит порта B
 PORTB.0=1;     // Устанавливаем на выходе 0 порта B логическую единицу
 delay_ms(5000); // Удерживаем вывод в течении 5 секунд
 lcd_clear();   // Очищаем экран LCD
  m=0;  // Обнуляем минуты
  h=0;  // и часы
  // Устанавливаем режим работы АЦП
  ADMUX &= 0xDF &  0x7F & 0xF2; ADMUX |= 0x40 | 0x02; // Разрядность 10бит, опорное напряжение AVCC Вольт,
  // внутренний источник опорного напряжения, линия PC2
  // ADCSRA &= 0xFE;
  ADCSRA |= 0x80 | 0x40 | 0x20 | 0x07; // Включить АЦП, запуск нового замера, постоянное измерение,
  //частота АЦП=8.0МГц/128
while (1) // Запуск бесконечного цикла измерений
  {    
           if (PINC.0==0) // если нажата первая кнопка (порт C бит 0)
           {
               m++; // к значению минуты добавляем единицу
           }
           if (PINC.1==0) // если нажата вторая кнопка  (порт C бит 1)
          {
              h++; // к значению часов добавляем единицу
           }
  // Условия часов.
            if(s==60) // если сек = 60
           {
              m++; // добавляем 1 к переменной "минута"
              s=0; // зануляем переменную "секунда"
           }
            if(m==60) // если мин = 60
            {
               h++; // добавляем 1 к переменной "час"
               m=0; // зануляем переменную "минута"
             }
      if (h==24) // так как у нас часы имеют 24 часовый формат
       { // при достыжении 24 часов, зануляем все переменные.
         h=0;
         m=0;
         s=0;
        }
  // Выводим переменные (время)
  lcd_gotoxy(0,0);
  lcd_putchar(h/10+0x30);
  lcd_putchar(h%10+0x30);
  lcd_putchar(':');
  lcd_putchar(m/10+0x30);
  lcd_putchar(m%10+0x30);
  lcd_putchar(':');
  lcd_putchar(s/10+0x30);
  lcd_putchar(s%10+0x30);
 // Определяем напряжение     
  volt=ADCL;
  volt += ((int)ADCH << 8);
  volt=volt*VREF/1024;
  volt1=volt/1000.0;
  volt1=3.224*volt1; // Вычисляем напряжение в соответсвии с выбранным делителем напряжения
  // Выводим напряжение
    sprintf(lcd_volt," U=%5.2f",volt1);
          lcd_puts(lcd_volt);              
 // Определяем и выводим температуру               
        temp1=ds18b20_temperature(0);  // Функция определения температуры для одног датчика 
         sprintf(znak,"+");
         if (temp1 < 0.0 ){ // Определяем знаки температуры
         temp1=-temp1;      
         sprintf(znak,"-");
         }
    // Формируем дробное значение температура при работе с целыми числами (это полезно
    // в целях экономии памяти)   
          temp=temp1;       
          temp2=temp1-temp; 
          temp3=100*temp2;       
          if (temp>=32){ // Если температура больше или равна 32 град.
            PORTB.0=0;   // Выставит ноль на нулевом выходе порта B (включится светодиод)
            } 
         if (temp<32 32="" nbsp="" o:p="">
         PORTB.0=1; // Выставит единицу на нулевом выходе порта B (выключится светодиод)
            }
        // Вывод температуры            
         lcd_gotoxy(0,1);
         sprintf(lcd_buffer," temp = %s%i.%i",znak,temp,temp3);                        
         lcd_gotoxy(0,1);
         lcd_puts(lcd_buffer);               
  }; // Закрыть бесконечный цикл     
}  // Закрыть программу main()
8. Выбираем закладку Project->Configure->C Compile и устанавливаем в поле (s)printf Features значения float,width,precision

9.Компилируем проект (команда Compile):
10. Собираем проект командой Make:
В каталоге, где расположена программы на Си, появится файл с расширением .hex, который необходимо будет переслать в микроконтроллер с помощью адаптера и программатора. Для этой цели воспользуемся программой-программатором PonyProg, которая должна быть предварительно установлена на компьютере со свободным COM портом. После установки PonyProg должна быть выполнена процедура ее калибровки. Для этого необходимо зайти в закладки Setup->Calibration  и нажать на Yes.
Рассмотрим следующие этапы использования программы PonyProg.
1.Запускаем программу и настраиваем интерфейс связи между компьютером и  микроконтроллером:
Указываем использование адаптера для связи с COM портом:
2.Открываем файл с расширением .hex
3.После загрузки файла заходим в закладку Command->Security and Configuration Bits и устанавливаем  фьюз-биты (биты конфигурации). Ниже на рисунке представлена заводская конфигурация фьюз-битов для ATMega8. Для нашей конфигурации необходимо снять все галочки и нажать на Write.
4.Выполняем передачу файла микроконтроллеру по команде Ctrl-P. Или можно зайти во вкладку Command и курсором мышки выбрать Program.  В случае успешной передачи программы в микроконтроллер должно появиться окно с надписью Program succesful. При программировании необходимо не забыть на устройство подать напряжение (10…15В). После ввода программы устройство должно сразу же заработать.
Выводы.
1.Показан на простом примере возможный способ управления с помощью  распространенных и недорогих микроконтроллеров технологическим оборудованием на основе таймера, температуры, напряжения.
2.На основании используемого пакета  CodeVision AVR показано, как просто и прозрачно на языке Си создается программное обеспечение для функционирования микроконтроллера фирмы Atmel.
3.Приведена схема простейшего надежно работающего адаптера и последовательность работы с программатором для пересылки откомпилированной программы в микроконтроллер.
Литература.
1. Рюмик С.М. Микроконтроллеры AVR. Ступень 1. http://forum.radiospec.ru/index.php?showtopic=5612 - 2007