基于stm32f407VGT6控制WS2812的TIM1+PWM+DMA实现方式

项目中使用到了ws2812灯带,作为产品的外观显示灯,经过开发之后,整理一下,仅供大家参考。

WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源。 其外型与一个5050 LED灯珠相同, 每个元件即为一个像素点。 像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路, 还包含有高精度的内部振荡器和可编程定电流控制部分, 有效保证了像素点光的颜色高度一致。数据协议采用单线归零码的通讯方式, 像素点在上电复位以后, DIN端接受从控制器传输过来的数据, 首先送过来的24bit数据被第一个像素点提取后, 送到像素点内部的数据锁存器, 剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的像素点, 每经过一个像素点的传输, 信号减少24bit。 像素点采用自动整形转发技术, 使得该像素点的级联个数不受信号传送的限制, 仅受限信号传输速度要求。高达2KHz的端口扫描频率, 在高清摄像头的捕捉下都不会出现闪烁现象,非常适合高速移动产品的使用。280μs以上的RESET时间, 出现中断也不会引起误复位, 可以支持更低频率、 价格便宜的MCU。LED具有低电压驱动、 环保节能、 亮度高、 散射角度大、 一致性好超、 低功率及超长寿命等优点。 将控制电路集成于LED上面, 电路变得更加简单, 体积小, 安装更加简便。

WS2812B主要特点:
● IC控制电路与LED点光源共用一个电源。
● 控制电路与RGB芯片集成在一个5050封装的元器件中, 构成一个完整的外控像素点。
● 内置信号整形电路, 任何一个像素点收到信号后经过波形整形再输出, 保证线路波形畸变不会累加。
● 内置上电复位和掉电复位电路。
● 每个像素点的三基色颜色可实现256级亮度显示, 完成16777216种颜色的全真色彩显示。
● 端口扫描频率2KHz/s。
● 串行级联接口, 能通过一根信号线完成数据的接收与解码。
● 当刷新速率30帧/秒时, 级联数不小于1024点。
● 数据发送速度可达800Kbps。
● 光的颜色高度一致, 性价比高。
● 电源反接不会损坏。
● 外围不需要包含电容在内的所有任何电子元器件。

首先看一下其实现原理吧,对于应用者来说,硬件电路设计只需要三根线即可(5V电源线,地线,IO数据线)。其电路模型也很简单,如下:
在这里插入图片描述
对于嵌入式程序开发者来说也比较简单,仅仅控制IO数据线就可以了,但是里要控制这个神奇的灯的颜色,这就需要对其数据传输的要求有所了解了。
在这里插入图片描述
在这里插入图片描述
如上图所示,当数据线传入灯带后,第一个灯珠截取第一个24位数据留做己用,而后会将其余的数据进行整形后发送给第二颗灯珠,第二颗灯珠会依次截取24位数据并对剩余数据进行整形发送,以此类推。直到最后一组数据被显示为止。每一组24位数根据数据的不同显示不同的颜色和亮度(每个像素点的三基色颜色可实现256级亮度显示, 完成16777216种颜色的全真色彩显示)。
再来看一看每个灯的24位数据,其每一位数据只能是0或1,但是其电位时序室友要个要求的,如下:
在这里插入图片描述在这里插入图片描述
根据其电位控制要求,我们可以通过软件实现IO控制模拟0码和1码的实现。并进行数据传输。

说完了灯珠控制原理,再来谈一谈控制平台,众所周知,stm32系列的内核速率有限,考虑到指令周期的严格性,采用IO电平反转的方式来实现灯珠码型是不可靠,不现实的。

考虑到项目中实际应用的灯珠数量是比较大的(我的项目是使用270个灯珠,约4.5米的灯带),每个灯珠24bit数据,数据量每次传输量即为 270 * 24 / 8 = 810Bytes。传送800Bytes数据对于stm32来说并不苦难,但是频繁的传送可能会导致其不可靠行增大。

所以在项目中采用了TIM1+PWM+DMA的控制方式。设计思路是这样的,根据其特性可知数据发送速率最大达800kbps,根据此速率计算其周期为T= 1.25us。通过定时器实现其周期,再利用PWM实现其0码和1码,在通过DMA将数据传送出去。

废话不多说,看代码。

首先看一下定时器的初始化:

void time1andPwmInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

    /* GPIO remap to TIM1*/
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_TIM1);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
    GPIO_Init(GPIOE, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOC, GPIO_Pin_13);


    /* timer period : T =(arr + 1) * (PSC + 1) / Tck.   arr: period value PSC:prescaler value  Tck: system clock */
    TIM_TimeBaseStructure.TIM_Period = 210 - 1;                     /* T = (TIM_Period + 1)*(0+1)/168M  = 800kHz*/
    TIM_TimeBaseStructure.TIM_Prescaler = 0;                        /* 0 */
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;         /* 0 */
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    /* PWM1 Mode configuration: Channel1 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;                                  /* 1 ~ TIM_TimeBaseStructure.TIM_Period */
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;    //TIM_OutputNState_Enable;
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCNIdleState_Set;
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

    TIM_Cmd(TIM1, ENABLE);
    TIM_CtrlPWMOutputs(TIM1,ENABLE);
}

在我的工程中,我采用的是stm32f407VGT6,其内核时钟为168M,可根据时钟更改210这个值。采用的IO数据端口为E9作为TIM1_CH1输出,将其配置为PWM输出。

再来看一下DMA模块初始化:

#define TIM1_CCR1_Address   (TIM1_BASE + 0x34)    //0x40001034

void dmaInit(void)
{
    DMA_InitTypeDef DMA_InitStructure;

    /* DMA clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

    DMA_DeInit(DMA2_Stream6);

    /* DMA2 Stream6 Config for PWM1 by TIM1_CH1*/
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM1_CCR1_Address;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)g_ledDataBuffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize = 42;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    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(DMA2_Stream6, &DMA_InitStructure);

    /* TIM1 DMA Request enable */
    TIM_DMACmd(TIM1, TIM_DMA_CC1, ENABLE);

}

参考数据手册有关DMA模块可知。
在这里插入图片描述
TIM1_CH1外设只能使用DMA2_Stream6_Ch0和DMA2_Stream1_Ch6,本项目采用前者。对于此配置,最重要的是就是以下三点:
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM1_CCR1_Address; //外设基地址
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)g_ledDataBuffer; //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //传输方式:内存到映射

根据TIM1的PWM输出原理,可知我们此时要传输的外设地址为TIM1_CCR1寄存器地址。DMA内存地址即是我们传输数据的首地址。

最后看一看数据发送模块的代码:

#define TIMING_ONE          (143)
#define TIMING_ZERO         (67)

uint8_t rgbRed[][3] = {{0xff, 0x00, 0x00}};

void ledSingleShow(uint8_t (*color)[3], uint16_t len)
{
    uint8_t i = 0;
    uint16_t memaddr = 0;
    uint16_t buffersize = 0;

    buffersize = (len * 24) + 1;       // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes
    memaddr = 0;                        // reset buffer memory index

    while (len)
    {
        /*  green data */
        for(i = 0; i < 8; i++)
        {
            g_ledDataBuffer[memaddr] = ((color[0][1] << i) & 0x0080) ? TIMING_ONE : TIMING_ZERO;
            memaddr++;
        }

        /*  red data */
        for(i = 0; i < 8; i++)
        {   
            g_ledDataBuffer[memaddr] = ((color[0][0] << i) & 0x0080) ? TIMING_ONE : TIMING_ZERO;
            memaddr++;
        }

        /*  blue data */
        for(i = 0; i < 8; i++)
        {
            g_ledDataBuffer[memaddr] = ((color[0][2] << i) & 0x0080) ? TIMING_ONE : TIMING_ZERO;
            memaddr++;
        }

        len--;
    }

    DMA_SetCurrDataCounter(DMA2_Stream6, buffersize);
    TIM_Cmd(TIM1, ENABLE);
    TIM_DMACmd(TIM1, TIM_DMA_CC1, ENABLE);
    DMA_Cmd(DMA2_Stream6, ENABLE);
    while(!DMA_GetFlagStatus(DMA2_Stream6, DMA_FLAG_TCIF6));
    DMA_Cmd(DMA2_Stream6, DISABLE);
    DMA_ClearFlag(DMA2_Stream6, DMA_FLAG_TCIF6);
    TIM_Cmd(TIM1, DISABLE);

}

上述代码实际包含两部分功能,数据组成和DMA传送。
根据传入参数格式可知,参数为灯珠颜色的数组,根据其数组各元素定义,重新定义DMA要发送的内存数据。而后将DMA发送功能打开。 其实此时我们只是将不同的占空比系数发送给了TIM1->CCR1寄存器,TIM1的周期为1.25us,再此周期中,通过占空比,会实现led灯珠的0码和1码。从而实现led显示。

#define TIMING_ONE (143)
#define TIMING_ZERO (67)
至于这两个宏定义,大家根据led灯的时序波形图计算记得得出。

经过过这个小东西,发现是stm32的强大之处还是很多的,需要再接再厉。

  • 11
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
### 回答1: 基于STM32F407VGT6的电子秤是一种集成了STM32F407芯片的计量设备,可用于测量物体的重量。该芯片是一款高性能的32位ARM Cortex-M4处理器,拥有丰富的外设和高速运算能力,非常适合用于开发电子秤。 电子秤的工作原理是通过称重传感器测量物体的重量,并转换为电信号后,由STM32F407芯片进行处理和显示。秤面上通常会配备一个液晶显示屏,用于显示物体的重量数值。STM32F407芯片通过使用ADC(模数转换器)将传感器测得的电信号转换为数字量,然后经过一系列运算和处理,最终转换为重量数值并显示在液晶屏上。 基于STM32F407的电子秤具有很多优势。首先,由于其高性能的处理能力,可以实现快速精确的重量测量。其次,由于该芯片具有丰富的外设和多个IO口,可以方便地接入其他模块和传感器,如温度传感器、湿度传感器等,从而实现更多功能和监测。另外,STM32F407芯片也具有低功耗特性,能够有效延长电子秤的使用寿命。 尽管基于STM32F407芯片的电子秤具有许多优势,但在设计和制造过程中仍需注意一些问题。首先,需要合理选择和安装称重传感器,以确保测量结果的准确性。其次,要注意通信和数据传输的稳定性,以保证重量数据的传输和显示的可靠性。另外,为了提高用户体验,还需考虑设计一个友好的人机界面和操作方式。 总体而言,基于STM32F407VGT6的电子秤是一种高性能、精确可靠的计量设备。通过合理设计和制造, 可以满足各种应用领域对重量测量的需求。 ### 回答2: 基于stm32f407vgt6的电子秤是一种利用stm32f407vgt6控制器芯片设计和制造的测量质量的装置。它具有高精度、稳定性好、功耗低等优点。 首先,stm32f407vgt6是一款性能强大的微控制器芯片,具有高速处理能力和多种外设接口。借助这款芯片,电子秤能够实现高精度的重量测量和数据处理。并且,该芯片具备较低的功耗特性,可以提高电子秤的省电性能。 其次,基于stm32f407vgt6的电子秤的硬件设计和封装结构也是非常重要的。电子秤通常采用电子传感器来测量物体的质量,通过与stm32f407vgt6控制器芯片进行连接和通信,将传感器采集到的数据进行处理,并将结果显示在数管或者液晶显示屏上。 此外,基于stm32f407vgt6的电子秤在软件方面的设计也至关重要。软件方面的设计包括驱动程序的编写、数据解析算法的实现以及通信协议的开发等。通过合理的软件设计,可以实现电子秤的稳定工作和准确的质量测量。 综上所述,基于stm32f407vgt6的电子秤具有高精度、稳定性好、功耗低等优点。它的设计和制造依赖于强大的stm32f407vgt6控制器芯片,以及合理的硬件结构和软件设计。这种电子秤可以广泛应用于商业、工业和家庭等领域,满足人们对质量测量的需求。 ### 回答3: 基于STM32F407VGT6的电子秤是一种使用STM32F407VGT6控制器作为核心控制器的电子称重设备。这款电子秤能够实时测量物体的重量,并且具备一系列高性能的功能和特点。 首先,STM32F407VGT6控制器是一款基于ARM Cortex-M4内核的低功耗高性能微控制器,具有高速处理器和丰富的外设资源,能够满足电子秤对于数据处理和精准度的要求。其高速处理器能够快速计算出被称重物体的重量,并且准确度高,能够满足电子秤对于准确称重的需求。 其次,基于STM32F407VGT6的电子秤能够通过数字信号处理技术,将模拟重量信号转换为数字信号,并且通过AD转换器将其转化为数字量,以满足数字信号的处理和存储需求。通过精心设计的电路和算法,可以保证电子秤的称量准确度和稳定性。 此外,基于STM32F407VGT6的电子秤还具备一系列人性化的功能和接口。比如,它可以通过LCD显示屏实时显示被称重物体的重量,便于用户观察和记录。同时,它还可通过串口或其他通信接口与其他设备进行数据交换和远程监控,提高了电子秤的实用性和便利性。 总之,基于STM32F407VGT6的电子秤采用了先进的处理器和数字信号处理技术,具备高精度、稳定性强、功能丰富等优点,可以广泛应用于各个领域,如商业、家庭、工业等,满足不同用户的需求。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我若成精

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

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

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

打赏作者

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

抵扣说明:

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

余额充值