Реклама на сайте English version  DatasheetsDatasheets

KAZUS.RU - Электронный портал. Принципиальные схемы, Datasheets, Форум по электронике

Новости электроники Новости Литература, электронные книги Литература Документация, даташиты Документация Поиск даташитов (datasheets)Поиск PDF
  От производителей
Новости поставщиков
В мире электроники

  Сборник статей
Электронные книги
FAQ по электронике

  Datasheets
Поиск SMD
Он-лайн справочник

Принципиальные схемы Схемы Каталоги программ, сайтов Каталоги Общение, форум Общение Ваш аккаунтАккаунт
  Каталог схем
Избранные схемы
FAQ по электронике
  Программы
Каталог сайтов
Производители электроники
  Форумы по электронике
Помощь проекту

Микроконтроллеры, АЦП, память и т.д Темы касающиеся микроконтроллеров разных производителей, памяти, АЦП/ЦАП, периферийных модулей...

 
Опции темы
Непрочитано 19.03.2010, 20:52  
neiver
Временная регистрация
 
Регистрация: 30.07.2007
Сообщений: 51
Сказал спасибо: 1
Сказали Спасибо 12 раз(а) в 7 сообщении(ях)
neiver на пути к лучшему
По умолчанию О программировании AVR на C++

Бытует мнение, что программировать 8-ми битные контроллеры, такие как AVR, ввиду их скромных ресурсов, лучше всего на на ассемблере или в крайнем случае на С. А С++ с его классами, виртуальными функциями, исключениями и прочей объектно ориентированной шелухой для них мало подходит. Так ли это на самом деле. Попробуем разобраться в этом вопросе.
Полные исходные тексты к примерам: http://github.com/KonstantinChizhov/AvrProjects
Реклама:
neiver вне форума  
Непрочитано 19.03.2010, 20:54  
neiver
Временная регистрация
 
Регистрация: 30.07.2007
Сообщений: 51
Сказал спасибо: 1
Сказали Спасибо 12 раз(а) в 7 сообщении(ях)
neiver на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

Для начала выясним, какие возможноси языка С++ можно использовать для эффективного программирования для AVR:
- классы – само собой.
- исключения – сразу про них забудем – знатный пожиратель ресурсов (для AVR их поддержка по умолчанию выключена).
- шаблоны и метапрограммирование.
- полиморфизм (виртуальные функции).

Разбираться будем на конкретных примерах:
- ножкодрыгательство – работа с портами и пинами.
- АЦП.
- структуры данных: массив, стек, кольцевой буффер.
- USART.
- динамический диспетчер с программными таймерами.
- ЖК индикатор (HD44780).
- семисегментный индикатор.
- драйвер шагового двигателя.
neiver вне форума  
Непрочитано 19.03.2010, 21:45  
_guardianangel
Почётный гражданин KAZUS.RU
 
Регистрация: 01.04.2009
Адрес: Рязань
Сообщений: 1,140
Сказал спасибо: 21
Сказали Спасибо 635 раз(а) в 344 сообщении(ях)
_guardianangel на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

neiver - ждем с не терпением
_guardianangel вне форума  
Непрочитано 19.03.2010, 22:52  
neiver
Временная регистрация
 
Регистрация: 30.07.2007
Сообщений: 51
Сказал спасибо: 1
Сказали Спасибо 12 раз(а) в 7 сообщении(ях)
neiver на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

Общие положения.
В язаках С и С++ поддержка модульности развита очень слабо:
заголовочные файлы вставляются простым копированием на место директивы #include, а работа с разными единицами трансляции полностью отдана компоновщику, с которой он не всегда справляется хорошо.
Пример:
В одном *.c или *.cpp файле находится несколько функций. Если функция не используется внутри файла, компилятор не может исключить её из компиляции, поскольку не знает, будет ли она использоваться в других единицах трансляции. Линковщик неиспользуюмую функцию так-же удалить не сможет, поскольку не знает используется ли она внутри своего модуля.
В Си, чтобы избежать таких вещей при написании библиотек, пользуются правилом: одна единица трансляции (читай *.с файл) – одна функция. Тогда линковщик с лёгкостью может отфильтровать неиспользуемые функции. Однако это не очень удобно. Компилятор может проводить оптимизацию только в пределах одной единицы трансляции. Конфигурировать код при этом можно только с помощью препроцессора.
Я предпочитаю использовать другой подход. В программе имеется только одна единица трансляции, например файл «main.cpp», а весь библиотечный код находится в заголовках. Все функции в библиотеках объявлены как inline. Это позволяет компилятору оптимизировать всю программу целиком. Это позволяет получить более компактный и быстрый код. Что очень существенно для маленьких встраиваемых систем. Но увеличивает время компиляции – каждый раз программа перекомпилируется полностью. В данном случае, я думаю можно пожертвовать временем компиляции в пользу более компактного и быстрого кода. Собственно, все приведенные примеры будут следовать этому принципу.
neiver вне форума  
Непрочитано 19.03.2010, 23:40  
kison
Почётный гражданин KAZUS.RU
 
Регистрация: 13.12.2004
Сообщений: 3,172
Сказал спасибо: 11
Сказали Спасибо 692 раз(а) в 504 сообщении(ях)
kison на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

Сообщение от neiver Посмотреть сообщение
В одном *.c или *.cpp файле находится несколько функций. Если функция не используется внутри файла, компилятор не может исключить её из компиляции, поскольку не знает, будет ли она использоваться в других единицах трансляции. Линковщик неиспользуюмую функцию так-же удалить не сможет, поскольку не знает используется ли она внутри своего модуля.
Начались "великие открытия". Все эти проблемы проблемами не являются. Путь первый - квалификатор static на функции не используемые в других модулях. Все - компилятор имеет контроль и прекрасно может выкидывать неиспользуемые функции сам. Кроме того это повышает возможности самого оптимизатора - он может инлайнить функции подчистую, не оставляя копий ее на случай вызова из других модулей.
Второе решение - сообщить компилятору ( есть такие смешные штуки - ключи и параметры) что мы требуем от него размещать каждую функцию в отдельной секции.
Ну и попросить у линкера откинуть неиспользуемые секции. И все - проблем нет.
Ничего лишнего в код не попадет. Есть правда смешные исключения - можно потерять например бутлоадер, ведь он в коде часто явно не вызывается. Но можно просто собрать его отдельно, хотя это и неудобно. А можно просто для файла с загрузчиком не просить выкидывать неиспользуемое.

Сообщение от neiver Посмотреть сообщение
Я предпочитаю использовать другой подход. В программе имеется только одна единица трансляции, например файл «main.cpp», а весь библиотечный код находится в заголовках.
Надо еще не забыть, что и отлаживаться придется не по простой и логичной структуре, а по свалке "все в одном". На любителя. Вообще в таком подходе минусов больше чем плюсов в десятки раз. А думать о лучшей оптимизации - смешно. Компилятор оптимизирует функциями, а не файлами. Так что выгоды не будет. А вот попотеть с перекрестными связями между модулями придется. Причем в результате потом нельзя будет просто взять модуль и включить его в другой проект, модуль надо править и существенно. Что в С, что в С++ это плохой стиль. Особенно в С++, язык создавался для упрощения ведения больших проектов. А всесто упрощения получим помойку из исходников. Вообще странно - принципам раздельной компиляции уже много лет. И все они никак не могут уйти. Может причина в том, что они достаточно эффективны?
kison вне форума  
Непрочитано 19.03.2010, 23:57  
neiver
Временная регистрация
 
Регистрация: 30.07.2007
Сообщений: 51
Сказал спасибо: 1
Сказали Спасибо 12 раз(а) в 7 сообщении(ях)
neiver на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

Ножкодрыгательство – работа с портами и пинами.
Ножкодрыгательство это основное предназначение маленьких контроллеров и работа с портами составляет большую часть объёма программы. Поэтому хотелось бы с ними работать как можно удобнее.
Традиционно для этого используются битовые операции:
PORTA |= (1 ‹‹ 5); // установить бит 5 в порте А.
PORTA &= ~(1 ‹‹ 5); // сбросить бит 5 в порте А.
Что умело преобразуется компилятором в:
sbi PORTA, 5
cbi PORTA, 5
Что не может не радовать. Порт и номер пина обычно задаются с помощью #define-а. И когда мы хотим подергать пин, надо помнить в каком он порту (а ещё есть DDR и PIN регистры), и какой у него номер. Не очень удобно. А хочется завести переменную для пина, передавать её как параметр в функции и т.д. Некоторые компиляторы предлагают для таких целей нестандартные расширения, но это всё баловство.

Попробуем объединить порт и номер пина в одной сущности:

Код:
class Pin{
public:
	Pin(volatile unsigned char &port, uint8_t pin)
	:_port(port)
	{
		_pin = pin; 
	}
	void Set()const
	{
		_port |= (1 ‹‹ _pin);
	}
	void Set(uint8_t val)const
	{
		if(val)
			Set();
		else Clear();
	}
	void Clear()const
	{
		_port &= (uint8_t)~(1 ‹‹ _pin);
	}
	void Togle()const
	{
		_port ^= (1 ‹‹ _pin);
	}
	void SetDirWrite ()const
	{
		*(&_port - 1) |= (1 ‹‹ _pin);
	}
	void SetDirRead()const
	{
		*(&_port - 1) &= (uint8_t)~(1 ‹‹ _pin);
	}
	uint8_t IsSet()const
	{
		return (*(&_port - 2)) & (uint8_t)(1 ‹‹ _pin);
	}
private:
	volatile unsigned char &_port; 
	uint8_t _pin;
};
В функциях SetDirRead, SetDirWrite и IsSet мы воспользовались фактом, что DDRX и PINX регистры порта имеют адреса на 1 и 2 меньше соответствующего регистра PORTX.

Теперь напишем маленький пример использования такого класса:
Код:
int main()
{
	Pin pin1(PORTA, 1);
	pin1.SetDirWrite();
	pin1.Set();
	pin1.Clear();
	pin1.Togle();
	pin1.SetDirRead();
	if(pin1.IsSet())
		pin1.Clear();
}
И его ассемблерный листинг:
Код:
 	
main:
sbi 58-32,1	// pin1.SetDirWrite();
sbi 59-32,1	// pin1.Set();
	cbi 59-32,1	// pin1.Clear();

	in r24,59-32	// pin1.Togle();
	ldi r25,lo8(2)
	eor r24,r25
	out 59-32,r24
.
	cbi 58-32,1 	// pin1.SetDirRead();

	sbic 57-32,1	// if(pin1.IsSet())
	cbi 59-32,1	// pin1.Clear();

	ldi r24,lo8(0)	//return 0; раз уж объявили main как int 
	ldi r25,hi8(0)
	ret
Уже не плохо.
neiver вне форума  
Непрочитано 20.03.2010, 00:09  
kison
Почётный гражданин KAZUS.RU
 
Регистрация: 13.12.2004
Сообщений: 3,172
Сказал спасибо: 11
Сказали Спасибо 692 раз(а) в 504 сообщении(ях)
kison на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

Сообщение от neiver Посмотреть сообщение
Уже не плохо.
А на заказ можете? Все то же самое:
Код:
int main()
{
	Pin pin1(PORTA, 1);
	pin1.SetDirWrite();
	pin1.Set();
	pin1.Clear();
	pin1.Togle();
	pin1.SetDirRead();
	if(pin1.IsSet())
		pin1.Clear();
}
Кристалл мега 128. Порт F, вывод PF3 (58 ножка)
Хочу увидеть листинг.
kison вне форума  
Непрочитано 20.03.2010, 00:17  
neiver
Временная регистрация
 
Регистрация: 30.07.2007
Сообщений: 51
Сказал спасибо: 1
Сказали Спасибо 12 раз(а) в 7 сообщении(ях)
neiver на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

Для kison:
Код:
.global	main
	.type	main, @function
main:
.LFB55:
.LSM0:
/* prologue: function */
/* frame size = 0 */
.LBB20:
.LBB21:
.LBB22:
.LSM1:
	lds r24,97
	ori r24,lo8(8)
	sts 97,r24
.LBE22:
.LBE21:
.LBB23:
.LBB24:
.LSM2:
	lds r24,98
	ori r24,lo8(8)
	sts 98,r24
.LBE24:
.LBE23:
.LBB25:
.LBB26:
.LSM3:
	lds r24,98
	andi r24,lo8(-9)
	sts 98,r24
.LBE26:
.LBE25:
.LBB27:
.LBB28:
.LSM4:
	lds r24,98
	ldi r25,lo8(8)
	eor r24,r25
	sts 98,r24
.LBE28:
.LBE27:
.LBB29:
.LBB30:
.LSM5:
	lds r24,97
	andi r24,lo8(-9)
	sts 97,r24
.LBE30:
.LBE29:
.LBB31:
.LBB32:
.LSM6:
	lds r24,96
.LBE32:
.LBE31:
.LSM7:
	sbrs r24,3
	rjmp .L2
.LBB33:
.LBB34:
.LSM8:
	lds r24,98
	andi r24,lo8(-9)
	sts 98,r24
.L2:
.LBE34:
.LBE33:
.LBE20:
.LSM9:
	ldi r24,lo8(0)
	ldi r25,hi8(0)
/* epilogue start */
	ret
neiver вне форума  
Непрочитано 20.03.2010, 00:19  
neiver
Временная регистрация
 
Регистрация: 30.07.2007
Сообщений: 51
Сказал спасибо: 1
Сказали Спасибо 12 раз(а) в 7 сообщении(ях)
neiver на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

А теперь попробуем объявить pin1 глобальной переменной:

Код:
 	
Pin pin1(PORTA, 1);
int main()
{
	pin1.SetDirWrite();
	pin1.Set();
	pin1.Clear();
	pin1.Togle();
	pin1.SetDirRead();
	if(pin1.IsSet())
		pin1.Clear();
}
И…..

И pin1.SetDirWrite() превращается в:

Код:
lds r30,pin1
	lds r31,(pin1)+1
	ld r18,-Z
	ldi r20,lo8(1)
	ldi r21,hi8(1)
	movw r24,r20
	lds r0,pin1+2
	rjmp 2f
1:	lsl r24
	rol r25
2:	dec r0
	brpl 1b
	or r18,r24
	st Z,r18
Этот код работает с портом через указатель – медленно и громоздко.
Глобальные переменные защищены от посягательств оптимизатора.
Здесь нужен какой-то другой подход…
neiver вне форума  
Непрочитано 20.03.2010, 00:25  
neiver
Временная регистрация
 
Регистрация: 30.07.2007
Сообщений: 51
Сказал спасибо: 1
Сказали Спасибо 12 раз(а) в 7 сообщении(ях)
neiver на пути к лучшему
По умолчанию Re: О программировании AVR на C++.

Сообщение от kison Посмотреть сообщение
он может инлайнить функции подчистую, не оставляя копий ее на случай вызова из других модулей.
А если у него нет доступа к ее исходному коду, только объектный файл?
Незаинлайнит.
neiver вне форума  
 

Закладки
Опции темы

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

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
AVR JTAGICE MKII - проблемы firmware... Luxurious AVR 25 20.10.2014 10:50
Ищу книги по AVR rocky7 Микроконтроллеры, АЦП, память и т.д 9 17.03.2010 12:43
AVR. Как правильно совместить LCD и ISP на PORTB? Serg3621 Микроконтроллеры, АЦП, память и т.д 8 04.02.2010 14:03


Часовой пояс GMT +4, время: 19:13.


Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot