Лучше быть голодным, чем есть что попало,
лучше быть одиноким, чем быть с кем попало.
Омар Хайям

Меню навигации для мобильных

Вольтамперметр, 7 сегментные индикаторы, SPI

Автор zenon, 29 Сен., 2020, 22:18

« предыдущая - следующая »

zenon

Тут вот как получается, сейчас, когда без таймера и опрос постоянный, - мне нравится как работает.
Скорость не большая, вот снял (https://www.youtube.com/watch?v=DBajoM08A6s).
freq_test.lss приложил.
https://github.com/minamonra/

Slabovik

#126
Крутится там вот это
 82:	45 9a       	sbi	0x08, 5	; 8
  84:	45 98       	cbi	0x08, 5	; 8
  86:	fd cf       	rjmp	.-6      	; 0x82 <main+0x2>
sbi-cbi выполняются за два цикла
rjmp тоже выполняется два цикла.
Итого два цикла в положении "1", четыре цикла в положении "0", всего шесть.
при 16 мегагерцах один цикл 62,5 наносекунды.
В положении "1" ожидаем 125 наносекунд, в положении "0" - 250 наносекунд. Всего 375 наносекунд, что соответствует 2,66(6)МГц частоты.
Именно это твой осциллограф и показывает.

зы: Если после SBI вставить два NOP, то на ноге будет ровный меандр, а его частота 2 МГц.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Пока оставил тот вариант как запасной.
Таймер пришлось настроить для комфортного обновления на 8 kHz (6 тоже нормально).
Тут пока некоторые моменты не так, но мне надо понять где теряются данные.
Если вот этот кусок в теле основного цикла, то работает.
// ---
        if (adc_channel==0) {
            bin_bcd(V/2); // закидываем прочитанное значение АЦП в функцию bin_bcd, результат отправляется в структуру bcd
            counter7s[3] = bcd.thousands;
            counter7s[2] = bcd.hundreds;
            counter7s[1] = bcd.tens;
            counter7s[0] = bcd.units;
        } else {
            bin_bcd(A);
            counter7s[7] = bcd.thousands;
            counter7s[6] = bcd.hundreds;
            counter7s[5] = bcd.tens;
            counter7s[4] = bcd.units;
        }
// ---
Если я его отправляю под if (clk1000hz==0) { то всё по 0000.
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>


// Переменные
typedef unsigned char byte; //создание типа - байт
byte adc_channel = 0;
byte clk1000hz = 0;
volatile uint32_t adc_oversampled[2];
#define adc_select_channel(channel) (ADMUX = (ADMUX & 0xF0) + channel)

struct { uint8_t tens,hundreds,thousands; uint16_t units; }bcd;
struct { uint8_t tens,hundreds,thousands; uint16_t units; }bcd_V;
struct { uint8_t tens,hundreds,thousands; uint16_t units; }bcd_A;
// Места знаков и точек
uint8_t const digi7mass [] PROGMEM = { 0b01111111, 0b10111111, 0b11011111, 0b11101111, 0b11110111, 0b11111011, 0b11111101, 0b11111110, 0b11011111, 0b11111101 };
// ABCEDFGH
uint8_t const segm7mass [] PROGMEM = { 0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111, 0b10000000, 0b00000000 };
uint8_t counter7s[10] = { 0,  0,  0,  0, 0,  0,  0,  0, 10, 10 };
unsigned char n_count=0;
uint16_t adc_buffer[2];

// Функции
void adc_init(void);
void timer1000hz_init(void);
void spi_init(void);

void bin_bcd(uint16_t a);
void seg7_show (void);

static uint32_t    sum[2];
static uint8_t counter[2];

uint32_t V = 0, A = 0;

#define LED PD7
#define output_low(port,pin) port &= ~(1<<pin)
#define output_high(port,pin) port |= (1<<pin)
#define set_input(portdir,pin) portdir &= ~(1<<pin)
#define set_output(portdir,pin) portdir |= (1<<pin)

// +++
int main() {
    adc_init();
    spi_init();
    timer1000hz_init();
    set_sleep_mode(SLEEP_MODE_ADC);
    sleep_enable();
    set_output(DDRC, PC5);
    sei();


    while(1) {

        if (clk1000hz==0) {
output_low(PORTC, PC5);
            clk1000hz=255;
            ADCSRA |= (1<<ADSC);                      // запуск преобразования ADC
            sleep_cpu();                              // и засыпаем, проснёмся в прерывании, после того, как результат ADC будет готов
            sum[adc_channel] += ADC;
            if (++counter[adc_channel] > 128 - 1) { 
                adc_oversampled[adc_channel] = sum[adc_channel] >> 4;
                sum[adc_channel] = 0;
                counter[adc_channel] = 0;
            }
            if (adc_channel==0) V = (adc_oversampled[1] + (V << 2) - V) >> 2;
            else                A = (adc_oversampled[0] + (A << 2) - A) >> 2;
            adc_channel = (adc_channel+1) % 2;        // следующий канал
            adc_select_channel(adc_channel+6);
output_high(PORTC, PC5);
        }
// ---
        if (adc_channel==0) {
            bin_bcd(V/2); // закидываем прочитанное значение АЦП в функцию bin_bcd, результат отправляется в структуру bcd
            counter7s[3] = bcd.thousands;
            counter7s[2] = bcd.hundreds;
            counter7s[1] = bcd.tens;
            counter7s[0] = bcd.units;
        } else {
            bin_bcd(A);
            counter7s[7] = bcd.thousands;
            counter7s[6] = bcd.hundreds;
            counter7s[5] = bcd.tens;
            counter7s[4] = bcd.units;
        }
// ---
    }
}
// +++

ISR(ADC_vect) {
// тут ничего не делаем, нужно, чтобы проснуться, (1<<ADIE)
}

void adc_init(void) {
    ADMUX  |= (0<<REFS1)|(0<<REFS0); // REFS1=0 REFS0=0 = Внешний источник, подключенный к AREF, внутренний VREF отключен
    ADCSRA |= (1<<ADIE )|(1<<ADEN )|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); // ADC_CLOCK_PRESCALER128 задаем тактовую частоту ADC (16000/128 = 125 кГц). 
    
}

void timer1000hz_init(void) {
// TIMER 1 for interrupt frequency 8000 Hz:
cli(); // stop interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1  = 0; // initialize counter value to 0
// set compare match register for 8000 Hz increments
OCR1A = 1999; // = 16000000 / (1 * 8000) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12, CS11 and CS10 bits for 1 prescaler
TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei(); // allow interrupts
}

ISR(TIMER1_COMPA_vect) {
    clk1000hz = 0; 
    seg7_show ();
}

void spi_init() {
// инициализация портов сдвигового регистра  SPI  ======================
    DDRB  |=  (1<<PB5);      // линия тактирования   clock
    DDRB  |=  (1<<PB3);      // линия данных         data
    DDRB  |=  (1<<PB2);      // линия стробирования  latch                   // или краткая запись  DDRB  |=  ((1<<PB2)|(1<<PB3)|(1<<PB5)); //ножки SPI на выход
    PORTB &= ~(1<<PB2);      // подать низ на .. линия стробирования  latch
    PORTB &= ~(1<<PB3);      // подать низ на .. линия данных         data
    PORTB &= ~(1<<PB5);      // подать низ на .. линия тактирования   clock  // или краткая запись  PORTB &= ~((1<<PB2)|(1<<PB3)|(1<<PB5)); // низкий уровень
    SPCR  =  ((1<<SPE)|(1<<MSTR)); // включим шину, объявим ведущим | Биты SPR0 SPR1 нулевыми остаются, значит, при 16 на кварце получается 4 МГц тактирование на SPI       // SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0); // Enable SPI, Set as Master, Prescaler: Fosc/16, Enable Interrupts
// ====================================================================
}


void seg7_show (void) {
    PORTB &= ~(1<<PB2);                                     // опускаем строб RDY
    SPDR   = pgm_read_byte(&digi7mass[n_count]);            // засылаем первый байт
    while(!(SPSR & (1<<SPIF)));                             // ожидаем освобождение буфера
    SPDR   = pgm_read_byte(&segm7mass[counter7s[n_count]]); // засылаем второй байт
    while(!(SPSR & (1<<SPIF)));                             // снова ждём, когда байт уйдёт из буфера
    PORTB |= (1<<PB2);                                      // поднимаем строб RDY, защёлкивая данные на выход 595
    n_count++; if (n_count>9) n_count=0;
}

// Раскладываем 9999 на цифры
void bin_bcd(uint16_t a) {
    bcd.tens=0;
    bcd.hundreds=0;
    bcd.thousands=0;
    bcd.units=a;
    while (bcd.units>=1000) {
      bcd.units-=1000;
      bcd.thousands++;
    }
    while (bcd.units>=100) {
      bcd.units-=100;
      bcd.hundreds++;
    }
    while (bcd.units>=10) {
      bcd.units-=10;
      bcd.tens++;
    }
}

... делать вторую структуру для bin_bcd?
https://github.com/minamonra/

Slabovik

Не могу увидеть причину неработы. По мне так структура нафиг не нужна (просто лишняя сущность, без которой можно прекрасно обойтись), но почему там нули - это мне не ясно. Значит, на входе процедуры BCD нули. А это A и V. Обсчитываются они странно, что-то внутри клока, остальное снаружи... Считать ведь надо один раз, а не каждый проход, уж как мне сейчас это поправить? Только переписать полностью, но мой макет не готов...

Но... почему бы не начать писать свой код, добиваясь его работоспособности, а не пытаться обрабатывать методом тыка чужой? (это я про немца). По крайней мере будешь чётко понимать, что и зачем делаешь - уже будет легче.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Так он уже почти свой.
Цитата: Slabovik от 25 Окт., 2020, 00:40Значит, на входе процедуры BCD нули. А это A и V.
Как они будут нулевыми, если я их забираю сразу после преобразования?
Мне кажется дело в сопоставлении массива counter7s[] цифрам что-то не так.
+++
Ну или пойти по другому пути, оставить одно прерывание АЦП, добавить две глобальные переменные, которые инкрементируются каждый цикл while(1), допустим:
freq7seg_update - будет отвечать за частоту обновления дисплея.
freq_adc_update - соответственно АЦП.
... при достижении нужного кол-ва - делаем то что надо, сбрасываем счёт.
Ну или к одной переменной можно привязать.
+++
Вот, ещё раз, так = все нули:
// +++
int main() {
    adc_init();
    spi_init();
    timer1000hz_init();
    set_sleep_mode(SLEEP_MODE_ADC);
    sleep_enable();
    set_output(DDRC, PC5);
    sei();


    while(1) {

        if (clk1000hz==0) {
output_low(PORTC, PC5);
            clk1000hz=255;
            ADCSRA |= (1<<ADSC);                      // запуск преобразования ADC
            sleep_cpu();                              // и засыпаем, проснёмся в прерывании, после того, как результат ADC будет готов
            sum[adc_channel] += ADC;
            if (++counter[adc_channel] > 128 - 1) { 
                adc_oversampled[adc_channel] = sum[adc_channel] >> 4;
                sum[adc_channel] = 0;
                counter[adc_channel] = 0;
            }
            if (adc_channel==0) V = (adc_oversampled[1] + (V << 2) - V) >> 2;
            else                A = (adc_oversampled[0] + (A << 2) - A) >> 2;
            adc_channel = (adc_channel+1) % 2;        // следующий канал
            adc_select_channel(adc_channel+6);


// ---
        if (adc_channel==0) {
            bin_bcd(V/2); // закидываем прочитанное значение АЦП в функцию bin_bcd, результат отправляется в структуру bcd
            counter7s[3] = bcd.thousands;
            counter7s[2] = bcd.hundreds;
            counter7s[1] = bcd.tens;
            counter7s[0] = bcd.units;
        } else {
            bin_bcd(A);
            counter7s[7] = bcd.thousands;
            counter7s[6] = bcd.hundreds;
            counter7s[5] = bcd.tens;
            counter7s[4] = bcd.units;
        } 
// ---



output_high(PORTC, PC5);
        }  //// if (clk1000hz==0)

    } //// while(1)
} //// int main()
А вот так всё работает:
    while(1) {

        if (clk1000hz==0) {
output_low(PORTC, PC5);
            clk1000hz=255;
            ADCSRA |= (1<<ADSC);                      // запуск преобразования ADC
            sleep_cpu();                              // и засыпаем, проснёмся в прерывании, после того, как результат ADC будет готов
            sum[adc_channel] += ADC;
            if (++counter[adc_channel] > 128 - 1) { 
                adc_oversampled[adc_channel] = sum[adc_channel] >> 4;
                sum[adc_channel] = 0;
                counter[adc_channel] = 0;
            }
            if (adc_channel==0) V = (adc_oversampled[1] + (V << 2) - V) >> 2;
            else                A = (adc_oversampled[0] + (A << 2) - A) >> 2;
            adc_channel = (adc_channel+1) % 2;        // следующий канал
            adc_select_channel(adc_channel+6);
output_high(PORTC, PC5);
        }  //// if (clk1000hz==0)

// ---
        if (adc_channel==0) {
            bin_bcd(V/2); // закидываем прочитанное значение АЦП в функцию bin_bcd, результат отправляется в структуру bcd
            counter7s[3] = bcd.thousands;
            counter7s[2] = bcd.hundreds;
            counter7s[1] = bcd.tens;
            counter7s[0] = bcd.units;
        } else {
            bin_bcd(A);
            counter7s[7] = bcd.thousands;
            counter7s[6] = bcd.hundreds;
            counter7s[5] = bcd.tens;
            counter7s[4] = bcd.units;
        } 
// ---



    } //// while(1)
} //// int main()
https://github.com/minamonra/

Slabovik

Нельзя использовать ADC Noise Reduction асинхронно с таймером обновления экрана. Потому что таймер во время Noise Reduction останавливается. Если это игнорировать, будет неодинаковая засветка разрядов.  Именно поэтому ведущим объявляется таймер обновления сегментов, а запуск ADC встраивается внутрь его циклов, благо время позволяет с большим запасом. Если так не делать, то либо отказаться от Noise Reduction, либо от динамической индикации средствами процессора.
И просмотри внимательно точки с запятыми и скобки в if'ах. Там мне как раз в место подсчёта неоднозначность какая-то рисуется. Лучше лишнюю скобку поставить, чем гадать, что выполнится, а что нет...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Цитата: Slabovik от 25 Окт., 2020, 01:20благо время позволяет с большим запасом.
Ну как же хватает? Я пробовал выполнять оверсэмплинг с частотой 1 кГц, а после обновлять сегменты, - не хватает.
Ну или я что-то делаю не так.
Пойду думать... :)
https://github.com/minamonra/

Slabovik

Ну, вполне хватает. Частота тактирования ADC 125 кГц, если делитель не врёт. Получение результата замера с ADC занимает 13 тактов - это 105 микросекунд. Посчитать фуфер - ну ещё сотня микросекунд (твой осциллограф это прекрасно подстверждает). Выгрузить в SPI - ну пусть 50 микросекунд. А таймер тикает каждые 1000 микросекунд. Где же не хватает? Хватает и с солидным запасом.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#133
Тут вот какая штука вылилась - начал переписывать заново.
Переписал только для сегментов.
Вот такой код вышел.
#include <avr/io.h>
#include <avr/interrupt.h>
typedef unsigned char byte;

void spi_init(void);
void spi_send_bytes(byte S1, byte S2);
void timer0_init(void);

uint8_t const place7array[] = {
0b11111110,
0b11111101,
0b11111011,
0b11110111,
0b11101111,
0b11011111,
0b10111111,
0b01111111
};

// ABCEDFGH
uint8_t const segmt7array[] = {
0b00111111, 
0b00000110, 
0b01011011, 
0b01001111, 
0b01100110, 
0b01101101, 
0b01111101, 
0b00000111, 
0b01111111, 
0b01101111, 
0b10000000, 
0b00000000 };

byte clk1000hz = 0;
byte count7 = 0;
// +++
int main() {
    spi_init();    //
    timer0_init();  // 
    sei();          // разрешить прерывания

    while(1) {
        if (clk1000hz==0) {
            clk1000hz==255;

            spi_send_bytes ( place7array[count7], segmt7array[0]);
            count7++; if (count7 > 9) count7 = 0;

        } //+ if (clk1000hz == 0)
            

    } //+ while(1)

} //+ int main()
// +++

void spi_init(void) {
    DDRB  |=  (1<<PB2)|(1<<PB3)|(1<<PB5);  // RDY, Data, Clock //Set control pins as outputs
    PORTB &= ~(1<<PB2)|(1<<PB3)|(1<<PB5);  // RDY, Data, Clock //Set control pins low
    SPCR  =  ((1<<SPE)|(1<<MSTR));          // включим шину, объявим ведущим //Start SPI as Master
    PORTB &= ~(1<<PB2);                    // Pull LATCH low (Important: this is necessary to start the SPI transfer!)
}

void spi_send_bytes(byte S1, byte S2) {
    PORTB &= ~(1<<PB2);        // опускаем строб RDY
    SPDR = S1;                  // отсылаем байт
    while(!(SPSR & (1<<SPIF))); //
    SPDR = S2;                  // отсылаем второй
    while(!(SPSR & (1<<SPIF))); //
    PORTB |=  (1<<PB2);        // поднимаем строб RDY, защёлкивая данные на выход 595
}

void timer0_init(void) { // TIMER 0 for interrupt frequency 1000 Hz:
    TCCR0A = 0; // set entire TCCR0A register to 0
    TCCR0B = 0; // same for TCCR0B
    TCNT0  = 0; // initialize counter value to 0
    OCR0A = 249; // = 16000000 / (64 * 1000) - 1 (must be <256)          // set compare match register for 1000 Hz increments
    TCCR0B |= (1 << WGM01);  // turn on CTC mode
    TCCR0B |= (0 << CS02) | (1 << CS01) | (1 << CS00);      // Set CS02, CS01 and CS00 bits for 64 prescaler
    TIMSK0 |= (1 << OCIE0A);    // enable timer compare interrupt
}

ISR(TIMER0_COMPA_vect) { //interrupt commands for TIMER 0 here
    clk1000hz = 0;
}
По идее на всех индикаторах должны быть нули.
Заливаю = ноль только на первом!
Крутил и так и так, короче вывел он меня, бросил, потом подошёл
typedef unsigned char byte;
и помянуя о "ты не поверишь! (lss)"поменял на
typedef volatile unsigned char byte;
и оно заработало = нули на всех, ну ладно, дай уберу volatile = убрал, и оно продолжает работать как надо, ходя заливал по нескольку раз....
Ну я в Makefile смотрю
Optimization level, can be [0, 1, 2, 3, s]. 0 turns off optimization.
У меня стоит s....  :'(
https://github.com/minamonra/

Slabovik

volatile - это способ отвадить компилятор от попыток "оптимизировать" переменную. Если volatile нет, то переменная может просто исчезнуть в недрах оптимизации, когда компилятор посчитает, что с ней никто и не работает.
Вот, по-быстрому ликбезик нашёл: http://microsin.net/programming/avr/how-to-use-volatile-in-c.html

Кстати, c "if" возможно надо было попробовать не действие "~" (neg), а условие "!" (no). Есть подозрение, что действие в качестве условия не прокатило. Хотя х.з. на самом деле.

зы: ещё раз упомяну, что я не знаток Си и для меня эти сишные выкрутасы самому в диковинку.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Ну вот нюансы Си я сам только познаю.
Хотя я хорошо знаю, что, например при установке Gentoo в Makefile  (https://wiki.gentoo.org/wiki/GCC_optimization/ru)с оптимизацией gcc шутить не надо, те обычно это -O2, а я влепил -Os.
Цитата: undefined-Os: На этом уровне код будет оптимизирован по объему. Он активирует все параметры -O2, которые не приводят к увеличению размера генерируемого кода. Он может быть полезным на компьютерах, которые обладают чрезвычайно ограниченным пространством жесткого диска и/или процессоры с небольшим размером кэша.
Макет жирный такой! :)
ы. У меня тут вопрос как накатить маску на байт цифры для точки?
https://github.com/minamonra/

Slabovik

Там не маску надо, а за-OR'ить символ точки с символом знакоместа, в котором точка нужна (ну или через &, если отображение инверсное, т.е. когда "0" в порту - это "горит").

Вычисляешь BCD, сопоставляешь этому BCD 7-сегментный код, пока без точки, кладёшь его в буфер вывода (из которого SPI будет брать байты для выдвигания). К позиции буфера, в котором нужна точка, добавляешь операцию типа

dig_for_out[n] = dig_for_out[n] | symbol_DP

где symbol_DP - это код точки для семисегментника (у тебя оно вроде 11-я позиция по
списку, после девятки). Названия вымышлены с целью адаптации для понимания :)

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

Процик засуну в дип-корпусе, чисто ради удобства паяния проводами. Ну, а схема приближена к тому, что я рисовал (у тебя по сути аналогично, только 595-х вроде две). Только добавил два индикатра-столбика, вывод в них сделаю 5-м символом. Есть мыслишка по поводу "мельтешащих цифр"...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Ага, спс, сделал вот так, точки работают.
    if ((count7 == 1 ) || (count7 == 4)) 
         SPDR = S2 |= 0b10000000; 
    else SPDR = S2;                  // отсылаем второй, дорисовав точки где надо
Да, на этом макете у меня две 595, есть второй с тремя, но я уж сделаю попозже как на схеме.
https://github.com/minamonra/

zenon

#138
А сколько значений достаточно для оверсэмплинга?
Вот я попробовал 16 суммировать:
            if (++counter[adc_channel] > 16 - 1) {
                adc_oversampled[adc_channel] = sum[adc_channel] >> 1;
                sum[adc_channel] = 0;
                counter[adc_channel] = 0;
            }
Тогда, при условии выполнения опроса 1 кГц, скорость обновления показаний достаточная.
Если 32 - ещё более менее, а уже 64 и 128 надо кидать либо в основной цикл программы либо ещё один таймер задействовать.
Залил код с 16-тью сэмплами и сглаживанием, понаблюдаю как будет себя вести.
ы. И вопрос к линейности, не претендую на точность, но если настраиваю на 20 вольт, то при уменьшении до менее 1 вольта расхождение в один-два знака, например 20 точно, 0.23 у меня, 0,24 на мультиметре.
https://github.com/minamonra/

Slabovik

Минимально-достаточно 16, подстраховаться - 32.
При 16 сдвигать вправо нужно на 2 (>>2), при 32 - на 3.
Не забыть добавить (забыто) перед сдвигом 1/2 (половину) от количества просуммированных отсчётов (т.е. 8 для 16 или 16 для 32)
             if (++counter[adc_channel] > BufLength - 1) {
                adc_oversampled[adc_channel] = (sum[adc_channel] + (BufLength >> 1) ) >> 2;
                sum[adc_channel] = 0;
                counter[adc_channel] = 0;
            }
BufLength определить в самом начале как глобальную установочную константу типа unsigned char (т.е. один беззнаковый байт). Можно две - на каждый канал свою. Это потому что в общем случае глубина буфера у каналов не обязана быть одинаковой.

В результате adc_oversampled содержит величину, которую нужно "скормить" Bin-2-BCD. Уже без всяких делений - это готовый результат измерений.

adc_oversampled и sum - двубайтовые величины (не помню, вроде unsigned short int - проверь сам). Двубайтовая обрабатывается быстрее, чем просто int (он четырёхбайтовый - проверь тоже, вдруг нет). Двубайтовыми они могут быть до глубины буфера включая 64 (проверка: 03FFh * 64dec = 0FFC0h, 0FFFFh-0FFC0h=3Fh (3Fh=63dec)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Цитата: Slabovik от 26 Окт., 2020, 20:39При 16 сдвигать вправо нужно на 2 (>>2), при 32 - на 3.
Это, если не делить на два. А так да при 16 сдвигаю на 2 получаю сразу результат в напряжении нужный, это уже понял :)
Цитата: Slabovik от 26 Окт., 2020, 20:39Не забыть добавить (забыто) перед сдвигом 1/2 (половину) от количества просуммированных отсчётов (т.е. 8 для 16 или 16 для 32)
Сорри, эту фразу не могу распознать. ::)
https://github.com/minamonra/

Slabovik

1/2 нужна для правильного усреднения результата. Потому что всё, что меньше 0,5 - это 0. Все, что 0,5 и больше - это 1.
Если 0,5 не добавлять, то нулём будет даже 0,9. Раздирали здесь (https://anklab.ru/forum/index.php?msg=286)

Делить на два при подсчёте BCD - совершенно не нужная операция, размазывающая процедуру сдвига вправо по нескольким местам. Зачем? Подсчитать нужно всё в одном месте.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Те:
if (++counter[adc_channel] > 16 - 1) {
                adc_oversampled[adc_channel] = (sum[adc_channel] + 8) >> 3;
Ноль тогда не ноль, а 0,02.
https://github.com/minamonra/

Slabovik

Это уже вопрос к ОУ. Или реализации схемотехники. Можно попробовать проверить, закоротив ногу нужного входа ADC на землю (там по схеме можно прямо закоротить, вреда не будет).
Возможно это то, чего я и опасаюсь - несколько милливольт на выходе ОУ, которые удалить можно только переводом ОУ на собственное отдельное питание с минусовым напряжением хотя бы на 0,2 вольта ниже, чем Gnd...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Так это я сразу сделал = 0,02 при АЦП на землю.
Если включить при замкнутом АЦП (или входа пробовал и так и так) вниз, сначала нули, при первом проходе вверх, вниз уже ноля нет.
https://github.com/minamonra/

Slabovik

А что такое "проход вверх"?
Но при старте 0 есть. Значит всё считает правильно. Можно попробовать отключить дизер, посмотреть, что изменится. Проконтролировать милливольтметром, осциллографом, что там на ноге АЦП.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Цитата: Slabovik от 26 Окт., 2020, 22:28А что такое "проход вверх"?
Поднятие напряжения.
Дизер отключал, - не влияет.
https://github.com/minamonra/

Slabovik

Поднятие напряжения - чем? Резистор - одно, ОУ - другое, закоротка - третье. Я же точно сказал, что именно сделать, чтобы можно было думать дальше...
Вангую, что при закоротке входа АЦП (физически, проволочкой на ногу Gnd), показания будут стабильно 0. Если я прав, то всё дело в аналоговой части - она не может обеспечить 0 на своём выходе.
Проверяется это также милливольтметром (мультиметром) либо осциллографом (это хуже т.к. у цифровых чувствительность мала).
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Дело не в аналоговой.
Коротил и то и то, ведёт себя одинаково.
ы. На самом деле меня уже такая работа вольтметра уже устраивает.
Сейчас, когда отключал дизер заметил, что работает хуже, то-ли плавать то-ли дёргаться показания начали.
С током сейчас осталось разобраться, как лучше считать, у меня шунт 0,05 делитель 15k/1k.
https://github.com/minamonra/

Slabovik

#149
Тогда я не знаю. При нуле на АЦП и верной программе там должен быть чёткий 0. Нуля нет.
Хотя... ноль-то есть, при старте. Не хочешь ли ты сказать, что при старте формула по-другому считает?  :o
Надо проверять всё. Гадать невозможно. Всё и досконально. Сколько милливольт на входе АЦП при выкрученной в "0" ручке? Чтобы показывало 0,02, должно быть не менее 6 (шести) милливольт. Они получаются легко и непринуждённо при применённой схемотехнике и ОУ, у которых только напряжение смещения может достигать четырёх милливольт.

p.s. А это ты ЗАЧЕМ ОСТАВИЛ?  :o  :-\
if (adc_channel==0) V = (https://anklab.ru/forum/index.php?msg=409[1] + (V << 2) - V) >> 2; // сглаживаем V
            else                A = (adc_oversampled[0] + (A << 2) - A) >> 2; // сглаживаем A
Зачем таскать всюду код, который не понятно как работает? Уж столько раз говорил про это, но ведь нет - это ВСЁ там, и эта формула, и совершенно неуместное деление на двойку в BCD  :'(

Сглаживание УЖЕ есть, без вот это й херни... Вот эта фигня (а занимается эта фигня тем, что даёт якобы "плавное приближение цифр") и даёт этот неустранимый остаток, плюсом таща за собой затормаживание с выдачей верных показаний.

Суть этой "немецкой" формулы в том, чтобы плавно приближать показания при изменении. Но это дико тормозит выдачу верных показаний, наблюдающий вынуден ждать много проходов, прежде чем показания станут близки к верным. А "забыв" откинуть "мусорные" биты от суммы, недоделив, начав работать с ними (а ты именно работаешь с мусосом, "недосдвинув" ту adc_oversampled), получаешь соответствующий мусор, видимый при околонулевых значениях. Ну и получается, "на колу висит мочало"... Уж извини, я крепко расстроен...

Убрать это нафиг и таки попытаться сделать так, как говорил, ну хотя бы здесь (https://anklab.ru/forum/index.php?msg=409)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.