STM32
直播中

蔡鼎瑾

7年用户 1031经验值
私信 关注
[问答]

STM32G4如何利用ADC-DMA对全桥威廉希尔官方网站 实现闭环控制?

开始接触STM32G474,用于对一个全桥威廉希尔官方网站 实现闭环控制,里面包括PI控制,准PR控制,和一个相位环;在学习完输出pwm和ADC-DMA两个模块后无法将两者整合在一起,对于后续步骤更加没有思路,希望各位大佬分享下相关控制类代码进行学习参考,或者大佬指导下该怎样学习,引一下路,谢谢(目前卡在了利用ADC采样值控制芯片输出)。


回帖(1)

百灵千岛酱

2024-3-22 16:48:25
实现全桥威廉希尔官方网站 的闭环控制需要以下几个步骤:

1. 配置ADC-DMA:
   - 设置ADC实例,包括通道、采样时间、采样周期等参数。
   - 设置DMA实例,使其可以将ADC采样的值传输到内存。
   - 配置ADC和DMA的中断,以便在数据转换完成时触发中断。

2. 采样并处理ADC数据:
   - 在DMA中断处理程序中,获取ADC采样的数据,并进行处理,如滤波、校准等。
   - 根据处理后的ADC数值,在闭环控制算法中计算所需的控制量。

3. 实现闭环控制算法:
   - 使用PI控制器、准PR控制器和相位环等闭环控制算法,根据期望值和反馈值计算出控制量。
   - 根据控制量调节PWM输出信号,实现对全桥威廉希尔官方网站 的闭环控制。可以使用定时器和PWM模块来生成PWM信号。

在学习的过程中,可以先分别实现ADC-DMA和PWM模块的配置和使用,确保能够获取到ADC采样的数据和生成PWM信号。然后,在理解闭环控制算法的基础上,将ADC采样数据与控制算法整合起来,并调整PWM输出信号。

以下是一个简单的示例代码,其中采用了ADC1和DMA1的Channel 1,生成PWM信号的定时器为TIM1。

```c
#include "stm32g4xx.h"

// ADC采样数据缓冲区
#define ADC_DMA_BUFFER_SIZE 100
volatile uint16_t adcDataBuffer[ADC_DMA_BUFFER_SIZE];

// ADC-DMA初始化
void ADC_DMA_Init(void)
{
    // 使能ADC时钟
    RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN;

    // 配置ADC时钟
    RCC->CCIPR |= RCC_CCIPR_ADC12SEL_0 | RCC_CCIPR_ADC12SEL_2; // PCLK2作为ADC时钟

    // 使能DMA时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;

    // 配置DMA传输
    DMA1_Channel1->CPAR = (uint32_t) &(ADC1->DR); // ADC数据寄存器地址
    DMA1_Channel1->CMAR = (uint32_t) adcDataBuffer; // 数据缓冲区的地址
    DMA1_Channel1->CNDTR = ADC_DMA_BUFFER_SIZE; // 数据传输长度
    DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0; // 使能存储器递增模式、存储器和外设数据长度为半字(16位)
    DMA1_Channel1->CCR |= DMA_CCR_CIRC | DMA_CCR_EN; // 循环模式、使能DMA通道

    // 配置ADC
    ADC1->CFGR |= ADC_CFGR_CONT; // 连续转换模式
    ADC1->CFGR |= ADC_CFGR_DMAEN; // 使能DMA传输
    ADC1->CFGR |= ADC_CFGR_DMACFG; // DMA传输顺序:按照转换顺序
    ADC1->CFGR |= ADC_CFGR_EXTEN_0 | ADC_CFGR_EXTSEL_2; // 触发源:TIM1_TRGO

    // 配置ADC通道
    ADC1->SQR1 |= ADC_SQR1_SQ1_0 | ADC_SQR1_SQ1_1; // ADC通道1
    ADC1->SQR1 |= ADC_SQR1_L_0; // 2个转换在规则序列中
   
    // 使能ADC
    ADC1->CR |= ADC_CR_ADEN;
    while (!(ADC1->ISR & ADC_ISR_ADRDY)) {}

    // 启动ADC转换
    ADC1->CR |= ADC_CR_ADSTART;

    // 使能ADC DMA请求
    ADC1->CR |= ADC_CR_DMAEN;
}

// PWM初始化
void PWM_Init(void)
{
    // 使能GPIOA时钟
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
    // 使能TIM1时钟
    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;

    // 配置GPIOA8、GPIOA9为复用推挽输出
    GPIOA->MODER &= ~(GPIO_MODER_MODE8_0 | GPIO_MODER_MODE8_1);
    GPIOA->MODER |= GPIO_MODER_MODE8_1;
    GPIOA->AFR[1] |= GPIO_AFRH_AFSEL8_0 | GPIO_AFRH_AFSEL8_1 | GPIO_AFRH_AFSEL8_2; // 设置AF1

    GPIOA->MODER &= ~(GPIO_MODER_MODE9_0 | GPIO_MODER_MODE9_1);
    GPIOA->MODER |= GPIO_MODER_MODE9_1;
    GPIOA->AFR[1] |= GPIO_AFRH_AFSEL9_0 | GPIO_AFRH_AFSEL9_1| GPIO_AFRH_AFSEL9_2; // 设置AF1

    // 配置TIM1为边沿对齐的向上计数模式
    TIM1->CR1 = TIM_CR1_DIR | TIM_CR1_CMS_0 | TIM_CR1_CMS_1;
    TIM1->CR2 = 0;
    TIM1->PSC = 0;
    TIM1->ARR = 255;

    // 配置TIM1通道1和通道2为PWM输出
    TIM1->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E;
    TIM1->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE; // PWM模式1,预装载模式使能
    TIM1->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
    TIM1->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE; // 设置OC1PE,OC1M
    TIM1->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;

    // 使能TIM1输出
    TIM1->BDTR |= TIM_BDTR_MOE;

    // 启动TIM1定时器
    TIM1->CR1 |= TIM_CR1_CEN;
}

// 采样并处理ADC数据(在DMA中断处理程序中调用)
void ProcessADCData(void)
{
    uint16_t adcValue = adcDataBuffer[0]; // 获取ADC采样数据
    // 处理ADC数值,如滤波、校准等

    // 根据处理后的ADC数值,计算控制量

    // 调整PWM输出信号,实现控制
    TIM1->CCR1 = control_value; // 设置PWM占空比(0-255)
    TIM1->CCR2 = control_value; // 设置PWM占空比(0-255)
}

// DMA中断处理函数
void DMA1_Channel1_IRQHandler(void)
{
    // 判断DMA1_Channel1传输完成中断
    if (DMA1->ISR & DMA_ISR_TCIF1)
    {
        DMA1_Channel1->IFCR |= DMA_IFCR_CTCIF1; // 清除中断标志位
        ProcessADCData(); // 处理ADC数据
    }
}
```

请根据你的具体需求和硬件配置对上述代码进行适当的修改。希望以上信息对你有所帮助!
举报

更多回帖

发帖
×
20
完善资料,
赚取积分