stm32 f407 控制ws2812问题

2019-07-20 02:08发布

本帖最后由 笨鸟先飞鱼 于 2016-7-5 16:14 编辑


控制ws2812   ,单线归零协议,相同周期,不同的高低电平时间,代表“1”和“0”。

现在用stm32 f407   控制ws2812灯珠, 采用网上别人所说的,用DMA传输+PWM的方式,  就是用DMA传输,传过来不同的占空比值,也就是DMA传过来的值,控制不同的CCR1的值,
我现在最大的疑惑在于,如何实现同步?
如何恰好在pwm走完一个周期时,pwm的占空比被改变?

请大神指点下,如何用DMA+PWM的方式,来实现具体的某个波形的占空比是由自己控制的。或者谁有f407控制ws2812灯珠的程序,发一份也行。 (自己用软件延时的就不需要了)

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
12条回答
我有一个梦想
1楼-- · 2019-07-20 18:15
我用的是STM32F407ZET6,使用功能的是DMA传输+PWM的方式,我使用的是TIM3->CCR3,通道选择的是DMA1_Stream7,DMA_Channel_5,这个是stm32f407芯片内部规定好的。
下面是程序
#include  "WS2812B.h"
#include "stm32f4xx_dma.h"//#include "stm32f10x_dma.h"
//#include "stm32f10x.h"
#include "stm32f4xx_tim.h"//#include "stm32f10x_tim.h"
#include "stdio.h"
#include "dma.h"
/* Buffer that holds one complete DMA transmission
*
* Ensure that this buffer is big enough to hold
* all data bytes that need to be sent
*
* The buffer size can be calculated as follows:
* number of LEDs * 24 bytes + 42 bytes
*
* This leaves us with a maximum string length of
* (2^16 bytes per DMA stream - 42 bytes)/24 bytes per LED = 2728 LEDs
*/
#define TIM3_CCR3_Address 0x4000043c         // physical memory address of Timer 3 CCR1 register
//#define TIM3_CCR1_Address 0x40000434        // physical memory address of Timer 3 CCR1 register
       
#define TIMING_ONE  59
#define TIMING_ZERO 29
uint16_t LED_BYTE_Buffer[300];//注意  这里最多能使用10个灯,有上线
//---------------------------------------------------------------//





  void TIM_Config(void)
{
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        TIM_OCInitTypeDef  TIM_OCInitStructure;


        GPIO_InitTypeDef GPIO_InitStructure;
        DMA_InitTypeDef DMA_InitStructure;
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3);  //端口B0复用为定时器3
  /* GPIOA and GPIOB clock enable */
  RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB, ENABLE);

  /* GPIOA Configuration: Channel 3 as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;  //
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  //复用功能
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //速度50M
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;     //推挽复用
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;  //不上拉也不下拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化端口
       






        MYDMA_Config(DMA1_Stream7,DMA_Channel_5,(u32)&TIM3->CCR3,(u32)LED_BYTE_Buffer,43,DMA_PeripheralDataSize_HalfWord);//DMA1,STEAM7,CH5,外设定时器3的通道3,存储器为SendBuff,长度为:SEND_BUF_SIZE.


        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

        TIM_TimeBaseStructure.TIM_Period = 105-1;   //自动重装载值  频率是800khz
        TIM_TimeBaseStructure.TIM_Prescaler = 0;   //定时器分频
        TIM_TimeBaseStructure.TIM_ClockDivision = 0;
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式
        TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;        //时钟分频
        TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

        /* PWM1 Mode configuration: Channel1 */
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   //选择定时器模式:TIM脉冲宽度调制模式1
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  //比较输出使能
        TIM_OCInitStructure.TIM_Pulse =0;//待装入捕获比较寄存器的脉冲值  30
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //输出极性:TIM输出比较极性高
        //TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Set;
        TIM_OC3Init(TIM3, &TIM_OCInitStructure);
  TIM3->CR2=0x0008;
  /* Enable preload feature */
//  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
  
  /* TIM1 counter enable */
//  TIM_Cmd(TIM3, ENABLE);
  
  /* DMA enable*/
//  DMA_Cmd(DMA1_Stream7, ENABLE);
  
  /* TIM3 Update DMA Request enable */
  TIM_DMACmd(TIM3, TIM_DMA_CC3 , ENABLE);

  /* Main Output Enable */
// TIM_CtrlPWMOutputs(TIM3, ENABLE);//注意  这里如果是高级定时器,需要打开才能用pwm输出


}







/* This function sends data bytes out to a string of WS2812s
* The first argument is a pointer to the first RGB triplet to be sent
* The seconds argument is the number of LEDs in the chain
*
* This will result in the RGB triplet passed by argument 1 being sent to
* the LED that is the furthest away from the controller (the point where
* data is injected into the chain)
/ *此函数将数据字节发送到WS2812的字符串
*第一个参数是指向要发送的第一个RGB三元组的指针
* seconds参数是链中的LED数量
*:
*这将导致通过参数1传递的RGB三元组发送到
*离控制器最远的LED(
*数据被注入链中
*/
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] = (uint32_t)( ((color[0][1]<<i) & 0x0080) ? TIMING_ONE:TIMING_ZERO);
                                        memaddr++;
                        }
                        for(i=0; i<8; i++) // RED
                        {
                                        LED_BYTE_Buffer[memaddr] =(uint32_t)( ((color[0][0]<<i) & 0x0080) ? TIMING_ONE:TIMING_ZERO);
                                        memaddr++;
                        }
                        for(i=0; i<8; i++) // BLUE
                        {
                                        LED_BYTE_Buffer[memaddr] =(uint32_t)( ((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++;
                }
        MYDMA_Config(DMA1_Stream7,DMA_Channel_5,(u32)&TIM3->CCR3,(u32)LED_BYTE_Buffer,43,DMA_PeripheralDataSize_HalfWord);//DMA1,STEAM7,CH5,外设定时器3的通道3,存储器为SendBuff,长度为:SEND_BUF_SIZE.

                DMA_SetCurrDataCounter(DMA1_Stream7, buffersize);         // load number of bytes to be transferred
                DMA_Cmd(DMA1_Stream7, ENABLE);                         // enable DMA channel 6
                TIM_Cmd(TIM3, ENABLE);                                                 // enable Timer 3
                while(!DMA_GetFlagStatus(DMA1_Stream7,DMA_FLAG_TCIF7)) ;         // wait until transfer complete
                TIM_Cmd(TIM3, DISABLE);         // disable Timer 3
                DMA_Cmd(DMA1_Stream7, DISABLE);                         // disable DMA channel 6
                DMA_ClearFlag(DMA1_Stream7,DMA_FLAG_TCIF7);                                 // clear DMA1 Channel 6 transfer complete flag
}



下面是DMA的初始化函数:
#include "dma.h"                                                                                                                                                     
#include "delay.h"                 
//////////////////////////////////////////////////////////////////////////////////         
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F407开发板
//DMA 驱动代码          
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/5/6
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved                                                                          
//////////////////////////////////////////////////////////////////////////////////          


//DMAx的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_StreamxMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
//chxMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量  
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr,uint32_t DataSize)
{

        DMA_InitTypeDef  DMA_InitStructure;
       
        if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
        {
          RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
               
        }else
        {
          RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
        }
  DMA_DeInit(DMA_Streamx);
       
        while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置
       
  /* 配置 DMA Stream */
  DMA_InitStructure.DMA_Channel = chx;  //通道选择
  DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址
  DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器0地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式
  DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
  DMA_InitStructure.DMA_PeripheralDataSize = DataSize;//外设数据长度:16位   注意这里的数据长度,对于高级定时器是32个位   对于普通定时器,可能是16个字节,可能是32个字节
  DMA_InitStructure.DMA_MemoryDataSize = DataSize;//存储器数据长度:16位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ;//使用循环模式 //DMA_Mode_Normal;// 使用普通模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
  DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream
       

}
//开启一次DMA传输
//DMA_StreamxMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
//ndtr:数据传输量  
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{

        DMA_Cmd(DMA_Streamx, DISABLE);                      //关闭DMA传输
       
        while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}        //确保DMA可以被设置  
               
        DMA_SetCurrDataCounter(DMA_Streamx,ndtr);          //数据传输量  

        DMA_Cmd(DMA_Streamx, ENABLE);                      //开启DMA传输
}          

我所实现的功能是:
通过DMA来更新定时器的输出pwm占空比,从而实现控制ws2812b的功能
































zhaicool
2楼-- · 2019-07-21 00:13
本帖最后由 zhaicool 于 2018-1-2 18:33 编辑
我有一个梦想 发表于 2017-12-29 17:32
我用的是STM32F407ZET6,使用功能的是DMA传输+PWM的方式,我使用的是TIM3->CCR3,通道选择的是DMA1_Stream7 ...

请问要点亮多个led该怎么配置呢?或者我指定点亮第几颗灯代码该怎么写呢?还望不吝赐教!
颖大哥
3楼-- · 2019-07-21 04:26
 精彩回答 2  元偷偷看……
宋宋
4楼-- · 2019-07-21 05:56
后面解决了吗?我最近也在做这个,能不能指导下QQ : 634299815
宋宋
5楼-- · 2019-07-21 07:07
 精彩回答 2  元偷偷看……
seccscarc
6楼-- · 2019-07-21 13:03
 精彩回答 2  元偷偷看……

一周热门 更多>