STM32 Development Techniques and Best Practices

KEIL Configuration

System Reset Implementation

__set_FAULTMASK(1);
NVIC_SystemReset();

Crystal Oscillator Configuration

In the stm32f10x.h file, modify the system clock setup:

static void ConfigureSystemClockTo72MHz(void)
{
  __IO uint32_t startupCounter = 0, hseStatus = 0;
  
  /*!< Enable the High Speed External oscillator (HSE) */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /*!< Wait till HSE is ready, timeout if not */
  do
  {
    hseStatus = RCC->CR & RCC_CR_HSERDY;
    startupCounter++;  
  } while((hseStatus == 0) && (startupCounter != HSEStartUp_Timeout));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    hseStatus = (uint32_t)0x01;
  }
  else
  {
    hseStatus = (uint32_t)0x00;
  }  

  if (hseStatus == (uint32_t)0x01)
  {
    /*!< Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /*!< Set 2 wait states for Flash */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    
 
    /*!< HCLK = SYSCLK  */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /*!< PCLK2 = HCLK (Typically used for APB2 peripherals) */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /*!< PCLK1 = HCLK / 2 (Typically used for APB1 peripherals) */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
    
    /*!< PLLCLK = HSE * 6 = 72 MHz (8*9=72 or 6*12=72) */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL6);  //12M*6=72M

    /*!< Enable PLL */
    RCC->CR |= RCC_CR_PLLON;

    /*!< Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }

    /*!< Select PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

    /*!< Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  }
  else
  { /*!< If HSE fails to start, application will have wrong clock configuration. User can add code here to handle this error */    

    /*!< Infinite loop */
    while (1)
    {
    }
  }
}

STM32CubeIDE Features

Implementing printf Funcsionality

Add the following code to either usart.c or main.c:

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#define GETCHAR_PROTOTYPE int __io_getchar(FILE *f)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#define GETCHAR_PROTOTYPE int fgetc(FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}
GETCHAR_PROTOTYPE
{
	uint8_t ch = 0;
	HAL_UART_Receive(&huart1,(uint8_t *)&ch, 1, 0xFFFF);
	if (ch == '\r')
	{
		__io_putchar('\r');
		ch = '\n';
	}
	return __io_putchar(ch);
}

Include the necessary header:

#include <stdio.h>

Configure the IDE to use scanf library files.

Generating Hex and Bin Files

Navigate to toolbar: Project → Properties

Follow these steps to configure the output format:

  1. Select the appropriate options for generating both Hex and Bin files
  2. Apply and Close
  3. After compilation, check the Debug folder for the generated files

Code Highlighting Feature

Enable this feature to highlight identical code sections throughout your project. When activated, matching code blocks will be highlighted; when disabled, only the currently selected portion will be shown.

String Prefix Detection Function

/* String processing function for char or uint8_t arrays */
uint8_t has_prefix(const char *source, const char *prefix)
{
    while (*prefix)
        if (*prefix++ != *source++)
            return 0;
    return 1;
}

Usage example:

if(has_prefix((char *)uart_rx_buffer, "CMD,")) // Check prefix

Data Parsing and Conversion Functions

Example input format: CMD,12,23,45,87,88,\r\n

if(has_prefix((char *)uart5_rx_buffer, "CMD,"))  // Check prefix
 {
  char *token;
  token = strtok((char *)uart5_rx_buffer, ",");      // First part before comma -- CMD
  char *roll_value = strtok(NULL, ",");   // 12
  char *pitch_value = strtok(NULL, ","); // 23
  double roll_data = strtod(roll_value, NULL);  // Convert string to double
  double pitch_data = strtod(pitch_value, NULL); // Convert string to double
  
  /* Optional: Print received values */
  printf("%.1f,%.1f\r\n", roll_data, pitch_data);
  
  /* Clear buffer if needed */
 }

Modifying Code Start Address in Flash

For a 128KB Flash with a 32KB bootloader at address 0x08000000:

The main code should include a function to handle the Flash offset.

Suporting Chinese Character Output

While IDE defaults to UTF-8 encoding, you can add GBK support for Chinese characters:

In the miscellaneous compiler options, add:

-fexec-charset=GBK

Modifying Font and Size

  1. Go to Window → Preferences
  2. Navigate to General → Appearance → Color and Fonts → C/C++ → Editor → Text Font
  3. Select your preferred font, style, and size
  4. Click Apply and Close

Troubleshooting Project Creation

If STMcube appears grayed out during new project creation and only "Empty" is available:

  1. Check available disk space
  2. Verify if an IDE udpate is needed

Additional Programming Tips

C Language Bit Manipulation

Example of flipping a specific bit in an 8-bit value:

0xAA = 1010 1010 To flip the 3rd bit:

uint8_t data = 0xAA;
data ^= 0x08;  // XOR operation to flip bit 3
// Result: 0xA2 = 1010 0010

Using ST-Link to View MCU Memory Contents

To view memory contents with ST-Link:

  1. Use STM32 ST-LINK Utility or ST Visual Programmer software
  2. Before connecting, ensure the MCU is reset
  3. Connect the ST-Link and view the memory contents

Tags: STM32 Keil STM32CubeIDE printf UART

Posted on Wed, 13 May 2026 05:54:23 +0000 by simpli