STM32 DMA-TIM输出PWM DMA通道与TIM匹配的刨坑记录

本文记录了一位开发者在使用STM32F103C8T6通过DMA和TIM配置PWM输出时遇到的问题。官方资料与实际配置的通道不匹配,作者通过实验发现了每个TIM实际对应的DMA通道。在配置过程中,需要注意GPIO、TIM、DMA的初始化,并特别指出高级定时器TIM1需要额外开启PWM输出。此外,文章提供了详细的代码示例和引脚配置,对于其他开发者具有参考价值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DMA+TIM方式输出PWM,无需CPU干预,所以想用DMA的方式直接输出到PWM

本次测试采用的是STM32F103C8T6 测试发现定时器的DMA通道与官方资料不符,实际情况与CUBEmx硬件配置也不符,刨了两天终于发现问题,遂做记录,后方道友谨慎这个坑...

DMA+TIM方式输出PWM配置大致过程

1、使能GPIO、TIM、DMA对应的时钟

2、GPIO初始化:配置相关IO口

3、DMA初始化:配置数据源、目标,输出传输方向,数据长度、大小,DMA通道........

4、TIM初始化:配置装载值,分频、通道......

重要

1、STM32F103C8T6 共有四个 TIM1-TIM4  其中TIM1为高级定时器  输出PWM配置与其他配置不一致,最后需要加上这一句 

  TIM_CtrlPWMOutputs(TIM1, ENABLE);    //高级定时器才有  必须打开

具体可以看我另外一篇文章-----高级定时器输出PWM

2、STM32F103C8T6 只有DMA1   通道1-7

3、TIM对应的DMA通道实测与官方资料不符,没有试过其他外设,使用的标准库versionV3.4.0

(按理说官方资料很严谨,应该不会有问题,也可能我的芯片或者代码哪里有问题,反正最后功能实现了,欢迎各位道友评论、讨论~~~)

各个通道的DMA1请求一览官方资料如下图,官方CUBE的里的TIM配置也和下图一致

实测情况是一个定时器对应一个DMA通道,如下:

TIM1:

PA8---TIM1-CH1  -------DMA1-CH5

PA9---TIM1-CH2  -------DMA1-CH5

PA10---TIM1-CH3  -------DMA1-CH5

PA11---TIM1-CH4  -------DMA1-CH5

TIM2:

PA0---TIM2-CH1  -------DMA1-CH2

PA1---TIM2-CH2  -------DMA1-CH2

PA2---TIM2-CH3  -------DMA1-CH2

PA3---TIM2-CH4  -------DMA1-CH2

TIM3:

PA6---TIM3-CH1  -------DMA1-CH3

PA7---TIM3-CH2  -------DMA1-CH3

PB0---TIM3-CH3  -------DMA1-CH3

PB1---TIM3-CH4  -------DMA1-CH3

TIM4:

PB6---TIM4-CH1  -------DMA1-CH7

PB7---TIM4-CH2  -------DMA1-CH7

PB8---TIM4-CH3  -------DMA1-CH7

PB9---TIM4-CH4  -------DMA1-CH7

STM32工程文件:

STM32TIM+DMA输出PWM工程源码-C/C++文档类资源-CSDN下载

源码:

其他芯片可能需要更改相关引脚配置

主函数调用:

#define MAX_TABLE 8

uint16_t Buffer_1[MAX_TABLE] =
{
    36, 72, 108, 144, 180, 216, 252, 288

}

uint16_t Buffer_2[MAX_TABLE] =
{
   1800, 1836, 1872, 1908, 1944, 1980, 2016, 2052

}

int main(void)
{
    int i = 0;
    SystemInit();
    uart1_Init(115200 );
    TIM_DMA_Configuration(TIM1, 1, (uint32_t*)Buffer_1, MAX_TABLE);
    TIM_DMA_Configuration(TIM2, 1, (uint32_t*)Buffer_2, MAX_TABLE);

    while (1)//断电打在此处即可查看DMA 和TIM 相关寄存器配置
    {
    }
}

DMA-TIM 输出PWM相关配置:

void TIM_DMA_Configuration(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t *pData, uint16_t Length)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;

    //使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3  | RCC_APB1Periph_TIM4, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);


//GPIO配置   根据芯片更改引脚配置
    if(TIMx == TIM1)
    {
        switch (Channel)
        {
            case 1:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;
                break;
            case 2:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_9;//与串口1同引脚  不建议使用
                break;
            case 3:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10;//与串口1同引脚  不建议使用
                break;
            case 4:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11;
                break;
            default:
                break;
        }
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);

    }
    else if(TIMx == TIM2)
    {
        switch (Channel)
        {
            case 1:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0;
                break;
            case 2:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1;
                break;
            case 3:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_2;
                break;
            case 4:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_3;
                break;
            default:
                break;
        }
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
    else if(TIMx == TIM3)
    {
        switch (Channel)
        {
            case 1:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOA, &GPIO_InitStructure);
                break;
            case 2:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_7;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOA, &GPIO_InitStructure);
                break;
            case 3:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOB, &GPIO_InitStructure);
                break;
            case 4:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOB, &GPIO_InitStructure);
                break;
            default:
                break;
        }
    }
    else if(TIMx == TIM4)
    {
        switch (Channel)
        {
            case 1:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6;
                break;
            case 2:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_7;
                break;
            case 3:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;
                break;
            case 4:
                GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_9;
                break;
            default:
                break;
        }

        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
    }
//GPIO配置   根据芯片更改引脚配置

//DMA配置    //DMA配置    //DMA配置
    switch (Channel)
    {
        case 1:
            DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIMx->CCR1;//DMA外设基地址 TIMx CH1
            break;
        case 2:
            DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIMx->CCR2;//DMA外设基地址  TIMx CH2
            break;
        case 3:
            DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIMx->CCR3;//DMA外设基地址  TIMx CH3
            break;
        case 4:
            DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIMx->CCR4;//DMA外设基地址  TIMx CH4
            break;
        default:
            break;
    }

    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pData;//DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//数据方向 内存到外设
    DMA_InitStructure.DMA_BufferSize = Length;//数据长度
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;// 外设地址寄存器不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据宽度为16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;// 内存数据宽度为16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA_Mode_Circular--循环模式   DMA_Mode_Normal--工作在正常模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;// DMA通道 x拥有中优先级 */
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;/// 禁用内存到内存传输

    if(TIMx == TIM1)
    {

        DMA_Init(DMA1_Channel5, &DMA_InitStructure);//配置DMA通道
        DMA_Cmd(DMA1_Channel5, ENABLE);//DMA使能
    }
    else if(TIMx == TIM2)
    {
        DMA_Init(DMA1_Channel2, &DMA_InitStructure);//配置DMA通道
        DMA_Cmd(DMA1_Channel2, ENABLE);//DMA使能
    }

    else if(TIMx == TIM3)
    {

        DMA_Init(DMA1_Channel3, &DMA_InitStructure);//配置DMA通道
        DMA_Cmd(DMA1_Channel3, ENABLE);//DMA使能
    }
    else if(TIMx == TIM4)
    {
        DMA_Init(DMA1_Channel7, &DMA_InitStructure);//配置DMA通道
        DMA_Cmd(DMA1_Channel7, ENABLE);//DMA使能
    }

    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period = 3600 - 1; //
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;//分频值
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 50;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
    switch (Channel)
    {
        case 1:
            TIM_OC1Init(TIMx, &TIM_OCInitStructure);//TIMx-CH1
            break;
        case 2:
            TIM_OC2Init(TIMx, &TIM_OCInitStructure);//TIMx-CH2
            break;
        case 3:
            TIM_OC3Init(TIMx, &TIM_OCInitStructure);//TIMx-CH3
            break;
        case 4:
            TIM_OC4Init(TIMx, &TIM_OCInitStructure);//TIMx-CH4
            break;
        default:
            break;
    }

    TIM_DMACmd(TIMx, TIM_DMA_Update, ENABLE);
    TIM_Cmd(TIMx, ENABLE);

    if(TIMx == TIM1)
    {
        TIM_CtrlPWMOutputs(TIM1, ENABLE);//高级定时器必须使能
    }
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌乱的小老弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值