123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- из каментов к
- http://easyelectronics.ru/avr-uchebnyj-kurs-programmirovanie-na-si-chast-2.html
- То есть (по крайне мере, по моему) вместо
- while(PORTB & (1 << MYPIN_DRIVE_LOCK))
- гораздо приятнее писать
- while(ControlBus.DriveLock)
- Я так себе вообще написал удобные макросы, с помощью которых порты определяю так:
- BEGIN_PORT_DEFINITION
- DEFINE_PORT_PIN(RowFeedback)
- DEFINE_PORT_PIN(RowForwarder)
- DEFINE_PORT_PIN(RowDataline)
- DEFINE_PORT_PIN(ColFeedback)
- DEFINE_PORT_PIN(ColForwarder)
- DEFINE_PORT_PIN(AnodeSwitch)
- END_PORT_DEFINITION(Matrix, A)
- инициализирую так:
- HalConfigurePin(Matrix, RowFeedback, IN, PULLUP );
- HalConfigurePin(Matrix, RowForwarder, OUT, );
- HalConfigurePin(Matrix, RowDataline, OUT, );
- HalConfigurePin(Matrix, ColFeedback, IN, PULLUP );
- HalConfigurePin(Matrix, ColForwarder, OUT, );
- HalConfigurePin(Matrix, AnodeSwitch, IN, Z );
- А юзаю примерно так:
- Matrix.Out->RowDataline = 1;
- ImpulseP(Matrix.Out->RowForwarder);
- Matrix.Out->RowDataline = 0;
- if(Matrix.In->RowFeedback)
- {
- //
- // Отсутствует отклик с RCM-шины. Нет ни одного RCM
- // или сбой в первом RCM
- //
- PostErrorSound(POSTERROR_NO_RCM_FEEDBACK);
- }
- ИМХО это выразительнее, чем побитовые опрерации, при равной производительности
- и компактности кода.
- ----
- ОК, сейчас объясню.
- Фишка в том, что в Си мы имеем возможность юзать bit-field’ы (кто не знает что
- это такое, ищите сами, я здесь объяснять не буду).
- У нас порт это (обычно) 8 пинов и мы работаем с каждым пином дёргая
- соответствующие биты в соответствующих портах.
- Ну а доступ к отдельным битам можно получать по разному: можно с помощью
- bitwise-операций, а можно с помощью bit-field’ов. Адекватному компилятору не
- должно быть никакой разницы, написали ли мы
- foo |= 2
- или же у нас
- foo.SecondBitField = 1
- он в обоих случаях генерирует один и тот же машинный код.
- Так вот, есть у нас, скажем, PORTC, у которого первые три пина — выходы, на
- которых висят светодиоды, потом один пулап-вход и 2 высокоимп. входа.
- Мы могли бы объявить 6 констант и работать с регистрами порта с помощью
- обычных побитовых операторов, примерно так:
- #define MY_PIN_RED_LED 0
- #define MY_PIN_YELLOW_LED 1
- #define MY_PIN_GREEN_LED 2
- #define MY_PIN_BUTTON 3
- #define MY_PIN_AUX1 4
- #define MY_PIN_AUX2 5
- ...
- DDRC = 0;
- // Делаем первые три пина выходами
- DDRC |= ((1 << MY_PIN_RED_LED) | (1 << MY_PIN_YELLOW_RED) | (1 << MY_PIN_GREEN_LED));
- // Включаем подтяжку на нужном пине
- PORTC = 1 << MY_PIN_BUTTON;
- ...
- // Зажигаем красный и зелёный сиды:
- PORTC |= ((1 << MY_PIN_RED_LED) | (1 << MY_PIN_GREEN_LED));
- // Проверяем вход последнего пина:
- if(PINC & (1 << MY_PIN_AUX2)) ...
- но могли бы объявить такую структуру:
- typedef struct
- {
- char RedLed:1;
- char YellowRed:1;
- char GreenLed:1;
- char Button:1;
- char Aux1:1;
- char Aux2:1;
- } MY_PORT_PINS;
- и делать то же самое, но кастуя тип регистров к этой структуре:
- // Делаем первые три пины выходами
- ((MY_PORT_PINS)DDRC).RedLed = 1;
- ((MY_PORT_PINS)DDRC).YellowRed = 1;
- ((MY_PORT_PINS)DDRC).GreenRed = 1;
- // Включаем подтяжку на нужном пине
- ((MY_PORT_PINS)PORTC).Button = 1
- ...
- // Зажигаем красный и зелёный сиды:
- ((MY_PORT_PINS)PORTC).RedLed = 1;
- ((MY_PORT_PINS)PORTC).GreenLed = 1;
- Проверяем вход последнего пина:
- if(((MY_PORT_PINS)PINC).Aux2) ...
- Как видите, здесь структура MY_PORT_PINS выступает чем-то вроде контейнера для
- бит-фиелдов и отражает реальную структуру порта.
- Это выглядит чуть лучше, но всё ещё плохо: куча кастования. Кроме того, этот
- код вообще не скомпилируетя. По крайней мере AvrGCC отказался кастовать PORTC
- к структуре. Да и у нас тут везде всё те же PORTC, PINC, DDRC. Если мы захотим
- сменить порт на B, это что же нам, везде менять? Даже если мы заюзам макросы,
- придётся использовать три макроса (ну и менять соответственно 3 строчки).
- Негоже! :)
- Поэтому окончательное решение состоит в том, чтобы создать глобальную
- переменную для всего порта, имя которой будет отражать предназначение порта.
- Типом этой глобальной переменной будет структура с трёмя полями-указателями на
- PORTX, PINX и DDRX.
- Важно (!!!) поставить квалификатор const в объявления указателей.
- Использование константных указателей компилятор оптимизирует, поэтому никакой
- разницы в коде между предыдущим примером не будет, зато внешне всё будет
- выглядеть красивее:
- (Ахтунг! Я советую вам чем раньше, тем лучше подробно разобраться с
- CV-квалификаторами. Крайне важно понимать, чем указатели на константу (const
- int * a) отличаются от константных указателей (int * const a), очень важно,
- чтобы ваш мозг не взорвался когда вы увидите сочетание const- и volatile-
- квалификаторов (это абсолютно нормальное сочетание, на самом деле))
- typedef struct
- {
- char RedLed:1;
- char YellowRed:1;
- char GreenLed:1;
- char Button:1;
- char Aux1:1;
- char Aux2:1;
- } MY_PORT_PINS;
- typedef struct
- {
- MY_PORT_PINS* const In;
- volatile MY_PORT_PINS* const Out;
- MY_PORT_PINS* const DDR;
- } MY_PORT_CONTROL_BUS ControlBus = {
- (MY_PORT_PINS*)&PINC,
- (MY_PORT_PINS*)&PORTC,
- (MY_PORT_PINS*)&DDRC,
- };
- // Всё, теперь доступ к битам регистра PIN порта С осуществляетсчя так:
- // ControlBus->In.xxxxxx
- // доступ к битам регистра PORT порта C, так:
- // ControlBus->Out.xxxxxx
- // доступ к битам регистра DDR порта C, так:
- // COntrolBus->DDR.xxxxxx
- // где xxxxx -- логический пин, он же поле из структуры MY_PORT_PINS,
- // например GreenLed.
- Чтобы для каждого совего порта не писать такую громадину из сткрутур, я создал
- следующие три макроса:
- #define BEGIN_PORT_DEFINITION typedef struct {
- #define DEFINE_PORT_PIN(name) char name:1;
- #define END_PORT_DEFINITION(portname, hardport) } SYSPORTPINS_##portname; \
- typedef struct \
- { \
- SYSPORTPINS_##portname* const In; \
- volatile SYSPORTPINS_##portname* const Out; \
- SYSPORTPINS_##portname* const DDR; \
- } SYSPORT_##portname; \
- SYSPORT_##portname portname = {(SYSPORTPINS_##portname*)&PIN##hardport,
- (SYSPORTPINS_##portname*)&PORT##hardport, (SYSPORTPINS_##portname*)&DDR##hardport};
- С применением этих макросов вместо такого объявления:
- typedef struct
- {
- char RedLed:1;
- char YellowRed:1;
- char GreenLed:1;
- char Button:1;
- char Aux1:1;
- char Aux2:1;
- } MY_PORT_PINS;
- typedef struct
- {
- MY_PORT_PINS* const In;
- volatile MY_PORT_PINS* const Out;
- MY_PORT_PINS* const DDR;
- } MY_PORT_CONTROL_BUS ControlBus = {
- (MY_PORT_PINS*)&PINC,
- (MY_PORT_PINS*)&PORTC,
- (MY_PORT_PINS*)&DDRC,
- };
- мы можем писать так:
- BEGIN_PORT_DEFINITION
- DEFINE_PORT_PIN(RedLed)
- DEFINE_PORT_PIN(YellowRed)
- DEFINE_PORT_PIN(GreenLed)
- DEFINE_PORT_PIN(Button)
- DEFINE_PORT_PIN(Aux1)
- DEFINE_PORT_PIN(Aux2)
- END_PORT_DEFINITION(ControlBus,C)
- Осталось только сделать удобное конфигурирование портов.
- Как известно, если в DDR бит установлен, то пин — выход, в противном случае вход.
- Если порт выход, то бит в регистре PORT это его состояние, если же вход — то
- бит в регистре PORT определяет, включена подтяжка или нет.
- Поэтому я сделал макрос HalConfigurePin, чтобы конфигурировать пины быстро,
- красиво, и в таблице-подобном виде.
- Сам макрос (+ прилагающиеся к нему константы) выглядит так:
- #define __HALCONF_ 0
- #define __HALCONF_OUT 1
- #define __HALCONF_IN 0
- #define __HALCONF_PULLUP 1
- #define __HALCONF_Z 0
- #define HalConfigurePin(__port, __pin, __in_out, __pullup_val) __port.DDR->__pin = __HALCONF_##__in_out;
- __port.Out->__pin = __HALCONF_##__pullup_val
- Соответственно, код для конфигурирования наших 6 пинов будет выглядеть так:
- HalConfigurePin(ControlBus, RedLed , OUT , );
- HalConfigurePin(ControlBus, YellowRed , OUT , );
- HalConfigurePin(ControlBus, GreenLed , OUT , );
- HalConfigurePin(ControlBus, Button , IN , PULLUP );
- HalConfigurePin(ControlBus, Aux1 , IN , Z );
- HalConfigurePin(ControlBus, Aux2 , IN , Z );
- Ну и первоначальный код соответственно будет выглядеть так:
- BEGIN_PORT_DEFINITION
- DEFINE_PORT_PIN(RedLed)
- DEFINE_PORT_PIN(YellowRed)
- DEFINE_PORT_PIN(GreenLed)
- DEFINE_PORT_PIN(Button)
- DEFINE_PORT_PIN(Aux1)
- DEFINE_PORT_PIN(Aux2)
- END_PORT_DEFINITION(ControlBus,C)
- ...
- HalConfigurePin(ControlBus, RedLed , OUT , );
- HalConfigurePin(ControlBus, YellowRed , OUT , );
- HalConfigurePin(ControlBus, GreenLed , OUT , );
- HalConfigurePin(ControlBus, Button , IN , PULLUP );
- HalConfigurePin(ControlBus, Aux1 , IN , Z );
- HalConfigurePin(ControlBus, Aux2 , IN , Z );
- // Зажигаем красный и зелёный сиды:
- ControlBus->Out.RedLed = 1;
- ControlBus->Out.GreenLed = 1;
- //Проверяем вход последнего пина:
- if(ControlBus->In.Aux2) ...
- ----
- К конкретному порту идёт привязка в макросе END_PORT_DEFINITION. Там вторым
- параметром указывается буковка. Я ступил и забыл написать в этом
- посте-примере, но в самом первом посте (где Matrix) это есть.
- А в этом посте, если у тебя есть возможность, поправь
- END_PORT_DEFINITION
- на
- END_PORT_DEFINITION(ControlBus, X)
- где X — поставь букву любого порта, к которому душа лежит :)
|