STM32F407_HAL_TIM_DMA驱动WS2812

发布者:朱颜素韵最新更新时间:2022-08-09 来源: csdn关键字:驱动  WS2812 手机看文章 扫描二维码
随时随地手机看文章

初始化函数可以通过CubeMX配置,这里直接通过原码记录:

注意点:


STM32F407配置时钟频率为168MHz,TIM3挂载再APB1总线上时钟频率为84MHz,WS2812B需要的时钟频率为800K则定时器预装载寄存器的初值应配置为84M/800K = 105

0码和1码的配置需要根据预装载寄存器设定的初值来配置以符合WS2812手册中的0码和1码的时序。可参考:https://blog.csdn.net/xiaoyuanwuhui/article/details/99639068

芯片外设层驱动

tim.h

#ifndef __TIM_H

#define __TIM_H


#include "stm32f4xx.h"


extern TIM_HandleTypeDef htim3;


void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle);

void tim3_init(void);


#endif /* __TIM_H */


tim.c

#include "tim.h"


TIM_HandleTypeDef htim3;

DMA_HandleTypeDef hdma_tim3_ch1_trig;


void tim3_init(void)

{

  TIM_MasterConfigTypeDef sMasterConfig;

  TIM_OC_InitTypeDef sConfigOC;


  htim3.Instance = TIM3;

  htim3.Init.Prescaler = 0;

  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;

  htim3.Init.Period = 105-1;

  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)

  {

    // _Error_Handler(__FILE__, __LINE__);

  }


  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;

  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)

  {

    // _Error_Handler(__FILE__, __LINE__);

  }


  sConfigOC.OCMode = TIM_OCMODE_PWM1;

  sConfigOC.Pulse = 0;

  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;

  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)

  {

    // _Error_Handler(__FILE__, __LINE__);

  }


  HAL_TIM_MspPostInit(&htim3);

}


void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle)

{


  if(tim_pwmHandle->Instance==TIM3)

  {

    /* TIM3 clock enable */

    __HAL_RCC_TIM3_CLK_ENABLE();

    /* DMA controller clock enable */

    __HAL_RCC_DMA1_CLK_ENABLE();

  

    /* TIM3 DMA Init */

    /* TIM3_CH1_TRIG Init */

    hdma_tim3_ch1_trig.Instance = DMA1_Stream4;

    hdma_tim3_ch1_trig.Init.Channel = DMA_CHANNEL_5;

    hdma_tim3_ch1_trig.Init.Direction = DMA_MEMORY_TO_PERIPH;

    hdma_tim3_ch1_trig.Init.PeriphInc = DMA_PINC_DISABLE;

    hdma_tim3_ch1_trig.Init.MemInc = DMA_MINC_ENABLE;

    hdma_tim3_ch1_trig.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

    hdma_tim3_ch1_trig.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

    hdma_tim3_ch1_trig.Init.Mode = DMA_NORMAL;

    hdma_tim3_ch1_trig.Init.Priority = DMA_PRIORITY_MEDIUM;

    hdma_tim3_ch1_trig.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

    if (HAL_DMA_Init(&hdma_tim3_ch1_trig) != HAL_OK)

    {

      // _Error_Handler(__FILE__, __LINE__);

    }


    /* Several peripheral DMA handle pointers point to the same DMA handle.

     Be aware that there is only one stream to perform all the requested DMAs. */

    __HAL_LINKDMA(tim_pwmHandle,hdma[TIM_DMA_ID_CC1],hdma_tim3_ch1_trig);

    __HAL_LINKDMA(tim_pwmHandle,hdma[TIM_DMA_ID_TRIGGER],hdma_tim3_ch1_trig);


    /* TIM3 interrupt Init */

    HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);

    HAL_NVIC_EnableIRQ(TIM3_IRQn);

    

    /* DMA interrupt init */

    /* DMA1_Stream4_IRQn interrupt configuration */

    HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);

    HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);


  }

}


void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)

{

  GPIO_InitTypeDef GPIO_InitStruct;

  if(timHandle->Instance==TIM3)

  {

    /* GPIOA clock enable */

    __HAL_RCC_GPIOA_CLK_ENABLE();

  

    /**TIM3 GPIO Configuration    

    PA6     ------> TIM3_CH1 

    */

    GPIO_InitStruct.Pin = GPIO_PIN_6;

    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

    GPIO_InitStruct.Pull = GPIO_NOPULL;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;

    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  }

}


void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef* tim_pwmHandle)

{

  if(tim_pwmHandle->Instance==TIM3)

  {

    /* Peripheral clock disable */

    __HAL_RCC_TIM3_CLK_DISABLE();


    /* TIM3 DMA DeInit */

    HAL_DMA_DeInit(tim_pwmHandle->hdma[TIM_DMA_ID_CC1]);

    HAL_DMA_DeInit(tim_pwmHandle->hdma[TIM_DMA_ID_TRIGGER]);


    /* TIM3 interrupt Deinit */

    HAL_NVIC_DisableIRQ(TIM3_IRQn);

  }


/**

* @brief This function handles DMA1 stream4 global interrupt.

*/

void DMA1_Stream4_IRQHandler(void)

{

  HAL_DMA_IRQHandler(&hdma_tim3_ch1_trig);

}


/**

* @brief This function handles TIM3 global interrupt.

*/

void TIM3_IRQHandler(void)

{

  HAL_TIM_IRQHandler(&htim3);

}


WS2812硬件驱动

ws281x.h

#ifndef __WS281X_H

#define __WS281x_H


#include "tim.h"


void ws281x_init(void);

void ws281x_show(void);

uint32_t ws281x_color(uint8_t r, uint8_t g, uint8_t b);

void ws281x_setColor(uint16_t n, uint32_t color);

void ws281x_close(void);


#endif /* __WS281X_H */


ws281x.c

#include "WS281x.h"

#include


#ifndef PIXEL_NUM

  #define PIXEL_NUM 5

#endif

#define GRB (3*8)


#define WS_TERO 30

#define WS_ONE  45


/* pwm 占空比数值为uint16_t 类型,DMA传输时只能以半字输出,pixelBuffer应为uint16_t 类型 */

uint16_t pixelBuffer[PIXEL_NUM][GRB]; 


void ws281x_init(void)

{

  /* 配置TIM3 pwm频率为800Khz */

  tim3_init();

}


void ws281x_show(void)

{

  HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t *)pixelBuffer, PIXEL_NUM * GRB);

}


uint32_t ws281x_color(uint8_t r, uint8_t g, uint8_t b)

{

  return (uint32_t)(g << 16 | r << 8 | b);

}


/* pixel num from 0 start */

void ws281x_setColor(uint16_t n, uint32_t color)

{

  if(n < PIXEL_NUM)

  {

    for(uint8_t i = 0; i < GRB; ++i)

    {

      pixelBuffer[n][i] = ((color << i) & 0x800000) ? WS_ONE : WS_TERO;

    }

  }

}


void ws281x_close(void)

{

  uint16_t* ptr = (uint16_t *)pixelBuffer; 

  for(uint16_t i = 0; i < PIXEL_NUM * GRB; ++i)

  {

    *ptr++ = WS_TERO;

  }

  ws281x_show();

}


/* 中断回调函数,在设定的pwm通过DMA发送完成后会调用 */

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef* htim)

{

  __HAL_TIM_SetCompare(htim, TIM_CHANNEL_1, 0); //占空比清0,若不清会导致灯珠颜色不对 

  HAL_TIM_PWM_Stop_DMA(htim, TIM_CHANNEL_1);

}


结语

这里只实现了底层驱动接口,未移植显示效果部分的代码,显示效果可参考我的其他WS2812相关文章。HAL库分层效果确实好,但内部代码的理解难度要高很多

关键字:驱动  WS2812 引用地址:STM32F407_HAL_TIM_DMA驱动WS2812

上一篇:彻底搞清printf在STM32上的使用
下一篇:STM32HAL库串口处理---中断收发

推荐阅读最新更新时间:2024-11-13 19:04

英思嘉半导体业界领先的53Gbaud DML Driver开始提供样片
导读 成都英思嘉半导体宣布其业界领先的53Gbaud DML Driver开始提供样片。该芯片可被广泛用于各种单波100Gbs产品,涵盖100Gbs到800Gbs应用。 随着5G、大数据、人工智能的快速发展,数据中心对带宽的需求与日俱增。数据中心内部连接速率不断提高,需要光模块支持更高的单波速率,更低的功耗,更低的成本。 当前市场100G DR1和400G DR4光模块产品主要基于EML激光器或硅光调制器技术架构。英思嘉53Gbaud DML Driver(ISG-D5619)是将DML用于更高波特率应用的关键技术突破,搭配DFB激光器,可实现直驱单波100Gbs应用,为更低功耗、更低成本的53Gbaud应用铺平
[网络通信]
英思嘉半导体业界领先的53Gbaud DML <font color='red'>Driver</font>开始提供样片
采埃孚中央电驱动系统CeTrax荣获世界客车博览会创新大奖
来自德国腓德烈斯哈芬/比利时布鲁塞尔的消息。采埃孚凭借其研发的 中央电驱动系统CeTrax在世界客车博览会上获得创新大奖。该系统有助 于公共交通运营客车实现零排放。经过激烈的角逐,采埃孚电驱动技术 在汽车部件和相关领域组别的评比中脱颖而出,最终获得创新大奖。 图注: 采埃孚公交客车和公路客车车桥与变速器系统负责人Andreas Gossl博士,在布鲁塞尔的颁奖典礼上,接受了世界客车博览会对采埃孚中央电 驱动系统CeTrax颁发的创新大奖。 在布鲁塞尔举行的世界客车博览会开幕的前夜,采埃孚的中央电驱动系 统CeTrax击败了参加汽车部件和相关领域组别评选的其他众多产品,赢 得了由专家评审团的一致认可,获得了创新大奖。
[汽车电子]
采埃孚中央电<font color='red'>驱动</font>系统CeTrax荣获世界客车博览会创新大奖
奥迪A8搭载预测式主动悬架 视情况通过机电驱动调节车辆高度
(图源:奥迪官网) 据外媒报道,新奥迪A8豪华轿车搭载预测式主动悬架。通过机电驱动器调节悬架,可以单独升降每个车轮,在各种情况下,都能调节车身行驶高度。 奥迪A8的预测悬架与前置摄像头协同工作,能够提前检测到不平整地面,预先调整悬架,实现最大的舒适性。摄像头以每秒18次的速度收集前方路面信息,而主动悬架能相应地在0.5秒内将车身降低或抬起85毫米。奥迪A8的每个车轮附近都安装了紧凑型电机,由电力电子设备控制,运行在汽车的48伏主电力系统上。通过皮带驱动和紧凑的谐波驱动,电机的扭矩将提高近200倍至 1,100 Nm,并应用于钢旋转管。后者永久性连接到预先加载的内部钛棒,并能够旋转超过20度。力从旋转管的末端,通过杠杆
[汽车电子]
奥迪A8搭载预测式主动悬架 视情况通过机电<font color='red'>驱动</font>调节车辆高度
高亮度LED应用为LED驱动器集成电路带来新的机会和挑战
目前,对高亮度 LED(发光二极管)的市场预测存在很大差别。尽管预测数据不同,但是趋势是明显的:高亮度(HB)LED 市场正在以惊人的速度增长。有些预测数据为年复合增长率 15%,另一些则为年复合增长率 35%。2007 年汽车领域的 LED 照明处于起步阶段,销售额为 6.7 亿美元,但是预计将以 15% 的年复合增长率增长,到 2011年(1)达到 12 亿美元。如果将非汽车应用考虑在内,那么年复合增长率接近 35%,而且到 2011 年,总的高亮度 LED 市场有可能轻松超过 25 亿美元。 这么高的增长潜力靠什么驱动?首先,LED 产生光的效率是白炽灯的 10 倍,同时几乎是荧光灯的两倍,因此极大地降低了提供特定光输出量(
[电源管理]
高亮度LED应用为LED<font color='red'>驱动</font>器集成电路带来新的机会和挑战
详解户外LED驱动电源的防雷要求
户外照明在国内外众多的示范工程中得到广泛应用,基于其苛刻的使用环境,户外LED照明驱动电源在设计时必须要重点评估诸如因防水、高温、雷击等因素而引发的问题。本文将针对其中的防雷要求作重点讨论。   雷击是一种常见的自然现象,特别是在雨季尤为常见。其所带来的危害和损失全球每年以千亿美元来计。雷击分为直接雷和间接雷,间接雷主要包括传导雷和感应雷。由于直接雷所带来的能量冲击非常大,破坏力极强,一般电源是无法承受的。故本文要讨论的是指间接雷,包括传导雷和感应雷二种雷击类型。   雷击所形成的浪涌冲击是一种瞬态波,是一种瞬变干扰,可以是浪涌电压也可以是浪涌电流。沿着电源线或其它路径(传导雷)或通过电磁场(感应雷)而传送至电源线路。其波形特征是
[电源管理]
使用ATtiny85单片机驱动四个RGB LED指示灯
该应用程序非常简单,主要展示了如何使用 ATtiny85单片机 驱动四个RGB LED指示灯。 每个LED可以设置为16个不同等级之一,从关闭到全亮,电路中预留一个I / O引脚用于其他应用。 电路 以下是本文采用的电路: 该应用程序的主要工作原理是您可以通过使用Charlieplexing的方式利用四个I/O线驱动12个LED。下表显示了当您将一个I / O线设置为高电平,另一个I / O线设置为低电平时,哪个RGB LED指示灯会被点亮: 指示灯与普通的阴极RGB LED封装兼容;我使用了低成本的四引线共阴极5mm RGB LED,但您可以使用任何类型的RGB LED。 显示复用 使用Timer
[单片机]
使用ATtiny85单片机<font color='red'>驱动</font>四个RGB LED指示灯
Tiny210驱动之按键poll机制
forth_drv.c驱动源码: #include linux/device.h #include linux/interrupt.h #include linux/module.h #include linux/kernel.h #include linux/fs.h #include linux/init.h #include linux/delay.h #include linux/irq.h #include asm/uaccess.h #include asm/irq.h #include asm/io.h #include mach/gpio.h #include linux/poll.h s
[单片机]
DS18B20测温51驱动程序
#include AT89X52.h sbit DQ = P3^4; //定义DS18B20总线I/O //16进制转10进制数 uchar code ditab = {0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x04,0x05,0x06,0x06,0x07,0x08,0x08,0x09,0x09}; void Delay_DS18B20(int num) { while(num--) ; } void Init_DS18B20(void) { unsigned char x=0; DQ = 1; //DQ复位 Delay_DS18B20(8); //
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved