一、开发环境搭建
1. 开发板选择
嵌入式开发需基于硬件平台实践,建议选择 STM32系列开发板(如STM32F103C8T6)。此类开发板集成丰富外设接口(GPIO、UART、ADC等),适合初学者验证基础功能。
2. Linux环境配置
嵌入式开发通常依赖Linux系统,推荐通过虚拟机或双系统安装 Ubuntu 22.04 LTS,并配置以下工具链:
交叉编译工具链:通过命令 sudo apt-get install gcc-arm-none-eabi 安装ARM架构编译器。
调试工具:安装OpenOCD(sudo apt-get install openocd)用于程序烧录与调试。
IDE选择:推荐使用VS Code + PlatformIO插件,或专用工具如STM32CubeIDE。
3. 工程管理工具
掌握 Makefile 编写规则,实现代码编译自动化。例如:
CC = arm-none-eabi-gcc
CFLAGS = -mcpu=cortex-m3 -Wall
all: main.o
$(CC) $(CFLAGS) -o project.elf main.c
clean:
rm -f *.o *.elf
此Makefile定义了编译规则与清理操作。
二、代码实战:LED闪烁案例
1. 硬件连接
假设开发板的LED连接在 GPIOA Pin5,需配置为推挽输出模式。
2. C代码实现
c
#include "stm32f1xx.h"
int main(void) {
// 启用GPIOA时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// 配置PA5为推挽输出模式
GPIOA->CRL &= ~(0xF << 20); // 清除原有配置
GPIOA->CRL |= (0x1 << 20); // 输出模式,最大速度10MHz
while (1) {
GPIOA->ODR ^= GPIO_ODR_ODR5; // 翻转PA5电平
for (int i = 0; i < 1000000; i++); // 简单延时
}
}
代码解析:
通过寄存器操作控制GPIO,直接操作硬件资源提升效率。
RCC 寄存器启用外设时钟,GPIOx_CRL 配置引脚模式,GPIOx_ODR 控制输出电平。
3. 编译与烧录
编译:执行 make 生成 project.elf 文件。
烧录:使用OpenOCD连接开发板,通过命令:
bash
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "program project.elf verify reset exit"
将程序写入Flash。
三、功能扩展:按键控制LED
1. 新增代码逻辑
c
// 配置PA0为输入模式(按键)
GPIOA->CRL &= ~(0xF << 0);
GPIOA->CRL |= (0x4 << 0); // 浮空输入模式
while (1) {
if (GPIOA->IDR & GPIO_IDR_IDR0) { // 检测PA0电平
GPIOA->ODR |= GPIO_ODR_ODR5; // 点亮LED
} else {
GPIOA->ODR &= ~GPIO_ODR_ODR5; // 熄灭LED
}
}
功能说明:通过读取GPIO输入状态实现按键控制LED亮灭,涉及中断与轮询的初级应用。
四、调试技巧
逻辑分析仪:使用Saleae或PulseView观察GPIO信号时序。
GDB调试:通过OpenOCD启动GDB服务器,远程调试程序运行状态。
五、学习路径建议
掌握C语言核心语法:重点学习指针、结构体、位操作。
理解硬件外设原理:如UART通信、定时器、ADC采集等。
进阶操作系统:学习FreeRTOS或嵌入式Linux多任务调度。
通过以上步骤,初学者可快速建立嵌入式开发知识体系,逐步实现从点亮LED到复杂系统设计的跨越。
以下是一个基于STM32F103C8T6的完整外设调用示例,整合了GPIO控制、USART通信、ADC采集和PWM输出功能,采用HAL库进行开发:
c
/* 包含文件 */
#include "stm32f1xx_hal.h"
#include <string.h>
/* 硬件定义 */
#define LED_PIN GPIO_PIN_13
#define LED_GPIO_PORT GPIOC
#define USART_INSTANCE USART3
#define ADC_INSTANCE ADC1
#define PWM_TIM TIM3
/* 全局变量 */
ADC_HandleTypeDef hadc;
UART_HandleTypeDef huart3;
TIM_HandleTypeDef htim3;
char uart_buf;
uint16_t adc_value = 0;
/* 系统时钟初始化 */
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置72MHz主频
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RSE_STATE_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;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
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;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
/* ADC初始化 */
void ADC_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
hadc.Instance = ADC_INSTANCE;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
HAL_ADC_Init(&hadc);
sConfig.Channel = ADC_CHANNEL_0; // PA0作为ADC输入:ml-citation{ref="2,3" data="citationList"}
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES5;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
}
/* USART初始化 */
void USART_Init(void) {
huart3.Instance = USART_INSTANCE;
huart3.Init.BaudRate = 9600;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart3);
}
/* PWM初始化 */
void PWM_Init(void) {
TIM_OC_InitTypeDef sConfigOC = {0};
htim3.Instance = PWM_TIM;
htim3.Init.Prescaler = 72-1; // 1MHz计数频率
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1000-1; // 1kHz PWM频率:ml-citation{ref="5,6" data="citationList"}
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500; // 初始占空比50%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
}
/* GPIO初始化 */
void GPIO_Init(void) {
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// LED配置
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_GPIO_PORT, &GPIO_InitStruct);
}
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init();
ADC_Init();
USART_Init();
PWM_Init();
HAL_ADC_Start(&hadc); // 启动ADC连续转换:ml-citation{ref="2" data="citationList"}
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动PWM输出:ml-citation{ref="6" data="citationList"}
while(1) {
// LED状态切换
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN); // :ml-citation{ref="3,4" data="citationList"}
// 读取ADC值
adc_value = HAL_ADC_GetValue(&hadc);
// 通过USART发送数据
sprintf(uart_buf, "ADC Value: %d\r\n", adc_value);
HAL_UART_Transmit(&huart3, (uint8_t*)uart_buf, strlen(uart_buf), 100); // :ml-citation{ref="1,7" data="citationList"}
// 调整PWM占空比
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, adc_value/4); // ADC值映射到PWM
HAL_Delay(500); // 500ms延时
}
}
硬件连接说明:
ADC输入:PA0引脚连接电位器(0-3.3V输入)
PWM输出:PA6引脚连接LED或电机驱动电路
USART接口:PB10(TX)、PB11(RX)连接蓝牙模块
状态LED:PC13连接LED指示灯
功能实现说明:
系统时钟:配置72MHz主频,APB1总线36MHz,APB2总线72MHz
ADC采集:连续转换模式,71.5周期采样时间,右对齐数据格式
PWM输出:1kHz频率,占空比可调,使用TIM3通道1输出
USART通信:9600波特率,8位数据格式,支持透传数据
控制逻辑:每500ms更新LED状态,发送ADC数据,调整PWM占空比
编译环境要求:
使用STM32CubeIDE 2.0+开发环境
需安装STM32F1 HAL库v1.8.5+
连接ST-LINK V2调试器进行烧录
该示例展示了STM32F103C8T6的典型应用场景,包含时钟管理、外设初始化和实时控制逻辑,可直接用于物联网节点、电机控制等应用场景。