为第二次考核的电控学习

电控在二次考核前只有这次培训,这次培训主要给大家讲述考核相关的知识点,和一些使用工具的技巧(调试和使用AI),以PWM的学习为例,希望大家认真学习,在下次考核中取得好成果,加入队伍。

PWM学习

什么PWM

PWM 是一种通过周期性脉冲信号的占空比来模拟模拟信号的技术。核心参数包括:

  • 周期(Period):一个完整脉冲的时间长度(T),单位通常是毫秒(ms)或微秒(μs)
  • 频率(Frequency):周期的倒数(f=1/T),表示每秒脉冲的数量
  • 占空比(Duty Cycle):高电平时间在一个周期中所占的比例(D = 高电平时间 / T×100%)

例如:一个周期为 10ms(频率 100Hz)、占空比 30% 的 PWM 信号,意味着在 10ms 内,3ms 为高电平,7ms 为低电平。

其实,这个在高中就已经接触到类似的知识:平均值

PWM有什么用

PWM(脉冲宽度调制)是嵌入式开发中非常基础且应用广泛的技术,其核心价值在于通过数字信号模拟模拟量控制,实现对各种外设的精准调节。以下是 PWM 的主要应用场景,结合 STM32 等嵌入式系统的实际开发场景说明:

1. 灯光亮度调节(最基础应用)

  • 原理 :LED 的亮度由流过的电流决定,而 PWM 通过改变高电平占空比,调节单位时间内的平均电流(占空比越高,平均电流越大,亮度越高)。
  • 优势 :相比直接用 DAC(数模转换器)调节电压,PWM 仅需普通 GPIO(复用为定时器通道)即可实现,硬件成本低,且调节范围更广(0~100% 占空比对应 0~ 最大亮度)。
  • 示例 :STM32 通过 PWM 控制 LED 实现 "呼吸灯" 效果(占空比从 0 逐渐增加到 100%,再逐渐减小,模拟呼吸节奏)。

2. 电源与能量控制(高效节能)

DC-DC 转换器 :在开关电源中,PWM 控制功率开关管(MOS 管、三极管)的导通 / 关断时间,通过电感、电容储能释放,将输入电压转换为稳定的输出电压(如 5V 转 3.3V)。相比线性稳压器(如 LDO),PWM 开关电源效率更高(通常 80%~95%),适合大电流场景。

3. 音频信号生成(简单发声场景)

蜂鸣器发声 :无源蜂鸣器需要外部交流信号驱动,PWM 的频率决定音调(如 440Hz 对应 "A 调"),占空比决定音量(占空比 50% 时音量最大)。STM32 通过改变定时器的 ARR 值(调整频率)和 CCRx 值(调整占空比),可实现不同音调、音量的报警声或简单音乐。

如何实现PWM(呼吸灯)

呼吸灯 :用定时器实现呼吸灯的核心原理是通过动态改变 PWM 的占空比,使 LED 的平均电流随时间线性变化,从而呈现 "呼吸" 效果。

stm32如何代码实现:

1.只用定时器基本功能

复制代码
#include "main.h"

TIM_HandleTypeDef htim3;
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 呼吸灯参数
uint16_t bright_time = 1;   // 亮的时间(单位:10ms,初始10ms)
uint16_t dark_time = 99;    // 灭的时间(单位:10ms,初始990ms)
int8_t step = 1;            // 步长(每次增减1)
uint8_t led_state = 0;      // LED状态(0:灭,1:亮)
uint16_t counter = 0;       // 计数变量(累计中断次数)

void SystemClock_Config(void);
static void MX_TIM3_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  
  // 初始化LED引脚(PA6)
  __HAL_RCC_GPIOA_CLK_ENABLE();
  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  
  MX_TIM3_Init();
  HAL_TIM_Base_Start_IT(&htim3);  // 启动定时器中断

  while (1)
  {
    // 主循环空闲,中断中处理核心逻辑
  }
}

// 定时器初始化
static void MX_TIM3_Init(void)
{
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 7199;       // 72MHz/(7199+1)=10kHz(0.1ms/计数)
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 99;            // 99+1=100计数 → 定时100×0.1ms=10ms
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
}

// 定时器更新中断回调函数(每10ms触发一次)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim == &htim3)
  {
    counter++;  // 累计中断次数(每次10ms)
    
    if (led_state == 1)  // 当前LED亮
    {
      // 亮的时间达到设定值,切换为灭
      if (counter >= bright_time)
      {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);  // 灭
        led_state = 0;
        counter = 0;  // 重置计数器
      }
    }
    else  // 当前LED灭
    {
      // 灭的时间达到设定值,切换为亮
      if (counter >= dark_time)
      {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);  // 亮
        led_state = 1;
        counter = 0;  // 重置计数器
        
        // 调整亮/灭时间比例(模拟占空比变化)
        bright_time += step;
        dark_time -= step;
        
        // 到达边界时反向调整
        if (bright_time >= 99) step = -1;
        if (bright_time <= 1) step = 1;
      }
    }
  }
}

// 系统时钟配置(CubeMX自动生成)
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  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;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{
  while(1) {}
}

2.使用pwm输出

复制代码
#include "main.h"

TIM_HandleTypeDef htim3;

// PWM占空比参数
uint16_t pwm_duty = 0;   // 比较值(0~999,对应0~100%占空比)
int8_t step = 5;         // 步长(每次增减5)

void SystemClock_Config(void);
static void MX_TIM3_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_TIM3_Init();
  
  // 启动TIM3_CH1的PWM输出
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);

  while (1)
  {
    // 更新PWM占空比(核心函数)
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwm_duty);
    
    // 延时20ms,控制呼吸速度
    HAL_Delay(20);
    
    // 调整占空比,到达边界时反向
    pwm_duty += step;
    if (pwm_duty >= 999) step = -5;
    if (pwm_duty <= 0) step = 5;
  }
}

// 定时器及PWM初始化
static void MX_TIM3_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 71;       // 72MHz/(71+1)=1MHz(1μs/计数)
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 999;         // 周期1000×1μs=1ms(频率1kHz)
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  
  // 配置时钟源为内部时钟
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  
  // 初始化PWM模式
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  
  // 主模式配置(默认即可)
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  
  // PWM通道1配置
  sConfigOC.OCMode = TIM_OCMODE_PWM1;  // PWM模式1:CNT < CCRx时输出高电平
  sConfigOC.Pulse = 0;                 // 初始比较值(占空比0)
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;  // 高电平有效
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  
  // 初始化复用GPIO(PA6)
  HAL_TIM_MspPostInit(&htim3);
}

// 复用GPIO初始化(CubeMX自动生成,在HAL_TIM_MspPostInit中调用)
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(timHandle->Instance==TIM3)
  {
    __HAL_RCC_GPIOA_CLK_ENABLE();
    // PA6 -> TIM3_CH1
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  }
}

// 系统时钟配置(同前,省略)
void SystemClock_Config(void) { /* ... */ }
void Error_Handler(void) { while(1) {} }

关于AI

上面都是用ai写的,其实能够灵活地运用ai是一个非常重要的技能。ai的使用是有技巧的。随着机器学习的普及,相信大家在日常生活中也会听到"训练"这个词,在使用ai时我们要合理地投喂ai,才能从ai得到我们想要的结果。

例如,你想要ai帮你生成呼吸灯的代码,你不能直接跟ai说"我需要呼吸灯的代码,给我生成一份",这样得到的代码往往不能令人满意。如果你懂得训练ai,你可能会先问他一些相关的问题,比如"呼吸灯的原理是什么","pwm是什么","我是stm32 hal库的初学者,请你教教我如何用简单的定时器中断实现呼吸灯",往往会得到想要的结果。(事先投喂相关的知识,加上限制词,是很好的方法)

另外,不要盲目的相信ai(尤其是豆包)。我之前考核时就犯了这个错误,当时也是糊涂了,豆包给我生成的有错误,我就一直问"为什么xxx不符合预期结果",其实,对于ai生成有错误的,我们应该汲取有用的,对于可能导致出现问题的,单独进行提问,而不是把一整套发上去,盲目地提问,要有针对性

其他地没办法多说,这个在后续使用过程中汲取经验也能学会,在不明白可以去b站上学一下如何使用ai(没有废物的ai,只有废物的使用者,豆包除外)

https://www.doubao.com/thread/wfa88382ee212c3c7

关于调试

这个也是经验,基础的学习其实很简单。现在使用的调试就是打断点,和添加变量看变量的实时变化,再就是单步运行之类的了。

希望大家经过这次的培训能学到一些知识,祝大家通过第二次考核

相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J4 天前
从“Hello World“ 开始 C++
c语言·c++·学习