29 Нояб., 2021, 13:34

Настоящий друг -- это человек, который выскажет тебе в глаза все, что о тебе думает, а всем скажет, что ты -- замечательный человек.

Омар Хайям


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

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

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

Slabovik

Не синхронизированы запуски ADC и отображения. ADC по факту бегает сам по себе, хотя и в режиме Noise Reduction.
Нужно: 2 режима Sleep. Перед запуском ADC нужно установить режим "ADC Noise Reduction", а по выходу из него нужно установить режим "Idle", снова впасть в Sleep (уже как Idle), теперь уже дожидаясь прерывания от таймера (перед Seg7_show), и продолжить уже показом цифр. Это если не менять тут по ходу написанного.
Мне ещё вот не ясно. Bin2BCD вызывается каждый раз, когда меняется показываемая цифра. Собственно, а зачем? Его надо вызывать один раз, когда окончен расчёт буфера ADC. Соответственно, BCD надо где-то похранить (памяти-то полно), сделав им массивчик (или он уже есть?). Итого будет 128 опросов АЦП (по текущему состоянию счётчиков), 16 показов индикатора, одно преобразование BCD. Экономия существенная (хотя и незаметная - всё-равно проц стоит, ждёт прерывания... да...).

Расчёты немца сне не очень понятны. Если ты ихъ прокомментируешь, может, будет лучше. А пока мне неясно, для чего он вычитает

V = (adc_oversampled[1] + (V << 2) - V) >> 2;
A = (adc_oversampled[0] + (A << 2) - A) >> 2;


а потом ещё ты делишь

seg7_show ( V/2, A);

когда можно сразу

V = (adc_oversampled[1] + (V << 2) - V) >> 3;

И ещё неясное место. Цифр вроде 8. А зачем

n_count++; if (n_count>9) n_count=0;
}


? Почему 9?
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Цитата: Slabovik от 23 Окт., 2020, 19:09что R16, R17 и AGnd смотрят в три разные точки
А они должны быть на Agnd?
bin2bcd вызывается только перед отправкой данных по SPI.
Цикл основный программы молотит, а if (~clk1000hz) при изменении переменной clk1000hz выполняется опрос ADC.
Переменную clk1000hz изменяет прерывание таймера.
Цитата: Slabovik от 23 Окт., 2020, 20:38И ещё неясное место. Цифр вроде 8. А зачем
Точки же. :)
Немца преобразования мне самому до конца не понятны..  :o
Цитата: Slabovik от 23 Окт., 2020, 20:38когда можно сразу
Это было временное.

Slabovik

23 Окт., 2020, 20:57 #102 Последнее редактирование: 23 Окт., 2020, 21:15 от Slabovik
Ну да, я как раз про это. Отправка по SPI отдельная для каждой цифры. Вызывается 8 раз для всего индикатора. А Bin2BCD нужно считать хотя бы только для индикатора в целом. А ещё лучше только один раз - по результатам обсчёта ADC
Цитата: zenon от 23 Окт., 2020, 20:46Цикл основный программы молотит, а if (~clk1000hz) при изменении переменной clk1000hz выполняется опрос ADC.
А это реально работает? Выведи, как я советовал, в свободный пин какого-нибудь порта (то же D) единичку перед вызовом ADC и нулик сразу после окончания работы ADC. Осциллографом можно будет посмотреть, есть ли там 1000 Гц. Мне кажется, что сейчас в условии какое-то неправильное выражение и работает не так, как задумано (а именно, 500 мкс цикл "чешет мимо условия", а вторые 500 мкс гоняет АЦП в хвост и гриву). Лично я бы предпочёл SystemTick байтовой величины, накладывал бы на неё маску и по результатам выбирал и канал, и цифру индикатора.
Цитата: zenon от 23 Окт., 2020, 20:46Точки же.
Точки - это 8-й бит в байте цифры. Передаётся вместо с цифрой в нужном разряде. Зачем 9? 8-)

Я полагаю, что при 128 отсчётах надо
V = (sum[adc_channel] + 64) >> 5;
конечно, это нужно делать в момент, когда счётчик достиг 128 (расчёт окончен).
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Передачу данных на сегменты писал выдумывал сам, она работает, да может не правильно, посоветоваться не с кем было. :)
Счёт от 0, всего 10, 8-цифры и две последние точки.
Для этого был сделан:
uint8_t counter7s[10] = { 0,  0,  0,  0, 0,  0,  0,  0, 10, 10 };
if (~clk1000hz) - работает, я uart'ом проверял, переменную какую-то дополнительно, осциллографом попробую.
Ещё раз bin2bcd считает только для индикатора, тут лишнего точно не вижу.

Slabovik

23 Окт., 2020, 21:17 #104 Последнее редактирование: 23 Окт., 2020, 21:35 от Slabovik
Ты имеешь в виду точки - двоеточие для часов? Дык ведь оно не используется.
Цитата: zenon от 23 Окт., 2020, 21:13bin2bcd считает только для индикатора
Оно считает только одну цифру? Или сразу все? Показываешь-то ты только одну...

UART'ом нельзя проверить - он слишком медленен. Условие (~clk1000hz) работает каждый раз, когда оно ~true, а  не true (т.е. false) оно каждый второй тик таймера. Пока оно false - гоняется АЦП по кругу всего цикла по принципу "а сколько успеет", а потом, когда таймер его перекидывает в true, АЦП вовсе не запускается, главный цикл гоняется впустоту. А гонять нужно один раз за один тик. Исправить можно, если clk1000 перекидывать в первоначальное состояние после (перед) работой АЦП. Тогда каждый тик таймера будет его "взводить", а запуск АЦП "спускать". Будет один запуск АЦП на тик.
ЦитироватьА они должны быть на Agnd?
Собственно, земля там едина, но вот если по ней побежать - не совсем, ибо проводник длинный и имеет сопротивление. Но для тестовой платы, повторюсь, это скорее всего не имеет значения. В продакшн такое нельзя - выводить надо точнее (поэтому, потому я за двуслойные платы, там это делать много проще, чем на однослойке)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Вот смотри тут тикнули в одну сторону 0/255:
ISR(TIMER1_COMPA_vect) {
    clk1000hz = ~clk1000hz; // тик-так
}
При следующем - в другую.
А тут if (~clk1000hz) при любом изменении сработает.
Плата двусторонняя, но с землей намудрил однозначно. Переходы запаяны кусками выводов.
Слишком медленное замеряю счетчиками, даже светодиодом можно проверить быстрые вещи.

zenon

Эммм, имеешь ввиду вот это разбить на то, чтобы только один раз тут bin2bcd вызывалась?
void seg7_show (uint16_t x, uint16_t y) {

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

...
...
...
}

Сейчас подумаю как.
.... хотя не знаю как...

Slabovik

23 Окт., 2020, 22:05 #107 Последнее редактирование: 23 Окт., 2020, 22:30 от Slabovik
Да.
0 - это False
255 (точнее всё, что не 0) - это true
Таймер у тебя перекидывает значение переменной : -false-true-false-


Проверка if на условие (~clk1000hz) по факту означает, что then выполняется, когда clk1000hz имеет значение false
А теперь смотри. У тебя "then" выполнялось, скобочки закрылись. clk1000hz как-то  изменяла своё значение? По-моему, нет, я не вижу в коде. Проверка в if значение clk1000hz не меняет (это же только проверка). Это значит, что согласно while(1) тут же начнётся, без всяких вариантов и каких-либо ожиданий, снова этот этот же if и снова с точно этим же условием... А нужно, чтобы он дождался, пока таймер перекинет clk1000hz в противоположное значение...

Надо так
while(1) {

      if (~clk1000hz) {
        clk1000hz = ~clk1000hz;    // вот это остановит безумную гонку АЦП до следующего тика таймера
        ADCSRA |= (1<<ADSC);
при этом у условии что ~clk, что просто clk - результат будет один. Таймер перекинет clk в одну сторону, первый же if (clk) перекинет его обратно, выполнив условие только один раз до следующего таймера...

Bin2BCD скорее всего саму редактировать не надо. Надо убрать её вызов из SegmentShow
bin_bcd([font=Arial Black]V[/font]); // закидываем прочитанное значение АЦП в функцию bin_bcd, результат отправляется в структуру bcd
    counter7s[3] = bcd.thousands;
    counter7s[2] = bcd.hundreds;
    counter7s[1] = bcd.tens;
    counter7s[0] = bcd.units;
   
    bin_bcd([font=Arial Black]A[/font]);
    counter7s[7] = bcd.thousands;
    counter7s[6] = bcd.hundreds;
    counter7s[5] = bcd.tens;
    counter7s[4] = bcd.units;
и вставить туда, где

if (++counter[adc_channel] > 128 - 1) { сюда }

На выходе BCD у тебя циферки BCD уложенные в индексированный массив "counter7s". Следом, уже вне этого условия, у тебя SegmentShow берёт очередную циферку по индексу (нормер разряда индикатора) и дует его в SPI. А counter7s, однажды посчитанный по результатам обсчёта буфера ADC, стоит как вкопанный до получения следующего результата от фуфера ADC, что случается только один раз из 128 проходов clk1000hz

Кстати, сюда же, в скобки нужно и подсчёт V и A вставить, а то они тоже считаются с каждым проходом, что совершенно не нужно. Заодно и промежуточное adc_oversampled можно будет забыть (а может и оставить, надо только придумать, зачем)

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

zenon

23 Окт., 2020, 22:53 #108 Последнее редактирование: 23 Окт., 2020, 23:17 от zenon
По порядку.
Вот кусок кода 1.
    while(1) {

      if (~clk1000hz) {
  output_low(PORTD, LED);

        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;
        }
        V = (adc_oversampled[1] + (V << 2) - V) >> 2;
        A = (adc_oversampled[0] + (A << 2) - A) >> 2;
        seg7_show ( V/2, A);
        adc_channel = (adc_channel+1) % 2;        // следующий канал
        adc_select_channel(adc_channel+6);
   
       
    output_high(PORTD, LED);
      }
    }
После if добавил переброс ноги вниз, в конце вверх.
Кусок кода 2.
    while(1) {

      if (~clk1000hz) {
  output_low(PORTD, LED);

        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;
        }
        V = (adc_oversampled[1] + (V << 2) - V) >> 2;
        A = (adc_oversampled[0] + (A << 2) - A) >> 2;
        seg7_show ( V/2, A);
        adc_channel = (adc_channel+1) % 2;        // следующий канал
        adc_select_channel(adc_channel+6);
   
    clk1000hz = ~clk1000hz;
   
    output_high(PORTD, LED);
      }
    }
Где добавил clk1000hz = ~clk1000hz; в конце then.
Ну и картинки с этой ноги. По-моему ничего не изменилось?
Только вот почему частота под 5 kHz???

Slabovik

И теперь внимание: во время работы ADC ты ногу опускаешь, по окончании поднимаешь. Первое: почему у тебя ожидание (нога поднята) такое короткое? А опущенна нога 200 с небольшим микросекунд - это время работы АЦП + расчёт. При 125 кГц 13 циклов работы АЦП занимают примерно 105 микросекунд, ещё сотня - расчёты да выпих в SPI.
Из всего получается, не работает таймер. Совсем. Если бы он работал, преобладала бы поднятая нога и период был бы порядка 1100-1200 мксек, а не 200 с небольшим, как сейчас.

Делай:
1. внутри прерывания таймера clk1000hz++ (увеличение на 1)
2. внутри while(1)
{
seg7show (clk1000hz & 3) // номер цифры индикатора от 0 до 7

set_sleep_mode(SLEEP_MODE_ADC);
sei()

if ( (clk1000hz & 1) = 0)
   then { чтение ADC канал V;
          if (counterV=counterVmax)
             then { расчёт нового V;
                    преобразование V в BCD; // в массив Counter7S
                    counterV=0
                  }
             else {counterV=counterV+1;
                  }

        }
   else { чтение ADC канал A
          if (counterA=counterAmax)
             then { расчёт нового A;
                    преобразование A в BCD; // в массив Counter7S
                    counterA=0;
                  }
             else {counterA=counterA+1;
                  }
        };

set_sleep_mode(SLEEP_MODE_IDLE); // уточни, как правильно написать, но вроде так
sei(); // а не забыл ли ты SEI на выходе из прерывания по таймеру? А ведь надо. Вставим здесь.
sleep_cpu();
} // конец главного цикла While - прыг на начало, как только случится прерывание по таймеру (или какое-нибудь другое, если вдруг оно будет настроено).
Я не уточняю, какие именно буквы писать, я показал общий алгоритм, как оно должно работать.

Seg7show надо поправить. У тебя есть преобразованный массив bcd цифр для показа (counter7s), он глобальный, на входе Seg7Show нужен только индекс цифры, которая предназначена к показу.

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

zenon

Хммм.
Сделал.
    while(1) {

      if (~clk1000hz) {
        clk1000hz = ~clk1000hz;
output_low(PORTD, LED);

        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) {
                    bin_bcd(adc_oversampled[1]); // закидываем прочитанное значение АЦП в функцию bin_bcd, результат отправляется в структуру bcd
                    counter7s[3] = bcd.thousands;
                    counter7s[2] = bcd.hundreds;
                    counter7s[1] = bcd.tens;
                    counter7s[0] = bcd.units;
                } else {
                    bin_bcd(adc_oversampled[0]);
                    counter7s[7] = bcd.thousands;
                    counter7s[6] = bcd.hundreds;
                    counter7s[5] = bcd.tens;
                    counter7s[4] = bcd.units;
                }
         
        }
        seg7_show ( V, A);
        adc_channel = (adc_channel+1) % 2;        // следующий канал
        adc_select_channel(adc_channel+6);
      }
    }
Но это не решило проблему, болтанка только увеличилась.
+++++++
Нашёл!
Я тут экспериментировал с этим таймером, который 1000Гц.
Там есть строка:
OCR1A  = 125 - 1;                            // установка регистра совпадения  250 - 1;  /// если надо например 2000 Гц, меняем на 125 - 1Цифру 125 заменил на 250 = всё! болтанка (дребезг) ушла!
Но вопрос по частоте остаётся, сейчас она 8,47 kHz на ноге контроллера.

Slabovik

Ну, потому что таймер таки не работает...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Так, ну я окончательно запутался тогда.
Вот крайний мой вариант, изменил всё как ты сказал.
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
//#include <util/delay.h>
#include <avr/sleep.h>
//#include "uartz.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;
// Места знаков и точек
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);
// void seg7_show (uint16_t x, uint16_t y);

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(DDRD, LED);
    sei();

    while(1) {

      if (~clk1000hz) {
clk1000hz = ~clk1000hz;
output_low(PORTD, LED);

        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;
       
                V = (adc_oversampled[1] + (V << 2) - V) >> 2;
                A = (adc_oversampled[0] + (A << 2) - A) >> 2;
                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;
                }
         
        }

        seg7_show ();
        adc_channel = (adc_channel+1) % 2;        // следующий канал
        adc_select_channel(adc_channel+6);

output_high(PORTD, LED);
      }
    }
}
// +++

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 кГц).
    //DIDR0  |= (1<<ADC1D)|(1<<ADC0D); // disable digital inputs on ADC pins
}

void timer1000hz_init(void) {
// настройка прерываний по таймеру 1000 Гц =============================
    TCCR1A  = 0; TCCR1B = 0;                      // установить регистры в 0
    OCR1A  = 250 - 1;                            // установка регистра совпадения  250 - 1;  /// если надо например 2000 Гц, меняем на 125 - 1
    TCCR1B |= (1 << WGM12);                        // включить CTC режим
    TCCR1B |= (1 << CS11); TCCR1B |= (1 << CS10);  // включить делитель /64
    TIMSK1 |= (1 << OCIE1A);                      // включить прерывание по совпадению таймера
}

ISR(TIMER1_COMPA_vect) {
    clk1000hz = ~clk1000hz; // тик-так
}

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++;
    }
}

И сейчас это работает хорошо.

Slabovik

Помедитируй-таки над алгоритмом с предыдущей страницы (белый такой). Именно его я изобразил парой постов выше. Должно работать, только нужно немного переработать процедуру show7seg - она должна в качестве параметра получать номер показываемой цифры, которые она и так берёт из массива, куда любезно их уложил Bin2BCD. Только в "белом алгоритме" позиций индикатора у меня 4 (я так задумывал), а у тебя все восемь (если я не ошибаюсь)

Также, если пока не ломать, попробуй добавить sei(); на выходе из прерывания по таймеру, а также на выходе из прерывания от ADC. Я не знаю, добавляет ли Си автоматом это, или нет. ВРоде в бинарном коде я не видел SEI на выходе из прерывания. А если нет SEI, значит, прерывания не работают (работают, но только один раз).
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Цитата: Slabovik от 23 Окт., 2020, 22:05Сейчас вспомнил... у меня же ардуина есть и бредбоард. Вполне могу собрать макетку... Правда, она поганая будет по части аналоговой разводки, что нуль там крайне маловероятен, но можно отлаживать.
Фьюзы даже менять не надо, только бутлоадер затрётся.
Архив с makefile.
Все что надо - geany и две команды:
make all
make flash
:)
Добавлю, на всякий архив проекта кикад, земля там не правильная :)

zenon

Цитата: Slabovik от 23 Окт., 2020, 23:50Ну, потому что таймер таки не работает...
Ну как же не работает?
Вот есть генератор кода.
Взял кусок оттуда:
// TIMER 1 for interrupt frequency 1000 Hz:
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 1000 Hz increments
OCR1A = 15999; // = 16000000 / (1 * 1000) - 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);
Ровным счётом ничего не изменилось, работает хорошо, на осцилорафе с ноги LED = 8,4 kHz.
sei(); пробовал добавлять .... не.
Попробую другие таймеры ещё.

Slabovik

А, вот ещё. clk1000hz объявление и инициализация где? В процедуре TimerInit ей надо что-то присвоить. "Что-то" - это 0. Потому что в бинарном коде там ерунда00000118 <__vector_11>:
 118: 1f 92        push r1
 11a: 0f 92        push r0
 11c: 0f b6        in r0, 0x3f ; 63
 11e: 0f 92        push r0
 120: 11 24        eor r1, r1
 122: 8f 93        push r24
 124: 80 91 0b 01 lds r24, 0x010B
 128: 80 95        com r24
 12a: 80 93 0b 01 sts 0x010B, r24
 12e: 8f 91        pop r24
 130: 0f 90        pop r0
 132: 0f be        out 0x3f, r0 ; 63
 134: 0f 90        pop r0
 136: 1f 90        pop r1
 138: 18 95        reti
строка 128 - команда на комплементарное преобразование. У тебя clk по факту знаковое!
Надо: а) объявить clk беззнаковым; б) при инициализации присвоить 0. Поскольку при старте проца в ОЗУ может находиться какой-нибудь мусор от прошлых запусков. Да и вообще взять за правило начальную инициализацию переменных.

Да, команды SEI я тут не вижу. Она должна быть перед RETI (если конечно не задумывалось сто-нибудь другое). Вход в прерывание сбрасывает флаг EI, запрещая другие прерывания совсем. Восстанавливать флаг нужно вручную - это программист должен заботиться.
Макет я всё-равно быстро не соберу. На выходные снова в поля, помёрзну малость (у нас уже минус, снег вон не тает).

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

zenon

Цитата: Slabovik от 24 Окт., 2020, 00:24clk1000hz объявление и инициализация где?
В самом начале:
// Переменные
typedef unsigned char byte; //создание типа - байт
byte adc_channel = 0;
byte clk1000hz = 0;
Цитата: Slabovik от 24 Окт., 2020, 00:24На выходные снова в поля, помёрзну малость (у нас уже минус, снег вон не тает).
Путаюсь, поля - это дача? Или велопрогулки?
Мы ещё в лёгких курточках, сегодня +18, на солнце даже жарко было.
;)

Slabovik

А, это я проглядел. Ну, значит, здесь порядок - есть инициализация. Хотя с другой стороны, в .lss я не обнаружил, чтобы в эту ячейку что-то писалось до входа в main... Правда, там какой-то блок из памяти программ в ОЗУ в самом начале копируется. Возможно, это инициализация и есть. Тут без отладчика трудно смотреть...
Цитата: zenon от 24 Окт., 2020, 00:26поля - это дача? Или велопрогулки?
Это деревня. Ведь Тюмень - столица деревень :) А там и огород, и сарай, и поля, и велосипед, и добрейшей души уличная собака  ::) И работы край непочатый  :'(
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Красота!
А я вот сюда часто езжу.
Речка, собака тоже там... В этом году только редко получалось.

Slabovik

Красота красотой, но полгода зимы вгоняют в депрессняк :(
скрытый текст


правда, фотографироваться вообще не любит
[свернуть]
А местность у вас весёлая. Более "бугристая", а отличие от местных болотистых равнин :) На велике должно быть прикольно :))

Тем не менее, продолжаю смотреть .lss
Будешь смеяться: вот как выглядит тот самый if (~clk1000) 250: 80 91 0b 01 lds r24, 0x010B
 254: 80 95        com r24
 256: 80 93 0b 01 sts 0x010B, r24
 25a: 5f 98        cbi 0x0b, 7 ; 11
 25c: 80 91 7a 00 lds r24, 0x007A
 260: 80 64        ori r24, 0x40 ; 64
 262: 80 93 7a 00 sts 0x007A, r24
 266: 88 95        sleep
Ну т.е. никак. Никакого IF компилятор не создал вообще. Он читает этот clk, инвертирует его и идёт опрашивать ADC. Никаких ветвлений здесь нет. Значит, IF не работает - его просто нет. Что в свою очередь значит, что нужно менять условия - делать их более явными. Ну, или смотреть, где накосячено со скобками, точками с запятыми и т.п...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

24 Окт., 2020, 18:47 #121 Последнее редактирование: 24 Окт., 2020, 19:35 от zenon
Ха, ты прав!
Какая - такая штуковина получилась.  :o
Сделал явное:
if (clk1000hz == 0) {
    if (clk1000hz == 0) clk1000hz=255; else clk1000hz=0;
...
...
...
}
...
...
...
ISR(TIMER1_COMPA_vect) {
    if (clk1000hz == 0) clk1000hz=255; else clk1000hz=0;
}
Нууууу.... заработать-то заработало, но! индикатор стал медленным как черепаха.
Попробовал вообще убрал прерывание по таймеру и этот злополучный if, - таки всё работает великолепно.
Может что не так с тактовой частотой?
На кварце нормальные 16 MHz.
ы. Код похудел до 1008 байт... :)
+++
ыы. Залил вот такой код, для 28-ой ноги:
#include <avr/io.h>
int main (void) {
  DDRC |= (1 << PC5);
  while (1)
  {
      PORTC |=  (1 << PC5 );
      PORTC &= ~(1 << PC5 );
  }
}
Картинка такая получилась:

Slabovik

Значит, компилятор наумничал :)
Теперь давай посчитаем.
Пусть предел по напряжению 4096. 4096 больше 1024 в 4 раза. Для увеличения разрешения вдвое нужно 4 отсчёта. Вчетверо - четыре по четыре. Следовательно, для получения точности 1 на шкале 4096 необходимо 16 отсчётов.
Сейчас делается 128 отсчётов.

Таймер тикает 1 кГц (реально немного меньше из-за торможения от Noise Reduction). Канал замеряется один раз в два тика, т.е. 500 Гц. Для накопления 128 отсчётов нужно 128*2=256 тиков. Итого: показания обновляются два раза в секунду. Не часто.

Когда ты отказываешься от таймера, частота опроса ADC идёт с максимально возможной скоростью (картинку ты ранее показывал).

Необходимо сделать (ччёрт, я же это писал уже)
вариант 1: снизить количество отсчётов до необходимого. Нет никакого смысла считать чего-то долго, если мы всё-равно не пользуемся результатом. Вполне достаточно иметь 32 отсчёта. При 32 отсчётах обновление будет 8 раз в секунду - вполне приемлемо. Если хочется, можно немного (раза в полтора) ускорить таймер - место (по времени) там есть. Можно отказаться от принципа "один цикл - один канал ADC" и опрашивать в каждом цикле оба канала ADC - это ещё увеличит скорость накопления буфера вдвое.

вариант 2: сделать кольцевой буфер. Тогда показания можно будет обновлять хоть каждый отсчёт. Однако каждый отсчёт обновлять индикатор настоятельно не рекомендую. Человек всё-равно не в состоянии уловить столь быструю смену цифр. Быстрее 10-15 Гц нафиг не нужно...

А с опытом и картинкой не понял, какая цель преследовалась. То,ч то получился короткий цикл и такая скорость переключения - это всё закономерно.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Я хотел удостовериться - действительно ли тактируется от кварца 16 MHz.
Фьюзы сейчас у меня: L:0xFF, H:0xDA, E:0x05.

Slabovik

Если код (.lss) покажешь, что там, скажу, сколько машинных циклов в этом "while".
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.