打印
[MM32硬件]

通过灵动MCU实现多路电压监控且尽量不影响功耗

[复制链接]
72|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
雨果喝水|  楼主 | 2025-4-29 16:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
技术实现方案
1. 使用ADC多通道复用
灵动MCU的ADC一般支持多个输入通道(通常支持8~16个模拟输入引脚):

单ADC、多通道扫描模式

配置ADC为扫描模式(Scan Mode),支持在一个采样周期内轮询多个通道。

自动通道切换,减少CPU干预。

示例:

c
复制
编辑
ADC_InitStructure.ScanConvMode = ENABLE;      // 扫描模式
ADC_InitStructure.ContinuousConvMode = DISABLE;
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles);
// 多通道配置...
2. 定时采样调度(低频触发)
采用定时器触发ADC采样,代替持续扫描:

节省CPU时间与功耗

可调采样频率(如每10ms、每100ms采一次)

c
复制
编辑
// ADC触发源配置为TIM1/TRGO
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_ExternalTrigConvConfig(ADC1, ADC_ExternalTrigConv_T1_TRGO);
3. 低功耗运行模式结合ADC唤醒
如果MCU支持低功耗模式(如SLEEP或STOP),可以:

在ADC采样前唤醒

采样完成后进入SLEEP

采样间隔足够长(如>100ms),非常节能

示例流程:

css
复制
编辑
[低功耗睡眠中]
   ↓
[定时器唤醒 MCU]
   ↓
[启动 ADC 采样所有通道]
   ↓
[采样完成 → 中断通知 → 数据处理]
   ↓
[重新进入睡眠]
4. 参考电压 & 分压输入设计
建议使用外部稳压参考源,提升不同通道采样一致性

若被测电压高于ADC允许输入,需通过高阻分压(如1MΩ+1MΩ)接入

三、功耗优化建议

技术策略        优化点
使用Scan模式        避免频繁切换通道,提升效率
定时器触发采样        降低主循环干预,定时调度更节能
低功耗模式(Sleep)        空闲时进入睡眠,减少整体能耗
分时/分组采样        将通道按优先级或更新速率分批采样
软/硬件滤波处理        减少重复采样次数,提高单次采样可靠性
DMA传输(如支持)        减少CPU负担(部分型号支持ADC DMA)
示例应用结构图(描述)
css
复制
编辑
           多路电压源(如 Vbat1~Vbat4)
                  │
         [电阻分压/滤波电路]
                  │
        ┌──────────────┐
        │ ADC 多通道复用│ ← 参考电压稳定(内部/外部)
        └──────────────┘
                  ▲
       定时器触发采样(定时唤醒)
                  ▲
         低功耗主循环(Sleep)
总结

项目        实现建议
多路采样        使用Scan模式+定时器触发
节能控制        Sleep模式 + 定时调度采样
精度控制        稳定参考电压 + 软件滤波
软件架构        中断驱动 + 循环休眠唤醒机制

使用特权

评论回复
沙发
雨果喝水|  楼主 | 2025-4-29 16:54 | 只看该作者
采集 4 路模拟电压(可扩展)

每 100ms 采样一次

利用 ADC 扫描模式 + 定时器触发

系统进入 Sleep 模式 节能

采样完成通过 中断通知

main.c
#include "mm32_device.h"
#include "adc.h"
#include "timer.h"
#include "lowpower.h"

volatile uint16_t adcValues[4];

int main(void) {
    ADC_Config();         // 配置ADC
    TIM2_Config();        // 设置定时器用于触发ADC
    LowPower_Config();    // 设置低功耗模式(Sleep)

    while (1) {
        __WFI();          // 等待中断唤醒,进入 Sleep 模式
        // 中断唤醒后在这里处理采样结果
        ProcessVoltage(adcValues);
    }
}

adc.c
void ADC_Config(void) {
    // 使能ADC时钟
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

    // 设置为扫描模式,单次转换
    ADC1->CR1 |= ADC_CR1_SCAN;
    ADC1->CR2 |= ADC_CR2_EXTTRIG | ADC_CR2_EXTSEL_TIM2_TRGO;
    ADC1->CR2 |= ADC_CR2_EOCIE; // 转换完成中断

    // 配置通道序列
    ADC1->SQR1 = ADC_SQR1_L_3; // 4通道
    ADC1->SQR3 = (0 << 0) | (1 << 5) | (2 << 10) | (3 << 15); // CH0-CH3

    // 启用ADC
    ADC1->CR2 |= ADC_CR2_ADON;

    // 启用中断
    NVIC_EnableIRQ(ADC1_2_IRQn);
}

void ADC1_2_IRQHandler(void) {
    static uint8_t channelIndex = 0;
    if (ADC1->SR & ADC_SR_EOC) {
        adcValues[channelIndex++] = ADC1->DR;
        if (channelIndex >= 4) {
            channelIndex = 0;
        }
    }
}


timer.c
void TIM2_Config(void) {
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

    TIM2->PSC = 7200 - 1;     // 72MHz -> 10kHz
    TIM2->ARR = 1000 - 1;     // 10kHz -> 每100ms触发
    TIM2->CR2 |= TIM_CR2_MMS_1; // TRGO为更新事件
    TIM2->CR1 |= TIM_CR1_CEN;
}


lowpower.c
void LowPower_Config(void) {
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // 可选,若需进入深度睡眠
    // SLEEP 模式默认 WFI 指令有效
}






使用特权

评论回复
板凳
雨果喝水|  楼主 | 2025-4-29 16:54 | 只看该作者
数据处理示例
void ProcessVoltage(uint16_t *adc) {
    for (int i = 0; i < 4; i++) {
        float voltage = (3.3f / 4096.0f) * adc[i]; // 假设参考电压为3.3V
        if (voltage < 2.8f) {
            TriggerLowVoltageAlarm(i, voltage);
        }
    }
}




使用特权

评论回复
地板
雨果喝水|  楼主 | 2025-4-29 16:55 | 只看该作者
多路采样调度流程图(逻辑示意)
┌────────────┐
│ Timer中断  │ ← 每100ms
└────┬───────┘
     │ 触发 ADC 开始转换(4通道)
┌────▼───────┐
│ ADC采样完成│
│(每通道EOC) │
└────┬───────┘
     │
     ▼
[采样数据缓存 → 低压判断 → 发出告警]
     │
     ▼
┌────────┐
│ __WFI()│ ← MCU重新休眠,等待下次触发
└────────┘


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

89

主题

1199

帖子

0

粉丝