2014 dxdy logo

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

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




На страницу Пред.  1, 2, 3, 4, 5  След.
 
 Re: Странное поведение fuses в atmega8a
Сообщение25.04.2025, 22:24 
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 
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 
DemISdx в сообщении #1683758 писал(а):
Меня, честно говоря, смущает наличие двойного вызова этой функции по коду.
Нет, это как раз обычное дело - повторный старт без освобождения шины (без стопа), первый с командой записи (обычно номера читаемого регистра), второй уже с командой чтения (в ней уже передать ничего нельзя, только прочитать). Формально можно и после первого дать стоп, но иногда, редко (сейчас даже не вспомню где видел), это запрещено и нужен именно повторный старт без освобождения шины (по стопу). Так что всё правильно, самая обычная процедура чтения из i2c, вот такой он примитивный протокол, не USB с его кучей служебных сообщений.

-- 26.04.2025, 02:26 --

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

 
 
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 03:58 
Уважаемый 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 
Объявил done и buf как volatile, почему то стало работать.
Заодно всплыли "подводные камни", в каждой строке данных выводится примерно 24 буквы "А", это значит, за время вывода по uart данные успевают обновиться 24 раза. Пробовал убрать деление в основном цикле, просто выводить буфер. Тогда количество букв "А" уменьшается до 16. Ну и видно, что строка обновляется очень медленно. Почему то основной цикл сильно тормозит.

 
 
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 11:44 
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 
Заменил деления 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 
Теперь понятно, почти всё время МК проводит в обработчике прерываний. Попробовал пропускать каждое 2-е прерывание, и стало всего хватать. Получается, что деления конечно влияют, но это не главное.

 
 
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 21:19 
Missir
Какова частота работы I2C?
Используется ли аппаратный I2C или сделан программно?
А то вообще непонятно с чем боретесь.

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

 
 
 
 Re: Странное поведение fuses в atmega8a
Сообщение26.04.2025, 23:35 
Dmitriy40 в сообщении #1683858 писал(а):
Какова частота работы I2C?
Используется ли аппаратный I2C или сделан программно?

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

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

 
 
 
 Re: Странное поведение fuses в atmega8a
Сообщение27.04.2025, 01:18 
Missir в сообщении #1683862 писал(а):
это я сужу по буквам "А

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

 
 
 
 Re: Странное поведение fuses в atmega8a
Сообщение27.04.2025, 02:08 
На крайний случай можно обойтись и без осцила: сварганить фильтр на 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 
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 
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 
Missir в сообщении #1683883 писал(а):
Но как это использовать?

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

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

 
 
 [ Сообщений: 75 ]  На страницу Пред.  1, 2, 3, 4, 5  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group