В моем уроке « Биты и булавки с Kinetis» я показал, как использовать подход BitIO_LDD для доступа к битовому вводу-выводу. Мне не нравится этот подход LDD (драйвер логического устройства) по нескольким причинам:
- Требуется дополнительная «ручка устройства», передаваемая функциям, даже если такая ручка устройства не нужна или нежелательна.
- Это негативно влияет на эффективность и размер кода.
К счастью, есть способ взломать это.
DeviceDataPtr для BitIO_LDD
Если используется подход BitIO_LDD, как в « Битах и булавках с Kinetis», то каждый вызов подпрограмм BitIO должен иметь параметр указателя «device», который создается с помощью Init()
метода. Если для свойства компонента «Auto Initialization» установлено значение «yes», он автоматически вызовет этот Init()
метод для меня как часть PE_low_level_init()
внутреннего метода main ():
Этот автоматически созданный дескриптор устройства должен быть передан самой функции BitIO_LDD:
GREEN_ClrVal(GREEN_DeviceData);
Как вы можете себе представить, передача такого параметра требует определенных затрат (размер кода и циклы выполнения). И это еще более разочаровывает, если я посмотрю на исходный код функции под названием:
/* ** =================================================================== ** Method : GREEN_ClrVal (component BitIO_LDD) */ /*! ** @brief ** Clears (set to zero) the output value. It is equivalent to ** the [PutVal(FALSE)]. This method is available only if the ** direction = _[output]_ or _[input/output]_. ** @param ** DeviceDataPtr - Pointer to device data ** structure returned by <Init> method. */ /* ===================================================================*/ void GREEN_ClrVal(LDD_TDeviceData *DeviceDataPtr) { (void)DeviceDataPtr; /* Parameter is not used, suppress unused argument warning */ GPIO_PDD_ClearPortDataOutputMask(GREEN_MODULE_BASE_ADDRESS, GREEN_PORT_MASK); }
Мы передаем параметр, который вообще не используется.
Взлом по параметру устройства
Если этот параметр не используется, зачем вообще применять его в API? Этот параметр имеет смысл, если я хочу иметь одновременный доступ к периферийному устройству (например, если у меня есть ОСРВ и несколько задач, которые хотят использовать битовый ввод-вывод). Но в моем случае я не использую ОСРВ, и Processor Expert должен это знать. Я ожидаю, что Processor Expert воспользуется этими знаниями, но, по крайней мере, до MCU10.5 это не так. Хорошая новость: я могу взломать его.
Идея состоит в том, чтобы определить макросы, подобные приведенным ниже, которые * не * нуждаются в параметре дескриптора устройства:
#define RED_Clr() GPIO_PDD_ClearPortDataOutputMask(RED_MODULE_BASE_ADDRESS, RED_PORT_MASK) #define RED_Set() GPIO_PDD_SetPortDataOutputMask(RED_MODULE_BASE_ADDRESS, RED_PORT_MASK) #define GREEN_Clr() GPIO_PDD_ClearPortDataOutputMask(GREEN_MODULE_BASE_ADDRESS, GREEN_PORT_MASK) #define GREEN_Set() GPIO_PDD_SetPortDataOutputMask(GREEN_MODULE_BASE_ADDRESS, GREEN_PORT_MASK) #define BLUE_Clr() GPIO_PDD_ClearPortDataOutputMask(BLUE_MODULE_BASE_ADDRESS, BLUE_PORT_MASK) #define BLUE_Set() GPIO_PDD_SetPortDataOutputMask(BLUE_MODULE_BASE_ADDRESS, BLUE_PORT_MASK) #define SW1_Get() (GPIO_PDD_GetPortDataInput(SW1_MODULE_BASE_ADDRESS) & SW1_PORT_MASK) define SW1_Get() (GPIO_PDD_GetPortDataInput(SW2_MODULE_BASE_ADDRESS) & SW2_PORT_MASK)
Макросы — это то, что реализовано внутри методов (которые используют параметр NULL, но не используют его).
С этим я могу изменить мою оригинальную реализацию от:
GREEN_ClrVal(GREEN_DeviceData); /* turn on green LED */ WAIT1_Waitms(1000); GREEN_SetVal(GREEN_DeviceData); /* turn off green LED */ for(;;) { if (SW1_GetVal(SW1_DeviceData)==0) { /* button low level => pressed */ RED_ClrVal(RED_DeviceData); /* LED cathode is connected to microcontroller pin: low level turns it on */ BLUE_SetVal(BLUE_DeviceData); /* turn off blue led */ } else { RED_SetVal(RED_DeviceData); /* turn off red led */ BLUE_ClrVal(BLUE_DeviceData); /* turn on blue led */ } if (SW2_GetVal(SW2_DeviceData)==0) { /* switch low level => pressed */ BLUE_ClrVal(BLUE_DeviceData); /* turn on blue led */ } else { BLUE_SetVal(BLUE_DeviceData); /* turn off blue led */ } }
К этому:
GREEN_Clr(); /* turn on green LED */ WAIT1_Waitms(1000); GREEN_Set(); /* turn off green LED */ for(;;) { if (SW1_Get()==0) { /* button low level => pressed */ RED_Clr(); /* LED cathode is connected to microcontroller pin: low level turns it on */ BLUE_Set(); /* turn off blue led */ } else { RED_Set(); /* turn off red led */ BLUE_Clr(); /* turn on blue led */ } if (SW2_Get()==0) { /* switch low level => pressed */ BLUE_Clr(); /* turn on blue led */ } else { BLUE_Set(); /* turn off blue led */ } }
Положительное влияние: вместо 1544 байт оптимизированной версии требуется 1416 байт кода для приложения, что примерно на 10% меньше.
Резюме
Компонент Processor Expert BitIO_LDD требует, чтобы дескриптор устройства передавался самой функции в его API, но этот параметр вообще не используется внутри функций, если не используется RTOS. Даже с использованием ОСРВ и без необходимости одновременного доступа к битам этот параметр не является обязательным и увеличивает только размер кода и задержку кода. Но макросы можно использовать, чтобы обойти это для повышения эффективности кода без потери преимущества использования Processor Expert. Просто эти дополнительные макросы взломаны вокруг того, что Processor Expert должен сделать из коробки.
Было бы очень здорово, если бы Processor Expert мог иметь свойство «не использовать указатель дескриптора устройства» или что-то подобное. По крайней мере, Processor Expert знает, что он не использует этот параметр, поэтому я ожидаю, что он создаст API / макросы, как я делал выше, для повышения эффективности кода.
Счастливая оптимизация