2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу Пред.  1, 2, 3  След.
 
 Re: Странное поведение fuses в atmega8a
Сообщение25.04.2025, 22:24 
Заслуженный участник


20/08/14
12135
Россия, Москва
Missir в сообщении #1683748 писал(а):
не совсем так, данные идут непостоянно, прерывания приходят с частотой около 1 кгц. Там всего 14 байт, это 22,5*14=315мкс, т.е. 1/3 от периода. Остаётся 600мкс, в них нужно и поместиться.
Не совсем так:
Вы не посчитали 5 байтов заголовка перед данными (и где-то 5мкс на два старта);
пока один байт обрабатывается по i2c идёт следующий, если обработка менее 22мкс, то тормозов не будет и фактически обработка времени занимать не будет (кроме последних двух байтов);
за 1мс можно переслать по i2c 44 байта, а используется лишь 19, менее 40%;
в оставшееся время (1000-19*22.5=570мкс) надо успеть их обработать, или в данном случае отослать по uart, на скорости 115200 и 8N1 это 87мкс на байт, 570мкс хватает всего на 6 байтов, а у вас 8 байтов шлётся, по идее буквально на грани работает (первый и два следующих байта улетают в буфер uart без ожидания).
Поставьте в прерывании установку на входе и сброс на выходе любого свободного выходного пина и посмотрите осциллографом и частоту прерываний и сколько времени оно занимает. Я всегда так делаю если есть подозрения или интерес.
А вот если отсылку по uart вынести из прерывания, то будет время 1000/87=11.5 байтов отослать. А тем более это можно делать в 512 раз реже, т.е. раз в полсекунды, вообще море времени. Но код сложнее, да.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 01:10 


22/11/17
70
Missir в сообщении #1683741 писал(а):
start_i2c()
Меня, честно говоря, смущает наличие двойного вызова этой функции по коду.

(Оффтоп)

В каком именно виде она сделана у Вас не увидел.
Посмотрел по аналогии:
https://count-zero.ru/2017/msp430_usi_i2c/
и на гите
https://github.com/BeFra/I2C-Atmega32u4

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 02:19 
Заслуженный участник


20/08/14
12135
Россия, Москва
DemISdx в сообщении #1683758 писал(а):
Меня, честно говоря, смущает наличие двойного вызова этой функции по коду.
Нет, это как раз обычное дело - повторный старт без освобождения шины (без стопа), первый с командой записи (обычно номера читаемого регистра), второй уже с командой чтения (в ней уже передать ничего нельзя, только прочитать). Формально можно и после первого дать стоп, но иногда, редко (сейчас даже не вспомню где видел), это запрещено и нужен именно повторный старт без освобождения шины (по стопу). Так что всё правильно, самая обычная процедура чтения из i2c, вот такой он примитивный протокол, не USB с его кучей служебных сообщений.

-- 26.04.2025, 02:26 --

Вспомнил, в каких-то многоканальных ЦАП-ах, обновление выходных сигналов было по стопу, а если хочется синхронно изменить не подряд каналы, то надо подавать два и более старта с командами записи разных каналов без промежуточных стопов, тогда обновление всех записанных будет по последнему стопу.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 03:58 


15/12/22
233
Уважаемый Dmitriy40, не могу разобраться в чём проблема. Решил вынести деление и отправку по uart в основной цикл, как Вы и советовали. Тогда в прерывании данные будут просто суммироваться в буфер, а в основном цикле - уже не спеша обрабатываться.

Вот обработчик:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
ISR(INT0_vect)
{
   if (start_i2c())
   {  if (send_i2c(GY521<<1, 0x18))   // адрес GY-521 на запись
         if (send_i2c(0x3B, 0x28))     // адрес первой ячейки памяти accel_xout_high
           if (start_i2c())
             if (send_i2c((GY521<<1)|0x01, 0x40)) // адрес GY-521 для чтения
             {
                 for (int i=0; i<6; i++)
                 {num.byts[1]=rec_i2c(1); num.byts[0]=rec_i2c(1); sum[i]+=num.v;}
                 num.byts[1]=rec_i2c(1); num.byts[0]=rec_i2c(0); sum[6]+=num.v; st++;
                 
                 if (st==512)
                 { send_uart('A'); for (int i=0; i<7; i++) buf[i]=sum[i]; st=0; done=1; }                
             }
       TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);    // STOP i2c
   }
 }
 

А вот основной цикл:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
while (1)
  {
    if (done==1)
    {  
       for (int i=0; i<7; i++)
       { x=buf[i]/512;
         if (x<0) {ch[0]='-'; x*=(-1);} else ch[0]='+';
         send_uart(' '); send_uart(' '); send_uart(' ');
         for (int j=6; j>0; j--) {ch[j]=(x%10)+48; x/=10;}
         for (int j=0; j<7; j++) send_uart(ch[j]);
         send_uart(' '); send_uart(' '); send_uart(' ');    
        }
        send_uart('\r'); done=0;
    }
  }  
 


буквы "A" приходят примерно 2 раза в секунду (как раз 1000 Гц / 512), но на этом всё. Из основного цикла данные по uart не отправляются совсем. Почему так происходит? Если вставить содержимое цикла в прерывание, то всё работает.
Я ставил cli(); после того как результат 512 усреднений копируется в буфер, а в цикле снова sei(); но бесполезно. Такое впечатление, что программа вообще не заходит в основной цикл

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 10:33 


15/12/22
233
Объявил done и buf как volatile, почему то стало работать.
Заодно всплыли "подводные камни", в каждой строке данных выводится примерно 24 буквы "А", это значит, за время вывода по uart данные успевают обновиться 24 раза. Пробовал убрать деление в основном цикле, просто выводить буфер. Тогда количество букв "А" уменьшается до 16. Ну и видно, что строка обновляется очень медленно. Почему то основной цикл сильно тормозит.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 11:44 
Заслуженный участник


20/08/14
12135
Россия, Москва
Missir в сообщении #1683779 писал(а):
Объявил done и buf как volatile, почему то стало работать.
Volatile указывает что переменные могут меняться сами по себе где-то ещё - например в прерывании. Это запрещает оптимизацию их использования, они каждый раз читаются и пишутся строго в память. Так что всё правильно, volatile для них обязателен. Но не стоит любые переменные объявлять volatile - это резко (в умном компиляторе, тупому то пофиг) гробит оптимизацию.

Условие if (st==512) надо доработать до if (st==512&&done==0) - чтобы не было конфликта если буфер отослаться не успел.
И эти две строки, условие и цикл по нему, лучше вынести ниже по коду, в конец прерывания, после i2c_stop - пусть шина освобождается быстрее.

Missir в сообщении #1683779 писал(а):
Почему то основной цикл сильно тормозит.
Учитывая что буквы "А" выводятся два раза в секунду - вообще непонятно что происходит. Убирайте частями код в основном цикле, вплоть до if(done) {send_uart('+'); done=0;}, и смотрите что больше всего тормозит.
Или может включены другие прерывания, которые и отбирают всё время у основного потока?

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 18:17 


15/12/22
233
Заменил деления buf[] отбрасыванием младшего байта через union, правда теперь деление только на 256, но это нормально.
Как я уже писал, это деление занимало 1/4 времени основного цикла. Теперь эти тормоза почти исчезли.
Также заменил фрагмент кода формирования символов, с делением на 10 и вычислением остатка от деления на 10,
вместо этого поставил, что символ просто равен "B". В таком виде, в строке появляться всего 3-4 буквы "A", это в принципе уже более-менее нормально. В целом внутренний цикл ускорился примерно в 10 раз. Получается дело вовсе не в uart (к стати, он у меня на 9600).
Судя по всему, всё это было с самого начала, просто я не замечал. Код реально выводил только 1/30 от поступивших по i2c данных.

Что было оставил и закомментировал, новое поставил перед ним:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
while (1)
  {
    if (done==1)
    {  
       for (int i=0; i<7; i++)
       {lnum.v=buf[i]; n.byts[1]=lnum.byts[2]; n.byts[0]=lnum.byts[1]; x=n.v;
         //x=buf[i]/512;
         if (x<0) {ch[0]='-'; x*=(-1);} else ch[0]='+';
         send_uart(' '); send_uart(' '); send_uart(' ');
         for (int j=6; j>0; j--) {ch[j]='B';}   //(x%10)+48; x/=10;}
         for (int j=0; j<7; j++) send_uart(ch[j]);
         send_uart(' '); send_uart(' '); send_uart(' ');    
        }
        send_uart('\r'); done=0;
    }
 

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 20:53 


15/12/22
233
Теперь понятно, почти всё время МК проводит в обработчике прерываний. Попробовал пропускать каждое 2-е прерывание, и стало всего хватать. Получается, что деления конечно влияют, но это не главное.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 21:19 
Заслуженный участник


20/08/14
12135
Россия, Москва
Missir
Какова частота работы I2C?
Используется ли аппаратный I2C или сделан программно?
А то вообще непонятно с чем боретесь.

Ведь предлагал же посмотреть осцилом сколько реально прерывание занимает. Не должно быть более 95% (потому что ещё и вход/выход) в наихудшем случае.
Ну или хоть сделайте тупо счётчик в прерывании и выведите его в консоль раз в секунду/минуту в основном цикле и сравните сколько прерываний реально прошло, а сколько должно было пройти.
Ещё можно в конце прерывания проверить не выставился ли флаг этого же прерывания уже снова (и если да, то выдать 1 в любой внешний пин, например на светодиод отладочной платы) - значит обработчик не успел. Это не 100% гарантия, но грубые накладки покажет.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 23:35 


15/12/22
233
Dmitriy40 в сообщении #1683858 писал(а):
Какова частота работы I2C?
Используется ли аппаратный I2C или сделан программно?

аппаратный, 400 кгц

у меня логика такая: если пропускаю каждое второе прерывание и после этого времени хватает на все деления и т.п. основного цикла,
а если обрабатывать все прерывания, то остаётся времени лишь на 1/30 основного цикла (это я сужу по буквам "А"), значит - основное время тратится на обработку прерываний, и чуть чуть остаётся. Это чуть чуть можно оптимизировать за счёт замены делений и т.п.
Хуже всего, что на 8 МГц похоже он вообще не сможет их никак обработать

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение27.04.2025, 01:18 


27/08/16
11798
Missir в сообщении #1683862 писал(а):
это я сужу по буквам "А

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

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение27.04.2025, 02:08 
Заслуженный участник


20/08/14
12135
Россия, Москва
На крайний случай можно обойтись и без осцила: сварганить фильтр на RC цепочке с частотой среза в единицы герц (сотню кОм на пару мкФ) и измерить вольтметром эффективное (среднее) напряжение - его отношение к Vcc будет равно коэффициенту заполнения. Но довольно грубо, процентов 15 (ёмкость обычно известна плохо, тем более если её нечем измерить).

Missir в сообщении #1683862 писал(а):
Хуже всего, что на 8 МГц похоже он вообще не сможет их никак обработать
400кГц аппаратных это хорошо, значит пока байтики ползут можно делать что-то другое (добавлять их к буферу).
Что можно сделать.
Уменьшить усреднение до 256 - деление на 256 можно сделать просто выборкой со сдвигом на байт (снова хитрый union). Плюс 4 раза в секунду сделать преобразование чисел в строку (пусть даже понадобится 7 делений) это слёзы, доли мс из 250мс.
Перенести все локальные переменные в прерываниях внутрь прерываний и объявить их register - есть надежда что работа с ними ускорится (хотя компилятор и так мог это сделать для всех кроме volatile).
Сделать пересылку по i2c тоже по прерываниям. Всю. И выдачу, и приём. Сложно, да. (Я бы сделал как автомат состояний, причём ради скорости даже не на куче if или switch, а векторным goto на метки.) Зато будет тратиться лишь столько времени сколько реально надо чтобы считать байт и записать его в буфер это порядка мкс (ну плюс накладные расхода на прерывание, но они порядка пары мкс, зато не ждать впустую 20мкс байта). И сразу добавлять их к буферу (это по идее быстро, мкс).

Честно говоря я не сталкивался чтобы вот так прям не хватало скорости, подумаешь 19 Кбайт/с по i2c принять, на 8МГц это по 400 тактов на байт, времени завались, реально должно хватать тактов по 30-50 на байт, т.е. затраты на приём данных (и сложение в буфере) должны быть порядка 10%. А уж вывод 4 раза в секунду вообще единицы процента, если не доли. Так что по моему запас огромный, и куда он весь деётся неплохо бы разобраться. Ну то есть понятно что 22.5*19=430мкс сейчас может уходить на приём по i2c (80% из которых тупое ожидание пересылки байта), но даже это ведь меньше половины доступного времени.

Кардинальное решение - переписать прерывания (и uart и i2c) на асме, это позволит не сохранять регистры и уложить по максимуму всё в регистры (не верю что компилятор с этим хорошо справляется). Правда не знаю можно ли компилятору указать что некие регистры заняты прерыванием и не использовать их. Да и асм дело такое, не самое лёгкое.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение27.04.2025, 05:42 


15/12/22
233
Dmitriy40 в сообщении #1683875 писал(а):
понятно что 22.5*19=430мкс сейчас может уходить на приём по i2c (80% из которых тупое ожидание пересылки байта), но даже это ведь меньше половины доступного времени


это чисто приём по i2c, и только в теории,
в общем не знаю ... в действительности получается что не половина, а почти всё время тратится на приём по i2c

я понял Вы о чём, вот в этом месте
Используется синтаксис C
while(!(TWCR & (1<<TWINT))) {};

она в основном и простаивает. Но как это использовать? Данные в это время я получать никак не смогу, они ещё не поступили.
Если только выполнять другие задачи, тот же основной цикл. Вместо while нужно сделать прерывание по условию !(TWCR & (1<<TWINT),
уж не знаю, возможно такое вообще или нет. И в этом прерывании уже обрабатывать оставшуюся часть приёма i2c. Тогда простаивать не должно. Но я пока не знаю как это реализовать, и есть ли вообще смысл в таких усложнениях

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение27.04.2025, 12:09 
Заслуженный участник


20/08/14
12135
Россия, Москва
Missir
Прерывание по I2C есть конечно: "18 0x011 TWI Two-wire Serial Interface".
Как им пользоваться смотрите описание работы TWI, например на стр.167 Figure 21-10 для записи данных в I2C, стр.171 Table 21-3 как реагировать на разные события при передаче. Со стр.173 раздел 21.6.3 о приёме, на стр.175 подробно статус и реакции. Все отсылки по текущему выложенному pdf.

Выгода же в том что вместо тупо ожидания около 20мкс (2.5мкс отвожу на чтение байта и добавление его к буферу) будет лишь вызываться прерывание с анализом статуса, вызов должен быть порядка 1мкс (если регистры не сохранять, компилятор должен такое уметь если не пользоваться арифметикой в процедуре), пусть 3мкс анализ статуса и реакция, плюс те же 2.5мкс на обработку байта, всего 6.5мкс, вместо 22.5мкс, т.е. 16мкс освобождаются. На каждый байт из 19, т.е. 300мкс из 430мкс. Вот такая выгода.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение27.04.2025, 12:13 


27/08/16
11798
Missir в сообщении #1683883 писал(а):
Но как это использовать?

А у вас приём не в прерываниях? Пишите хитрый обработчик прерываний, который будет складывать принятые символы в буфер. Грамотно написать непросто, думайте, учитесь.

Ещё вариант - взять более современный и быстрый процессор с аппаратным DMA, для которого ещё есть и библиотеки драйверов, реализующие приём в прерываниях.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 35 ]  На страницу Пред.  1, 2, 3  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group