Oresat-notes

From Wiki at Neela Nurseries
Revision as of 07:04, 1 April 2021 by Ted (talk | contribs) (^ IMU development work)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search


Notes, not well organized, mostly on Oresat firmware topics. Oh and there's a great Oresat block diagram of the entire avionics system. Satnogs web site is here at https://network-dev.satnogs.org/observations/31491/.


^ Oresat type driver object

Putting together an Oresat device driver type object, realized in C, starting with devp:

112 void tmp101Start(TMP101Driver *devp, const TMP101Config *config) {

Object devp is a pointer to an object/data structure of type TMP101Driver. TMP101Driver and TMP101config are defined in oresat-firmware/src/f0/app_solar/source as:

1 2
/**
 * @brief TMP101AN temperature sensor class.
 */

struct TMP101Driver {
    /** @brief Virtual Methods Table.*/
    const struct TMP101VMT     *vmt;   
    _tmp101_data
};
/**
 * @brief   TMP101 configuration structure.
 */

typedef struct {
#if (TMP101_USE_I2C) || defined(__DOXYGEN__)
    /**
     * @brief I2C driver associated with this TMP101. 
     */
    I2CDriver                   *i2cp;
    /**
     * @brief I2C configuration associated with this TMP101. 
     */
    const I2CConfig             *i2ccfg;
    /**
     * @brief TMP101 Slave Address 
     */
    i2caddr_t                   saddr;
#endif /* TMP101_USE_I2C */

} TMP101Config;

For first steps work we really only need the I2C related structures and pointers. If we are to use TMP101Config for this we must understand how this data structure is configured at start up. There is a place where devp->config is assigned a value, a value which is passed by reference to the void routine tmp101Start(). Looking at who calls this routine, we must return to the driver code in ina226.c because nothing yet calls the just started TMP101AN driver code. Source file solar.c calls this routine:

  215     ina226Start(&ina226dev, &ina226config);

The structure or object ina226dev is declared as a static instance of type INA226Driver:

  ./src/f0/app_solar/source/solar.c:76:static INA226Driver ina226dev;

This in turn is defined in ./common/include/ina226.h as:

/**
 * @brief INA226 Power Monitor class.
 */
struct INA226Driver {
    /** @brief Virtual Methods Table.*/
    const struct INA226VMT     *vmt;   
    _ina226_data
};

And ina226config is initialized in solar.c this way:

static const INA226Config ina226config = {
    &I2CD2, 
    &i2cconfig,
    INA226_SADDR,
    INA226_CONFIG_MODE_SHUNT_VBUS | INA226_CONFIG_MODE_CONT |
    INA226_CONFIG_VSHCT_140US | INA226_CONFIG_VBUSCT_140US |
    INA226_CONFIG_AVG_16,
    (5120000/(RSENSE*CURR_LSB)),
    CURR_LSB
};

Source file solar.c knows about ChibiOS' handle or pointer to the STM32Fx I2C peripheral, here expressed &I2CD2, and solar.c declares a static instance of INA226Config. These two references to data structures are passed to the object oriented implementation of the INA226 driver, via the call to routine ina226Start(). So within this design framework we must in solar.c declare a device structure instance and a configuration structure instance specific to the TMP101AN sensor. We must adapt the INA226 sensor driver sources enough to support the different, simpler TMP101AN device.

For our first step purposes we really only need the first three data members of this data structure: an I2C device reference, and I2C configuration structure, and the I2C address of a given TMP101AN sensor. Those two sensors are wired with addresses 0x48 and 0x4A on the solar board. So we'll simplify the TMP101Config type in tmp101an.h, which right now is in the solar sources directory.


^ Comparisons of static const instances

In the common folder there are two forms of static const declarations. Declarations of the form static const type_name are used with what look to be file system types and memory devices. Similar declarations starting with the form static const struct declarations are used only for physical external peripherals, here the INA and MAX parts:

ted@localhost:~/projects/psas/oresat-firmware/common$ grep -nr 'static const' ./*
./ina226.c:81:static const struct INA226VMT vmt_device = {
./lfs_util.c:11:    static const uint32_t rtable[16] = {
./max580x.c:81:static const struct MAX580XVMT vmt_device = {
./max7310.c:78:static const struct MAX7310VMT vmt_device = {
./mmc.c:11:static const SDCConfig sdccfg = {
./opd.c:13:static const I2CConfig i2cconfig = {

In the board specific firmware source file solar.c, there are only static declarations of custom configuration types. Because we're developing a driver for an external device we carry over into tmp101an.c the declaration for a 'vmt_device' type from ina226.c, the existing device driver mentioned above:

ted@asha:~/projects/psas/oresat-firmware/src/f0/app_solar/source$ grep -nr 'static const' ./*
./solar.c:31:static const I2CConfig i2cconfig = {
./solar.c:48:static const INA226Config ina226config = {
./solar.c:60:static const DACConfig dac1cfg = {
./tmp101an.c:81:static const struct TMP101VMT vmt_device = {


^ I2C stuff

An example of Oresat calling I2C API is found in ina226.c:

i2c_result = i2cMasterTransmitTimeout(i2cp, sad, &reg, 1, rxbuf, n, TIME_INFINITE);

Routine i2cMasterTransmitTimeout() is defined in oresat-firmware/ext/ChibiOS/os/hal/src/hal_i2c.c, and returns a value of type msg_t. When called in a project in which the I2C bus is shared this routine must be bounded by a couple of I2C API calls which protect the I2C bus as a resource, like in the following excerpted lines of ina226.c:

   i2cAcquireBus(config->i2cp);

   i2cStart(config->i2cp, config->i2ccfg);

   ...one or more indirect or direct calls to i2cMasterTransmitTimeout(...)...

   i2cReleaseBus(config->i2cp);


TO DO - check that following define is in effect:

   239 /**
   240  * @brief   TMP101 shared I2C switch.
   241  * @details If set to @p TRUE the device acquires I2C bus ownership
   242  *          on each transaction.
   243  * @note    The default is @p FALSE. Requires I2C_USE_MUTUAL_EXCLUSION.
   244  */
   245 #if !defined(TMP101_SHARED_I2C) || defined(__DOXYGEN__)
   246 #define TMP101_SHARED_I2C                   TRUE
   247 #endif
   248 /** @} */



The type defined value msg_t appears to be declared and defined in multiple ChibiOS files. It's likely that solar firmware includes only one of these files:

./ext/ChibiOS/os/sb/user/sbuser.h:82:typedef uint32_t msg_t;
./ext/ChibiOS/os/sb/user/sbuser.h:112:#define MSG_OK              (msg_t)0
./ext/ChibiOS/os/sb/user/sbuser.h:113:#define MSG_TIMEOUT         (msg_t)-1
./ext/ChibiOS/os/sb/user/sbuser.h:114:#define MSG_RESET           (msg_t)-2
   .
   .
   .
./ext/ChibiOS/os/common/ports/SIMIA32/compilers/GCC/chtypes.h:59:typedef int32_t             msg_t;          /**< Inter-thread message.      */

./ext/ChibiOS/os/hal/templates/osal/osal.h:56:#define MSG_OK                              (msg_t)0
./ext/ChibiOS/os/hal/templates/osal/osal.h:57:#define MSG_TIMEOUT                         (msg_t)-1
./ext/ChibiOS/os/hal/templates/osal/osal.h:58:#define MSG_RESET                           (msg_t)-2
   .
   .
   .
./ext/ChibiOS/os/hal/osal/os-less/AVR/osal.h:61:#define MSG_OK                              (msg_t)0
./ext/ChibiOS/os/hal/osal/os-less/AVR/osal.h:62:#define MSG_RESET                           (msg_t)-1
./ext/ChibiOS/os/hal/osal/os-less/AVR/osal.h:63:#define MSG_TIMEOUT                         (msg_t)-2
./ext/ChibiOS/os/hal/osal/os-less/AVR/osal.h:64:#define MSG_WAIT                            (msg_t)-10

./ext/ChibiOS/os/hal/osal/os-less/AVR/osal.h:160:typedef int16_t msg_t;

./ext/ChibiOS/os/hal/osal/os-less/ARMCMx/osal.h:60:#define MSG_OK                              (msg_t)0
./ext/ChibiOS/os/hal/osal/os-less/ARMCMx/osal.h:61:#define MSG_RESET                           (msg_t)-1
./ext/ChibiOS/os/hal/osal/os-less/ARMCMx/osal.h:62:#define MSG_TIMEOUT                         (msg_t)-2
./ext/ChibiOS/os/hal/osal/os-less/ARMCMx/osal.h:63:#define MSG_WAIT                            (msg_t)-10
./ext/ChibiOS/os/hal/osal/os-less/ARMCMx/osal.h:177:typedef int32_t msg_t;

./ext/ChibiOS/os/hal/osal/rt-nil/osal.h:59:#define MSG_OK                              (msg_t)0
./ext/ChibiOS/os/hal/osal/rt-nil/osal.h:60:#define MSG_TIMEOUT                         (msg_t)-1
./ext/ChibiOS/os/hal/osal/rt-nil/osal.h:61:#define MSG_RESET                           (msg_t)-2
./ext/ChibiOS/os/hal/osal/rt-nil/osal.h:142:typedef int32_t msg_t;


^ CAN stuff

Oreset computer systems and firmware use CANopen and a couple of specific CAN protocol extensions (2021-03-09 need to identify and cite - TMH), and on the development tools side team is connecting laptops and work stations to local CAN bus via CANable USB<->CAN adapter. On a Linux host such as Ubuntu with udev installed.

When the given Linux host detects and creates more two or more /dev/ttyACMn devices, udevadm may be used to identify which of these devices corresponds to the CANable adapter:

Figure x - shell commands excerpt

ted@localhost:~$ udevadm info /dev/ttyACM0 | grep MODEL
E: ID_MODEL_ID=60c4
E: ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller
E: ID_MODEL=CANtact_dev
E: ID_MODEL_ENC=CANtact\x20dev

ted@localhost:~$ udevadm info /dev/ttyACM1 | grep MODEL
E: ID_MODEL_ID=374e
E: ID_MODEL_FROM_DATABASE=STLINK-V3
E: ID_MODEL=STLINK-V3
E: ID_MODEL_ENC=STLINK-V3

Once you know which device in /dev is the CAN transceiver, you can do this:

$ sudo slcand -o -c -f -s8 /dev/ttyACM0 can0
$ sudo ip link set up can0


Some references on CAN protocol:

  1 # CAN Interface Documentation
  2 
  3 ## Overview
  4 OreSat uses the [ECSS-E-ST-50-15C](https://ecss.nl/standard/ecss-e-st-50-15c-space-engineering-canbus-extension-protocol-1-may-2015/) CANbus E    xtension Protocol Standard to integrate subsystems together. This standard is built upon [CANopen](https://www.can-cia.org/canopen/), and spec    ifies how CANopen should be utilized to provide services described in [CCSDS Spacecraft Onboard Interface Services](https://public.ccsds.org/P    ubs/850x0g2.pdf). To accomplish this goal, OreSat employs the open source [CANopenNode](https://github.com/CANopenNode/CANopenNode) protocol s    tack.
  5 
  6 ### CANopen
  7 CANopen is a protocol stack meant to expand the underlying CAN bus services to facilitate things like arbitrary data transfers, network manage    ment, emergency messages, process data synchronization, and more. The core of the CANopen specification is something called the Object Diction    ary, which provides an interface for a device to the CAN bus. There are two common ways to interface with this object dictionary: Service Data     Object (SDO) and Process Data Object (PDO).
  8 
  9 An SDO transfer is initiated by whatever node is considered the manager of the network, and allows what is essentially an abritrary sized read     or write of an object dictionary entry.
 10 
 11 A PDO, on the other hand, does not depend on any particular system to initiate anything. A PDO maps one or more object dictionary entries into     a CAN message that is sent over the network either periodically, on a Change of State, after a SYNC message, or some combination of these. A     PDO can be thought of as a publisher->subscriber model, in that one device publishes some of its object dictionary to the network while one or     more other devices listen for these PDOs and record the data to their own object dictionaries.
 12 
 13 CANopen also specifies a Network Management (NMT) protocol for node health monitoring and state control. This protocol allows nodes to broadca    st a Heartbeat message that indicates their current state to the network, and allows a Network Manager to tell nodes to change state. The stat    es mentioned here are only the most basic states: Initialization, Pre-Operational, Operational, and Stopped. These states govern when worker p    rocesses are told to start or stop.
 14 
 15 ## Architecture

In directory and file oresat-firmware/src/f4/app_control/source/command.c is an example of . . .

 36     if (sdocli->state == SDOCLI_ST_DOWNLOAD) {
 37         space = CO_fifo_getSpace(&sdocli->sdo_c->bufFifo);
 38         do {
 39             size = lfs_file_read(&lfs, &data->file, data->buf, lfs_min(space, BUF_SIZE));
 40             if (size < 0) {
 41                 *abort_code = CO_SDO_AB_NO_DATA;
 42                 return true;
 43             }
 44             CO_SDOclientDownloadBufWrite(sdocli->sdo_c, data->buf, size);
 45         } while (size && (space -= size));
 46     } else if (sdocli->state == SDOCLI_ST_UPLOAD) {
 47         if (ret == CO_SDO_RT_uploadDataBufferFull || ret == CO_SDO_RT_ok_communicationEnd) {
 48             do {
 49                 size = CO_SDOclientUploadBufRead(sdocli->sdo_c, data->buf, BUF_SIZE);
 50                 lfs_file_write(&lfs, &data->file, data->buf, size);
 51             } while (size);
 52         }
 53     }


 92     chprintf(chp, "Initiating transfer... ");
 93     tp = sdo_transfer(argv[0][0], node_id, index, subindex, size, sdo_file_cb, &data);
 94     if (tp == NULL) {
 95         chprintf(chp, "Failed to initiate transfer\r\n");
 96         return;
 97     }
 98     chThdWait(tp);

Also in oresat-firmware/src/f4/app_control/main.c:

 63 /**
 64  * @brief App Initialization
 65  */
 66 static void app_init(void)
 67 {
 68     /* Initialize WDT worker thread */
 69     init_worker(&wdt_worker, "WDT", wdt_wa, sizeof(wdt_wa), NORMALPRIO, wdt, NULL, true);
 70     reg_worker(&wdt_worker);
 71 
 72     /* Initialize shell worker thread */
 73     init_worker(&cmd_worker, "Shell", cmd_wa, sizeof(cmd_wa), NORMALPRIO, cmd, NULL, true);
 74     reg_worker(&cmd_worker);
 75 
 76     /* Initialize OPD */
 77     opd_init();
 78     /*opd_start();*/
 79 
 80     /* Initialize SDO client */
 81     sdo_init();
 82 
 83     /* Initialize shell and start serial interface */
 84     shellInit();
 85     sdStart(&SD3, NULL);
 86 
 87     /* Configure SCET time object */
 88     CO_OD_configure(CO->SDO[0], OD_2010_SCET, OD_SCET_Func, NULL, 0, 0);
 89 }
 90 


A clue found in what looks like OpenCAN library source"

/ext/CANopenNode/301/CO_SDOserver.h:681:} CO_SDO_return_t;

Some interesting CAN links outside of Oresat space:

^ Notes to sort

Note) Looks like a command shell is realized in code near end of file:

   psas/oresat-firmware/src/f4/app_control/source/command.c


Note) ina226.c and other device type driver source files in 'psas/oresat-firmware/common' give examples of I2C calls in ChibiOS environment.

INA226_SHARED_I2C


References:

  *  https://www.playembedded.org/blog/stm32-i2c-chibios/


Note)  Example file which calls an I2C init function among other things, in routine app_init():

 oresat-firmware/src$ vi ./archive/f0/app_solardemo/main.c

   /*
 * I2C configuration
 */
static const I2CConfig i2cfg1 =
{
    I2C_100KHZ_TIMINGR,
    0,
    0,
};

uint8_t data[8];

static void app_init(void) {
    //=== App initialization

    // Start up debug output
    sdStart(&SD2, &ser_cfg);

    i2cInit();
    i2cStart(&I2CD1, &i2cfg1);

    for (uint8_t i = 0; i < 8; ++i) {
        data[i] = 0;
    }

    canTPDOObjectInit(CAN_PDO_1, CAN_ID_DEFAULT, 0, 0, 8, data);
}


Note)  pointer *devp is of type INA226Driver, where is this defined?

   185 /**
   186  * @brief   Sets INA226 Alert type and value
   187  *
   188  * @param[in] devp       pointer to the @p INA226Driver object
   189  * @param[in] alert_me   the value to write to Mask/Enable register (0 to disable)
   190  * @param[in] alert_lim  the value to write to Alert Limit register
   191  *
   192  * @api
   193  */
   194 void ina226SetAlert(INA226Driver *devp, uint16_t alert_me, uint16_t alert_lim) {
   195     i2cbuf_t buf;
   196 
   197     osalDbgCheck(devp != NULL);
   198     osalDbgAssert(devp->state == INA226_READY,
   199             "ina226SetAlert(), invalid state");


Note) Device states for INA226 enumerated as follows:

:~/projects/psas/oresat-firmware/common/include$ grep -nr -A 2 -B 2 INA226_STOP ./*
./ina226.h-277-typedef enum {
./ina226.h-278-    INA226_UNINIT = 0,                  /**< Not initialized.                 */
./ina226.h:279:    INA226_STOP = 1,                    /**< Stopped.                         */
./ina226.h-280-    INA226_READY = 2,                   /**< Ready.                           */
./ina226.h-281-} ina226_state_t;


^ Virtual Methods Table VMT

So there's this idea and reference to virtual methods table data structures in ina226.h and .c. In ina226.h *vmt is defined as being:

    326 /**
    327  * @extends BaseObjectVMT
    328  *
    329  * @brief   @p INA226 virtual methods table.
    330  */
    331 struct INA226VMT {
    332     _ina226_methods
    333 };
  .
  .
  .
    345 /**
    346  * @brief INA226 Power Monitor class.
    347  */
    348 struct INA226Driver {
    349     /** @brief Virtual Methods Table.*/
    350     const struct INA226VMT     *vmt;
    351     _ina226_data
    352 };


In file oresat-firmware/ext/ChibiOS/os/hal/include/hal_objects.h:

     39 #define _base_object_methods                                                \^M
     40   /* Instance offset, used for multiple inheritance, normally zero. It^M
     41      represents the offset between the current object and the container^M
     42      object*/                                                               \^M
     43   size_t instance_offset;^M
     44 ^M

     45 /**^M
     46  * @brief   @p BaseObject specific data.^M
     47  * @note    This object defines no data.^M
     48  */^M
     49 #define _base_object_data^M
     50 ^M
     51 /**^M
     52  * @brief   @p BaseObject virtual methods table.^M
     53  */^M
     54 struct BaseObjectVMT {^M
     55   _base_object_methods^M
     56 };^M
     57 ^M
     58 /**^M
     59  * @brief   Base stream class.^M
     60  * @details This class represents a generic blocking unbuffered sequential^M
     61  *          data stream.^M
     62  */^M
     63 typedef struct {^M
     64   /** @brief Virtual Methods Table.*/^M
     65   const struct BaseObjectVMT *vmt;^M
     66   _base_object_data^M
     67 } BaseObject;^M


^ Enabling Serial Output for Development

Some notes relating to pull request 43:

To enable serial output, and serial console on C3 board, must have following reference which starts UART &SD2:

 44 /**
 45  * @brief App Initialization
 46  */
 47 static void app_init(void)
 48 {
 49     /* App initialization */
 50     init_worker(&worker1, "Blink", blink_wa, sizeof(blink_wa), NORMALPRIO, blink, NULL, true);
 51     init_worker(&worker2, "Solar Application", solar_wa, sizeof(solar_wa), NORMALPRIO, solar, NULL, true);
 52     init_worker(&worker3, "Read Temperature", read_temperature_wa, sizeof(read_temperature_wa), NORMALPRIO, read_temperature, NULL, true);
 53 
 54     reg_worker(&worker1);
 55     reg_worker(&worker2);
 56     reg_worker(&worker3);
 57 
 58     /* Start up debug output */
 59     sdStart(&SD2, NULL);
 60 
 61 }


^ IMU development work

Early studies:

*  https://learn.adafruit.com/comparing-gyroscope-datasheets
*  https://community.bosch-sensortec.com/t5/Knowledge-base/BMI08x-Design-Guide/ta-p/6929

Some example BMI088 driving code:

*  https://github.com/bolderflight/bmi088-arduino

BMI088 part and shuttle boards:

*  https://www.digikey.com/en/products/detail/bosch-sensortec/SHUTTLE-BOARD-BMI088/8634939
*  https://www.arrow.com/en/products/shuttle-board-bmi088/bosch
*  https://www.mouser.com/ProductDetail/Bosch-Sensortec/BMI088-Shuttle-Board?qs=%2Fha2pyFadugThBZfibBtysZH%252BRew77ZXPOGT6AyMWUO%252BUYqqucsM4A%3D%3D


- 2021-03-22 Monday - Looking for instances of clock setting file in Oresat firmware:

ted@localhost:~/projects/psas/oresat-firmware/ext/ChibiOS$ find . -name mcuconf.h | grep STM32F0
./demos/STM32/RT-STM32F031K6-NUCLEO32/cfg/mcuconf.h
./demos/STM32/RT-STM32F042K6-NUCLEO32/cfg/mcuconf.h
./demos/STM32/RT-STM32F070RB-NUCLEO64/cfg/mcuconf.h
./demos/STM32/RT-STM32F072RB-NUCLEO64/cfg/mcuconf.h
./demos/STM32/RT-STM32F072-DISCOVERY/cfg/mcuconf.h
./demos/STM32/RT-STM32F030R8-NUCLEO64/cfg/mcuconf.h
./demos/STM32/RT-STM32F091RC-NUCLEO64/cfg/mcuconf.h   <--- checking this one first - TMH
./demos/STM32/RT-STM32F051-DISCOVERY/cfg/mcuconf.h
./demos/STM32/NIL-STM32F051-DISCOVERY/cfg/mcuconf.h
./testhal/STM32/STM32F0xx/CAN/mcuconf.h
./testhal/STM32/STM32F0xx/ADC/mcuconf.h
./testhal/STM32/STM32F0xx/USB_CDC/mcuconf.h
./testhal/STM32/STM32F0xx/WDG/mcuconf.h
./testhal/STM32/STM32F0xx/IRQ_STORM/mcuconf.h
./testhal/STM32/STM32F0xx/PWM-ICU/mcuconf.h
ted@localhost:~/projects/psas/oresat-firmware/ext/ChibiOS$ 

- 2021-03-23 Tuesday -

External crystal oscillator not settling in ACS board:

(gdb) 
(gdb) bt
#0  0x08001620 in stm32_clock_init () at ../../../ext/ChibiOS/os/hal/ports/STM32/STM32F0xx/hal_lld.c:293
#1  0x0800977a in __early_init () at ../../../boards/ST_NUCLEO64_F091RC/board.c:213
#2  0x080000e0 in _crt0_entry () at ../../../ext/ChibiOS/os/common/startup/ARMCMx/compilers/GCC/crt0_v6m.S:181
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) cont
Continuing.

ChibiOS mentions in routine comment block in hal_lld.c that board.h is where certain clock constants are defined. Need to run gcc -M or similar to confirm which board.h file is a dependency of imu_dev code, but the board.h files which look most promising are:

ted@asha:~/projects/psas/oresat-firmware$ find . -name board.h | grep STM32F0
./ext/ChibiOS/os/hal/boards/ST_STM32F0_DISCOVERY/board.h
./ext/ChibiOS/os/hal/boards/ST_STM32F072B_DISCOVERY/board.h

Some possible example code for setting up internal RC oscillator as fallback:


Following workshop on new ChibiOS application board file creation:

  Files minimal set needed to be updated include:

    *  oresat-firmware/boards/[SPECIFIC_ORESAT_BOARD_NAME]/board.h
    *  f0/app_imu/cfg/mcuconf.h
    *  f0/app_imu/Makefile

2021-03-25 Thursday


^ MPPT Perturb and Disturb algorithm

Research paper found Monday evening:

Some additional publications on this algorithm:


^ References

arm-none-eabi-gcc tools:

Add to local datasheets:

RSSI - Received Signal Strength Indicator
CW - Continuous Wave
ASK - Amplitude Shift Key
./ext/ChibiOS/os/common/ext/ARM/CMSIS/Core/Include/cmsis_armclang.h:866:#define __REVSH(value) (int16_t)__builtin_bswap16(value)
./ext/ChibiOS/os/common/ext/ARM/CMSIS/Core/Include/cmsis_iccarm.h:247:  #if defined(__REVSH)
./ext/ChibiOS/os/common/ext/ARM/CMSIS/Core_A/Include/cmsis_armclang.h:177:#define __REVSH(value) (int16_t)__builtin_bswap16(value)
./ext/ChibiOS/os/common/ext/ARM/CMSIS/Core_A/Include/cmsis_iccarm.h:212:  #if defined(__REVSH)