Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

How do you write code for STM32

John99407

Junior Member level 3
Joined
Jul 14, 2019
Messages
26
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
239
I have worked enough with PIC micro and now need to work with STM32 (ARM). I never use code generated by MCC MPLAB Xc8 I write my own c code. because working with code generated by MCC is a pain for me. I only use generated code to configure device registers.

I have STM32F407 discovery board I am using STM32Cueb IDE. It has also code generator that generates code.

How do you write code for STM32 ?

Do you use code generated by code generator ? or do you use standard method to write code as we do for PIC MCU ?
 

doraemon

Super Moderator
Staff member
Joined
Jun 21, 2009
Messages
1,101
Helped
278
Reputation
564
Reaction score
260
Trophy points
1,363
Location
Japan
Activity points
11,012
Hello!

How do you write code for STM32 ?

I use a text editor.
No, seriously, what do you want to know exactly?
If you download STM32CubeIDE, you get everything you need.
There is also a separate CubeMX which is handy for configuration, but anyway CubeMX is now integrated into STM32CubeIDE.
With the graphical interface, you can configure your chip for what you want to use, and you get the whole code generated. From there, you just add your own code.
Drawback: if you write in C++, be aware that CubeMX only generates C. But STM32CubeIDE allows to convert
to C++, in which case you have to rename main.c to main.cpp.

Dora
 

Georgy.Moshkin

Full Member level 5
Joined
Oct 9, 2017
Messages
245
Helped
44
Reputation
88
Reaction score
34
Trophy points
28
Activity points
2,276
Usually you start new STM32 project by clicking File → New → STM32 Project, select required microcontroller from the list and perform preliminary configuration.
The minimums is to set crystal/ceramic resonator and enable debug pins. To do that, see the list in "Pinout & Configuration" tab of STM32CubeIDE configurator, and go through these settings:
1. System Core → RCC → High Speed Clock (HSE) → select "Crystal/Ceramic Resonator"
2. Trace and Debug → Debug → select "Serial Wire"
3. Open "Clock Configuration" tab, enter Crystal "Input frequency", set PLL source to HSE, usually set all dividers and multipliers to achieve maximum allowable frequencies on APB buses.
4. Then you go back to "Pinout & Configuration" tab. In "Pinout view" you can set pins to GPIO_Output or GPIO_Input state. Other states may be configured by enabling peripherals from the list on the left side (Analog, Timers, Connectivity, etc.). After enabling peripherals some pins may be re-defined in "Pinout view" to more convenient position for your design. For alternative pin position you can Ctrl+Click on the pin that is already defined (highlighted with green light). For example on some MCU USART5_RX by default may be configured on PB12, but redefined later in "Pinout View" to PB6 or PC12.
5. Save configuration project, generate the code.

Now about writing actual code. Note that STM32CubeIDE configuration tool generates mostly peripheral initialization code, and not the actual algorithms for your program. Most of time you will write code in two places.
First, is between "/* USER CODE BEGIN 2 */" and "/* USER CODE END2 */" comments. You put all initialization code that runs once during microcontroller power-up or after reset. You can fill some arrays here, call some HAL functions to start timers, ADCs, and other peripherals. For example, you can start ADC to sample input pin voltage each 100 milliseconds and put it to circular array using DMA. Actual values of sampling rate and DMA configuration usually already set using configurator tool, but in some (rare) cases you may want to re-define something. Also you can start UART to receive data and wait to some command.
Second, is below "/* Infinite loop */". You can put code after "while (1) {", just make sure it stays between "USER CODE BEGIN/END" sections. It is the main loop of the application. A quick test may be putting something like HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1); HAL_Delay(1000); and check if PA1 pin will change voltage between 0v and 3.3v each second. Note that PA1 must be configured as GPIO_Output in configurator tool. A more complex task may be waiting for command from UART and then sending ADC receive buffer in response.
 

John99407

Junior Member level 3
Joined
Jul 14, 2019
Messages
26
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
239
Hello!
No, seriously, what do you want to know exactly?
Do you use CubeMX in STM32CubeIDE to write code for your project? or you use CubeMX to initialize peripheral only and then you write your own code.


Hi,

* Config via CubeMX
* own code via Atollic Studio
* C

Klaus
I have written code that is working on my board.

My question is regarding to debugging, I don't have any interface between PC and STM32 board. I just have one cable that I use to connect ESP and PC. Can I directly see the result of debugging such as variable value location etc on PC screen.

C:
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */


  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
       HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
       HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
       HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);
       HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_15);
        HAL_Delay(500);
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);

  /*Configure GPIO pins : PD12 PD13 PD14 PD15 */
  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
 

doraemon

Super Moderator
Staff member
Joined
Jun 21, 2009
Messages
1,101
Helped
278
Reputation
564
Reaction score
260
Trophy points
1,363
Location
Japan
Activity points
11,012
Hello!

Do you use CubeMX in STM32CubeIDE to write code for your project? or you use CubeMX to
initialize peripheral only and then you write your own code.

CubeMX (standalone or integrated in the STM32 IDE) only sets up the peripherals of your chip.
Configure what you want to use, and then generate the code. That's apparently what you are doing
in your code since there are functions with MX prefix. Of couse it doesn't write application code
for you and you have to write your own code.

My question is regarding to debugging, I don't have any interface between PC and STM32 board.
I just have one cable that I use to connect ESP and PC. Can I directly see the result of debugging
such as variable value location etc on PC screen.

You mean the USB cable? That should be enough. You mean this one?
The top part is the interface between your PC and the big STM32 chip at the center.
It allows the PC to communicate with the chip, and you will have access to all the registers at
runtime. Everytime you set a breakpoint, you can access any of the CPU registers, port status,
SPI flag or whatever. By the way, this interface is called STLink. There are newer interfaces, you can
buy for example a stlink 3, but I'm not sure of the differences.

Pascal
 

Aussie Susan

Advanced Member level 4
Joined
Jan 5, 2015
Messages
1,323
Helped
387
Reputation
774
Reaction score
394
Trophy points
83
Activity points
14,285
There are books around (such as 'Mastering STM32') that can be downloaded (some for free or a very low price) that can take you step by step through the architecture of the STM32 families, plus how to install the STM32CubeIDE and understand the HAL.
I also have programmed PICs for many years and I also don't use the MCC. However in my opinion, any ARM based MCU is just too hard to program. even something very basic as 'flash a LED' without something such as STM2CubeIDE to do the low level basics.
Once you have the framework set up, it is just like any other MCU where you need to understand what your app is to do, break it down into small and manageable functions and build from there.
Susan
 

doraemon

Super Moderator
Staff member
Joined
Jun 21, 2009
Messages
1,101
Helped
278
Reputation
564
Reaction score
260
Trophy points
1,363
Location
Japan
Activity points
11,012
Hello!

I wouldn't be surprised that ARM chips are actually not as complicated as
they look, if they were programmed the right way. I don't have time, so I use
the HAL layer but I'm sure there are simpler ways.
I have the impression that HAL layer hides simplicity with a layer of
complexity. So yes, I agree, the HAL layer is not easy to understand.

I was just looking recently what MX_GPIO_setup() does, and for a simple
register assignment, there are myriads of macro scattered over 2 or 3 files,
most of which do nothing (e.g UNUSED(some_value) which is here just to avoid
warnings which anyway can be avoided). And also the do <something> while(0).
It reminds me the BeOS API where there was this function:
bool IsComputerOn(), which returns 1 only if your computer is powered on.
As for the return value when the computer is off, nobody knows. Like the
light in the fridge.
But at least this one was a joke.

Dora.
 

KlausST

Super Moderator
Staff member
Joined
Apr 17, 2014
Messages
20,531
Helped
4,463
Reputation
8,935
Reaction score
4,496
Trophy points
1,393
Activity points
135,752
Hi,

I just have a little experience with STM32F10x.
We use CubeMX and C, we also use HAL libraries.

I have the impression that HAL layer hides simplicity with a layer of
complexity.
The idea - I think - is to include the HAL layler as somehow "hardware_independent" layer.
I agree the HAL layer is not that simple and sometimes using own functions/libraries is much faster and more effective.
But going through each microcontroller type´s datasheet isn´t fun...

Even setting/clearing a port pin isn´t only writing 1 or 0 to a register bit....

But the periferals and the modes to use them (straight, interrupt, DMA) saves a lot of processing power.

I am impressed.

Klaus
 

Georgy.Moshkin

Full Member level 5
Joined
Oct 9, 2017
Messages
245
Helped
44
Reputation
88
Reaction score
34
Trophy points
28
Activity points
2,276
For debugging I usually use SWD (actual debugging functionality) + UART output.
You can use USB hub with 4 ports to connect three pieces of hardware:
USB Port 1: STM32 development kit
USB Port 2: Cheap ST-Link v2 USB stick
USB Port 3: USB to TTL converter USB stick
USB Port 4: unused
Most of time debugging is done by evaluating UART output (graphs, values) using custom software and / or serial terminal. If this information is not enough to solve the issue, I run actual debugging process in IDE. You can step through code, see variable values, variable memory address, etc..

In some cases STM32CubeMonitor software may be useful:
Using this application you can open *.ELF from compiled binary directory and put variables on 2D graph or in table. Values are updated through SWD st-link debug connection in real-time. If you just need how few values change over time there are some video tutorials you can find.
 

vinodstanur

Advanced Member level 3
Joined
Oct 31, 2009
Messages
752
Helped
114
Reputation
234
Reaction score
114
Trophy points
1,333
Location
Kerala (INDIA)
Activity points
7,065
I have worked enough with PIC micro and now need to work with STM32 (ARM). I never use code generated by MCC MPLAB Xc8 I write my own c code. because working with code generated by MCC is a pain for me. I only use generated code to configure device registers.

I have STM32F407 discovery board I am using STM32Cueb IDE. It has also code generator that generates code.

How do you write code for STM32 ?

Do you use code generated by code generator ? or do you use standard method to write code as we do for PIC MCU ?
We can use the code generated by cubemx, that is absolutely fine. Now if you want to generate low level driver code instead of the bulky HAL libraries, you can opt that option in the code generator, (LL), then you will be able to fine more or less similar to you PIC style of coding. Also without code generator, you can use the standard peripheral library provided by st which seems to be more stable. If you still want to write your own, you can go ahead by reading the datasheet but compared to an AVR/PIC, more steps will be there, say more registers need to be configured.

Imagine a use case, say you want to use the USB peripheral, writing code for it from scratch is not practical, so have to use the library. :)
 

LaTeX Commands Quick-Menu:

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Top