关注

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

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 课后练习

  1. 编写程序:使用STM32控制LED闪烁
  2. 编写程序:使用STM32的UART通信
  3. 编写程序:使用STM32的ADC读取电压值
  4. 编写程序:使用STM32的SPI通信
  5. 编写程序:使用STM32的I2C通信
  6. 编写程序:使用STM32的PWM控制LED亮度
  7. 编写程序:使用STM32的定时器实现定时中断
  8. 编写程序:使用STM32的中断系统实现外部中断
  9. 编写程序:使用STM32的DMA传输数据
  10. 编写程序:使用STM32的Flash存储数据

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/COLLINSXU/article/details/157650547

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--