Свежие обсуждения
Микроконтроллеры

Помогите настроить АЦП в Atmega

1 4

Добрый день
Вот решил сделать блок питания с управлением на МК
Возникли проблемы с АЦП, а именно:
Не могу настроить его так чтобы он мерил многократно, а не 1 раз
Работаю в CVAVR, симулирую в ПРОТЕУСЕ
Делаю так:
interrupt [ADC_INT] void adc_isr(void){
Vizm=ADCW;
Vpr = (float) Vizm; //переводим формат данных из целочисленно в фиксированную арифметику
Vpr = Vpr * 0.03128; //умножаем на множитель масштаба(измерение до 32В)
Vdec = (int) Vpr; //выделяем целую часть;
Vpr = Vpr - (float) Vdec;//выделяем милливольты
Vpr = Vpr * 1000; //избавляемся от целой части
Vmili = (int) Vpr; //переводим милливольты в вольты
}
Дальше преобразовываю в BCD и вывожу на LED.(ADCSRA=0b11011111;
ADMUX=0b11000000
Но он делает преобразование 1 раз и останавливается.
Если установить ADFR=1, то в протеусе на индикаторе кракозябры
(может на железе будет нормально?)
Как заставить делать преобразование многократно?

 

Я бы не делал все эти вычисления в прерывании. При FR=1 прерывание будет вызываться постоянно.

Либо выносить вычисления из прерывания, либо вручную запускать преобразование по окончании вычислений (хуже).

 

Не только лучше вынести вычисления из прерывания, но и не полениться усреднить показания - АЦП практически никогда два одинаковых показания Вам не даст. Обычно 16..32 выборок достаточно, чтобы цифры не скакали.

 

Да но если я выношу измерение за прерывание то начинает показывать крякозябрики:
interrupt [ADC_INT] void adc_isr(void){
adc_data=ADCW;
}
.....
void main(void)
{
ADMUX=0b11000000;
ADCSRA=0b11011111;

#asm("sei")

while(1)
{

Vizm=adc_data ;
Vpr = (float) Vizm;
Vpr = Vpr * 0.03128;
Vdec = (int) Vpr;
Vpr = Vpr - (float) Vdec;
Vpr = Vpr * 1000;
Vmili = (int) Vpr;
Помогите пожалуйста разобраться

 

Сдаётся мне, что дело не в АЦП. Если даже АЦП работает неверно, то отображались бы просто не те цифры, а никак не "кракозябрики"

Давайте кусок кода где вывод.

 

Вывод на LED:
ADMUX=0b11000000;
ADCSRA=0b11011111;

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

while(1){

if (Vdec>=10) {
Vdec=(Vdec-10);
tis++; }
else { sot=Vdec;} ; //Преобразование BCD
if (Vmili>=100) {
Vmili=(Vmili-100);
des++; }
if (Vmili>=10) {
Vmili=(Vmili-10);
ed++; }
if (r==2) {
PORTD.7=0;};

switch (razrad[r]) {
case 1 :
PORTD=digits[ed];
break;
case 2:
PORTD=digits[des];
break;
case 4:
PORTD=digits[sot];
break;
case 8:
PORTD=digits[tis];
break;
};
Вроде потому-что АЦП мереет намного больше раз в секунду чем выводится на индикатор?
Как по грамотному организовать вывод?

 

А где переключение разряда?

Ну и желательно делать это не постоянно, а с заданной частотой. Поместите вывод в таймерное прерывание например.

Что касается кода...

shemmer:
if (Vdec>=10) {
Vdec=(Vdec-10);
tis++; }
else { sot=Vdec;} ; //Преобразование BCD

, то это некорректный код. Если Vdec больше 10, то переменной sot не присваивается никакого значения. Если Vdec больше 20, то тоже неверный результат...

Надо примерно так:

sot = Vdec;
while (sot >= 10)
{
    tis++;
    sot -= 10;
}

Аналогично с Vmili.

ЗЫ. И форматируйте текст получше, сложно читать.

 

1. razrad[r] - это бегущая единица?
2. if (r==2) {PORTD.7=0;}; нужно перенести за switch.
2. Не совсем понятно - кусок расчета Vizm, Vpr и прочее - он в этом же цикле while? Если он - в прерывании, на дисплее действительно будет полный бред.

 

Да razrad[r] это бегущая еденица
Вольтмер у меня на 32 вольта
Поясняю:

int Vizm;
float Vpr;
int Vdec, Vmilli;
Vizm = ADCW;
Vpr = (float) Vizm; //переводим формат данных из целочисленно в фиксированную арифметику
Vpr = Vpr * 0.03128; //умножаем на множитель масштаба:
Vdec = (int) Vpr; //выделяем целую часть;
Vpr = Vpr - (float) Vdec; //выделяем милливольты
Vpr = Vpr * 1000; //избавляемся от целой части
Vmilli = (int) Vpr; //переводим милливольты в вольты
Теперь в Vdec число в вольтах, а в Vmili миливольты
Теперь если этот расчет поместить в прерывание АЦП то все правильно работает но 1 раз и останавоивается
Если выношу из прерывания показывает не весть что.
Как заставить мерить всегда и правильно?

 

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

1. Во время выполнения оператора if (Vdec>=10) {Vdec=(Vdec-10); tis++; }, например, между Vdec=(Vdec-10) и tis++; возникает прерывание от АЦП - почему бы и нет
2. В обработчике прерываний выполняется, в том числе, Vdec = (int) Vpr;
3. Теперь мы возвращаемся в основную программу, и теперь наш Vdec содержит уже совсем не то значение, которое было до момента возникновения прерывания. Запросто можно и 20 сотен в числе насчитать С разделяемыми ресурсами работать нужно очень аккуратно.

Вариантов решения здесь два - либо выносить вычисления из обработчика, либо внутри цикла в основной программе запрещать прерывания, на время, пока не сформируете правильно все свои ed, des, sot и т.д.

Насчет "останавливается" - попробуйте в обработчике руками сбросить флаг прерывания от АЦП. Он, по идее, должен сбрасываться сам, но кашу маслом не испортишь. Частенько помогает. Можете также перед выходом из обработчика перепрограммировать регистр управления АЦП.