目录
一、引言
STM32F103C8T6 是一款基于 ARM Cortex-M3 内核的 32 位微控制器,以其高性能、低功耗和丰富的外设资源而广泛应用于嵌入式系统开发。对于嵌入式开发初学者来说,掌握 STM32F103C8T6 的基本知识并实现简单的功能(如点亮 LED 灯)是迈向嵌入式开发的重要一步。本文将详细介绍 STM32F103C8T6 的基本特性、开发环境搭建以及如何通过 GPIO 控制点亮第一个 LED 灯。
二、STM32F103C8T6 基本特性
1. 内核与性能
STM32F103C8T6 采用 ARM Cortex-M3 内核,工作频率最高可达 72MHz,提供高性能处理能力。其哈佛架构的存储器系统支持高达 60MHz 的系统时钟,确保了高效的代码执行。
2. 存储器
Flash 存储器 :内置 64KB 的 Flash 存储器,用于存储程序代码和常量数据。
SRAM :提供 20KB 的 SRAM,用于存储变量和运行时数据。
3. 时钟系统
内部时钟源 :包括 8MHz 的高速内部时钟(HSI)和 40KHz 的低速内部时钟(LSI)。
外部时钟源 :支持 12MHz 的高速外部时钟(HSE)和 32.768KHz 的低速外部时钟(LSE)。
时钟树结构 :通过配置时钟树,可以灵活调整系统时钟频率,满足不同应用的需求。
4. GPIO(通用输入输出)
引脚数量 :具有 42 个 GPIO 引脚,分布在 5 个端口(A、B、C、D、E)。
工作模式 :每个 GPIO 引脚可以配置为输入、输出、复用功能或模拟模式。
输出模式 :输出模式下,可配置为推挽输出或开漏输出。
5. 外设
定时器 :包含多个通用定时器和高级定时器,支持多种计时和 PWM 输出功能。
通信接口 :支持 UART、SPI、I2C 等多种通信接口,方便与其他设备进行数据交换。
ADC(模数转换器) :提供 12 位 ADC,用于将模拟信号转换为数字信号。
三、开发环境搭建
1. 硬件准备
开发板 :STM32F103C8T6 开发板,通常配备有 LED 灯、按键等基本外设。
下载器 :用于将编译后的程序烧录到 STM32 芯片中,常见的下载器有 ST-Link、J-Link 等。
电脑 :用于编写和编译代码。
2. 软件安装
集成开发环境(IDE) :推荐使用 Keil uVision 或 STM32CubeIDE。Keil 是一款功能强大的 IDE,支持 STM32 系列芯片的开发;STM32CubeIDE 是由 ST 公司推出的免费 IDE,集成了丰富的开发工具。
驱动库 :安装 STM32 标准外设库或 HAL 库,这些库提供了对 STM32 外设的封装函数,简化了开发过程。
四、点亮第一个 LED 灯
1. 硬件连接
-
将开发板上的 LED 灯连接到 GPIO 引脚,例如 PA0 引脚。确保 LED 的正极连接到 GPIO 引脚,负极通过电阻连接到地(GND)。
2. 软件实现
(1)创建新工程
-
打开 Keil uVision 或 STM32CubeIDE,创建一个新的 STM32 工程,选择 STM32F103C8T6 为目标芯片。
(2)配置时钟
-
在
system_stm32f10x.c
文件中,配置系统时钟。通常使用 HSE(外部高速时钟)作为系统时钟源,并通过 PLL(锁相环)倍频到所需的频率,例如 72MHz。
void SystemInit(void)
{
// 配置时钟
SystemCoreClockUpdate();
SetSysClock();
}
void SetSysClock(void)
{
// 配置 HSE 和 PLL
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
// 使能 HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 12MHz * 9 = 108MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | 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;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
(3)配置 GPIO
-
在主函数中,配置 PA0 引脚为输出模式。
#include "stm32f10x.h"
int main(void)
{
// 初始化时钟
SystemInit();
// 配置 GPIOA 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置 PA0 引脚为推挽输出模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 点亮 LED
GPIO_SetBits(GPIOA, GPIO_Pin_0);
while (1)
{
// 主循环
}
}
(4)编译与下载
-
编译工程,确保没有错误。使用下载器将编译后的程序烧录到 STM32 芯片中。如果一切正常,LED 灯将被点亮。
五、代码解析
1. 时钟配置
-
时钟配置是 STM32 初始化的重要步骤。通过配置时钟树,可以确保系统时钟频率满足要求。在上述代码中,我们使用 HSE 作为时钟源,并通过 PLL 倍频到 72MHz。
2. GPIO 配置
-
GPIO 引脚需要先使能对应的时钟,然后通过
GPIO_InitTypeDef
结构体配置引脚模式和速度。在本例中,我们将 PA0 配置为推挽输出模式,速度为 50MHz。
3. LED 控制
-
使用
GPIO_SetBits
函数将 PA0 引脚置为高电平,点亮 LED 灯。如果需要熄灭 LED,可以使用GPIO_ResetBits
函数。
六、总结
通过本文的介绍,我们学习了 STM32F103C8T6 的基本特性、开发环境搭建以及如何通过 GPIO 控制点亮第一个 LED 灯。点亮 LED 灯是 STM32 开发的入门实验,掌握了这一基本操作后,可以进一步学习 STM32 的其他外设功能,如定时器、ADC、通信接口等。希望本文能够帮助初学者顺利入门 STM32 开发。
七、参考代码
以下是完整的代码示例,用于点亮 PA0 引脚上的 LED 灯:
#include "stm32f10x.h"
void SystemInit(void)
{
// 配置时钟
SystemCoreClockUpdate();
SetSysClock();
}
void SetSysClock(void)
{
// 配置 HSE 和 PLL
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
// 使能 HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 12MHz * 9 = 108MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | 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;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
int main(void)
{
// 初始化时钟
SystemInit();
// 配置 GPIOA 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置 PA0 引脚为推挽输出模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 点亮 LED
GPIO_SetBits(GPIOA, GPIO_Pin_0);
while (1)
{
// 主循环
}
}
八、常见问题与解答
1. 为什么 LED 灯不亮?
检查硬件连接是否正确,确保 LED 的正负极连接正确。
检查程序是否正确配置了 GPIO 引脚。
检查时钟配置是否正确,确保 GPIO 时钟已使能。
2. 如何更改 LED 灯的引脚?
修改 GPIO 配置部分的代码,将
GPIO_Pin_0
更改为所需的引脚编号,例如GPIO_Pin_1
。确保对应的 GPIO 时钟已使能。
3. 如何实现 LED 灯闪烁?
在主循环中,使用延时函数交替调用
GPIO_SetBits
和GPIO_ResetBits
函数,实现 LED 灯的闪烁。
while (1)
{
GPIO_SetBits(GPIOA, GPIO_Pin_0); // 点亮 LED
for (int i = 0; i < 1000000; i++); // 延时
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 熄灭 LED
for (int i = 0; i < 1000000; i++); // 延时
}