Example for updating the MTi-1's firmware. Uses a platform independent, retargetable pure C implementation of the firmware updater protocol.

Dependencies:   mbed-rtos mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

Go to the documentation of this file.
00001 /*! \file
00002     \copyright Copyright (C) Xsens Technologies B.V., 2015.
00003 
00004     Licensed under the Apache License, Version 2.0 (the "License"); you may not
00005     use this file except in compliance with the License. You may obtain a copy
00006     of the License at
00007 
00008     http://www.apache.org/licenses/LICENSE-2.0
00009 
00010     Unless required by applicable law or agreed to in writing, software
00011     distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00012     WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
00013     License for the specific language governing permissions and limitations
00014     under the License.
00015 
00016     \page Embedded firmware example
00017     The purpose of this example is to demonstrate how to update the firmware of a MTi-1 series module using the FwUpdate library.
00018     The example embeds an Xsens firmware file (xff) which is converted to a data array with srecord (http://srecord.sourceforge.net).
00019     The example is designed to run on a Nucleo F401 board. Porting to other mbed platforms should be easy but not all boards may
00020     have enough flash memory to hold the full xff
00021 
00022     \section Software setup
00023     Select the required bus mode by uncommenting one of the following defines at the top of main.cpp
00024     \verbatim
00025     BUS_MODE_SPI
00026     BUS_MODE_I2C
00027     BUS_MODE_UART
00028     \endverbatim
00029     Next, build the example and program it in the Nucleo F401 board.
00030 
00031     \section Development board setup
00032     Configure the required bus mode (UART, I2C, SPI) with the dip switches on the MTi-1 development board.
00033     (At the bottom of the development board a table with switch settings is shown)
00034 
00035     \section Hardware setup
00036 
00037     Unpower the board and make the following connections between the Nucleo F401 board and the
00038     MTi 1 series development board (depending on the selected bus mode, the unused connections may be
00039     left unconnected)
00040 
00041     Power supply and reset:
00042     \verbatim
00043     |----------------|---------------|-------------------------|
00044     |   Signal       | Nucleo F401   | Mti-1s Dev board header |
00045     |----------------|---------------|-------------------------|
00046     |    5V          |     5V        |       1                 |
00047     |    3V3         |     3V3       |       2                 |
00048     |    GND         |     GND       |       3                 |
00049     |   RESET        |     PC9       |       5                 |
00050     |----------------|---------------|-------------------------|
00051     \endverbatim
00052 
00053     Uart:
00054     \verbatim
00055     |----------------|---------------|-------------------------|
00056     |   Signal       | Nucleo F401   | Mti-1s Dev board header |
00057     |----------------|---------------|-------------------------|
00058     |  Tx of Nucleo  |    PA9        |       11                |
00059     |  Tx of MTi     |    PA10       |       9                 |
00060     |----------------|---------------|-------------------------|
00061     \endverbatim
00062 
00063     I2C:
00064     \verbatim
00065     |----------------|---------------|-------------------------|
00066     |   Signal       | Nucleo F401   | Mti-1s Dev board header |
00067     |----------------|---------------|-------------------------|
00068     |    SCL         |    PB8        |       9                 |
00069     |    SDA         |    PB9        |       11                |
00070     |    DRDY        |    PB3        |       15                |
00071     |    ADD0        |    PB13       |       17                |
00072     |    ADD1        |    PB14       |       19                |
00073     |    ADD2        |    PB15       |       21                |
00074     |----------------|---------------|-------------------------|
00075     \endverbatim
00076 
00077     SPI:
00078     \verbatim
00079     |----------------|---------------|-------------------------|
00080     |   Signal       | Nucleo F401   | Mti-1s Dev board header |
00081     |----------------|---------------|-------------------------|
00082     |    SCK         |    PB13       |       17                |
00083     |    MISO        |    PB14       |       19                |
00084     |    MOSI        |    PB15       |       21                |
00085     |    nCS         |    PB6        |       23                |
00086     |    DRDY        |    PB3        |       15                |
00087     |----------------|---------------|-------------------------|
00088     \endverbatim
00089 
00090     \section Connection with host PC
00091     Connect the Nucleo board with a USB cable to the host PC. Find out on which
00092     COM port the Nucleo board is mapped by the OS, and open a terminal emulator (e.g. PuTTY)
00093     on this port. The default baud rate of the example is 921600. Pressing 'h' in the terminal
00094     window should return the following usage text:
00095 
00096     \verbatim
00097     Embedded firmware updater example
00098     Interface: SPI
00099 
00100     h: Print this text
00101     c: GotoConfig
00102     m: GotoMeasurement
00103     r: Soft reset the module
00104     b: GotoBootloader
00105     v: Request firmware revision
00106     d: Request deviceId
00107     u: Start firmware update (make sure module is in bootloader mode)
00108     x: Hard reset the module and make it stay in bootloader
00109     \endverbatim
00110 
00111     After power-up the module automatically goes to measurement mode. In this mode
00112     only a limited set of commands is supported. Therefore, the module must first be
00113     switched to config mode by pressing 'c'. The module should response with XMID_GotoConfigAck.
00114 
00115     Next, the firmware revision can be requested by pressing 'v'. This should return
00116     the firmware revision in the format Major.Minor.Patch. If the Major version number is 255, the
00117     module is in bootloader mode. Any other Major version number means that the MTi-1s main firmware is
00118     running. In this case the module must be set in bootloader mode before a firmware update can be done.
00119     The module can be placed in bootloader mode by pressing either the 'b' or 'x' key
00120  */
00121 
00122 
00123 #include "mbed.h"
00124 #include "rtos.h"
00125 #include "board.h "
00126 #include "xbusmessage.h "
00127 #include "xbusparser.h "
00128 #include "xbusmessageid.h "
00129 #include "mtinterface_mtssp.h "
00130 #include "mtinterface_uart.h "
00131 #include "mtssp_i2c_driver.h "
00132 #include "mtssp_spi_driver.h "
00133 #include "fwupdate.h "
00134 #include "xffdata.h"
00135 
00136 
00137 /*! \brief Bus mode to use by this example.
00138 */
00139 //#define BUS_MODE_SPI
00140 #define BUS_MODE_I2C
00141 //#define BUS_MODE_UART
00142 
00143 static void updateUartBaudrate();
00144 
00145 /*! \brief Instance of MtInterface. Should be initialized by createMtInterface().
00146 */
00147 static MtInterface* mtInterface = 0;
00148 
00149 
00150 /*! \brief Serial object for communicating with the PC.
00151 */
00152 static Serial pc(PC_TX, PC_RX);
00153 
00154 /*! \brief Reset line towards the module. Pulling this line down keeps the module in reset.
00155 */
00156 static DigitalInOut resetLine(MT_RESET, PIN_INPUT, OpenDrain, 1);
00157 
00158 
00159 /*! \brief Instance of the firmware updater
00160 */
00161 static FwUpdate *g_fwUpdate = NULL;
00162 
00163 
00164 /*! \brief Create an instance of MtInterface.
00165 */
00166 static void createMtInterface()
00167 {
00168 #ifdef BUS_MODE_SPI
00169     MtsspDriver* mtsspDriver = new MtsspSpiDriver;
00170     mtInterface = new MtInterfaceMtssp(mtsspDriver);
00171 #endif
00172 
00173 #ifdef BUS_MODE_I2C
00174     MtsspDriver* mtsspDriver = new MtsspI2cDriver;
00175     mtInterface = new MtInterfaceMtssp(mtsspDriver);
00176 #endif
00177 
00178 #ifdef BUS_MODE_UART
00179     mtInterface = new MtInterfaceUart(115200);
00180 #endif
00181 }
00182 
00183 
00184 /*! \brief Callback function for FwUpdate for reading data from the xff (xsens firmware file) file.
00185     \param buffer Target buffer in which the xff data should be written
00186     \param offset Offset in the xff file where reading should start
00187     \param length Number of bytes which is requested
00188     \returns Number of bytes which is actually written to the buffer
00189 */
00190 static int readXffData(uint8_t *buffer, int offset, int length)
00191 {
00192     static int lastOffset = 0;
00193     int n;
00194     if (offset == 0)
00195         lastOffset = 0;
00196 
00197     if ((offset == 0) || ((offset - lastOffset) > 4096))
00198     {
00199         pc.printf("%05d\r", offset);
00200         lastOffset = offset;
00201     }
00202     
00203     for (n = 0; n < length; n++)
00204     {
00205         if (offset + n == g_xffData_length)
00206             break;
00207         buffer[n] = g_xffData[offset + n];
00208     }
00209     return n;
00210 }
00211 
00212 
00213 /*! \brief Callback function for FwUpdate for signaling that a firmware update has completed.
00214     \param result Result of the firmware update, either FWU_Success or FWU_Failed
00215 */
00216 static void readyHandler(FWU_Result result)
00217 {
00218     if (result == FWU_Success)
00219         pc.printf("\r\nFirmware update ready\r\n");
00220     else if (result == FWU_Failed)
00221         pc.printf("\r\nFirmware update failed\r\n");
00222     
00223     if (mtInterface->busFormat() == XBF_Uart)
00224         updateUartBaudrate();
00225 }
00226 
00227 
00228 /*! \brief Sends a XMID_GotoConfig message to the Module
00229 */
00230 static void gotoConfig()
00231 {
00232     XbusMessage xbusMessage(XMID_GotoConfig);
00233     mtInterface->sendXbusMessage(&xbusMessage);
00234 }
00235 
00236 /*! \brief Handles xbus messages from the module.
00237     \param xbusMessage The xbus message received from the module
00238 
00239     Messages with message identifier XMID_FirmwareUpdate are passed to the firmware updater, other messages are just printed.
00240 */
00241 static void handleXbusMessage(XbusMessage *xbusMessage)
00242 {
00243     switch (xbusMessage->m_mid)
00244     {
00245     case XMID_Wakeup:
00246     {
00247         //If the wakeup message is received we force the module in config mode
00248         gotoConfig();
00249         pc.printf("XMID_Wakeup\r\n");
00250     } break;
00251 
00252     case XMID_GotoConfigAck:
00253     {
00254         pc.printf("XMID_GotoConfigAck\r\n");
00255     } break;
00256 
00257     case XMID_GotoMeasurementAck:
00258     {
00259         pc.printf("XMID_GotoMeasurementAck\r\n");
00260     } break;
00261 
00262     case XMID_ResetAck:
00263     {
00264         pc.printf("XMID_ResetAck\r\n");
00265     } break;
00266 
00267     case XMID_GotoBootLoaderAck:
00268     {
00269         pc.printf("XMID_GotoBootLoaderAck\r\n");
00270     } break;
00271 
00272     case XMID_Error:
00273     {
00274         pc.printf("XMID_Error\r\n");
00275     } break;
00276 
00277     case XMID_FirmwareUpdate:
00278     {
00279         if (g_fwUpdate != NULL)
00280         {
00281             FwUpdate_handleXbus(g_fwUpdate, xbusMessage);
00282         }
00283     } break;
00284 
00285     case XMID_FirmwareRevision:
00286     {
00287         pc.printf("XMID_FirmwareRevision: %d.%d.%d\r\n", xbusMessage->m_data[0], xbusMessage->m_data[1], xbusMessage->m_data[2]);
00288     } break;
00289 
00290     case XMID_DeviceId:
00291     {
00292         uint32_t deviceId = (xbusMessage->m_data[0] << 24) | (xbusMessage->m_data[1] << 16) | (xbusMessage->m_data[2] << 8) | xbusMessage->m_data[3];
00293         pc.printf("XMID_DeviceId: %08X\r\n", deviceId);
00294     } break;
00295 
00296     case XMID_MtData2:
00297     {
00298         pc.printf("XMID_MtData2\r\n");
00299     } break;
00300 
00301     default:
00302     {
00303         pc.printf("Unhandled xbus message, mid = 0x%02X, len = %d", xbusMessage->m_mid, xbusMessage->m_length);
00304         if (xbusMessage->m_length > 0)
00305         {
00306             pc.printf(", data = ");
00307             for (int n = 0; n < xbusMessage->m_length; n++)
00308             {
00309                 pc.printf("%02X ", xbusMessage->m_data[n]);
00310             }
00311         }
00312         pc.printf("\r\n");
00313     }
00314     }
00315 }
00316 
00317 
00318 /*! \brief Reset the module via the hardware reset line.
00319     \param stayInBootloader: If stayInBootloader is true, an XMID_GotoBootLoader is sent immediately after the reset.
00320 
00321     By sending an XMID_GotoBootLoader within 100 ms after reset the module stays in bootloader mode
00322     instead of booting the application. This can be used if the normal GotoBootloader command fails to put
00323     the module in bootloader mode, e.g. because of a faulty firmware.
00324 */
00325 static void hardwareReset(bool stayInBootloader)
00326 {
00327     resetLine = 1;
00328     resetLine.output();
00329     wait(0.001);
00330     resetLine = 0;
00331     wait(0.001);
00332     resetLine.input();  // (configure the line as input because the OpenDrain setting of the mbed gpio pin does not seem to work correctly)     
00333 
00334     if (stayInBootloader)
00335     {
00336         wait(0.02);
00337         XbusMessage xbusMessage(XMID_GotoBootLoader);
00338         mtInterface->sendXbusMessage(&xbusMessage);
00339     } else
00340     {
00341         wait(0.2);
00342     }
00343 }
00344 
00345 
00346 /*! \brief Try to find the correct uart baudrate
00347 */
00348 static void updateUartBaudrate()
00349 {
00350     if (mtInterface->busFormat() != XBF_Uart)
00351         return;
00352     
00353     pc.printf("Detecting uart baudrate of module\r\n");
00354     
00355     const int nofBaudrates = 2;
00356     int baudrates[nofBaudrates] = {921600, 115200};
00357     
00358     bool success = false;
00359     for (int n = 0; n < nofBaudrates; n++)
00360     {
00361         ((MtInterfaceUart*)mtInterface)->setBaudrate(baudrates[n]);
00362         pc.printf("Baudrate set to %d\r\n", baudrates[n]);
00363         XbusMessage gotoConfigMessage(XMID_GotoConfig);
00364         XbusMessage* result = mtInterface->sendAndWait(&gotoConfigMessage);
00365         success = result && result->m_mid == XMID_GotoConfigAck;
00366         mtInterface->releaseXbusMessage(result);
00367         if (success)
00368             break;
00369     }
00370     if (success)
00371         pc.printf("Detecting uart baudrate of module done\r\n");
00372     else
00373         pc.printf("Detecting uart baudrate of module failed\r\n");
00374 }
00375 
00376 bool isInBootloader()
00377 {
00378     XbusMessage fwRevisionMessage(XMID_ReqFirmwareRevision);
00379     XbusMessage* ack = mtInterface->sendAndWait(&fwRevisionMessage);
00380 
00381     bool result = false;
00382     if (ack && ack->m_mid == XMID_FirmwareRevision)
00383         result = ack->m_data[0] == 255;
00384 
00385     mtInterface->releaseXbusMessage(ack);
00386     return result;
00387 }
00388 
00389 /*! \brief C-wrapper for sendXbusMessage callback function of FwUpdate.
00390 */
00391 static void sendXbusMessageWrapper(XbusMessage const* xbusMessage)
00392 {
00393     if (mtInterface)
00394         mtInterface->sendXbusMessage(xbusMessage);
00395 }
00396 
00397 
00398 /*! \brief Helper function for converting a XbusLowLevelFormat to string.
00399 */
00400 static char* lowLevelFormatToString(XbusBusFormat format)
00401 {
00402     switch (format)
00403     {
00404     case XBF_I2c:
00405         return "I2C";
00406     case XBF_Spi:
00407         return "SPI";
00408     case XBF_Uart:
00409         return "UART";
00410     }
00411     return "unknown";
00412 }
00413 
00414 
00415 /*! \brief Print usage info of this example.
00416 */
00417 static void printUsageInfo()
00418 {
00419     pc.printf("\r\nEmbedded firmware updater example\r\n");
00420     pc.printf("Interface: %s\r\n\r\n", lowLevelFormatToString(mtInterface->busFormat()));
00421     pc.printf("h: Print this text\r\n");
00422     pc.printf("c: GotoConfig\r\n");
00423     pc.printf("m: GotoMeasurement\r\n");
00424     pc.printf("r: Soft reset the module\r\n");
00425     pc.printf("b: GotoBootloader\r\n");
00426     pc.printf("v: Request firmware revision\r\n");
00427     pc.printf("d: Request deviceId\r\n");
00428     pc.printf("u: Start firmware update (make sure module is in bootloader mode)\r\n");
00429     pc.printf("x: Hard reset the module and make it stay in bootloader\r\n");
00430     pc.printf("\r\n");
00431 }
00432 
00433 /*! \brief Main entry point of the embedded firmware updater example.
00434 */
00435 int main()
00436 {
00437     resetLine.mode(OpenDrain);
00438     // Configure communication with the pc:
00439     pc.baud(921600);
00440     pc.format(8, Serial::None, 1);
00441     pc.printf("\r\n\r\n\r\n--------------------------------------\r\n");
00442 
00443     // Initialize the mtInterface object for communicating with the module:
00444     createMtInterface();
00445     
00446     // If Uart mode is used update the baudrate to match the baudrate of the module:
00447     if (mtInterface->busFormat() == XBF_Uart)
00448         updateUartBaudrate();
00449 
00450     // Reset the module
00451     hardwareReset(false);
00452 
00453     // Initialize the firmware updater:
00454     g_fwUpdate = new FwUpdate();
00455     uint8_t* fwuTxBuffer = new uint8_t[FWU_REQUIRED_TXBUFFER_SIZE];
00456     g_fwUpdate->m_readXffData = readXffData;
00457     g_fwUpdate->m_sendXbusMessage = sendXbusMessageWrapper;
00458     g_fwUpdate->m_readyHandler = readyHandler;
00459     g_fwUpdate->m_txBuffer = fwuTxBuffer;
00460     FwUpdate_init(g_fwUpdate);
00461 
00462     printUsageInfo();
00463 
00464     // Main loop:
00465     bool running = true;
00466     while (running)
00467     {
00468         mtInterface->process();
00469 
00470         XbusMessage* xbusMessage = mtInterface->getXbusMessage();
00471         bool updateUartBaudrateRequired = false;
00472         if (xbusMessage != NULL)
00473         {
00474             handleXbusMessage(xbusMessage);
00475             updateUartBaudrateRequired = (mtInterface->busFormat() == XBF_Uart && (xbusMessage->m_mid == XMID_ResetAck || xbusMessage->m_mid == XMID_GotoBootLoaderAck));
00476             mtInterface->releaseXbusMessage(xbusMessage);
00477         }
00478         
00479         if (updateUartBaudrateRequired)
00480         {
00481             wait(0.2);
00482             updateUartBaudrate();
00483         }
00484 
00485         if (pc.readable())
00486         {
00487             char cmd = pc.getc();
00488             switch (cmd)
00489             {
00490             case 'h':
00491             {
00492                 printUsageInfo();
00493             } break;
00494 
00495             case 'c':
00496             {
00497                 gotoConfig();
00498             } break;
00499 
00500             case 'm':
00501             {
00502                 XbusMessage xbusMessage(XMID_GotoMeasurement);
00503                 mtInterface->sendXbusMessage(&xbusMessage);
00504             } break;
00505 
00506             case 'r':
00507             {
00508                 XbusMessage xbusMessage(XMID_Reset);
00509                 mtInterface->sendXbusMessage(&xbusMessage);
00510             } break;
00511 
00512             case 'b':
00513             {
00514                 XbusMessage xbusMessage(XMID_GotoBootLoader);
00515                 mtInterface->sendXbusMessage(&xbusMessage);
00516             } break;
00517 
00518             case 'v':
00519             {
00520                 XbusMessage xbusMessage(XMID_ReqFirmwareRevision);
00521                 mtInterface->sendXbusMessage(&xbusMessage);
00522             } break;
00523 
00524             case 'd':
00525             {
00526                 XbusMessage xbusMessage(XMID_ReqDid);
00527                 mtInterface->sendXbusMessage(&xbusMessage);
00528             } break;
00529 
00530             case 'u':
00531             {
00532                 if (isInBootloader())
00533                 {
00534                     pc.printf("Starting firmware update\r\n");
00535                     FwUpdate_init(g_fwUpdate);
00536                     FwUpdate_start(g_fwUpdate);
00537                 }
00538                 else
00539                 {
00540                     pc.printf("Firmware update not possible now. First switch module to bootloader.\r\n");
00541                 }
00542             } break;
00543 
00544             case 'x':
00545             {
00546                 hardwareReset(true);
00547             } break;
00548             }
00549         }
00550     }
00551 
00552     delete g_fwUpdate;
00553     delete[] fwuTxBuffer;
00554     return -1;
00555 }
00556