Простой вольтметр-амперметр можно собрать на микроконтроллере Atmega8. Вывод информации осуществляется на символьный LCD дисплей 16x2.
Для работы нам понадобятся два канала АЦП, это каналы ADC0 и ADC1, при помощи одного мы мерием напяржение при помощи другого силу тока. В качестве источника внутренного напряжения используется внтуренней опорное напряжение в 2.56 В. АЦП работают с разрядность в 10 бит. Микроконтроллер Atmega8 затактирован от внутреннего RC генератора с частотой в 4 МГц.
Вывод информации на дисплей осуществляется следующим образом:
Напряжение которое нам нужно померить сначала подается на делитель напряжения, и уже с него подается на вход АЦП ADC1. Делить собран на резисторах с номиналами в 100 кОм и 10 кОм, получается что отношение входного и выходного напряжения 10 к 1. Максимальное напряжение которое можно подать на вход делителя составляет 28.13 В.
Для того чтобы померить силу тока нам понадобится токовый шунт, его включаем в разры цепи ток в которой хотим померть. Падение напряжения на шутнте нетрудно определить по закону Ома, эта величина меряется други АЦП ADC0. Нужно стремится к уменьшению сопротивлению шунта, чем он меньше тем лучше. Если сопротивление равно 0,1 Ом то при силе тока в 1 Ампера получается падение напряжения в 0,1В.
Если у нас ток в 2 А то падение напряжения составит 0,2 В. Это значение очень мало для того чтобы его подать на вход АЦП микроконтроллера, поэтому его можно устелить при помощи ОУ (операционного усилителя). В нашем примере можно использовать схему неинвертирующего усилителя. Коэффициент усиления составит
Ku=1+(R2/R2)
Нудно чтобы это коэффициент был равен 10, для того чтобы измеряемы ток, к примеру 1 А соответствовал напряжение на выходе ОУ в 2В. В связи с тем что ИОП (источник опорного напряжения) 2.56 В. то мы не может подать больше этого значения. Шаг измерения тока состовит: 2.56А/2024=2.5 мА.
Таким образом для того чтобы получить значение тока, нам нужно напряжение измеренное АЦП умножить на 2.5
Измерение происходит по прерыванию окончания преобразования АЦП. Сначала выбирается канал 1 и снимается напряжения, далее выбирается второй и также снимается напряжение. Измерения каналов происхоид 400 раз, далее вычисляется среднее значение и выводятся на символьный дисплей.
// Измерение постоянного тока с помощью AVR
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
unsigned int voltage, current, adc_counter;
volatile unsigned long voltage_value, current_value;
// Функции работы с LCD
#define RS PD0
#define EN PD2
// Функция передачи команды
void lcd_com(unsigned char p)
{
PORTD &= ~(1 << RS); // RS = 0 (запись команд)
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}
// Функция передачи данных
void lcd_data(unsigned char p)
{
PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}
// Функция вывода строки на LCD
void lcd_string(unsigned char command, char *string)
{
lcd_com(0x0C);
lcd_com(command);
while(*string != '\0')
{
lcd_data(*string);
string++;
}
}
// Функция вывода переменной
void lcd_num_to_str(unsigned int value, unsigned char nDigit)
{
switch(nDigit)
{
case 4: lcd_data((value/1000)+'0');
case 3: lcd_data(((value/100)%10)+'0');
case 2: lcd_data(((value/10)%10)+'0');
case 1: lcd_data((value%10)+'0');
}
}
// Функция инициализации LCD
void lcd_init(void)
{
DDRD = 0xFF;
PORTD = 0x00;
_delay_ms(50); // Ожидание готовности ЖК-модуля
// Конфигурирование четырехразрядного режима
PORTD |= (1 << PD5);
PORTD &= ~(1 << PD4);
// Активизация четырехразрядного режима
PORTD |= (1 << EN);
PORTD &= ~(1 << EN);
_delay_ms(5);
lcd_com(0x28); // шина 4 бит, LCD - 2 строки
lcd_com(0x08); // полное выключение дисплея
lcd_com(0x01); // очистка дисплея
_delay_us(100);
lcd_com(0x06); // сдвиг курсора вправо
lcd_com(0x0C); // включение дисплея, курсор не видим
}
// Обработчик прерывания от АЦП
ISR(ADC_vect)
{
ADCSRA = 0; // Выключаем АЦП
if((ADMUX & 0x0F)==1) // Если был выбран канал ADC1
{
voltage_value = voltage_value + ADC; // Суммируем измеренные значения напряжения и помещаем в буфер
ADMUX = (ADMUX & 0xF0) | 0; // Выбираем канал ADC0
}
else
{
current_value = current_value + ADC; // Суммируем измеренные значения тока и помещаем в буфер
ADMUX = (ADMUX & 0xF0) | 1; // Выбираем канал ADC1
adc_counter++; // Увеличиваем счетчик выборок АЦП на 1
}
// Включаем АЦП
ADCSRA |= (1 << ADEN)|(1 << ADSC)|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0)|(1 << ADIE);
}
int main(void)
{
ADMUX |= (1 << REFS1)|(1 << REFS0); // Внутренний ИОН 2,56V
ADMUX |= (1 << MUX0); // Подключаем канал ADC1
ADCSRA |= (1 << ADEN) // разрешение АЦП
|(1 << ADSC) // запуск преобразования
|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0) // предделитель на 128
|(1 << ADIE); // разрешение прерывания от АЦП
sei(); // Глобально разрешаем прерывания
lcd_init(); // Инициализация LCD
_delay_ms(25);
lcd_string(0x80 ,"VOLTS * AMPERES");
lcd_string(0xC0 ," . * . ");
while(1)
{
// вычисляем среднее значение АЦП
if (adc_counter > 400)
{
ADCSRA = 0; // Выключаем АЦП
// преабразуем данные в реальное значение напряжения
voltage = (voltage_value/adc_counter) * 11/4;
// преабразуем данные в реальное значение тока
current = (current_value/adc_counter) * 10/4;
adc_counter = 0; // Обнуляем счетчик выборок АЦП
voltage_value = 0; // Обнуляем буфер значений напряжения
current_value = 0; // Обнуляем буфер значений тока
// Выводим данные на LCD
lcd_com(0xC0);
lcd_num_to_str(voltage/100, 2);
lcd_com(0xC3);
lcd_num_to_str(voltage, 2);
lcd_com(0xC9);
lcd_num_to_str(current/1000, 1);
lcd_com(0xCB);
lcd_num_to_str(current, 3);
// Включаем АЦП
ADCSRA |= (1 << ADEN)|(1 << ADSC)|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0)|(1 << ADIE);
}
_delay_ms(1);
}
}
Комментарии
Судя по фюзам на картинке - CKSEL3 0 CKSEL2 0 CKSEL1 0 CKSEL0 1 (без инверсии) - это 1 МГц.
Как мне кажется ток невозможно измерить на прямую,не случайно во всех измерительных приборах,которы е могут измерить ток,
присутствует шунт,поэтому измеряя ток прибором его подключают в разрыв цепи и по падению напряжения во внутреннем шунте
прибора измеряется ток.