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.
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.
Download the project to your STM32CubeIDE workspace directory.
Go to [File] => Import:
Select [Exesting Projects into Workspace].
Set the root directory to your Workspace folder and Select all 3 projects:
Make sure to select Search for nested projects
Finish. Now you should have one master project with 2 nested projects.
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:
Soldering a wire to the BOOT pin on the back of the board
I don't really recommend this, the pad is absolutely tiny and you might damage the board in the process. But this is the method I had to use initially. After a wire was soldered I used a 3V lithium coin battery to set this pin high (in reference to ground of course) and then plugged in the USB cable, this put the controller in DFU mode and enabled me to program it normally.
The yellow wire is soldered to the BOOT pad in the picture below.
Settig the BOOT Dip Switch on the Breakout Board
There is a dual dip switch on the Breakout Board, one of those switches is labeled BOOT, when you set this one to ON then reset the board, your board will be in DFU mode and can be programed using your ST-LINK device.
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
The project comes predefined with 5 different run/debug configurations:
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.
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.
Portenta_STM_template_CM7 Release
Same as configuration 1 but with optimization level at -O3. No debugging available.
Portenta_STM_template_CM4 Release
Same as configuration 2 but with optimization level at -O3. No debugging available.
Portenta_STM_template_CM7_DEBUG_NO_BUILD_NO_FLASH
This was used to be able to debug an already running code, mostly not important.
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:
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.
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.
You will have to do the following to have your program working properly after a modification by this tool:
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;
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 */
}
/* 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.
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.
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.
Then select Link to files and recreate folder structure with virtual folders, keep the links relative to the PROJECT_LOC.
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.
The project has some configuration files for setup.