实战项目
智能家居控制器
项目需求
实现一个基于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. 如何选择无线通信方式?
答案:
| 技术 | 距离 | 速率 | 功耗 | 成本 | 应用场景 |
|---|---|---|---|---|---|
| 蓝牙BLE | 10-50m | 1Mbps | 低 | 低 | 可穿戴设备、智能家居 |
| WiFi | 50-100m | 150Mbps | 高 | 中 | 视频传输、互联网接入 |
| LoRa | 2-15km | 50kbps | 极低 | 中 | 农业物联网、智慧城市 |
| NB-IoT | 全国 | 250kbps | 低 | 高 | 水表、烟感、定位 |
| Zigbee | 10-100m | 250kbps | 低 | 中 | 工业控制、智能照明 |
选择原则:
- 数据量大、实时性高 → WiFi
- 低功耗、短距离 → BLE
- 超远距离、低功耗 → LoRa/NB-IoT
- 组网、中等距离 → Zigbee
2. 如何实现可靠的数据存储?
答案:
多层保护机制:
- CRC校验: 检测数据损坏
- 双备份: 两份副本,交替写入
- 掉电保护: 写入前检查电压,关键时刻关闭外设
- 文件系统: 使用FAT32等成熟方案
- 定期校验: 后台任务验证已存储数据
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):
- 设Ki=Kd=0,逐步增大Kp直到系统震荡
- 记录临界Kp值(Ku)和震荡周期Tu
- 计算: Kp=0.6Ku, Ki=1.2Ku/Tu, Kd=0.075Ku*Tu
手动调节:
- 先调Kp:响应快但震荡
- 加入Ki:消除稳态误差
- 加入Kd:减小超调
调节效果:
- Kp过大:震荡
- Ki过大:积分饱和
- Kd过大:噪声敏感
4. 如何优化功耗?
答案:
系统级优化:
- 降低主频: 根据任务需求选择最低够用频率
- 睡眠模式: 空闲时进入Sleep/Stop
- 外设管理: 动态开关外设时钟
- 通信优化: 批量发送减少唤醒次数
- 硬件选型: 选择低功耗MCU和传感器
实测案例(电池供电温度计):
- 原始: 运行72MHz,实时显示 → 50mA,续航3天
- 优化后: 8MHz主频,1分钟唤醒一次,OLED仅按键时点亮 → 0.5mA,续航300天
5. 如何进行项目调试?
答案:
阶段化调试:
- 硬件测试: 万用表量电压,示波器看波形
- 模块调试: 单独测试每个传感器/通信模块
- 集成测试: 逐步增加模块,定位问题
- 压力测试: 长时间运行,边界条件测试
工具使用:
- 逻辑分析仪: I2C/SPI时序分析
- 串口助手: 日志输出
- J-Link: 断点调试
- printf: 关键位置打印状态
常见问题:
- I2C无ACK → 检查地址、上拉电阻
- 程序跑飞 → 检查栈溢出、看门狗
- 数据错乱 → 检查临界区保护、中断优先级