Помогите понять, что творит оптимизатор Си AVR

Обсуждаем контроллеры компании Atmel.
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение COKPOWEHEU »

Наверное, я проглядел где вы указали добавить файлы в проект. Ну и подробности по работе #include. Это, конечно, расписано у тех же K&R, но пнуть явным образом все равно лучше.
С объявлениями переменных вопрос сложный. Если уж делить логику на несколько файлов, стоит минимизировать количество связей, а это тоже непросто.
Кстати, именно в данном случае может иметь смысл обойтись без *.c-файла, оставив всю логику в заголовке. Как я уже говорил, это применимо только когда этот заголовок подключается только в один файл исходного кода. Зато можно вынести настройки за пределы этого файла.
Не удержался и реализовал :oops:
Вложения
enc.rar
(18.66 КБ) 157 скачиваний
Последний раз редактировалось COKPOWEHEU Вс апр 03, 2016 09:49:12, всего редактировалось 1 раз.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение ARV »

COKPOWEHEU писал(а):  DDR_0( ENCODER_PIN1 ); //это мои хитрые макросы для расшифровки макроопределений наподобие (B,1) в PORTB, DDRB, PINB, 1
  DDR_0( ENCODER_PIN2 ); //если кому будет надо скину их.
надо же... и я сделал аналогичные макросы :) но, как мне кажется, мои поудобнее ваших будут. посмотрите сами (выкладываю файл целиком, но только начало у него более-менее завершенное, а остальное пока в стадии обдумывания целесообразности, хотя сам уже активно пользуюсь):
Спойлер

Код: Выделить всё

// Вспомогательные макросы для работы с AVR

#ifndef AVR_CONST_H_
#define AVR_CONST_H_

// вспомогательные общие макросы
#define _CONCAT_(x,y)      x ## y
/// вспомогательный макрос слияния символов
#define CONCAT(y,x)      _CONCAT_(y,x)

#define DDR(x)            CONCAT(DDR,x)
#define PORT(x)            CONCAT(PORT,x)
#define PIN(x)            CONCAT(PIN,x)

/// макрос для определения функции-инициализатора (вызывается автоматически в секции .inix)
#define INIT(x)         static void __attribute__((naked, used, section(".init" #x))) CONCAT(init, __COUNTER__) (void)
#define AUTOINIT()      INIT(2)
/// макрос для определения компактной версии функции main()
#define MAIN()         int __attribute__((OS_main)) main(void)

#define NOINIT         __attribute__((section(".noinit")))

// совместимые типы для целых чисел
typedef uint8_t      U8;
typedef uint16_t   U16;
typedef uint32_t   U32;
typedef int8_t      S8;
typedef int16_t      S16;
typedef int32_t      S32;

// делители тактовой для АЦП
#define ADC_DIV_2   _BV(ADPS0)
#define ADC_DIV_4   _BV(ADPS1)
#define ADC_DIV_8   (_BV(ADBP0) | _BV(ADPS1))
#define ADC_DIV_16   _BV(ADPS2)
#define ADC_DIV_32   (_BV(ADPS0) | _BV(ADPS2))
#define ADC_DIV_64   (_BV(ADPS2) | _BV(ADPS1))
#define ADC_DIV_128   (_BV(ADPS0) | _BV(ADPS1) | _BV(ADPS2))

// события для автозапуска АЦП
/// непрерывный автозапуск
#define ADC_TRIG_FRUN      0
/// аналоговый компаратор
#define ADC_TRIG_ACOMP      _BV(ADTS0)
/// запрос внешнего прерывания INT0
#define ADC_TRIG_INT0      _BV(ADTS1)
/// сравнение канала А нулевого таймера
#define ADC_TRIG_T0_COMPA   (_BV(ADTS0) | _BV(ADTS1))
/// переполнение нулевого таймера
#define ADC_TRIG_T0_OVF      _BV(ADTS2)
/// сравнение канала В первого таймера
#define ADC_TRIG_T1_COMPB   (_BV(ADTS2) | _BV(ADTS0))
/// сравнение канала В нулевого таймера
#define ADC_TRIG_T0_COMPB   ADC_TRIG_T1_COMPB
/// переполнение первого таймера
#define ADC_TRIG_T1_OVF      (_BV(ADTS2) | _BV(ADTS1))
/// изменение состояния пинов
#define ADC_TRIG_PCI      ADC_TRIG_T1_OVF
/// захват первого таймера
#define ADC_TRIG_T1_CAPTURE   (_BV(ADTS2) | _BV(ADTS0) | _BV(ADTS1))

// каналы АЦП
#define ADC_0            0
#define ADC_1            1
#define   ADC_2            2
#define ADC_3            3
#define ADC_4            4
#define ADC_5            5
#define ADC_6            6
#define ADC_7            7

#if defined(__AVR_ATmega88__) || defined(__AVR_ATmega48__) || defined(__AVR_ATmega168)
#define ADC_BANDGAP         15
#define ADC_GND            16
#endif

#if defined(__AVR_ATmega128__)
#define ADC_0_DIF_0_G10      8
#define ADC_1_DIF_0_G10      9
#define ADC_0_DIF_0_G200   10
#define ADC_1_DIF_0_G200   11
#define ADC_2_DIF_2_G10      12
#define ADC_3_DIF_2_G10      13
#define ADC_2_DIF_2_G20      14
#define ADC_3_DIF_2_G200   15
#define ADC_0_DIF_1_G1      16
#define   ADC_1_DIF_1_G1      17
#define ADC_2_DIF_1_G1      18
#define ADC_3_DIF_1_G1      19
#define ADC_4_DIF_1_G1      20
#define ADC_5_DIF_1_G1      21
#define ADC_6_DIF_1_G1      22
#define ADC_7_DIF_1_G1      23
#define ADC_0_DIF_2_G1      24
#define ADC_1_DIF_2_G1      25
#define ADC_2_DIF_2_G1      26
#define ADC_3_DIF_2_G1      27
#define ADC_4_DIF_2_G1      28
#define ADC_5_DIF_2_G1      29
#define ADC_BANDGAP         30
#define ADC_GND            31
#endif

#if defined(__AVR_ATtiny26__)
   #define ADC_REF_AVCC         0
   #define ADC_REF_EXTERNAL      _BV(REFS0)
   #define ADC_REF_INT_NO_ECAP      _BV(REFS1)
   #define ADC_REF_INT_WITH_ECAP   (_BV(REFS1) | _BV(REFS0))
#else
   #define ADC_REF_EXTERNAL      0
   #define ADC_REF_AVCC         _BV(REFS0)
   #define ADC_REF_INT_WITH_ECAP   (_BV(REFS1) | _BV(REFS0))
#endif


// аналоговый компаратор - момент возникновения прерываний
#define AC_INT_ON_TOGGLE         0
#define AC_INT_ON_FALLING         _BV(ACIS1)
#define AC_INT_ON_RISING         (_BV(ACIS1) | _BV(ACIS0))

// таймеры
// предделители тактовой
#define TIMER_CLK_DIV_1               1
#define TIMER_CLK_DIV_8               2
#define TIMER_CLK_DIV_64            3
#define TIMER_CLK_DIV_256            4
#define TIMER_CLK_DIV_1024            5
#define TIMER_CLK_EXT_FALL            6
#define TIMER_CLK_EXT_RISE            7
#define TIMER_CLK_DIV(x)            CONCAT(TIMER_CLK_DIV_,x)

// режимы работы выходов OCx - вместо параметра t ввести номер канала таймера,
// например 0 - нулевой таймер, 1А - первый таймер канал А
#define TIMER_OC_NONE(t)         0
#define TIMER_OC_TOGGLE(t)      _BV(COM ## t ## 0)
#define TIMER_OC_CLEAR(t)      _BV(COM ## t ## 1)
#define TIMER_OC_SET(t)         _BV(COM ## t ## 0) | _BV(COM ## t ## 1)

// внешние прерывания - условия срабатывания
// вместо i поставить номер запроса прерывания 0 или 1
#define   EXINT_LOWLEVEL(i)      0
#define EXINT_CHANGE(i)         _BV(ISC ## i ## 0)
#define EXINT_FALLING(i)      _BV(ISC ## i ## 1)
#define EXINT_RISING(i)         _BV(ISC ## i ## 0) | _BV(ISC ## i ## 1)

// делители тактовой SPI
#define SPI_CLK_DIV_4         0
#define SPI_CLK_DIV_16         _BV(SPR0)
#define SPI_CLK_DIV_64         _BV(SPR1)
#define SPI_CLK_DIV_128         _BV(SPR0) | _BV(SPR1)

// модуль TWI
// предделитель тактовой
#define TWI_CLK_DIV_1         0
#define TWI_CLK_DIV_4         _BV(TWPS0)
#define TWI_CLK_DIV_16         _BV(TWPS1)
#define TWI_CLK_DIV_64         _BV(TWPS0) | _BV(TWPS1)

#endif /* AVR_CONST_H_ */


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

например, вот так выглядит код демонстрационной программы, которая выводит текст на ЖКИ, если использованы эти макросы:

Код: Выделить всё

#include <avr/io.h>
#include "avr_helper.h"
#include "lcd.h"
#include "stdio.h"

MAIN(){
   printf("Hello,\nWorld!");
}
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение COKPOWEHEU »

Вы про эти макросы?
#define DDR(x) CONCAT(DDR,x)
#define PORT(x) CONCAT(PORT,x)
#define PIN(x) CONCAT(PIN,x)
Похожие у меня есть, но мне удобнее задавать букву и номер одним макросом, а не несколькими. Макросы настроек частот SPI, АЦП и т.д. конечно полезны, но я бы, наверное, вынес в отдельный файл. Можете посмотреть использование, да и реализацию моей версии.
L.O.D
Встал на лапы
Сообщения: 139
Зарегистрирован: Чт фев 11, 2016 18:35:37

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение L.O.D »

COKPOWEHEU писал(а):но пнуть явным образом все равно лучше.
Так явным образом и было, надо было только заглянуть под спойлер. :)

COKPOWEHEU писал(а):С объявлениями переменных вопрос сложный. Если уж делить логику на несколько файлов, стоит минимизировать количество связей, а это тоже непросто. Кстати, именно в данном случае может иметь смысл обойтись без *.c-файла, оставив всю логику в заголовке. Как я уже говорил, это применимо только когда этот заголовок подключается только в один файл исходного кода.
Ну, раз уж вы оба решили забежать вперед, приведу для Мikа свои пояснения.
Итак, препроцессор, встретив #include, подставляет вместо него все содержимое указанного в нем файла в тот файл, где он (#include) встретился. Из этого есть два основных следствия:
1. Можно разделить все функции и переменные .c-модуля на локальные и глобальные, и внести объявления глобальных в .h-файл, что обеспечит видимость этих объявлений в каждом .c-файле, имеющем соответствующий #include.
2. Необходимо позаботиться о том, чтобы многократное включение .h-файла не приводило к многократному созданию одинаковых функций и переменных в каждом .c-файле с таким #include'ом. С функциями это делается просто - у них объявление отличается от реализации отсутствием блока кода (func(); вместо func(){...;}). С переменными есть разные подходы. Один из самых распространенных заключается в том, что переменные объявляются в .h-файле с модификатором extern, что равносильно объявлению без создания. Но тогда приходится давать второе объявление этих переменных в .c-файле модуля, что ухудшает модифицируемость исходного кода, так как программист должен помнить о необходимости синхронного изменения объявлений переменных в .c и .h частях модуля. Одно время я выходил из положения таким образом:

Код: Выделить всё

sr595.c ----------
// сообщаем, что текущий файл - это исходник модуля:
#define   __SR595_MOD__
// теперь включаем объявления модуля:
#include "sr595.h"
---------- sr595.c

sr595.h ----------
...
#ifndef __SR595_MOD__
  #define __Vstorage   extern
#else
  #define __Vstorage
#endif

__Vstorage  unsigned char Encoder_State;
__Vstorage  signed   char Encoder_Summ;
__Vstorage  unsigned char Encoder_NewState;
...
---------- sr595.h

Теперь объявления переменных модуля существуют в единственном экземпляре и их редактирование не может привести к разнобою. Правда, необходимо будет определять флаг модуля перед #include'ом, но это можно забыть сделать только один раз для каждого модуля - до первой жалобы линкера на отсуствие этих переменных в объектниках. :)

А вот насчет помещения кода в заголовочные файлы ("в данном случае может иметь смысл обойтись без *.c-файла, оставив всю логику в заголовке") - я принципиально против этого. Всегда может понадобиться превратить такой модуль в нормальный, пригодный для многократного включения... и тогда придется, чертыхаясь на самого себя за лень, разделять объявления и реализацию между .h- и .c-файлами. Особенно громко приходится чертыхаться, когда это доводится делать спустя некоторое время после реализации модуля, когда её детали уже успели подзабыться. :)))
- Из овощей я больше всего люблю пельмени... © Соседский Мальчик
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение ARV »

COKPOWEHEU писал(а):но мне удобнее задавать букву и номер одним макросом, а не несколькими
вангую, что вы еще используете макросы для установки, сброса или переключения бита? :) тут, к сожалению, я противник такого подхода, потому ограничиваюсь только минимумом макросов.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение COKPOWEHEU »

А чего ванговать когда я пример кода выложил. Возьмите и посмотрите удобно это или нет.
В некоторых задачах прямое ногодрыжество удобнее, чем работа с целым портом. Например, при работе с энкодером, выводы RS и E в знакосинтезирующем дисплее (линии данных D0-D7 к ним не относятся), софтовый SPI, часть управления семисегментным индикатором и т.д. Это дает возможность упростить разводку без усложнения программы.
А вот насчет помещения кода в заголовочные файлы ("в данном случае может иметь смысл обойтись без *.c-файла, оставив всю логику в заголовке") - я принципиально против этого.
В некоторых случаях это допустимо. Скажем, дергать переферию из нескольких разных файлов исходного кода лично мне не представляется хорошей идеей. Кроме того, учитывая настройку портов через макросы, одинокий заголовочный файл подключать к нескольким проектам проще, чем парный (*.c + *.h). Впрочем, один раз я уже наступил на эти грабли, хотя пока и не выработал оптимальный для меня способ обхода.
Так явным образом и было, надо было только заглянуть под спойлер.
Прошу прощения, действительно проглядел первый пункт.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение ARV »

COKPOWEHEU писал(а):А чего ванговать когда я пример кода выложил. Возьмите и посмотрите удобно это или нет.
жизнь слишком коротка, чтобы рассматривать все коды :)))
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
L.O.D
Встал на лапы
Сообщения: 139
Зарегистрирован: Чт фев 11, 2016 18:35:37

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение L.O.D »

COKPOWEHEU писал(а):В некоторых случаях это допустимо.
Это субъективная оценка - без абсолютных, независимых от т.з. оценивающего, фактов. Я привел свои причины, по которым отказываюсь от таких "упрощений", и теперь каждый может примерить их к своей практике. :)

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

COKPOWEHEU писал(а):одинокий заголовочный файл подключать к нескольким проектам проще, чем парный (*.c + *.h)
Вообще не понимаю в чем трудность: что включить в проект еще один файл, что вписать еще один #iclude - дело нескольких секунд, а разработка длится днями, неделями и даже месяцами... в чем экономия-то?

COKPOWEHEU писал(а):когда я пример кода выложил
Кстати, там нужно транспонировать матрицу, на которой основан массив encoder_arr[16], так как координаты в матрице поменяны местами.
- Из овощей я больше всего люблю пельмени... © Соседский Мальчик
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение Мikа »

Здравствуйте, коллеги.

Сегодня или вчера большой день - первый раз мою тему закрепили, да еще и на технарском форуме. Растем! :)

Устройство, которое я делал, вчера полностью заработало и теперь отладка будет производиться "в полях".

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

Но думаю, что вопросов я вам еще позадаю, т.к. моя цель не добиться работоспособности дополнительных .c и .h файлов, а именно полностью понять, что и зачем в них делается.

На данный момент в итоге я хотел бы написать 2 файла (скажем, для энкодера), внутри которых необходимо только прописать дефайны на соответствие портов и бит, которые принимают участие в работе, а в самом main.c делать толькj

Код: Выделить всё

#include "Encoder.h"
...
Encoder();


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

Вот такие Наполеоновские планы.

Еще раз всем большое спасибо за помощь!
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
L.O.D
Встал на лапы
Сообщения: 139
Зарегистрирован: Чт фев 11, 2016 18:35:37

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение L.O.D »

Мikа писал(а):Далее хотелось бы несколько усложнить с программой динамической индикации, а именно при вызове функции передавать в неё переменную, которую необходимо разделить на отдельные символы и в последствии вывести их на индикатор.
А, так вот где эти goto понатыканы. :))) Да, это конечно делается не так. :)
- Из овощей я больше всего люблю пельмени... © Соседский Мальчик
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение Мikа »

Как я говорил, я заменил там goto на while и по занимаемой памяти оказалось больше, т.к.к у меня были смнения, влезет ли программа в 2к флеш, я оставил как было. И все же я не понимаю, почему это считается таким плохим, это же просто перевод программы на другую строку, или в Си это влечет за собой что-то страшное?
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
L.O.D
Встал на лапы
Сообщения: 139
Зарегистрирован: Чт фев 11, 2016 18:35:37

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение L.O.D »

Мikа писал(а):Как я говорил, я заменил там goto на while и по занимаемой памяти оказалось больше, ...
Не видел той замены, но на первый взгляд, объем не д.б. вырасти.
Мikа писал(а):И все же я не понимаю, почему это считается таким плохим, это же просто перевод программы на другую строку, или в Си это влечет за собой что-то страшное?
Это "влечет за собой что-то страшное" не в языке Си, а в голове программиста. :) Структурное программирование - это результат большой работы по борьбе за создание такого процесса программирования, который приводит к наименьшей головной боли, как для создателя исходного кода, так и для тех, кто потом этот код будет поддерживать. Каждый элементарный кусок алгоритма должен быть переведен в соотвествующий ему элементарный оператор, что не только упрощает понимание логики программы, но и снижает вероятность внесения ошибки в алгоритм, во время его изменения. Вот Вы уже захотели выделить часть кода, формирующего число для отображения, в отдельную функцию (пусть она будет называться FormNum) - что если во время модификации, в тело функции будет перетащен этот участок кода без самой первой метки? Было так:

Код: Выделить всё

int main()
{
unsigned int KoefficientSEC = Koefficient;

       ...
       Sotni:
       if (KoefficientSEC >= 100)
       {
           KoefficientSEC = KoefficientSEC - 100;
           Sotni++;
           goto Sotni;
       }
       Desyatki:
       if (KoefficientSEC >= 10)
       {
           KoefficientSEC = KoefficientSEC - 10;
           Desyatki++;
           goto Desyatki;
       }
       Edinici:
       if (KoefficientSEC >= 1)
       {
           KoefficientSEC = KoefficientSEC - 1;
           Edinici++;
           goto Edinici;
       }
       ...
}
Переписали так:

Код: Выделить всё

void FormNum(uint16_t Counter)
{
       if (Counter >= 100)
       {
           Counter = Counter - 100;
           Sotni++;
           goto Sotni;
       }
       Desyatki:
       if (Counter >= 10)
       {
           Counter = Counter - 10;
           Desyatki++;
           goto Desyatki;
       }
       Edinici:
       if (Counter >= 1)
       {
           Counter = Counter - 1;
           Edinici++;
           goto Edinici;
       }
}

int main()
{
       ...
       Sotni:
       FormNum(Koefficient);
       ...
}
Из этой функции, по goto Sotni, управление будет передано в функцию main без осуществления возврата, а за меткой Sotni снова стоит вызов функции FormNum - и так до бесконечности, точнее - до исчерпания стека и вылета программы. Вероятность таких ошибок растет по мере роста числа операторов в теле такого цикла (метка становится все дальше от конца цикла) и по мере прохождения времени (забываются детали реализации).
А если бы было так:

Код: Выделить всё

int main()
{
       ...
       Edinici = Desyatki = Sotni = 0;

       while ( (Koefficient -= 100) >= 100 )   ++Sotni;

       while ( (Koefficient -= 10)  >=  10 )   ++Desyatki;

       while ( (Koefficient -= 1)   >=   1 )   ++Edinici;  // а точнее : Edinici = Koefficient;
       ...
}
Такая ошибка была бы в принципе невозможна. :)

А в жизни модификации кода бывают и позаковыристее, и помногочисленнее! И ищи потом свищи этого поручика Киже. :)))

P.S. Заметьте: действия в этих wile - абсолютно те же самые, что и в Вашем коде с if и goto, а потому объем кода д.б. таким же!
- Из овощей я больше всего люблю пельмени... © Соседский Мальчик
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение COKPOWEHEU »

жизнь слишком коротка, чтобы рассматривать все коды :)))

Если уж заинтересовались, могли бы и посмотреть. К тому же код простой. Впрочем, дело ваше.
Это субъективная оценка
Именно поэтому я не говорю так категорично: "нет так делать нельзя никогда и точка".
Не вижу связи с выделением функций работы с неким периферийным устройством в отдельный модуль.
В противном случае придется городить семафоры или еще какие способы разделения доступа, чтобы два куска не полезли одновременно.
Кстати, там нужно транспонировать матрицу, на которой основан массив encoder_arr[16], так как координаты в матрице поменяны местами.
А разница, если все равно не известно какой из выводов должен замыкаться раньше? Кому надо будет поменяет местами выводы или знак результата. Ну и использовал я не вашу матрицу, а пересчитал свою. Если они совпадают - хорошо, значит все правильно.
Как я говорил, я заменил там goto на while и по занимаемой памяти оказалось больше, т.к.к у меня были смнения, влезет ли программа в 2к флеш, я оставил как было
Тогда бы уж ассемблерную вставку сделали, если гонитесь за оптимальностью.
А это означает, что самое время перейти к "причесыванию" программы.
Хотелось бы посмотреть на результат.
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение Мikа »

Сейчас сел за работу. И тут же задался вопросом: а на чем лучше писать программы для AVR? С или С++ ? Велика ли разница? Пойду погуглю для начала, как раз чайник закипел :)

UPD: нашел статью на Хабре (https://habrahabr.ru/post/149683/), учитывая то, что я не понимаю того, о чём там пишут, думаю пока что не выдумывать и поработать на С, хотя последний абзац звучит очень многообещающе:

СпойлерДля чего нужен C++
Это тема для отдельной статьи, которую я готов написать, если вам это интересно. Если кратко, то при грамотном использовании C++ позволяет писать столь же эффективные решения, что и решения на C, но при этом получать более читаемый и более безопасный за счет проверок компилятора код. А механизм шаблонов позволяет писать эффективные реализации обобщенных алгоритмов, что проблематично на C. А еще я к нему привык. В любом случае, я крайне прошу воздержаться от дискуссии на эту тему сейчас.
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение COKPOWEHEU »

Если писать на С++ в стиле "Си с классами", это может быть эффективнее, чем чистый Си. Однако обилие неочевидных особенностей провоцирует использовать "красивые" решения. Однако, скажем, переопределение методов в наследных классах, слишком сложно для контроллера. Короче, использовать можно, но если четко понимать что делаешь. С Си попроще. Ну и в любом случае чтобы хоть как-то оценить качество генерируемого компилятором кода и его узкие места надо знать ассемблер. Так что советую остановиться на связке Ассемблер + Си. Когда освоитесь, можно будет попробовать С++. В таком применении он не будет сильно отличаться от обычного Си, так что переучиваться как с ПК-версиями не придется.
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение Мikа »

С Ассемблером я знаком, я с ним часы напролет тусовался и дома и на работе :)

Я, кстати, скачал файлы, в которых реализован Энкодер от COKPOWEHEU, там мало понятного для меня, поэтому улучшение самой функции опроса энкодера оставим на потом, сейчас нужно научиться делать функции во внешних файлах :)

В общем, что и следовало ожидать - с ходу у меня ничего не получилось, но сегодня с новыми ошибками :)

При компиляции получается вот что:

Error ld returned 1 exit status
Error undefined reference to `Encoder' EncoderExternal
Error undefined reference to `getEncoderState' EncoderExternal

О чём гласит первая ошибка мне не понятно, следующие говорят о том, что не определена ссылка на эти функции, но я же написал их прототипы в .h файле, сам файл подключил в обоих .c файлах, все как было описано.

Ниже под спойлерами я продублирую содержимое файлов, также в приложении архив с проектом и скриншот того, как я добавил файлы в проект.

Итак, файл main.c
Спойлер

Код: Выделить всё

/*
 * EncoderExternal.c
 *
 * Created: 04.04.2016 0:01:35
 * Author : Михаил
 */

#include <avr/io.h>
#include "Encoder.h"


int main(void)
{
    // Port D init
   DDRD = 0x00;
   
   
   getEncoderState(); // Получить состояние энкодера в первый раз
   
    while (1)
    {
      Encoder();
    }
}




файл Encoder.h
Спойлер

Код: Выделить всё

#ifndef  __Encoder_H__
#define  __Encoder_H__

// Здесь нет никаких переменных, потому что функция пользууется только локальными переменными

void getEncoderState(void);
void Encoder(void);

#endif


Файл Encoder.c
Спойлер

Код: Выделить всё

#include <avr/io.h>
#include "Encoder.h"

static unsigned char Encoder_State = 0;
static signed char Encoder_Summ = 0;
static unsigned char Encoder_NewState = 0;

void getEncoderState(void)      // Это нужно для того чтобы получить первое значение состояния энкодера при пуске программы
{
   Encoder_State = PIND & 0x3;
}

void Encoder(void)
{
   Encoder_NewState = PIND & 0x3;
   if(Encoder_State != Encoder_NewState)
   {

       asm("cli"); //!

      switch(Encoder_State)
      {
         case 2:
         {
            if(Encoder_NewState == 3) Encoder_Summ++;
            if(Encoder_NewState == 0) Encoder_Summ--;
            break;
         }

         case 0:
         {
            if(Encoder_NewState == 2) Encoder_Summ++;
            if(Encoder_NewState == 1) Encoder_Summ--;
            break;
         }
         case 1:
         {
            if(Encoder_NewState == 0) Encoder_Summ++;
            if(Encoder_NewState == 3) Encoder_Summ--;
            break;
         }
         case 3:
         {
            if(Encoder_NewState == 1) Encoder_Summ++;
            if(Encoder_NewState == 2) Encoder_Summ--;
            break;
         }
      }
      
      if (Encoder_Summ == 4) // Увеличение значения числа
      {
         // Выполняемое действие 1
         // В прицнципе здесь должна увеличиться или уменьшиться переменная, передаваемая из основновной программы, но как это сделать пока что не знаю
         Encoder_Summ = 0;
      }

      if (Encoder_Summ == (-4)) // Уменьшение значения числа
      {
         // Выполняемое действие 2
         // В прицнципе здесь должна увеличиться или уменьшиться переменная, передаваемая из основновной программы, но как это сделать пока что не знаю
         Encoder_Summ = 0;
      }

         Encoder_State=Encoder_NewState;
         asm("sei")
         // Код здесь выполнится в любом случаае
   }
}


Я писал я все это по полному подобию того, как вы говорили, но что-то не так.
Надеюсь, вы еще не спите :D
Вложения
Include.png
(8.57 КБ) 301 скачивание
EncoderExternal.zip
(17.12 КБ) 142 скачивания
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
pokk
Вымогатель припоя
Сообщения: 574
Зарегистрирован: Вт ноя 02, 2010 17:46:37

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение pokk »

Мikа писал(а):..........
L.O.D, относительно перевода чисел в уме - я мел ввиду именно десятичные в двоичные. .......

Я для перевода чисел в двоичную систему нашёл программу TheLucentHexCalс простая прога, но гораздо удобнее калькулятора. Установки не требует. Для быстрого запуска закинул ярлык в папку system32 и вызываю её через Win+R.
https://cloud.mail.ru/public/CEJV/gCpQRaBw6
L.O.D
Встал на лапы
Сообщения: 139
Зарегистрирован: Чт фев 11, 2016 18:35:37

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение L.O.D »

COKPOWEHEU писал(а):В противном случае придется городить семафоры или еще какие способы разделения доступа, чтобы два куска не полезли одновременно.
Какое "два куска полезли одновременно", если цель выделения всей логики работы с периф.у-вом в отдельный модуль как раз в том и состоит, чтобы никто другой к этому п/у не обращался напрямую? Что-то Вы не договариваете, оттого я не могу понять, в чем состоят Ваши опасения и contra.
COKPOWEHEU писал(а):А разница, если все равно не известно какой из выводов должен замыкаться раньше? Кому надо будет поменяет местами
Разница в том, что Вы пишете конкретному человеку (Mika) и он еще далеко не как рыба в воде в наших с Вами примерах кода, призванных помочь ему написать его функции правильнее, оптимальнее и т.д. Взяв Ваш вариант, он обнаружит, что все заработало строго наоборот и решит, что снова допустил ошибку, станет ее искать, потратит еще два дня неизвестно на что...
Мikа писал(а):а на чем лучше писать программы для AVR? С или С++ ?
На Си. Язык должен соответствовать задаче, а потому применять язык с высоким уровнем абстракции для программирования довольно простого контроллера - неадекватный подход. Кроме того, приступать к ООП, не освоившись еще с ФОП, несколько нерационально и самонадеянно. Как однажды сказал Иоанн Кронштадтский - глупо пытаться освоить высшую математику, не владея арифметикой. Он, правда, привел такое сравнение, говоря не о программировании, но мысль остается справедливой по отношению к любой области человеческой деятельности.
Мikа писал(а):файлы, в которых реализован Энкодер от COKPOWEHEU, там мало понятного для меня
Он просто запрограммировал самостоятельно то, что делает компилятор, когда работает с двумерными массивами. Не во всех ситуациях правильно брать на себя эту работу, но для Вас будет правильно сначала понять мой код, заменяющий switch'и на извлечение из массива, а потом уже убедиться в том, что код COKPOWEHEU является разновидностью этой замены и увидеть тот хинт, который он применил. Не раскусите сами - стучитесь, но лучше сделать это самому - больше пользы, да и интеллектуальное удовлетворение получите. :)
Мikа писал(а):Надеюсь, вы еще не спите
Лично я настолько не сплю, что придется отложить рассмотрение на вечер. :)
- Из овощей я больше всего люблю пельмени... © Соседский Мальчик
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение COKPOWEHEU »

Взяв Ваш вариант, он обнаружит, что все заработало строго наоборот
А с чего вы взяли что заработает наоборот? Я лично не возьмусь вот так, без исходных данных, гарантировать, что при подключении первого вывода энкодера (ага, первого с какой стороны?) к ENCODER_PIN_1 положительное направление будет соответствовать вращению по часовой стрелке (кто сказал что это удобнее?). Тут достаточно собрать схему и уже в железе смотреть чему соответствует вращение, при необходимости инвертировав программно.
Он просто запрограммировал самостоятельно то, что делает компилятор, когда работает с двумерными массивами.
Грубо говоря, да. Вот только битовая магия работает гораздо быстрее, чем "честная" работа с двумерным массивом. Правда, при составлении кода я шел немного с другой стороны. Чтобы определить направление вращения нужно знать текущее состояние (2 бита) и предыдущее (еще 2 бита). Комбинация этих четырех байт в 75% случаев однозначно соответствует углу поворота (переходы 00<->11 и 01<->10 правильно декодировать невозможно). Собираем эти две пары в одно четырехбитое число и используем его в качестве индекса массива. По сути тоже самое, но о двумерном массиве я тогда и не думал.
Впрочем, можно совместить оба варианта с помощью битовых полей, но это будет еще большая магия.
L.O.D
Встал на лапы
Сообщения: 139
Зарегистрирован: Чт фев 11, 2016 18:35:37

Re: Помогите понять, что творит оптимизатор Си AVR

Сообщение L.O.D »

COKPOWEHEU писал(а):А с чего вы взяли что заработает наоборот?
Сравните с исходным кодом Mika и убедитесь.
COKPOWEHEU писал(а):битовая магия работает гораздо быстрее, чем "честная" работа с двумерным массивом.
Не о магии сейчас нужно заботиться, а о том, как Mika в этом разбираться будет. :)
- Из овощей я больше всего люблю пельмени... © Соседский Мальчик
Ответить

Вернуться в «AVR»