C语言嵌入式开发:STM32微控制器、外设驱动、实时操作系统(RTOS)深度解析

一、前言:为什么STM32是嵌入式开发的首选平台?
学习目标
- 理解STM32的本质:基于ARM Cortex-M内核的微控制器,广泛应用于嵌入式系统
- 明确STM32的重要性:资源丰富、性能强大、开发工具成熟
- 掌握本章学习重点:STM32微控制器、外设驱动、RTOS(FreeRTOS)的开发方法、避坑指南、实战案例分析
- 学会使用C语言开发STM32嵌入式系统,实现硬件交互和控制
重点提示
💡 STM32是嵌入式开发的首选平台!它基于ARM Cortex-M内核,具有丰富的外设和强大的性能,广泛应用于物联网、工业控制、消费电子等领域。
二、模块1:STM32微控制器基础
2.1 学习目标
- 理解STM32微控制器的本质:基于ARM Cortex-M内核的32位微控制器
- 掌握STM32的核心架构:CPU内核、内存结构、外设接口
- 掌握STM32的开发环境:CubeIDE、Keil MDK、GCC工具链
- 掌握STM32的避坑指南:避免引脚配置错误、避免时钟配置错误、避免内存访问违规
- 避开STM32使用的3大常见坑
2.2 STM32的核心架构
CPU内核:ARM Cortex-M3/M4/M7(不同型号内核不同)
内存结构:Flash(程序存储器)、RAM(数据存储器)、EEPROM(非易失性数据存储器)
外设接口:GPIO、UART、SPI、I2C、ADC、DAC等
2.3 STM32的开发环境
CubeIDE:
# 下载并安装CubeIDE
# 创建项目,选择STM32型号
# 配置引脚和外设
# 生成代码并编译
Keil MDK:
# 下载并安装Keil MDK
# 创建项目,选择STM32型号
# 配置引脚和外设
# 生成代码并编译
三、模块2:外设驱动开发
3.1 学习目标
- 理解外设驱动的本质:控制STM32外设的软件,负责管理外设资源
- 掌握外设驱动的开发方法:使用HAL库、LL库、直接操作寄存器
- 掌握常用外设驱动:GPIO、UART、SPI、I2C、ADC的开发方法
- 掌握外设驱动的避坑指南:避免引脚配置错误、避免时钟配置错误、避免外设初始化失败
- 避开外设驱动使用的3大常见坑
3.2 常用外设驱动
GPIO驱动:
#include "stm32f1xx_hal.h"
#define LED_GPIO_PORT GPIOA
#define LED_GPIO_PIN GPIO_PIN_5
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LED_GPIO_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);
}
void LED_Toggle(void) {
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PIN);
}
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init();
while (1) {
LED_Toggle();
HAL_Delay(1000);
}
}
UART驱动:
#include "stm32f1xx_hal.h"
UART_HandleTypeDef huart1;
void UART_Init(void) {
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
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;
HAL_UART_Init(&huart1);
}
void UART_SendString(const char *str) {
HAL_UART_Transmit(&huart1, (uint8_t *)str, strlen(str), HAL_MAX_DELAY);
}
void UART_ReceiveString(char *buffer, uint16_t size) {
HAL_UART_Receive(&huart1, (uint8_t *)buffer, size, HAL_MAX_DELAY);
}
int main(void) {
HAL_Init();
SystemClock_Config();
UART_Init();
char buffer[100];
while (1) {
UART_SendString("请输入字符串:");
UART_ReceiveString(buffer, sizeof(buffer));
UART_SendString("你输入的字符串是:");
UART_SendString(buffer);
UART_SendString("\r\n");
}
}
四、模块3:实时操作系统(FreeRTOS)开发
4.1 学习目标
- 理解FreeRTOS的本质:开源的实时操作系统,适合STM32等嵌入式系统
- 掌握FreeRTOS的核心组件:任务管理、内存管理、队列、信号量、互斥锁
- 掌握FreeRTOS的避坑指南:避免任务调度错误、避免内存泄漏、避免死锁
- 避开FreeRTOS使用的3大常见坑
4.2 FreeRTOS的核心组件
任务管理:
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f1xx_hal.h"
#define LED_GPIO_PORT GPIOA
#define LED_GPIO_PIN GPIO_PIN_5
void LED_Task(void *pvParameters) {
while (1) {
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PIN);
vTaskDelay(1000);
}
}
void UART_Task(void *pvParameters) {
while (1) {
HAL_UART_Transmit(&huart1, (uint8_t *)"Hello, FreeRTOS!", 16, HAL_MAX_DELAY);
vTaskDelay(2000);
}
}
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init();
UART_Init();
xTaskCreate(LED_Task, "LED_Task", 128, NULL, 1, NULL);
xTaskCreate(UART_Task, "UART_Task", 128, NULL, 1, NULL);
vTaskStartScheduler();
return 0;
}
队列管理:
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "stm32f1xx_hal.h"
QueueHandle_t xQueue;
void Sender_Task(void *pvParameters) {
int value = 0;
while (1) {
xQueueSend(xQueue, &value, portMAX_DELAY);
value++;
vTaskDelay(1000);
}
}
void Receiver_Task(void *pvParameters) {
int value;
while (1) {
xQueueReceive(xQueue, &value, portMAX_DELAY);
char buffer[100];
sprintf(buffer, "Received: %d\r\n", value);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
}
}
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init();
UART_Init();
xQueue = xQueueCreate(10, sizeof(int));
xTaskCreate(Sender_Task, "Sender_Task", 128, NULL, 1, NULL);
xTaskCreate(Receiver_Task, "Receiver_Task", 128, NULL, 1, NULL);
vTaskStartScheduler();
return 0;
}
五、模块4:实战案例分析——STM32温湿度监测系统
5.1 学习目标
- 掌握STM32温湿度监测系统:使用STM32、DHT11温湿度传感器、OLED显示屏实现温湿度监测
- 学会使用STM32的外设驱动和FreeRTOS进行实时数据处理
- 避开实战案例使用的3大常见坑
5.2 STM32温湿度监测系统
硬件连接:
- STM32F103C8T6开发板
- DHT11温湿度传感器:连接到PA0引脚
- OLED显示屏:通过I2C接口连接到PB6(SCL)和PB7(SDA)
代码示例4:DHT11传感器驱动
#include "dht11.h"
#include "stm32f1xx_hal.h"
#define DHT11_GPIO_PORT GPIOA
#define DHT11_GPIO_PIN GPIO_PIN_0
void DHT11_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET);
}
uint8_t DHT11_ReadByte(void) {
uint8_t data = 0;
for (int i = 0; i < 8; i++) {
while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_RESET);
HAL_Delay(30);
if (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_SET) {
data |= (1 << (7 - i));
}
while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_SET);
}
return data;
}
uint8_t DHT11_ReadData(uint8_t *temperature, uint8_t *humidity) {
uint8_t buffer[5];
HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET);
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_SET);
while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_RESET);
while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_SET);
for (int i = 0; i < 5; i++) {
buffer[i] = DHT11_ReadByte();
}
if (buffer[4] == buffer[0] + buffer[1] + buffer[2] + buffer[3]) {
*humidity = buffer[0];
*temperature = buffer[2];
return 0;
} else {
return 1;
}
}
代码示例5:OLED显示屏驱动
#include "oled.h"
#include "stm32f1xx_hal.h"
#define OLED_I2C_PORT hi2c1
#define OLED_I2C_ADDRESS 0x3C
void OLED_WriteCommand(uint8_t cmd) {
uint8_t buffer[2] = {0x00, cmd};
HAL_I2C_Master_Transmit(&OLED_I2C_PORT, OLED_I2C_ADDRESS, buffer, 2, HAL_MAX_DELAY);
}
void OLED_WriteData(uint8_t data) {
uint8_t buffer[2] = {0x40, data};
HAL_I2C_Master_Transmit(&OLED_I2C_PORT, OLED_I2C_ADDRESS, buffer, 2, HAL_MAX_DELAY);
}
void OLED_Init(void) {
HAL_Delay(100);
OLED_WriteCommand(0xAE); // 关闭显示
OLED_WriteCommand(0x00); // 设置低列地址
OLED_WriteCommand(0x10); // 设置高列地址
OLED_WriteCommand(0x40); // 设置起始行地址
OLED_WriteCommand(0xB0); // 设置页地址
OLED_WriteCommand(0x81); // 对比度设置
OLED_WriteCommand(0xFF); // 对比度值
OLED_WriteCommand(0xA1); // 设置段重定向
OLED_WriteCommand(0xA6); // 正常显示
OLED_WriteCommand(0xA8); // 设置多路复用
OLED_WriteCommand(0x3F); // 64行
OLED_WriteCommand(0xC8); // 设置扫描方向
OLED_WriteCommand(0xD3); // 设置显示偏移
OLED_WriteCommand(0x00); // 偏移值
OLED_WriteCommand(0xD5); // 设置时钟分频
OLED_WriteCommand(0x80); // 时钟分频值
OLED_WriteCommand(0xD9); // 设置预充电周期
OLED_WriteCommand(0xF1); // 预充电周期
OLED_WriteCommand(0xDA); // 设置COM引脚配置
OLED_WriteCommand(0x12); // COM引脚配置
OLED_WriteCommand(0xDB); // 设置VCOMH电压
OLED_WriteCommand(0x40); // VCOMH电压
OLED_WriteCommand(0x8D); // 电荷泵配置
OLED_WriteCommand(0x14); // 电荷泵开启
OLED_WriteCommand(0xAF); // 开启显示
}
void OLED_Clear(void) {
for (int i = 0; i < 8; i++) {
OLED_WriteCommand(0xB0 + i);
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x10);
for (int j = 0; j < 128; j++) {
OLED_WriteData(0x00);
}
}
}
void OLED_SetCursor(uint8_t x, uint8_t y) {
OLED_WriteCommand(0xB0 + y);
OLED_WriteCommand((x & 0x0F));
OLED_WriteCommand(((x & 0xF0) >> 4) | 0x10);
}
void OLED_WriteChar(uint8_t x, uint8_t y, char c) {
OLED_SetCursor(x, y);
for (int i = 0; i < 8; i++) {
OLED_WriteData(font8x8[c - ' '][i]);
}
}
void OLED_WriteString(uint8_t x, uint8_t y, const char *str) {
int i = 0;
while (str[i] != '\0') {
OLED_WriteChar(x + i * 8, y, str[i]);
i++;
}
}
代码示例6:主程序
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "stm32f1xx_hal.h"
#include "dht11.h"
#include "oled.h"
QueueHandle_t xQueue;
void DHT11_Task(void *pvParameters) {
uint8_t temperature, humidity;
while (1) {
if (DHT11_ReadData(&temperature, &humidity) == 0) {
int data = (temperature << 8) | humidity;
xQueueSend(xQueue, &data, portMAX_DELAY);
}
vTaskDelay(2000);
}
}
void OLED_Task(void *pvParameters) {
int data;
while (1) {
if (xQueueReceive(xQueue, &data, portMAX_DELAY) == pdPASS) {
uint8_t temperature = (data >> 8) & 0xFF;
uint8_t humidity = data & 0xFF;
char buffer[50];
sprintf(buffer, "Temp: %d°C", temperature);
OLED_WriteString(0, 0, buffer);
sprintf(buffer, "Hum: %d%%", humidity);
OLED_WriteString(0, 1, buffer);
}
vTaskDelay(1000);
}
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
DHT11_Init();
OLED_Init();
OLED_Clear();
xQueue = xQueueCreate(10, sizeof(int));
xTaskCreate(DHT11_Task, "DHT11_Task", 128, NULL, 1, NULL);
xTaskCreate(OLED_Task, "OLED_Task", 128, NULL, 1, NULL);
vTaskStartScheduler();
return 0;
}
六、本章总结与课后练习
6.1 总结
✅ STM32微控制器:基于ARM Cortex-M内核的32位微控制器,资源丰富、性能强大
✅ 外设驱动:控制STM32外设的软件,负责管理外设资源
✅ 实时操作系统(FreeRTOS):开源的实时操作系统,适合嵌入式系统
✅ 实战案例分析:STM32温湿度监测系统,使用DHT11传感器和OLED显示屏
6.2 课后练习
- 编写程序:使用STM32控制LED闪烁
- 编写程序:使用STM32的UART通信
- 编写程序:使用STM32的ADC读取电压值
- 编写程序:使用STM32的SPI通信
- 编写程序:使用STM32的I2C通信
- 编写程序:使用STM32的PWM控制LED亮度
- 编写程序:使用STM32的定时器实现定时中断
- 编写程序:使用STM32的中断系统实现外部中断
- 编写程序:使用STM32的DMA传输数据
- 编写程序:使用STM32的Flash存储数据
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/COLLINSXU/article/details/157650547



