说明

  1. 本文描述的驱动原理是通用的,但是下文的初始化代码只供参考。

资料下载

RGB_LED灯带_5050慢闪_datasheet

STM32控制LED灯带

根据上面的说明书可知,通过修改800KHz的PWM波形的占空比可以控制LED的颜色。
假设现在有3颗串联起来的灯珠,如下图:


如果U1/U2/U3需要显示红/绿/蓝色,根据说明书,需要从DIN发送0x00FF00_FF0000_0000FF(高位先发)。

假设现在U1/U2/U3需要的颜色为从DIN发送0x123456_789ABC_DEF0AA(高位先发),则我们需要从DIN输入如下脉冲:
从上面的PWM波形可以看出,PWM的频率一直都是800KHz(周期为1.25us),但是占空比一直在32%(T0)68%(T1)两个值中变化。
我们平时使用STM32PWM输出时,都是设置一个占空比,在有需要的时候再修改占空比,但是这种方式并不能保证每个PWM波形后都更新一次占空比,那怎么办呢?
这就需要使用到我们的HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length);或者HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length);函数了(具体使用哪个,根据占空比需要的高低电平状态决定)。这个函数通过DMA的方式,指定某个定时器的某一通道,发送LengthPWM波形,每个PWM波形高电平的计数值(相当于占空比)存储在pData数组中,当然使用前需要初始化定时器以及相应的DMA外设。

DMA初始化

void Tim8DMAInit(void)
{static DMA_HandleTypeDef hdma_tim_A;__HAL_RCC_DMA2_CLK_ENABLE();/* config led A DMA stream */hdma_tim_A.Init.Channel  = DMA_CHANNEL_7;hdma_tim_A.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_tim_A.Init.PeriphInc = DMA_PINC_DISABLE;hdma_tim_A.Init.MemInc = DMA_MINC_ENABLE;hdma_tim_A.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;hdma_tim_A.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;hdma_tim_A.Init.Mode = DMA_NORMAL;hdma_tim_A.Init.Priority = DMA_PRIORITY_HIGH;hdma_tim_A.Init.FIFOMode = DMA_FIFOMODE_DISABLE;hdma_tim_A.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;hdma_tim_A.Init.MemBurst = DMA_MBURST_SINGLE;hdma_tim_A.Init.PeriphBurst = DMA_PBURST_SINGLE; /* Set hdma_tim_A instance */hdma_tim_A.Instance = DMA2_Stream4;/* Link hdma_tim_A to hdma[3] (channel3) */__HAL_LINKDMA(&Tim8PwmHandle.TimHandleInit, hdma[3], hdma_tim_A);  //@fixme/* Initialize TIMx DMA handle */HAL_DMA_Init(Tim8PwmHandle.TimHandleInit.hdma[3]);   //@fixme/*##-4- Configure the NVIC for DMA #########################################*/HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 15, 0);   HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);BSP_IntVectSet(DMA2_Stream4_IRQn, DMA2_Stream4_IRQHandler);
}

TIM初始化

void Tim8PwmConfig(void)
{Tim8PwmHandle.TimHandleInit.Instance               = TIM8;Tim8PwmHandle.TimHandleInit.Init.Prescaler          = 1;       // 系统时钟到TIM的分频系数,由于TIM8是挂在APB2总线上,而APB2总线最高时钟不能超过90MHz,系统时钟配置为168MHz,所以设置为1分频,即TIM8的时钟为84MHz。Tim8PwmHandle.TimHandleInit.Init.Period             = 105 - 1; // PWM周期,105 * 800KHz = 84MHzTim8PwmHandle.TimHandleInit.Init.RepetitionCounter     = 0;    // 不需要重加载Tim8PwmHandle.TimHandleInit.Init.ClockDivision        = 0;    // 定时器时钟不需要分频Tim8PwmHandle.TimHandleInit.Init.CounterMode          = TIM_COUNTERMODE_UP;   // 向上计数Tim8PwmHandle.TimClkEnable                          = Tim8ClkEnable;        // 使能定时器时钟的函数指针Tim8PwmHandle.PwmChannel1.EnableFlag                = false;Tim8PwmHandle.PwmChannel2.EnableFlag               = false;Tim8PwmHandle.PwmChannel4.EnableFlag               = false;/** channel 3 使能 **/Tim8PwmHandle.PwmChannel3.EnableFlag               = true;Tim8PwmHandle.PwmChannel3.GpioClkInit               = Tim8Channel3ClkEnable;    // 通道1的GPIO时钟使能Tim8PwmHandle.PwmChannel3.GpioInit.Pin              = GPIO_PIN_15;Tim8PwmHandle.PwmChannel3.GpioInit.Mode              = GPIO_MODE_AF_PP;Tim8PwmHandle.PwmChannel3.GpioInit.Pull              = GPIO_PULLDOWN;Tim8PwmHandle.PwmChannel3.GpioInit.Speed           = GPIO_SPEED_FAST;Tim8PwmHandle.PwmChannel3.GpioInit.Alternate     = GPIO_AF3_TIM8;Tim8PwmHandle.PwmChannel3.GpioPort                 = GPIOH;Tim8PwmHandle.PwmChannel3.OConfig.OCMode           = TIM_OCMODE_PWM1;Tim8PwmHandle.PwmChannel3.OConfig.OCNPolarity        = TIM_OCNPOLARITY_HIGH;Tim8PwmHandle.PwmChannel3.OConfig.OCFastMode        = TIM_OCFAST_ENABLE;Tim8PwmHandle.PwmChannel3.OConfig.OCIdleState       = TIM_OCIDLESTATE_RESET;Tim8PwmHandle.PwmChannel3.OConfig.OCNIdleState      = TIM_OCNIDLESTATE_SET;Tim8PwmHandle.PwmChannel3.OConfig.Pulse               = 0;if(BspPwmHandleInit(&Tim8PwmHandle) != HAL_OK){}  // todoTim8DMAInit();
}

LED颜色控制逻辑

#define PWM_LOW     34          // 根据LED的datasheet,T0的占空比为'0.4/1.25=32%',所以需要'105*32%=34'个计数
#define PWM_HIGH    71          // 根据LED的datasheet,T1的占空比为'0.85/1.25=68%',所以需要'105*68%=71'个计数
bool ledAFinishFlag = true;     // LED颜色控制的PWM波形是否已经发送完成
typedef struct
{u32 LedMaxNum;         // LED串联的个数u32 *LedInfoBuff;        // LED色素缓存数组,每个灯需要24位空间(实际分配位u32类型),所以这个指针指向"u32 LedInfoBuff[LedMaxNum]"类型u32 *PWMValueBuff;        // 有LedMaxMum个灯,每个灯有24位表示颜色,每个位都对应了一个PWM,而这个PWM的占空比是一个u32类型的值,所以这个指针指向"u32 PWMValueBuff[LedMaxNum*24]"类型
} LEDHandleDef;BspLEDStatusTypeDef PWMValueGet(LEDHandleDef *LEDHandle)
{u32 i, j;if(LEDHandle == NULL)return LED_HANDLE_ERR;for(i = 0; i < LEDHandle->LedMaxNum; i++){for(j = 0; j < 24; j++){if(LEDHandle->LedInfoBuff[i] & (0x00800000u >> j))  // 根据某一位的值设置相应的占空比计数LEDHandle->PWMValueBuff[i*24 + j] = PWM_HIGH;elseLEDHandle->PWMValueBuff[i*24 + j] = PWM_LOW;}}return LED_GET_DATA_OK;
}BspLEDStatusTypeDef ledDealA(LEDHandleDef *LEDHandle)
{u32 i;while(ledAFinishFlag != true)   // 等待上次LED颜色发送完成,这个值在void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)函数中赋值为true{}// 将所有灯的颜色都设置为 0x000000 颜色for(i = 0; i < LEDHandle->LedMaxNum * 24; i++){LEDHandle->PWMValueBuff[i] = PWM_LOW;}// if(  HAL_TIM_PWM_Start_DMA(&Tim8PwmHandle.TimHandleInit, TIM_CHANNEL_3, (u32*)LEDHandle->PWMValueBuff, LEDHandle->LedMaxNum * 24) != HAL_OK)if(  HAL_TIMEx_PWMN_Start_DMA(&Tim8PwmHandle.TimHandleInit, TIM_CHANNEL_3, (u32*)LEDHandle->PWMValueBuff, LEDHandle->LedMaxNum * 24) != HAL_OK)return LED_HAL_ERR;elseledAFinishFlag = false;while(ledAFinishFlag != true)   // 等待LED颜色重置完成{}// 根据每个LED的颜色值计算其占空比的计数值PWMValueGet(&LEDHandleA);// 按需求设置LED颜色// if(  HAL_TIM_PWM_Start_DMA(&Tim8PwmHandle.TimHandleInit, TIM_CHANNEL_3, (u32*)LEDHandle->PWMValueBuff, LEDHandle->LedMaxNum * 24) != HAL_OK)if(  HAL_TIMEx_PWMN_Start_DMA(&Tim8PwmHandle.TimHandleInit, TIM_CHANNEL_3, (u32*)LEDHandle->PWMValueBuff, LEDHandle->LedMaxNum * 24) != HAL_OK)   return LED_HAL_ERR;elseledAFinishFlag = false;return LED_DEAL_OK;
}void SetLedPartAColor(u8 ledRed, u8 ledGreen, u8 ledBlue)
{u8 j = 0;u32 uTmpColor = 0;uTmpColor = ((ledGreen<<16) | (ledRed<<8) | (ledBlue));     // 根据datasheet,给LED发送的颜色顺序是GRBfor(j = 0; j < LED_NUM; j++){gLedAInfoBuff[j] = uTmpColor;}LEDHandleA.LedMaxNum = LED_NUM;LEDHandleA.LedInfoBuff = gLedAInfoBuff;LEDHandleA.PWMValueBuff = gLedAPwmvalBuff;ledDealA(&LEDHandleA);
}/*
* @brief:   Led灯带颜色设置
* @input:   color[31-24] - don't care
*           color[23-16] - R
*           color[15-8]  - G
*           color[7-0]   - B
* @return:  None
*/
static void LedStripePartAColorSet(const u32 color)
{u8  R = 0, G = 0, B = 0;R = (u8)((color>>16) & 0xFF);G = (u8)((color >> 8) & 0xFF);B = (u8)((color ) & 0xFF);SetLedPartAColor(R, G, B);
}void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM8){if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1){
//          HAL_TIM_PWM_Stop_DMA(&Tim8PwmHandle.TimHandleInit, TIM_CHANNEL_1);HAL_TIMEx_PWMN_Stop_DMA(&Tim8PwmHandle.TimHandleInit, TIM_CHANNEL_1);ledFinishFlag = true;Tim8PwmHandle.TimHandleInit.Instance->CCR1  = 0;Tim8PwmHandle.TimHandleInit.Instance->EGR = TIM_EGR_UG;}if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3){
//          HAL_TIM_PWM_Stop_DMA(&Tim8PwmHandle.TimHandleInit, TIM_CHANNEL_3);HAL_TIMEx_PWMN_Stop_DMA(&Tim8PwmHandle.TimHandleInit, TIM_CHANNEL_3);ledAFinishFlag = true;Tim8PwmHandle.TimHandleInit.Instance->CCR3 = 0;Tim8PwmHandle.TimHandleInit.Instance->EGR = TIM_EGR_UG;}       }
}

通过PWM控制串行LED灯相关推荐

  1. 用3个IO口控制6个LED灯,怎么做到的?查理复用!

    ▲ 本文要分析的电路 事情是这样开始的. 买了个电动牙刷,几十块钱那种: 收到的实物长这样: 牙刷手柄上有1个按键和6个LED灯: 拆开看看电路板: 可以看出,电路板上用的单片机,只有8个脚,却要控制 ...

  2. 支付宝小程序控制硬件②】 全网首篇,个人支付宝小程序控制智能硬件esp8266,从设计电路到设计协议控制两盏LED灯调节亮度。

    本系列属于支付宝小程序控制智能硬件 esp8266等芯片的思路编程,欢迎大家点点手指关注我半颗心脏,博客文章列表干货多多,有任何疑问评论区留言,第一时间看到回复! [支付宝小程序控制硬件①] 申请个人 ...

  3. 控制三色LED灯的闪烁——Arduino

    最近要弄下Arduino.动过手,知道问题,细节可能在哪. 知识点我不清楚,直接看程序.直接控制也相对简单. 程序 /*实战案例1:控制三色LED灯的闪烁2019/3/12 */ int redpin ...

  4. 单片机两个IO口控制三个LED灯

    在项目中经常会遇到单片机IO口资源不够用的情况,那么如何让单片机的IO口利用最大化呢,这里分享一下用单片机两个IO口控制三个LED灯的几种方法. 方法一:      S1和S2分别接单片机两个IO口, ...

  5. 【实战】物联网安防监控项目【4】———从网页上控制A9的LED灯

    前言 学习了一个新知识,当然要记录一下啦.这两天学习了boa服务器.cgic标准库和html标签语言,又双叕解锁一个嵌入式的新玩法.cgic库是沟通C语言和html网页编程语言的一座桥梁,通过在lin ...

  6. 【TB-02模组专题⑤】微信小程序通讯TB02 模块控制 STM32 单片机LED灯

    本<安信可ble mesh蓝牙模组TB-02模组专题>系列博客学习由官方博客 CSDN安信可博客 潜心所力所写.如有不对之处,请留言,我们及时更改. 1.BLE MESH开发环境linux ...

  7. 安防监控实现之从网页上控制A9的LED灯

    文章目录 声明 实验整体框架图: 网页发送数据模拟控制Ubuntu的LED灯 网页发送数据控制A9的LED灯 声明 华清远见教育集团 15年专注高端IT培训 做良心教育,做专业教育,做受人尊敬的职业教 ...

  8. 用1个拨码开关控制所有的LED灯亮灭

    FPGA电路开发入门实验1:项目创建.编译和下载 一.实验要求 用1个拨码开关控制L0-L7的LED灯亮灭. 二.项目的创建 1.选择项目所用开发板并配置参数,命名为"shiyan1&quo ...

  9. 记录一个 三个io口控制四个LED灯和一个按键的电路和怎么检测

    昨天要写个底层程序 发现要控制四个led灯和一个按键,按键开始一直不能很好的检测, 后面论坛问人才搞好. 分时扫描: 前1-4驱动LED,5检测KEY 1,LED1输出高,LED2输出低,LED3输入 ...

最新文章

  1. 基于vue-cli,做个nuxt脚手架~
  2. OCS Inventory NG使用之在windows 2008 R2平台下安装服务器端(三)
  3. 如何使wordpress导航栏在新窗口打开
  4. linux 手动解压or增加or更新 jar 包中文件
  5. 在WPF中进行碰撞检测
  6. 暴力打表之hdu 2089
  7. linux bind日志级别,BIND日志相关(一)
  8. docker基础入门和docker compose实战
  9. php漏洞黑掉数据库,WDCPnbsp;add_user.php任意数据库添加任意用户漏洞分析
  10. gitleb 登陆方式_gitlab连接方式
  11. 工作流的大致开发流程
  12. 正好股票开户有色金属应声大涨
  13. 怎么修改teredo服务器,技术员设置win7系统通过teredo连接IPv6的修复方案
  14. 关于DoG角点检测matlab实现
  15. 关于 Vue.js 的那些事儿
  16. LaTeX Subfigure 中间添加垂直线
  17. #64 Abstract Interpretation: Introduction #66 Galois Connections - 课程笔记
  18. 随记--做一个“懒惰”的程序员
  19. 北京师范大学新闻与传播专硕考研一战上岸经验分享
  20. C语言中的static的作用~

热门文章

  1. ImportError lib64libstdc++.so.6 version `GLIBCXX_3.4.29’ not found
  2. 录制电脑屏幕的软件哪个性价比高?这4款软件就很实用
  3. oppo手机删除计算机怎样恢复,【数据恢复篇】oppo手机删掉的照片怎么恢复
  4. java strem 分组并提取对象中的某个字段
  5. 【天王星小课堂】第2讲 | 什么是量化投资
  6. CSS 3.0实现猫脸动画
  7. WIN10系统下PS软件卡顿问题的解决方法
  8. 五步法,做有用的经营分析
  9. 计算机组成原理 赖晓铮,计算机组成原理实验 2.5 运算器 赖晓铮
  10. Mac必备的优秀软件集合