Микроконтроллер LGT8F328p и является клоном популярной AVR ATmega328p (Arduino NANO). Микроконтроллер LGT8F328p практически полностью совместим с микроконтроллером ATmega328p и обладает рядом дополнительных функций и возможностей превышающих ATmega328p.
Плата LGT8F328P-LQFP32 MiniEVB
Основные характеристики LGT8F328P
- FLASH (ПЗУ): 32 Кбайт
- SRAM (ОЗУ): 2 Кбайт
- E2PROM (EEPROM): 0K / 1K / 2K / 4K / 8K (эмуляция)
- PWM (ШИМ): 8
- Частота: 32 МГц
- АЦП: 9 пинов, 12 бит
- ЦАП: 1 пин, 8 бит
- Силовые пины: 4 (до 80 мА)
- Таймеры 2x 8bit, 2x 16bit
- UART: 1
- SPI: 1
- I2C: 1
- PLL: 1
- Опорное напряжение: 1.024В / 2.048В / 4.09В ± 0,5%
- Логический уровень: 5В
Особенность АЦП в LGT8F328P является наличие нескольких опорных напряжений (1,024 В, 2,048 B, 4,096 B, VCC), несколько множителей входного сигнала (1, 8, 16 , 32) и увеличенная по сравнению Atmega328 разрядность АЦП до 12 бит. Так же имеется возможность использования дифференциального входа.
В статье будет показано несколько примеров использования ЦАП в LGT8F328P.
Стандартный код для измерения напряжения в Arduino IDE
void setup(){
Serial.begin(9600);
pinMode(A0,INPUT);
analogReadResolution(12);// АЦП 12 БИТ
analogReference(DEFAULT);// DEFAULT(Uп)
}
void loop(){
Serial.println(analogRead(A0));
delay(1000);
}
На вход А0 подается напряжение 3,3 В (с платы LGT8F328P-LQFP32 MiniEVB) и измеренное напряжение выводится в монитор порта:
void setup() {
Serial.begin(9600);
ADMUX |= 1 << REFS0 | 0b000; /* REFS2 REFS1 REFS0 0 0 0 AREF 0 0 1 AVCC 0 1 0 2.048V 0 1 1 1.024V 1 0 0 4.096V Бит REFS2 относится к регистру ADCSRD */
ADCSRA |= 1 << ADEN | 1 << ADSC | 1 << ADATE | 0b111;
}
void loop() {
while((ADCSRA & (1 << ADIF)) == 0);
int u_data = (ADCL|ADCH << 8);
Serial.println(5.00/4096*u_data,2);
delay(1000);
}
Вывод монитора порта:
ADMUX — регистр управления мультиплексора АЦП, позволяет выбрать источник опорного напряжения, выбрать источник входного сигнала и осуществить выравнивание по левому или по правому краю результата измерения.
Выбор источника опорного напряжения:
Обратите внимание, что бит REFS2 относится к регистру ADCSRD.
Выбор источника входного сигнала
Как видно из кода:
ADMUX |= 1 << REFS0 | 0b0000;
выбрано опорно напряжение VCC (напряжение питания микроконтроллера), активен вход PC0 (A0).
ADCSRA — регистр A управления и состояния АЦП
ADEN — бит управления включением АЦП. Если бит ADEN установлен на 1,то АЦП включен.
ADSC — бит начала преобразование АЦП. Если бит ADSC установлен на 1, то активно непрерывное преобразование.
ADATE — бит запуска включением автоматически управления АЦП. Если бит ADATE установлен на 1, активируется функция автоматического запуска.
ADPS — бит выбора коэффициент деления предделителя ADC.
В коде выбраны все выше описанные биты регистра ADCSRA:
ADCSRA |= 1 << ADEN | 1 << ADSC | 1 << ADATE | 0b111;
После настройки конфигурации АЦП следует чтение регистров данных ADCH и ADCL. Следует отметить что первым необходимо считывать младший регистр данных ADCL.
Считывание данных возможно только после окончания преобразования, то есть как бит (флаг) ADIF станет равным 1.
while((ADCSRA & (1 << ADIF)) == 0);
После считываем данные:
int u_data = (ADCL|ADCH << 8);
и выводим результат в вольтах в монитор порта:
Serial.println(5.00/4096*u_data,2);
где:
5.00 — напряжение питания микроконтроллера, оно же и опорное
4096 — числовое значение равное максимальному числу 12 бит
Так как производится измерение напряжения 3,3 В, то целесообразней перейти на опорное напряжение 4,096 В для увеличения точности напряжения:
void setup() {
Serial.begin(9600);
ADMUX = 0b000; ADCSRD |= 1 << REFS2; /* REFS2 REFS1 REFS0 0 0 0 AREF 0 0 1 AVCC 0 1 0 2.048V 0 1 1 1.024V 1 0 0 4.096V Бит REFS2 относится к регистру ADCSRD */
ADCSRA |= 1 << ADEN | 1 << ADSC | 1 << ADATE | 0b111;
}
void loop() {
while((ADCSRA & (1 << ADIF)) == 0);
int u_data = (ADCL|ADCH << 8);
Serial.println(u_data/1000.00,2);
delay(1000);
}
Монитор порта:
В LGT8F328 имеется регистры калибровки VCAL1, VCAL2, VCAL3 для напряжения 1,024, 2,048 и 4,096. Регистр VCAL используется как общий для всех калибровочных значений. При включении по умолчанию выбрано опорное напряжение 1,024 В, калибровочное значение хранится в регистре VCAL1, которое записывается в регистр VCAL. Если выбрать например опорное напряжение 4,096 В , то значение регистра VCAL3 так же будет записано в регистр VCAL. Как видно по показаниям, значение VCAL3 не обеспечивает большую точность измерения. Для более точного изменения необходимо подобрать более точный коэффициент и записать его в регистр VCAL.
Для просмотра калибровочного коэффициента установленного производителем микроконтроллера можно воспользоваться командой:
Serial.println(VCAL3); // 4,096 V
void setup() {
Serial.begin(9600);
ADMUX = 0b000; ADCSRD |= 1 << REFS2; /* REFS2 REFS1 REFS0 0 0 0 AREF 0 0 1 AVCC 0 1 0 2.048V 0 1 1 1.024V 1 0 0 4.096V Бит REFS2 относится к регистру ADCSRD */
ADCSRA |= 1 << ADEN | 1 << ADSC | 1 << ADATE | 0b111;
VCAL = 30;
}
void loop() {
while((ADCSRA & (1 << ADIF)) == 0);
int u_data = (ADCL|ADCH << 8);
Serial.println(u_data/1000.00,2);
delay(1000);
}
Монитор порта:
Дифференциальный режим работы АЦП
Микроконтроллер LGT8F328 поддерживает дифференциальный режим работы аналогового входа. При этом на два аналоговых входа подается напряжение любой полярности не превышающее напряжение питания микроконтроллера.
Основные настройки дифференциального входа находятся в регистре DAPCR.
Бит DAPEN разрешает работу дифференциального усилителя
Биты DNS[4:2] активируют работу инвертирующего входа дифференциальный усилителя
Биты DPS[1:0] активируют работу неинвертирующего входа дифференциальный усилителя
Измерение напряжения поданного на дифференциальный вход происходит в два этапа. Сначала делаем первый вход как дифференциальный, а второй как аналоговая земля, проводим измерение, далее входы меняем местами, то есть первый вход делаем как аналоговая земля, а второй вход делаем дифференциальным. Далее снова проводим измерения. В итоге получается два измерения, для получения конечного результата надо из результата одного измерения вычесть результат второго измерения.
Перед измерением в регистре ADMUX указывает активный вход и при необходимости делаем выбор опорного напряжения.
Пример кода:
void setup() {
Serial.begin(9600);
ADCSRD |= 1 << REFS2;// REF 4.096
VCAL = 30;
ADCSRA |= 1 << ADEN | 1 << ADSC | 1 << ADATE | 0b111;
}
void loop() {
ADMUX = 0b11;DAPCR = 0;
DAPCR |= 1 << DAPEN | 0b11001;
while((ADCSRA & (1 << ADIF)) == 0);
int u_data0 = (ADCL|ADCH << 8);
// Serial.println(u_data0);
delay(500);
ADMUX = 0b00;
DAPCR = 0;
DAPCR |= 1 << DAPEN | 0b00111;
while((ADCSRA & (1 << ADIF)) == 0);
int u_data1 = (ADCL|ADCH << 8);
// Serial.println(u_data1);
Serial.println(float(u_data0-u_data1)/1000);
delay(500);
}
Для примера были выбраны входы А0 и А3 (ADC3 и ADC0), на них с элемента питания подается напряжение 1,5 В. Выбран внутренний опорный источник 4,096 В.
Как видно на скриншоте дифференциальный вход при смене полярности вполне корректно измерил напряжение элемента питания.
При выборе входа один должен быть инвертирующий, другой неинвертирующим. Так же измеряемое напряжение не должно иметь общего провода с GND микроконтроллера.