🎓博主介绍:精通 C、Python、Java、JavaScript 等编程语言,具备全栈开发能力。日常专注于分享编程干货、算法解析、项目实战经验,以及前沿技术动态。让我们一起在技术的道路上不断探索,共同成长!
嵌入式开发入门:C 语言在 STM32 项目中的实战应用
一、引言
嵌入式系统在当今科技领域无处不在,从智能家居到工业自动化,从汽车电子到医疗设备,嵌入式技术都发挥着至关重要的作用。STM32 系列微控制器作为 ARM Cortex - M 内核的经典代表,以其高性能、低功耗、丰富的外设等特点,成为嵌入式开发的热门选择。而 C 语言凭借其高效、灵活和可移植性,成为了 STM32 开发的首选编程语言。本文将带领大家走进嵌入式开发的世界,详细介绍 C 语言在 STM32 项目中的实战应用。
二、STM32 开发环境搭建
2.1 硬件准备
2.1.1 STM32 开发板
常见的 STM32 开发板有 STM32F103 系列、STM32F407 系列等。以 STM32F103C8T6 开发板为例,它具有丰富的 GPIO 引脚、串口、SPI、I2C 等外设接口,适合初学者进行基础实验和项目开发。
2.1.2 调试工具
常用的调试工具是 ST - Link 或 J - Link 仿真器,用于将程序下载到开发板并进行调试。
2.2 软件准备
2.2.1 开发工具
可以选择 Keil MDK(Microcontroller Development Kit)作为开发环境,它是专门为 ARM 微控制器设计的集成开发环境,支持 C、C++ 等编程语言,具有强大的代码编辑、编译、调试功能。可以从 Keil 官方网站(https://www.keil.com/)下载并安装。
2.2.2 固件库
STM32 官方提供了标准外设库和 HAL(Hardware Abstraction Layer)库。标准外设库是早期的开发库,对硬件的控制更加底层;HAL 库则是后来推出的抽象层库,使用更加方便,易于移植。可以从 STM32 官方网站(https://www.st.com/)下载所需的固件库。
三、C 语言基础回顾
3.1 变量与数据类型
在 C 语言中,常见的数据类型有 int
(整数)、float
(浮点数)、char
(字符)等。例如:
#include <stdio.h>
int main() {
int num = 10;
float f = 3.14;
char ch = 'A';
printf("整数: %d, 浮点数: %f, 字符: %c\n", num, f, ch);
return 0;
}
3.2 函数与指针
函数是一段具有特定功能的代码块,指针是存储变量内存地址的变量。例如:
#include <stdio.h>
// 函数定义
int add(int a, int b) {
return a + b;
}
int main() {
int num1 = 3, num2 = 5;
int result = add(num1, num2);
int *p = &num1;
printf("两数之和: %d, 指针指向的值: %d\n", result, *p);
return 0;
}
四、STM32 项目实战:点亮 LED 灯
4.1 项目需求分析
点亮 LED 灯是嵌入式开发的入门项目,通过控制 STM32 的 GPIO(通用输入输出)引脚输出高低电平,来控制 LED 灯的亮灭。
4.2 代码实现步骤
4.2.1 配置 GPIO 引脚
在 STM32 中,需要先对 GPIO 引脚进行初始化配置,包括选择引脚、设置引脚模式等。以下是使用 HAL 库进行 GPIO 初始化的代码示例:
#include "stm32f1xx_hal.h"
// 定义 LED 引脚
#define LED_PIN GPIO_PIN_13
#define LED_PORT GPIOC
// 系统时钟配置
void SystemClock_Config(void);
// GPIO 初始化
static void MX_GPIO_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1)
{
// 点亮 LED
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
HAL_Delay(1000); // 延时 1 秒
// 熄灭 LED
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
HAL_Delay(1000); // 延时 1 秒
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** 初始化 RCC 振荡器
*/
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();
}
/** 初始化 RCC 时钟
*/
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();
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO 端口时钟使能 */
__HAL_RCC_GPIOC_CLK_ENABLE();
/* 配置 GPIO 引脚 */
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
}
4.2.2 控制 LED 灯亮灭
使用 HAL_GPIO_WritePin
函数来控制 GPIO 引脚的输出电平,从而实现 LED 灯的亮灭控制。GPIO_PIN_RESET
表示输出低电平,GPIO_PIN_SET
表示输出高电平。
4.2.3 延时函数的使用
使用 HAL_Delay
函数来实现延时功能,控制 LED 灯亮灭的时间间隔。
4.3 代码编译与下载
4.3.1 编译代码
在 Keil MDK 中,打开项目文件,点击编译按钮(“Build”),Keil 会对代码进行编译和链接,生成可执行文件(.hex 或 .bin 文件)。
4.3.2 下载程序
将 ST - Link 仿真器与开发板连接,在 Keil MDK 中点击下载按钮(“Download”),将生成的可执行文件下载到 STM32 开发板中。下载完成后,开发板上的 LED 灯将按照程序设定的逻辑进行亮灭闪烁。
五、STM32 项目进阶:串口通信
5.1 项目需求分析
串口通信是嵌入式系统中常用的通信方式,通过串口可以实现开发板与计算机或其他设备之间的数据传输。本项目将实现 STM32 开发板通过串口向计算机发送数据。
5.2 代码实现步骤
5.2.1 配置串口
使用 HAL 库对串口进行初始化配置,包括选择串口、设置波特率、数据位、停止位等。以下是串口初始化的代码示例:
#include "stm32f1xx_hal.h"
UART_HandleTypeDef huart1;
// 系统时钟配置
void SystemClock_Config(void);
// GPIO 初始化
static void MX_GPIO_Init(void);
// 串口初始化
static void MX_USART1_UART_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
char *message = "Hello, Serial Communication!\r\n";
while (1)
{
// 通过串口发送数据
HAL_UART_Transmit(&huart1, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);
HAL_Delay(1000); // 延时 1 秒
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** 初始化 RCC 振荡器
*/
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();
}
/** 初始化 RCC 时钟
*/
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();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
5.2.2 发送数据
使用 HAL_UART_Transmit
函数向串口发送数据,该函数的参数包括串口句柄、数据缓冲区指针、数据长度和超时时间。
5.2.3 接收数据(可选)
如果需要接收计算机发送的数据,可以使用 HAL_UART_Receive
函数,该函数的使用方法与 HAL_UART_Transmit
类似。
5.3 串口调试
使用串口调试助手(如 SecureCRT、串口调试精灵等),将计算机与 STM32 开发板通过串口线连接,设置好波特率等参数,即可在串口调试助手中看到开发板发送的数据。
六、总结
通过以上两个实战项目,我们了解了 C 语言在 STM32 项目中的基本应用,从简单的 GPIO 控制到串口通信,逐步掌握了 STM32 开发的基本流程和方法。在实际开发中,还可以结合更多的外设和功能,如 ADC(模拟 - 数字转换)、定时器、SPI、I2C 等,开发出更加复杂和实用的嵌入式系统。希望本文能为嵌入式开发初学者提供一些帮助,让大家在嵌入式开发的道路上迈出坚实的一步。