|
Использование макросов с аргументами в Си |
|
|
|
Dec 2 2013, 11:53
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(_Pasha @ Dec 2 2013, 10:29)  Это несерьезно. Для меня это никак не определят серьёзность. Просто компиляторы и уровни оптимизации бывают разные - зачем делать не единообразно? Где есть инлайн - так, а где его нет - сяк... На мой вкус это несъедобно. А что касается макросов, то уж что-что, то для GPIO - самое оно. Обеспечивается 100% унификация и переносимость между всеми платформами...
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Dec 2 2013, 12:14
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(_Pasha @ Nov 29 2013, 21:19)  Я обычно так делаю Код #define LCD_WR1_pin 4 static inline void LCD_wr1(const char value) { GPIOB->BSRR = 1 << (LCD_WR1_pin + value?0:16); } Никаких макро и никаких неясных абстракций. Все конкретно. по порядку: 1) Во первых Keil 4.7 не дает инлайнить функции. 2) Не ясен смысла записи GPIOB->BSRR = 1 << (LCD_WR1_pin + value?0:16); нужно передать в порт фиксированное значение ( номер пина) 3) Нет возможности изменить порт вывода. И еще , что делает выражение value?0:16 и зачем ? я вообще не понял , возможно это мои пробелы в знаниях Си.
Сообщение отредактировал MaxiMuz - Dec 2 2013, 12:10
|
|
|
|
|
Dec 2 2013, 12:34
|

Частый гость
 
Группа: Участник
Сообщений: 127
Регистрация: 31-10-12
Пользователь №: 74 189

|
Цитата(MaxiMuz @ Dec 2 2013, 16:14)  по порядку: 1) Во первых Keil 4.7 не дает инлайнить функции.  Да? Может у него что-то свое есть специфическое... типа __inline__ и т.п.? Цитата(MaxiMuz @ Dec 2 2013, 16:14)  2) Не ясен смысла записи GPIOB->BSRR = 1 << (LCD_WR1_pin + value?0:16); BSRR - управляет 16-тью пинками GPIO. Младшие 16 бит отвечают за установку пинов в "1", старшие 16 - за установку в "0". Можно одной записью одновременно установить и сбросить несколько разных пинов... А вышеприведенная (и, возможно, местами изъебисто написанная) конструкция буквально означает следующее: Код // n - номер GPIO-пина (от 0 до 15) #define SET_PIN(n) (0x0001 << (n)) #define CLR_PIN(n) (0x0100 << (n)) // ну или так — (0x0001 << ((n) + 16))
// какой-то конкретный пин #define LCD_wr1_pin 4
// Функция для установки/сброса какого-то конкретного пина static inline void LCD_wr1(const char value) { if (value) GPIOB->BSRR = SET_PIN(LCD_wr1_pin); else GPIOB->BSRR = CLR_PIN(LCD_wr1_pin); }
Сообщение отредактировал winipuh - Dec 2 2013, 12:35
|
|
|
|
|
Dec 2 2013, 19:09
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(winipuh @ Dec 2 2013, 15:34)   Да? Может у него что-то свое есть специфическое... типа __inline__ и т.п.? вот этого я пока не знаю Цитата(winipuh @ Dec 2 2013, 15:34)  BSRR - управляет 16-тью пинками GPIO. Младшие 16 бит отвечают за установку пинов в "1", старшие 16 - за установку в "0". Можно одной записью одновременно установить и сбросить несколько разных пинов... все мне известно но конструкция Код static inline void LCD_wr1(const char value) { if (value) GPIOB->BSRR = SET_PIN(LCD_wr1_pin); else GPIOB->BSRR = CLR_PIN(LCD_wr1_pin); } подразумевает вычисление или выбор по условию аргумента, а смысл макроса просто подстановка кода с уже готовым аргументом, вот этого я и добиваюсь
|
|
|
|
|
Dec 2 2013, 20:00
|

Частый гость
 
Группа: Участник
Сообщений: 127
Регистрация: 31-10-12
Пользователь №: 74 189

|
Цитата(MaxiMuz @ Dec 2 2013, 23:09)  но конструкция ... подразумевает вычисление или выбор по условию аргумента, а смысл макроса просто подстановка кода с уже готовым аргументом Возможно Вы удивитесь, но на этапе компиляции не то-что инлайн — даже статические функции разворачиваются в конечное выражение не хуже макросов. Пожалуйста: Код // СИ: extern void my_func(unsigned int x); static unsigned int my_value(unsigned int n) { return n ? 0x80 : 0x8000; }
void call_func(n) { my_func(value(4)); // на самом деле все это вычисляется на этапе компиляции }
// ASM: stmfd sp!, {r3, lr} mov r0, #128 // и в итоге получается my_func(0x80) ( 0x80 = 128 ) bl my_func ldmfd sp!, {r3, lr} bx lr .size call_func, .-call_func .ident "GCC: (Sourcery CodeBench Lite 2013.05-23) 4.7.3" Ну и наверное сочту своим долгом повторить еще раз то, что Вам уже говорил MrYuranЦитата Если абстрагироваться, то нужно и от пинов, и от портов, и от уровней. А так вот что имеем: RbitP(A,7); // вкл. на запись адреса A0=0 SbitP(A,5); // вкл.строба записи Вопрос: - Что будете делать, если Вам потребуется перенести пины с GPIOA на GPIOB?
- Что будете делать, если Вам потребуется использовать для строба записи на Pin5, а напр. Pin8?
Правильный вариант — это когда для этого нужно открыть некий h-файл и поправить там несколько строчек. В вашем случае - править придется все места, где вызываются макросы SbitP, RbitP и т.д. И в этом случае (при всем уважении) Ваши макросы — просто какая-то обертка, чтобы писать меньше букв кода. Преимущество, мягко говоря, весьма сомнительное...
|
|
|
|
|
Dec 3 2013, 04:24
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (winipuh @ Dec 1 2013, 22:37)  А зачем у аргумента функции квалификатор const? Тут же параметр не по ссылке передается, а по значению.  Это стиль такой. Если не предполагается изменять аргумент внутри функции, то он фиксируется этим квалификатором. Для безопасности. Например, передаётся в функцию количество чего-нибудь, внутри оно используется несколько раз в разных местах. Если не зафиксировать, то имеется ненулевая вероятность, что в какой-то точке значение будет изменено под текущие локальные нужды (такое обычно бывает при редактировании сорцов, которые давно не трогали - внимания на вникание во все нюансы не хватает) без учёта того, что ниже по коду оно используется в предположении, что должно быть неизменным. Это полезная практика - фиксировать ещё на этапе проектирования.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Dec 3 2013, 04:30
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(winipuh @ Dec 2 2013, 23:00)  А так вот что имеем: RbitP(A,7); // вкл. на запись адреса A0=0 SbitP(A,5); // вкл.строба записи Вопрос: - Что будете делать, если Вам потребуется перенести пины с GPIOA на GPIOB?
- Что будете делать, если Вам потребуется использовать для строба записи на Pin5, а напр. Pin8?
Правильный вариант — это когда для этого нужно открыть некий h-файл и поправить там несколько строчек. В вашем случае - править придется все места, где вызываются макросы SbitP, RbitP и т.д. Код #define SbitP(Port,Nbit) GPIO##Port->BSRR=GPIO_BSRR_BS##Nbit вместо Port подставляем букву порта, второй параметр - номер пина. И ничего править не надо.
|
|
|
|
|
Dec 3 2013, 12:23
|

Частый гость
 
Группа: Участник
Сообщений: 127
Регистрация: 31-10-12
Пользователь №: 74 189

|
Цитата(MaxiMuz @ Dec 3 2013, 08:30)  Код #define SbitP(Port,Nbit) GPIO##Port->BSRR=GPIO_BSRR_BS##Nbit вместо Port подставляем букву порта, второй параметр - номер пина. И ничего править не надо. Вы меня не поняли!  Все бывает в первый раз: 1) Пришлось корректировать разводку — как следствие поменялись функции пинов GPIO... 2) Пришлось добавить новую модель изделия. Немного другая разводка (другие функции пинов). Дерево исходников общее. Собираем либо под одно железо, либо под другое... Имеем например: SbitP(A,5); // вкл.строба записиДопустим теперь строб записи не на GPIOA(Pin_5) а на GPIOB(Pin_12) ... Как будете править исходники? Еще хуже — если править их придется кому-то другому...  Придется пройтись по сишным файлам и везде строчку SbitP(A,5) поменять на SbitP(B,12). ... Можно, но сложно и некрасиво... А собирать из одного дерева под разное железо - задача в Вашем случае вообще непосильная... Ок?  Макросы пишутся для того, чтобы обойти эту проблему... Ваши макросы (при все уважении) проблему эту не решают. Теперь объясните — зачем же Вы их придумали? Цитата(dxp @ Dec 3 2013, 08:24)  Это стиль такой. Если не предполагается изменять аргумент внутри функции, то он фиксируется этим квалификатором. Понял.
Сообщение отредактировал winipuh - Dec 3 2013, 12:23
|
|
|
|
|
Dec 11 2013, 07:36
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(winipuh @ Dec 3 2013, 15:23)  Имеем например: SbitP(A,5); // вкл.строба записиДопустим теперь строб записи не на GPIOA(Pin_5) а на GPIOB(Pin_12) ... Как будете править исходники? Еще хуже — если править их придется кому-то другому...  Придется пройтись по сишным файлам и везде строчку SbitP(A,5) поменять на SbitP(B,12). ... Можно, но сложно и некрасиво... А собирать из одного дерева под разное железо - задача в Вашем случае вообще непосильная... Ок?  Макросы пишутся для того, чтобы обойти эту проблему... Ваши макросы (при все уважении) проблему эту не решают. Теперь объясните — зачем же Вы их придумали? Решение проблемы в моем случае - дефейнить название портов. gpio.hКод /****************** Macros Set/Clear Bit of Port ***************************/ #define SbitP(Port,Nbit) _SbitP(Port,Nbit) #define RbitP(Port,Nbit) _RbitP(Port,Nbit) #define _SbitP(Port,Nbit) GPIO##Port->BSRR=GPIO_BSRR_BS##Nbit #define _RbitP(Port,Nbit) GPIO##Port->BSRR=GPIO_BSRR_BR##Nbit #define SmbitP(Port,mask) GPIO##Port->BSRR=(mask) #define RmbitP(Port,mask) GPIO##Port->BSRR=(mask)<<16 main.cКод #define LCD_ctrlPort A // Порт управляющих сигналов #define LCD_dataPort A // Порт шины данных #define LCD_dataShift 1 // Смещение шины данных от начала порта
#define LCD_A0 7 // Выбор: Адрес A0=L/ Данные A0=H #define LCD_WR1 5
#include "gpio.h" //описание макросов ввода/вывода .... .... void LCD_wrAdr (u8 Adr) { SmbitP(LCD_dataPort,((Adr&0x0f)<<LCD_dataShift)); // выставляем на шину адрес RbitP(LCD_dataPort,LCD_A0); // вкл. на запись адреса A0=0 SbitP(LCD_dataPort,LCD_WR); // вкл.строба записи ... ... } Единственное неудобство это описание пина двумя параметрами, но это все решаемо.
|
|
|
|
|
Dec 11 2013, 08:02
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(MaxiMuz @ Dec 11 2013, 09:36)  Единственное неудобство это описание пина двумя параметрами, но это все решаемо. "Мыши кололись, плакали, но продолжали жрать кактус" Цитата(demiurg_spb @ Nov 29 2013, 14:52)  Вы не до конца поняли намёк про макросы Аскольда Волкова. Не поленитесь - погуглите да изучите. Вот они.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|