skjafar / Portenta_Cube_Template

A sample project for running the Arduino portenta on STM32CubeIDE.
Other
29 stars 3 forks source link

Arduino Portenta Bare Metal Programming in STM32CubeIDE

Intro

I was looking for a very powerful controller to use for some work related projects. The Portenta was released in 2020 with not a lot of documentation at the beggining, but the high density connectors on the back were a great way to integrate this board into any PCB. But I wanted to program it using STM32CubeIDE, and with this I began reverse engineering the PCB and all the initialization code it requires.

What is this

An STM32CubeIDE project for the Arduino Portenta. This porject is not optimized in terms of power, the goal here is maximum speed. If you care for efficiency you will need to work some more on the code. check SystemClock_Config() in main.c in the M7 project.

Features

Getting started

  1. Download the project to your STM32CubeIDE workspace directory.

  2. Go to [File] => Import:

  3. Select [Exesting Projects into Workspace].

  4. Set the root directory to your Workspace folder and Select all 3 projects:

    • Portenta_Cube_Template.
    • Portenta_STM_Temaplate_CM4
    • Portenta_STM_Temaplate_CM7

    Make sure to select Search for nested projects

  5. Finish. Now you should have one master project with 2 nested projects.

    2021-12-15-025300_1906x1031_scrot1

Flashing

The Portenta comes with a custom bootloader that we are going to replace by the procedure that follows.

VERY IMPORTANT NOTE: AFTER THIS YOUR BOARD WILL NO LONGER WORK WITH THE ARDUINO IDE.

If you want to return the board to its original state you will have to flash the ArduinoCore-mbed bootloader. I tested the portentah7_bootloader_mbed_hs_v2.bin and it worked fine.

You should be able to directly flash the board without any problems, the SWD pins seem to be defined in the Arduino bootloader. Should you have any problems I recommend putting the controller in DFU mode, there are two ways of acheiving that:

JTAG / SWD

You will need a Debugging/Programming probe to program your controller, I have tested both the ST-LINK V3-MINI and the ST-LINK V3, and they both worked fine.

Unfortunately the Portenta board itself does not expose the SWD pins, you will need either the Breakout Board or the Vision Shield.

You can connect the 10 pin connector to the Vision Shield as shown below, it also takes the 20 pin connector.

As for the Breakout Board, this only takes the 20 pin connector as shown below

Building and running the code

The project comes predefined with 5 different run/debug configurations:

  1. Portenta_STM_template_CM7 Debug

    This will compile both the CM4 and CM7 projects, then flash them both to the controller. As the STM32H747 is a dual core controller it needs two different projects, one for each core, this build configuration takes care of building and flashing both. This will then enter Debug mode for the M7 core only.

  2. Portenta_STM_template_CM4 Debug

    After running the the Run configuration above you can use this to enter the Debug mode for the M4 core. This can be run only after the run configuration above is run.

  3. Portenta_STM_template_CM7 Release

    Same as configuration 1 but with optimization level at -O3. No debugging available.

  4. Portenta_STM_template_CM4 Release

    Same as configuration 2 but with optimization level at -O3. No debugging available.

  5. Portenta_STM_template_CM7_DEBUG_NO_BUILD_NO_FLASH

    This was used to be able to debug an already running code, mostly not important.

Interfaces

Virual Com Port

As soon as the board is connected to a PC it populates a virtual com port that can be accessed by any Serial Com Port interface, such as Putty or Minicom.

Configuration

This is what the screen will show once connected

It is mostly self-explanatory. FreeRTOS Heap and Ethernet buffers are shown, as well as the uptime in seconds.

The Task Statistics shows how much CPU time each FreeRTOS task is consuming. This can be used for profiling tasks.

If an Ethernet cable is connected to the board through one of the expansion boards, such as the Vision Sheild or the Breakout Board, Phy Link Up will be displayed along with the IP address the device received by your local DHCP server.

This interface is handled by two FreeRTOS tasks:

TCP based Command Line Interface

As soon as the device is connected to the local network it will start the task TCP CLI which takes care of this interface. You can connect to this using Putty or any other TCP telnet interface such as netcat.

Configuration

You can type help to see available commands.

STM32Cube Device Configuration Tool

When you double click the .ioc file the Device Configuration Tool tab will open, this enables you to easily configure your board and the libraries that will be added.

One important thing to note, I have tried to make the code as stable as possible, minimizng the effect of a modificaiton by this tool, but I was not 100% successful.

You will have to check what files have changed and check if you expected those files to change or not, I use the Source Control tool provided by VSCode, it shows all the moidifications that have been applied on the files.

VSCode_GitDiff

You will have to do the following to have your program working properly after a modification by this tool:

  1. Remove lines 19-46 from CM7/Core/Src/eth.c

    /* USER CODE END Header */
    /* Includes ------------------------------------------------------------------*/
    #include "eth.h"
    
    #if defined ( __ICCARM__ ) /*!< IAR Compiler */
    
    #pragma location=0x30040000
    ETH_DMADescTypeDef  DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
    #pragma location=0x300400C0
    ETH_DMADescTypeDef  DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
    #pragma location=0x300402F0
    uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_MAX_PACKET_SIZE]; /* Ethernet Receive Buffers */
    
    #elif defined ( __CC_ARM )  /* MDK ARM Compiler */
    
    __attribute__((at(0x30040000))) ETH_DMADescTypeDef  DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
    __attribute__((at(0x300400C0))) ETH_DMADescTypeDef  DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
    __attribute__((at(0x300402F0))) uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_MAX_PACKET_SIZE]; /* Ethernet Receive Buffer */
    
    #elif defined ( __GNUC__ ) /* GNU Compiler */
    
    ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT] __attribute__((section(".RxDecripSection"))); /* Ethernet Rx DMA Descriptors */
    ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT] __attribute__((section(".TxDecripSection")));   /* Ethernet Tx DMA Descriptors */
    uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_MAX_PACKET_SIZE] __attribute__((section(".RxArraySection"))); /* Ethernet Receive Buffers */
    
    #endif
    
    ETH_TxPacketConfig TxConfig;
  2. Remove lines 53-98 from CM7/Core/Src/eth.c

    ETH_HandleTypeDef heth;
    
    /* ETH init function */
    void MX_ETH_Init(void)
    {
    
     /* USER CODE BEGIN ETH_Init 0 */
    
     /* USER CODE END ETH_Init 0 */
    
      static uint8_t MACAddr[6];
    
     /* USER CODE BEGIN ETH_Init 1 */
    
     /* USER CODE END ETH_Init 1 */
     heth.Instance = ETH;
     MACAddr[0] = 0x00;
     MACAddr[1] = 0x80;
     MACAddr[2] = 0xE1;
     MACAddr[3] = 0x00;
     MACAddr[4] = 0x00;
     MACAddr[5] = 0x00;
     heth.Init.MACAddr = &MACAddr[0];
     heth.Init.MediaInterface = HAL_ETH_RMII_MODE;
     heth.Init.TxDesc = DMATxDscrTab;
     heth.Init.RxDesc = DMARxDscrTab;
     heth.Init.RxBuffLen = 1524;
    
     /* USER CODE BEGIN MACADDRESS */
    
     /* USER CODE END MACADDRESS */
    
     if (HAL_ETH_Init(&heth) != HAL_OK)
     {
       Error_Handler();
     }
    
     memset(&TxConfig, 0 , sizeof(ETH_TxPacketConfig));
     TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
     TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
     TxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT;
     /* USER CODE BEGIN ETH_Init 2 */
    
     /* USER CODE END ETH_Init 2 */
    
    }
  3. Remove the first call for MX_USB_DEVICE_Init() in CM7/Core/Src/main.c
    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_TIM2_Init();
    MX_USB_DEVICE_Init();
    /* USER CODE BEGIN 2 */

    It is called again after properly setting up the USB hardware.

  4. The Configuration Tool decides to remove the linked files for some reason. This is the most frustrating problem in this procedure, I am not able to keep the files that I add to each project stay. Of course this is usually not a problem, but since this is a nested project it shares the STM_HAL_Drivers, there is only one copy of those files and it located at the base folder not inside the nested folders. You need to link these files to each project to get it to work. The Configuration Tool decides to remove these links every time it is used.

    Here is how I do it, if you know a better way please let me know:

    • Right click on Drivers folder in the base project, Show In -> System Explorer.

      Readding_Links1

      This will open the folder in windows explorer.

    • Drag the folder named STM32H7xx_HAL_Driver to the Drivers folder inside the portenta_STM_template_CM4 Project.

      Readding_Links2

      Then select Link to files and recreate folder structure with virtual folders, keep the links relative to the PROJECT_LOC.

      Readding_Links3

    • Then delete the Inc folder and the License file. Make sure they have the small squares next to them, indicating that they are just links not the actual files. This step is optional for a cleaner project.

      Readding_Links4

Configuration Files

The project has some configuration files for setup.