Современные информационные технологии/Компьютерная инженерия

 

Мясищев А.А.

Хмельницкий национальный университет, Украина

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

Для построения охранно-информационных систем широко используются микроконтроллеры, например фирмы AVR. Предположим, что необходимо практически реализовать устройство, которое информирует по мобильному телефону о прекращении подачи электроэнергии в какое-то подразделение, о срабатывании датчика, изменении каких либо параметров (температуры) и т.д. Причем дозвон должен выполняться с помощью модема, подключенного к городской телефонной линии связи. Микроконтроллер при срабатывании датчиков в этом случае  посылает на модем AT – команды для выполнения дозвона по заданному номеру мобильного телефона. Посылка данных от микроконтроллера должна выполняться через интерфейс RS232, который реализован в модеме. Таким образом, спроектированное устройство должно выполнять дозвон для следующих событий: 

1.Срабатывание реле, замыкающего контакты, например, при пропадании напряжения в осветительной сети (один звонок на мобильный телефон).

2.Превышение температуры в помещении 30 градусов (два звонка).

3.Время на микроконтроллере соответствует 10 часам дня (один звонок).

Вывод времени и температуры, должно выполняться на индикатор LCD 16х2. В качестве микроконтроллера используется ATmega16. Частота кварцевого резонатора, тактирующего работу микроконтроллера выбрана равной  7,3728 MHz. В работе рассматривается использование отладочного  комплекса AVREASY для построения заданного устройства [1]. В качестве датчика температуры используется  микросхема DS18B20 фирмы DALLAS

На рис.1 представлена схема устройства, собранная на отладочном комплексе AVREASY.

Рис.1

Программа, по которой работает микроконтроллер, написана на C,  откомпилирована для ATmega16 с помощью компилятора CodeVision AVR фирмы  HP InfoTech, Румыния (http://www.hpinfotech.ro/html/cvavr.htm).  Он популярен тем, что включает в себя:

- Кросс-компилятор С, обеспечивающий выполнение почти всех элементов языка Си, разрешенных архитектурой AVR;

- Интегрированную среду разработки, в состав которой входит программатор чипов AVR для автоматической пересылки кодов после компиляции в микроконтроллерный чип через адаптер, а также набор библиотек языка C для работы с LCD модулями, температурными датчиками,  часами реального времени и т.д.;

- Автоматический генератор программ CodeWizardAVR, позволяющий написать за несколько минут весь код, необходимый для выполнения таких функций, как инициализация таймеров/счетчиков, инициализация LCD, инициализация АЦП и др.

Рассмотрим этапы работы с компилятором.

1. Запускаем CodeVision AVR и выбираем пункт CodeWizardAVR:

2.Выбираем тип микроконтроллера и частоту (скриншот с ATmega8 представлен как пример, в действительности выбирается ATmega16):

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

Видно, что автоматически устанавливается соответствие между подключаемыми битами микроконтроллера и выводами LCD модуля.

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

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

В качестве источника тактовой частоты (Clock Source) выбираем System Clock, т.е. таймер будет тактироваться с частотой, на которой работает микроконтроллер (7,3728MHz). Выберем тактовую частоту для таймера-счетчика (Clock Value) равную 7,200kHz. Она получается делением частоты работы микроконтроллера на коэффициент деления(1024). Включаем прерывание (Interrupt on), которое должно произойти, если в регистре A  появиться  значение, указанное в поле Comp.A. Поле Value  определяет начальное значение таймера-счетчика. Таким образом, наш таймер должен «тикнуть» 7200 раз, чтобы прошла ровно 1 секунда, если кварцевый резонатор идеально настроен на частоту 7,3728 MHz. Исходя из практических соображений для увеличения точности работы часов в поле Comp.A поставим 7199 тактов (1c1f для шестнадцатеричной системы исчисления). В этом случае  опережение времени будет только на 2 секунды в сутки.

6.Выбираем вкладку USART. Устанавливаем режимы его работы, как показано ниже:

 

7.Заходим последовательно во вкладки File -> Generate, Save and Exit:

 Поменяв все на то, что имеем на реальной схеме (рис.1), получим следующую программу:

/*****************************************************

Охранно - информационное устройство.

Работает совместно с модемом по телефонной линии связи.

Выполняет дозвон для событий:

1.Срабатывание концевого выключателя, например срабатывание реле при отключении напряжения в сети (один звонок)

2.Превышение температуры в помещении 30 градусов (два звонка)

3.Время на микроконтроллере соответствует 10 часам дня (один звонок)

Температура, время - на LCD.

Chip type           : ATmega16

Clock frequency     : 7,372800 MHz

*****************************************************/

#include <mega16.h>

#include <1wire.h>

#include <ds18b20.h>

#include <delay.h>

#include <stdio.h>

#define TIME 40

// Вставка ассемблерного кода для снятия показаний

// с датчика DS18B20

#asm

   .equ __w1_port=0x12; PORTD

   .equ __w1_bit=6

#endasm   

unsigned int s=0;

unsigned int m=0;

unsigned int h=0;

unsigned int temp=0;

//-----------Функция задержки времени --

void pause(unsigned int a) // a-длительность паузы

{ unsigned int cn;         // cn-счетчик времени

 for (cn=a;cn>0;cn--);     // Цикл задержки времени

 }

//-----------Функция записи команды LCD--

void lcd_com(unsigned char p) //  p-байт команды

{

PORTC.2=0;  // Сигнал RS=0 (Запись команд)

PORTC.3=1;  // Сигнал EN=1

PORTC &= 0x0F; PORTC |= (p & 0xF0); // Установка старшей части байта

pause(TIME);  // Длительность сигнала EN

PORTC.3=0;    // Сигнал EN=0 - запись старшей части байта в LCD

pause(TIME);  // Длительность сигнала EN

PORTC.3=1;    // Сигнал EN=1

PORTC &= 0x0F; PORTC |= (p << 4); // Установка младшей части байта

pause(TIME);  // Длительность сигнала EN

PORTC.3=0;  // Сигнал EN=0 - запись младшей части байта в LCD

pause(5 * TIME); // Пауза для выполнения команды

}

//--------Функция записи данных в LCD---

void lcd_dat(unsigned char p) //  p-байт данных

{

PORTC.2=1;  // Сигнал RS=1(Запись данных)

PORTC.3=1;  // Сигнал EN=1

PORTC &= 0x0F; PORTC |= (p & 0xF0); // Установка старшей части байта

pause(TIME); // Длительность сигнала EN

PORTC.3=0;  // Сигнал EN=0 - запись старшей части байта в LCD

pause(TIME);  // Длительность сигнала EN

PORTC.3=1;    // Сигнал EN=1

PORTC &= 0x0F; PORTC |= (p << 4); // Установка младшей части байта

pause(TIME);  // Длительность сигнала EN

PORTC.3=0;  // Сигнал EN=0 - запись младшей части байта в LCD

pause(5 * TIME); // Пауза для выполнения команды

}

//---------Функция инициализации LCD-----

void lcd_init(void)

{

lcd_com(0x30);delay_ms(5); // Инициализация по DATASHEET на HD44780

lcd_com(0x30);delay_ms(1); // Инициализация по DATASHEET на HD44780

lcd_com(0x30); // Инициализация по DATASHEET на HD44780

lcd_com(0x01);delay_ms(2); // Очистка дисплея (по опыту)

lcd_com(0x02);delay_ms(2); // Возврат курсора в начало  (по опыту)

lcd_com(0x20); // Инициализация по DATASHEET на HD44780

lcd_com(0x28); // 4-е линии данных, двухстрочный LCD

lcd_com(0x08); // Выключение дисплея

lcd_com(0x01); delay_ms(2); // Очистка дисплея

lcd_com(0x02); delay_ms(2); // Возврат курсора в начало  (по опыту)

lcd_com(0x06); // Сдвиг курсора вправо

lcd_com(0x0c); // Включение дисплея, выключение курсора

}

// Вызов функции обработки прерывания Timer1

interrupt [TIM1_COMPA] void timer1_compa_isr(void)

{

char lcd_t[17];

unsigned int a;

 TCNT1H=0;

 TCNT1L=0; 

// Увеличиваем секунду на 1 в случае появления

// в регистре A числа 7200 (изменено на 7199)

  s++;

// Установка часов     

  if (PINA.0==0)  // Если нажата кнопка m+

{ m++; } // к числу минуты прибавляется единица

 // (для выполнения установки времени на устройстве локально)

  if (PINA.1==0)  // Если нажата кнопка h+

  { h++;} // к числу часов прибавляется единица

  // Условия часов        

  if(s==60)  // Если число секунд = 60

  { m++; s=0;} // к минутам добавляется 1

  if(m==60) // Если число минут = 60

{h++; m=0;} // к часам добавляется 1

if (h==24) // Если число часов = 24

{ h=0;m=0;s=0;} // к дням добавляется 1 

// Переводим C2...7 в режим вывода

 DDRC = 0xFC;

// Устанавливаем  PC4...7 в 1

 PORTC = 0xF0; 

// Инициализация LCD

lcd_init();

// Выполнить в 1-й строке LCD распечатку по примеру

// " ВРЕМЯ=10:11:15 "

//{

lcd_dat(' ');

lcd_dat(0x42);lcd_dat(0x70);lcd_dat(0x65);lcd_dat(0xBC);

lcd_dat(0xC7);lcd_dat('=');

  lcd_dat(h/10+0x30);

  lcd_dat(h%10+0x30);

  lcd_dat(':');

  lcd_dat(m/10+0x30);

  lcd_dat(m%10+0x30);

  lcd_dat(':');

  lcd_dat(s/10+0x30);

  lcd_dat(s%10+0x30);

// Определение температуры каждые 15 секунд

      if ( s==0 || s==15 || s==30 || s==45 )

      { temp=ds18b20_temperature(0);}      

  // Переход на 2-ю строку LCD 

  lcd_com(0xc0);

// Выполнить во 2-й строке LCD распечатку по примеру

//   "    t=26C"

for(a=0;a<17;a++){lcd_t[a]=' ';}

sprintf(lcd_t,"t=%2dC",temp); 

for(a=0;a<6;a++) {lcd_dat(' ');}

for(a=0;a<5;a++){lcd_dat(lcd_t[a]);}

 }  

void main(void)

{

PORTA=0xFF;

DDRA=0x00;//Порт А работает на ввод

PORTB=0xFF; //Потушить диоды

DDRB=0xFF; //Порт B работает на вывод

PORTC=0x00;

DDRC=0x00;

PORTD=0xFF;

DDRD=0xFF; //Порт D работает на вывод

// Timer/Counter 0 initialization

TCCR0=0x00;

TCNT0=0x00;

OCR0=0x00;

// Timer/Counter 1 initialization

// Clock source: System Clock

// Clock value: 7,200 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=0x1C; // Частота работы таймера

OCR1AL=0x1F; // скорректирована на 7,199(0x1C1F).Была 7,200(0x1C20)

OCR1BH=0x00;

OCR1BL=0x00;

// Timer/Counter 2 initialization

ASSR=0x00;

TCCR2=0x00;

TCNT2=0x00;

OCR2=0x00;

// External Interrupt(s) initialization

MCUCR=0x00;

MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization

TIMSK=0x10; //Разрешить прерывание по таймеру

// USART initialization

// Communication Parameters: 8 Data, 1 Stop, No Parity

// USART Receiver: On

// USART Transmitter: On

// USART Mode: Asynchronous

// USART Baud Rate: 9600

UCSRA=0x00;

UCSRB=0x18;

UCSRC=0x86;

UBRRH=0x00;

UBRRL=0x2F;

// Analog Comparator initialization

// Analog Comparator: Off

// Analog Comparator Input Capture by Timer/Counter 1: Off

ACSR=0x80;

SFIOR=0x00;

// Разрешить прерывания

#asm("sei")

// 1 Wire Bus initialization

 w1_init();

while (1)

      {

 if (PINA.2==0) // Однократный дозвон при срабатывании концевого выключателя

 {

 PORTB.0=0; // Включить светодиод 0

 printf("ATM1N7DP0679735581\r\n");

 // AT - AT команда; M1 - включить динамик; N7 - max громкость динамика

 // DP - импульсный набор номера; 0679735581 - номер телефона

 // \r\n - передать команду модему.

 // Модем звонит только тогда, когда подключен к линии.

 delay_ms(30000);

 printf("a");

 goto end;

}         

if ( temp>=30 ) // Двукратный дозвон при превышении температура 30 градусов

 {

 PORTB.1=0; //Включить светодиод 1

 printf("ATM1N7DP0679735581\r\n");

 delay_ms(30000);

 printf("a"); 

 delay_ms(3000);

 printf("ATM1N7DP0679735581\r\n");

 delay_ms(30000);

 printf("a");

 goto end;

 }         

 if (h==10 && m==0 && s==0 ) // Однократный дозвон в 10 часов 0 сек. дня

 {

 printf("ATM1N7DP0679735581\r\n");

 delay_ms(30000);

 printf("a");

 delay_ms(35000);// выждать 35 секунд после того, как модем положит трубку

 }         

      };

 // Выход из цикла и блокирование, пока не будет нажата кнопка RESET

end:

         При программировании LCD индикатора необходимо учесть следующее. Для индикации данных на экране необходимо, чтобы микроконтроллер  посылал  по правилам, определенным контроллером индикатора, соответствующие команды и синхронизирующие импульсы. Сигнал RS должен быть установлен в 0, когда необходимо в LCD передать команды, и в 1 – при передаче данных. Поэтому в листинге программ выделяются две функции:  lcd_com – запись команд и lcd_dat – запись данных. По перепаду сигнала EN с 1 в 0 выполняется запись в LCD команды. Сигнал RD равен 0 при записи данных в LCD и 1 при чтении данных с LCD. Так как данные для устройства всегда записываются в LCD, вывод RD занулен. В таблице 1 представлены некоторые команды LCD на базе HD44780, которые используются в программе.

Таблица 1

Команды LCD

HEX код

Время в мкс

Очистка дисплея

0x01

1640

Возврат курсора в начало

0x02

1640

Сдвиг курсора вправо

0x06

40

Выключение дисплея

0x08

40

Интерфейс связи 8 бит, 1 строка на дисплее

0x30

40

Интерфейс связи 4 бит, 1 строка на дисплее

0x20

40

Выключение курсора

0x0C

40

Интерфейс связи 4 бит, 2 строки на дисплее

0x28

40

 

Из таблицы 1  видно, что LCD может использовать две ширины интерфейса связи – 8 бит и 4 бита. Согласно рис.1 LCD подключен к микроконтроллеру по шине связи шириной 4 бита. Поэтому байты необходимо передавать двумя порциями. На рис.2 показаны временные диаграммы выполнения команды 0xC0 “Установка курсора в первое знакоместо второй строки экрана” и индикация в нем буквы “Д” пересылкой команды 0xE0.

Рис.2

         В листинге программы в соответствии с диаграммой представлены отдельно функция передачи команд (lcd_com, RS=0), функция передачи данных (lcd_dat, RS=1) и функция инициализации LCD (lcd_init) согласно DATASHEET на HD44780  http://www.gaw.ru/pdf/lcd/Chips/Hitachi/hd44780u.pdf.

По опыту работы с LCD для вывода информации на экран без «мусора» необходимо было  дополнительно добавить две команды - lcd_com(0x01) и lcd_com(0x02) (см. листинг программы).

Компиляция исправленной программы выполняется в следующей последовательности:

1.Компилируем проект (команда Compile):

2. Собираем проект командой Make:

В каталоге, где расположена программы на Си, появится файл с расширением .hex, который необходимо будет переслать в микроконтроллер с помощью адаптера и программатора. Для этой цели воспользуемся программой-программатором PonyProg, которая должна быть предварительно установлена на компьютере со свободным LPT портом. После установки PonyProg должна быть выполнена процедура ее калибровки. Для этого необходимо зайти в закладки Setup->Calibration  и нажать на Yes.

Рассмотрим следующие этапы использования программы PonyProg.

1.Запускаем программу и настраиваем интерфейс связи между компьютером и  микроконтроллером:

Указываем использование адаптера для связи с LPT портом:

2.Открываем файл с расширением .hex

3.После загрузки файла заходим в закладку Command->Security and Configuration Bits и устанавливаем  фьюз-биты (биты конфигурации). Ниже на рисунке представлена заводская конфигурация фьюз-битов для ATMega8 Для ATmega16 необходимо снять все галочки и нажать на Write.

4.Выполняем передачу файла микроконтроллеру по команде Ctrl-P. Или можно зайти во вкладку Command и курсором мышки выбрать Program.  В случае успешной передачи программы в микроконтроллер должно появиться окно с надписью Program successful. При программировании необходимо на устройство подать напряжение (10…15В). После ввода программы устройство сразу начинает работать.

 

Литература

1.Отладочный комплекс AVR-EASY.

 http://www.kosmodrom.com.ua/data/avr-easy.php

2. Рюмик С.М. Микроконтроллеры AVR. Ступень 1. http://forum.radiospec.ru/index.php?showtopic=5612 - 2007

3. Мясищев А.А.  Использование контроллеров AVR для управления технологическим оборудованием через  TCP/IP сети. Материали за 6-а международна научна практична конференция, «Найновите постижения на европейската наука», -2010.  Съвременни технологии на информации. Математика. Здание и  архитектура  София .«Бял ГРАД-БГ» ООД – 104 стр.