HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 技术面试完全指南

    • 技术面试完全指南
    • 8年面试官告诉你:90%的简历在第一轮就被刷掉了
    • 刷了500道LeetCode,终于明白大厂算法面试到底考什么
    • 高频算法题精讲-双指针与滑动窗口
    • 03-高频算法题精讲-二分查找与排序
    • 04-高频算法题精讲-树与递归
    • 05-高频算法题精讲-图与拓扑排序
    • 06-高频算法题精讲-动态规划
    • Go面试必问:一道GMP问题,干掉90%的候选人
    • 08-数据库面试高频题
    • 09-分布式系统面试题
    • 10-Kubernetes与云原生面试题
    • 11-系统设计面试方法论
    • 前端面试高频题
    • AI 与机器学习面试题
    • 行为面试与软技能

实战项目

智能家居控制器

项目需求

实现一个基于STM32的智能家居控制节点:

  • DHT11采集温湿度
  • 继电器控制灯光/风扇
  • OLED显示实时数据
  • ESP8266 WiFi上报到云平台
  • 按键本地控制
  • 超温/湿报警

系统架构

硬件层:
  STM32F103C8T6 (主控)
  DHT11 (PA0)
  OLED (SPI: PA5-SCK, PA7-MOSI)
  继电器×2 (PB0, PB1)
  ESP8266 (UART2: PA2-TX, PA3-RX)
  按键×3 (PC13, PC14, PC15)

软件层:
  FreeRTOS
    ├─ sensor_task (优先级3): 1s读取温湿度
    ├─ display_task (优先级2): 100ms刷新OLED
    ├─ wifi_task (优先级2): 5s上报数据
    ├─ key_task (优先级4): 10ms扫描按键
    └─ ctrl_task (优先级3): 控制继电器

核心代码实现

// 全局变量和队列
QueueHandle_t sensor_queue;
SemaphoreHandle_t relay_mutex;

typedef struct {
    float temperature;
    float humidity;
    uint32_t timestamp;
} SensorData_t;

// 传感器任务
void sensor_task(void *pvParameters) {
    SensorData_t data;
    uint8_t temp, humi;

    while (1) {
        if (DHT11_Read(&temp, &humi)) {
            data.temperature = temp;
            data.humidity = humi;
            data.timestamp = HAL_GetTick();

            // 发送到队列
            xQueueSend(sensor_queue, &data, pdMS_TO_TICKS(100));

            // 检查报警条件
            if (temp > 35) {
                // 打开风扇
                relay_control(RELAY_FAN, ON);
            } else if (temp < 30) {
                relay_control(RELAY_FAN, OFF);
            }
        }

        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 显示任务
void display_task(void *pvParameters) {
    SensorData_t data;
    char buf[32];

    OLED_Init();
    OLED_Clear();

    while (1) {
        if (xQueuePeek(sensor_queue, &data, 0) == pdTRUE) {
            OLED_SetCursor(0, 0);
            sprintf(buf, "Temp: %.1f C", data.temperature);
            OLED_ShowString(0, 0, buf);

            sprintf(buf, "Humi: %.1f %%", data.humidity);
            OLED_ShowString(0, 2, buf);

            // 显示继电器状态
            sprintf(buf, "Light: %s", relay_state[RELAY_LIGHT] ? "ON" : "OFF");
            OLED_ShowString(0, 4, buf);

            sprintf(buf, "Fan:   %s", relay_state[RELAY_FAN] ? "ON" : "OFF");
            OLED_ShowString(0, 6, buf);
        }

        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// WiFi上报任务
void wifi_task(void *pvParameters) {
    SensorData_t data;
    char json[128];

    ESP8266_Init();
    ESP8266_ConnectAP("SSID", "PASSWORD");

    while (1) {
        if (xQueuePeek(sensor_queue, &data, 0) == pdTRUE) {
            // 构造JSON
            sprintf(json, "{\"temp\":%.1f,\"humi\":%.1f,\"time\":%lu}",
                    data.temperature, data.humidity, data.timestamp);

            // HTTP POST到云平台
            if (ESP8266_HTTPPost("http://api.iot.com/data", json) == 0) {
                printf("Upload success\n");
            }
        }

        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

// 按键任务
void key_task(void *pvParameters) {
    static uint8_t key_state[3] = {0};

    while (1) {
        // KEY1: 切换灯光
        if (key_scan(KEY1) && !key_state[0]) {
            relay_toggle(RELAY_LIGHT);
            key_state[0] = 1;
        } else if (!key_scan(KEY1)) {
            key_state[0] = 0;
        }

        // KEY2: 切换风扇
        if (key_scan(KEY2) && !key_state[1]) {
            relay_toggle(RELAY_FAN);
            key_state[1] = 1;
        } else if (!key_scan(KEY2)) {
            key_state[1] = 0;
        }

        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

// 继电器控制
uint8_t relay_state[2] = {0, 0};

void relay_control(uint8_t relay, uint8_t state) {
    xSemaphoreTake(relay_mutex, portMAX_DELAY);

    relay_state[relay] = state;

    if (relay == RELAY_LIGHT) {
        HAL_GPIO_WritePin(RELAY1_GPIO, RELAY1_PIN, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
    } else if (relay == RELAY_FAN) {
        HAL_GPIO_WritePin(RELAY2_GPIO, RELAY2_PIN, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
    }

    xSemaphoreGive(relay_mutex);
}

// 主函数
int main(void) {
    HAL_Init();
    SystemClock_Config();

    // 初始化外设
    GPIO_Init();
    SPI_Init();
    UART_Init();

    // 创建队列和信号量
    sensor_queue = xQueueCreate(5, sizeof(SensorData_t));
    relay_mutex = xSemaphoreCreateMutex();

    // 创建任务
    xTaskCreate(sensor_task, "Sensor", 256, NULL, 3, NULL);
    xTaskCreate(display_task, "Display", 256, NULL, 2, NULL);
    xTaskCreate(wifi_task, "WiFi", 512, NULL, 2, NULL);
    xTaskCreate(key_task, "Key", 128, NULL, 4, NULL);

    // 启动调度器
    vTaskStartScheduler();

    while (1);
}

ESP8266驱动

// AT命令封装
uint8_t ESP8266_SendCmd(const char *cmd, const char *expect, uint32_t timeout) {
    char rx_buf[256];
    uint16_t rx_len = 0;

    // 发送命令
    HAL_UART_Transmit(&huart2, (uint8_t*)cmd, strlen(cmd), 100);
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", 2, 100);

    // 接收响应
    uint32_t start = HAL_GetTick();
    while (HAL_GetTick() - start < timeout) {
        if (HAL_UART_Receive(&huart2, (uint8_t*)&rx_buf[rx_len], 1, 10) == HAL_OK) {
            rx_len++;
            if (rx_len >= sizeof(rx_buf) - 1) break;
        }
    }
    rx_buf[rx_len] = '\0';

    // 检查期望响应
    return (strstr(rx_buf, expect) != NULL);
}

void ESP8266_Init(void) {
    HAL_Delay(2000);  // 等待模块启动

    ESP8266_SendCmd("AT", "OK", 1000);
    ESP8266_SendCmd("AT+CWMODE=1", "OK", 1000);  // Station模式
}

uint8_t ESP8266_ConnectAP(const char *ssid, const char *pwd) {
    char cmd[128];
    sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"", ssid, pwd);
    return ESP8266_SendCmd(cmd, "OK", 10000);
}

uint8_t ESP8266_HTTPPost(const char *url, const char *data) {
    char cmd[256];

    // 建立TCP连接
    sprintf(cmd, "AT+CIPSTART=\"TCP\",\"api.iot.com\",80");
    if (!ESP8266_SendCmd(cmd, "OK", 5000)) {
        return 1;
    }

    // 构造HTTP请求
    char http_req[512];
    sprintf(http_req,
            "POST /data HTTP/1.1\r\n"
            "Host: api.iot.com\r\n"
            "Content-Type: application/json\r\n"
            "Content-Length: %d\r\n"
            "\r\n"
            "%s", strlen(data), data);

    // 发送数据
    sprintf(cmd, "AT+CIPSEND=%d", strlen(http_req));
    ESP8266_SendCmd(cmd, ">", 2000);
    HAL_UART_Transmit(&huart2, (uint8_t*)http_req, strlen(http_req), 1000);

    // 关闭连接
    ESP8266_SendCmd("AT+CIPCLOSE", "OK", 1000);

    return 0;
}

传感器数据采集系统

项目需求

多通道传感器数据采集并存储:

  • ADC采集4路模拟信号(0-3.3V)
  • I2C读取BMP280气压传感器
  • SPI读取加速度计MPU6050
  • 数据存储到SD卡(FAT32文件系统)
  • 实时数据滤波

ADC多通道采集

// DMA方式连续采集
#define ADC_CHANNELS  4
uint16_t adc_buffer[ADC_CHANNELS];

void ADC_MultiChannel_Init(void) {
    // 配置ADC为扫描模式
    hadc1.Instance = ADC1;
    hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
    hadc1.Init.ContinuousConvMode = ENABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = ADC_CHANNELS;
    HAL_ADC_Init(&hadc1);

    // 配置通道
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;

    for (int i = 0; i < ADC_CHANNELS; i++) {
        sConfig.Channel = ADC_CHANNEL_0 + i;
        sConfig.Rank = ADC_REGULAR_RANK_1 + i;
        HAL_ADC_ConfigChannel(&hadc1, &sConfig);
    }

    // 启动DMA
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_CHANNELS);
}

// 卡尔曼滤波
typedef struct {
    float Q;  // 过程噪声协方差
    float R;  // 测量噪声协方差
    float P;  // 估计误差协方差
    float K;  // 卡尔曼增益
    float X;  // 状态估计
} KalmanFilter_t;

void Kalman_Init(KalmanFilter_t *kf, float Q, float R) {
    kf->Q = Q;
    kf->R = R;
    kf->P = 1.0f;
    kf->X = 0.0f;
}

float Kalman_Update(KalmanFilter_t *kf, float measurement) {
    // 预测
    kf->P = kf->P + kf->Q;

    // 更新
    kf->K = kf->P / (kf->P + kf->R);
    kf->X = kf->X + kf->K * (measurement - kf->X);
    kf->P = (1 - kf->K) * kf->P;

    return kf->X;
}

// 采集任务
void adc_task(void *pvParameters) {
    KalmanFilter_t kf[ADC_CHANNELS];
    for (int i = 0; i < ADC_CHANNELS; i++) {
        Kalman_Init(&kf[i], 0.02, 0.5);
    }

    while (1) {
        for (int i = 0; i < ADC_CHANNELS; i++) {
            float voltage = adc_buffer[i] * 3.3f / 4095.0f;
            float filtered = Kalman_Update(&kf[i], voltage);
            printf("CH%d: %.3fV (filtered: %.3fV)\n", i, voltage, filtered);
        }

        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

SD卡数据存储

#include "fatfs.h"

FATFS fs;
FIL file;
char filename[32];

void SDCard_Init(void) {
    FRESULT res;

    // 挂载文件系统
    res = f_mount(&fs, "", 1);
    if (res != FR_OK) {
        printf("Mount failed: %d\n", res);
        return;
    }

    // 创建日志文件
    uint32_t timestamp = RTC_GetTimestamp();
    sprintf(filename, "log_%lu.csv", timestamp);

    res = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
    if (res == FR_OK) {
        f_printf(&file, "Time,Temp,Pressure,AccX,AccY,AccZ\n");
        f_close(&file);
    }
}

void SDCard_SaveData(SensorData_t *data) {
    FRESULT res;

    res = f_open(&file, filename, FA_OPEN_APPEND | FA_WRITE);
    if (res == FR_OK) {
        f_printf(&file, "%lu,%.2f,%.2f,%d,%d,%d\n",
                 data->timestamp,
                 data->temperature,
                 data->pressure,
                 data->acc_x, data->acc_y, data->acc_z);
        f_close(&file);
    }
}

// 数据记录任务
void log_task(void *pvParameters) {
    SensorData_t data;

    SDCard_Init();

    while (1) {
        // 从队列获取数据
        if (xQueueReceive(sensor_queue, &data, portMAX_DELAY) == pdTRUE) {
            SDCard_SaveData(&data);
        }
    }
}

电机控制系统

步进电机细分驱动

// 使用TB6600驱动器
#define STEP_PIN   GPIO_PIN_0
#define DIR_PIN    GPIO_PIN_1
#define ENABLE_PIN GPIO_PIN_2

// 步进电机参数
#define STEPS_PER_REV  200   // 1.8度/步
#define MICROSTEPS     16    // 16细分
#define TOTAL_STEPS    (STEPS_PER_REV * MICROSTEPS)  // 3200步/圈

typedef struct {
    int32_t current_pos;   // 当前位置(步数)
    int32_t target_pos;    // 目标位置
    uint16_t speed;        // 速度(步/秒)
    uint8_t running;       // 运行标志
} StepMotor_t;

StepMotor_t motor;

void StepMotor_Init(void) {
    HAL_GPIO_WritePin(GPIOA, ENABLE_PIN, GPIO_PIN_RESET);  // 使能驱动器

    // 配置定时器产生步进脉冲
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 72 - 1;  // 1MHz
    htim2.Init.Period = 1000;       // 1kHz (1000步/秒)
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    HAL_TIM_Base_Init(&htim2);

    // 配置PWM输出
    TIM_OC_InitTypeDef sConfigOC = {0};
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 500;  // 50%占空比
    HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
}

// 移动到指定位置
void StepMotor_MoveTo(int32_t pos) {
    motor.target_pos = pos;

    // 设置方向
    if (pos > motor.current_pos) {
        HAL_GPIO_WritePin(GPIOA, DIR_PIN, GPIO_PIN_SET);  // 正向
    } else {
        HAL_GPIO_WritePin(GPIOA, DIR_PIN, GPIO_PIN_RESET);  // 反向
    }

    motor.running = 1;
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
}

// 定时器中断(每步触发)
void TIM2_IRQHandler(void) {
    if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) {
        __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);

        if (motor.running) {
            if (motor.current_pos < motor.target_pos) {
                motor.current_pos++;
            } else if (motor.current_pos > motor.target_pos) {
                motor.current_pos--;
            } else {
                motor.running = 0;
                HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
            }
        }
    }
}

// 梯形速度曲线(加减速控制)
void StepMotor_MoveSmooth(int32_t pos) {
    int32_t distance = abs(pos - motor.current_pos);
    int32_t accel_steps = distance / 3;  // 加速阶段占1/3
    int32_t decel_steps = distance / 3;  // 减速阶段占1/3
    int32_t const_steps = distance - accel_steps - decel_steps;

    uint16_t min_speed = 200;   // 起始速度
    uint16_t max_speed = 2000;  // 最大速度

    // 加速阶段
    for (int32_t i = 0; i < accel_steps; i++) {
        uint16_t speed = min_speed + (max_speed - min_speed) * i / accel_steps;
        set_motor_speed(speed);
        step_once();
    }

    // 匀速阶段
    set_motor_speed(max_speed);
    for (int32_t i = 0; i < const_steps; i++) {
        step_once();
    }

    // 减速阶段
    for (int32_t i = 0; i < decel_steps; i++) {
        uint16_t speed = max_speed - (max_speed - min_speed) * i / decel_steps;
        set_motor_speed(speed);
        step_once();
    }
}

直流电机PID调速

// PID控制器
typedef struct {
    float Kp, Ki, Kd;
    float setpoint;
    float integral;
    float prev_error;
    float output_min, output_max;
} PID_t;

void PID_Init(PID_t *pid, float Kp, float Ki, float Kd) {
    pid->Kp = Kp;
    pid->Ki = Ki;
    pid->Kd = Kd;
    pid->integral = 0;
    pid->prev_error = 0;
    pid->output_min = 0;
    pid->output_max = 100;
}

float PID_Compute(PID_t *pid, float input, float dt) {
    float error = pid->setpoint - input;

    // 比例项
    float P = pid->Kp * error;

    // 积分项
    pid->integral += error * dt;
    // 积分限幅
    if (pid->integral > 100) pid->integral = 100;
    if (pid->integral < -100) pid->integral = -100;
    float I = pid->Ki * pid->integral;

    // 微分项
    float D = pid->Kd * (error - pid->prev_error) / dt;
    pid->prev_error = error;

    // 输出
    float output = P + I + D;

    // 输出限幅
    if (output > pid->output_max) output = pid->output_max;
    if (output < pid->output_min) output = pid->output_min;

    return output;
}

// 电机控制任务
void motor_control_task(void *pvParameters) {
    PID_t pid;
    PID_Init(&pid, 2.0, 0.5, 0.1);  // 调节参数
    pid.setpoint = 1000;  // 目标转速:1000 RPM

    uint32_t last_time = HAL_GetTick();

    while (1) {
        // 读取编码器获取当前转速
        float current_speed = read_encoder_speed();

        // 计算时间间隔
        uint32_t now = HAL_GetTick();
        float dt = (now - last_time) / 1000.0f;
        last_time = now;

        // PID计算
        float pwm_duty = PID_Compute(&pid, current_speed, dt);

        // 设置PWM占空比
        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (uint16_t)pwm_duty * 10);

        printf("Speed: %.1f RPM, PWM: %.1f%%\n", current_speed, pwm_duty);

        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

无线通信

蓝牙BLE通信

// 使用HC-08蓝牙模块(串口透传)
void BLE_Init(void) {
    // 进入AT模式配置
    HAL_Delay(100);
    BLE_SendCmd("AT+NAME=SmartDevice");
    BLE_SendCmd("AT+BAUD=9600");
    BLE_SendCmd("AT+ROLE=0");  // 从机模式
}

void BLE_SendData(const uint8_t *data, uint16_t len) {
    HAL_UART_Transmit(&huart2, data, len, 100);
}

// 自定义协议
#define CMD_READ_TEMP     0x01
#define CMD_CONTROL_LED   0x02
#define CMD_READ_STATUS   0x03

typedef struct {
    uint8_t header;   // 0xAA
    uint8_t cmd;
    uint8_t len;
    uint8_t data[32];
    uint8_t checksum;
} BLE_Packet_t;

void BLE_HandlePacket(BLE_Packet_t *pkt) {
    if (pkt->header != 0xAA) return;

    // 校验和
    uint8_t sum = 0;
    for (int i = 0; i < pkt->len; i++) {
        sum += pkt->data[i];
    }
    if (sum != pkt->checksum) return;

    // 处理命令
    switch (pkt->cmd) {
        case CMD_READ_TEMP: {
            BLE_Packet_t resp;
            resp.header = 0xAA;
            resp.cmd = CMD_READ_TEMP;
            resp.len = 4;
            float temp = read_temperature();
            memcpy(resp.data, &temp, 4);
            resp.checksum = calculate_checksum(resp.data, resp.len);
            BLE_SendData((uint8_t*)&resp, sizeof(resp));
            break;
        }

        case CMD_CONTROL_LED:
            HAL_GPIO_WritePin(LED_GPIO, LED_PIN, pkt->data[0] ? GPIO_PIN_SET : GPIO_PIN_RESET);
            break;

        default:
            break;
    }
}

LoRa远程通信

// SX1278 LoRa模块驱动
#define LORA_CS_PIN    GPIO_PIN_4
#define LORA_RST_PIN   GPIO_PIN_0
#define LORA_DIO0_PIN  GPIO_PIN_1

// 写寄存器
void LoRa_WriteReg(uint8_t addr, uint8_t data) {
    addr |= 0x80;  // 写标志
    CS_LOW();
    HAL_SPI_Transmit(&hspi1, &addr, 1, 100);
    HAL_SPI_Transmit(&hspi1, &data, 1, 100);
    CS_HIGH();
}

// 读寄存器
uint8_t LoRa_ReadReg(uint8_t addr) {
    uint8_t data;
    addr &= 0x7F;  // 读标志
    CS_LOW();
    HAL_SPI_Transmit(&hspi1, &addr, 1, 100);
    HAL_SPI_Receive(&hspi1, &data, 1, 100);
    CS_HIGH();
    return data;
}

// 初始化
void LoRa_Init(void) {
    // 复位模块
    HAL_GPIO_WritePin(GPIOA, LORA_RST_PIN, GPIO_PIN_RESET);
    HAL_Delay(10);
    HAL_GPIO_WritePin(GPIOA, LORA_RST_PIN, GPIO_PIN_SET);
    HAL_Delay(10);

    // 进入Sleep模式
    LoRa_WriteReg(0x01, 0x00);

    // LoRa模式
    LoRa_WriteReg(0x01, 0x80);

    // 设置频率:433MHz
    // Freq = (FRF * 32MHz) / 2^19
    // FRF = 433MHz * 2^19 / 32MHz = 7094272
    LoRa_WriteReg(0x06, 0x6C);  // MSB
    LoRa_WriteReg(0x07, 0x40);
    LoRa_WriteReg(0x08, 0x00);  // LSB

    // 配置参数
    LoRa_WriteReg(0x1D, 0x72);  // BW=125kHz, CR=4/5, SF=7
    LoRa_WriteReg(0x1E, 0x74);  // SF=7, CRC on

    // 进入Standby模式
    LoRa_WriteReg(0x01, 0x81);
}

// 发送数据
void LoRa_Send(const uint8_t *data, uint8_t len) {
    // 设置FIFO地址
    LoRa_WriteReg(0x0E, 0x00);  // FIFO TX base
    LoRa_WriteReg(0x0D, 0x00);  // FIFO addr ptr

    // 写入数据长度
    LoRa_WriteReg(0x22, len);

    // 写入FIFO
    for (uint8_t i = 0; i < len; i++) {
        LoRa_WriteReg(0x00, data[i]);
    }

    // 进入TX模式
    LoRa_WriteReg(0x01, 0x83);

    // 等待发送完成(DIO0中断)
    while (!HAL_GPIO_ReadPin(GPIOA, LORA_DIO0_PIN));

    // 清除中断标志
    LoRa_WriteReg(0x12, 0xFF);
}

// 接收数据
uint8_t LoRa_Receive(uint8_t *data, uint8_t *len) {
    // 进入RX模式
    LoRa_WriteReg(0x01, 0x85);

    // 等待接收完成(带超时)
    uint32_t timeout = HAL_GetTick() + 5000;
    while (!HAL_GPIO_ReadPin(GPIOA, LORA_DIO0_PIN)) {
        if (HAL_GetTick() > timeout) {
            return 0;  // 超时
        }
    }

    // 读取数据长度
    *len = LoRa_ReadReg(0x13);

    // 读取FIFO地址
    uint8_t addr = LoRa_ReadReg(0x10);
    LoRa_WriteReg(0x0D, addr);

    // 读取数据
    for (uint8_t i = 0; i < *len; i++) {
        data[i] = LoRa_ReadReg(0x00);
    }

    // 清除中断标志
    LoRa_WriteReg(0x12, 0xFF);

    return 1;
}

硬件电路图

智能家居控制器电路:

[STM32F103C8T6]
  PA0  ────────────── DHT11 Data
  PA5  ──┬─────────── OLED SCK
  PA7  ──┼─────────── OLED MOSI
  PA6  ──┴─────────── OLED DC
  PB0  ───[1kΩ]────|> 继电器1 (IN)
  PB1  ───[1kΩ]────|> 继电器2 (IN)
  PA2  ────────────── ESP8266 RX
  PA3  ────────────── ESP8266 TX
  PC13 ───[10kΩ]─┬─── KEY1
                 └─── GND
  3.3V ────────────── VCC (所有模块)
  GND  ────────────── GND (所有模块)

继电器模块:
  VCC ────── 5V
  GND ────── GND
  IN  ────── STM32 PB0/PB1
  COM ────── 220V L
  NO  ────── 负载

高频面试题

1. 如何选择无线通信方式?

答案:

技术距离速率功耗成本应用场景
蓝牙BLE10-50m1Mbps低低可穿戴设备、智能家居
WiFi50-100m150Mbps高中视频传输、互联网接入
LoRa2-15km50kbps极低中农业物联网、智慧城市
NB-IoT全国250kbps低高水表、烟感、定位
Zigbee10-100m250kbps低中工业控制、智能照明

选择原则:

  • 数据量大、实时性高 → WiFi
  • 低功耗、短距离 → BLE
  • 超远距离、低功耗 → LoRa/NB-IoT
  • 组网、中等距离 → Zigbee

2. 如何实现可靠的数据存储?

答案:

多层保护机制:

  1. CRC校验: 检测数据损坏
  2. 双备份: 两份副本,交替写入
  3. 掉电保护: 写入前检查电压,关键时刻关闭外设
  4. 文件系统: 使用FAT32等成熟方案
  5. 定期校验: 后台任务验证已存储数据
typedef struct {
    uint32_t magic;
    uint32_t version;
    uint8_t data[252];
    uint32_t crc32;
} SafeRecord_t;

void save_record(const uint8_t *data) {
    SafeRecord_t record;
    record.magic = 0xDEADBEEF;
    record.version = 1;
    memcpy(record.data, data, 252);
    record.crc32 = calc_crc32((uint8_t*)&record, sizeof(record) - 4);

    // 写入主副本
    flash_write(PRIMARY_ADDR, &record, sizeof(record));
    // 写入备份
    flash_write(BACKUP_ADDR, &record, sizeof(record));
}

3. PID参数如何整定?

答案:

经验法(Ziegler-Nichols):

  1. 设Ki=Kd=0,逐步增大Kp直到系统震荡
  2. 记录临界Kp值(Ku)和震荡周期Tu
  3. 计算: Kp=0.6Ku, Ki=1.2Ku/Tu, Kd=0.075Ku*Tu

手动调节:

  1. 先调Kp:响应快但震荡
  2. 加入Ki:消除稳态误差
  3. 加入Kd:减小超调

调节效果:

  • Kp过大:震荡
  • Ki过大:积分饱和
  • Kd过大:噪声敏感

4. 如何优化功耗?

答案:

系统级优化:

  1. 降低主频: 根据任务需求选择最低够用频率
  2. 睡眠模式: 空闲时进入Sleep/Stop
  3. 外设管理: 动态开关外设时钟
  4. 通信优化: 批量发送减少唤醒次数
  5. 硬件选型: 选择低功耗MCU和传感器

实测案例(电池供电温度计):

  • 原始: 运行72MHz,实时显示 → 50mA,续航3天
  • 优化后: 8MHz主频,1分钟唤醒一次,OLED仅按键时点亮 → 0.5mA,续航300天

5. 如何进行项目调试?

答案:

阶段化调试:

  1. 硬件测试: 万用表量电压,示波器看波形
  2. 模块调试: 单独测试每个传感器/通信模块
  3. 集成测试: 逐步增加模块,定位问题
  4. 压力测试: 长时间运行,边界条件测试

工具使用:

  • 逻辑分析仪: I2C/SPI时序分析
  • 串口助手: 日志输出
  • J-Link: 断点调试
  • printf: 关键位置打印状态

常见问题:

  • I2C无ACK → 检查地址、上拉电阻
  • 程序跑飞 → 检查栈溢出、看门狗
  • 数据错乱 → 检查临界区保护、中断优先级