» » Грызем микроконтроллеры. Урок 4.

 
 
 
1

Грызем микроконтроллеры. Урок 4.

Разместил Spirit 22 декабря 2008. Просмотров: 53 026

Мигалки – это хорошо, по новогоднему…
Но ведь нельзя останавливаться на достигнутом! Пора сделать что-то посложнее!
Хотя, на самом деле, это только кажется сложным. И к концу статьи Вы скажете, что измерить температуру или напряжение при помощи AVR – сущий пустяк.
В связи с усложнением задачи, будет неплохо иметь под рукой более “навороченный” микроконтроллер. Можно конечно этого не делать, но у tiny2313 маловато памяти и нет Аналого-Цифрового преобразователя. Поэтому мы переезжаем на МК серии Mega!
В виду того, что конструкции дальше будут только усложняться, выбор пал на ATmega16.


А теперь задачи: нам нужно измерить напряжение, подаваемое с движка переменного резистора, измерить температуру в 4 точках и как-нибудь это показать.

Измерить напряжение довольно просто. Наш микроконтроллер имеет 8 входов аналого-цифрового преобразователя (АЦП). Сам АЦП имеет разрядность 10 бит, что дает 1024 ступени измерения.

Например, если измерения проводить в интервале 0-10 Вольт, то результат можно получить с точностью примерно до 0,01 вольта (а если быть абсолютно точным, то до 0,009765625)!


Температуру измерить можно подключив к одному из входов АЦП терморезистор или термопару. Но тут могут возникнуть сложности с программной реализацией, ведь нужно будет по каким-либо формулам пересчитывать полученное значение напряжения в температуру. Поверьте, это не так уж и просто!
К великому счастью, мы живем в мире развитом и нам не нужно изобретать велосипед tongue

Есть чудесная мелкосхемка под названием DS18S20. Заключена она в корпус TO-92 (как маломощный транзистор) и имеет всего три вывода.

Зато внутри этого скромного корпуса содержится цифровой датчик температуры с точностью до 0,1С и диапазоном от -55 до +125 С.

С микроконтроллером всё это сопрягается по протоколу 1wire. О его принципе рассказывать не буду. Кому интересно – может поискать в интернете.
А вот о его прелестях не сказать не смогу.

1wire – однопроводной интерфейс, позволяющий подключить к одной линии данных практически неограниченное количество периферии. Каждое устройство имеет уникальный 64-х битный идентификационный номер. Т.е., максимальное число устройств на одной шине – 2 в степени 64. Единственный недостаток – если использовать максимальное число датчиков, опрос всех может занять лет 10 smile

В общем, берем 4 таких датчика и не задумываясь соединяем их все параллельно и шину DQ цепляем к микроконтроллеру.



Таким образом, для всех датчиков будем использовать только 1 вывод МК. По-моему, очень даже красивое схемное решение fellow


А как же нам посмотреть на результаты всех этих измерений?
Можно выводить это всё на наши любимые светодиоды, а потом сидеть с блокнотиком или калькулятором и всё это расшифровывать wink
А можно взять LCD дисплей и поднять нашу конструкцию на недосягаемую высоту. Предлагаю дисплей на основе контроллера HD44780.


И, в нагрузку, научимся использовать Watchdog (дословно - сторожевой пес), или, так называемый, сторожевой таймер. Предназначен он для того, чтобы сбрасывать МК при зависании программы.

Он имеет свой собственный тактовый генератор на 1 МГц и предделитель, который позволяет выставить интервал времени от 0,016 до 2,048 секунды. При переполнении этого таймера происходит принудительный сброс МК. Этот таймер нужно периодически сбрасывать в каком-либо месте программы, не допуская его переполнения при нормальном исполнении программы.


В протоколы, команды и прочую ерунду погружаться пока не будем. С компилятором CVAvr этого можно и не знать, так что, создаем новый проект!

Указываем тип и частоту МК.

Грызем микроконтроллеры. Урок 4.


Настраиваем порты.







Теперь на вкладке LCD выбираем порт для подключения дисплея.



А на вкладке 1 Wire указываем вывод для подключения датчиков и ставим галочки Enabled (включено), и Multiple Devices (множество устройств), чтобы включить использование датчиков DS18S20.



Переходим на вкладку ADC и включаем аналого-цифровой преобразователь.



И еще заходим на вкладку Timers -> Watchdog и выставляем следующее:





Генерируем, сохраняем и приступаем к разбору.



// 1 Wire Bus functions
#asm
   .equ __w1_port=0x12;PORTD
   .equ __w1_bit=0
#endasm


Эта часть кода указывает компилятору, какой вывод МК должен использоваться для интерфейса 1wire.
Код этот записан на ассемблере.

Ассемблерные вставки можно делать так:
#asm(“sei”)

Команда ассемблера пишется в ковычкай. Удобно, если нужно выполнить только одну команду.
“sei” – команда разрешения всех прерываний
А если необходимо выполнить несколько команд, написанных на ассемблере, их удобнее будет вставить между директивами #asm (начало кода) и #endasm (конец кода).


#include <1wire.h>
// DS1820 Temperature Sensor functions
#include <ds1820.h>

Подключение файлов с функциями, необходимыми для работы с интерфейсом 1wire и датчиками DS18S20.


// maximum number of DS1820 devices
// connected to the 1 Wire bus
#define MAX_DS1820 8

Константа, определяющая максимальное число датчиков, подключенных к МК.


// number of DS1820 devices
// connected to the 1 Wire bus
unsigned char ds1820_devices;

Переменная, в которую записывается число найденных датчиков.


// DS1820 devices ROM code storage area,
// 9 bytes are used for each device
// (see the w1_search function description in the help)
unsigned char ds1820_rom_codes[MAX_DS1820][9];

Переменная, в которой хранятся идентификационные коды найденных датчиков.


// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x15;PORTC
#endasm
#include <lcd.h>

Тут определяется порт, используемый для дисплея, и подключается библиотека для работы с ним.


#define ADC_VREF_TYPE 0xC0

Эта переменная указывает на источник опорного напряжения для АЦП. У нас это внутренний стабилизатор 2,56В. Следовательно, одно деление АЦП будет равно 0,0025В


// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}

Функция, возвращающая результат аналого-цифрового преобразования для указанного входа.


// Determine the number of DS1820 devices
// connected to the 1 Wire bus
ds1820_devices=w1_search(0xf0,ds1820_rom_codes);

Запуск функции поиска 1wire устройств и запись их количества в переменную ds1820_devices.


// LCD module initialization
lcd_init(16);

Инициализация дисплея


// Global enable interrupts
#asm("sei")

Разрешение прерываний.



Теперь будем добавлять свой код.

Во-первых, нам надо сбрасывать сторожевой таймер.
Это выполняется командой #asm(“wdr”)
Поместим ее в начало основного цикла.

Теперь разберем функции, необходимые для работы с нашими периферийными устройствами.

void lcd_clear(void);

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

void lcd_gotoxy(unsigned char x, unsigned char y);

Установка курсора на символ с номером x в строке y

void lcd_putchar(char c);

Записать символ c туда, где находится курсор. После записи курсор смещается на следующую ячейку.

void lcd_puts(char *str);

Записать в дисплей строку str, начиная с того места, где находится курсор. После записи курсор смещается на следующую ячейку после конца строки.

void lcd_putsf(char flash *str);

То же самое, что и lcd_puts(), только используется для вывода строк, хранящихся в памяти программ (флеш-памяти).
Например
flash char text[]=”Hi Datagor.ru!”;

Переменные, хранящиеся во флеш-памяти не могут быть изменены, т.е., являются константами. Их удобно использовать для хранения больших строк, чтобы не забивать ими оперативную память.


int ds1820_temperature_10(unsigned char *addr);

Эта функция возвращает значение температуры, считанное из датчика с идентификационным кодом addr, и умноженное на 10. Т.е., если температура 13,5 градусов, то функция вернет значение 135.

unsigned int read_adc(unsigned char adc_input)

Возвращает результат АЦ преобразования для выбранного входа ADC 0-7.


Но вот выводить числа сразу на дисплей не получится. Они будут восприниматься его контроллером как коды символов и ничего хорошего мы не увидим.


В этой ситуации можно использовать библиотеку stdio.h, которая содержит функцию sprint()

void sprintf(char *str, char flash *fmtstr,...);

Первым параметром мы передаем переменную, в которую будет записан результат выполнения функции.
Второй параметр – форматированная строка.
Остальные – переменные.
Вот пример использования:
char text[17];
char day=22, month=12,  year=2008;
sprint(text,“Data: %d.%d.%d”,day,month,year);

В результате выполнения этой команды, в переменную text будет помещена строка “data: 10.12.2008”.
%d – в этом примере является маской, означающей вывод целого числа. Эти числа берутся по порядку из переменных, которые мы передаем после строки.

Маски:
'%d' – целое десятичное число
'%u' – целое бесзнаковое десятичное число (unsigned)
'%f' – число с плавающей точкой, выводимое в формате [-]ddd.dddddd
'%X' – вывод в виде шестнадцатичного числа
чтобы вывести знак процента (%) его нужно продублировать, т.е., написать”%%”.


Так же, маску можно расширить, указав формат отображения числа

%[указатель_знака][кол-во_разрядов_до_точки][. кол-во_разрядов_после_точки]маска

Квадратные скобки указывают, что эти параметры не обязательны.

Указатель_знака:
“+” – выводить знак всегда (плюс для положительных, минус для отрицательных);
“ “ (пробел) – выводить минус для отрицательных и пробел для положительных.
Например, если мы хотим вывести дробное число с тремя знаками после запятой, то нужно написать
%.3f
или, если ширина числа должна быть постоянной (чтобы цифры не сползали влево-вправо при разном количестве разрядов в них), то пишем:
%3d

И последнее. Чтобы использовать маски в CVAvr необходимо указать разрешенные типы в окне Project -> Configure на вкладке C Compiler



int – вывод только целых (char, int) чисел в простой форме или с указанием знака
int, width – вывод целых чисел (char, int) с возможностью указать количество символов и знак
float, width, precision – вывод целых и дробных чисел с использованием всех возможностей

Чем больше возможностей используете, тем больше получается размер программы, поэтому не стоит включать режим чисел с плавающей точкой, если выводить собираетесь только целые числа.


С функциями вроде разобрались. Осталось лишь написать саму программу.
Вот, что получилось у меня:


/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.9 Standard
Automatic Program Generator
© Copyright 1998-2008 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com

Project : 
Version : 
Date    : 22.12.2008
Author  : Spirit                            
Company : Spirit                            
Comments: 


Chip type           : ATmega16
Program type        : Application
Clock frequency     : 8,000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/

#include <mega16.h>

#include <stdio.h>

// 1 Wire Bus functions
#asm
   .equ __w1_port=0x12;PORTD
   .equ __w1_bit=0
#endasm
#include <1wire.h>

// DS1820 Temperature Sensor functions
#include <ds1820.h>

// maximum number of DS1820 devices
// connected to the 1 Wire bus
#define MAX_DS1820 8
// number of DS1820 devices
// connected to the 1 Wire bus
unsigned char ds1820_devices;
// DS1820 devices ROM code storage area,
// 9 bytes are used for each device
// (see the w1_search function description in the help)
unsigned char ds1820_rom_codes[MAX_DS1820][9];

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x15;PORTC
#endasm
#include <lcd.h>

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
// Place your code here

}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
// Place your code here

}

#include <delay.h>

#define ADC_VREF_TYPE 0xC0

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A 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 
PORTA=0x00;
DDRA=0x00;

// 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=0x00;
DDRB=0xFF;

// Port C 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 
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=P State6=P State5=P State4=P State3=P State2=P State1=P State0=P 
PORTD=0xFF;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// 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: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
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: On
// INT0 Mode: Falling Edge
// INT1: On
// INT1 Mode: Falling Edge
// INT2: Off
GICR|=0xC0;
MCUCR=0x0A;
MCUCSR=0x00;
GIFR=0xC0;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// ADC initialization
// ADC Clock frequency: 1000,000 kHz
// ADC Voltage Reference: Int., cap. on AREF
// ADC Auto Trigger Source: None
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x83;

// Watchdog Timer initialization
// Watchdog Timer Prescaler: OSC/2048k
#pragma optsize-
WDTCR=0x1F;
WDTCR=0x0F;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Determine the number of DS1820 devices
// connected to the 1 Wire bus
ds1820_devices=w1_search(0xf0,ds1820_rom_codes);

// LCD module initialization
lcd_init(16);

// Global enable interrupts
#asm("sei")

while (1)
      {
        char text[16];
        float volt;
        
        #asm("wdr")                             // сброс сторожевого таймера
        
        lcd_gotoxy(0,0);                        // перемещение курсора
        volt=read_adc(0);                       // считывание значения АЦП
        volt*=0.0025;                           // перевод в напряжение
        sprintf(text,"Voltage: %.4f",volt);     // форматирование строки
        lcd_puts(text);                         // вывод напряжения
        

       
        lcd_gotoxy(0,1);
        sprintf(text,"%+3d ",ds1820_temperature_10(&ds1820_rom_codes[0][0])/10);  // считывание датчика и деление на 10
        lcd_puts(text);                                                           // вывод температуры
        
        sprintf(text,"%+3d ",ds1820_temperature_10(&ds1820_rom_codes[1][0])/10);
        lcd_puts(text);
        
        sprintf(text,"%+3d ",ds1820_temperature_10(&ds1820_rom_codes[2][0])/10);
        lcd_puts(text);
        
        sprintf(text,"%+3d ",ds1820_temperature_10(&ds1820_rom_codes[3][0])/10);
        lcd_puts(text); 
        
      };
}



Можно компилировать и проверять.


При моделировании в ISIS собираем такую схему.



Указываем файл прошивки и параметры тактового генератора (!).



Если задать слижком маленькую частоту (можете попробовать 1МГц), может срабатывать сторожевой таймер (из-за редких сбросов) и не будут работать датчики.
Если частота будет слишком высокой – не будут считываться датчики температуры.


А датчикам присваиваем разные ROM Serial Number





Вот так вот всё на самом деле просто! А Вы мне, наверное, не верили…


В следующий раз мы отойдем от программирования и будем учиться прошивать микроконтроллеры. am

До скорой встречи!
bully
Владимир (Spirit)
Старый Оскол
Профиль Spirit
Электронщик-практик, в основном занимаюсь микроконтроллерами. Есть неплохой опыт и в аналоговой технике (все мы начинали с УМЗЧ =)).
Одержим идеей автоматизации жилища а-ля "Умный дом" =)
 

Понравилось? Палец вверх!

  • всего лайков: 22

Поделись с друзьями!

Связанные материалы:


Схема на Датагоре. Новая статья Моделирование линейного блока питания в программе «PSU Designer II»... Многие радиолюбители используют трансформаторы в качестве основы блоков питания, в том числе...
Схема на Датагоре. Новая статья Измерения переменного напряжения звуковой частоты мультиметрами М-832... Вряд ли будет преувеличением сказать, что тестер семейства М-83х есть у каждого радиолюбителя....
Схема на Датагоре. Новая статья Тестер ёмкости автомобильного аккумулятора (ATmega8A + LM2575). Готовимся к зиме... Приветствую, граждане Датагории! Позвольте представить вам очередное моё творение — тестер емкости...
Схема на Датагоре. Новая статья Простой модульный вольтметр переменного напряжения на PIC16F676... Простой вольтметр переменного напряжения с частотой 50 Гц, выполнен в виде встраиваемого модуля,...
Схема на Датагоре. Новая статья Простой прибор для подбора пар мощных транзисторов... Предельно простое, но удобное устройство для подбора пар кремниевых транзисторов средней и большой...
Схема на Датагоре. Новая статья Сделай сам RMS-вольтметр на PIC18F2520. Немного теории и исходники... В розетке 220 Вольт - это RMS, а амплитудное там 310 Вольт. По пальцам бьют 310В, но воду кипятят...
Схема на Датагоре. Новая статья Характеристика резистора для пассивного регулятора громкости... Давайте по простому разберемся, какая кривая зависимости сопротивления от угла поворота должна быть...
Схема на Датагоре. Новая статья Простой пробник-измеритель полевых JFET транзисторов... Вот уж не думал, что придется развлекаться с полевыми транзисторами. Когда транзисторы попали...
Схема на Датагоре. Новая статья Микроконтроллеры. Связь с внешним миром. Часть 3.... И снова приветствую Вас в моей лекционной! На этот раз я расскажу Вам как "сэкономить" выводы...
Схема на Датагоре. Новая статья Грызем микроконтроллеры. Урок 3.... Эту статью я начну с провокационного вопроса… А какую конструкцию на основе МК хотите создать ВЫ? ...
Схема на Датагоре. Новая статья Микроконтроллеры AVR семейств Tiny и Mega фирмы ATMEL, Евстифеев А.В.... Издательство: Додэка XXI [М.], 560 стр. 2005 г. Книга посвящена вопросам практического применения...
Схема на Датагоре. Новая статья PowerTrans - программа для расчета параметров трансформатора линейного БП... Еще одна программа для расчета параметров трансформатора. Существует множество программ для...
<
  • Прохожий
22 декабря 2008 11:39

/ Spirit

Цитата
  • С нами с --
  • 0 комментариев
  • 0 публикаций
 
  • 0
Други, комментирование закрыто!
Перемещайтесь на наши Форумы и смело создавайте новые темы.
Если не уверенны в нужном разделе - создавайте на Заваленке, потом перенесем.
forum.datagor.ru/

Добавление комментария


Налетай! Паяльники, станции, жала с доставкой
  • smilelolbyewinkyahoocoollaughing
    crazybadcryingsadirefulsickstraight
    ballooncakegooddrinksmailbombsun
    nightrainstarscolddashguitar-manhandshake
    musicnegativenopardonshoksleepunknown
    wackoyawnblushbullyhashsmokingwhew
Скопируйте текст вашего комментария на случай неверного ответа на контрольный вопрос.