Только в этом месяце - скидки на паяльники и электронику с нашими кодами (123avr.com) :




Страницы курса:

Уроки

Задания


index.htm
01.htm
02.htm
03.htm
04.htm
05.htm
06.htm
07.htm
08.htm
09.htm


z1.htm
z2.htm
z3.htm
z4.htm
z5.htm
z6.htm
z7.htm
z8.htm
z9.htm
z10.htm
z11.htm

New! - ФОРУМ!

Совет - умейте правильно находить информацию!


Задача 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 - выравнивание в лево:


Credit Star


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;
// ДвухБайтовая беззнаковая 
// переменная для результата АЦПП

 
     

 


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

 

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

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

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Rating All.BY Rambler's Top100 ћ≈“ј - ”краина. –ейтинг сайтов



Copyright 2009-2019 123avr.com