Курс AVR123.nm.ru


Electronic Banner Exchange (ElBE)
 
6

 

   
 
   
         
   


AVR раз, два, три...   это просто!

Краткий Курс  
МикроКонтроллеры 

AVR Начинающим "с нуля" 

http://proavr.narod.ru
-


Задача 9. 

Электронный вольтметр, измеритель вибрации. 

Цель задачи: разработать устройство и программу для МК ATmega16 для измерения напряжения и частоты сигнала от датчика вибрации и отображения результата на 2-х разрядном 7-ми сегментном светодиодном индикаторе.

Что нужно для выполнения этой задачи:   

- Компилятор Си для AVR CodeVisionAVR 

- Программный эмулятор для AVR VMLAB = Visual Micro Lab

- DataSheet (ДШ) на МК AVR ATmega16

- знание других материалов курса

- твердое знание первой страницы курса на 5 с плюсом  

- свободное время и желание.

 

Параметры сигнала от датчика вибрации: 

Датчик выдает синусоидальное переменное напряжение с частотой вибрации не превышающей 50 Гц и максимальным действующим значением 150 мВ.

Значит амплитуда (это максимальное отклонение от нуля) 
сигнала будет:   150 * (корень из 2) =  212 (мВ)

Виброперемещение  вычисляется по формуле:  
(мкм) = U (мВ) / (0,03 * F (Гц))         

Результат выводится на двухразрядный светодиодный индикатор - это два индикатора TDSL5150 с общим анодом - это значит что все 8 светодиодов  (7 сегментов цифры и точка) соединены "тупыми сторонами" вместе и это соединение нужно подключить к "+" питания. 

А для включения сегмента нужно создать на соответствующей ножке ток 2 мА на "землю" 

при этом падение напряжение на светодиоде составит примерно 1.8 вольт 

это данные и график из ДШ на индикатор.

 

 

Давайте подумаем над схемой и 
алгоритмом работы устройства. 

Нам надо измерить амплитуду и частоту входного сигнала - первое что приходит на ум это подать его на вход АЦП (Аналоговый сигнал в Цифровой Преобразователь)  МК ATmega16 - оцифровывая сигнал с частотой значительно превышающей максимальную частоту входного сигнала - например 1000 раз в секунду - мы можем найти его максимальную положительную величину обнаружив что результат следующего измерения стал меньше, затем аналогично обнаружим следующий максимум и время между этими максимумами будет периодом сигнала.

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

Т.е. вычисление виброперемещения можно проводить несколько  раз между каждым обновлением показаний на индикаторе. А на индикатор выводить максимальный результат из этих нескольких вычислений.

 

 

Реклама недорогих радиодеталей почтой:

 

 

Теперь нужно посмотреть 
описание АЦП  ATmega16.

Откройте в ДШ раздел "Analog to Digital Converter" 

В моей версии ДШ это стр. 202  

Мы видим что встроенный в МК АЦП имеет возможность усиливать сигнал в 10 и 200 раз но при этом разрешение составит 8 бит и по примечанию 1 реализована эта функция только в квадратных корпусах для поверхностного монтажа! Учтем это!

Значит при использовании усиления х10 мы будем оцифровывать положительные полуволны сигнала в диапазоне от 0 до 2,12 вольт.

Опять смотрим ДШ и видим, что МК имеет внутренний калиброванный источник опорного напряжения на 2,56 вольт.

 

Определить на какой вход подавать входной сигнал можно по таблице Table 84. Input Channel and Gain Selections 

Я предлагаю воспользоваться примером расчета на стр. 215

Example:

ADMUX = 0xED 
(ADC3 - ADC2, 10x gain, 2.56V reference, left adjusted result)

Voltage on ADC3 is 300 mV, 
voltage on ADC2 is 500 mV.

ADCR = 512 * 10 * (300 - 500) / 2560 = -400 = 0x270

ADCL will thus read 0x00, and ADCH will read 0x9C. 

Writing zero to ADLAR right
adjusts the result: 
ADCL = 0x70, ADCH = 0x02.


Мы сконфигурируем и включим АЦП аналогично -
используем дифференциальный режим АЦП .

вход "+" это ADC3 - на него мы подадим входной сигнал.

вход "-"  это ADC2 - на него мы подадим 0.

Усиление х10 и внутренний источник опорного  напряжения.

Максимальный результат будет таким: 

ADCR = 512 * 10 * (212 - 0) / 2560 = 424 
или в 16-ричной системе:  0x1A8 или 1A8h

или 1 единица соответствует 0.5 мВ.

 

К сожалению дифференциальное включение АЦП и доп. усиление не доступны в 40-ка выводном DIP-корпусе и в симуляторе VMLAB.

А вот PROTEUS это симулирует !

 


Результат АЦП может по разному записываться 
в два байта (два регистра) для хранения по разному. 

Если выравнивать результат  влево - это значит, что в 16 ячейках старшего байта результата ADCH и младшего байта результата ADCL  - 10 бит результата АЦП будут размещены так что 9-й бит будет 7-ым битом в ADCH а 0-й бит результата будет 6-м в ADCL  


бит ADLAR = 1 - выравнивание в лево:

10-бит результат АЦП
9 8 7 6 5 4 3 2 1 0 номера бит
регистр ADCH регистр ADCL
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0

 

бит ADLAR = 0 - выравнивание в право:

10-бит результат АЦП
номера бит 9 8 7 6 5 4 3 2 1 0
регистр ADCH регистр ADCL
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0

Так записывается результат по умолчанию ! 
смотри в ДШ: Initial Value в регистре
ADMUX 

 

 

Внимание! 

CodeVisionAVR позволяет использовать в программе виртуальный 16 битный регистр ADCW: 

регистр ADCW
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
9
9
регистр ADCH регистр ADCL
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0

это бывает удобно ! 

 

 

Запускаем компилятор CVAVR 
(CodeVisionAVR   или  CV

и затем генератор начального кода программы:

Прекрасный помошник для начинающего эмбедера!

 

1) выбираем ATmega16 и частоту 8 МГц

2) откроем ярлык АЦП - ADC:

- включили АЦП
- включи прерывание по завершению АЦ преобразования
- внутренний источник опроного напряжения с внешним
  конденсатором на ножке AREF
- частота тактирования процессов в АЦП = 125 КГц 

 

3) теперь настройте Timer 0

- источник тактового сигнала - системный такт - т.е. та
  частота которую мы выбрали в "chip" - 8 МГц
- частота с которой булдет "тик"ать таймер 125 КГц
- при переполнении прерывание
- Timer Value - это значение с которого буде считать таймер.

Мы хотим мерить 1000 раз в секунду, а счет идет 125000 раз в секунду, значит нам надо чтоб таймер считал 125 "тик"ов, выдавал прерывание (и выше в п.2 мы настроили чтоб запускал АЦП) и начинал опять считать.

но таймер переполняется при "тик"е когда уже содержит 255 значит он должен считать не с  0  а с  256 -125 = 131  

а 131 в 16-ричном виде будет 0х83 или 83h - вот я его и вписал в окошко.

Если не понятно - перечитайте!

 

4) Всегда полезно иметь возможность вывести отладочную либо еще какую информацию через адаптер rs232 на ПК - для этого активируем передатчик USART 

- включить передатчик
- скорость 38400 и больше ни чего не обычного.

 

5) можно указать некоторую информацию о проекте -
         это может быть удобно

 

 

6) Теперь надо сгенерировать начальный текст программы и сохранить.

Так как я планирую дальнейшую отладку в симуляторе VMLAB то создал папку для этого проекта c:\VMLAB\z09

Сохраните файлы с такими именами:

 

 

Сохраняйте ...

 

Вы увидите сгенерированный по сделанным
настройкам текст программы на языке Си. 

 

Включите поддержку кириллицы и удобный для вас
размер шрифта через меню компилятора
Settings -> Editor

 

 

Давай те слегка структурируем программу, а именно уберем текст конфигурации инициализации "железа" МК из главной функции main в специально созданную функцию: init_avr

1) Выделите и вырежьте текст из программы со строки:

     
  // Input/Output Ports initialization  
     

до строки:

     
  #asm("sei")  
     

2) на месте вырезанного напишите вызов функции:

     
  init_avr();  
     

добавленный код я написал и буду писать синим !

3) после текста:

     
  adc_data=ADCW;
// Place your code here
}
 
     

напишите объявление функции:

     
  void init_avr(void) {

вырезанное вставте сюда !

}
 
     


Функции нужно объявить (значит создать) до вызова!

Причем функция может быть и пустой т.е. ничего не содержать между скобками {} - это удобно при написании скелета программы, а далее по мере проработки алгоритма в скобках появляется текст того, что делает функция. 

 

 

 

Итак, что делает эта программа?

По задуманному при включении МК с кварцем на 8 МГц (конечно с конденсаторами по 22 пФ с ног на землю и соответственно прошитыми фьюзами!) тамер 0  должен 1000 раз в секунду переполнятся и будет возникать соответствующее прерывание - т.е. мы будем попадать в функцию обработчик прерывания от timer 0.

Добавим в нее код запускающий АЦП входного напряжения:

     
  // Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0=0x83;
// начать новый счет с числа 83h

// Place your code here

ADCSRA.6 = 1; 
// сделать бит_6 = 1 значит начать АЦП

}

 
     


Если бит_7 регистра ADCSRA "установлен" (это значит равен "1") - то модуль АЦП включен. Мы установили бит_7 при инициализации МК.

А здесь, в прерывании, мы сделали бит_6 = "1" это запускает АЦ преобразование.  

ВНИМАНИЕ!  бит_6 станет "0" автоматически при завершении АЦП - программно сделать его "0" нельзя! 

 

По завершении АЦП должно возникать прерывание и мы должны попадать в процедуру = функцию обработки прерывания:

     
  // ADC interrupt service routine
interrupt [ADC_INT] void adc_isr(void)
{
unsigned int adc_data;
// ДвухБайтовая беззнаковая 
// переменная для результата АЦП

// Read the AD conversion result
adc_data=ADCW;
// поместить результат АЦП 
// в переменную adc_data


// Place your code here
}

 
     

 

Как нам проверить что программа работает
так как мы предполагаем?

Простые методы проверки как работает программа я 
описал в задаче 06

Давайте их использовать.

 

 

Проверка и отладка программы

Нужно создать файл-проект для симулятора. В любом текстовом редакторе создайте файл vmlab9.prj и поместите его в папку  c:\VMLAB\z09

Не советую использовать кириллицу ! VMLAB тупит порой по этому поводу.

Давайте наполнять файл vmlab9.prj содержанием. 

Все что я буду туда записывать вы можете найти 
- в хелпе к симулятору, 
- в предыдущих задачах, 
- в примерах симулятора - это файлы .prj  

Ищите их поиском Vindows в папке c:\VMLAB\ и вложенных.

 

Во-первых указываем тип МК, затем файл прошивки z09.hex которую даст компилятор, затем файл z09.cof в котором содержится привязка данных прошивки к тексту программы на Си

     
  .MICRO "ATmega16"
.TOOLCHAIN "GENERIC"


.TARGET "z09.hex"
.COFF "z09.cof"

.CLOCK 8meg

 
     

 

Теперь логично добавить источник напряжения V_in для измерения, пусть пока это будет не генератор синусоиды а переменный резистор - одна нога на "землю" другая на 2.5 вольт а средний вывод пойдет на вход АЦП -  ADC3 (ножка PA3 МК)

     
  V_in PA3 VSS SLIDER_1(0 2.5)  
     


Так как симулятор не позволяет симулировать усиление сигнала в АЦП мы будем подавать на вход усиленный в 10 раз сигнал, а после отладки в симуляторе в программе можно будет изменить формулу пересчета результата АЦП.

 

Еще нам надо подать 0 на ADC2 (ножка PA2 МК) 

в  VMLAB  нельзя соединять напрямую два узла! 

поэтому используем резистор R1 на 1 Ом от ножки PA2
МК на землю устройства
:

     
  R1 PA2 VDD 1  
     

 

VDD - это "земля" или 0 вольт

VSS - это питание, по умолчанию +5 вольт

 

Чтоб принимать данные от USART МК как бы через подключенный к ножке PD1 rs232 адаптер на COM-порт ПК включим симулятор терминала:

     
  X1 TTY(38400 8) PD0 PD1  
     

Момент возникновения различных событий удобно наблюдать в окне виртуального осциллографа SCOPE симулятора. Я думаю двух ножек МК будет достаточно для этого. 

Подключим ножки МК: PB0, PB1 и ножку вывода информации с USART PD1 к осциллографу и вход PA3 для V_in

     
  .PLOT V(PA3) V(PB0) V(PB1) V(PD1)  
     

Теперь мы увидим напряжения на этих четырех ножках при симуляции. 

 

При включении МК или после сброса (замыкания вывода reset на землю и создания на нем лог. "1") все выводы МК становятся входами. 

 

Чтобы вывести на ножки МК нужные нам сигналы (логические уровни "0" или "1") - нужно сделать соответствующие ножки вЫходами. 

Для этого соответствующие биты в регистре направления работы выводов порта DDRx нужно сделать "1" = "ножка вЫход"

соответственно "0" = "ножка вход"

В текст нашей программы на Си в созданную нами функцию нужно добавить после строк: 

     
  PORTB=0x00;
DDRB=0x00;
 
     

такой код:

     
  DDRB=0x03; // 0000 0011
// записать в бит0 и в бит1 регистра 
// DDRB числа "1"
 
     

после исполнения МК этих строк программы ножки PB0 и PB1 станут ВЫХОДАМИ и на них появятся напряжения (= уровни) определяемые содержимым соответствующих бит регистра PORTB - у нас это будут логические "0" (см. код программы чуть выше!)

 

     
  Внимание! очень Важно!  
     
 

На языке Си, первая строка (операция "=") означает дословно: 
записать (присвоить) в ячейку памяти (в переменную, в регистр) называемую DDRB результат того что справа от знака "="

На языке Си операция "=" означает поместить результат выражения справа от знака "=" в переменную слева от "="

т.е. вы можете написать:
  х = х + 5; 

Арифметически это будет бессмыслица, а 

на Си это означает: 
к значению переменной х прибавить 5 и поместить результат обратно в х. 

это же можно записать вот так:
  х + = 5; 

 
     

  


Да! но почему  мы не сделали вЫходом ножку PD1 по которой выводится информация передаваемая USART - ведь при включении МК она тоже становится входом и на неё нельзя вывести какой либо логический уровень?

Всё просто! При включении каких либо устройств (периферии МК) ножки относящиеся к ним автоматически конфигурируются так как это нужно для правильной работы этого устройства.

Однако я рекомендую вам уточнять это всегда в ДШ или проверочной симуляцией тестовой программы!

 

 

 

Теперь давайте обозначим какие события будут вызывать изменения уровней на PB0 и PB1 и вывод сообщений на симулятор терминала ПК.

Пусть ножка PB0 изменяет свой логический уровень при каждом  переполнении таймер 0:

     
  // Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0=0x83;
// начать новый счет с числа 83h

// Place your code here

PORTB.0^=1; //инвертировать бит0 регистра 
//если был "0" станет "1" и наоборот

ADCSRA.6 = 1; 
// сделать бит_6 = 1 значит начать АЦП

}

 
     

 

дословно на Си добавленная синяя строчка означает: 
взять значение бит_0 регистра PORTB сделать XOR (исключающее или) этого значения с числом 1 (а проще = инвертировать бит) и результат записать опять в бит_0 регистра PORTB.

 

Старайтесь думать над значением каждой строки программы и четко проговаривать выполняемые операции !

 

Теперь после переполнения таймера 0 мы попадем в эту функцию обработчик прерывания, в ней мы восстановим значение начала отсчета, затем изменим логический уровень на ножке PB0 и увидим это в окне SCOPE симулятора и еще запустим АЦП.

 

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

потом не сделаете!

 

PB1 будет менять свой уровень при завершении АЦП -
напишем строчку соответствующего кода:

     
  // ADC interrupt service routine
interrupt [ADC_INT] void adc_isr(void)
{
unsigned int adc_data;
// ДвухБайтовая беззнаковая 
// переменная для результата АЦП

PORTB.1^=1; //инвертировать бит1 регистра 
//если был "0" станет "1" и наоборот

// Read the AD conversion result
adc_data=ADCW;
// поместить результат АЦП 
// в переменную adc_data


// Place your code here
}

 
     

 

 

на ПК пока через USART выведем обычную строчку начинающих : 

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

init_avr();

putsf("Hello, wold!");

   while (1)
   {
     // Place your code here
   };
}
 
     

 

Кстати если вы помните генератор начального текста программы - он не спрашивал нас на какой вход мы будем подавать измеряемое напряжение V_in и какой коэф. усиления будем использовать. 

Нужно указать это в программе самим!

По ДШ и примеру гораздо выше - видно что этими вопросами ведает регистр МК ADMUX  - в нашем тексте программы уже есть кое что  по этому регистру:

     
  #define ADC_VREF_TYPE 0xC0  
     

и вот это:

   
  ADMUX=ADC_VREF_TYPE;  
     

Строку #define удалите совсем, а значение которое нужно записать в регистр ADMUX определим сами, по ДШ 

Table 83. Voltage Reference Selections for ADC - из неё мы видим что 

бит 7 и 6 нужно сделать "1" и "1" чтоб выбрать:
Internal 2.56V Voltage Reference with external capacitor at AREF pin

бит_5 ADLAR оставляем "0" - результат выравнивается нормально, по правому краю.

бит4_0 для нашей конфигурации (выше я объяснил подробно) должны быть по табл. 84      01101

итак в регистр нужно записать:   

1 1 0 0 1 1 0 1


Но! это в рабочем варианте программы. 

А пока, так как симулятор не симулирует дифференциальное включение, мы будем подавать сигнал просто без усиления на ножку PA3  - это 3-й канал АЦП и значит его номер - число 3 нужно вписать в биты_4_0

Итак для симуляции в регистр ADMUX нужно записать:   

1 1 0 0 0 0 1 1

 

Записываем число в двоичном виде: 

   
  ADMUX=0b11000011; // V_in на вход ADC3
// Vref 2.56V внутренний 
 
     

 

Еще я бы хотел сделать переменную для результата АЦП глобальной - т.е. доступной для использования в любом месте программы. 

Вырежьте вот эти строки из программы: 

     
  unsigned int adc_data;
// ДвухБайтовая беззнаковая 
// переменная для результата АЦП
 
     

и вставьте после строки 
#include <stdio.h>  
вот так:

   
  // Standard Input/Output functions
#include <stdio.h> 

// Глобальные переменные

unsigned int adc_data;
// ДвухБайтовая беззнаковая 
// переменная для результата АЦП
П
 
     

 


Можно компилировать!

 

Надеюсь вы правильно все сделали, но на всякий случай выкладываю архив z09_v01.rar (6.3 Кб) всех файлов готовых для компиляции и симуляции.

 

 

Компилируем.

1) Кликните эту иконку в CodeVisionAVR: 

2) если вы сделали все аккуратно то получите вот такое:

а в вашей папке C:\VMLAB\z09 появится множество файлов, но нас интересуют три файла: z09__.c   z09.hex   z09.cof   

Эти файлы нужны VMLAB для симуляции. 

 

 

 

 

 

Симулируем ...

 

Подробно и с картинками симуляция
в VMLAB описана в задаче 3

 

1) запустите VMLAB 

2) откройте проект - vmlab9.prj 

3) нажмите F9 чтоб "отбилдить" проект. Должна выскочить такая надпись - Успешно! все готово к забегу!

в противном случае начните с 1) и повнимательней. 

4) На панели "peripherals" - "переферия МК" кликните плюсики у АЦП и Таймер 0 чтоб развернуть их.

Должно получится вот так:

 

 

5) для запуска симуляции нажимайте светофор до тех пор пока симулятор наругается: 

и пойдет нормальная, непрерывная симуляция...

 

 

При симуляции вы можете менять положение движка левого резистора на Control Panel и видеть в цифровом виде (рисунок над предыдущим) напряжение V_in и одновременно результат его оцифровки в регистрах ADCH и ADCL.

 

 

Я померил в симуляторе время между переполнениями таймер 0 и оно очень точно равно 1 мС  - вот картинка измерения.

А вот замер времени передачи сообщения по USART:

оно составило 3.4 мС 

Мы передали 12 символов и символ "LF" - значит всего 13. 

На каждый символ тратится 10 бод. значит расчетное время составляет: 130/38400 = 3,385 мС   -
довольно близко к измеренному по осциллографу!

 

 

Еще важный момент

Благодаря осциллографу  SCOPE  мы
можем увидеть электрические сигналы!

Посмотрите внимательно этот рисунок:

На осциллограмме видно, что в начале, после включения МК и до выполнения программы функции инициализации уровни на трех ножках были по 2,5 вольта - так симулятор показал что они являются высоко-омными входами. 

 

2,5 вольта симулятор показал просто для определенности - на самом деле напряжения на ножках реального МК будут определятся тем что к ним подключено!

 

Затем программа выполнила строку:

     
  DDRB=0x03; 
// записать в бит0 и в бит1 ...
 
     

и на ножках PB0 и PB1сразу стали логические "0" (что соответствует 0 вольт)  потому, что ранее была выполнена вот такая строчка:

     
  PORTB=0x00;  
     

 

 По истечении 4.1 мкС выполняется эта строка:

     
  UCSRB=0x08;  
     

она включает USART и, как я писал выше, конфигурирует вывод PD1 для правильной работы этого устройства - т.е. делает его выходом.  

Но - опять внимание! 

делает PD1 выходом с уровнем "1" 

несмотря на то, что ранее в программе была выполнена строчка:

   
  PORTD=0x00;  
     

"1" появилась на PD1 потому что включился USART - а при отсутствии передачи данных на его выходе высокий логический уровень!

Далее, через некоторое время (рисунок разорван!) началась передача текста со спада "1-0" на PD1 - это "старт бит".

  

 

Погоняйте симуляцию программы.

У вас не должно остаться вопросов без ответа!

Если вопросы останутся, то запишите их и ищите ответ по методике описанной на первой странице курса! 

 

 

 

 

2-я часть задачи 9

 

 



AVR раз, два, три...   это просто!

Краткий Курс  
МикроКонтроллеры 

AVR Начинающим "с нуля" 

http://proavr.narod.ru
-

 

   

 

 

Сайт управляется системой uCoz