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

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

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

Сообщение COKPOWEHEU »

Наверное, я проглядел где вы указали добавить файлы в проект. Ну и подробности по работе #include. Это, конечно, расписано у тех же K&R, но пнуть явным образом все равно лучше.
С объявлениями переменных вопрос сложный. Если уж делить логику на несколько файлов, стоит минимизировать количество связей, а это тоже непросто.
Кстати, именно в данном случае может иметь смысл обойтись без *.c-файла, оставив всю логику в заголовке. Как я уже говорил, это применимо только когда этот заголовок подключается только в один файл исходного кода. Зато можно вынести настройки за пределы этого файла.
Не удержался и реализовал :oops:
Вложения
enc.rar
(18.66 КБ) 158 скачиваний
Последний раз редактировалось COKPOWEHEU Вс апр 03, 2016 09:49:12, всего редактировалось 1 раз.
Реклама
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18546
Зарегистрирован: Чт дек 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
Ум, честь и совесть. И скромность.
Сообщения: 18546
Зарегистрирован: Чт дек 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
Ум, честь и совесть. И скромность.
Сообщения: 18546
Зарегистрирован: Чт дек 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 КБ) 302 скачивания
EncoderExternal.zip
(17.12 КБ) 143 скачивания
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
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»