社区导航

 

搜索
123
返回列表 发新帖
楼主: 通宵敲代码

[原创] WS2812灯珠的STM32驱动方式(二)——DMA+PWM

  [复制链接]

4

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2020-1-19 20:45 | 显示全部楼层

看了一下程序,不是独立的,是顺序的,三路的那个,除了第一路比较正常,其它路不正常,学习了



回复

使用道具 举报

4

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2020-1-21 10:52 | 显示全部楼层

里面有一些地方理解不了

prescaler=0,这个占空比这里设置为0

但纵观整个程序,没有看到有其它设置占空比的地方,除非这个:

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM4_CCR1_Address;    // physical address of Timer 3 CCR1

如果是这个的话,它是怎么改变的呢?

 



回复

使用道具 举报

5

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2020-3-20 19:56 | 显示全部楼层

image-20200320195604-1.png 这里发送的(50,25)是如何计算出来的?

点评

根据WS2812驱动波形近似出来的一个比例数,具体看一下驱动原理就明白了  详情 回复 发表于 2020-4-16 10:46


回复

使用道具 举报

1

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2020-4-4 21:58 | 显示全部楼层

谢谢指教



回复

使用道具 举报

1274

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2020-4-16 10:46 | 显示全部楼层
n_m 发表于 2020-3-20 19:56 这里发送的(50,25)是如何计算出来的?

根据WS2812驱动波形近似出来的一个比例数,具体看一下驱动原理就明白了



回复

使用道具 举报

4

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2020-4-23 13:57 | 显示全部楼层

楼主我有个疑惑,为什么非要用DMA呢?我用F4的板子还需要用DMA吗?

点评

用DMA是为了给MCU留出更多的时间去处理别的事情, 如果MCU只是用来点个灯,可以不用DMA方式  详情 回复 发表于 2020-4-25 00:24


回复

使用道具 举报

1274

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2020-4-25 00:24 | 显示全部楼层
Heart丶。 发表于 2020-4-23 13:57 楼主我有个疑惑,为什么非要用DMA呢?我用F4的板子还需要用DMA吗?

用DMA是为了给MCU留出更多的时间去处理别的事情,

如果MCU只是用来点个灯,可以不用DMA方式



回复

使用道具 举报

4

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2020-4-26 10:28 | 显示全部楼层
通宵敲代码 发表于 2020-4-25 00:24 用DMA是为了给MCU留出更多的时间去处理别的事情, 如果MCU只是用来点个灯,可以不用DMA方式

楼主我自己用PWM模式控制,灯一直以很高的频率在闪完全不受控制。我又按照你的驱动改了一份F4的DMA+PWM的,还是一直闪不受控制。看了好几天了感觉代码写的确实是没有问题,能不能麻烦你帮我看看到底哪里出了问题?

 

#define TIMING_ONE  70
#define TIMING_ZERO 30
 uint16_t LED_BYTE_Buffer[300];
 
DMA_HandleTypeDef  TIM3xDMA_Handler;   
TIM_HandleTypeDef  TIM3_Handler;         //定时器3PWM句柄 
TIM_OC_InitTypeDef TIM3_CH4Handler;        //定时器3通道4句柄
//TIM3 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init()

    TIM3_Handler.Instance=TIM3;            //定时器3
    TIM3_Handler.Init.Prescaler=0;       //定时器分频
    TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
    TIM3_Handler.Init.Period=100-1;          //自动重装载值
    TIM3_Handler.Init.ClockDivision=0;
    HAL_TIM_PWM_Init(&TIM3_Handler);       //初始化PWM
    
    TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
    TIM3_CH4Handler.Pulse=0;            //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
    TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_HIGH; //输出比较极性为高
    HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道4
    
    HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);//开启PWM通道4
}


//定时器底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_PWM_Init()调用
//htim:定时器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_TIM3_CLK_ENABLE();            //使能定时器3
    __HAL_RCC_GPIOB_CLK_ENABLE();            //开启GPIOB时钟
    
    GPIO_Initure.Pin=GPIO_PIN_1;               //PB1
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;      //复用推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    GPIO_Initure.Alternate= GPIO_AF2_TIM3;    //PB1复用为TIM3_CH4
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}


////设置TIM通道4的占空比
////compare:比较值
//void TIM_SetTIM3Compare4(u32 compare)
//{
//    TIM3->CCR4=compare; 
//}
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx)

    __HAL_RCC_DMA1_CLK_ENABLE();
    __HAL_LINKDMA(&TIM3_Handler,hdma[TIM_DMA_ID_CC4],TIM3xDMA_Handler);
    
    
     HAL_DMA_DeInit(&TIM3xDMA_Handler);
     TIM3xDMA_Handler.Instance=DMA1_Stream2;
     TIM3xDMA_Handler.Init.Channel=DMA_CHANNEL_5;
     TIM3xDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH;
     TIM3xDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
     TIM3xDMA_Handler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL;
     TIM3xDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE;
     TIM3xDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE;
     TIM3xDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD;
     TIM3xDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;
     TIM3xDMA_Handler.Init.MemInc=DMA_MINC_ENABLE;
     TIM3xDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE;
     TIM3xDMA_Handler.Init.Mode=DMA_NORMAL; 
     TIM3xDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM;
     HAL_DMA_Init(&TIM3xDMA_Handler);


void WS2812_send(uint8_t (*color)[3], uint16_t len)
{
    uint8_t i;
    uint16_t memaddr;
    uint16_t buffersize;
    buffersize = (len*24)+43;    // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes
    memaddr = 0;                // reset buffer memory index

    while (len)
    {    
        for(i=0; i<8; i++) // GREEN data
        {
            LED_BYTE_Buffer[memaddr] = ((color[0][1]<<i) & 0x0080) ? TIMING_ONE:TIMING_ZERO;
            memaddr++;
        }
        for(i=0; i<8; i++) // RED
        {
                LED_BYTE_Buffer[memaddr] = ((color[0][0]<<i) & 0x0080) ? TIMING_ONE:TIMING_ZERO;
                memaddr++;
        }
        for(i=0; i<8; i++) // BLUE
        {
                LED_BYTE_Buffer[memaddr] = ((color[0][2]<<i) & 0x0080) ? TIMING_ONE:TIMING_ZERO;
                memaddr++;
        }
        len--;
    }
//===================================================================//    
//bug:最后一个周期波形不知道为什么全是高电平,故增加一个波形
      LED_BYTE_Buffer[memaddr] = ((color[0][2]<<8) & 0x0080) ? TIMING_ONE:TIMING_ZERO;
//===================================================================//    
      memaddr++;    
        while(memaddr < buffersize)
        {
            LED_BYTE_Buffer[memaddr] = 0;
            memaddr++;
        }  

         HAL_TIM_PWM_Start_DMA(&TIM3_Handler, TIM_CHANNEL_4, (u32 *)&LED_BYTE_Buffer, buffersize);     // load number of bytes to be transferred
         while(!__HAL_DMA_GET_FLAG(&TIM3xDMA_Handler,DMA_FLAG_TCIF2_6));        
         HAL_TIM_PWM_Stop_DMA(&TIM3_Handler, TIM_CHANNEL_4);
        __HAL_DMA_CLEAR_FLAG(&TIM3xDMA_Handler,DMA_FLAG_TCIF2_6);
}
 



回复

使用道具 举报

4

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2020-4-26 10:36 | 显示全部楼层
通宵敲代码 发表于 2020-4-25 00:24 用DMA是为了给MCU留出更多的时间去处理别的事情, 如果MCU只是用来点个灯,可以不用DMA方式

这一份PWM的逻辑就更简单了,我就在主函数调用WS2812_send(r,1),r这个数组我定义的是

r[][0]={255,0,0},理论上应该是红色,可是实际的效果是各种颜色已很高的频率不停的闪,因为用的是F4的板子,时钟是180MHZ的,TIM的时钟是90Mhz,arr给100-1,psc给0,理论上一个周期1.11us,感觉这里设定的也没有错

 

 

#define TIMING_ONE  70
#define TIMING_ZERO 30
 u16 LED_BYTE_Buffer[300];

TIM_HandleTypeDef TIM3_Handler;         //定时器3PWM句柄 
TIM_OC_InitTypeDef TIM3_CH4Handler;        //定时器3通道4句柄

//TIM3 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)

    TIM3_Handler.Instance=TIM3;            //定时器3
    TIM3_Handler.Init.Prescaler=psc;       //定时器分频
    TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
    TIM3_Handler.Init.Period=arr;          //自动重装载值
    TIM3_Handler.Init.ClockDivision=0;
    HAL_TIM_PWM_Init(&TIM3_Handler);        //初始化PWM
    
    TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
    TIM3_CH4Handler.Pulse=0;            //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
    TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_HIGH; //输出比较极性为高
    HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道4
    
    HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);//开启PWM通道4
}


//定时器底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_PWM_Init()调用
//htim:定时器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_TIM3_CLK_ENABLE();            //使能定时器3
    __HAL_RCC_GPIOB_CLK_ENABLE();            //开启GPIOB时钟
    
    GPIO_Initure.Pin=GPIO_PIN_1;               //PB1
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;      //复用推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    GPIO_Initure.Alternate= GPIO_AF2_TIM3;    //PB1复用为TIM3_CH4
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}


//设置TIM通道4的占空比
//compare:比较值
void TIM_SetTIM3Compare4(u32 compare)
{
    TIM3->CCR4=compare; 
}

void WS2812_send(u8 *rgb[3], u16 len)
{    u16 j;
    u8 i;
    u16 memaddr;
    u16 buffersize;
    buffersize = (len*24)+43;    // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes
    memaddr = 0;                // reset buffer memory index

    while (len)
    {    
        for(i=0; i<8; i++) //  GREEN
        {
            LED_BYTE_Buffer[memaddr] = ((rgb[0][1]<<i) & 0x0080) ?   TIMING_ONE:TIMING_ZERO;
            memaddr++;
                
        }
        for(i=0; i<8; i++) // RED
        {
                LED_BYTE_Buffer[memaddr] = ((rgb[0][0]<<i) & 0x0080) ?  TIMING_ONE:TIMING_ZERO;
                memaddr++;
        
        }
        for(i=0; i<8; i++) // BLUE
        {
                LED_BYTE_Buffer[memaddr] = ((rgb[0][2]<<i) & 0x0080) ?  TIMING_ONE:TIMING_ZERO;
                memaddr++;
            
        }
        len--;
          
    }

    for(j=0;j<300;j++){
        
        
        TIM_SetTIM3Compare4(LED_BYTE_Buffer[j]);
        
        
        
                      }
    
        HAL_TIM_PWM_Stop(&TIM3_Handler,TIM_CHANNEL_4);        
                      
}



回复

使用道具 举报

4

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2020-4-29 08:32 | 显示全部楼层

楼主我用示波器测了,波形是正常的,说明代码是没问题的,但是每一段时间内就会有一个小的高电平脉冲,所以导致了灯各种颜色一直闪,找来找去发现我在初始化HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);和HAL_TIM_PWM_Start_DMA(&TIM3_Handler, TIM_CHANNEL_4, (u32 *)&LED_BYTE_Buffer, buffersize); 这两句,只要有初始化就会有一个高电平脉冲,可是这两句都是放在while循环里的,就会一直执行导致了会有断断续续的高电平影响整个信号,这个bug我还没找到解决方法,打算先用您第三章的SPI+DMA来试试

点评

多尝试尝试吧,有问题得慢慢追  详情 回复 发表于 2020-4-29 08:36


回复

使用道具 举报

1274

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2020-4-29 08:36 | 显示全部楼层
Heart丶。 发表于 2020-4-29 08:32 楼主我用示波器测了,波形是正常的,说明代码是没问题的,但是每一段时间内就会有一个小的高电平脉冲,所以 ...

多尝试尝试吧,有问题得慢慢追



回复

使用道具 举报

2

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2020-5-22 15:02 | 显示全部楼层
通宵敲代码 发表于 2018-5-6 22:49 定义了三个数组,主函数里写个三色循环呼吸灯看看效果。 附上相关程序 这个是用TIM2_CH1实 ...

老哥,这个4路灯的代码,里面的TIM5初始化应该是有些问题的,初始化TIM5后会进断言



回复

使用道具 举报

2

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2020-5-22 15:40 | 显示全部楼层
Anonymous_W 发表于 2020-5-22 15:02 老哥,这个4路灯的代码,里面的TIM5初始化应该是有些问题的,初始化TIM5后会进断言

哈哈,问题找到了,我的问题,没仔细看手册。



回复

使用道具 举报

1274

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2020-5-23 00:37 | 显示全部楼层
Anonymous_W 发表于 2020-5-22 15:40 哈哈,问题找到了,我的问题,没仔细看手册。



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

关闭

站长推荐上一条 1/8 下一条

  • 论坛活动 E手掌握

    扫码关注
    EEWORLD 官方微信

  • EE福利  唾手可得

    扫码关注
    EE福利 唾手可得

Archiver|手机版|小黑屋|电子工程世界 ( 京ICP证 060456 )

GMT+8, 2020-5-28 06:11 , Processed in 0.411014 second(s), 34 queries , Gzip On, MemCache On.

快速回复 返回顶部 返回列表