尚硅谷51单片机入门到高手,最新全套51单片机教程!

92 阅读6分钟

嵌入式系统开发实战:从基础到高级应用

本文将全面介绍嵌入式系统开发的核心技术和实践方法,涵盖硬件接口编程、实时操作系统、低功耗设计和物联网应用等关键领域,通过大量可落地的代码示例帮助开发者掌握嵌入式开发的完整技能栈。

一、嵌入式开发基础与环境搭建

1. 开发环境配置

# 安装ARM交叉编译工具链 (Linux)
sudo apt-get install gcc-arm-none-eabi
sudo apt-get install gdb-arm-none-eabi

# 验证安装
arm-none-eabi-gcc --version

2. 简单的LED闪烁程序 (STM32 HAL)

#include "stm32f4xx_hal.h"

void SystemClock_Config(void);
static void MX_GPIO_Init(void);

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();

  while (1) {
    HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);  // 切换LED状态
    HAL_Delay(500);  // 延时500ms
  }
}

void SystemClock_Config(void) {
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  // 配置主PLL为168MHz
  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.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  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_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
}

static void MX_GPIO_Init(void) {
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOD_CLK_ENABLE();
  
  // 配置PD12为输出
  GPIO_InitStruct.Pin = GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}

二、硬件接口编程

1. UART通信实现

#include "stm32f4xx_hal.h"

UART_HandleTypeDef huart2;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART2_UART_Init();

  char message[] = "Hello UART!\r\n";
  
  while (1) {
    HAL_UART_Transmit(&huart2, (uint8_t*)message, strlen(message), HAL_MAX_DELAY);
    HAL_Delay(1000);
  }
}

static void MX_USART2_UART_Init(void) {
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  HAL_UART_Init(&huart2);
}

// 重定向printf到UART
int __io_putchar(int ch) {
  HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, HAL_MAX_DELAY);
  return ch;
}

2. I2C传感器读取 (BME280温湿度传感器)

#include "stm32f4xx_hal.h"
#include "bme280.h"

I2C_HandleTypeDef hi2c1;
struct bme280_dev bme;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_I2C1_Init();

  // 初始化BME280
  bme.dev_id = BME280_I2C_ADDR_PRIM;
  bme.intf = BME280_I2C_INTF;
  bme.read = user_i2c_read;
  bme.write = user_i2c_write;
  bme.delay_ms = HAL_Delay;
  
  bme280_init(&bme);

  // 配置传感器
  struct bme280_settings settings;
  settings.filter = BME280_FILTER_COEFF_16;
  settings.osr_h = BME280_OVERSAMPLING_1X;
  settings.osr_p = BME280_OVERSAMPLING_16X;
  settings.osr_t = BME280_OVERSAMPLING_2X;
  settings.standby_time = BME280_STANDBY_TIME_62_5_MS;
  bme280_set_sensor_settings(BME280_SEL_ALL_SETTINGS, &settings, &bme);
  
  bme280_set_sensor_mode(BME280_FORCED_MODE, &bme);

  while (1) {
    struct bme280_data comp_data;
    bme280_get_sensor_data(BME280_ALL, &comp_data, &bme);
    
    printf("温度: %.2f C, 湿度: %.2f %%, 气压: %.2f hPa\r\n",
           comp_data.temperature, 
           comp_data.humidity,
           comp_data.pressure/100.0);
    
    HAL_Delay(2000);
  }
}

// I2C读写函数
int8_t user_i2c_read(uint8_t reg_addr, uint8_t *data, uint32_t len, void *intf_ptr) {
  HAL_I2C_Mem_Read(&hi2c1, bme.dev_id<<1, reg_addr, 1, data, len, HAL_MAX_DELAY);
  return 0;
}

int8_t user_i2c_write(uint8_t reg_addr, const uint8_t *data, uint32_t len, void *intf_ptr) {
  HAL_I2C_Mem_Write(&hi2c1, bme.dev_id<<1, reg_addr, 1, (uint8_t*)data, len, HAL_MAX_DELAY);
  return 0;
}

三、实时操作系统(RTOS)应用

1. FreeRTOS多任务创建

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

TaskHandle_t xTask1Handle = NULL;
TaskHandle_t xTask2Handle = NULL;
QueueHandle_t xQueue = NULL;

void vTask1(void *pvParameters) {
  uint32_t count = 0;
  while (1) {
    printf("任务1运行中,计数值: %lu\r\n", count++);
    xQueueSend(xQueue, &count, portMAX_DELAY);
    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

void vTask2(void *pvParameters) {
  uint32_t receivedValue;
  while (1) {
    if (xQueueReceive(xQueue, &receivedValue, portMAX_DELAY) == pdPASS) {
      printf("任务2接收到数据: %lu\r\n", receivedValue);
    }
  }
}

int main(void) {
  HAL_Init();
  SystemClock_Config();
  
  // 创建队列
  xQueue = xQueueCreate(5, sizeof(uint32_t));
  
  // 创建任务
  xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 2, &xTask1Handle);
  xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 1, &xTask2Handle);
  
  // 启动调度器
  vTaskStartScheduler();
  
  while (1);
}

2. RTOS下的互斥锁应用

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

SemaphoreHandle_t xMutex = NULL;
int sharedResource = 0;

void vTaskA(void *pvParameters) {
  while (1) {
    if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
      printf("任务A获取共享资源: %d\r\n", ++sharedResource);
      xSemaphoreGive(xMutex);
    }
    vTaskDelay(pdMS_TO_TICKS(500));
  }
}

void vTaskB(void *pvParameters) {
  while (1) {
    if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
      printf("任务B获取共享资源: %d\r\n", --sharedResource);
      xSemaphoreGive(xMutex);
    }
    vTaskDelay(pdMS_TO_TICKS(700));
  }
}

int main(void) {
  HAL_Init();
  SystemClock_Config();
  
  // 创建互斥锁
  xMutex = xSemaphoreCreateMutex();
  
  // 创建任务
  xTaskCreate(vTaskA, "TaskA", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
  xTaskCreate(vTaskB, "TaskB", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
  
  // 启动调度器
  vTaskStartScheduler();
  
  while (1);
}

四、低功耗设计

1. STM32低功耗模式实现

#include "stm32l4xx_hal.h"

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ICACHE_Init(void);

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_ICACHE_Init();
  
  // 配置唤醒引脚
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
  
  while (1) {
    printf("进入停止模式...\r\n");
    HAL_Delay(100);
    
    // 进入停止模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    
    // 唤醒后重新配置系统时钟
    SystemClock_Config();
    printf("从停止模式唤醒\r\n");
    HAL_Delay(2000);
  }
}

void EXTI15_10_IRQHandler(void) {
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
  if (GPIO_Pin == GPIO_PIN_13) {
    // 唤醒处理
  }
}

2. 动态频率调整

#include "stm32f4xx_hal.h"

void SystemClock_Config_LowPower(void) {
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  // 配置为低功耗模式 (24MHz)
  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.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 96;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  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_1);
}

int main(void) {
  HAL_Init();
  
  // 根据需求动态调整时钟频率
  if (needHighPerformance) {
    SystemClock_Config();  // 168MHz
  } else {
    SystemClock_Config_LowPower();  // 24MHz
  }
  
  while (1) {
    // 应用逻辑
  }
}

五、嵌入式Linux开发

1. 简单的字符设备驱动

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "simple_char"

static int major;
static char msg[100] = {0};

static int device_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "设备已打开\n");
    return 0;
}

static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) {
    int bytes_read = 0;
    if (*msg == 0) return 0;
    
    while (length && *msg) {
        put_user(*(msg++), buffer++);
        length--;
        bytes_read++;
    }
    return bytes_read;
}

static ssize_t device_write(struct file *filp, const char *buf, size_t len, loff_t *off) {
    memset(msg, 0, 100);
    if (copy_from_user(msg, buf, len))
        return -EFAULT;
    return len;
}

static struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
};

static int __init simple_char_init(void) {
    major = register_chrdev(0, DEVICE_NAME, &fops);
    if (major < 0) {
        printk(KERN_ALERT "注册字符设备失败\n");
        return major;
    }
    printk(KERN_INFO "注册字符设备成功,主设备号: %d\n", major);
    return 0;
}

static void __exit simple_char_exit(void) {
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "设备驱动已卸载\n");
}

module_init(simple_char_init);
module_exit(simple_char_exit);
MODULE_LICENSE("GPL");

2. 嵌入式Python应用 (RPi GPIO控制)

# Raspberry Pi GPIO控制示例
import RPi.GPIO as GPIO
import time

# 设置GPIO模式
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

# 定义引脚
LED_PIN = 17
BUTTON_PIN = 18

# 初始化引脚
GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def button_callback(channel):
    """按钮中断回调函数"""
    GPIO.output(LED_PIN, not GPIO.input(LED_PIN))
    print("按钮按下,切换LED状态")

# 添加中断检测
GPIO.add_event_detect(BUTTON_PIN, GPIO.FALLING, 
                     callback=button_callback, bouncetime=200)

try:
    print("程序运行中,按CTRL+C退出")
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    GPIO.cleanup()

六、物联网应用开发

1. MQTT客户端实现 (ESP32)

#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "your_SSID";
const char