Arduino:在没有编辑库的情况下更改定义[已解决,编译器中的错误,可用的解决方法]

Arduino: Change defines without edit library [solved, bug in compiler, workaround available]

编辑:看下面我的回答


我是 Arduino 平台的新手,现在正在使用 AdaFruit 的 attiny85 (Trinket) 库创建一个 USB HID 项目。另请参阅我之前回答的有关该库选项的问题:

我被一个罕见的与 C 相关的限制难住了(另请参阅上面对上一个问题的 link 中回答的最新评论),通过单独编译对二进制文件进行单独的后期绑定。如果您想更改 USB 设备的选项,则必须更改库本身,这对我来说有点奇怪,因为它是一个库。

例如,如果您在加载库之前包含一个选项已更改的头文件,则不会发生任何更改,因为库不知道它,因为它也必须包含在该库中。

示例:这不起作用:

#include "myUSBOptions.h"
#include "TrinketHidCombo.h"

另一种方法:

所以我需要为每个项目更改库本身,在本例中为 usbconfig.h 文件。这对我来说听起来有点愚蠢,因为它是一个库,可以被其他项目使用。 这可能是 Adafruit 库中的设计错误,供应商名称、设备名称等在该库之外必须是可选的,而且当库更新时,您的设置也可能被覆盖。并且...您需要为每个单独的项目再次编辑文件。

所以我想到了在 usbconfig.h 文件中包含一个可选头文件的想法,#include "user_usbconfig.h" 只需要一个更改。但这不是唯一的 usbconfig.h 文件,至少有三个版本可用!在项目目录中创建一个 user_usbconfig.h 文件,编写一个批处理文件以使其自动化并将其包含在项目目录中,以便您在切换项目时只需单击它即可更改选项。

在 usbconfig.h 文件中添加的行:

.......

#include "cmdline_defs.h"

#ifndef __usbconfig_h_included__
#define __usbconfig_h_included__

#include "user_usbconfig.h" // <-- added this

.......

例如user_usbconfig.h包含在项目中

/* DEVICE SETTINGS */

/* Version number of the device: Minor number first, then major number. */
#define USB_CFG_DEVICE_VERSION  0x00, 0x01


/* VENDOR_NAME
 * These two values define the vendor name returned by the USB device. The name
 * must be given as a list of characters under single quotes. The characters
 * are interpreted as Unicode (UTF-16) entities.
 * If you don't want a vendor name string, undefine these macros.
 * ALWAYS define a vendor name containing your Internet domain name if you use
 * obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for
 * details.
 */
#define USB_CFG_VENDOR_NAME     'm', 'y', 'C', 'o', 'm', 'p', 'a', 'n', 'y', 'n'
#define USB_CFG_VENDOR_NAME_LEN 10


/* DEVICE_NAME
 * Same as above for the device name. If you don't want a device name, undefine
 * the macros. See the file USB-IDs-for-free.txt before you assign a name if
 * you use a shared VID/PID.
 */
#define USB_CFG_DEVICE_NAME     'm', 'y', 'D', 'e', 'v', 'i', 'c', 'e'
#define USB_CFG_DEVICE_NAME_LEN 8


/* SERIAL_NUMBER
 * Same as above for the serial number. If you don't want a serial number,
 * undefine the macros.
 * It may be useful to provide the serial number through other means than at
 * compile time. See the section about descriptor properties below for how
 * to fine tune control over USB descriptors such as the string descriptor
 * for the serial number.
 */
/*#define USB_CFG_SERIAL_NUMBER   'N', 'o', 'n', 'e' */
/*#define USB_CFG_SERIAL_NUMBER_LEN   0 */

示例源代码 myProject.ino,除了激活 USB 外什么都不做:

#include "TrinketHidCombo.h"

void setup()
{
  TrinketHidCombo.begin(); 
}

void loop() // Main program - main()
{
  // do nothing, check if USB needs anything done
  TrinketHidCombo.poll();  
}

我创建的批处理文件,在项目目录下添加:

@echo off
rem ********************************************************
rem * setTrinketUSBLibraryOptions.bat                      *
rem * ---------------------------------------------------- *
rem * Author       : Erwin Haantjes                        *
rem * Project      : Arduino Trinket USB                   * 
rem * Dev date     : 11-06-2016                            *
rem * Last changed : 11-06-2016                            *
rem * What it do   : 'Copy' (link) USB options project to  *
rem *                Trinket lib to make settings optional.* 
rem *                                                      *
rem ********************************************************


rem *** Config parts that can be modified if required
SET ARDUINO_LIB_DIR=F:\Program Files\Arduino\DigiSpark\Digispark-Arduino-1.0.4\libraries
SET USER_HEADER_FILE=user_usbconfig.h
SET ATU_HEADER_FILE=usbconfig.h
SET ATU_DIRNAME1=TrinketHidCombo
SET ATU_DIRNAME2=TrinketKeyboard
SET ATU_DIRNAME3=TrinketMouse

SET ATU_HEADER_FILE1=%ARDUINO_LIB_DIR%\%ATU_DIRNAME1%\%ATU_HEADER_FILE%
SET ATU_HEADER_FILE2=%ARDUINO_LIB_DIR%\%ATU_DIRNAME2%\%ATU_HEADER_FILE%
SET ATU_HEADER_FILE3=%ARDUINO_LIB_DIR%\%ATU_DIRNAME3%\%ATU_HEADER_FILE%
SET ATU_USER_HEADER_FILE1=%ARDUINO_LIB_DIR%\%ATU_DIRNAME1%\%USER_HEADER_FILE%
SET ATU_USER_HEADER_FILE2=%ARDUINO_LIB_DIR%\%ATU_DIRNAME2%\%USER_HEADER_FILE%
SET ATU_USER_HEADER_FILE3=%ARDUINO_LIB_DIR%\%ATU_DIRNAME3%\%USER_HEADER_FILE%

rem *** START
echo.
echo.
echo Set Thrinket USB Library options to match your project
echo ------------------------------------------------------
echo.
if "@%1"=="@/?" goto USAGE
if "@%1"=="@-?" goto USAGE
goto INTRO

:USAGE
SET USAGEMODE=true
echo Created by Erwin Haantjes 2016
echo.
echo USAGE:
echo %0 [/?,/C,-?,-C]
echo.
echo SWITCHES:
echo - ?   Shows this help
echo - C   Use copy command instead of symlink to hardcopy the "%USER_HEADER_FILE%" to it's targets
echo.

:INTRO
rem *** WARNING and NOTICE
echo WARNING:
echo - This batch changes/symlink options to be able to config the Trinket USB Library by project easily. Run this file once each time you work on a project to be sure you are using the right settings.
echo - If a physical %USER_HEADER_FILE% file exists in the library directory, it will be deleted!
echo.
echo NOTICE: 
echo - Each "%ATU_HEADER_FILE%" file in (parts/the) Trinket USB library must include the line: #include "%USER_HEADER_FILE%" at top of the file after #define __usbconfig_h_included__ .
echo - Once applying this 'patch', you can change USB settings in the "%USER_HEADER_FILE%" file in this directory on the fly without the need to run this batch again. Just compile it after a change and your changes will be 'visible'. 
echo.
echo.
echo Library directory is set to:
echo %ARDUINO_LIB_DIR%
echo.
echo. 
if "%USAGEMODE%"=="true" goto END
echo Do you want to continue? Press any key.
pause >NUL
echo.

rem *** Check destinations
if exist "%USER_HEADER_FILE%" goto NEXT1
goto ERROR_NO_USER_FILE
:NEXT1
if exist "%ATU_HEADER_FILE1%" goto NEXT2
goto ERROR_LIB_NOT_EXISTS
:NEXT2
if exist "%ATU_HEADER_FILE2%" goto NEXT3
goto ERROR_LIB_NOT_EXISTS
:NEXT3
if exist "%ATU_HEADER_FILE3%" goto NEXT4
goto ERROR_LIB_NOT_EXISTS
:NEXT4
goto APPLY

rem *** ERRORS
:ERROR_NO_USER_FILE
echo ERROR: Create a "%USER_HEADER_FILE%" in this directory first.
goto ABORTED
:ERROR_LIB_NOT_EXISTS
echo %ATU_HEADER_FILE1%
echo ERROR: Check the directory location match your Arduino IDE setup, see "ARDUINO_LIB_DIR" at top of this batch file. Check also if you have the Trinket USB Library currently installed.
goto ABORTED

:APPLY
echo All seems to be fine, applying patch (symlinks).....
echo Checking and removing target files....
if exist "%ATU_USER_HEADER_FILE1%" goto REMOVE1
goto APPLY_NEXT2
:REMOVE1
echo Remove symlink of "%ATU_USER_HEADER_FILE1%" ....
del "%ATU_USER_HEADER_FILE1%"

:APPLY_NEXT2
if exist "%ATU_USER_HEADER_FILE2%" goto REMOVE2
goto APPLY_NEXT3
:REMOVE2
echo Remove symlink of "%ATU_USER_HEADER_FILE2%" ....
del "%ATU_USER_HEADER_FILE2%"

:APPLY_NEXT3
if exist "%ATU_USER_HEADER_FILE3%" goto REMOVE3
goto APPLY_NEXT
:REMOVE3
echo Remove file/symlink of "%ATU_USER_HEADER_FILE3%" ....
del "%ATU_USER_HEADER_FILE3%"

:APPLY_NEXT
echo.
if "@%1"== "@/c" goto APPLY_COPY
if "@%1"== "@/C" goto APPLY_COPY
if "@%1"== "@-c" goto APPLY_COPY
if "@%1"== "@-C" goto APPLY_COPY
echo Applying symlinks....
mklink /H "%ATU_USER_HEADER_FILE1%" "%USER_HEADER_FILE%" 
mklink /H "%ATU_USER_HEADER_FILE2%" "%USER_HEADER_FILE%" 
mklink /H "%ATU_USER_HEADER_FILE3%" "%USER_HEADER_FILE%" 
goto SUCCESS

:APPLY_COPY
echo Copy file(s)....
copy "%USER_HEADER_FILE%" "%ATU_USER_HEADER_FILE1%"  
copy "%USER_HEADER_FILE%" "%ATU_USER_HEADER_FILE2%"  
copy "%USER_HEADER_FILE%" "%ATU_USER_HEADER_FILE3%"  
SET COPYMODE=true
goto SUCCESS

:ABORTED
echo Batch aborted due error.
goto END

:SUCCESS
echo.
echo SUCCESS!
echo NOTICE: 
echo - Patch succesfully applied when no error is visible on screen.
if "%COPYMODE%"=="true" echo - Because you specify the copymode switch, you have to take care of updates yourself. Run this batch file again when you have changed USB settings. 

:END
SET USAGEMODE=
SET COPYMODE=
echo.
echo Press any key to exit...
pause >NUL

:DIE
echo.

结论与问题:

虽然这个方法工作正常,但我还是不满意,想知道是否有更简单的方法可以在不更改库的任何代码行的情况下实现。一个更 "on the fly" 的解决方案,这样您就不必考虑这个问题来避免错误和其他令人头疼的问题。

经过很长一段时间,我发现Arduino编译器是如何编译代码的。这不是 C/C++ 问题,实际上 它是 Arduino 编译器 中的错误,组合和编译顺序。此外,编译器在编译之前添加了一些特定的代码。


解决方法:

使用 exra 项目 h 文件包含您的 libraries/independencies。在包含之前定义将不起作用,因为编译方法在应用定义(在您的 ino 文件中)之前包含 h 文件。使用定义时,例如要更改库,需要一个单独的 h 文件。例如 "myproject.inc.h",其中包含您要使用的所有库。

现在将应用您在包含库之前指定的所有定义。

示例:


myproject.ino


#include "myproject.inc.h"

void setup()
{
  TrinketHidCombo.begin(); 
}

void loop() // Main program - main()
{
  // do nothing, check if USB needs anything done
  TrinketHidCombo.poll();  
}

myproject.inc.h


#define USB_CFG_VENDOR_NAME     'm', 'y', 'C', 'o', 'm', 'p', 'a', 'n', 'y', 'n'
#define USB_CFG_VENDOR_NAME_LEN 10

#include "TrinketHidCombo.h"