|
Работа с ногами AVR на С++, Проблема переноса с IAR на WinAVR |
|
|
|
Nov 4 2009, 17:42
|

Местный
  
Группа: Свой
Сообщений: 268
Регистрация: 4-11-05
Пользователь №: 10 470

|
Всем доброго времени суток! Понадобилось перенести проект с IAR (EWAVR 530) на WinAVR (20090313) для юридической чистоты... Вылезла такая бяка: Иар успешно работает с такой конструкцией: Код template <unsigned char volatile * port, const char bit> class mypin { public: inline mypin() {*(port-1)|=(1<<bit);} // смещение -1 - регистр DDR - настраиваем на выход. static inline void set() {*port|=(1<<bit);} }; int main() { mypin<&PORTA,4> mypinname; // тут мы обьявляем место подключения и инициализируем одновременно //... mypinname.set(); //... } А WinAVR глючит на конструкции &PORTA в объявлении класса и заявляет что не хватает параметров: Код - wrong number of template arguments (1, should be 2) provided for 'template<volatile uint8_t* port, char bit> class mypin' хотя в другом месте адрес порта успешно изымается: Код volatile uint8_t* portC = &PORTC; *portC &= ~(1<<7); Это глюк компилятора? Как это можно обойти? Подскажите пожалуйста!
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 22)
|
Nov 4 2009, 20:19
|

Местный
  
Группа: Свой
Сообщений: 268
Регистрация: 4-11-05
Пользователь №: 10 470

|
Код template <uint8_t volatile & port, const char bit> class mypin { public: __inline mypin(){*(&port-1)|=(1<<bit);} static __inline void set() {port|=(1<<bit);} };
mypin<PORTA,4> mypinname; mypinname.set(); такой вариант работает в иаре, но ухудшается оптимизация. ВинАВР же опять не доволен строчкой "mypin<PORTA,4> mypinname;": Код - template argument 1 is invalid - `*' cannot appear in a constant-expression - a cast to a type other than an integral or enumeration type cannot appear in a constant-expression звезда там берется из разложения макроса PORTA => (*(volatile uint8_t *)((0x1B) + 0x20))
|
|
|
|
|
Nov 4 2009, 21:20
|

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

|
Цитата(Genadi Zawidowski @ Nov 4 2009, 23:01)  А не надо делать предположения об адресе регистра управления направлением. Заведите явный параметр для регистра управления направлением и настанет благодать... Шоб это было так просто... Если объявить volatile-переменную и передавать её адрес mypin< &vv >, то и с этим констурктором работает и ожидаемый код генерит, а если даже его убрать а на место &PORTA вручную вписать Код mypin< (volatile uint8_t *)0x1B > mypinname; то всё равно ругается Код p.cpp:13: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression p.cpp:13: error: template argument 1 is invalid
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Nov 4 2009, 22:37
|

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

|
Цитата(Genadi Zawidowski @ Nov 4 2009, 23:26)  Что не так в версии: А я почём знаю, что ему не так. Код ================= E:\>c:\WinAVR/bin/avr-g++ -Wall -Wextra --version 2>&1 avr-g++ (GCC) 4.4.0 20090323 (experimental) Copyright (C) 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
E:\>c:\WinAVR/bin/avr-gcc -Wall -Wextra -Os -S -mmcu=atmega64 p.cpp 2>&1 p.cpp: In function 'void foo()': p.cpp:14: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression p.cpp:14: error: '*' cannot appear in a constant-expression p.cpp:14: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression p.cpp:14: error: '*' cannot appear in a constant-expression p.cpp:14: error: template argument 1 is invalid p.cpp:14: error: template argument 2 is invalid p.cpp:14: error: invalid type in declaration before ';' token p.cpp:15: error: request for member 'set' in 'mypinname', which is of non-class type 'int'
===================
avr-gcc.exe (WinAVR 20090313) 4.3.2 Copyright (C) 2008 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
p.cpp: In function 'void foo()': p.cpp:14: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression p.cpp:14: error: `*' cannot appear in a constant-expression p.cpp:14: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression p.cpp:14: error: `*' cannot appear in a constant-expression p.cpp:14: error: template argument 1 is invalid p.cpp:14: error: template argument 2 is invalid p.cpp:14: error: invalid type in declaration before ';' token p.cpp:15: error: request for member 'set' in 'mypinname', which is of non-class type 'int'
===================
avr-gcc.exe (GCC) 4.2.2 (WinAVR 20071221) Copyright (C) 2007 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
p.cpp: In function 'void foo()': p.cpp:14: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression p.cpp:14: error: `*' cannot appear in a constant-expression p.cpp:14: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression p.cpp:14: error: `*' cannot appear in a constant-expression p.cpp:14: error: template argument 1 is invalid p.cpp:14: error: template argument 2 is invalid p.cpp:14: error: invalid type in declaration before ';' token p.cpp:15: error: request for member 'set' in 'mypinname', which is of non-class type 'int'
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Nov 5 2009, 05:45
|

Местный
  
Группа: Свой
Сообщений: 268
Регистрация: 4-11-05
Пользователь №: 10 470

|
Genadi, даже если уйти от смещений и явно указывать регистры, то проблема остается. Ведь компилятор ругается не на это. Он не хочет принимать указатель как константу. В теме с исходниками нашел такой файлик "avrio.hpp" от Аlex312. Там эта задача реализована через enum-ы: Код enum __bits {__bit_0=0, __bit_1=1, __bit_2=2, __bit_3=3, __bit_4=4, __bit_5=5, __bit_6=6, __bit_7=7}; enum __ports{__port_A,__port_B,__port_C,__port_D,__port_E,__port_F,__port_G}; enum __ports_reg {__reg_PORT, __reg_DDR, __reg_PIN};
// Шаблон функции, определяющей требуемые адреса в пространстве В/В template <__ports port, __ports_reg reg> volatile uint8_t& __ports_ref();
#define AVRLIB_SPECIALIZE_PORT_REF(_port_) \ template <> inline volatile uint8_t& __ports_ref<__port_##_port_, __reg_PORT>() { return PORT##_port_; } \ template <> inline volatile uint8_t& __ports_ref<__port_##_port_, __reg_DDR >() { return DDR##_port_; } \ template <> inline volatile uint8_t& __ports_ref<__port_##_port_, __reg_PIN >() { return PIN##_port_; }
#if defined (PORTA) AVRLIB_SPECIALIZE_PORT_REF(A) #endif #if defined (PORTB) AVRLIB_SPECIALIZE_PORT_REF(B) #endif ..... template <__ports port, __bits bit> class __pin { public: static inline void set(void) { __ports_ref<port, __reg_PORT>()|=bit_mask(bit); } }; __pin<__port_C,__bit_4> out4; out4.set(); Довольно муторно, но работает. Правда на этот раз несварение возникает у Иара. Даже на максимальной оптимизации он упорно отказывается сводить "out4.set();" к ассемблерному "sbi".
|
|
|
|
|
Nov 6 2009, 18:07
|

Местный
  
Группа: Свой
Сообщений: 268
Регистрация: 4-11-05
Пользователь №: 10 470

|
AHTOXA, посмотрел я на ваши труды и попробовал написать подобное для АВР. Сначала было так: Код template<GPIO_mode_t mode, char port, int bit, char activestate> class Pin{ inline volatile uint8_t & PORTx() { #if defined (PORTA) if (port == 'A') return PORTA; #endif #if defined (PORTB) if (port == 'B') return PORTB; #endif //..................такая конструкция для буков С..F и все вместе для DDRх и PINх} inline void On() { if ( mode==BIDIR || mode==OUTPUT ) {activestate == 'L' ? PORTx()&=~mask : PORTx()|=mask;} if ( mode==OUTPUT_OK ) {activestate == 'L' ? DDRx()|=mask : DDRx()&=~mask;} } // и тп } Потом пытался уменьшить количество писанины в части ссылок на регистры: Код template<char port> class Base_Pin;
template<> // такой шаблон для всех буков - можно сделать макрос class Base_Pin<'A'> { public: inline volatile uint8_t & PORTx(){return PORTA;} inline volatile uint8_t & DDRx(){return DDRA;} inline volatile uint8_t & PINx(){return PINA;} };
template<GPIO_mode_t mode, char port, int bit, char activestate> class Pin2 : public Base_Pin<port> { inline void On(){Base_Pin<port>::PORTx()|=(1<<bit);} }; И если дальше "упрощать" создав макрос для объявления базовых классов всех портов, то приходим к необходимости отказаться от символьных констант и воспользоваться enum-ом. В итоге получаем код Alex312. Но я оставил себе первый вариант. Он хоть и громоздкий, но зато легко читаемый\понимаемый. И да кстати. Я так и не понял в чем вкусность переопределения операторов в примере Антона.
|
|
|
|
|
Nov 6 2009, 20:02
|

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

|
Цитата(Waso @ Nov 6 2009, 23:07)  Сначала было так: Код template<GPIO_mode_t mode, char port, int bit, char activestate> ... Ну и нормально  Только #if defined (PORTA) - лишнее, ибо не будет вызываться, пока вы не определите Pin<'A'...>. В этом прелесть шаблонов  Цитата Потом пытался уменьшить количество писанины в части ссылок на регистры: Код template<char port> class Base_Pin; ... template<GPIO_mode_t mode, char port, int bit, char activestate> class Pin2 : public Base_Pin<port> ... От наследования тут мало толку, всё равно приходится указывать имя базового класса. Здесь удобнее определить включаемый класс, типа Код // объявление шаблона регистров порта: template<char port>struct port_regs_t; // специализация для каждого порта: template <> struct port_regs_t<'A'> { inline volatile uint8_t & PORTx(){return PORTA;} ... }; ... // и наконец, собственно шаблон ноги: template<GPIO_mode_t mode, char port, int bit, char activestate> class Pin2 { private: typedef port_regs_t<port> regs_t; public: typename regs_t::PORTx PORTx; typename pins_t::DDRx DDRx; typename pins_t::PINx PINx; ... }; В варианте для MSP430 (а именно на него я давал ссылку  ) у меня примерно так и сделано. А вы, похоже, смотрели вариант для stm32? Цитата И да кстати. Я так и не понял в чем вкусность переопределения операторов в примере Антона. Дык, просто что-то не сложилось со ссылками (или просто не догадался). Потому пришлось определять все операции. Если работает со ссылками, то так конечно лучше. Надо будет попробовать... И, кстати, одно замечание по параметрам. Имхо, включать режим работы ножки (GPIO_mode_t) в перечень параметров шаблона - идеологически неверно. Потому что иногда случается, что режим работы ноги надо переключать на ходу.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 6 2009, 20:43
|

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

|
Цитата(AHTOXA @ Nov 6 2009, 22:02)  Потому что иногда случается, что режим работы ноги надо переключать на ходу. "я даже больше скажу" - для тех ног, для которых оно в начале программы один раз ставится и всё - для них не жалко и в начале программы несколько строк DDRx = 0xXX написать, а вот если уж начинаешь переключать.... offtopic: Я пока в эти все темы с интересом заглядываю, но использую всё те же "Волковские" макросы, нет времени на эти эксперименты. Зато, похоже, мне нравится placement new
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Nov 7 2009, 19:19
|

Местный
  
Группа: Свой
Сообщений: 268
Регистрация: 4-11-05
Пользователь №: 10 470

|
Цитата Ну и нормально Только #if defined (PORTA) - лишнее, ибо не будет вызываться, пока вы не определите Pin<'A'...>. В этом прелесть шаблонов Да? А я ведь сначала без этих излишеств написал. У меня WinAVR тогда ругается Код 'PORTE' was not declared in this scope ну и действительно такого порта нет в восьмой меге например. А компилятор всетаки анализирует весь текст видимо. Может я чтото не так сделал? С другой стороны для порта A дейсвительно это лишняя проверка, потомучто этот порт есть у всех авр-ок  По поводу режима ноги - для сложных случаев я оставил режим BIDIR. Можно еще режимов напридумывать. Я ж ведь ради чего это делаю - чтобы не париться с инициализацией ножек. Хочется чтоб это все делал конструктор. Вот кстати непонятка: если обьявление Pin<'A'...> сделано НЕ внутри какойто функции, то куда вставит компилятор код конструкторов? Цитата А вы, похоже, смотрели вариант для stm32? Да. Но я просмотрел всю тему. Полезного почерпнул не только из последнего поста.
|
|
|
|
|
Nov 7 2009, 22:17
|

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

|
Цитата(Waso @ Nov 8 2009, 00:19)  У меня WinAVR тогда ругается Код 'PORTE' was not declared in this scope Хм. Действительно. Надо подумать  Цитата По поводу режима ноги - для сложных случаев я оставил режим BIDIR. Можно еще режимов напридумывать. Я ж ведь ради чего это делаю - чтобы не париться с инициализацией ножек. Хочется чтоб это все делал конструктор. Зачем плодить сущности? Пусть её инициализирует конструктор, но не конструктор ножки, а конструктор объекта, которому принадлежит ножка. Ну хотя дело конечно ваше  Цитата Вот кстати непонятка: если обьявление Pin<'A'...> сделано НЕ внутри какойто функции, то куда вставит компилятор код конструкторов? В глобальную инициализацию. Обычно после обнуления секции BSS. Цитата Но я просмотрел всю тему. Полезного почерпнул не только из последнего поста.  Это хорошо  Просто мне показалось, что вариант MSP больше подходит для AVR.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 24 2009, 18:19
|

Местный
  
Группа: Свой
Сообщений: 268
Регистрация: 4-11-05
Пользователь №: 10 470

|
Продолжу задавать вопросы по плюсам в этой теме. Предыстория та-же. Написал класс клавиатуры MtxKeyboard со статическими переменными и функциями. Не забыл добавить в него виртуальные функции, которые будут и у наследников. Наследники такие: Код template<int row, int col> class MtxButton : public MtxKeyboard { public: /*virtual /*inline*/ uint8_t Get() { return State[(col-1)]&(1<<(row-1));} /*virtual /*inline */ uint8_t Pressed() { return (Get()&&(Ticks>=KEYPRES_TRESHOLD))? 1 : 0;} /*virtual /*inline */ uint8_t Locked() { return (Get()&&(Ticks>=KEYLOCK_TRESHOLD))? 1 : 0;} }; Простенькая тестовая прога: Код MtxButton<1,1> Key_Num1; MtxButton<1,2> Key_Num2; MtxButton<1,3> Key_Num3; MtxButton<1,4> Key_Num4; //========== еще 16 кнопок
class Client_c {public: Client_c(MtxKeyboard * pB):pButton(pB){}; MtxKeyboard * pButton;};
Client_c Client_1(&Key_Num1); Client_c Client_2(&Key_Num2); Client_c * pClient[]={&Client_1,&Client_2};
int main(void){ for(;;) { Keys.Scan(); if (Key_Num1.Pressed())pin1.Toggle(); if (Client_1.pButton->Pressed())pin2.Toggle(); } return 0;} на максимальной оптимизации в WinAVR130309 компилится в 500+ байт кода и около 10байт ОЗУ. Но вот как только я делаю функции наследников виртуальными - таже прога компилится в 3000+ байт кода и 230байт ОЗУ. Я в курсе про таблицу вызова виртуальных процедур, но 2 тыщи слов она не сожрет. В чем проблема? По сгенеренному асму не могу понять.. Какбудто оптимизация отключается. На всякий случай приложил исходники.
|
|
|
|
|
Nov 24 2009, 18:44
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Как только функции стали виртуальными у класса MtxButton<> появились собственные данные - указатель на таблицу виртуальных методов. Соответственно появились конструкторы, что бы эти данные проинициализировать. Далее, т.к. объекты статические и с конструктором, появились вызовы конструкторов до main, что потянуло всю run-time поддержку для вызова таких конструкторов и деструкторов. Можно спросить - нафига наследовать MtxButton от MtxKeyboard , которая по жизни должна быть одна на всех, да еще и с виртуальными функциями, что вообще нонсенс. Такое наследование логически породит столько клавиатур, сколько будет создано кнопок. Если уж хочется приткнуть куда нибудь клавиатуру, сделай так: Код class BtnBase { protected: static MtxKeyboard keyboard; };
template<int row, int col> class MtxButton : public BtnBase { public: uint8_t Get() { return keyboard.State[(col-1)]&(1<<(row-1));} uint8_t Pressed() { return (Get()&&(keyboard.Ticks>=KEYPRES_TRESHOLD))? 1 : 0;} uint8_t Locked() { return (kGet()&&(keyboard.Ticks>=KEYLOCK_TRESHOLD))? 1 : 0;} };
|
|
|
|
|
Nov 25 2009, 06:16
|

Местный
  
Группа: Свой
Сообщений: 268
Регистрация: 4-11-05
Пользователь №: 10 470

|
Цитата Можно спросить - нафига наследовать MtxButton от MtxKeyboard , которая по жизни должна быть одна на всех, да еще и с виртуальными функциями, что вообще нонсенс. Ну я сделал все члены класса MtxKeyboard статическими, так что этот класс не должен размножаться при появлении дочерних классов. Но ваша запись со вложенным статическим классом более грамотна, спасибо. Виртуализовать функции кнопок я хочу чтобы была возможность загонять указатели на них в массивы и потом динамически перебирать в цикле. Хотя это и не всегда надо и всегда можно обойти, но если есть возможность без огромных неоправданных затрат использовать красивости плюсов - я хочу их использовать. Просто я только начинаю осваивать плюсы и могу легко сваять слона из мухи по неопытности. Такчто не раздражайтесь пожалуйста сильно по этому поводу.
|
|
|
|
|
Nov 26 2009, 07:19
|

Местный
  
Группа: Свой
Сообщений: 268
Регистрация: 4-11-05
Пользователь №: 10 470

|
Ладно, чтобы не смотрели на меня косо, открою карты. =) Мне надо написать прогу для работы пульта оператора, к которому подключается до 20 панелей клиентов. Для каждого клиента на пульте есть кнопка и двухцветный светик. Поведение всех клиентов описывается одной машиной состояний (StateMachine). Я задумал описать класс клиента, который содержит статус, процедуру обработки состояний, и ссылки на свои кнопку и светик, которые инициализируются в конструкторе. Кнопки и светики описаны в отдельных классах с интерфейсом "Нажата?", "Включить красный", "Выключить" и тп.. В main будет цикл, который будет перебирать массив указателей на классы клиентов и запускать процедуру обработки очередного клиента. Одна виртуальная функция добавляет 50байт кода для каждой кнопки или светика. Умножаем на кол-во кнопок и светиков, и еще на колво виртуальных методов и получаем много килобайт, невлезаем. Вот я и думаю как бы поизящнее решить эту проблему, чтобы поменьше писанины и пооптимальнее код.
От виртуальных функций можно избавиться, избавившись от шаблонов и заплатив парой байт ОЗУ на каждую кнопку и светик - помнить номер строки и столбца не как параметр шаблона, а как константу.
Интересно как решали такую проблему другие. =)
|
|
|
|
|
Nov 26 2009, 08:09
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(Waso @ Nov 26 2009, 10:19)  Одна виртуальная функция добавляет 50байт кода для каждой кнопки или светика. Это не виртуальная функция добавляет, а шаблонный класс. Для такого применения шаблоны не годятся. Цитата Вот я и думаю как бы поизящнее решить эту проблему, чтобы поменьше писанины и пооптимальнее код. От виртуальных функций можно избавиться, избавившись от шаблонов и заплатив парой байт ОЗУ на каждую кнопку и светик - помнить номер строки и столбца не как параметр шаблона, а как константу. Именно так. Пара констант (не обязательно в ОЗУ, кстати) займут много меньше кода, чем 3 функции. Цитата Интересно как решали такую проблему другие. =) Можно вообще отказаться от констант, если передавать их в качестве параметра (или глобала) в цикле в main. Такое решение позволит съэкономить память, но идет вразрез с ООП. (Собственно при таком подходе это будет обычная, не ООП программа)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|