STM32实战:基于STM32F103的智能宠物喂食器(定时+定量)

文章目录

一、前言

1.1 技术背景

现代都市生活节奏快,很多养宠物的上班族经常面临无法按时喂食的困扰。智能宠物喂食器应运而生,它能够按照预设的时间和份量自动投放食物,让宠物主人即使不在家也能确保爱宠按时进食。

STM32F103系列微控制器凭借其丰富的外设接口、低功耗特性和高性价比,成为物联网智能硬件开发的理想选择。本项目将展示如何使用STM32F103实现一个功能完善的智能宠物喂食系统。

1.2 应用场景

  • 上班族家庭:工作日自动定时喂食,周末手动控制
  • 短期出差:设置多日喂食计划,远程监控进食情况
  • 多宠物家庭:精确控制每只宠物的食量
  • 宠物店/寄养中心:批量管理多个喂食器

1.3 本文目标

通过本教程,你将学会:

  • STM32F103的RTC实时时钟配置与使用
  • 步进电机驱动与控制算法
  • 重量传感器的校准与数据采集
  • 红外传感器的物体检测
  • OLED显示屏的图形界面开发
  • 按键与旋转编码器的人机交互设计
  • 低功耗待机模式实现

技术栈:

  • 硬件平台:STM32F103C8T6
  • 电机驱动:28BYJ-48步进电机 + ULN2003驱动板
  • 称重模块:HX711 + 5kg压力传感器
  • 显示模块:0.96寸I2C OLED (SSD1306)
  • 通信模块:ESP8266 WiFi模块(可选)
  • 输入设备:旋转编码器 + 按键
  • 传感器:红外对射传感器(检测食物余量)
  • 开发工具:STM32CubeIDE

二、环境准备

2.1 硬件清单

器件名称 型号/规格 数量 说明
主控芯片 STM32F103C8T6 1 Blue Pill开发板
步进电机 28BYJ-48 1 5V减速步进电机
电机驱动 ULN2003 1 步进电机驱动板
称重传感器 HX711模块 1 24位AD转换
压力传感器 5kg称重传感器 1 检测食物重量
OLED显示屏 0.96寸 I2C 1 SSD1306驱动
旋转编码器 EC11 1 带按键功能
红外传感器 对射式 1 检测食物余量
WiFi模块 ESP8266-01S 1 可选,远程控制
电源 5V/2A 1 系统供电

2.2 硬件连接图

PA0-PA3
PB12/PB13
PB6/PB7
PA8/PA9/PA10
PA11
PA2/PA3
STM32F103C8T6
28BYJ-48步进电机
HX711称重模块
OLED显示屏
旋转编码器
红外传感器
ESP8266 WiFi

引脚连接表:

STM32引脚 连接设备 功能说明
PA0-PA3 ULN2003 步进电机驱动
PB12 HX711 DT 称重数据
PB13 HX711 SCK 称重时钟
PB6 OLED SCL I2C时钟
PB7 OLED SDA I2C数据
PA8 编码器A相 旋转检测
PA9 编码器B相 方向检测
PA10 编码器按键 确认/菜单
PA11 红外传感器 食物检测
PA2/PA3 ESP8266 WiFi通信

三、核心实现

3.1 STM32CubeMX配置

时钟配置:

  • HSE Crystal/Ceramic Resonator
  • System Clock: 72MHz
  • RTC时钟源:LSE Crystal (32.768kHz)

外设配置:

  • RTC:使能日历功能
  • TIM2:1ms定时器,用于步进电机控制
  • TIM3:编码器模式,用于旋转编码器
  • I2C1:PB6(SCL), PB7(SDA),OLED通信
  • USART2:PA2(TX), PA3(RX),WiFi通信
  • GPIO
    • PA0-PA3:输出,步进电机
    • PB12:输入,HX711 DT
    • PB13:输出,HX711 SCK
    • PA11:输入,红外传感器
    • PA10:输入,编码器按键(外部中断)

3.2 主程序实现

📄 创建文件:Core/Src/main.c

c 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Smart Pet Feeder System
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "rtc.h"
#include "tim.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"

/* USER CODE BEGIN Includes */
#include "oled_driver.h"
#include "hx711_driver.h"
#include "stepper_motor.h"
#include "encoder.h"
#include "menu_system.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* Private defines -----------------------------------------------------------*/
#define FEED_INTERVAL_MS     60000    // 喂食间隔检查(1分钟)
#define MOTOR_SPEED_DELAY    2        // 电机步进延时(ms)
#define FOOD_THRESHOLD       50       // 食物不足阈值(克)

/* Private variables ---------------------------------------------------------*/
// 喂食计划结构体
typedef struct {
    uint8_t hour;
    uint8_t minute;
    uint16_t amount;      // 喂食量(克)
    uint8_t enabled;      // 是否启用
} FeedSchedule;

// 系统状态
volatile FeedSchedule schedule[4];    // 最多4个喂食时段
volatile uint8_t schedule_count = 0;
volatile uint8_t feeding_in_progress = 0;
volatile float current_weight = 0.0;
volatile uint8_t food_low_warning = 0;

// 显示缓冲区
char display_buffer[32];

/* USER CODE BEGIN PV */
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void Check_Feeding_Schedule(void);
static void Process_Feeding(uint16_t target_amount);
static void Update_Display(void);
static void Enter_LowPower_Mode(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */

/**
  * @brief  主函数入口
  */
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    
    MX_GPIO_Init();
    MX_RTC_Init();
    MX_TIM2_Init();
    MX_TIM3_Init();
    MX_I2C1_Init();
    MX_USART2_UART_Init();
    
    /* USER CODE BEGIN 2 */
    printf("\\r\\n===== Smart Pet Feeder =====\\r\\n");
    
    // 初始化OLED
    OLED_Init();
    OLED_Clear();
    OLED_ShowString(0, 0, "Pet Feeder", 16);
    OLED_ShowString(0, 2, "Initializing...", 8);
    HAL_Delay(1000);
    
    // 初始化HX711
    HX711_Init();
    HX711_Calibrate();  // 校准零点
    
    // 初始化步进电机
    Stepper_Init();
    
    // 初始化编码器
    Encoder_Init();
    
    // 初始化菜单系统
    Menu_Init();
    
    // 加载保存的喂食计划(从Flash或EEPROM)
    Load_Schedule();
    
    printf("System ready\\r\\n");
    OLED_Clear();
    /* USER CODE END 2 */
    
    /* Infinite loop */
    while (1)
    {
        /* USER CODE BEGIN 3 */
        
        // 读取当前重量
        current_weight = HX711_GetWeight();
        
        // 检测食物余量
        food_low_warning = HAL_GPIO_ReadPin(IR_SENSOR_GPIO_Port, IR_SENSOR_Pin);
        
        // 检查喂食计划
        if (!feeding_in_progress)
        {
            Check_Feeding_Schedule();
        }
        
        // 处理用户输入(编码器+按键)
        Menu_ProcessInput();
        
        // 更新显示
        Update_Display();
        
        // 低功耗处理
        if (!feeding_in_progress && !Menu_IsActive())
        {
            Enter_LowPower_Mode();
        }
        
        HAL_Delay(100);
    }
    /* USER CODE END 3 */
}

/**
  * @brief  检查喂食计划
  */
static void Check_Feeding_Schedule(void)
{
    RTC_TimeTypeDef sTime;
    RTC_DateTypeDef sDate;
    static uint8_t last_minute = 255;
    
    // 获取当前时间
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
    
    // 避免同一分钟重复喂食
    if (sTime.Minutes == last_minute)
    {
        return;
    }
    last_minute = sTime.Minutes;
    
    // 检查所有喂食计划
    for (uint8_t i = 0; i < schedule_count; i++)
    {
        if (schedule[i].enabled &&
            schedule[i].hour == sTime.Hours &&
            schedule[i].minute == sTime.Minutes)
        {
            printf("Starting scheduled feeding #%d\\r\\n", i + 1);
            Process_Feeding(schedule[i].amount);
            break;
        }
    }
}

/**
  * @brief  执行喂食过程
  * @param  target_amount: 目标喂食量(克)
  */
static void Process_Feeding(uint16_t target_amount)
{
    feeding_in_progress = 1;
    float start_weight = HX711_GetWeight();
    float target_total = start_weight - target_amount;
    
    printf("Feeding started: target %d grams\\r\\n", target_amount);
    
    // 显示喂食状态
    OLED_Clear();
    OLED_ShowString(0, 0, "Feeding...", 16);
    sprintf(display_buffer, "Target: %dg", target_amount);
    OLED_ShowString(0, 2, display_buffer, 8);
    
    // 启动步进电机
    Stepper_SetDirection(CW);  // 顺时针旋转
    
    while (feeding_in_progress)
    {
        current_weight = HX711_GetWeight();
        float dispensed = start_weight - current_weight;
        
        // 更新显示
        sprintf(display_buffer, "Dispensed: %dg", (int)dispensed);
        OLED_ShowString(0, 4, display_buffer, 8);
        
        // 检查是否达到目标量
        if (current_weight <= target_total || dispensed >= target_amount)
        {
            break;
        }
        
        // 检查食物是否充足
        if (food_low_warning)
        {
            printf("Warning: Food running low!\\r\\n");
            OLED_ShowString(0, 6, "Low Food!", 8);
        }
        
        // 步进电机转动一步
        Stepper_Step();
        HAL_Delay(MOTOR_SPEED_DELAY);
    }
    
    // 停止电机
    Stepper_Stop();
    
    // 显示完成
    OLED_Clear();
    OLED_ShowString(0, 0, "Feeding Done!", 16);
    sprintf(display_buffer, "Total: %dg", (int)(start_weight - current_weight));
    OLED_ShowString(0, 2, display_buffer, 8);
    HAL_Delay(3000);
    
    feeding_in_progress = 0;
    printf("Feeding completed\\r\\n");
}

/**
  * @brief  更新OLED显示
  */
static void Update_Display(void)
{
    RTC_TimeTypeDef sTime;
    RTC_DateTypeDef sDate;
    
    // 如果菜单激活,由菜单系统控制显示
    if (Menu_IsActive())
    {
        return;
    }
    
    // 获取当前时间
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
    
    // 第一行:时间
    sprintf(display_buffer, "%02d:%02d:%02d", sTime.Hours, sTime.Minutes, sTime.Seconds);
    OLED_ShowString(0, 0, display_buffer, 16);
    
    // 第二行:食物重量
    sprintf(display_buffer, "Food: %.1fg", current_weight);
    OLED_ShowString(0, 2, display_buffer, 8);
    
    // 第三行:下次喂食时间
    uint8_t next_feed = 255;
    for (uint8_t i = 0; i < schedule_count; i++)
    {
        if (schedule[i].enabled)
        {
            if (schedule[i].hour > sTime.Hours ||
                (schedule[i].hour == sTime.Hours && schedule[i].minute > sTime.Minutes))
            {
                next_feed = i;
                break;
            }
        }
    }
    
    if (next_feed != 255)
    {
        sprintf(display_buffer, "Next: %02d:%02d", schedule[next_feed].hour, schedule[next_feed].minute);
    }
    else
    {
        sprintf(display_buffer, "Next: --:--");
    }
    OLED_ShowString(0, 4, display_buffer, 8);
    
    // 第四行:状态指示
    if (food_low_warning)
    {
        OLED_ShowString(0, 6, "LOW FOOD!", 8);
    }
    else
    {
        OLED_ShowString(0, 6, "Status: OK", 8);
    }
}

/**
  * @brief  进入低功耗模式
  */
static void Enter_LowPower_Mode(void)
{
    // 关闭OLED显示以节省电量
    OLED_DisplayOff();
    
    // 进入睡眠模式,等待中断唤醒
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
    
    // 唤醒后恢复显示
    OLED_DisplayOn();
}

/* USER CODE BEGIN 4 */

/**
  * @brief  编码器按键中断回调
  */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == ENCODER_BUTTON_Pin)
    {
        // 消抖
        HAL_Delay(20);
        if (HAL_GPIO_ReadPin(ENCODER_BUTTON_GPIO_Port, ENCODER_BUTTON_Pin) == GPIO_PIN_RESET)
        {
            Menu_ButtonPressed();
        }
    }
}

/* USER CODE END 4 */

/**
  * @brief System Clock Configuration
  */
void SystemClock_Config(void)
{
    // 系统时钟配置代码(由CubeMX生成)
    // ...
}

void Error_Handler(void)
{
    __disable_irq();
    while (1)
    {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
        HAL_Delay(100);
    }
}

3.3 HX711称重驱动

📄 创建文件:Core/Inc/hx711_driver.h

c 复制代码
#ifndef __HX711_DRIVER_H
#define __HX711_DRIVER_H

#include "main.h"

// HX711引脚定义
#define HX711_SCK_GPIO_Port   GPIOB
#define HX711_SCK_Pin         GPIO_PIN_13
#define HX711_DT_GPIO_Port    GPIOB
#define HX711_DT_Pin          GPIO_PIN_12

// 函数声明
void HX711_Init(void);
int32_t HX711_Read(void);
float HX711_GetWeight(void);
void HX711_Calibrate(void);
void HX711_SetScale(float scale);
void HX711_Tare(void);

#endif /* __HX711_DRIVER_H */

📄 创建文件:Core/Src/hx711_driver.c

c 复制代码
#include "hx711_driver.h"
#include "gpio.h"

// 校准参数
static float calibration_scale = 0.0035;  // 默认校准系数
static int32_t offset = 0;

/**
  * @brief  初始化HX711
  */
void HX711_Init(void)
{
    // SCK初始化为输出低电平
    HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET);
}

/**
  * @brief  读取HX711原始数据
  * @retval 24位ADC值
  */
int32_t HX711_Read(void)
{
    int32_t data = 0;
    
    // 等待DT变低(数据准备好)
    while (HAL_GPIO_ReadPin(HX711_DT_GPIO_Port, HX711_DT_Pin) == GPIO_PIN_SET);
    
    // 读取24位数据
    for (int8_t i = 0; i < 24; i++)
    {
        HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET);
        data = data << 1;
        HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET);
        
        if (HAL_GPIO_ReadPin(HX711_DT_GPIO_Port, HX711_DT_Pin) == GPIO_PIN_SET)
        {
            data++;
        }
    }
    
    // 第25个脉冲设置增益(128)
    HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET);
    
    // 转换为有符号数
    if (data & 0x800000)
    {
        data |= 0xFF000000;
    }
    
    return data;
}

/**
  * @brief  获取重量值(克)
  * @retval 重量值
  */
float HX711_GetWeight(void)
{
    int32_t raw = HX711_Read();
    return (float)(raw - offset) * calibration_scale;
}

/**
  * @brief  校准零点(去皮)
  */
void HX711_Calibrate(void)
{
    int32_t sum = 0;
    
    // 取10次平均值作为零点
    for (uint8_t i = 0; i < 10; i++)
    {
        sum += HX711_Read();
        HAL_Delay(10);
    }
    
    offset = sum / 10;
}

/**
  * @brief  设置校准系数
  * @param  scale: 校准系数(克/ADC值)
  */
void HX711_SetScale(float scale)
{
    calibration_scale = scale;
}

/**
  * @brief  去皮(当前重量设为0)
  */
void HX711_Tare(void)
{
    HX711_Calibrate();
}

3.4 步进电机驱动

📄 创建文件:Core/Inc/stepper_motor.h

c 复制代码
#ifndef __STEPPER_MOTOR_H
#define __STEPPER_MOTOR_H

#include "main.h"

// 旋转方向
#define CW    0   // 顺时针
#define CCW   1   // 逆时针

// 步进电机引脚
#define MOTOR_IN1_GPIO_Port   GPIOA
#define MOTOR_IN1_Pin         GPIO_PIN_0
#define MOTOR_IN2_GPIO_Port   GPIOA
#define MOTOR_IN2_Pin         GPIO_PIN_1
#define MOTOR_IN3_GPIO_Port   GPIOA
#define MOTOR_IN3_Pin         GPIO_PIN_2
#define MOTOR_IN4_GPIO_Port   GPIOA
#define MOTOR_IN4_Pin         GPIO_PIN_3

// 函数声明
void Stepper_Init(void);
void Stepper_SetDirection(uint8_t dir);
void Stepper_Step(void);
void Stepper_Stop(void);
void Stepper_RotateSteps(uint16_t steps, uint8_t dir);

#endif /* __STEPPER_MOTOR_H */

📄 创建文件:Core/Src/stepper_motor.c

c 复制代码
#include "stepper_motor.h"

// 28BYJ-48步进序列(4相8拍)
static const uint8_t step_sequence[8][4] = {
    {1, 0, 0, 0},
    {1, 1, 0, 0},
    {0, 1, 0, 0},
    {0, 1, 1, 0},
    {0, 0, 1, 0},
    {0, 0, 1, 1},
    {0, 0, 0, 1},
    {1, 0, 0, 1}
};

static uint8_t current_step = 0;
static uint8_t direction = CW;

/**
  * @brief  初始化步进电机
  */
void Stepper_Init(void)
{
    // 所有引脚初始化为低电平
    HAL_GPIO_WritePin(MOTOR_IN1_GPIO_Port, MOTOR_IN1_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN2_GPIO_Port, MOTOR_IN2_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN3_GPIO_Port, MOTOR_IN3_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN4_GPIO_Port, MOTOR_IN4_Pin, GPIO_PIN_RESET);
}

/**
  * @brief  设置旋转方向
  * @param  dir: CW(顺时针)或CCW(逆时针)
  */
void Stepper_SetDirection(uint8_t dir)
{
    direction = dir;
}

/**
  * @brief  执行一步
  */
void Stepper_Step(void)
{
    // 根据方向更新步数
    if (direction == CW)
    {
        current_step++;
        if (current_step >= 8) current_step = 0;
    }
    else
    {
        if (current_step == 0) current_step = 7;
        else current_step--;
    }
    
    // 设置引脚状态
    HAL_GPIO_WritePin(MOTOR_IN1_GPIO_Port, MOTOR_IN1_Pin, 
                      step_sequence[current_step][0] ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN2_GPIO_Port, MOTOR_IN2_Pin, 
                      step_sequence[current_step][1] ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN3_GPIO_Port, MOTOR_IN3_Pin, 
                      step_sequence[current_step][2] ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN4_GPIO_Port, MOTOR_IN4_Pin, 
                      step_sequence[current_step][3] ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

/**
  * @brief  停止电机(所有线圈断电)
  */
void Stepper_Stop(void)
{
    HAL_GPIO_WritePin(MOTOR_IN1_GPIO_Port, MOTOR_IN1_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN2_GPIO_Port, MOTOR_IN2_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN3_GPIO_Port, MOTOR_IN3_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOTOR_IN4_GPIO_Port, MOTOR_IN4_Pin, GPIO_PIN_RESET);
}

/**
  * @brief  旋转指定步数
  * @param  steps: 步数
  * @param  dir: 方向
  */
void Stepper_RotateSteps(uint16_t steps, uint8_t dir)
{
    Stepper_SetDirection(dir);
    for (uint16_t i = 0; i < steps; i++)
    {
        Stepper_Step();
        HAL_Delay(2);  // 2ms每步
    }
    Stepper_Stop();
}

四、测试验证

4.1 硬件测试

测试1:称重传感器校准

c 复制代码
void Test_Weight_Sensor(void)
{
    printf("Weight sensor test\\r\\n");
    
    // 零点校准
    HX711_Calibrate();
    printf("Zero calibrated\\r\\n");
    
    // 放置已知重量(如100g砝码)
    HAL_Delay(2000);
    
    // 读取并计算校准系数
    float weight = HX711_GetWeight();
    printf("Raw reading: %.2f\\r\\n", weight);
    
    // 校准系数 = 实际重量 / 读数
    float scale = 100.0 / weight;
    printf("Calibration scale: %.6f\\r\\n", scale);
}

测试2:步进电机测试

c 复制代码
void Test_Stepper_Motor(void)
{
    printf("Stepper motor test\\r\\n");
    
    // 顺时针旋转一圈(2048步)
    printf("Rotating CW...\\r\\n");
    Stepper_RotateSteps(2048, CW);
    HAL_Delay(1000);
    
    // 逆时针旋转一圈
    printf("Rotating CCW...\\r\\n");
    Stepper_RotateSteps(2048, CCW);
    
    printf("Motor test complete\\r\\n");
}

4.2 功能测试

测试3:喂食功能测试

  • 设置喂食量为50克
  • 验证实际出食量是否准确
  • 测试多次喂食的一致性

五、故障排查

5.1 常见问题

问题1:称重数据不稳定

原因分析:

  • 电源噪声干扰
  • 机械振动
  • 传感器安装不牢固

解决方案:

  • 使用独立稳压电源给HX711供电
  • 添加软件滤波算法
  • 加固传感器安装
c 复制代码
// 软件滤波:滑动平均
float GetFilteredWeight(void)
{
    static float buffer[10];
    static uint8_t index = 0;
    
    buffer[index] = HX711_GetWeight();
    index = (index + 1) % 10;
    
    float sum = 0;
    for (uint8_t i = 0; i < 10; i++)
    {
        sum += buffer[i];
    }
    return sum / 10.0;
}
问题2:电机堵转或异响

原因分析:

  • 食物卡住
  • 电机驱动电流不足
  • 步进序列错误

解决方案:

  • 添加堵转检测功能
  • 使用更大电流的驱动板
  • 检查步进序列配置

六、总结

6.1 核心知识点

  1. RTC实时时钟:实现精准的定时功能
  2. 步进电机控制:精确的定量投放控制
  3. 称重传感器:高精度的重量检测
  4. OLED图形界面:友好的用户交互
  5. 低功耗设计:延长电池续航时间

6.2 扩展方向

  • 摄像头监控:添加摄像头记录宠物进食情况
  • 语音播报:添加语音模块播放录音吸引宠物
  • 手机APP:开发配套APP远程控制
  • 多宠物识别:使用RFID识别不同宠物

6.3 学习资源

相关推荐
水云桐程序员3 小时前
用C语言写LED灯嵌入式系统案例|STM32 LED控制与按键输入系统
c语言·stm32·单片机
电子工程师成长日记-C513 小时前
51单片机电子打铃系统
单片机·嵌入式硬件·51单片机
iCxhust4 小时前
Keil µVision 调试指南---UART#1 模拟/调试窗口 完全使用教程
stm32·单片机·嵌入式硬件
iCxhust4 小时前
51单片机引脚 ALE EA PSEN的用途
单片机·嵌入式硬件·51单片机
碎像4 小时前
51单片机创建项目
单片机·嵌入式硬件·51单片机
木白CPP4 小时前
MCU 进程内存布局详解(.text, .rodata, .data, .bss, 堆, 栈)
单片机·嵌入式硬件
Lugas Luo4 小时前
车载录像存储性能模拟测试工具设计
linux·嵌入式硬件·测试工具
v132665623684 小时前
BK7258 wifi6音视频soc芯片应用分析
嵌入式硬件·物联网·音视频·iot·wifi6
風清掦5 小时前
【江科大STM32学习笔记-10】I2C通信协议 - 10.2 硬件 I2C 读写MPU6050
笔记·stm32·单片机·嵌入式硬件·学习