Keil代码移植

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

105.2.8Keil代码移植

/* ==================== Core\Inc\main.h ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

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

#define KEY1_Pin GPIO_PIN_3
#define KEY1_GPIO_Port GPIOE
#define KEY2_Pin GPIO_PIN_4
#define KEY2_GPIO_Port GPIOE
#define LED2_Pin GPIO_PIN_5
#define LED2_GPIO_Port GPIOE
#define SUN_Pin GPIO_PIN_11
#define SUN_GPIO_Port GPIOB
#define LED1_Pin GPIO_PIN_5
#define LED1_GPIO_Port GPIOB
#define BEEP_Pin GPIO_PIN_8
#define BEEP_GPIO_Port GPIOB

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

/* Private defines -----------------------------------------------------------*/

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

/* ==================== Core\Inc\gpio.h ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.h
  * @brief   This file contains all the function prototypes for
  *          the gpio.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __GPIO_H__
#define __GPIO_H__

#ifdef __cplusplus
extern "C" {
#endif

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

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MDK_GPIO_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */


/* ==================== Core\Src\main.c ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"


void SystemClock_Config(void);


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

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

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



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



  /* Initialize all configured peripherals */
  MDK_GPIO_Init();

  /* Infinite loop */

  while (1)
  {
		if(0 == HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin))
	  {
		  HAL_Delay(120);
		  if(0 == HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin))
		  {
			  HAL_GPIO_TogglePin(BEEP_GPIO_Port, BEEP_Pin);
		  }
	  }
	  if(0 == HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin))
	  {
		  HAL_Delay(120);
		  if(0 == HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin))
		  {
			  HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
		  }
	  }

	  if(0 == HAL_GPIO_ReadPin(SUN_GPIO_Port, SUN_Pin))
	  {
		  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 1);
	  }else{
		  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 0);
	  }
  }

}

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

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/* 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 */
  __disable_irq();
  while (1)
  {
  }
  /* 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,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/* ==================== Core\Src\gpio.c ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.c
  * @brief   This file provides code for the configuration
  *          of all used GPIO pins.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

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

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/** Configure pins as
        * Analog
        * Input
        * Output
        * EVENT_OUT
        * EXTI
*/
void MDK_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : PEPin PEPin */
  GPIO_InitStruct.Pin = KEY1_Pin|KEY2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED2_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = SUN_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(SUN_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = LED1_Pin|BEEP_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

一、寄存器地址相关概念

课程先用 51 单片机的 reg52.h 帮助理解“头文件如何把名字映射到硬件”。在 8051 中,特殊功能寄存器和位地址常写成:

sfr P0 = 0x80;
sbit P0_0 = P0^0;

这表示 P0 对应特殊功能寄存器地址 0x80,P0_0 对应该寄存器的第 0 位。它不是在 RAM 中创建一个普通变量,而是让编译器生成对固定硬件地址的访问。

STM32 使用 CMSIS 结构体完成同一类映射:

#define GPIOB_BASE  0x40010C00UL
#define GPIOB       ((GPIO_TypeDef *)GPIOB_BASE)

访问 GPIOB->ODR 时,编译器根据 GPIO_TypeDef 中 ODR 的 0x0C 偏移,最终访问 0x40010C0C。两种写法语法不同,核心思想相同:

可读名称 ──> 固定地址/位 ──> 总线访问 ──> 真实硬件寄存器

Keil C51 的 sfr/sbit 是 8051 编译器扩展,不能直接用于 ARM;STM32 工程应使用设备头文件中的 CMSIS 类型、宏和 HAL 接口。

二、代码移植

这里的“移植”不是把整个 CubeIDE 工程目录复制到 Keil,而是把同一芯片、同一 HAL 版本下的用户层功能合入已经能编译的 Keil 工程。课程移植的是 GPIO 实验,目标保持:

  • PB5、PE5 控制两只低电平有效 LED;
  • PE3、PE4 读取两只低电平有效按键;
  • PB11 读取传感器 DO;
  • PB8 控制高电平有效蜂鸣器;
  • 使用 8 MHz HSE,经 PLL×9 得到 72 MHz 系统时钟。

移植前必须核对芯片型号、启动文件、链接地址、HAL 驱动版本和编译器。若工程目标芯片不同,即使函数名称相同,启动文件、中断向量和可用外设也可能不匹配。

三、网盘模板介绍

课程配套模板已经包含 Keil 工程框架、CMSIS、STM32F1 HAL 驱动、启动文件和芯片选择。模板的作用是提供可编译的基础设施,用户仍要理解各文件的职责:

目录/文件职责
Core/Inc应用头文件、引脚宏和函数声明
Core/Srcmain、GPIO 初始化、系统中断等应用源码
Drivers/CMSISCortex-M 内核接口与芯片寄存器定义
Drivers/STM32F1xx_HAL_DriverHAL 外设驱动
MDK-ARM/startup_stm32f103xe.s中断向量表、栈、复位入口
.uvprojxKeil 工程分组、文件与目标设置

模板只能解决工程组织问题,不能替代引脚核对。开发板电路和 main.h 中的宏必须一致。

四、移植内容

课程操作的核心内容包括:

  1. 从 CubeIDE 的 GPIO 工程复制 main.c 中的用户逻辑和 SystemClock_Config;
  2. 将 CubeMX 生成的 MX_GPIO_Init 改名为 MDK_GPIO_Init,避免与模板中已有符号或课程约定冲突;
  3. 从 main.h 复制按键、LED、传感器和蜂鸣器的引脚宏;
  4. 将 gpio.c/gpio.h 加入 Keil 工程并保证函数声明一致;
  5. 清理只用于 CubeMX 再生成保护的 USER CODE 注释,不改变有效语句;
  6. 确认 Keil 工程包含所需 HAL 源文件和头文件路径。

函数改名必须同时修改声明、定义和调用:

Core/Inc/gpio.h        void MDK_GPIO_Init(void);
Core/Src/gpio.c        void MDK_GPIO_Init(void) { ... }
Core/Src/main.c        MDK_GPIO_Init();

三处只改一处会分别产生“隐式声明”“未定义符号”或链接失败。

五、gpioc文件配置

GPIO 初始化的顺序应保持:

flowchart TD
    CLK[开启 GPIOE/GPIOA/GPIOB 时钟] --> LEVEL[预置 LED 与蜂鸣器输出电平]
    LEVEL --> INPUT[配置 PE3/PE4、PB11 输入]
    INPUT --> OUTPUT[配置 PB5/PE5、PB8 推挽输出]

先预置输出电平,再切换成输出模式,可以减少上电瞬间的错误闪烁或蜂鸣。课程电路中:

网络引脚模式有效电平
KEY1PE3上拉输入
KEY2PE4上拉输入
SUNPB11上拉输入取决于模块 DO
LED1PB5推挽输出
LED2PE5推挽输出
BEEPPB8推挽输出

按键松开由内部上拉保持为 1,按下后接地变为 0。LED 接在 3.3 V 与 GPIO 之间,需要 GPIO 拉低吸收电流;蜂鸣器由 S8050 三极管驱动,PB8 经 1 kΩ 使三极管导通,因此高电平响。

六、主函数配置

主函数的必要顺序:

HAL_Init();
SystemClock_Config();
MDK_GPIO_Init();

while (1)
{
    /* 应用逻辑 */
}
  • HAL_Init() 初始化 HAL、SysTick 和默认中断优先级分组;
  • SystemClock_Config() 配置 HSE、PLL、AHB/APB 分频;
  • MDK_GPIO_Init() 开启端口时钟并配置引脚;
  • 无限循环中再进行按键检测、LED、传感器与蜂鸣器控制。

若在 HAL_Init 之前调用依赖 HAL 时基的函数,或在端口时钟开启前访问 GPIO,都会破坏初始化前提。

七、编译运行

编译和下载按以下顺序检查:

  1. 在 Options for Target 的 Device 中确认实际 STM32F103 型号;
  2. 检查 C/C++ 的 Include Paths,确保 Core/Inc、CMSIS 和 HAL Inc 可见;
  3. 检查预处理宏,尤其是目标器件宏和 USE_HAL_DRIVER;
  4. 确认 startup_stm32f103xe.s 与芯片容量匹配;
  5. Build 后先处理第一条编译错误,后续错误常由它连锁产生;
  6. 在 Debug 中选择实际下载器,在 Flash Download 中加入正确算法;
  7. 需要下载后自动运行时勾选 Reset and Run;
  8. 下载后按开发板复位键,验证 LED、按键和蜂鸣器。

常见报错定位:

现象优先检查
找不到 main.h/gpio.hInclude Paths
找不到 HAL_GPIO_InitHAL GPIO 源文件是否加入工程
找不到 MDK_GPIO_Init声明、定义、调用是否同名,gpio.c 是否参与编译
大量芯片类型未定义设备宏、stm32f1xx.h 与目标型号
可编译但下载失败下载器、驱动、SWD 接线、供电、Flash 算法
下载成功但不运行Reset and Run、时钟源、启动地址、Error_Handler

不要通过随意关闭调试选项来掩盖问题。最可靠的顺序是先让空模板可编译、可下载、可停在 main,再逐项合入 GPIO 功能;每加入一组文件就重新编译一次,便于缩小故障范围。