реклама на сайте
подробности

 
 
> Сопоставление проектов С и АСМ, Немного цифр
SasaVitebsk
сообщение Feb 17 2008, 20:18
Сообщение #1


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Написал сначала проект на ASM в AVR Studio, а позже очень похожий на Си. И там и там вылизывал по скорости. Точнее особо не вылизывал. В обоих случаях камни с большим запасом. Естественно во втором проекте было всё намного грамотнее построено. Вопервых дробление шагов на основе ШИМ с большим коэффициентом, во вторых более красивое движение стрелок обеспечено. 18 скоростей с плавным переключением. Фильтрация более красивая. Обработка CAN некоторое место занимает. Тем не менее сравнивать можно

Результаты достаточно любопытны для неопределившихся. Попробовал свести в небольшую таблицу. smile.gif

Код
Камень       ! atmega8 ! at90can128 !
язык         !   asm   !    IAR C   !
-------------+---------+------------+
Число ШД     !    6    !     6      !
управл.      ! дрб 6   ! ШИМ дрб 16 !
Вх аналоговые!    6    !     3      !
Вх частотные !    0    !     1      !
Вх CAN       !    0    !     2      !
Цифр. флтр   !    +    ! + более слж!
Плавность    !    -    ! спец алг   !
Авар         !    +    !+ более слж !
-------------+---------+------------+
Размер кода  !  5019   !   6901     !
без доп табл !   -     !    672     !
Итого        !  5019   !   6229     !
Итого %      !   81    !    100     !
RAM          !  241    !    415     !
Итого %      !   58    !    100     !
-------------------------------------

Несколько попозже буду перетаскивать Этот проект на м8. То есть можно будет сопоставить практически 1 к 1 (с учётом значительных улучшений).
Go to the top of the page
 
+Quote Post
5 страниц V   1 2 3 > »   
Start new topic
Ответов (1 - 62)
_Pasha
сообщение Feb 18 2008, 07:58
Сообщение #2


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Для меня лично есть одно НО:
вот я использую мультипоточное программирование на асме, стек-фреймы и прочее - так проще и читабельнее выглядят программы. Диспетчеризация кооперативная. При переходе от потока к потоку в свопинге участвуют только содержимое PC и SP. Все крутится на минимальном наборе регистров, потому что большая их часть задействована в прерываниях, в которых тоже свопинга мало. И под такую Homebrew - ось я заточить "С" не смогу никак.
Вот вчера запустил ногодрыгалку на меге 640 по теме, которой я к Вам в личку напрашивался.
Из 15 возможных ШИМ используются все с частотой до 10кГц.
Из 16 АЦП используются все.
+ Дисплей 7 сегментный + клава + 2 модбаса + 1 RS232.
И рад бы я это на "С" написать, потому как времени на это немного, но прикинул, что с учетом неперенесенных наработок и определенной критической массы Time-critical sections, да еще и трудностей с мультипотоком, писанины будет раза в 3 больше.

А тут же параллельно силовая поделка на:
пульт на меге 8 (+модбас)
контроллер: мега48+PIC18f2431 общаются по I2C (+модбас)
Здесь без "С" вообще никуда.

А по качеству кода у меня и к Winavr претензий нет. И что самое приятное - чем тупее/тривиальнее/читабельнее код, тем лучше оптимизация.
Go to the top of the page
 
+Quote Post
KRS
сообщение Feb 18 2008, 08:32
Сообщение #3


Профессионал
*****

Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555



Это все не показатель.
Если код на С заточить например под IARовский компилер и использовать его фичи:
#pragma inline=forced
__z
__z_x и т.д.
посмотреть как компилятся определенные конструкции
у меня например есть макросы работы с кольцевым буфером в 256 байт выравненым, которые компилятся в
Код
  LDS ZL, Head
  LDI ZH, HIGH(Buf)
  LD r??, Z+
  STS Head, ZL

или если несколько байт надо вытащить

  LDS ZL, Head
  LDI ZH, HIGH(Buf)
  LD r??, Z+
  LDI ZH, HIGH(Buf)
  LD r??, Z+
  STS Head, ZL


использовать временные перменные
Код
    {
         uint8_t tmp;
         if (tmp = TIFR1 & (1<<OCF1A)) {
             TIFR = tmp;
             ....
         }
    }


можно получить код не хуже чем на асме!
зато с большими функциями в которых используется много указателей и вычислений гемора не будет
(IAR прекрасно пересылает указатели movw ZL:ZH, .....)
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 18 2008, 15:45
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



2 _pasha.
Да с наработками не хочется расставаться - согласен.
И, на небольшом проекте, действительно объём проги на ASMе меньше чем на Си. Особенно если писать грамотно.

Но вот такой вот пример. У меня там фильтрация цифровая есть, так вот при переходе с 8-ми битного режима АЦП на 10-ти битный изменилась:
1) Одна константа (KADMUX)
2) В двух местах заменил ADCH на ADC
3) После фильтрации сигнал поделил на 4. ((z0>>2)&0xff)

И всё!!!
Чтобы коэффициенты 16-ти битные были заменил одно объявление int8_t на int16_t.

На Си мне не приходится собираться с мыслями, чтобы решиться на координальную переделку проекта. Switec-овский алгоритм осилил за час
Go to the top of the page
 
+Quote Post
Rst7
сообщение Feb 18 2008, 16:19
Сообщение #5


Йа моск ;)
******

Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610



Цитата
и использовать его фичи:#pragma inline=forced__z__z_x и т.д.


На самом деле, с точки зрения написания оптимального кода, данными модификаторами мы немного регулируем распределение регистров компилятором между сохраняемыми/несохраняемыми. Конечно, в пределе, хорошо бы иметь возможность крутить этот параметр самостоятельно куда в более широких пределах. Но и так хорошо, приятная полезность.


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post
Qwertty
сообщение Feb 18 2008, 18:33
Сообщение #6


Местный
***

Группа: Свой
Сообщений: 408
Регистрация: 21-10-06
Из: Санкт-Петербург
Пользователь №: 21 527



Цитата(SasaVitebsk @ Feb 18 2008, 18:45) *
Switec-овский алгоритм осилил за час

Извиняюсь за оффтоп, но что такое "Switec-овский алгоритм"?
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 18 2008, 20:17
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(Qwertty @ Feb 18 2008, 22:33) *
Извиняюсь за оффтоп, но что такое "Switec-овский алгоритм"?

Красивый алгоритм управления стрелками на ШД. Он обеспечивает плавность движения стрелок. Задача, на самом деле довольно сложная. Я реализовывал упрощённую модель.

Смысл в том чтобы плавно менять скорость движения стрелки в зависимости от ошибки по положению.
Go to the top of the page
 
+Quote Post
fmdost
сообщение Feb 18 2008, 23:25
Сообщение #8


Местный
***

Группа: Свой
Сообщений: 479
Регистрация: 8-05-07
Из: г. Ставрополь. Северный Кавказ. Россия
Пользователь №: 27 606



ИМХО. Написать С код соизмеримый с асмом можно, но требует таких же трудозатрат.

А вот интеретно, использовали ли Вы такие приёмы?
Код
unsigned char a;
a = delay_key; //токмо для "оптимизации". Иар почему-то все переменные считает как volatile.
if (KEY_ON())
{
  if ( a != 255 )
    delay_key = a+1;
}
else if (a)
  delay_key = a-1
Go to the top of the page
 
+Quote Post
IgorKossak
сообщение Feb 19 2008, 06:37
Сообщение #9


Шаман
******

Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221



Цитата(Т.Достоевский @ Feb 19 2008, 01:25) *
...Иар почему-то все переменные считает как volatile...

В первый раз подобное слышу (обычно народ жалуется, что наоборот).
Наверное потому, что оптимизацию никогда не выключаю.
Go to the top of the page
 
+Quote Post
Igor26
сообщение Feb 19 2008, 06:45
Сообщение #10


Знающий
****

Группа: Свой
Сообщений: 521
Регистрация: 10-02-05
Пользователь №: 2 544



Цитата
Написать С код соизмеримый с асмом можно, но требует таких же трудозатрат.

...если толком не писал ни на том, ни на другом.
Go to the top of the page
 
+Quote Post
Rst7
сообщение Feb 19 2008, 06:50
Сообщение #11


Йа моск ;)
******

Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610



Цитата
А вот интеретно, использовали ли Вы такие приёмы?


Дык это совершенно правильный код для помогания компилятору. В общем случае структура любой функции для камня с большим количеством регистров должна стремиться к такому виду:
Код
foo()
{
regvar1=memvar1;
regvar2=memvar2;
....
regvarn=memvarn;
....
собственно вычисления
....
memvar1=regvar1;
memvar2=regvar2;
...
memvarn=regvarn;
}


Другое дело, что сам компилятор должен стремиться к этому. Но не грех и помогать ручками. Конечно, пример вырожденный, но, я думаю, мысль мою описывает правильно...


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post
Dog Pawlowa
сообщение Feb 19 2008, 06:54
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823



Цитата(_Pasha @ Feb 18 2008, 11:58) *
Для меня лично есть одно НО:
вот я использую мультипоточное программирование на асме, стек-фреймы и прочее - так проще и читабельнее выглядят программы. ...

А что такое стек-фреймы?
Меня первый же безусловный переход вводит в ступор, а второй - в ярость.
Это я так, нисколько не собираясь споров затевать.


--------------------
Уходя, оставьте свет...
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 19 2008, 07:59
Сообщение #13


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(Dog Pawlowa @ Feb 19 2008, 09:54) *
А что такое стек-фреймы?
Меня первый же безусловный переход вводит в ступор, а второй - в ярость.
Это я так, нисколько не собираясь споров затевать.

Ну, я сразу все не напишу.
Модель вкратце, остальное по уточняющим вопросам.
Код
.dseg
Entry_Point:   .byte 2
Save_SP:      .byte 2
Local_SP:      .byte 2
Local_Stack:  .byte my_Stack_Size

Var1:            .byte 1
;..............................
VarN:            .byte 2

.set Local_Var_Org = Var1


Это по данным.
Предположим, у нас уже Y адресует начало этого блока данных
Тогда пролог процедуры:
Код
ld zL,Y+
ld zh,Y+; load entry point
in r0,spl
in r1,sph
st Y+,r0; store stack pointer
st Y+,r1

ld r0,Y+
ld r1,Y+
cli
out spL,r0
out sph,r1
sei
; at this line Y points to LOCAL DATA
ijmp; resume procedure execution

Эпилог соответственно восстанавливает все обратно
Очень полезный макрос
Код
.macro leave; <new_entry_point_addr>
  ldi zL,low(@0)
  ldi zh,high(@0)
  jmp Epilogue
.endm

Итого в нашу пользу:
1. Имеем реентерабельные процедуры. Могу выложить пример, где 4 одинаковых процесса крутятся таким образом, выдают инфу каждый на свой индикатор (7-сегм), обрабатывают энкодер как орган управления (правда, плохонько, но претензий нет, я и не работаю над собой)
2. Читабельность зашкаливает
Вместо приснопамятных переменных State мы переопределяем точку входа в процедуру.
Что-нить проверили, кое-какое условие, затем просто пишем leave addr вместо rjmp
Код
Label1:
            call Test_Some_Conditions;
            breq  Label2;
            leave Label1; exits here, but next entry point will be Label1
Label2:; continue program execution


3. Если глубина локального стека позволяет, можно вызывать другие подпрограммы, и если в них использовать leave, все еще интереснее. Т.е. с точки зрения программиста нормальная логика выполнения программ не нарушается.
4. Локальные данные адресуются с помощью yL:yH, намертво закрепленных за указателем
Продолжать ?
Go to the top of the page
 
+Quote Post
Dog Pawlowa
сообщение Feb 19 2008, 10:42
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823



Цитата(_Pasha @ Feb 19 2008, 11:59) *
Продолжать ?

Нет, спасибо.
Интересно, конечно, но я уже перешел на 'C' и возвращаться не имеет смысла.
Однажды видел проект (система продажи билетов на ЖД эпохи начала перестройки), в котором использование макросов и условной компиляции поражало воображение, но для этого нужна свежая молодая голова.
Too late smile.gif


--------------------
Уходя, оставьте свет...
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 19 2008, 10:57
Сообщение #15


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(Dog Pawlowa @ Feb 19 2008, 13:42) *
для этого нужна свежая молодая голова.
Too late smile.gif

Извиняюсь, не могу смолчать.
1. У меня голова уже точно не молодая и не свежая - сын в институте...
2. У атмелов-то правильных макросредств только недавно появилось smile.gif

3. Немножко все-таки продолжу про минусы того,чего написал выше.
Главный минус - это как детский вопрос : "Можно ли прикрутить туда 1-wire ?" smile.gif
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 19 2008, 11:42
Сообщение #16


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(Т.Достоевский @ Feb 19 2008, 03:25) *
ИМХО. Написать С код соизмеримый с асмом можно, но требует таких же трудозатрат.


А вы анализировали глобально производительность программы в процессе оптимизации? Существует теория оптимизации (естественно).

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

Опыт показывает, что оптимизировать этот кусочек лучше именно в виде "подсказок" для компилятора. У меня это дало практически идеальный код. потери в сравнении с асмом - 5% не больше. К сожалению, такая оптимизация тоже получается (CPU-зависима). Выработав некоторые общие принципы, можно сразу писать так, что будет код очень эффективный.

Вот пример
Код
void Point(uint8_t X,uint8_t Y,uint8_t Color)            // Вывести точку
{
uint8_t    maska;                                        // Смещение    относительно байта
uint8_t    *addr;                                        // Адрес экраного байта

maska =0x3;
if((X & 2)==0)
{
   maska = 0x30;
   Color <<= 4;
}
if((X & 1)==0)
{
   maska <<=2;
   Color <<=2;
}
maska = ~maska;
if(NEkr>0) addr = &Ekr[SIZE_EKR];
else addr = Ekr;

addr += (Y*SizeXb)+(X>>2);
maska &= *addr;
maska += Color;
*addr = maska;
}
/*offset    = (X & 3) << 1;                                // выделить    смещение точки в байте
maska = ~(3<<offset);
addr = (Y*SizeXb)+(X>>2);
maska &= Ekr[NEkr][addr];
maska += Color << offset;
Ekr[NEkr][addr] = maska;
}*/

Результат - двукратный рост производительности.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 19 2008, 12:04
Сообщение #17


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(SasaVitebsk @ Feb 19 2008, 14:42) *
Вот пример

Ну, я бы еще для масок завел массив в памяти программ, но, не видя листинга, могу и ошибиться.
Go to the top of the page
 
+Quote Post
forever failure
сообщение Feb 19 2008, 13:13
Сообщение #18


Местный
***

Группа: Участник
Сообщений: 256
Регистрация: 6-03-05
Из: Екатеринбург
Пользователь №: 3 112



Цитата(SasaVitebsk @ Feb 19 2008, 16:42) *
Результат - двукратный рост производительности.

И можно ещё быстрее и компактней...
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 19 2008, 17:00
Сообщение #19


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(forever failure @ Feb 19 2008, 17:13) *
И можно ещё быстрее и компактней...

Наверняка.
В данном примере просто наглядно видно что я вместо вызова п/п сдвига на N бит делаю анализ указателя сдвига с генерацией констант.

Табличный метод в данном месте хуже.
вот кусочек результирующего листинга
Код
    891           if(chBright<0)
   \   00000004   2322               TST     R18
   \   00000006   F422               BRPL    ??PointB_0
    892           {
    893             ColorX =0;
   \   00000008   E030               LDI     R19, 0
    894             ch = -chBright;
   \   0000000A   2F02               MOV     R16, R18
   \   0000000C   9501               NEG     R16
   \   0000000E   C002               RJMP    ??PointB_1
    895           }
    896           else
    897           {
    898             ColorX =3;
   \                     ??PointB_0:
   \   00000010   E033               LDI     R19, 3
    899             ch = chBright;
   \   00000012   2F02               MOV     R16, R18
    900           }
    901           maska =0x3;
   \                     ??PointB_1:
   \   00000014   E023               LDI     R18, 3
    902           if((X & 2)==0)
   \   00000016   FB41               BST     R20, 1
   \   00000018   F02E               BRTS    ??PointB_2
    903           {
    904             maska = 0x30;
   \   0000001A   E320               LDI     R18, 48
    905             ColorX <<= 4;
   \   0000001C   9532               SWAP    R19
   \   0000001E   7F30               ANDI    R19, 0xF0
    906             ch <<= 4;
   \   00000020   9502               SWAP    R16
   \   00000022   7F00               ANDI    R16, 0xF0
    907           }

Как видим вполне прилично. На асме у меня получился выигрыш в несколько тактов.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 20 2008, 08:15
Сообщение #20


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Теперь немного о другом. Рискую утомить читателя, поэтому-кратко.
0. Код под Winavr

1. В EEPROM имеется несколько наборов параметров, активный набор обозначен селектором:

Код
uint8_t EEMEM Selector=0x00;

#define Max_Applicable_Paramsets 4
typedef struct {
    uint16_t Cap_Chg_Time;         // ==0 then continuous mode
    uint16_t Cap_Charge_Volt;     // chg to volt
    uint16_t Cap_Discharge_Volt;     // disch downto this value (volts)
    uint16_t Operating_Current;    // ==0 then voltage mode
    uint8_t     Amplitude;        // used for voltage mode boost
    uint16_t PWMfreq;
    uint16_t Expose_Time;        // in 10ms step
    uint8_t  Rly_Mode;        // ==0 none rly is turned on at selection

    } App_Param_Record;
// eeprom implementation
App_Param_Record EEMEM Parameter_set[Max_Applicable_Paramsets] =
{{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};


2. В памяти программ есть дескрипторы этих параметров, но только для активной страницы

Код
//дескриптор параметра
typedef struct {
                      uint8_t xcmd; // параметр соответствует некоторой команде, получаемой извне
                      void *ojaddr; // адрес параметра
                      uint8_t ojsize;// размер данных+ здесь же добавил опции размещения (см ниже) этих данных
                     } Cm_Xref_;

typedef Cm_Xref_ PROGMEM Cm_Xref_t; // объявили
// опции размещения данных
#define Fitter_mask 0xC0
// размещено во флеше (т.е. данные будут только для чтения)
#define Flash_Fit 0x80
// размещено ЕЕ
#define EE_Fit 0xC0
// размещено в ОЗУ и только для чтения
#define Sram_Fit_RO 0x40  //fitted in sram and read only

// Размещено в буфере типа  App_Param_Record, в который будет копироваться страница параметров
#define Relative_Fit 0x00 // relative to struct

// Команды на чтение 0x00..0x7F на запись 0x80..0xFF
#define Inhibit_read_write_mask 0x7F

//собсно описания

static App_Param_Record Edited_Record; // sram shadow

#define Rel_Param(member) &Edited_Record.member,Relative_Fit+sizeof(Edited_Record.member)

static Cm_Xref_t Cm_Xref[Max_cmd+1]=
{
    {cm_Brief,0,0}, // version crc
    {cm_NetLocate,&Net_ID,EE_Fit+1},
    {cm_Set_Selector,&Selector,EE_Fit+1},
    {cm_Charge_Time,Rel_Param(Cap_Chg_Time)},
    {cm_Charge_Volt,Rel_Param(Cap_Charge_Volt)},
    {cm_Discharge_Volt,Rel_Param(Cap_Discharge_Volt)},
    {cm_Operating_Current,Rel_Param(Operating_Current)},
    {cm_Amplitude,Rel_Param(Amplitude) },
    {cm_Expose_Time,Rel_Param(Expose_Time)},
    {cm_Rly_Mode,Rel_Param(Rly_Mode)}
};


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

3. Проглотил я, короче, эти сложности - в конце концов памяти программ я пока не потерял.
В общем, дело доходит до реализации. Обрезал я поиск команды. Оставил обращение типа Cm_Xref[command].
Привожу процедуру без купюр и особых комментов. Все названия сторонних функций - сами за себя говорят.

Код
void Track_Pult(void)
{
uint8_t buffer[8],Rsz,*addr,k;
Packet_Layout *pack;
Cm_Xref_ xref,*xr;

    Rsz = RS485_pack_ready();
    if (!Rsz) return;
    // <datasize> <receiver> <sender> <command> <data> <crc>
    pack = &RS_buff;

        if((pack->receiver) != Get_Net_ID()) goto Catch;

    // else -continue parsing received pack
    Sender_Addr = pack->sender;// store sender
    Rsz -= 5;


           k = pack->command & Inhibit_read_write_mask;

    if (k == cm_Remote_Start){
      Set_Start_Status();
      Send_error(er_OK); // :) no errors
      goto Catch; }

    if (k == cm_Remote_Stop) {
      Set_Stop_Status();
      Send_error(er_OK);
      goto Catch; }

    if (k >= Max_cmd) {Send_error(er_Unknown_cmd);goto Catch;}

        xr = memcpy_P(&xref,&(Cm_Xref[k]),sizeof(xref));

uint8_t options,objsize;
void *compaddr;

    options = xref.ojsize;
    objsize = options & (~Fitter_mask);
    options &= Fitter_mask;
    addr = xref.ojaddr;
    k = pack->command != k;
    if (Dev_Status.IsWorking) {Send_error(er_Busy); goto Catch;}
    if (Rsz != objsize) {Send_error(er_InvParam); goto Catch;}

    switch (options){
    case Flash_Fit:
        if (k) Send_error(er_Read_Only);
        else   Send_data(er_OK,memcpy_P(&buffer,addr,objsize),objsize);
        break;

    case EE_Fit:
        eeprom_read_block(&buffer,addr,objsize);
        if (k) {eeprom_write_block(addr,&(pack->data),objsize);Send_error(er_OK);}
         else Send_data(er_OK,&buffer,objsize);
        break;
    case Relative_Fit:
        compaddr = Paramset();
        eeprom_read_block(&Edited_Record,compaddr,sizeof(Edited_Record));
        if (k) {eeprom_write_block(&Edited_Record,compaddr,sizeof(Edited_Record)); Send_error(er_OK);}
            else Send_data(er_OK, addr,objsize);
        break;

        }
Catch:
    RS485_purge_received();
    return;
}


Утомил?
Могу сказать одно - на асме эта байда была бы существенно проще. Я уже не говорю про смысл записи в EEPROM значений, которые не изменились. smile.gif Если я еще чего туда наверну, мне памяти точно не хватит.
Если все-же кто-то "асилил" то, что я накрапал, подскажите, как бороться со сложняком в системе описания параметров. Через "C", ессно.
З.Ы. О цифрах забыл размер кода 364 байт. Время - пофиг. Оптимизация -Os
З.З.Ы. Подчистил грязь - исправил "caller" на "receiver"
Go to the top of the page
 
+Quote Post
defunct
сообщение Feb 20 2008, 14:51
Сообщение #21


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(_Pasha @ Feb 20 2008, 10:15) *
Могу сказать одно - на асме эта байда была бы существенно проще. Я уже не говорю про смысл записи в EEPROM значений, которые не изменились. smile.gif

Стиль значит асмовый используется.. Попробуйте писАть на С, а не на asm сишными словами.
Одинаковые значения в eeprom тоже никто не заставляет писать, в функции записи байта сделать вычитку ячейки, совпадает с тем что пишем - выход.
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 20 2008, 16:25
Сообщение #22


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Не могу сказать, что я точно понял смысл, но попробую. Хотя если из другой оперы - то извиняй. (Мне кажется я делал что-то подобное)

1. Все команды строю однотипно типа так.
Код
struct AddrKomXx
{
uint8_t            Name;                    // Имя команды
uint16_t            TimeStart;                // Время начала исполнения команды
} *KomXx;

struct AddrKomC
{
uint8_t            Name;                    // Имя команды
uint16_t             TimeStart;                // Время начала исполнения команды
uint8_t             TimeMashtabTek,            // Текущее значение масштаба для времени исполнения
                    TimeMashtab;            // Масштаб для времени исполнения
uint16_t            TimeLife;                // Время исполнения команды с учётом масштаба
int16_t             BegX,BegY,                // Начало объекта (X,Y)
                    SizeX,SizeY;            // Размеры объекта (X,Y)
int8_t                VecX,VecY;                // вектор перемещения объекта (X,Y)
uint8_t            color;                    // цвет заливки
} *KomC;

struct AddrKomK
{
uint8_t            Name;                    // Имя команды
uint16_t             TimeStart;                // Время начала исполнения команды
uint8_t             KernTek;                 // Текущее значение масштаба для времени исполнения
} *KomK;


У меня их море, это кусочек. Причём первая AddrKomXx это несуществующая "виртуальная", для определения текущей. Определение делаю так.
Код
  KomXx    = (struct AddrKomXx*) AdrActiveKom[i];            // Прочитать адрес текущей активной    команды
  if(KomXx->TimeStart>Status.TekTime) continue;            // Если    не подошло время для исполнения    команды, то    пропустить данную команду
  switch (KomXx->Name) {
  /* Команда "END" - "Ролик    завершить" */
  case 'S':
  /* Команда "Stop-Kadr"    - "Остановить модификацию картинки" */
            KomS = (struct AddrKomS*) AdrActiveKom[i];    // Прочитать адрес текущей активной    команды
...

То есть сначала адрес начала команды "сопоставляю" с "виртуальной" командой все поля в которой в точности совпадают во всех командах и по ней осуществляю контроль команды, а потом "пересопоставляю" с уже реальной командой, где уже реальные поля и работаю с ней.
Если работаешь с сылками(адресами), как я, то без разницы где находится команда и её можно не пересылать вообще. Имей один буфер приёма - с ним и работай. Я например даже удаляю отработавшие и делаю "сборку мусора". zltigo ещё интереснее сделал, если я подосвобожусь тоже так попробую - он свой диспетчер кучи написал и работает с занятием/освобождением весьма грамотно. У меня, принципиально, возможны проколы в данном месте - я это знаю.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 20 2008, 18:49
Сообщение #23


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(defunct @ Feb 20 2008, 17:51) *
Стиль значит асмовый используется.. Попробуйте писАть на С, а не на asm сишными словами.
Одинаковые значения в eeprom тоже никто не заставляет писать, в функции записи байта сделать вычитку ячейки, совпадает с тем что пишем - выход.

1. Дописываю тот же кусок кода на асме. Для сравнения. Получается в полновесном варианте, со всеми обработками ошибок, с поиском правильной команды и с таблицей - меньше 200 байт. Завтра выложу в аттаче, дабы не превращать тему в "кодовый флуд". По поводу "писАть на С" - я "С" терпеть не могу еще с 1992 года smile.gif Проблемы-то начинаются с обращения к памяти программ за данными. Этот процесс нигде не автоматизирован, отсюда сложности.
2. Переопределять стандартные функции НЕ ХОЧУ.

Цитата(SasaVitebsk @ Feb 20 2008, 19:25) *
Не могу сказать, что я точно понял смысл, но попробую. Хотя если из другой оперы - то извиняй. (Мне кажется я делал что-то подобное)

Смысл: Хранение в девайсе настроечных параметров и обращение к ним, например, через команды по последовательному порту.

Цитата
... он свой диспетчер кучи написал и работает с занятием/освобождением весьма грамотно. У меня, принципиально, возможны проколы в данном месте - я это знаю.

Вот! Там, где дело касается ОЗУ - ессно "С" выиграет.
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 20 2008, 21:06
Сообщение #24


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(_Pasha @ Feb 20 2008, 22:49) *
Проблемы-то начинаются с обращения к памяти программ за данными. Этот процесс нигде не автоматизирован, отсюда сложности.
2. Переопределять стандартные функции НЕ ХОЧУ.
Смысл: Хранение в девайсе настроечных параметров и обращение к ним, например, через команды по последовательному порту.
Вот! Там, где дело касается ОЗУ - ессно "С" выиграет.

Никаких проблем при обращении к памяти программ за данными я не заметил. Обращаешься так же как и к обычной переменной. Более того код получается очень эффективным. Пересылать абсолютно ничего не надо. По крайней мере в IAR.
В редких случаях, когда идут разнотипные данные компилятору приходится явно указывать что идёт обращение к flash.
Например
Код
// Знакогенераторы
const    void    __flash   *symbol[MAX_FONTS] = {&fnt0_6x8,&fnt1_8x8,&fnt2_10x10,&fnt3_11x13,&fnt4_9x14,&fnt5_16x16,&fnt6_16x16,&fnt7_12x13,&fnt8_16x15,&fnt9_14x16};
....
uint8_t __flash  *addr;
.....
     addr =    ((uint8_t __flash *)symbol[tekfonts])+(Znak*Fonts[tekfonts][1]);
.....

А обычно и этого делать не приходится
Код
// структура данных по символам
struct CharZnak
{
uint16_t            sm;                        // Смещение от начала фонта
uint8_t            width;                    // Ширина символа
};

//extern             uint16_t                __checksum;        //    CHECKSUM;
//const __flash    uint16_t                EndFlashAdr @0xe5 = & __checksum;

extern const    uint8_t        __flash        Fonts[MAX_FONTS][2];
extern const __flash struct    CharZnak    struct_fnt[MAX_FONTS-2][223];
....
    symbolx=struct_fnt[tekfonts-2][Znak].width + tekkern;


Вот пример компиляции
Код
   1038              symbolx=struct_fnt[tekfonts-2][Znak].width + tekkern;
   \   0000002C   ....               LDI     R30, LOW((struct_fnt - 1336))
   \   0000002E   ....               LDI     R31, HIGH((struct_fnt - 1336))
   \   00000030   5221               SUBI    R18, 33
   \   00000032   E003               LDI     R16, 3
   \   00000034   9F02               MUL     R16, R18
   \   00000036   0DE0               ADD     R30, R0
   \   00000038   1DF1               ADC     R31, R1
   \   0000003A   E90D               LDI     R16, 157
   \   0000003C   E012               LDI     R17, 2
   \   0000003E   9F14               MUL     R17, R20
   \   00000040   2D10               MOV     R17, R0
   \   00000042   9F04               MUL     R16, R20
   \   00000044   0D11               ADD     R17, R1
   \   00000046   0DE0               ADD     R30, R0
   \   00000048   1FF1               ADC     R31, R17
   \   0000004A   9104               LPM     R16, Z
   \   0000004C   0F06               ADD     R16, R22
   \                     ??ZnakWidth_1:
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 21 2008, 01:19
Сообщение #25


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Вот это совсем другое дело! smile.gif
Че-то WINAVR тормозит немного. А может я торможу - начитался мануалов, а уже все работает. Пойду проверю.
Насчет диспетчера кучи: я так понял, надо либо собрать все указатели, работающие с выделяемыми объектами, в одну секцию. Тогда при дефрагментации памяти сравнительно легко все перемещается. Либо не париться и держать дескрипторы.
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Feb 21 2008, 07:20
Сообщение #26


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(_Pasha @ Feb 21 2008, 06:19) *
Вот это совсем другое дело! smile.gif
Че-то WINAVR тормозит немного.


WINAVR как раз в этом очень сильно тормозит. Работа с флешом в нём сделана мегакриво. Так как в примере SasaVitebsk-а, не выйдет.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 21 2008, 07:34
Сообщение #27


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(AHTOXA @ Feb 21 2008, 10:20) *
WINAVR как раз в этом очень сильно тормозит. Работа с флешом в нём сделана мегакриво.

... И похоже, что по политическим соображениям никогда не исправится.
Насчет кривизны:
Из предыдущего утомительного примера:
Код
static Cm_Xref_t Cm_Xref[Max_cmd+1]=
{
    {cm_Brief,0,0}, // version crc
    {cm_NetLocate,&Net_ID,EE_Fit+1},
    {cm_Set_Selector,&Selector,EE_Fit+1},
/*****************************************/
};
/******************************************/
uint8_t M;
M = Cm_Xref[2].ojsize;


Так constant propagation оно просчитывает. А стоит появиться адресному выражению - кирдык.
Цитата
Попробуйте писАть на С, а не на asm сишными словами...


Блин, вот зацепил...до сих пор помню.
Go to the top of the page
 
+Quote Post
ReAl
сообщение Feb 21 2008, 08:43
Сообщение #28


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(_Pasha @ Feb 21 2008, 03:19) *
Вот это совсем другое дело! smile.gif
Че-то WINAVR тормозит немного.

Ну, у avr-gcc код как правило похуже, с этим не спорю.
Код
#include <stdint.h>
#include <avr/pgmspace.h>
#define MAX_FONTS 10 // от балды, в приведеннмо примере не было задано. Но оно неважно

// структура данных по символам
struct CharZnak
{
uint16_t            sm;                        // Смещение от начала фонта
uint8_t            width;                    // Ширина символа
};

extern const  uint8_t   Fonts[MAX_FONTS][2] PROGMEM;
extern const  struct    CharZnak    struct_fnt[MAX_FONTS-2][223] PROGMEM;
extern uint8_t tekfonts, tekkern;

uint8_t sx(uint8_t Znak)
{
    return pgm_read_byte( & struct_fnt[tekfonts-2][Znak].width ) + tekkern;
}


CODE
.file "tst.c"
__SREG__ = 0x3f
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__tmp_reg__ = 0
__zero_reg__ = 1
.global __do_copy_data
.global __do_clear_bss
.text
.global sx
.type sx, @function
sx:
/* prologue: frame size=0 */
/* prologue end (size=0) */
ldi r25,lo8(3)
mul r24,r25
movw r30,r0
clr r1
lds r24,tekfonts
ldi r25,lo8(0)
ldi r18,lo8(669)
ldi r19,hi8(669)
movw r20,r24
mul r20,r18
movw r24,r0
mul r20,r19
add r25,r0
mul r21,r18
add r25,r0
clr r1
add r30,r24
adc r31,r25
subi r30,lo8(-(struct_fnt-1336))
sbci r31,hi8(-(struct_fnt-1336))
/* #APP */
lpm r30, Z
/* #NOAPP */
lds r24,tekkern
add r30,r24
mov r24,r30
ldi r25,lo8(0)
/* epilogue: frame size=0 */
ret
/* epilogue end (size=1) */
/* function sx size 31 (30) */
.size sx, .-sx
/* File "tst.c": code 31 = 0x001f ( 30), prologues 0, epilogues 1 */

Катастрофической я бы разницу не назвал - ни в написании текста, ни в сгенерированном коде.
Сравнивать надо одинаковые фрагменты, в приведенном выше куске часть регистров уже "откуда ни возьмись" заполнены и используются, тут всё "автономно".


Цитата(_Pasha @ Feb 20 2008, 20:49) *
По поводу "писАть на С" - я "С" терпеть не могу еще с 1992 года smile.gif
Я года с 1987..88 использую и ничего. Что-то писалось чисто асмовое, что-то с делением "это пусть на С, а эта часть на асме".
Сейчас если очень хочется - пишу вставку на асме. В последнее время хочется всё реже.

Цитата
2. Переопределять стандартные функции НЕ ХОЧУ.
Ну извините... Так можно дойти до "а на асме я могу что-то другое делать, пока EEPROM пишется, а С-шная программа станет колом на всё время записи блока, во какой плохой язык С".
И начать требоавть стандартную функцию для решения всей своей задачи.


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
defunct
сообщение Feb 21 2008, 11:52
Сообщение #29


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(_Pasha @ Feb 21 2008, 09:34) *
Блин, вот зацепил...до сих пор помню.

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

Цитата
Проблемы-то начинаются с обращения к памяти программ за данными. Этот процесс нигде не автоматизирован, отсюда сложности.

да и на асм он не автоматизирован, ровно в той же степени.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 21 2008, 16:57
Сообщение #30


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Вернемся к нашей баранине. "Не умеешь-научим, не хочешь-заставим." Заставили. Дописал функцию записи в ЕЕПРОМ, в аккурат по шаблону eeprom_write_block() .

Код
void EE_Proof_Write_Block(const void *pointer_ram,
              void *pointer_eeprom,
              size_t n)
{
size_t j;
uint8_t *src,*dst;

src = (uint8_t*)pointer_ram;
dst = (uint8_t*)pointer_eeprom;

for (j=0;j<n;j++)
{
  if (eeprom_read_byte(dst) != *src)
      eeprom_write_byte(dst,(uint8_t)*src);
  dst++;
  src++;
}
return;
}


ИТОГО такое: +56 байт. Имея ввиду, что была исходная eeprom_write_block(), вычтем, скажем, байт 16. В добавок, это все можно обрезать, задаваясь размером блока не size_t a uint8_t, если уж очень хотца. Это еще минус байта 4-6. Короче, +32..36 байт. Оптимайзер рулит!!!
Может, мы доживем до тех времен, когда свопинг лишний чистится будет...
Кстати, кто-нибудь знает, почему многопроходовая оптимизация в GCC не в фаворе?

Цитата(defunct @ Feb 21 2008, 14:52) *
Все тоже самое в Сишном стиле можно сделать красивее и быстрее чем на асм, и уже глядя на код нельзя будет его назвать "байдой".


Боюсь, что не получится. Команд будет поболее - до 30 в полновесном варианте. И надо, чтобы в идеале доступ был не по индексу, а по LookUp, т.к. коды команд могут меняться. Так что от таблицы никуда не денешься. Впрочем, если Вы пальцем ткнете, я пойму, что имеется ввиду...
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 21 2008, 17:49
Сообщение #31


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(_Pasha @ Feb 21 2008, 19:57) *
Код
void EE_Proof_Write_Block(const void *pointer_ram,
              void *pointer_eeprom,
              size_t n)
{
size_t j;
uint8_t *src,*dst;

src = (uint8_t*)pointer_ram;
dst = (uint8_t*)pointer_eeprom;

for (j=0;j<n;j++)
{
  if (eeprom_read_byte(dst) != *src)
      eeprom_write_byte(dst,(uint8_t)*src);
  dst++;
  src++;
}
return;
}

Несколько моментов про Ваш код:
- вместо цикла for лучше использовать do..while c проверкой size_t n == 0 только в самом начале..
- написав отдельно src++ и вызывая стандартные функции Вы сами себя лишили возможности
пользоваться автоинкрементом при доступе к данным в RAM
- каждая из функций eeprom_xxx будет ждать флага освобождения EEPROM
- каждая из функций eeprom_xxx будет прописывать один и тот же адрес в EEAR

попробуйте это отрихтовать и удивитесь лаконичности полученого кода...
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 21 2008, 19:09
Сообщение #32


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(defunct @ Feb 21 2008, 14:52) *
стиль кода Вашего примера не Сишный, и рад что вышло.


Нашел. Это паскалевский стиль. Обнаружился древний, почти безусловный рефлекс: как только в программе забрезжит хоть какой-то намек на ввод-вывод, бежим как от огня от перечислительных типов. Применительно к "С" это распространилось и на битовые поля. Так что, могу на любом языке, как на паскале. smile.gif

Кстати, был еще один рефлекс: клавишу F2 нажимать каждые 15 секунд. Ох и долго избавлялся!

Цитата(singlskv @ Feb 21 2008, 20:49) *
Несколько моментов про Ваш код:

попробуйте это отрихтовать и удивитесь лаконичности полученого кода...

Спасибо! Рихтуем, удивляемся... smile.gif
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 21 2008, 22:37
Сообщение #33


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(ReAl @ Feb 21 2008, 11:43) *
Ну извините... Так можно дойти до "а на асме я могу что-то другое делать, пока EEPROM пишется, а С-шная программа станет колом на всё время записи блока, во какой плохой язык С".
Вотъ, это тоже хотелось бы обсудить, хотя конкретно это, я уже пытался обсуждать,
но большинство опонентов высказалось в том смысле, что в IAR удобно обращаться к
таким(EEPROM) переменным и все тут...

А сейчас я вот задался интересным вопросом, если кто еще не заметил, в новых AVR (mega48/88/......)
есть такая фича, можно отдельно стирать ячейку и можно отдельно ее программировать...

Вопрос:
кто-нить уже пробовал записать в ячейку 2 раза без стирания ?
ИМХО, это открывает новые возможности для увеличения количества записи в eeprom,
но конечно, этот факт, требует дополнительной проверки, добровольцы есть ?
Go to the top of the page
 
+Quote Post
Artak
сообщение Feb 21 2008, 23:25
Сообщение #34


Участник
*

Группа: Свой
Сообщений: 41
Регистрация: 17-01-08
Пользователь №: 34 178



Ребята, может бить мой вопрос покажетса смехотwорным
но все- таки.
Если нужно умножить два восьмибитних числа и получить 16 битный результат достаточно написать
что то вроде
mul r16, r17 (конечно только для mega AVR)
и читать результат из пары r0,r1.
На C приходится писать
int result = (int)a * (int)b; или int result = (int)a * b; (a и b 8 битные)

Так как если написать int result = a * b то компилятор согласно правилам языка C сначала генерирует mul а потом обнуляет старший байт (результат умножения двух char получается в виде char и потом толко присвоивается int у)

А код скомплированный в результате int result = (int)a * (int)b; получаетса очень громоздкий, так как умножаяютса два 16 битных числа не обращая внимания на то что в старших байтах нули

чтобы решить проблему скорости мне пришлось в проекте написанном на C писать int result = a * b потом из сгенерированного асм листига вручную удалить clr r1 после mul-а и сгенерировать hex фаил в AVR Studio

А есть вообще грамотный способ решения таких проблем в C коде?

Сообщение отредактировал Artak - Feb 21 2008, 23:28
Go to the top of the page
 
+Quote Post
Rst7
сообщение Feb 22 2008, 06:29
Сообщение #35


Йа моск ;)
******

Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610



Цитата
А есть вообще грамотный способ решения таких проблем в C коде?


В IAR'е можете написать __multiply_unsigned( a , b ) - это непосредственно сгенерит mul. Для портабельности не забудьте на этапе компиляции проверить текущий процессор и компилятор, и если это не AVR и не IAR, сделать #define __multiply_unsigned(var1,var1) ((var1)*(var2)). Про остальные умножения можете прочитать в доке по IAR'у, ключевое слово "Intrinsic functions"


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post
Petka
сообщение Feb 22 2008, 07:25
Сообщение #36


Профессионал
*****

Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886



Цитата(singlskv @ Feb 22 2008, 01:37) *
Вопрос:
кто-нить уже пробовал записать в ячейку 2 раза без стирания ?
ИМХО, это открывает новые возможности для увеличения количества записи в eeprom,
но конечно, этот факт, требует дополнительной проверки, добровольцы есть ?


Велосипед уже изобретён. Atmel AN "AVR103"
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 22 2008, 07:41
Сообщение #37


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(singlskv @ Feb 21 2008, 20:49) *
Несколько моментов про Ваш код:
- вместо цикла for лучше использовать do..while c проверкой size_t n == 0 только в самом начале..
- написав отдельно src++ и вызывая стандартные функции Вы сами себя лишили возможности
пользоваться автоинкрементом при доступе к данным в RAM
- каждая из функций eeprom_xxx будет ждать флага освобождения EEPROM
- каждая из функций eeprom_xxx будет прописывать один и тот же адрес в EEAR

попробуйте это отрихтовать и удивитесь лаконичности полученого кода...


Поменял for() на while() и убрал лишнее копирование параметра size_t n . Т.е. while(n){}
Дальнейшее улучшение усиливает зависимость от железа, поэтому в данном случае (не библиотечном) не имеет смысла.
Бухгалтерия -12 байт. Итого в гору не более 24 байт на "крайне нежелательную" на первый взгляд функцию.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 22 2008, 09:45
Сообщение #38


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(Petka @ Feb 22 2008, 10:25) *
Велосипед уже изобретён. Atmel AN "AVR103"
Спасибо за наводку,
этот апликайшн как-то мимо меня пролетел...
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 22 2008, 10:47
Сообщение #39


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Теперь про асм.
В контексте многопоточного программирования очень полезно использование итераторов.
Пример:
Код
Iterate_16:
; input xL:xH = counter
; zL:zH = pointer to procedure/iteration body
    push    zL
    push    zh

    push    xL
    push    xH
    icall
    pop    xH
    pop    xL

    sbiw    xL,1
    brne    iterate_16_1    

    pop    zh
    pop    zL
    ret    

iterate_16_1:
    push    xL
    push    xh
    leave(Iterate_16_2)

Iterate_16_2:
    pop    xh
    pop    xL
    pop    zh
    pop    zL
    rjmp    Iterate_16


Данный итератор позволяет создавать циклы произвольной вложенности (в зависимости от размеров стека Вашего процесса). При этом подпрограмма -тело цикла вызывается косвенно, и может быть как атомарной, так и передавать управление системе, т.к. все параметры цикла как конструкции находятся в локальном стеке процесса.
Пример применения:
Код
func1:
; blah,blah,blah
    ret    
;...................................
func2:
    ldi16    xL,xh,0x100
    ldi16    zL,zh,func1;  
    jmp    Iterate_16
;...................................
func3:
    ldi16    xL,xh,0x0010
    ldi16    zL,zh,func2
    jmp    Iterate_16


В приведенном примере внешний цикл описан в func3, в котором вызывается внутренний цикл, описанный func2. Примерный сишный эквивалент
Код
int i,j;
for(i=0x0010;i >= 0;i--)
  for(j=0x100;j>=0;j--) func1();

... но в условиях кооперативного распределения ресурсов

P.S. Исправил примеры func2, func3. Была ошибка в загрузке Z. Кому интересно, обратите внимание.
Go to the top of the page
 
+Quote Post
ReAl
сообщение Feb 22 2008, 15:27
Сообщение #40


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(Artak @ Feb 22 2008, 01:25) *
На C приходится писать
int result = (int)a * (int)b; или int result = (int)a * b; (a и b 8 битные)
Так как если написать int result = a * b то компилятор согласно правилам языка C сначала генерирует mul а потом обнуляет старший байт (результат умножения двух char получается в виде char и потом толко присвоивается int у)
По стандарту языка С те два a,b обязаны быть приведены к int до выполнения команды умножения. Т.е. явне приведения не нужны, результат должен быть правильным. Иначе - в топку компилятор, С хорош стандартом, если для каждого компилятора надо менять привычки - то тогда проще на асме писать.

Цитата(Artak @ Feb 22 2008, 01:25) *
А код скомплированный в результате int result = (int)a * (int)b; получаетса очень громоздкий, так как умножаяютса два 16 битных числа не обращая внимания на то что в старших байтах нули
чтобы решить проблему скорости мне пришлось в проекте написанном на C писать int result = a * b потом из сгенерированного асм листига вручную удалить clr r1 после mul-а и сгенерировать hex фаил в AVR Studio


Код
#include <stdint.h>

uint16_t mul88(uint8_t a, uint8_t b)
{
    return a*b;
}


avr-gcc -O2 -S -mmcu=atmega8

Код
    .text
.global    mul88
    .type    mul88, @function
mul88:
/* prologue: frame size=0 */
/* prologue end (size=0) */
    mul r22,r24
    movw r24,r0
    clr r1
/* epilogue: frame size=0 */
    ret
Ну да, clr r1... Но уже после переноса результата умножения в пару R25:R24, так как по соглашениям avr-gcc регистр R1 содержит константу 0 (я бы предложил попробовать переделать это хозяйство на два временных регистра __tem_reg_l__, __temp_reg_h__ и R2 как константу 0, чтобы не мучиться каждый раз после умножения, но неизвестно, как занятие R2 скажется на остальном).


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Feb 22 2008, 23:10
Сообщение #41


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



В общем, наигрался я с "С". Память заканчивается, а проект лежит. Теперь переписываю на асме. Размер кода обещает быть в 2 раза короче (к слову о больших проектах). Да и пишется все по-быстрому, если знаешь, что делаешь. После чего все эти компиляторы, расплодившиеся в последнее время у меня в компе, летят в корзину. То же самое, кстати, касается и pic18 и dspic30/33 и HCS08, коими я иногда страдаю. Компилятор-то, оказывается, в голове...
Go to the top of the page
 
+Quote Post
defunct
сообщение Feb 22 2008, 23:37
Сообщение #42


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(_Pasha @ Feb 23 2008, 01:10) *
В общем, наигрался я с "С". Память заканчивается, а проект лежит. Теперь переписываю на асме.

Совет для того чтобы память не заканчивалась в проектах на "C"
1. Делать стек большим, а глобальных переменных поменьше.
2. Использовать heap.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 22 2008, 23:55
Сообщение #43


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(_Pasha @ Feb 23 2008, 02:10) *
В общем, наигрался я с "С". Память заканчивается, а проект лежит. Теперь переписываю на асме. Размер кода обещает быть в 2 раза короче (к слову о больших проектах).

Такая разница может получиться ТОЛЬКО на вычислительных алгоритмах и в основном
в местах где при использовании С необходимо делать преобразование типов,
ну или для вариантов когда просто в С нет соответствующего размера типа,
например в С нет 3-байтовых переменных...

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


Цитата(defunct @ Feb 23 2008, 02:37) *
Совет для того чтобы память не заканчивалась в проектах на "C"
1. Делать стек большим, а глобальных переменных поменьше.
2. Использовать heap.
Вот здесь хотелось бы разъяснений и поподробнее...(без подколок...),
я не понимаю как уменьшить количество глобальных переменных.
Я не понимаю как можно использовать heap для уменьшения расхода памяти вместо
использования глобальных переменных...
Go to the top of the page
 
+Quote Post
Alex B._
сообщение Feb 23 2008, 00:03
Сообщение #44


Знающий
****

Группа: Свой
Сообщений: 943
Регистрация: 6-07-04
Из: Санкт-Петербург
Пользователь №: 274



Цитата(singlskv @ Feb 23 2008, 02:55) *
я не понимаю как уменьшить количество глобальных переменных.
Я не понимаю как можно использовать heap для уменьшения расхода памяти вместо
использования глобальных переменных...

хм, а чего тут не понятного? sad.gif
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 23 2008, 00:08
Сообщение #45


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(Alex B._ @ Feb 23 2008, 03:03) *
хм, а чего тут не понятного? sad.gif
Содержательный ответ, а примерчик не подкините ?
Go to the top of the page
 
+Quote Post
defunct
сообщение Feb 23 2008, 00:10
Сообщение #46


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(singlskv @ Feb 23 2008, 01:55) *
Вот здесь хотелось бы разъяснений и поподробнее...(без подколок...),
я не понимаю как уменьшить количество глобальных переменных.

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

Цитата
Я не понимаю как можно использовать heap для уменьшения расхода памяти вместо
использования глобальных переменных...

это смотря под что у вас используются глобальные переменные.
Допустим имеем дело с неким протоколом где команды(пакеты) могут поступать не дожидаясь ответа на предыдущую команду(пакет). Предположим что глобально выделенно два буфера фиксированного размера (один обрабатывается, во второй принимается сл. команда). Попробуем сократить объем памяти.
Выделяем буфер из heap, ведем в него прием. По окончании приема, передаем указатель на этот буфер в функцию обработки, выделяем еще один буфер под прием сл. пакета и ведем прием дальше.
В функции обработки - перевыделяем память в соответсвии с реальным размером пакета (не всегда же пакет будет максимально возможной длины), или копируем во временный буфер в стеке, память освобождаем. Итого на лицо экономия памяти на 1 буфер в 99% времени работы нашего устройства (т.к. полных два буфера будет использоваться в очень короткий промежуток времени - только в момент между окончанием приема текущего пакета и запуском функции обработки).

Еще пример:
допустим по i2c раз в секунду надо что-то записать/прочитать.
Выделили буфер из heap - сказали i2c драйверу - читать/писать данные в него.
Все остальное время - эта память будет использоваться под что-то другое.

и так многие места где используются значительные объемы памяти можно разделять во времени с помощью heap'a - пользовать память эффективно (одну и ту же область для разных целей в разное время).
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 23 2008, 00:43
Сообщение #47


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(defunct @ Feb 23 2008, 03:10) *
А что Вы храните в глобальных переменных?
Ведь кроме конфигурации там больше нечего хранить.
Как это нечего ?
Вот вполне реальный пример, нужно по 4 каналам АЦП делать скользящее среднее по 8 измерениям,
ИТОГО:
4 * 8 * 2(байта на измерение) = 64 байта
+ 4(канала) * 2(байта) = 8 байт для хранения суммы
+ 1 байт на хранение номера канала
+ пару байт на всякие сервисные штучки
ИТОГО: ~75 байт глобальных переменных..

Пример N2:
Есть ~250байт которые есть суть текущее состояние системы, эти параметры могут быть запрошены
в любой момент например по протоколу модбас,
допустим что модбас работает на скорости 345600, можем ли мы хранить где-нить кроме глобальных
переменных эти данные ?
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 23 2008, 00:58
Сообщение #48


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



А мне просто супер нравится С. По-моему, для МК это совершенно оптимальный уровень абстракции. Здесь чётко видишь что такое объект или структура, что будет делать компилятор. Если необходимо вставить ASM кусок - пожалуйста, сразу видно как к сишным переменным обращаться. С другой стороны - совершенно наплевать как он память распределяет, - пусть сам думает. Как раз с этим не хочется возиться.

Я тоже наигрался с Си пытаясь добиться от него оптимального (как сделал бы сам на асме) кода. Ну кое где проколы есть у него, но он зато выигрывает при большом количестве глобальных переменных. Я не смог тут его сделать.

Не знаю как у вас получается "в два раза" - у меня 10-20%.

Единственное где можно капитально "надрать" компилятор, это если большое количество основных переменных в регистры влезает. Но это ведёт к сложности модификации программы. Что мы и имеем.

С другой стороны - какая песня иметь легко модифицируемый чёткий и ясный проект!!! Вот вы применяете кастрированную ОС своего исполнения. А если применить нормальную. Со всеми наворотами? Ну хорошо "не влазит задача". А можно спросить - куда она не влазит? Если, к примеру в m8, то есть m168/328. Разница - копейки. Если речь идёт о какой-нибудь m640, то ещё меньше разница на m1280/2560. Или на соответствующий ARM перейти со старшей модели тоже не фокус из под С. Короче, безусловно, дело вкуса, но очень спорный момент.

Когда просматриваешь кусочек, то да - кода много. А если сделать полный проект, то ваш выигрыш будет копеечный, а в замен сложная модифицируемость проекта. Си-шный сопровождать - на порядок легче. Это, конечно, моё личное мнение.
Go to the top of the page
 
+Quote Post
defunct
сообщение Feb 23 2008, 01:10
Сообщение #49


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(singlskv @ Feb 23 2008, 02:43) *
Как это нечего ?
Вот вполне реальный пример, нужно по 4 каналам АЦП делать скользящее среднее по 8 измерениям,
ИТОГО:
4 * 8 * 2(байта на измерение) = 64 байта

Я конечно не знаю вашей задачи полностью и возможно у вас есть некие строгие требования к сохранению фазы сигнала поэтому нужен КИХ, а я не брезгую БИХ'ом - ибо эффективней с т.з. памяти..
У меня скользящее по восьми измерениям занимает 2* U16 данных и кот наплакал кода.
итого 4 канала - 16 байт:
код для усреднения по 8-ми точкам:

Код
pChan->vAverage = (pChan->vSum + 4) >> 3;
pChan->vSum += val - pChan->vAverage;

но я не меняя размер данных могу и по 16-ти точкам усреднять и по 32-м и по 1024.
меняться будет только формула..
Конечно, это не тот случай когда heap чем-то помог, но все же думать об экономии там где это возможно - стоит.

Цитата
Пример N2:
Есть ~250байт которые есть суть текущее состояние системы, эти параметры могут быть запрошены
в любой момент например по протоколу модбас,
допустим что модбас работает на скорости 345600, можем ли мы хранить где-нить кроме глобальных
переменных эти данные ?

Статистика / параметры системы - это есть конфигурация...
Стало быть легально делать их глобальными.
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 23 2008, 11:09
Сообщение #50


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Из приведенного мной первого поста видно, что проигрыш по памяти - почти двукратный.
Правда ....
1) я не оптимизировал ...
2) На оба стэка выделил значительно больше памяти, чтобы не искать потом мучительно ....
3) На асме стек не учитывал ...

Тем не менее, если это всё поучитывать + взять равноценные проекты = всё равно коэффициент 1.3~1.4 думаю найболее реальный. Но памяти сейчас в микроконтроллерах вполне достаточно. Как правило, на большинстве задач на асме она остаётся, а на Си её просто достаточно. В тех же задачах где требуется много памяти, - не спасает использование ASMа, надо вешать внешнюю.
Go to the top of the page
 
+Quote Post
Rst7
сообщение Feb 23 2008, 20:18
Сообщение #51


Йа моск ;)
******

Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610



Цитата
pChan->vAverage = (pChan->vSum + 4) >> 3;pChan->vSum += val - pChan->vAverage;


Скажем так, это все таки обычный Low Pass Filter первого порядка. Немного от скользящего среднего отличается. Правда, обычно это пофиг. Мне другой вопрос не ясен. Нафига 2 переменных в структуре? Ведь Y=Y*(1-k)-k*v <=> Y=Y-k*(Y-v), не лишняя ли тут или vSum или vAverage?


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post
defunct
сообщение Feb 24 2008, 04:54
Сообщение #52


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(Rst7 @ Feb 23 2008, 22:18) *
Мне другой вопрос не ясен. Нафига 2 переменных в структуре? Ведь Y=Y*(1-k)-k*v <=> Y=Y-k*(Y-v), не лишняя ли тут или vSum или vAverage?

Для удобства пользования и для минимизации вычислений.
Если хранить только vSum то вычислять Average придется как минимум 2 раза. Первый раз при апдейте АЦП, второй раз при использовании.
Go to the top of the page
 
+Quote Post
Rst7
сообщение Feb 24 2008, 10:40
Сообщение #53


Йа моск ;)
******

Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610



Все... Туплю. Уже и ответ пространный написал, хорошо хоть отправить не бухнул, вовремя дошло. Просто хранится Y/k (k<1, конечно), а не Y. В младших битах - просто дробная часть...
Хотя, тут конечно, классическая дилемма время/память - или лишняя переменная или 2 раза считать.


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 24 2008, 18:47
Сообщение #54


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(defunct @ Feb 23 2008, 04:10) *
Я конечно не знаю вашей задачи полностью и возможно у вас есть некие строгие требования к сохранению фазы сигнала поэтому нужен КИХ, а я не брезгую БИХ'ом - ибо эффективней с т.з. памяти..
У меня скользящее по восьми измерениям занимает 2* U16 данных и кот наплакал кода.
итого 4 канала - 16 байт:
код для усреднения по 8-ми точкам:
Я тоже пользуюсь разными вариантами... иногда честное скользящее среднее
иногда экспоненциальный фильтр.
Цитата
Код
pChan->vAverage = (pChan->vSum + 4) >> 3;
pChan->vSum += val - pChan->vAverage;
и так тоже пользуюсь, тока для уменьшения рассчетов, я в таких случаях считаю что,
все вычисляется с коэфициентом (8 ну или сколько мы там решили),
тогда никагого оверхеда не происходит в принципе...
Цитата
Конечно, это не тот случай когда heap чем-то помог, но все же думать об экономии там где это возможно - стоит.
Ну вот я и хочу увидеть эту экономию, и особенно в сравнении с асм,
не, я конечно понимаю что для моих задач никакой экономии просто не будет,

Скажем так, хочу увидеть задачи (все таки контроллерные... хоть чуть...)
где использование heap будет нужным/оправданным..
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 24 2008, 19:50
Сообщение #55


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(singlskv @ Feb 24 2008, 22:47) *
Скажем так, хочу увидеть задачи (все таки контроллерные... хоть чуть...)
где использование heap будет нужным/оправданным..


Видете ли. Совершенно вам не в пику пишу. Это просто вопрос подходов. Вы его не приемлете (...хоть чуть..), а я считаю использование HEAP вполне оправданным.

Начнём с того, что использование HEAP на Си практически не увеличивает загрузку процессора. Поэтому вреда от этого никакого. А отсюда следует что его применение возможно везде. Теперь второе. В Си, в отличие от ASM, вы не контролируете память. Не знаю как правильнее выразится. Давайте лучше на примере.

Представьте, что вы пишите проект на ASM для которого чем больше свободной памяти - тем лучше. Вы его пишете и распределяете память вручную. Выделяете кусок в виде .byte, смотрите на стек и так далее. Задачу эту вы написали например на м64. Через некоторое время вы переползли на м640 где памяти вдвое больше. Вам придётся минимум пересчитать остаток свободной памяти и поменять одну константу. Можно так поступить и на Си. А можно использовать HEAP и тогда при переносе вам ничего менять не придётся. Не придётся ничего менять и пересчитывать также и при внесении изменений в проект . Но это пример, где можно или так или так.

Приведу пример, где я примененяю HEAP.

Представьте себе что контролер выполняет команды. Каждая команда имеет разную длину. От десятка байт до нескольких килобайт. Имеется очередь активных команд состоящая из 10 команд. Каждая команда имеет время старта и время смерти (начала и конца исполнения). То есть находящаяся в очереди команда может исполняться а может не исполняться, но после смерти удаляется из очереди и соответственно подгружается новая команда. Источник поступления команд может быть разный (причём одновременно) RS485, SD, Ethernet, Rf, EEPROM.

Здесь, на мой взгляд HEAP очень органично вписывается. Я делаю дефрагментацию (удаление дырки от выполненной команды), храню буфер активных команд (переменной длины) и буфер загружаемых команд (дабы к моменту когда понадобится новая команда - она уже находилась в контроллере - синхронизация загрузки и исполнения) тоже переменной длины. Чем более мощные команды я могу исполнять, тем больше возможности устройства. А это зависит от синхронизации загрузки и, соответственно от оптимальности распределения буферов внутри контроллера.
Go to the top of the page
 
+Quote Post
defunct
сообщение Feb 24 2008, 21:03
Сообщение #56


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(singlskv @ Feb 24 2008, 20:47) *
Скажем так, хочу увидеть задачи (все таки контроллерные... хоть чуть...)
где использование heap будет нужным/оправданным..

Ну так я ж выше привел такие примеры.
Везде где есть пакетная обработка - использование heap'a (точнее сказать использование динамической памяти) экономит расход.

Цитата
Здесь, на мой взгляд HEAP очень органично вписывается. Я делаю дефрагментацию (удаление дырки от выполненной команды), храню буфер активных команд (переменной длины)

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

плюсы - быстро, нет проблем с фрагментацией, простой менеджер памяти.
минусы - overhead на маленьких объемах, и ограничение по объему.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 24 2008, 22:23
Сообщение #57


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(SasaVitebsk @ Feb 24 2008, 22:50) *
Начнём с того, что использование HEAP на Си практически не увеличивает загрузку процессора.
Поэтому вреда от этого никакого. А отсюда следует что его применение возможно везде.
Увеличивает и очень прилично...
Я как бы понимаю необходимость использования heap например при загрузке картинки или
приеме пакета неизвестной длинны через Ethernet...
но для реалтайма непонимаю...
Цитата
Теперь второе. В Си, в отличие от ASM, вы не контролируете память. Не знаю как правильнее выразится.
Как это не контролирую ? Лично я контролирую, и это не сильно отличается от асм,
ну конечно printf, итд я просто не пользуюсь.
Цитата
Представьте, что вы пишите проект на ASM для которого чем больше свободной памяти - тем лучше. Вы его пишете и распределяете память вручную. Выделяете кусок в виде .byte, смотрите на стек и так далее. Задачу эту вы написали например на м64. Через некоторое время вы переползли на м640 где памяти вдвое больше. Вам придётся минимум пересчитать остаток свободной памяти и поменять одну константу. Можно так поступить и на Си. А можно использовать HEAP и тогда при переносе вам ничего менять не придётся. Не придётся ничего менять и пересчитывать также и при внесении изменений в проект . Но это пример, где можно или так или так.
Эээ..., ну просто стараюсь писать так чтобы перенос c одного чипа на другой заключался
только в переназначении пинов контроллера, обычно получаеться, правда иногда при переходе с более "старшего" чипа на младший приходится таймеры перенастраивать...
Цитата
Приведу пример, где я примененяю HEAP.
Представьте себе что контролер выполняет команды. Каждая команда имеет разную длину. От десятка байт до нескольких килобайт. Имеется очередь активных команд состоящая из 10 команд. Каждая команда имеет время старта и время смерти (начала и конца исполнения). То есть находящаяся в очереди команда может исполняться а может не исполняться, но после смерти удаляется из очереди и соответственно подгружается новая команда. Источник поступления команд может быть разный (причём одновременно) RS485, SD, Ethernet, Rf, EEPROM
Вот и я о том же, примерно, вопрос в том где это (использование heap) применимо/эфективно,
Вот для EEPROM, ИМХО, применимо/эфективно, только это не совместимо с использованием
стандартного для IAR _flash...
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 25 2008, 13:23
Сообщение #58


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(singlskv @ Feb 25 2008, 02:23) *
Увеличивает и очень прилично...


Тогда давайте сначала с терминами определимся. HEAP - просто слово и всё. Термин. С момента как вам понадобилась вся память, независимо от того сколько её, - вы уже работаете с HEAP, нравится вам этот термин или нет. А вот как вы с ней работаете - это уже вопрос.
Если вы работаете через стандартные процедуры malloc/free (как я понял об этом идёт речь), то никаких накладных расходов практически нет так как malloc фактически проверяет есть ли у вас нужный объём и прибавляет к указателю на свободное место этот объём. А free просто записывает новое значение указателя (с контроллем). Поскольку процедуры эти - не ногодрыганье, то, очевидно, что это времени не отнимает.
Если вы не используете стандартный диспетчер памяти, то вам приходится городить свой. Явно или неявно. Если не явно, то, в простейшем случае, вам придётся один раз при инициализации выделить всю память и в процессе работы контролировать границу, куда залазить нельзя (кольцевой буфер). В более сложной реализации вам придётся определять размер объекта и т.д., что по сути и делает стандартный набор средств. В ещё более сложной реализации (дефрагментация) вам придётся реализовать свой диспетчер.

Теперь опять возвратимся к терминам. Итак мы работаем с HEAP но не хотим в этом признаваться. Тогда у нас несколько путей. 1) вписываем работу с памятью прямо в тексте проги, 2) используем процедуры с уникальным названием, 3) используем стандартные названия (даже если процедуры пишем сами).

По-моему, лучше 3 путь. Всё таки это опыт программеров и общие термины. А вообще, и это мне тоже очень нравится, Си очень демократичный язык. Возьмите в своём проекте напишите процедуру malloc и вот вам стандартная и в тоже время более качественная (с вашей точки зрения) реализация.


PS: Не вижу ни каких проблем и с EEPROM и c FLASH. Можно сделать всё что угодно. Вплоть до реализации потоков и файлов. Или параллельной кучи и даже общей кучи. В винде вон часть памяти на винте находится территориально. А у меня, например EEPROM внешняя и внутренняя объёденены в общее пространство.
Всё в ваших руках. Не нравится - перепиши.
Go to the top of the page
 
+Quote Post
Непомнящий Евген...
сообщение Feb 26 2008, 09:29
Сообщение #59


Знающий
****

Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153



По-моему, с heap основная проблема в том, как рассчитать максимальную загрузку. И что делать, если память кончилась - допустимо ли отклонить запрос. Например, пример defunct - в 99% времени обработчику нужен только 1 пакет. Если таких обработчиков 5 - то какая куча нам нужна? На 10 пакетов? В этом случае мы не выигрываем по сравнению со статическим выделением памяти. А если, к примеру, на 6 пакетов - то что делать в ситуации, когда пакетов 7. Или можно доказать, что ни в один момент времени не будет больше 6 пакетов...
Следующий нюанс - если вы храните объекты разного размера, то можно получить ситуацию, когда из-за фрагментации невозможно будет разместить новый объект при наличии значительного кол-ва свободной памяти. Тут по-ходу придется мастерить что-то свое, стандартные механизмы работы с кучей не подойдут.
Лично я использую "самописную" кучу для пользовательского интерфейса - например, есть основной экран, из него можно попасть в меню, из него - в другое меню, оттуда - в настройку параметра, из нее иногда - в настройку еще одного параметра. При этом объекты уничтожаются в порядке, обратном созданию, соответственно реализация очень проста и нет фрагментации.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Feb 26 2008, 10:39
Сообщение #60


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(Непомнящий Евгений @ Feb 26 2008, 12:29) *
..............................
Примерно это я и имел в виду.
Просто в моих задачах выгоднее статически выделять память.
А про накладные расходы по работе с кучей я имел в виду не момент выделения памяти,
там действительно все быстро в случае если ее хватает и есть непрерывный кусок нужного
размера, а вопрос по сборке мусора после произвольных выделений/освобождений памяти,
и вот там совсем все не быстро/просто.
Go to the top of the page
 
+Quote Post
defunct
сообщение Feb 26 2008, 11:21
Сообщение #61


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(Непомнящий Евгений @ Feb 26 2008, 11:29) *
Например, пример defunct - в 99% времени обработчику нужен только 1 пакет. Если таких обработчиков 5 - то какая куча нам нужна? На 10 пакетов? В этом случае мы не выигрываем по сравнению со статическим выделением памяти. А если, к примеру, на 6 пакетов - то что делать в ситуации, когда пакетов 7. Или можно доказать, что ни в один момент времени не будет больше 6 пакетов...

Можно доказать, что не 10, а 9 пакетов будет достататочно всегда, а это уже экономия:
Вариант когда на все 5 интерфейсов одновременно приходит пакет, начинается обработка самого приоритетного интерфейса, пакет перекладывает в стек, остальные 4 интерфейса "отцепляют" свой текущий приемный буфер (ставят пакет в очередь на обработку) и выделяют себе другой. Итого 5+4 = 9.

Минимальное же количество необходимых буферов зависит от скорости интерфейсов и от скорости обработки пакетов - минимум который можно достичь - 6.. На практике - 7 буферов будет достаточно.
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Feb 26 2008, 11:50
Сообщение #62


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(singlskv @ Feb 26 2008, 14:39) *
Просто в моих задачах выгоднее статически выделять память.

Если динамическое распределение не нужно, то и не надо его за уши притягивать.
Я, к примеру, применять динамическое распределение при построении меню не стал бы, а вот Непомнящий Евгений реализовал и, как видите, доволен результатом. Это говорит, что мы все разные. А нужный инструмент выбирает программист исходя из видения своего проекта. А качество реализации зависит от того, как программист этим инструментом пользуется.

А знания, я в этом совершенно убеждён, никогда не являются лишними. Если ты знаешь, то точно можешь ответить, надо им пользоваться или нет. А если не знаешь, то не можешь и оценить.
Go to the top of the page
 
+Quote Post
Artak
сообщение Feb 26 2008, 20:26
Сообщение #63


Участник
*

Группа: Свой
Сообщений: 41
Регистрация: 17-01-08
Пользователь №: 34 178



Цитата(Rst7 @ Feb 22 2008, 10:29) *
В IAR'е можете написать __multiply_unsigned( a , b ) - это непосредственно сгенерит mul. Для портабельности не забудьте на этапе компиляции проверить текущий процессор и компилятор, и если это не AVR и не IAR, сделать #define __multiply_unsigned(var1,var1) ((var1)*(var2)). Про остальные умножения можете прочитать в доке по IAR'у, ключевое слово "Intrinsic functions"


Большое спасибо!!!
пользовалься CodeVisionAVR
перейду на IAR

Сообщение отредактировал Artak - Feb 26 2008, 20:40
Go to the top of the page
 
+Quote Post

5 страниц V   1 2 3 > » 
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 18:47
Рейтинг@Mail.ru


Страница сгенерированна за 0.02309 секунд с 7
ELECTRONIX ©2004-2016