本帖最后由 笨鸟先飞鱼 于 2016-7-5 16:14 编辑
控制ws2812 ,单线归零协议,相同周期,不同的高低电平时间,代表“1”和“0”。
现在用stm32 f407 控制ws2812灯珠, 采用网上别人所说的,用DMA传输+PWM的方式, 就是用DMA传输,传过来不同的占空比值,也就是DMA传过来的值,控制不同的CCR1的值,
我现在最大的疑惑在于,如何实现同步?
如何恰好在pwm走完一个周期时,pwm的占空比被改变?
请大神指点下,如何用DMA+PWM的方式,来实现具体的某个波形的占空比是由自己控制的。或者谁有f407控制ws2812灯珠的程序,发一份也行。 (自己用软件延时的就不需要了)
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
下面是程序
#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的功能
请问要点亮多个led该怎么配置呢?或者我指定点亮第几颗灯代码该怎么写呢?还望不吝赐教!
一周热门 更多>